gstavidemux.c 39.8 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
35
36
37
38
39
40
41
/* AviDemux signals and args */
enum {
  /* FILL ME */
  LAST_SIGNAL
};

enum {
  ARG_0,
42
  ARG_STREAMINFO,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
43
44
45
  /* FILL ME */
};

David Schleef's avatar
David Schleef committed
46
47
static GstStaticPadTemplate sink_templ =
GST_STATIC_PAD_TEMPLATE (
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
48
49
50
  "sink",
  GST_PAD_SINK,
  GST_PAD_ALWAYS,
David Schleef's avatar
David Schleef committed
51
  GST_STATIC_CAPS ("video/x-msvideo")
52
53
);

54
static void 	gst_avi_demux_base_init	        (GstAviDemuxClass *klass);
55
static void 	gst_avi_demux_class_init	(GstAviDemuxClass *klass);
56
static void 	gst_avi_demux_init		(GstAviDemux *avi);
57

58
static void	gst_avi_demux_reset		(GstAviDemux *avi);
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
static void 	gst_avi_demux_loop 		(GstElement  *element);

static gboolean gst_avi_demux_send_event 	(GstElement  *element,
						 GstEvent    *event);

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
81

Wim Taymans's avatar
Wim Taymans committed
82
static GstElementStateReturn
83
		gst_avi_demux_change_state 	(GstElement  *element);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
84

85
86
87
88
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
89

90
static GstRiffReadClass *parent_class = NULL;
91
/*static guint gst_avi_demux_signals[LAST_SIGNAL] = { 0 }; */
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
92
93
94
95
96
97
98
99

GType
gst_avi_demux_get_type(void) 
{
  static GType avi_demux_type = 0;

  if (!avi_demux_type) {
    static const GTypeInfo avi_demux_info = {
100
101
      sizeof (GstAviDemuxClass),      
      (GBaseInitFunc) gst_avi_demux_base_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
102
      NULL,
103
      (GClassInitFunc) gst_avi_demux_class_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
104
105
      NULL,
      NULL,
106
      sizeof (GstAviDemux),
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
107
      0,
108
      (GInstanceInitFunc) gst_avi_demux_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
109
    };
110
111
112
113
114

    avi_demux_type =
	g_type_register_static (GST_TYPE_RIFF_READ,
				"GstAviDemux",
				&avi_demux_info, 0);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
115
  }
116

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
117
118
119
  return avi_demux_type;
}

120
static void
121
gst_avi_demux_base_init (GstAviDemuxClass *klass)
122
123
124
125
126
127
{
  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"
128
129
    "Wim Taymans <wim.taymans@chello.be>\n"
    "Ronald Bultje <rbultje@ronald.bitfreak.net>"
130
  );
131
132
133
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstPadTemplate *videosrctempl, *audiosrctempl;
  GstCaps *audcaps, *vidcaps;
134

135
  audcaps = gst_riff_create_audio_template_caps ();
136
137
138
  audiosrctempl = gst_pad_template_new ("audio_%02d",
					GST_PAD_SRC,
					GST_PAD_SOMETIMES,
David Schleef's avatar
David Schleef committed
139
					audcaps);
140

David Schleef's avatar
David Schleef committed
141
142
  vidcaps = gst_riff_create_video_template_caps ();
  gst_caps_append (vidcaps, gst_riff_create_iavs_template_caps ());
143
144
145
  videosrctempl = gst_pad_template_new ("video_%02d",
					GST_PAD_SRC,
					GST_PAD_SOMETIMES,
David Schleef's avatar
David Schleef committed
146
					vidcaps);
147

148
149
150
  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
151
      gst_static_pad_template_get (&sink_templ));
152
153
154
  gst_element_class_set_details (element_class, &gst_avi_demux_details);
}

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
155
156
157
158
159
160
161
162
163
static void
gst_avi_demux_class_init (GstAviDemuxClass *klass) 
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

  gobject_class = (GObjectClass*)klass;
  gstelement_class = (GstElementClass*)klass;

164
165
166
  g_object_class_install_property (gobject_class, ARG_STREAMINFO,
    g_param_spec_boxed ("streaminfo", "Streaminfo", "Streaminfo",
                        GST_TYPE_CAPS, G_PARAM_READABLE));
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
167

168
169
170
171
  GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux",
			   0, "Demuxer for AVI streams");

  parent_class = g_type_class_ref (GST_TYPE_RIFF_READ);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
