gstavidemux.c 79.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
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
 * SECTION:element-avidemux
 *
 * <refsect2>
 * <para>
 * Demuxes an .avi file into raw or compressed audio and/or video streams.
 * </para>
 * <para>
 * This element currently only supports pull-based scheduling. 
 * </para>
 * <title>Example launch line</title>
 * <para>
 * <programlisting>
 * gst-launch filesrc test.avi ! avidemux name=demux  demux.audio_00 ! decodebin ! audioconvert ! audioresample ! autoaudiosink   demux.video_00 ! queue ! decodebin ! ffmpegcolorspace ! videoscale ! autovideosink
 * </programlisting>
 * Play (parse and decode) an .avi file and try to output it to
 * an automatically detected soundcard and videosink. If the AVI file contains
 * compressed audio or video data, this will only work if you have the
 * right decoder elements/plugins installed.
 * </para>
 * </refsect2>
 *
 */

45
46
47
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
48

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
49
50
#include <string.h>

51
#include "gst/riff/riff-media.h"
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
52
#include "gstavidemux.h"
53
#include "avi-ids.h"
54
#include <gst/gst-i18n-plugin.h>
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
55

56
57
58
GST_DEBUG_CATEGORY_STATIC (avidemux_debug);
#define GST_CAT_DEFAULT avidemux_debug

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
59
60
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_EVENT);

61
static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
62
63
64
65
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-msvideo")
    );
66

67
68
69
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);
70

71
static void gst_avi_demux_reset (GstAviDemux * avi);
72

73
#if 0
74
static const GstEventMask *gst_avi_demux_get_event_mask (GstPad * pad);
75
#endif
76
static gboolean gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event);
77
78

#if 0
79
static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad);
80
#endif
81
82
83
84
85
static const GstQueryType *gst_avi_demux_get_src_query_types (GstPad * pad);
static gboolean gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query);
static gboolean gst_avi_demux_src_convert (GstPad * pad, GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value);

86
87
88
static gboolean gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment);
static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad,
    GstEvent * event);
89
90
91
static void gst_avi_demux_loop (GstPad * pad);
static gboolean gst_avi_demux_sink_activate (GstPad * sinkpad);
static gboolean gst_avi_demux_sink_activate_pull (GstPad * sinkpad,
92
    gboolean active);
93
94
static GstStateChangeReturn gst_avi_demux_change_state (GstElement * element,
    GstStateChange transition);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
95

96
static GstElementClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
97

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
98
GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
99
gst_avi_demux_get_type (void)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
100
101
102
103
104
{
  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
105
      sizeof (GstAviDemuxClass),
106
      (GBaseInitFunc) gst_avi_demux_base_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
107
      NULL,
108
      (GClassInitFunc) gst_avi_demux_class_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
109
110
      NULL,
      NULL,
111
      sizeof (GstAviDemux),
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
112
      0,
113
      (GInstanceInitFunc) gst_avi_demux_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
114
    };
115
116

    avi_demux_type =
117
        g_type_register_static (GST_TYPE_ELEMENT,
118
        "GstAviDemux", &avi_demux_info, 0);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
119
  }
120

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
121
122
123
  return avi_demux_type;
}

