gstavidemux.c 37.9 KB
Newer Older
1
2
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@temple-baptist.com>
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
19
/* Element-Checklist-Version: 5 */
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
20

21
22
23
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
24

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
25
26
#include <string.h>

27
#include "gst/riff/riff-media.h"
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
28
#include "gstavidemux.h"
29
#include "avi-ids.h"
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
30

31
32
33
GST_DEBUG_CATEGORY_STATIC (avidemux_debug);
#define GST_CAT_DEFAULT avidemux_debug

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
34
/* AviDemux signals and args */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
35
36
enum
{
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
37
38
39
40
  /* FILL ME */
  LAST_SIGNAL
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
41
42
enum
{
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
43
  ARG_0,
44
  ARG_STREAMINFO,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
45
46
47
  /* FILL ME */
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
48
49
50
51
52
static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-msvideo")
    );
53

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
54
55
56
static void gst_avi_demux_base_init (GstAviDemuxClass * klass);
static void gst_avi_demux_class_init (GstAviDemuxClass * klass);
static void gst_avi_demux_init (GstAviDemux * avi);
57

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
58
59
static void gst_avi_demux_reset (GstAviDemux * avi);
static void gst_avi_demux_loop (GstElement * element);
60

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
61
62
static gboolean gst_avi_demux_send_event (GstElement * element,
    GstEvent * event);
63

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
64
65
66
67
68
69
70
71
72
static const GstEventMask *gst_avi_demux_get_event_mask (GstPad * pad);
static gboolean gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event);
static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad);
static const GstQueryType *gst_avi_demux_get_src_query_types (GstPad * pad);
static gboolean gst_avi_demux_handle_src_query (GstPad * pad,
    GstQueryType type, GstFormat * format, gint64 * value);
static gboolean gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
Wim Taymans's avatar
Wim Taymans committed
73

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
74
static GstElementStateReturn gst_avi_demux_change_state (GstElement * element);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
75

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
76
77
static void gst_avi_demux_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
78

79
static GstRiffReadClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
80

81
/*static guint gst_avi_demux_signals[LAST_SIGNAL] = { 0 }; */
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
82
83

GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
84
gst_avi_demux_get_type (void)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
85
86
87
88
89
{
  static GType avi_demux_type = 0;

  if (!avi_demux_type) {
    static const GTypeInfo avi_demux_info = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
90
      sizeof (GstAviDemuxClass),
91
      (GBaseInitFunc) gst_avi_demux_base_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
92
      NULL,
93
      (GClassInitFunc) gst_avi_demux_class_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
94
95
      NULL,
      NULL,
96
      sizeof (GstAviDemux),
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
97
      0,
98
      (GInstanceInitFunc) gst_avi_demux_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
99
    };
100
101
102

    avi_demux_type =
	g_type_register_static (GST_TYPE_RIFF_READ,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
103
	"GstAviDemux", &avi_demux_info, 0);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
104
  }
105

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
106
107
108
  return avi_demux_type;
}

109
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
110
gst_avi_demux_base_init (GstAviDemuxClass * klass)
111
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
112
113
114
115
116
117
118
  static GstElementDetails gst_avi_demux_details =
      GST_ELEMENT_DETAILS ("Avi demuxer",
      "Codec/Demuxer",
      "Demultiplex an avi file into audio and video",
      "Erik Walthinsen <omega@cse.ogi.edu>\n"
      "Wim Taymans <wim.taymans@chello.be>\n"
      "Ronald Bultje <rbultje@ronald.bitfreak.net>");
119
120
121
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstPadTemplate *videosrctempl, *audiosrctempl;
  GstCaps *audcaps, *vidcaps;
122

123
  audcaps = gst_riff_create_audio_template_caps ();
124
  audiosrctempl = gst_pad_template_new ("audio_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
125
      GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps);
126

David Schleef's avatar
David Schleef committed
127
128
  vidcaps = gst_riff_create_video_template_caps ();
  gst_caps_append (vidcaps, gst_riff_create_iavs_template_caps ());
129
  videosrctempl = gst_pad_template_new ("video_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
130
      GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps);
131

132
133
134
  gst_element_class_add_pad_template (element_class, audiosrctempl);
  gst_element_class_add_pad_template (element_class, videosrctempl);
  gst_element_class_add_pad_template (element_class,
David Schleef's avatar
David Schleef committed
135
      gst_static_pad_template_get (&sink_templ));
