gstavidemux.c 105 KB
Newer Older
1
2
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@temple-baptist.com>
3
 * Copyright (C) <2006> Nokia Corporation (contact <stefan.kost@nokia.com>)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 *
 * 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.
 */
20
/* Element-Checklist-Version: 5 */
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
21

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
 * 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>
 *
 */

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

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

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

58
59
60
GST_DEBUG_CATEGORY_STATIC (avidemux_debug);
#define GST_CAT_DEFAULT avidemux_debug

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
61
62
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_EVENT);

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

69
70
71
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);
72
static void gst_avi_demux_dispose (GObject * object);
73

74
static void gst_avi_demux_reset (GstAviDemux * avi);
75

76
#if 0
77
static const GstEventMask *gst_avi_demux_get_event_mask (GstPad * pad);
78
#endif
79
static gboolean gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event);
80
81

#if 0
82
static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad);
83
#endif
84
85
86
87
88
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);

89
90
91
static gboolean gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment);
static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad,
    GstEvent * event);
92
93
94
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,
95
    gboolean active);
96
97
98
static gboolean gst_avi_demux_activate_push (GstPad * pad, gboolean active);
static GstFlowReturn gst_avi_demux_chain (GstPad * pad, GstBuffer * buf);

99
100
static GstStateChangeReturn gst_avi_demux_change_state (GstElement * element,
    GstStateChange transition);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
101

102
static GstElementClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
103

104
105
/* GObject mathods */

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
106
GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
107
gst_avi_demux_get_type (void)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
108
109
110
111
112
{
  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
113
      sizeof (GstAviDemuxClass),
114
      (GBaseInitFunc) gst_avi_demux_base_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
115
      NULL,
116
      (GClassInitFunc) gst_avi_demux_class_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
117
118
      NULL,
      NULL,
119
      sizeof (GstAviDemux),
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
120
      0,
121
      (GInstanceInitFunc) gst_avi_demux_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
122
    };
123
124

    avi_demux_type =
125
        g_type_register_static (GST_TYPE_ELEMENT,
126
        "GstAviDemux", &avi_demux_info, 0);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
127
  }
128

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
129
130
131
  return avi_demux_type;
}

132
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
133
gst_avi_demux_base_init (GstAviDemuxClass * klass)
134
{
135
  static const GstElementDetails gst_avi_demux_details =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
136
137
138
139
140
141
      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>");
142
143
144
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstPadTemplate *videosrctempl, *audiosrctempl;
  GstCaps *audcaps, *vidcaps;
145

146
  audcaps = gst_riff_create_audio_template_caps ();
147
  gst_caps_append (audcaps, gst_caps_new_simple ("audio/x-avi-unknown", NULL));
148
  audiosrctempl = gst_pad_template_new ("audio_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
149
      GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps);
150

David Schleef's avatar
David Schleef committed
151
152
  vidcaps = gst_riff_create_video_template_caps ();
  gst_caps_append (vidcaps, gst_riff_create_iavs_template_caps ());
153
  gst_caps_append (vidcaps, gst_caps_new_simple ("video/x-avi-unknown", NULL));
154
  videosrctempl = gst_pad_template_new ("video_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
155
      GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps);
156

157
158
159
  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
160
      gst_static_pad_template_get (&sink_templ));
161
162
163
  gst_element_class_set_details (element_class, &gst_avi_demux_details);
}

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
164
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
165
gst_avi_demux_class_init (GstAviDemuxClass * klass)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
166
{
167
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
168
  GObjectClass *gobject_class = (GObjectClass *) klass;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
169

170
  GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
171
      0, "Demuxer for AVI streams");
172

173
  parent_class = g_type_class_peek_parent (klass);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
174

175
  gobject_class->dispose = gst_avi_demux_dispose;
Wim Taymans's avatar
Wim Taymans committed
176
  gstelement_class->change_state = gst_avi_demux_change_state;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
177
178
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
179
180
static void
gst_avi_demux_init (GstAviDemux * avi)
181
{
182
  avi->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink");
183
184
185
  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);
186
  gst_pad_set_activatepush_function (avi->sinkpad, gst_avi_demux_activate_push);
187
  gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad);
188
  gst_pad_set_chain_function (avi->sinkpad, gst_avi_demux_chain);
Wim Taymans's avatar
Wim Taymans committed
189

190
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
191

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