124
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
125
gst_avi_demux_base_init (GstAviDemuxClass * klass)
126
{
127
  static const GstElementDetails gst_avi_demux_details =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
128
129
130
131
132
133
      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>");
134
135
136
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstPadTemplate *videosrctempl, *audiosrctempl;
  GstCaps *audcaps, *vidcaps;
137

138
  audcaps = gst_riff_create_audio_template_caps ();
139
  gst_caps_append (audcaps, gst_caps_new_simple ("audio/x-avi-unknown", NULL));
140
  audiosrctempl = gst_pad_template_new ("audio_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
141
      GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps);
142

David Schleef's avatar
David Schleef committed
143
144
  vidcaps = gst_riff_create_video_template_caps ();
  gst_caps_append (vidcaps, gst_riff_create_iavs_template_caps ());
145
  gst_caps_append (vidcaps, gst_caps_new_simple ("video/x-avi-unknown", NULL));
146
  videosrctempl = gst_pad_template_new ("video_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
147
      GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps);
148

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

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
156
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
157
gst_avi_demux_class_init (GstAviDemuxClass * klass)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
158
{
159
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
160

161
  GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
162
      0, "Demuxer for AVI streams");
163

164
  parent_class = g_type_class_peek_parent (klass);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
165

Wim Taymans's avatar
Wim Taymans committed
166
  gstelement_class->change_state = gst_avi_demux_change_state;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
167
168
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
169
170
static void
gst_avi_demux_init (GstAviDemux * avi)
171
{
172
  avi->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink");
173
174
175
  gst_pad_set_activate_function (avi->sinkpad, gst_avi_demux_sink_activate);
  gst_pad_set_activatepull_function (avi->sinkpad,
      gst_avi_demux_sink_activate_pull);
176
  gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad);
Wim Taymans's avatar
Wim Taymans committed
177

178
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
179

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

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

189
190
  for (i = 0; i < avi->num_streams; i++) {
    g_free (avi->stream[i].strh);
191
    g_free (avi->stream[i].strf.data);
192
193
    if (avi->stream[i].name)
      g_free (avi->stream[i].name);
194
195
196
197
    if (avi->stream[i].initdata)
      gst_buffer_unref (avi->stream[i].initdata);
    if (avi->stream[i].extradata)
      gst_buffer_unref (avi->stream[i].extradata);
198
199
    if (avi->stream[i].pad)
      gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad);
200
201
202
203
    if (avi->stream[i].taglist) {
      gst_tag_list_free (avi->stream[i].taglist);
      avi->stream[i].taglist = NULL;
    }
204
205
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
206

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

211
  avi->state = GST_AVI_DEMUX_START;
212
  avi->offset = 0;
213

214
215
  g_free (avi->index_entries);
  avi->index_entries = NULL;
216
  avi->index_size = 0;
217
  avi->index_offset = 0;
218
  avi->current_entry = 0;
219
220
  g_free (avi->avih);
  avi->avih = NULL;
221

222
223
224
  if (avi->seek_event)
    gst_event_unref (avi->seek_event);
  avi->seek_event = NULL;
225

226
227
228
229
  if (avi->globaltags)
    gst_tag_list_free (avi->globaltags);
  avi->globaltags = NULL;

230
  avi->got_tags = FALSE;
231
232
}

233
static gst_avi_index_entry *
234
gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
235
{
236
  gint i;
237
  gst_avi_index_entry *result = NULL;
238

239
  for (i = start; i < avi->index_size; i++) {
240
241
242
243
    gst_avi_index_entry *entry = &avi->index_entries[i];

    if (entry->stream_nr == stream_nr) {
      result = entry;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
244
      break;
245
    }
246
  }
247

248
  return result;
249
}
250

251
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
252
253
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
    gint stream_nr, guint64 time, guint32 flags)
254
255
256
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
257

258
259
  GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT " flags:%d",
      stream_nr, GST_TIME_ARGS (time), flags);
260

261
262
  i = -1;
  do {
263
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
264
265
    if (!entry)
      return NULL;
266

267
    i = entry->index_nr;
268

269
270
271
272
    GST_LOG_OBJECT (avi,
        "looking at entry %d / ts:%" GST_TIME_FORMAT " / dur:%" GST_TIME_FORMAT
        " flags:%d", i, GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (entry->dur),
        entry->flags);
273

274
    if (entry->ts <= time && (entry->flags & flags) == flags)
275
      last_entry = entry;
276
  } while (entry->ts < time);
277

278
279
  return last_entry;
}
280

281
#if 0
282
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
283
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
284
{
285
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
286

287
288
289
290
291
292
293
294
295
296
297
  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
298

299
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
300
      src_a_formats : src_v_formats);
301
}
302
#endif
303