172
173
  
  gobject_class->get_property = gst_avi_demux_get_property;
Wim Taymans's avatar
Wim Taymans committed
174
175
  
  gstelement_class->change_state = gst_avi_demux_change_state;
176
  gstelement_class->send_event = gst_avi_demux_send_event;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
177
178
179
}

static void 
180
gst_avi_demux_init (GstAviDemux *avi) 
181
{
182
  GST_FLAG_SET (avi, GST_ELEMENT_EVENT_AWARE);
183

184
  avi->sinkpad = gst_pad_new_from_template (
David Schleef's avatar
David Schleef committed
185
	gst_static_pad_template_get (&sink_templ), "sink");
186
187
  gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad);
  GST_RIFF_READ (avi)->sinkpad = avi->sinkpad;
Wim Taymans's avatar
Wim Taymans committed
188

189
190
  gst_element_set_loop_function (GST_ELEMENT (avi), gst_avi_demux_loop);
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
191

192
193
194
  avi->streaminfo = NULL;
  avi->index_entries = NULL;
  memset (&avi->stream, 0, sizeof (avi->stream));
Wim Taymans's avatar
Wim Taymans committed
195
196
}

197
static void
198
gst_avi_demux_reset (GstAviDemux *avi)
199
{
200
  gint i;
201

202
203
204
205
206
  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));
207

208
209
210
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
211

212
213
  avi->state = GST_AVI_DEMUX_START;
  avi->level_up = 0;
214

215
216
217
  if (avi->index_entries) {
    g_free (avi->index_entries);
    avi->index_entries = NULL;
218
  }
219
  avi->index_size = 0;
220

221
222
  avi->num_frames = 0;
  avi->us_per_frame = 0;
223

224
  avi->seek_offset = (guint64) -1;
225

226
  gst_caps_replace (&avi->streaminfo, NULL);
227
228
229
}

static void
230
gst_avi_demux_streaminfo (GstAviDemux *avi)
231
232
233
{
  /* compression formats are added later - a bit hacky */

David Schleef's avatar
David Schleef committed
234
235
  gst_caps_replace (&avi->streaminfo,
      gst_caps_new_simple ("application/x-gst-streaminfo", NULL));
236

237
  /*g_object_notify(G_OBJECT(avi), "streaminfo");*/
238
239
}

240
241
242
243
244
static gst_avi_index_entry *
gst_avi_demux_index_next (GstAviDemux *avi,
			  gint         stream_nr,
			  gint         start,
			  guint32      flags)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
245
{
246
247
  gint i;
  gst_avi_index_entry *entry = NULL;
248

249
250
  for (i = start; i < avi->index_size; i++) {
    entry = &avi->index_entries[i];
251

252
    if (entry->stream_nr == stream_nr && (entry->flags & flags) == flags) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
253
      break;
254
255
    }
  }
256

257
258
  return entry;
}
259

260
261
262
263
264
265
266
267
static gst_avi_index_entry *
gst_avi_demux_index_entry_for_time (GstAviDemux *avi,
				    gint         stream_nr,
				    guint64      time,
				    guint32      flags)
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
268

269
270
271
272
273
  i = -1;
  do {
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags);
    if (!entry)
      return NULL;
274

275
    i = entry->index_nr;
276

277
278
279
280
    if (entry->ts <= time) {
      last_entry = entry;
    }
  } while (entry->ts <= time);
281

282
283
  return last_entry;
}
284

285
286
287
288
289
290
291
292
static gst_avi_index_entry *
gst_avi_demux_index_entry_for_byte (GstAviDemux *avi,
				    gint         stream_nr,
				    guint64      byte,
				    guint32      flags)
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
293

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

300
    i = entry->index_nr;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
301

302
303
304
305
    if (entry->bytes_before <= byte) {
      last_entry = entry;
    }
  } while (entry->bytes_before <= byte);
306

307
  return last_entry;
308
309
}

310
311
312
313
314
static gst_avi_index_entry *
gst_avi_demux_index_entry_for_frame (GstAviDemux *avi,
				     gint         stream_nr,
				     guint32      frame,
				     guint32      flags)
315
{
316
317
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
318

319
320
321
322
323
  i = -1;
  do {
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags);
    if (!entry)
      return NULL;
324

325
    i = entry->index_nr;
326

327
328
329
330
    if (entry->frames_before <= frame) {
      last_entry = entry;
    }
  } while (entry->frames_before <= frame);
331