196
197
198
199
200
201
202
203
204
205
206
207
208
209
static void
gst_avi_demux_dispose (GObject * object)
{
  GstAviDemux *avi = GST_AVI_DEMUX (object);

  GST_DEBUG ("AVI: Dispose");
  if (avi->adapter) {
    g_object_unref (avi->adapter);
    avi->adapter = NULL;
  }

  G_OBJECT_CLASS (parent_class)->dispose (object);
}

210
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
211
gst_avi_demux_reset (GstAviDemux * avi)
212
{
213
  gint i;
214

215
216
  for (i = 0; i < avi->num_streams; i++) {
    g_free (avi->stream[i].strh);
217
    g_free (avi->stream[i].strf.data);
218
219
    if (avi->stream[i].name)
      g_free (avi->stream[i].name);
220
221
222
223
    if (avi->stream[i].initdata)
      gst_buffer_unref (avi->stream[i].initdata);
    if (avi->stream[i].extradata)
      gst_buffer_unref (avi->stream[i].extradata);
224
225
    if (avi->stream[i].pad)
      gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad);
226
227
228
229
    if (avi->stream[i].taglist) {
      gst_tag_list_free (avi->stream[i].taglist);
      avi->stream[i].taglist = NULL;
    }
230
231
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
232

233
234
235
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
236

237
  avi->state = GST_AVI_DEMUX_START;
238
  avi->offset = 0;
239

240
241
  g_free (avi->index_entries);
  avi->index_entries = NULL;
242
  avi->index_size = 0;
243
  avi->index_offset = 0;
244
  avi->current_entry = 0;
245
246
  g_free (avi->avih);
  avi->avih = NULL;
247

248
249
250
  if (avi->seek_event)
    gst_event_unref (avi->seek_event);
  avi->seek_event = NULL;
251

252
253
254
255
  if (avi->globaltags)
    gst_tag_list_free (avi->globaltags);
  avi->globaltags = NULL;

256
  avi->got_tags = FALSE;
257
258
259
  avi->have_eos = FALSE;

  gst_segment_init (&avi->segment, GST_FORMAT_TIME);
260
261
}

262
263
/* Index helper */

264
static gst_avi_index_entry *
265
gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
266
{
267
  gint i;
268
  gst_avi_index_entry *result = NULL;
269

270
  for (i = start; i < avi->index_size; i++) {
271
272
    if (avi->index_entries[i].stream_nr == stream_nr) {
      result = &avi->index_entries[i];
Ronald S. Bultje's avatar
Ronald S. Bultje committed
273
      break;
274
    }
275
  }
276

277
  return result;
278
}
279

280
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
281
282
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
    gint stream_nr, guint64 time, guint32 flags)
283
284
285
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
286

287
288
  GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT " flags:%d",
      stream_nr, GST_TIME_ARGS (time), flags);
289

290
291
  i = -1;
  do {
292
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
293
    if (!entry)
294
      return last_entry;
295

296
    i = entry->index_nr;
297

298
299
300
301
    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);
302
303
    if (entry->ts <= time &&
        (entry->flags & flags) == flags && stream_nr == entry->stream_nr)
304
      last_entry = entry;
305
  } while (entry->ts < time);
306

307
308
  return last_entry;
}
309

310
311
/* GstElement methods */

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

318
319
320
321
322
323
324
325
326
327
328
  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
329

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

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

343
  avi_stream_context *stream = gst_pad_get_element_private (pad);
344

345
  GST_LOG_OBJECT (avidemux,
346
347
348
      "Received  src_format:%s, src_value:%" G_GUINT64_FORMAT
      ", dest_format:%s", gst_format_get_name (src_format), src_value,
      gst_format_get_name (*dest_format));
349

350
351
  if (src_format == *dest_format) {
    *dest_value = src_value;
352
353
354
355
356
    goto done;
  }
  if (!stream->strh || !stream->strf.data) {
    res = FALSE;
    goto done;
357
  }
358
  if (stream->strh->type == GST_RIFF_FCC_vids &&
359
360
361
362
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) {
    res = FALSE;
    goto done;
  }
363

364
365
366
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
367
        case GST_FORMAT_BYTES:
368
369
370
          *dest_value =
              gst_util_uint64_scale_int (src_value, stream->strf.auds->av_bps,
              GST_SECOND);
371
372
          break;
        case GST_FORMAT_DEFAULT:
373
374
          *dest_value = gst_util_uint64_scale (src_value, stream->strh->rate,
              stream->strh->scale * GST_SECOND);
375
376
377
378
          break;
        default:
          res = FALSE;
          break;
379
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
380
      break;
381
382
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
383
        case GST_FORMAT_TIME:
384
          if (stream->strf.auds->av_bps != 0) {
385
386
            *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
                stream->strf.auds->av_bps);
387
388
          } else
            res = FALSE;
389
390
391
392
          break;
        default:
          res = FALSE;
          break;
393
394
      }
      break;
395
396
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
397
        case GST_FORMAT_TIME:
398
399
400
          *dest_value =
              gst_util_uint64_scale (src_value,
              stream->strh->scale * GST_SECOND, stream->strh->rate);
401
402
403
404
          break;
        default:
          res = FALSE;
          break;
405
      }
Wim Taymans's avatar
Wim Taymans committed
406
407
      break;
    default:
408
      res = FALSE;
409
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
410

411
done:
412
413
414
  GST_LOG_OBJECT (avidemux,
      "Returning res:%d dest_format:%s dest_value:%" G_GUINT64_FORMAT, res,
      gst_format_get_name (*dest_format), *dest_value);
415
  gst_object_unref (avidemux);
416
  return res;
417
418
}

419
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
420
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
421
{
422
423
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
424
    GST_QUERY_DURATION,
425
426
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
427

428
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
429
430
}