304
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
305
306
307
gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
308
309
{
  gboolean res = TRUE;
310
  GstAviDemux *avidemux = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
311

312
  avi_stream_context *stream = gst_pad_get_element_private (pad);
313

314
315
316
317
  GST_LOG_OBJECT (avidemux,
      "Received  src_format:%d, src_value:%lld, dest_format:%d", src_format,
      src_value, *dest_format);

318
319
  if (src_format == *dest_format) {
    *dest_value = src_value;
320
321
322
323
324
    goto done;
  }
  if (!stream->strh || !stream->strf.data) {
    res = FALSE;
    goto done;
325
  }
326
  if (stream->strh->type == GST_RIFF_FCC_vids &&
327
328
329
330
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) {
    res = FALSE;
    goto done;
  }
331

332
333
334
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
335
        case GST_FORMAT_BYTES:
336
337
338
          *dest_value =
              gst_util_uint64_scale_int (src_value, stream->strf.auds->av_bps,
              GST_SECOND);
339
340
          break;
        case GST_FORMAT_DEFAULT:
341
342
          *dest_value = gst_util_uint64_scale (src_value, stream->strh->rate,
              stream->strh->scale * GST_SECOND);
343
344
345
346
          break;
        default:
          res = FALSE;
          break;
347
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
348
      break;
349
350
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
351
        case GST_FORMAT_TIME:
352
          if (stream->strf.auds->av_bps != 0) {
353
354
            *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
                stream->strf.auds->av_bps);
355
356
          } else
            res = FALSE;
357
358
359
360
          break;
        default:
          res = FALSE;
          break;
361
362
      }
      break;
363
364
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
365
        case GST_FORMAT_TIME:
366
367
368
          *dest_value =
              gst_util_uint64_scale (src_value,
              stream->strh->scale * GST_SECOND, stream->strh->rate);
369
370
371
372
          break;
        default:
          res = FALSE;
          break;
373
      }
Wim Taymans's avatar
Wim Taymans committed
374
375
      break;
    default:
376
      res = FALSE;
377
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
378

379
380
381
382
done:
  GST_LOG_OBJECT (avidemux, "Returning res:%d dest_format:%d dest_value:%lld",
      res, *dest_format, *dest_value);
  gst_object_unref (avidemux);
383
  return res;
384
385
}

386
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
387
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
388
{
389
390
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
391
    GST_QUERY_DURATION,
392
393
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
394

395
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
396
397
}