136
137
138
  gst_element_class_set_details (element_class, &gst_avi_demux_details);
}

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
139
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
140
gst_avi_demux_class_init (GstAviDemuxClass * klass)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
141
142
143
144
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
145
146
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
147

148
  g_object_class_install_property (gobject_class, ARG_STREAMINFO,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
149
150
      g_param_spec_boxed ("streaminfo", "Streaminfo", "Streaminfo",
	  GST_TYPE_CAPS, G_PARAM_READABLE));
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
151

152
  GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
153
      0, "Demuxer for AVI streams");
154
155

  parent_class = g_type_class_ref (GST_TYPE_RIFF_READ);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
156

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
157
  gobject_class->get_property = gst_avi_demux_get_property;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
158

Wim Taymans's avatar
Wim Taymans committed
159
  gstelement_class->change_state = gst_avi_demux_change_state;
160
  gstelement_class->send_event = gst_avi_demux_send_event;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
161
162
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
163
164
static void
gst_avi_demux_init (GstAviDemux * avi)
165
{
166
  GST_FLAG_SET (avi, GST_ELEMENT_EVENT_AWARE);
167

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
168
169
170
  avi->sinkpad =
      gst_pad_new_from_template (gst_static_pad_template_get (&sink_templ),
      "sink");
171
172
  gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad);
  GST_RIFF_READ (avi)->sinkpad = avi->sinkpad;
Wim Taymans's avatar
Wim Taymans committed
173

174
175
  gst_element_set_loop_function (GST_ELEMENT (avi), gst_avi_demux_loop);
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
176

177
178
179
  avi->streaminfo = NULL;
  avi->index_entries = NULL;
  memset (&avi->stream, 0, sizeof (avi->stream));
Wim Taymans's avatar
Wim Taymans committed
180
181
}

182
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
183
gst_avi_demux_reset (GstAviDemux * avi)
184
{
185
  gint i;
186

187
188
189
190
191
  for (i = 0; i < avi->num_streams; i++) {
    g_free (avi->stream[i].strh);
    gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad);
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
192

193
194
195
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
196

197
198
  avi->state = GST_AVI_DEMUX_START;
  avi->level_up = 0;
199

200
201
202
  if (avi->index_entries) {
    g_free (avi->index_entries);
    avi->index_entries = NULL;
203
  }
204
  avi->index_size = 0;
205

206
207
  avi->num_frames = 0;
  avi->us_per_frame = 0;
208

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
209
  avi->seek_offset = (guint64) - 1;
210

211
  gst_caps_replace (&avi->streaminfo, NULL);
212
213
214
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
215
gst_avi_demux_streaminfo (GstAviDemux * avi)
216
217
218
{
  /* compression formats are added later - a bit hacky */

David Schleef's avatar
David Schleef committed
219
220
  gst_caps_replace (&avi->streaminfo,
      gst_caps_new_simple ("application/x-gst-streaminfo", NULL));
221

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
222
  /*g_object_notify(G_OBJECT(avi), "streaminfo"); */
223
224
}

225
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
226
227
gst_avi_demux_index_next (GstAviDemux * avi,
    gint stream_nr, gint start, guint32 flags)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
228
{
229
230
  gint i;
  gst_avi_index_entry *entry = NULL;
231

232
233
  for (i = start; i < avi->index_size; i++) {
    entry = &avi->index_entries[i];
234

235
    if (entry->stream_nr == stream_nr && (entry->flags & flags) == flags) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
236
      break;
237
238
    }
  }
239

240
241
  return entry;
}
242

243
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
244
245
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
    gint stream_nr, guint64 time, guint32 flags)
246
247
248
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
249

250
251
252
253
254
  i = -1;
  do {
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags);
    if (!entry)
      return NULL;
255

256
    i = entry->index_nr;
257

258
259
260
261
    if (entry->ts <= time) {
      last_entry = entry;
    }
  } while (entry->ts <= time);
262

263
264
  return last_entry;
}
265

266
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
267
268
gst_avi_demux_index_entry_for_byte (GstAviDemux * avi,
    gint stream_nr, guint64 byte, guint32 flags)
269
270
271
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
272

273
274
275
276
277
  i = -1;
  do {
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags);
    if (!entry)
      return NULL;
278

279
    i = entry->index_nr;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
280