431
static gboolean
432
gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
433
{
434
  gboolean res = TRUE;
435
  GstAviDemux *demux = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
436

437
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
438

439
  if (!stream->strh || !stream->strf.data)
440
    return gst_pad_query_default (pad, query);
441
442
443

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:{
Wim Taymans's avatar
Wim Taymans committed
444
      gint64 pos = 0;
445
446

      if (stream->strh->type == GST_RIFF_FCC_auds) {
447
448
        if (!stream->is_vbr) {
          /* CBR */
449
450
          pos = gst_util_uint64_scale_int ((gint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
451
        } else if (stream->strf.auds->av_bps != 0) {
452
          /* VBR */
453
454
          pos = gst_util_uint64_scale_int (stream->current_byte, GST_SECOND,
              stream->strf.auds->av_bps);
455
        } else if (stream->total_frames != 0 && stream->total_bytes != 0) {
456
          /* calculate timestamps based on percentage of length */
457
458
459
          guint64 xlen = demux->avih->us_frame *
              demux->avih->tot_frames * GST_USECOND;

460
          if (stream->is_vbr)
461
462
            pos = gst_util_uint64_scale_int (xlen, stream->current_byte,
                stream->total_bytes);
463
464
465
          else
            pos = gst_util_uint64_scale_int (xlen, stream->current_frame,
                stream->total_frames);
466
        } else {
467
          /* we don't know */
468
          res = FALSE;
469
470
471
        }
      } else {
        if (stream->strh->rate != 0) {
472
473
474
          pos =
              gst_util_uint64_scale_int ((guint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
475
476
477
        } else {
          pos = stream->current_frame * demux->avih->us_frame * GST_USECOND;
        }
Wim Taymans's avatar
Wim Taymans committed
478
      }
479
480
      if (res) {
        GST_DEBUG ("pos query : %" GST_TIME_FORMAT, GST_TIME_ARGS (pos));
Wim Taymans's avatar
Wim Taymans committed
481
        gst_query_set_position (query, GST_FORMAT_TIME, pos);
482
483
      } else
        GST_WARNING ("pos query failed");
Wim Taymans's avatar
Wim Taymans committed
484
485
486
487
      break;
    }
    case GST_QUERY_DURATION:
    {
488
489
490
491
492
      if (stream->strh->type != GST_RIFF_FCC_auds &&
          stream->strh->type != GST_RIFF_FCC_vids) {
        res = FALSE;
        break;
      }
493
494
495
496
497
498
499
500
501
502
      /* use duration from the index if we have an
       * index instead of trusting the stream header */
      if (GST_CLOCK_TIME_IS_VALID (stream->idx_duration)) {
        gst_query_set_duration (query, GST_FORMAT_TIME, stream->idx_duration);
      } else {
        gint64 len = gst_util_uint64_scale ((guint64) stream->strh->length *
            stream->strh->scale, GST_SECOND, stream->strh->rate);

        gst_query_set_duration (query, GST_FORMAT_TIME, len);
      }
Wim Taymans's avatar
Wim Taymans committed
503
      break;
504
    }
Wim Taymans's avatar
Wim Taymans committed
505
    default:
506
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
507
508
509
510
511
512
      break;
  }

  return res;
}

513
#if 0
514
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
515
gst_avi_demux_get_event_mask (GstPad * pad)
516
517
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
518
519
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
520
521
522
523
  };

  return masks;
}
524
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
525

Wim Taymans's avatar
Wim Taymans committed
526
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
527
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
528
529
{
  gboolean res = TRUE;
530
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
531

532
  GST_DEBUG_OBJECT (avi,
533
      "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
Wim Taymans's avatar
Wim Taymans committed
534
535
536

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
537
538
539
540
541
542
543
      /* 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
544
545
      break;
    default:
546
      /* most other events are not very usefull */
Wim Taymans's avatar
Wim Taymans committed
547
548
549
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
550
  gst_event_unref (event);
Wim Taymans's avatar
Wim Taymans committed
551
552
553
  return res;
}

554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
/* streaming helper (push) */

/*
 * gst_avi_demux_peek_chunk_info:
 * @avi: Avi object
 * @tag: holder for tag
 * @size: holder for tag size
 *
 * Peek next chunk info (tag and size)
 *
 * Returns: TRUE when one chunk info has been got
 */
static gboolean
gst_avi_demux_peek_chunk_info (GstAviDemux * avi, guint32 * tag, guint32 * size)
{
  const guint8 *data = NULL;

  if (gst_adapter_available (avi->adapter) < 8) {
    return FALSE;
  }

  data = gst_adapter_peek (avi->adapter, 8);
  *tag = GST_READ_UINT32_LE (data);
  *size = GST_READ_UINT32_LE (data + 4);

  return TRUE;
}

/*
 * gst_avi_demux_peek_chunk:
 * @avi: Avi object
 * @tag: holder for tag
 * @size: holder for tag size
 *
 * Peek enough data for one full chunk
 *
 * Returns: %TRUE when one chunk has been got
 */
static gboolean
gst_avi_demux_peek_chunk (GstAviDemux * avi, guint32 * tag, guint32 * size)
{
  const guint8 *data = NULL;
  guint32 peek_size = 0;

  if (gst_adapter_available (avi->adapter) < 8) {
    return FALSE;
  }

  data = gst_adapter_peek (avi->adapter, 8);
  *tag = GST_READ_UINT32_LE (data);
  *size = GST_READ_UINT32_LE (data + 4);
  if (!(*size) || (*size) == -1) {
    GST_DEBUG ("Invalid chunk size");
    return FALSE;
  }
  GST_DEBUG ("Need to peek chunk of %d bytes to read chunk %" GST_FOURCC_FORMAT,
      *size, GST_FOURCC_ARGS (*tag));
  peek_size = (*size + 1) & ~1;

  if (gst_adapter_available (avi->adapter) >= (8 + peek_size)) {
    return TRUE;
  } else {
    return FALSE;
  }
}

/* AVI init */

/*
623
624
625
626
627
 * 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
628
 * bytes long. Takes ownership of @buf.
629
630
631
 *
 * Returns: TRUE if the file is a RIFF/AVI file, FALSE otherwise.
 *          Throws an error, caller should error out (fatal).
632
 */
633
static gboolean
634
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
635
{
636
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
637

638
  if (!gst_riff_parse_file_header (element, buf, &doctype))
639
    return FALSE;
640

641
642
643
644
645
646
647
648
  if (doctype != GST_RIFF_RIFF_AVI)
    goto not_avi;

  return TRUE;

  /* ERRORS */
not_avi:
  {
649
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
650
        ("File is not an AVI file: %" GST_FOURCC_FORMAT,
651
            GST_FOURCC_ARGS (doctype)));
652
653
654
655
    return FALSE;
  }
}

656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
/*
 * Read AVI file tag when streaming
 */
static GstFlowReturn
gst_avi_demux_stream_init_push (GstAviDemux * avi)
{
  if (gst_adapter_available (avi->adapter) >= 12) {
    GstBuffer *tmp = gst_buffer_new ();

    /* _take flushes the data */
    GST_BUFFER_DATA (tmp) = gst_adapter_take (avi->adapter, 12);
    GST_BUFFER_SIZE (tmp) = 12;

    GST_DEBUG ("Parsing avi header");
    if (!gst_avi_demux_parse_file_header (GST_ELEMENT (avi), tmp)) {
      return GST_FLOW_ERROR;
    }
    GST_DEBUG ("header ok");
    avi->offset += 12;
  }
  return GST_FLOW_OK;
}

/*
 * Read AVI file tag
 */
682
static GstFlowReturn
683
gst_avi_demux_stream_init_pull (GstAviDemux * avi)
684
685
686
687
{
  GstFlowReturn res;
  GstBuffer *buf = NULL;

688
689
  res = gst_pad_pull_range (avi->sinkpad, avi->offset, 12, &buf);
  if (res != GST_FLOW_OK)
690
    return res;
691
  else if (!gst_avi_demux_parse_file_header (GST_ELEMENT_CAST (avi), buf))
692
    goto wrong_header;
693
694
695
696

  avi->offset += 12;

  return GST_FLOW_OK;
697
698
699
700
701
702
703

  /* ERRORS */
wrong_header:
  {
    GST_DEBUG_OBJECT (avi, "error parsing file header");
    return GST_FLOW_ERROR;
  }
704
705
}

706
707
708
/* AVI header handling */

/*
709
710
711
712
713
714
715
716
717
718
719
 * 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).
720
 */
721
static gboolean
722
723
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
724
{
725
  gst_riff_avih *avih;
726

727
728
729
730
731
  if (buf == NULL)
    goto no_buffer;

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

733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
  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
751
752

  /* debug stuff */
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
  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;
770
771
772
  gst_buffer_unref (buf);

  return TRUE;
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787

  /* 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;
  }
788
789
}

790
/*
791
792
793
794
795
796
797
798
799
800
801
 * 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.
802
803
 */
static gboolean
804
805
gst_avi_demux_parse_superindex (GstElement * element,
    GstBuffer * buf, guint64 ** _indexes)
806
{
807
  guint8 *data;
808
809
810
  gint bpe = 16, num, i;
  guint64 *indexes;

811
812
  *_indexes = NULL;

813
814
815
  if (buf == NULL)
    goto no_buffer;

816
  if (GST_BUFFER_SIZE (buf) < 24)
817
    goto too_small;
818

819
820
  data = GST_BUFFER_DATA (buf);

821
822
823
824
825
  /* 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) {
826
827
    GST_WARNING_OBJECT (element,
        "Superindex for stream %d has unexpected "
828
829
830
831
832
833
834
835
836
837
838
839
        "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]);
  }
840
841
  indexes[i] = GST_BUFFER_OFFSET_NONE;
  *_indexes = indexes;
842
843
844
845

  gst_buffer_unref (buf);

  return TRUE;
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860

  /* 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;
  }
861
862
}

863
/*
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
 * 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.
 */
880
static gboolean
881
882
gst_avi_demux_parse_subindex (GstElement * element,
    GstBuffer * buf, avi_stream_context * stream, GList ** _entries_list)
883
{
884
885
886
  guint8 *data = GST_BUFFER_DATA (buf);
  gint bpe, num, x;
  guint64 baseoff;
887
  gst_avi_index_entry *entries, *entry;
888
  GList *entries_list = NULL;
889
890
  GstFormat format = GST_FORMAT_TIME;

891
892
  *_entries_list = NULL;

893
  /* check size */
894
895
896
897
898
  if (buf == NULL)
    goto no_buffer;

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

900
  /* We don't support index-data yet */
901
902
  if (data[3] & 0x80)
    goto not_implemented;
903

904
905
906
907
908
909
910
911
912
913
914
915
916
917
  /* 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]);
918

919
920
  entries = g_new (gst_avi_index_entry, num);
  for (x = 0; x < num; x++) {
921
922
    gint64 next_ts;

923
    entry = &entries[x];
924

925
926
    if (GST_BUFFER_SIZE (buf) < 24 + bpe * (x + 1))
      break;
927

928
929
930
931
932
933
934
    /* 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;
935

936
    /* timestamps */
937
938
939
    entry->ts = stream->total_time;
    if (stream->is_vbr) {
      /* VBR get next timestamp */
940
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
941
942
943
944
945
          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);
946
    }
947
948
    /* duration is next - current */
    entry->dur = next_ts - entry->ts;
949
950
951
952

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