398
static gboolean
399
gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
400
{
401
  gboolean res = TRUE;
402
  GstAviDemux *demux = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
403

404
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
405

406
407
408
409
410
  if (!stream->strh || !stream->strf.data)
    return FALSE;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:{
Wim Taymans's avatar
Wim Taymans committed
411
      gint64 pos = 0;
412
413

      if (stream->strh->type == GST_RIFF_FCC_auds) {
414
415
        if (!stream->is_vbr) {
          /* CBR */
416
417
          pos = gst_util_uint64_scale_int ((gint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
418
        } else if (stream->strf.auds->av_bps != 0) {
419
          /* VBR */
420
421
          pos = gst_util_uint64_scale_int (stream->current_byte, GST_SECOND,
              stream->strf.auds->av_bps);
422
        } else if (stream->total_frames != 0 && stream->total_bytes != 0) {
423
          /* calculate timestamps based on percentage of length */
424
425
426
          guint64 xlen = demux->avih->us_frame *
              demux->avih->tot_frames * GST_USECOND;

427
          if (stream->is_vbr)
428
429
            pos = gst_util_uint64_scale_int (xlen, stream->current_byte,
                stream->total_bytes);
430
431
432
          else
            pos = gst_util_uint64_scale_int (xlen, stream->current_frame,
                stream->total_frames);
433
        } else {
434
          /* we don't know */
435
          res = FALSE;
436
437
438
        }
      } else {
        if (stream->strh->rate != 0) {
439
440
441
          pos =
              gst_util_uint64_scale_int ((guint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
442
443
444
        } else {
          pos = stream->current_frame * demux->avih->us_frame * GST_USECOND;
        }
Wim Taymans's avatar
Wim Taymans committed
445
      }
446
      if (res)
Wim Taymans's avatar
Wim Taymans committed
447
448
449
450
451
        gst_query_set_position (query, GST_FORMAT_TIME, pos);
      break;
    }
    case GST_QUERY_DURATION:
    {
452
453
454
455
456
      if (stream->strh->type != GST_RIFF_FCC_auds &&
          stream->strh->type != GST_RIFF_FCC_vids) {
        res = FALSE;
        break;
      }
457
      gst_query_set_duration (query, GST_FORMAT_TIME, stream->idx_duration);
Wim Taymans's avatar
Wim Taymans committed
458
      break;
459
    }
Wim Taymans's avatar
Wim Taymans committed
460
    default:
461
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
462
463
464
465
466
467
      break;
  }

  return res;
}

468
#if 0
469
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
470
gst_avi_demux_get_event_mask (GstPad * pad)
471
472
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
473
474
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
475
476
477
478
  };

  return masks;
}
479
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
480

Wim Taymans's avatar
Wim Taymans committed
481
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
482
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
483
484
{
  gboolean res = TRUE;
485
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
486

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
487
  GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi,
488
      "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
Wim Taymans's avatar
Wim Taymans committed
489
490
491

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
492
493
494
495
496
497
498
      /* handle seeking */
      res = gst_avi_demux_handle_seek (avi, pad, event);
      break;
    case GST_EVENT_QOS:
      /* FIXME, we can do something clever here like skip to the next keyframe
       * based on the QoS values. */
      res = FALSE;
Wim Taymans's avatar
Wim Taymans committed
499
500
      break;
    default:
501
      /* most other events are not very usefull */
Wim Taymans's avatar
Wim Taymans committed
502
503
504
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
505
506
507

  gst_event_unref (event);

508
  GST_DEBUG_OBJECT (avi, "returning %d", res);
Wim Taymans's avatar
Wim Taymans committed
509
510
511
  return res;
}

512
513
514
515
516
517
/**
 * gst_avi_demux_parse_file_header:
 * @element: caller element (used for errors/debug).
 * @buf: input data to be used for parsing.
 *
 * "Open" a RIFF/AVI file. The buffer should be at least 12
518
 * bytes long. Takes ownership of @buf.
519
520
521
 *
 * Returns: TRUE if the file is a RIFF/AVI file, FALSE otherwise.
 *          Throws an error, caller should error out (fatal).
522
523
 */

524
static gboolean
525
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
526
{
527
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
528

529
  if (!gst_riff_parse_file_header (element, buf, &doctype))
530
    return FALSE;
531

532
533
534
535
536
537
538
539
  if (doctype != GST_RIFF_RIFF_AVI)
    goto not_avi;

  return TRUE;

  /* ERRORS */
not_avi:
  {
540
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
541
        ("File is not an AVI file: %" GST_FOURCC_FORMAT,
542
            GST_FOURCC_ARGS (doctype)));
543
544
545
546
    return FALSE;
  }
}

547
548
549
550
551
552
static GstFlowReturn
gst_avi_demux_stream_init (GstAviDemux * avi)
{
  GstFlowReturn res;
  GstBuffer *buf = NULL;

553
554
  res = gst_pad_pull_range (avi->sinkpad, avi->offset, 12, &buf);
  if (res != GST_FLOW_OK)
555
    return res;
556
  else if (!gst_avi_demux_parse_file_header (GST_ELEMENT_CAST (avi), buf))
557
    goto wrong_header;
558
559
560
561

  avi->offset += 12;

  return GST_FLOW_OK;
562
563
564
565
566
567
568

  /* ERRORS */
wrong_header:
  {
    GST_DEBUG_OBJECT (avi, "error parsing file header");
    return GST_FLOW_ERROR;
  }
569
570
571
572
573
574
575
576
577
578
579
580
581
582
}