332
  return last_entry;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
333
334
}

335
336
static const GstFormat *
gst_avi_demux_get_src_formats (GstPad *pad) 
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
337
{
338
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
339

340
341
342
343
344
345
346
347
348
349
350
  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
351

352
353
354
  return (stream->strh->type == GST_RIFF_FCC_auds ?
	  src_a_formats : src_v_formats);
}
355

356
357
358
359
360
361
362
363
364
365
static gboolean
gst_avi_demux_src_convert (GstPad    *pad,
			   GstFormat  src_format,
			   gint64     src_value,
	                   GstFormat *dest_format,
			   gint64    *dest_value)
{
  gboolean res = TRUE;
  /*GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad));*/
  avi_stream_context *stream = gst_pad_get_element_private (pad);
366

367
368
369
370
  if (stream->strh->type != GST_RIFF_FCC_auds && 
      (src_format == GST_FORMAT_BYTES ||
       *dest_format == GST_FORMAT_BYTES))
    return FALSE;
371

372
373
374
375
376
377
378
379
380
381
382
383
384
385
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
	case GST_FORMAT_BYTES:
          *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;
	default:
	  res = FALSE;
	  break;
386
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
387
      break;
388
389
390
391
392
393
394
395
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
	case GST_FORMAT_TIME:
          *dest_value = ((gfloat) src_value) * GST_SECOND / stream->strh->rate;
	  break;
	default:
	  res = FALSE;
	  break;
396
397
      }
      break;
398
399
400
401
402
403
404
405
406
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
	case GST_FORMAT_TIME:
          *dest_value = ((((gfloat) src_value) * stream->strh->scale)  /
			stream->strh->rate) * GST_SECOND;
	  break;
	default:
	  res = FALSE;
	  break;
407
      }
Wim Taymans's avatar
Wim Taymans committed
408
409
      break;
    default:
410
      res = FALSE;
411
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
412

413
  return res;
414
415
}