281
282
283
284
    if (entry->bytes_before <= byte) {
      last_entry = entry;
    }
  } while (entry->bytes_before <= byte);
285

286
  return last_entry;
287
288
}

289
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
290
291
gst_avi_demux_index_entry_for_frame (GstAviDemux * avi,
    gint stream_nr, guint32 frame, guint32 flags)
292
{
293
294
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
295

296
297
298
299
300
  i = -1;
  do {
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags);
    if (!entry)
      return NULL;
301

302
    i = entry->index_nr;
303

304
305
306
307
    if (entry->frames_before <= frame) {
      last_entry = entry;
    }
  } while (entry->frames_before <= frame);
308

309
  return last_entry;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
310
311
}

312
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
313
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
314
{
315
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
316

317
318
319
320
321
322
323
324
325
326
327
  static const GstFormat src_a_formats[] = {
    GST_FORMAT_TIME,
    GST_FORMAT_BYTES,
    GST_FORMAT_DEFAULT,
    0
  };
  static const GstFormat src_v_formats[] = {
    GST_FORMAT_TIME,
    GST_FORMAT_DEFAULT,
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
328

329
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
330
      src_a_formats : src_v_formats);
331
}
332

333
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
334
335
336
gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
337
338
{
  gboolean res = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
339
340

  /*GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad)); */
341
  avi_stream_context *stream = gst_pad_get_element_private (pad);
342

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
343
344
  if (stream->strh->type != GST_RIFF_FCC_auds &&
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
345
    return FALSE;
346

347
348
349
350
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
	case GST_FORMAT_BYTES:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
351
352
353
354
355
356
357
	  *dest_value = src_value * stream->strh->rate /
	      (stream->strh->scale * GST_SECOND);
	  break;
	case GST_FORMAT_DEFAULT:
	  *dest_value = src_value * stream->strh->rate /
	      (stream->strh->scale * GST_SECOND);
	  break;
358
359
360
	default:
	  res = FALSE;
	  break;
361
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
362
      break;
363
364
365
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
	case GST_FORMAT_TIME:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
366
	  *dest_value = ((gfloat) src_value) * GST_SECOND / stream->strh->rate;
367
368
369
370
	  break;
	default:
	  res = FALSE;
	  break;
371
372
      }
      break;
373
374
375
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
	case GST_FORMAT_TIME:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
376
377
	  *dest_value = ((((gfloat) src_value) * stream->strh->scale) /
	      stream->strh->rate) * GST_SECOND;
378
379
380
381
	  break;
	default:
	  res = FALSE;
	  break;
382
      }
Wim Taymans's avatar
Wim Taymans committed
383
384
      break;
    default:
385
      res = FALSE;
386
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
387

388
  return res;
389
390
}

391
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
392
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
393
{
394
395
396
397
398
  static const GstQueryType src_types[] = {
    GST_QUERY_TOTAL,
    GST_QUERY_POSITION,
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
399

400
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
401
402
}

403
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
404
405
gst_avi_demux_handle_src_query (GstPad * pad,
    GstQueryType type, GstFormat * format, gint64 * value)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
406
{
407
  gboolean res = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
408
409

  /*GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad)); */
410
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
411
412

  switch (type) {
Wim Taymans's avatar
Wim Taymans committed
413
    case GST_QUERY_TOTAL:
Wim Taymans's avatar
Wim Taymans committed
414
      switch (*format) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
415
416
417
	case GST_FORMAT_TIME:
	  *value = (((gfloat) stream->strh->scale) * stream->strh->length /
	      stream->strh->rate) * GST_SECOND;
Wim Taymans's avatar
Wim Taymans committed
418
	  break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
419
420
421
422
	case GST_FORMAT_BYTES:
	  if (stream->strh->type == GST_RIFF_FCC_auds) {
	    *value = stream->total_bytes;
	  } else
Wim Taymans's avatar
Wim Taymans committed
423
424
	    res = FALSE;
	  break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
425
426
427
	case GST_FORMAT_DEFAULT:
	  if (stream->strh->type == GST_RIFF_FCC_auds)
	    *value = stream->strh->length * stream->strh->samplesize;
428
	  else if (stream->strh->type == GST_RIFF_FCC_vids)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
429
	    *value = stream->strh->length;
Wim Taymans's avatar
Wim Taymans committed
430
431
432
433
	  else
	    res = FALSE;
	  break;
	default:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
434
	  res = FALSE;
Wim Taymans's avatar
Wim Taymans committed
435
436
437
	  break;
      }
      break;
Wim Taymans's avatar
Wim Taymans committed
438
    case GST_QUERY_POSITION:
Wim Taymans's avatar
Wim Taymans committed
439
      switch (*format) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
440
441
	case GST_FORMAT_TIME:
	  if (stream->strh->samplesize &&
442
	      stream->strh->type == GST_RIFF_FCC_auds) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
443
444
445
446
447
	    *value = ((gfloat) stream->current_byte) * GST_SECOND /
		stream->strh->rate;
	  } else {
	    *value = (((gfloat) stream->current_frame) * stream->strh->scale /
		stream->strh->rate) * GST_SECOND;
448
	  }
Wim Taymans's avatar
Wim Taymans committed
449
	  break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
450
451
	case GST_FORMAT_BYTES:
	  *value = stream->current_byte;
Wim Taymans's avatar
Wim Taymans committed
452
	  break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
453
454
455
456
457
458
	case GST_FORMAT_DEFAULT:
	  if (stream->strh->samplesize &&
	      stream->strh->type == GST_RIFF_FCC_auds)
	    *value = stream->current_byte * stream->strh->samplesize;
	  else
	    *value = stream->current_frame;
Wim Taymans's avatar
Wim Taymans committed
459
460
	  break;
	default:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
461
	  res = FALSE;
Wim Taymans's avatar
Wim Taymans committed
462
463
464
465
466
467
468
469
470
471
472
	  break;
      }
      break;
    default:
      res = FALSE;
      break;
  }

  return res;
}