/**
 * gst_avi_demux_parse_avih:
 * @element: caller element (used for errors/debug).
 * @buf: input data to be used for parsing.
 * @avih: pointer to structure (filled in by function) containing
 *        stream information (such as flags, number of streams, etc.).
 *
 * Read 'avih' header. Discards buffer after use.
 *
 * Returns: TRUE on success, FALSE otherwise. Throws an error if
 *          the header is invalid. The caller should error out
 *          (fatal).
583
584
 */

585
static gboolean
586
587
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
588
{
589
  gst_riff_avih *avih;
590

591
592
593
594
595
  if (buf == NULL)
    goto no_buffer;

  if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_avih))
    goto avih_too_small;
596

597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
  avih = g_memdup (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));

#if (G_BYTE_ORDER == G_BIG_ENDIAN)
  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);
#endif
615
616

  /* debug stuff */
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
  GST_INFO_OBJECT (element, "avih tag found:");
  GST_INFO_OBJECT (element, " us_frame    %u", avih->us_frame);
  GST_INFO_OBJECT (element, " max_bps     %u", avih->max_bps);
  GST_INFO_OBJECT (element, " pad_gran    %u", avih->pad_gran);
  GST_INFO_OBJECT (element, " flags       0x%08x", avih->flags);
  GST_INFO_OBJECT (element, " tot_frames  %u", avih->tot_frames);
  GST_INFO_OBJECT (element, " init_frames %u", avih->init_frames);
  GST_INFO_OBJECT (element, " streams     %u", avih->streams);
  GST_INFO_OBJECT (element, " bufsize     %u", avih->bufsize);
  GST_INFO_OBJECT (element, " width       %u", avih->width);
  GST_INFO_OBJECT (element, " height      %u", avih->height);
  GST_INFO_OBJECT (element, " scale       %u", avih->scale);
  GST_INFO_OBJECT (element, " rate        %u", avih->rate);
  GST_INFO_OBJECT (element, " start       %u", avih->start);
  GST_INFO_OBJECT (element, " length      %u", avih->length);

  *_avih = avih;
634
635
636
  gst_buffer_unref (buf);

  return TRUE;
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651

  /* ERRORS */
no_buffer:
  {
    GST_ELEMENT_ERROR (element, STREAM, DEMUX, (NULL), ("No buffer"));
    return FALSE;
  }
avih_too_small:
  {
    GST_ELEMENT_ERROR (element, STREAM, DEMUX, (NULL),
        ("Too small avih (%d available, %d needed)",
            GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_avih)));
    gst_buffer_unref (buf);
    return FALSE;
  }
652
653
}

654
655
656
657
658
659
660
661
662
663
664
665
/**
 * gst_avi_demux_parse_superindex:
 * @element: caller element (used for debugging/errors).
 * @buf: input data to use for parsing.
 * @locations: locations in the file (byte-offsets) that contain
 *             the actual indexes (see get_avi_demux_parse_subindex()).
 *             The array ends with GST_BUFFER_OFFSET_NONE.
 *
 * Reads superindex (openDML-2 spec stuff) from the provided data.
 *
 * Returns: TRUE on success, FALSE otherwise. Indexes should be skipped
 *          on error, but they are not fatal.
666
667
668
 */

static gboolean
669
670
gst_avi_demux_parse_superindex (GstElement * element,
    GstBuffer * buf, guint64 ** _indexes)