416
417
static const GstQueryType *
gst_avi_demux_get_src_query_types (GstPad *pad) 
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
418
{
419
420
421
422
423
  static const GstQueryType src_types[] = {
    GST_QUERY_TOTAL,
    GST_QUERY_POSITION,
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
424

425
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
426
427
}

428
429
430
431
432
static gboolean
gst_avi_demux_handle_src_query (GstPad      *pad,
				GstQueryType type, 
				GstFormat   *format,
				gint64      *value)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
433
{
434
435
436
  gboolean res = TRUE;
  /*GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad));*/
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
437
438

  switch (type) {
Wim Taymans's avatar
Wim Taymans committed
439
    case GST_QUERY_TOTAL:
Wim Taymans's avatar
Wim Taymans committed
440
441
      switch (*format) {
        case GST_FORMAT_TIME:
442
443
          *value = (((gfloat) stream->strh->scale) * stream->strh->length /
			stream->strh->rate) * GST_SECOND;
Wim Taymans's avatar
Wim Taymans committed
444
445
	  break;
        case GST_FORMAT_BYTES:
446
          if (stream->strh->type == GST_RIFF_FCC_auds) {
447
448
            *value = stream->total_bytes;
	  }
Wim Taymans's avatar
Wim Taymans committed
449
450
451
	  else
	    res = FALSE;
	  break;
Wim Taymans's avatar
Wim Taymans committed
452
        case GST_FORMAT_DEFAULT:
453
454
455
456
          if (stream->strh->type == GST_RIFF_FCC_auds)
            *value = stream->strh->length * stream->strh->samplesize;
	  else if (stream->strh->type == GST_RIFF_FCC_vids)
            *value = stream->strh->length;
Wim Taymans's avatar
Wim Taymans committed
457
458
459
460
461
462
463
464
	  else
	    res = FALSE;
	  break;
	default:
          res = FALSE;
	  break;
      }
      break;
Wim Taymans's avatar
Wim Taymans committed
465
    case GST_QUERY_POSITION:
Wim Taymans's avatar
Wim Taymans committed
466
467
      switch (*format) {
        case GST_FORMAT_TIME:
468
469
470
471
          if (stream->strh->samplesize &&
	      stream->strh->type == GST_RIFF_FCC_auds) {
            *value = ((gfloat) stream->current_byte) * GST_SECOND /
			stream->strh->rate;
472
473
	  }
	  else {
474
475
            *value = (((gfloat) stream->current_frame) * stream->strh->scale /
			stream->strh->rate) * GST_SECOND;
476
	  }
Wim Taymans's avatar
Wim Taymans committed
477
478
479
480
	  break;
        case GST_FORMAT_BYTES:
          *value = stream->current_byte;
	  break;
Wim Taymans's avatar
Wim Taymans committed
481
        case GST_FORMAT_DEFAULT:
482
483
484
          if (stream->strh->samplesize &&
	      stream->strh->type == GST_RIFF_FCC_auds) 
            *value = stream->current_byte * stream->strh->samplesize;
485
	  else 
Wim Taymans's avatar
Wim Taymans committed
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
            *value = stream->current_frame;
	  break;
	default:
          res = FALSE;
	  break;
      }
      break;
    default:
      res = FALSE;
      break;
  }

  return res;
}

501
502
503
504
505
506
507
508
static GstCaps *
gst_avi_demux_src_getcaps (GstPad *pad)
{
  avi_stream_context *stream = gst_pad_get_element_private (pad);

  return gst_caps_copy (stream->caps);
}

Wim Taymans's avatar
Wim Taymans committed
509
static gint32
510
511
gst_avi_demux_sync_streams (GstAviDemux *avi,
			    guint64      time)
Wim Taymans's avatar
Wim Taymans committed
512
513
{
  gint i;
Wim Taymans's avatar
Wim Taymans committed
514
  guint32 min_index = G_MAXUINT;
Wim Taymans's avatar
Wim Taymans committed
515
516
517
  avi_stream_context *stream;
  gst_avi_index_entry *entry;

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

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

523
524
    entry = gst_avi_demux_index_entry_for_time (avi, stream->num, time,
						GST_RIFF_IF_KEYFRAME);
Wim Taymans's avatar
Wim Taymans committed
525
526
527
528
    if (entry) {
      min_index = MIN (entry->index_nr, min_index);
    }
  }
529
  GST_DEBUG ("first index at %d", min_index);
Wim Taymans's avatar
Wim Taymans committed
530
531
532
  
  /* now we know the entry we need to sync on. calculate number of frames to
   * skip fro there on and the stream stats */
533
  for (i = 0; i < avi->num_streams; i++) {
Wim Taymans's avatar
Wim Taymans committed
534
    gst_avi_index_entry *next_entry;
535
    stream = &avi->stream[i];
Wim Taymans's avatar
Wim Taymans committed
536
537

    /* next entry */
538
539
    next_entry = gst_avi_demux_index_next (avi, stream->num,
					   min_index, 0);
Wim Taymans's avatar
Wim Taymans committed
540
    /* next entry with keyframe */
541
542
    entry = gst_avi_demux_index_next (avi, stream->num, min_index,
				      GST_RIFF_IF_KEYFRAME);
Wim Taymans's avatar
Wim Taymans committed
543
544
545
546
547

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

548
    GST_DEBUG ("%d skip %d", stream->num, stream->skip);
Wim Taymans's avatar
Wim Taymans committed
549
  }
550

551
  GST_DEBUG ("final index at %d", min_index);
Wim Taymans's avatar
Wim Taymans committed
552
553
554
555

  return min_index;
}

556
static gboolean
557
558
gst_avi_demux_send_event (GstElement *element,
			  GstEvent   *event)