473
static GstCaps *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
474
gst_avi_demux_src_getcaps (GstPad * pad)
475
476
477
478
479
480
{
  avi_stream_context *stream = gst_pad_get_element_private (pad);

  return gst_caps_copy (stream->caps);
}

Wim Taymans's avatar
Wim Taymans committed
481
static gint32
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
482
gst_avi_demux_sync_streams (GstAviDemux * avi, guint64 time)
Wim Taymans's avatar
Wim Taymans committed
483
484
{
  gint i;
Wim Taymans's avatar
Wim Taymans committed
485
  guint32 min_index = G_MAXUINT;
Wim Taymans's avatar
Wim Taymans committed
486
487
488
  avi_stream_context *stream;
  gst_avi_index_entry *entry;

489
490
  for (i = 0; i < avi->num_streams; i++) {
    stream = &avi->stream[i];
Wim Taymans's avatar
Wim Taymans committed
491

492
    GST_DEBUG ("finding %d for time %" G_GINT64_FORMAT, i, time);
Wim Taymans's avatar
Wim Taymans committed
493

494
    entry = gst_avi_demux_index_entry_for_time (avi, stream->num, time,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
495
	GST_RIFF_IF_KEYFRAME);
Wim Taymans's avatar
Wim Taymans committed
496
497
498
499
    if (entry) {
      min_index = MIN (entry->index_nr, min_index);
    }
  }
500
  GST_DEBUG ("first index at %d", min_index);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
501

Wim Taymans's avatar
Wim Taymans committed
502
503
  /* now we know the entry we need to sync on. calculate number of frames to
   * skip fro there on and the stream stats */
504
  for (i = 0; i < avi->num_streams; i++) {
Wim Taymans's avatar
Wim Taymans committed
505
    gst_avi_index_entry *next_entry;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
506

507
    stream = &avi->stream[i];
Wim Taymans's avatar
Wim Taymans committed
508
509

    /* next entry */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
510
    next_entry = gst_avi_demux_index_next (avi, stream->num, min_index, 0);
Wim Taymans's avatar
Wim Taymans committed
511
    /* next entry with keyframe */
512
    entry = gst_avi_demux_index_next (avi, stream->num, min_index,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
513
	GST_RIFF_IF_KEYFRAME);
Wim Taymans's avatar
Wim Taymans committed
514
515
516
517
518

    stream->current_byte = next_entry->bytes_before;
    stream->current_frame = next_entry->frames_before;
    stream->skip = entry->frames_before - next_entry->frames_before;

519
    GST_DEBUG ("%d skip %d", stream->num, stream->skip);
Wim Taymans's avatar
Wim Taymans committed
520
  }
521

522
  GST_DEBUG ("final index at %d", min_index);
Wim Taymans's avatar
Wim Taymans committed
523
524
525
526

  return min_index;
}