671
{
672
  guint8 *data;
673
674
675
  gint bpe = 16, num, i;
  guint64 *indexes;

676
677
  *_indexes = NULL;

678
679
680
  if (buf == NULL)
    goto no_buffer;

681
  if (GST_BUFFER_SIZE (buf) < 24)
682
    goto too_small;
683

684
685
  data = GST_BUFFER_DATA (buf);

686
687
688
689
690
  /* check type of index. The opendml2 specs state that
   * there should be 4 dwords per array entry. Type can be
   * either frame or field (and we don't care). */
  if (GST_READ_UINT16_LE (data) != 4 ||
      (data[2] & 0xfe) != 0x0 || data[3] != 0x0) {
691
692
    GST_WARNING_OBJECT (element,
        "Superindex for stream %d has unexpected "
693
694
695
696
697
698
699
700
701
702
703
704
        "size_entry %d (bytes) or flags 0x%02x/0x%02x",
        GST_READ_UINT16_LE (data), data[2], data[3]);
    bpe = GST_READ_UINT16_LE (data) * 4;
  }
  num = GST_READ_UINT32_LE (&data[4]);

  indexes = g_new (guint64, num + 1);
  for (i = 0; i < num; i++) {
    if (GST_BUFFER_SIZE (buf) < 24 + bpe * (i + 1))
      break;
    indexes[i] = GST_READ_UINT64_LE (&data[24 + bpe * i]);
  }
705
706
  indexes[i] = GST_BUFFER_OFFSET_NONE;
  *_indexes = indexes;
707
708
709
710

  gst_buffer_unref (buf);

  return TRUE;
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725

  /* ERRORS */
no_buffer:
  {
    GST_ERROR_OBJECT (element, "No buffer");
    return FALSE;
  }
too_small:
  {
    GST_ERROR_OBJECT (element,
        "Not enough data to parse superindex (%d available, %d needed)",
        GST_BUFFER_SIZE (buf), 24);
    gst_buffer_unref (buf);
    return FALSE;
  }
726
727
}

728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
/**
 * gst_avi_demux_parse_subindex:
 * @element: caller element (used for errors/debug).
 * @buf: input data to use for parsing.
 * @stream: stream context.
 * @entries_list: a list (returned by the function) containing all the
 *           indexes parsed in this specific subindex. The first
 *           entry is also a pointer to allocated memory that needs
 *           to be free´ed. May be NULL if no supported indexes were
 *           found.
 *
 * Reads superindex (openDML-2 spec stuff) from the provided data.
 * The buffer will be discarded after use.
 *
 * Returns: TRUE on success, FALSE otherwise. Errors are fatal, we
 *          throw an error, caller should bail out asap.
 */

746
static gboolean
747
748
gst_avi_demux_parse_subindex (GstElement * element,
    GstBuffer * buf, avi_stream_context * stream, GList ** _entries_list)
749
{
750
751
752
  guint8 *data = GST_BUFFER_DATA (buf);
  gint bpe, num, x;
  guint64 baseoff;
753
  gst_avi_index_entry *entries, *entry;
754
  GList *entries_list = NULL;
755
756
  GstFormat format = GST_FORMAT_TIME;

757
  /* check size */
758
759
760
761
762
  if (buf == NULL)
    goto no_buffer;

  if (GST_BUFFER_SIZE (buf) < 24)
    goto too_small;
763

764
  /* We don't support index-data yet */
765
766
  if (data[3] & 0x80)
    goto not_implemented;
767

768
769
770
771
772
773
774
775
776
777
778
779
780
781
  /* check type of index. The opendml2 specs state that
   * there should be 4 dwords per array entry. Type can be
   * either frame or field (and we don't care). */
  bpe = (data[2] & 0x01) ? 12 : 8;
  if (GST_READ_UINT16_LE (data) != bpe / 4 ||
      (data[2] & 0xfe) != 0x0 || data[3] != 0x1) {
    GST_WARNING_OBJECT (element,
        "Superindex for stream %d has unexpected "
        "size_entry %d (bytes) or flags 0x%02x/0x%02x",
        GST_READ_UINT16_LE (data), data[2], data[3]);
    bpe = GST_READ_UINT16_LE (data) * 4;
  }
  num = GST_READ_UINT32_LE (&data[4]);
  baseoff = GST_READ_UINT64_LE (&data[12]);
782

783
784
  entries = g_new (gst_avi_index_entry, num);
  for (x = 0; x < num; x++) {
785
786
    gint64 next_ts;

787
    entry = &entries[x];
788

789
790
    if (GST_BUFFER_SIZE (buf) < 24 + bpe * (x + 1))
      break;
791

792
793
794
795
796
797
798
    /* fill in */
    entry->offset = baseoff + GST_READ_UINT32_LE (&data[24 + bpe * x]);
    entry->size = GST_READ_UINT32_LE (&data[24 + bpe * x + 4]);
    entry->flags = (entry->size & 0x80000000) ? 0 : GST_RIFF_IF_KEYFRAME;
    entry->size &= ~0x80000000;
    entry->index_nr = x;
    entry->stream_nr = stream->num;
799

800
    /* timestamps */
801
802
803
    entry->ts = stream->total_time;
    if (stream->is_vbr) {
      /* VBR get next timestamp */
804
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
805
806
807
808
809
          stream->total_frames + 1, &format, &next_ts);
    } else {
      /* CBR get next timestamp */
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES,
          stream->total_bytes + entry->size, &format, &next_ts);
810
    }