559
{
Wim Taymans's avatar
Wim Taymans committed
560
  const GList *pads;
561
562
563
564
565
566
567

  pads = gst_element_get_pad_list (element);

  while (pads) { 
    GstPad *pad = GST_PAD (pads->data);

    if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
Wim Taymans's avatar
Wim Taymans committed
568
569
570
571
572
      /* 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);
573

Wim Taymans's avatar
Wim Taymans committed
574
575
	return TRUE;
      }
576
577
578
579
580
    }
    
    pads = g_list_next (pads);
  }
  
Wim Taymans's avatar
Wim Taymans committed
581
  gst_event_unref (event);
582

583
584
585
  return FALSE;
}

586
static const GstEventMask *
587
588
589
590
591
592
593
594
595
596
gst_avi_demux_get_event_mask (GstPad *pad)
{
  static const GstEventMask masks[] = {
    { GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT },
    { 0, }
  };

  return masks;
}
	
Wim Taymans's avatar
Wim Taymans committed
597
static gboolean
598
599
gst_avi_demux_handle_src_event (GstPad   *pad,
				GstEvent *event)
Wim Taymans's avatar
Wim Taymans committed
600
601
{
  gboolean res = TRUE;
602
  GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Wim Taymans's avatar
Wim Taymans committed
603
604
605
606
607
608
  avi_stream_context *stream;
  
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
609
610
611
      GST_DEBUG ("seek format %d, %08x", GST_EVENT_SEEK_FORMAT (event),
		 stream->strh->type);

Wim Taymans's avatar
Wim Taymans committed
612
613
      switch (GST_EVENT_SEEK_FORMAT (event)) {
	case GST_FORMAT_BYTES:
Wim Taymans's avatar
Wim Taymans committed
614
	case GST_FORMAT_DEFAULT:
615
616
	case GST_FORMAT_TIME: {
	  gst_avi_index_entry *seek_entry, *entry = NULL;
Wim Taymans's avatar
Wim Taymans committed
617
618
619
620
621
	  gint64 desired_offset = GST_EVENT_SEEK_OFFSET (event);
	  guint32 flags;
          guint64 min_index;

	  /* no seek on audio yet */
622
	  if (stream->strh->type == GST_RIFF_FCC_auds) {
Wim Taymans's avatar
Wim Taymans committed
623
624
625
	    res = FALSE;
	    goto done;
	  }
626
          GST_DEBUG ("seeking to %" G_GINT64_FORMAT, desired_offset);
Wim Taymans's avatar
Wim Taymans committed
627
628

          flags = GST_RIFF_IF_KEYFRAME;
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
          switch (GST_EVENT_SEEK_FORMAT (event)) {
	    case GST_FORMAT_BYTES:
              entry = gst_avi_demux_index_entry_for_byte (avi, stream->num,
							  desired_offset,
							  flags);
              break;
	    case GST_FORMAT_DEFAULT:
              entry = gst_avi_demux_index_entry_for_frame (avi, stream->num,
							   desired_offset,
							   flags);
              break;
	    case GST_FORMAT_TIME:
              entry = gst_avi_demux_index_entry_for_time (avi, stream->num,
							  desired_offset,
							  flags);
              break;
          }
Wim Taymans's avatar
Wim Taymans committed
646
647

	  if (entry) {
648
649
650
651
652
653
654
655
656
	    min_index = gst_avi_demux_sync_streams (avi, entry->ts);
            seek_entry = &avi->index_entries[min_index];

	    avi->seek_offset = seek_entry->offset + avi->index_offset;
            avi->last_seek = entry->ts;
	  } else {
            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
657
658
659
660
661
662
663
664
665
666
667
668
669
	    res = FALSE;
	  }
	  break;
	}
	default:
	  res = FALSE;
          break;
      }
      break;
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
670
671
672
673

done:
  gst_event_unref (event);

Wim Taymans's avatar
Wim Taymans committed
674
675
676
  return res;
}

677
678
679
680
681
682
/*
 * "Open" a RIFF file.
 */

gboolean
gst_avi_demux_stream_init (GstAviDemux *avi)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
683
{
684
685
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
686

687
688
689
  if (!gst_riff_read_header (riff, &doctype))
    return FALSE;
  if (doctype != GST_RIFF_RIFF_AVI) {
690
    GST_ELEMENT_ERROR (avi, STREAM, WRONG_TYPE, (NULL), (NULL));
691
692
    return FALSE;
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
693

694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
  return TRUE;
}

/*
 * Read 'avih' header.
 */

gboolean
gst_avi_demux_stream_avih (GstAviDemux *avi,
			   guint32     *flags,
			   guint32     *streams)
{
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 tag;
  GstBuffer *buf;
709
  gst_riff_avih avih, *_avih;
710
711
712
713
714
715
716
717
718
719
720

  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)",
721
	       GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_avih));
722
723
724
725
    gst_buffer_unref (buf);
    return FALSE;
  }

726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
  _avih = (gst_riff_avih *) GST_BUFFER_DATA (buf);
  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);
  avih.init_frames = GUINT32_FROM_LE (_avih->init_frames);
  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);
741
742
743

  /* debug stuff */
  GST_INFO ("avih tag found:");
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
  GST_INFO (" us_frame    %u",     avih.us_frame);
  GST_INFO (" max_bps     %u",     avih.max_bps);
  GST_INFO (" pad_gran    %u",     avih.pad_gran);
  GST_INFO (" flags       0x%08x", avih.flags);
  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);

  avi->num_frames = avih.tot_frames;
  avi->us_per_frame = avih.us_frame;
  *streams = avih.streams;
  *flags = avih.flags;
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804

  gst_buffer_unref (buf);

  return TRUE;
}