527
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
528
gst_avi_demux_send_event (GstElement * element, GstEvent * event)
529
{
Wim Taymans's avatar
Wim Taymans committed
530
  const GList *pads;
531
532
533

  pads = gst_element_get_pad_list (element);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
534
  while (pads) {
535
536
537
    GstPad *pad = GST_PAD (pads->data);

    if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
Wim Taymans's avatar
Wim Taymans committed
538
539
540
541
542
      /* we ref the event here as we might have to try again if the event
       * failed on this pad */
      gst_event_ref (event);
      if (gst_avi_demux_handle_src_event (pad, event)) {
	gst_event_unref (event);
543

Wim Taymans's avatar
Wim Taymans committed
544
545
	return TRUE;
      }
546
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
547

548
549
    pads = g_list_next (pads);
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
550

Wim Taymans's avatar
Wim Taymans committed
551
  gst_event_unref (event);
552

553
554
555
  return FALSE;
}

556
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
557
gst_avi_demux_get_event_mask (GstPad * pad)
558
559
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
560
561
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
562
563
564
565
  };

  return masks;
}
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
566

Wim Taymans's avatar
Wim Taymans committed
567
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
568
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
569
570
{
  gboolean res = TRUE;
571
  GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Wim Taymans's avatar
Wim Taymans committed
572
  avi_stream_context *stream;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
573

Wim Taymans's avatar
Wim Taymans committed
574
575
576
577
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
578
      GST_DEBUG ("seek format %d, %08x", GST_EVENT_SEEK_FORMAT (event),
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
579
	  stream->strh->type);
580

Wim Taymans's avatar
Wim Taymans committed
581
582
      switch (GST_EVENT_SEEK_FORMAT (event)) {
	case GST_FORMAT_BYTES:
Wim Taymans's avatar
Wim Taymans committed
583
	case GST_FORMAT_DEFAULT:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
584
	case GST_FORMAT_TIME:{
585
	  gst_avi_index_entry *seek_entry, *entry = NULL;
Wim Taymans's avatar
Wim Taymans committed
586
587
	  gint64 desired_offset = GST_EVENT_SEEK_OFFSET (event);
	  guint32 flags;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
588
	  guint64 min_index;
Wim Taymans's avatar
Wim Taymans committed
589
590

	  /* no seek on audio yet */
591
	  if (stream->strh->type == GST_RIFF_FCC_auds) {
Wim Taymans's avatar
Wim Taymans committed
592
593
594
	    res = FALSE;
	    goto done;
	  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
595
	  GST_DEBUG ("seeking to %" G_GINT64_FORMAT, desired_offset);
Wim Taymans's avatar
Wim Taymans committed
596

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
597
598
	  flags = GST_RIFF_IF_KEYFRAME;
	  switch (GST_EVENT_SEEK_FORMAT (event)) {
599
	    case GST_FORMAT_BYTES:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
600
601
602
	      entry = gst_avi_demux_index_entry_for_byte (avi, stream->num,
		  desired_offset, flags);
	      break;
603
	    case GST_FORMAT_DEFAULT:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
604
605
606
	      entry = gst_avi_demux_index_entry_for_frame (avi, stream->num,
		  desired_offset, flags);
	      break;
607
	    case GST_FORMAT_TIME:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
608
609
610
611
	      entry = gst_avi_demux_index_entry_for_time (avi, stream->num,
		  desired_offset, flags);
	      break;
	  }
Wim Taymans's avatar
Wim Taymans committed
612
613

	  if (entry) {
614
	    min_index = gst_avi_demux_sync_streams (avi, entry->ts);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
615
	    seek_entry = &avi->index_entries[min_index];
616
617

	    avi->seek_offset = seek_entry->offset + avi->index_offset;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
618
	    avi->last_seek = entry->ts;
619
	  } else {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
620
621
	    GST_DEBUG ("no index entry found for format=%d value=%"
		G_GINT64_FORMAT, GST_EVENT_SEEK_FORMAT (event), desired_offset);
Wim Taymans's avatar
Wim Taymans committed
622
623
624
625
626
627
	    res = FALSE;
	  }
	  break;
	}
	default:
	  res = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
628
	  break;
Wim Taymans's avatar
Wim Taymans committed
629
630
631
632
633
634
      }
      break;
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
635
636
637
638

done:
  gst_event_unref (event);

Wim Taymans's avatar
Wim Taymans committed
639
640
641
  return res;
}

642
643
644
645
646
/*
 * "Open" a RIFF file.
 */

gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
647
gst_avi_demux_stream_init (GstAviDemux * avi)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
648
{
649
650
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
651

652
653
654
  if (!gst_riff_read_header (riff, &doctype))
    return FALSE;
  if (doctype != GST_RIFF_RIFF_AVI) {
655
    GST_ELEMENT_ERROR (avi, STREAM, WRONG_TYPE, (NULL), (NULL));
656
657
    return FALSE;
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
658

659
660
661
662
663
664
665
666
  return TRUE;
}

/*
 * Read 'avih' header.
 */

gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
667
668
gst_avi_demux_stream_avih (GstAviDemux * avi,
    guint32 * flags, guint32 * streams)
669
670
671
672
{
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 tag;
  GstBuffer *buf;
673
  gst_riff_avih avih, *_avih;
674
675
676
677
678
679
680
681
682
683
684

  if (!gst_riff_read_data (riff, &tag, &buf))
    return FALSE;

  if (tag != GST_RIFF_TAG_avih) {
    g_warning ("Not a avih chunk");
    gst_buffer_unref (buf);
    return FALSE;
  }
  if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_avih)) {
    g_warning ("Too small avih (%d available, %d needed)",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
685
	GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_avih));
686
687
688
689
    gst_buffer_unref (buf);
    return FALSE;
  }