811
812
    /* duration is next - current */
    entry->dur = next_ts - entry->ts;
813
814
815
816

    /* stream position */
    entry->bytes_before = stream->total_bytes;
    entry->frames_before = stream->total_frames;
817
818

    stream->total_bytes += entry->size;
819
    stream->total_frames++;
820
    stream->total_time = next_ts;
821

822
823
    entries_list = g_list_prepend (entries_list, entry);
  }
824

825
  GST_LOG_OBJECT (element, "Read %d index entries", x);
826

827
828
829
830
831
832
833
834
  gst_buffer_unref (buf);

  if (x > 0) {
    *_entries_list = g_list_reverse (entries_list);
  } else {
    *_entries_list = NULL;
    g_free (entries);
  }
835

836
  return TRUE;
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859

  /* ERRORS */
no_buffer:
  {
    GST_ERROR_OBJECT (element, "No buffer");
    return TRUE;                /* continue */
  }
too_small:
  {
    GST_ERROR_OBJECT (element,
        "Not enough data to parse subindex (%d available, %d needed)",
        GST_BUFFER_SIZE (buf), 24);
    gst_buffer_unref (buf);
    *_entries_list = NULL;
    return TRUE;                /* continue */
  }
not_implemented:
  {
    GST_ELEMENT_ERROR (element, STREAM, NOT_IMPLEMENTED, (NULL),
        ("Subindex-is-data is not implemented"));
    gst_buffer_unref (buf);
    return FALSE;
  }
860
}
861

862
863
864
865
866
867
868
869
static void
gst_avi_demux_read_subindexes (GstAviDemux * avi,
    GList ** index, GList ** alloc_list)
{
  GList *list;
  guint32 tag;
  GstBuffer *buf;
  gint i, n;
870

871
872
  GST_DEBUG_OBJECT (avi, "gst_avi_demux_read_subindexes");

873
874
  for (n = 0; n < avi->num_streams; n++) {
    avi_stream_context *stream = &avi->stream[n];
875

876
877
878
879
880
881
    for (i = 0; stream->indexes[i] != GST_BUFFER_OFFSET_NONE; i++) {
      if (gst_riff_read_chunk (GST_ELEMENT (avi), avi->sinkpad,
              &stream->indexes[i], &tag, &buf) != GST_FLOW_OK)
        continue;
      else if (tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream->num / 10,
              '0' + stream->num % 10)) {
882
        GST_WARNING_OBJECT (GST_ELEMENT (avi),
883
884
            "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
            GST_FOURCC_ARGS (tag));
885
886
887
888
889
890
891
892
893
894
        gst_buffer_unref (buf);
        continue;
      }

      if (!gst_avi_demux_parse_subindex (GST_ELEMENT (avi), buf, stream, &list))
        continue;
      if (list) {
        *alloc_list = g_list_append (*alloc_list, list->data);
        *index = g_list_concat (*index, list);
      }
895
896
897
898
899
900
901
    }

    g_free (stream->indexes);
    stream->indexes = NULL;
  }
}

902
903
904
905
906
907
908
909
910
911
912
913
/**
 * gst_avi_demux_parse_stream:
 * @element: calling element (used for debugging/errors).
 * @buf: input buffer used to parse the stream.
 *
 * Parses all subchunks in a strl chunk (which defines a single
 * stream). Discards the buffer after use. This function will
 * increment the stream counter internally.
 *
 * Returns: whether the stream was identified successfully.
 *          Errors are not fatal. It does indicate the stream
 *          was skipped.
914
915
 */