/*
 * Add a stream.
 */

static gboolean
gst_avi_demux_add_stream (GstAviDemux *avi)
{
  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;
  union {
    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) {
805
    GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
806
		       ("Invalid AVI header (no strf as second tag)"));
807
808
809
810
811
812
813
814
815
816
817
818
819
820
    goto skip_stream;
  }
  switch (strh->type) {
    case GST_RIFF_FCC_vids:
      if (!gst_riff_read_strf_vids (riff, &strf.vids))
        return FALSE;
      break;
    case GST_RIFF_FCC_auds:
      if (!gst_riff_read_strf_auds (riff, &strf.auds))
        return FALSE;
      break;
    case GST_RIFF_FCC_iavs:
      if (!gst_riff_read_strf_iavs (riff, &strf.iavs))
        return FALSE;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
821
      break;
822
823
824
825
826
    default:
      g_warning ("Unknown stream type " GST_FOURCC_FORMAT,
		 GST_FOURCC_ARGS (strh->type));
      goto skip_stream;
  }
Wim Taymans's avatar
Wim Taymans committed
827

828
829
830
831
832
833
834
835
  /* 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
836

837
838
839
840
841
842
843
    switch (tag) {
      case GST_RIFF_TAG_strn:
        if (name)
          g_free (name);
        if (!gst_riff_read_ascii (riff, &tag, &name))
          return FALSE;
        break;
Wim Taymans's avatar
Wim Taymans committed
844

845
846
847
848
849
850
851
852
853
854
855
      default:
        GST_WARNING ("Unknown tag " GST_FOURCC_FORMAT " in AVI header",
		     GST_FOURCC_ARGS (tag));
        /* fall-through */

      case GST_RIFF_TAG_strd: /* what is this? */
      case GST_RIFF_TAG_JUNK:
        if (!gst_riff_read_skip (riff))
          return FALSE;
        break;
    }
Wim Taymans's avatar
Wim Taymans committed
856

857
858
    if (avi->level_up) {
      avi->level_up--;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
859
      break;
Wim Taymans's avatar
Wim Taymans committed
860
    }
861
862
863
864
865
  }

  /* create stream name + pad */
  switch (strh->type) {
    case GST_RIFF_FCC_vids:
866
867
868
    {
      char *codec_name = NULL;
      GstTagList *list = gst_tag_list_new ();
869
870
      padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
      templ = gst_element_class_get_pad_template (klass, "video_%02d");
871
872
873
874
875
876
877
      caps = gst_riff_create_video_caps (strf.vids->compression, strh,
                                         strf.vids, &codec_name);
      gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_VIDEO_CODEC,
                        codec_name, NULL);
      gst_element_found_tags (GST_ELEMENT (avi), list);
      gst_tag_list_free (list);
      if (codec_name) g_free (codec_name);
878
879
880
      g_free (strf.vids);
      avi->num_v_streams++;
      break;
881
    }
882
    case GST_RIFF_FCC_auds:
883
884
885
    {
      char *codec_name = NULL;
      GstTagList *list = gst_tag_list_new ();
886
887
      padname = g_strdup_printf ("audio_%02d", avi->num_a_streams);
      templ = gst_element_class_get_pad_template (klass, "audio_%02d");
888
889
890
891
892
893
894
      caps = gst_riff_create_audio_caps (strf.auds->format, strh, strf.auds,
                                         &codec_name);
      gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_AUDIO_CODEC,
                        codec_name, NULL);
      gst_element_found_tags (GST_ELEMENT (avi), list);
      gst_tag_list_free (list);
      if (codec_name) g_free (codec_name);
895
896
897
      g_free (strf.auds);
      avi->num_a_streams++;
      break;
898
    }
899
    case GST_RIFF_FCC_iavs:
900
901
902
    {
      char *codec_name = NULL;
      GstTagList *list = gst_tag_list_new ();
903
904
      padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
      templ = gst_element_class_get_pad_template (klass, "video_%02d");
905
906
907
908
909
910
911
      caps = gst_riff_create_iavs_caps (strh->fcc_handler, strh, strf.iavs,
                                        &codec_name);
      gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_VIDEO_CODEC,
                        codec_name, NULL);
      gst_element_found_tags (GST_ELEMENT (avi), list);
      gst_tag_list_free (list);
      if (codec_name) g_free (codec_name);