690
  _avih = (gst_riff_avih *) GST_BUFFER_DATA (buf);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
691
692
693
694
695
  avih.us_frame = GUINT32_FROM_LE (_avih->us_frame);
  avih.max_bps = GUINT32_FROM_LE (_avih->max_bps);
  avih.pad_gran = GUINT32_FROM_LE (_avih->pad_gran);
  avih.flags = GUINT32_FROM_LE (_avih->flags);
  avih.tot_frames = GUINT32_FROM_LE (_avih->tot_frames);
696
  avih.init_frames = GUINT32_FROM_LE (_avih->init_frames);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
697
698
699
700
701
702
703
704
  avih.streams = GUINT32_FROM_LE (_avih->streams);
  avih.bufsize = GUINT32_FROM_LE (_avih->bufsize);
  avih.width = GUINT32_FROM_LE (_avih->width);
  avih.height = GUINT32_FROM_LE (_avih->height);
  avih.scale = GUINT32_FROM_LE (_avih->scale);
  avih.rate = GUINT32_FROM_LE (_avih->rate);
  avih.start = GUINT32_FROM_LE (_avih->start);
  avih.length = GUINT32_FROM_LE (_avih->length);
705
706
707

  /* debug stuff */
  GST_INFO ("avih tag found:");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
708
709
710
  GST_INFO (" us_frame    %u", avih.us_frame);
  GST_INFO (" max_bps     %u", avih.max_bps);
  GST_INFO (" pad_gran    %u", avih.pad_gran);
711
  GST_INFO (" flags       0x%08x", avih.flags);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
712
713
714
715
716
717
718
719
720
721
  GST_INFO (" tot_frames  %u", avih.tot_frames);
  GST_INFO (" init_frames %u", avih.init_frames);
  GST_INFO (" streams     %u", avih.streams);
  GST_INFO (" bufsize     %u", avih.bufsize);
  GST_INFO (" width       %u", avih.width);
  GST_INFO (" height      %u", avih.height);
  GST_INFO (" scale       %u", avih.scale);
  GST_INFO (" rate        %u", avih.rate);
  GST_INFO (" start       %u", avih.start);
  GST_INFO (" length      %u", avih.length);
722
723
724
725
726

  avi->num_frames = avih.tot_frames;
  avi->us_per_frame = avih.us_frame;
  *streams = avih.streams;
  *flags = avih.flags;
727
728
729
730
731
732
733
734
735
736
737

  gst_buffer_unref (buf);

  return TRUE;
}