static gboolean
916
gst_avi_demux_parse_stream (GstElement * element, GstBuffer * buf)
917
{
918
919
920
921
922
923
924
925
926
  GstAviDemux *avi = GST_AVI_DEMUX (element);
  avi_stream_context *stream = &avi->stream[avi->num_streams];
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
  GstPadTemplate *templ;
  GstBuffer *sub = NULL;
  guint offset = 4;
  guint32 tag = 0;
  gchar *codec_name = NULL, *padname = NULL;
  const gchar *tag_name;
927
928
929
  GstCaps *caps = NULL;
  GstPad *pad;

930
931
932
  GST_DEBUG_OBJECT (element, "Parsing stream");

  /* read strh */
933
  if (!gst_riff_parse_chunk (element, buf, &offset, &tag, &sub) ||
934
935
      tag != GST_RIFF_TAG_strh) {
    GST_ERROR_OBJECT (element,
936
        "Failed to find strh chunk (tag: %" GST_FOURCC_FORMAT ")",
937
        GST_BUFFER_SIZE (buf), GST_FOURCC_ARGS (tag));
938
939
940
941
942
943
944
945
    goto fail;
  } else if (!gst_riff_parse_strh (element, sub, &stream->strh))
    goto fail;

  /* read strf */
  if (!gst_riff_parse_chunk (element, buf, &offset, &tag, &sub) ||
      tag != GST_RIFF_TAG_strf) {
    GST_ERROR_OBJECT (element,
946
        "Failed to find strh chunk (size: %d, tag: %"
947
        GST_FOURCC_FORMAT ")", GST_BUFFER_SIZE (buf), GST_FOURCC_ARGS (tag));
948
949
950
    goto fail;
  } else {
    gboolean res = FALSE;
Wim Taymans's avatar
Wim Taymans committed
951

952
953
    switch (stream->strh->type) {
      case GST_RIFF_FCC_vids:
954
        stream->is_vbr = TRUE;
955
956
957
958
        res = gst_riff_parse_strf_vids (element, sub,
            &stream->strf.vids, &stream->extradata);
        break;
      case GST_RIFF_FCC_auds:
959
960
961
962
963
        stream->is_vbr = (stream->strh->samplesize == 0)
            && stream->strh->scale > 1;
        res =
            gst_riff_parse_strf_auds (element, sub, &stream->strf.auds,
            &stream->extradata);
964
965
        break;
      case GST_RIFF_FCC_iavs:
966
        stream->is_vbr = TRUE;
967
968
969
970
971
        res = gst_riff_parse_strf_iavs (element, sub,
            &stream->strf.iavs, &stream->extradata);
        break;
      default:
        GST_ERROR_OBJECT (element,
972
            "Don´t know how to handle stream type %" GST_FOURCC_FORMAT,
973
974
            GST_FOURCC_ARGS (stream->strh->type));
        break;
975
    }
Wim Taymans's avatar
Wim Taymans committed
976

977
978
979
980
981
982
    if (!res)
      goto fail;
  }

  /* read strd/strn */
  while (gst_riff_parse_chunk (element, buf, &offset, &tag, &sub)) {
983
    /* sub can be NULL if the chunk is empty */
984
    switch (tag) {
985
      case GST_RIFF_TAG_strd:
986
987
988
        if (stream->initdata)
          gst_buffer_unref (stream->initdata);
        stream->initdata = sub;
989
        break;
990
      case GST_RIFF_TAG_strn:
991
        g_free (stream->name);
992
        if (sub != NULL) {
993
994
995
          stream->name =
              g_strndup ((gchar *) GST_BUFFER_DATA (sub),
              (gsize) GST_BUFFER_SIZE (sub));
996
997
998
999
1000
          gst_buffer_unref (sub);
          sub = NULL;
        } else {
          stream->name = g_strdup ("");
        }
1001
        break;