912
913
914
      g_free (strf.iavs);
      avi->num_v_streams++;
      break;
915
    }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
916
    default:
917
918
919
920
921
922
923
924
925
926
927
928
929
      g_assert (0);
  }

  /* set proper settings and add it */
  pad =  gst_pad_new_from_template (templ, padname);
  g_free (padname);

  gst_pad_set_formats_function (pad, gst_avi_demux_get_src_formats);
  gst_pad_set_event_mask_function (pad, gst_avi_demux_get_event_mask);
  gst_pad_set_event_function (pad, gst_avi_demux_handle_src_event);
  gst_pad_set_query_type_function (pad, gst_avi_demux_get_src_query_types);
  gst_pad_set_query_function (pad, gst_avi_demux_handle_src_query);
  gst_pad_set_convert_function (pad, gst_avi_demux_src_convert);
930
  gst_pad_set_getcaps_function (pad, gst_avi_demux_src_getcaps);
931
932

  stream = &avi->stream[avi->num_streams];
933
  stream->caps = caps ? caps : gst_caps_new_empty ();
934
935
936
937
938
939
940
941
942
943
944
945
946
  stream->pad = pad;
  stream->strh = strh;
  stream->num = avi->num_streams;
  stream->delay = 0LL;
  stream->total_bytes = 0LL;
  stream->total_frames = 0;
  stream->current_frame = 0;
  stream->current_byte = 0;
  stream->current_entry = -1;
  stream->skip = 0;
  gst_pad_set_element_private (pad, stream);
  avi->num_streams++;

947
  /* auto-negotiates */
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
  gst_element_add_pad (GST_ELEMENT (avi), pad);

  return TRUE;

skip_stream:
  while (TRUE) {
    if (!(tag = gst_riff_peek_tag (riff, &avi->level_up)))
      return FALSE;
    if (avi->level_up) {
      avi->level_up--;
      break;
    }
    if (!gst_riff_read_skip (riff))
      return FALSE;
  }

  /* add a "NULL" stream */
  avi->num_streams++;

  return TRUE; /* recoverable */
}

/*
 * Read an openDML-2.0 extension header.
 */

static gboolean
gst_avi_demux_stream_odml (GstAviDemux *avi)
{
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 tag;

  /* read contents */
  while (TRUE) {
    if (!(tag = gst_riff_peek_tag (riff, &avi->level_up)))
      return FALSE;
    else if (avi->level_up) {
      avi->level_up--;
      break;
    }

    switch (tag) {
      case GST_RIFF_TAG_dmlh: {
991
        gst_riff_dmlh dmlh, *_dmlh;
992
993
994
995
996
997
        GstBuffer *buf;

        if (!gst_riff_read_data (riff, &tag, &buf))
          return FALSE;
        if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_dmlh)) {
          g_warning ("DMLH entry is too small (%d bytes, %d needed)",
998
		     GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_dmlh));
999
1000
1001
          gst_buffer_unref (buf);
          break;
        }
1002
1003
        _dmlh = (gst_riff_dmlh *) GST_BUFFER_DATA (buf);
        dmlh.totalframes = GUINT32_FROM_LE (_dmlh->totalframes);
1004
1005

        GST_INFO ("dmlh tag found:");
1006
        GST_INFO (" totalframes: %u", dmlh.totalframes);
1007

1008
        avi->num_frames = dmlh.totalframes;
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
        gst_buffer_unref (buf);
        break;
      }

      default:
        GST_WARNING ("Unknown tag " GST_FOURCC_FORMAT " in AVI header",
		     GST_FOURCC_ARGS (tag));
        /* fall-through */

      case GST_RIFF_TAG_JUNK:
        if (!gst_riff_read_skip (riff))
          return FALSE;
        break;
    }

    if (avi->level_up) {
      avi->level_up--;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
1026
      break;
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
    }
  }

  return TRUE;
}

/*
 * Seek to index, read it, seek back.
 */