/*
 * Add a stream.
 */

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
738
gst_avi_demux_add_stream (GstAviDemux * avi)
739
740
741
742
743
744
745
746
747
748
{
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (avi);
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 tag;
  gst_riff_strh *strh;
  gchar *name = NULL, *padname = NULL;
  GstCaps *caps = NULL;
  GstPadTemplate *templ = NULL;
  GstPad *pad;
  avi_stream_context *stream;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
749
750
  union
  {
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
    gst_riff_strf_vids *vids;
    gst_riff_strf_auds *auds;
    gst_riff_strf_iavs *iavs;
  } strf;

  /* the stream starts with a 'strh' header */
  if (!(tag = gst_riff_peek_tag (riff, NULL)))
    return FALSE;
  if (tag != GST_RIFF_TAG_strh) {
    g_warning ("Invalid stream header (no strh at begin)");
    goto skip_stream;
  }
  if (!gst_riff_read_strh (riff, &strh))
    return FALSE;

  /* then comes a 'strf' of that specific type */
  if (!(tag = gst_riff_peek_tag (riff, NULL)))
    return FALSE;
  if (tag != GST_RIFF_TAG_strf) {
770
    GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
771
	("Invalid AVI header (no strf as second tag)"));
772
773
774
775
776
    goto skip_stream;
  }
  switch (strh->type) {
    case GST_RIFF_FCC_vids:
      if (!gst_riff_read_strf_vids (riff, &strf.vids))
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
777
	return FALSE;
778
779
780
      break;
    case GST_RIFF_FCC_auds:
      if (!gst_riff_read_strf_auds (riff, &strf.auds))
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
781
	return FALSE;
782
783
784
      break;
    case GST_RIFF_FCC_iavs:
      if (!gst_riff_read_strf_iavs (riff, &strf.iavs))
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
785
	return FALSE;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
786
      break;
787
788
    default:
      g_warning ("Unknown stream type " GST_FOURCC_FORMAT,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
789
	  GST_FOURCC_ARGS (strh->type));
790
791
      goto skip_stream;
  }
Wim Taymans's avatar
Wim Taymans committed
792

793
794
795
796
797
798
799
800
  /* read other things */
  while (TRUE) {
    if (!(tag = gst_riff_peek_tag (riff, &avi->level_up)))
      return FALSE;
    else if (avi->level_up) {
      avi->level_up--;
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
801

802
803
    switch (tag) {
      case GST_RIFF_TAG_strn:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
804
805
806
807
808
	if (name)
	  g_free (name);
	if (!gst_riff_read_ascii (riff, &tag, &name))
	  return FALSE;
	break;
Wim Taymans's avatar
Wim Taymans committed
809

810
      default:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
811
812
813
	GST_WARNING ("Unknown tag " GST_FOURCC_FORMAT " in AVI header",
	    GST_FOURCC_ARGS (tag));
	/* fall-through */
814

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
815
      case GST_RIFF_TAG_strd:	/* what is this? */
816
      case GST_RIFF_TAG_JUNK:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
817
818
819
	if (!gst_riff_read_skip (riff))
	  return FALSE;
	break;
820
    }
Wim Taymans's avatar
Wim Taymans committed
821

822
823
    if (avi->level_up) {
      avi->level_up--;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
824
      break;
Wim Taymans's avatar
Wim Taymans committed
825
    }
826
827
828
829
830
  }

  /* create stream name + pad */
  switch (strh->type) {
    case GST_RIFF_FCC_vids:
831
832
833
    {
      char *codec_name = NULL;
      GstTagList *list = gst_tag_list_new ();
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
834

835
836
      padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
      templ = gst_element_class_get_pad_template (klass, "video_%02d");
837
      caps = gst_riff_create_video_caps (strf.vids->compression, strh,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
838
	  strf.vids, &codec_name);
839
      gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_VIDEO_CODEC,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
840
	  codec_name, NULL);
841
842
      gst_element_found_tags (GST_ELEMENT (avi), list);
      gst_tag_list_free (list);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
843
844
      if (codec_name)
	g_free (codec_name);
845
846
847
      g_free (strf.vids);
      avi->num_v_streams++;
      break;
848
    }
849
    case GST_RIFF_FCC_auds:
850
851
852
    {
      char *codec_name = NULL;
      GstTagList *list = gst_tag_list_new ();
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
853

854
855
      padname = g_strdup_printf ("audio_%02d", avi->num_a_streams);
      templ = gst_element_class_get_pad_template (klass, "audio_%02d");
856
      caps = gst_riff_create_audio_caps (strf.auds->format, strh, strf.auds,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
857
	  &codec_name);
858
      gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_AUDIO_CODEC,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
859
	  codec_name, NULL);
860
861
      gst_element_found_tags (GST_ELEMENT (avi), list);
      gst_tag_list_free (list);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
862
863
      if (codec_name)
	g_free (codec_name);