gboolean
gst_avi_demux_stream_index (GstAviDemux *avi)
{
  GstBuffer *buf = NULL;
  guint i;
  GstEvent *event;
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint64 pos_before, pos_after, length;
  guint32 tag;

  /* first, we need to know the current position (to seek back
   * when we're done) and the total length of the file. */
  length = gst_bytestream_length (riff->bs);
  pos_before = gst_bytestream_tell (riff->bs);

  /* skip movi */
  if (!gst_riff_read_skip (riff))
    return FALSE;

  /* assure that we've got data left */
  pos_after = gst_bytestream_tell (riff->bs);
  if (pos_after + 8 > length) {
    g_warning ("File said that it has an index, but there is no index data!");
    goto end;
  }

  /* assure that it's an index */
  if (!(tag = gst_riff_peek_tag (riff, NULL)))
    return FALSE;
  if (tag != GST_RIFF_TAG_idx1) {
    g_warning ("No index after data, but " GST_FOURCC_FORMAT,
	       GST_FOURCC_ARGS (tag));
    goto end;
  }

  /* read index */
  if (!gst_riff_read_data (riff, &tag, &buf))
    return FALSE;

  /* parse all entries */
  avi->index_size = GST_BUFFER_SIZE (buf) / sizeof (gst_riff_index_entry);
  avi->index_entries = g_malloc (avi->index_size * sizeof (gst_avi_index_entry));
  GST_INFO ("%u index entries", avi->index_size);

  for (i = 0; i < avi->index_size; i++) {
1082
    gst_riff_index_entry entry, *_entry;
1083
1084
1085
1086
1087
    avi_stream_context *stream;
    gint stream_nr;
    gst_avi_index_entry *target;
    GstFormat format;

1088
1089
1090
1091
1092
    _entry = &((gst_riff_index_entry *) GST_BUFFER_DATA (buf))[i];
    entry.id     = GUINT32_FROM_LE (_entry->id);
    entry.offset = GUINT32_FROM_LE (_entry->offset);
    entry.flags  = GUINT32_FROM_LE (_entry->flags);
    entry.size   = GUINT32_FROM_LE (_entry->size);
1093
1094
    target = &avi->index_entries[i];

1095
    stream_nr = CHUNKID_TO_STREAMNR (entry.id);
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
    if (stream_nr >= avi->num_streams || stream_nr < 0) {
      g_warning ("Index entry %d has invalid stream nr %d",
		 i, stream_nr);
      target->stream_nr = -1;
      continue;
    }
    target->stream_nr = stream_nr;
    stream = &avi->stream[stream_nr];

    target->index_nr = i;
1106
1107
1108
    target->flags    = entry.flags;
    target->size     = entry.size;
    target->offset   = entry.offset;
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147

    /* figure out if the index is 0 based or relative to the MOVI start */
    if (i == 0) {
      if (target->offset < pos_before)
	avi->index_offset = pos_before + 8;
      else
	avi->index_offset = 0;
    }

    target->bytes_before = stream->total_bytes;
    target->frames_before = stream->total_frames;

    format = GST_FORMAT_TIME;
    if (stream->strh->type == GST_RIFF_FCC_auds) {
      /* all audio frames are keyframes */
      target->flags |= GST_RIFF_IF_KEYFRAME;
    }
      
    if (stream->strh->samplesize && stream->strh->type == GST_RIFF_FCC_auds) {
      /* constant rate stream */
      gst_pad_convert (stream->pad, GST_FORMAT_BYTES,
		       stream->total_bytes, &format, &target->ts);
    } else {
      /* VBR stream */
      gst_pad_convert (stream->pad, GST_FORMAT_DEFAULT,
		       stream->total_frames, &format, &target->ts);
    }

    stream->total_bytes += target->size;
    stream->total_frames++;
  }

  /* debug our indexes */
  for (i = 0; i < avi->num_streams; i++) {
    avi_stream_context *stream;

    stream = &avi->stream[i];
    GST_DEBUG ("stream %u: %u frames, %" G_GINT64_FORMAT " bytes", 
	       i, stream->total_frames, stream->total_bytes);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
1148
1149
  }

1150
1151
1152
1153
1154
1155
1156
end:
  if (buf)
    gst_buffer_unref (buf);

  /* seek back to the data */
  if (!(event = gst_riff_read_seek (riff, pos_before)))
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
1157
1158
  gst_event_unref (event);

1159
1160
  return TRUE;
}
Wim Taymans's avatar
Wim Taymans committed
1161

1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
/*
 * Scan the file for all chunks to "create" a new index.
 */

gboolean
gst_avi_demux_stream_scan (GstAviDemux *avi)
{
  //GstRiffRead *riff = GST_RIFF_READ (avi);

  /* FIXME */

  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
1174
1175
}

1176
1177
1178
/*
 * Read full AVI headers.
 */
1179

1180
1181
gboolean
gst_avi_demux_stream_header (GstAviDemux *avi)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
1182
{
1183
1184
1185
1186
1187