gstavidemux.c 103 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_finalize (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->finalize = gst_avi_demux_finalize;
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
  avi->adapter = gst_adapter_new ();
Wim Taymans's avatar
Wim Taymans committed
191

192
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
193
194
}

195
static void
196
gst_avi_demux_finalize (GObject * object)
197
198
199
{
  GstAviDemux *avi = GST_AVI_DEMUX (object);

200
201
202
  GST_DEBUG ("AVI: finalize");

  g_object_unref (avi->adapter);
203

204
  G_OBJECT_CLASS (parent_class)->finalize (object);
205
206
}

207
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
208
gst_avi_demux_reset (GstAviDemux * avi)
209
{
210
  gint i;
211

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

230
231
232
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
233

234
  avi->state = GST_AVI_DEMUX_START;
235
  avi->offset = 0;
236

237
238
  g_free (avi->index_entries);
  avi->index_entries = NULL;
239
  avi->index_size = 0;
240
  avi->index_offset = 0;
241
  avi->current_entry = 0;
242
243
  g_free (avi->avih);
  avi->avih = NULL;
244

245
246
247
  if (avi->seek_event)
    gst_event_unref (avi->seek_event);
  avi->seek_event = NULL;
248

249
250
251
252
  if (avi->globaltags)
    gst_tag_list_free (avi->globaltags);
  avi->globaltags = NULL;

253
  avi->got_tags = FALSE;
254
255
  avi->have_eos = FALSE;

256
257
  gst_adapter_clear (avi->adapter);

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

261
/* Index helper */
262
263
264
265
266
267
268
269
270
271
272
273
274
275
static gst_avi_index_entry *
gst_avi_demux_index_last (GstAviDemux * avi, gint stream_nr)
{
  gint i;
  gst_avi_index_entry *result = NULL;

  for (i = avi->index_size - 1; i >= 0; i--) {
    if (avi->index_entries[i].stream_nr == stream_nr) {
      result = &avi->index_entries[i];
      break;
    }
  }
  return result;
}
276

277
static gst_avi_index_entry *
278
gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
279
{
280
  gint i;
281
  gst_avi_index_entry *result = NULL;
282

283
  for (i = start; i < avi->index_size; i++) {
284
285
    if (avi->index_entries[i].stream_nr == stream_nr) {
      result = &avi->index_entries[i];
Ronald S. Bultje's avatar
Ronald S. Bultje committed
286
      break;
287
    }
288
  }
289
  return result;
290
}
291

292
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
293
294
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
    gint stream_nr, guint64 time, guint32 flags)
295
296
297
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
298

299
300
  GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT " flags:%d",
      stream_nr, GST_TIME_ARGS (time), flags);
301

302
303
  i = -1;
  do {
304
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
305
    if (!entry)
306
      break;
307

308
    i = entry->index_nr;
309

310
311
312
313
    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);
314
315
    if (entry->ts <= time &&
        (entry->flags & flags) == flags && stream_nr == entry->stream_nr)
316
      last_entry = entry;
317
  } while (entry->ts < time);
318

319
320
  return last_entry;
}
321

322
323
/* GstElement methods */

324
#if 0
325
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
326
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
327
{
328
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
329

330
331
332
333
334
335
336
337
338
339
340
  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
341

342
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
343
      src_a_formats : src_v_formats);
344
}
345
#endif
346

347
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
348
349
350
gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
351
352
{
  gboolean res = TRUE;
353
  GstAviDemux *avidemux = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
354

355
  avi_stream_context *stream = gst_pad_get_element_private (pad);
356

357
  GST_LOG_OBJECT (avidemux,
358
359
360
      "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));
361

362
363
  if (src_format == *dest_format) {
    *dest_value = src_value;
364
365
366
367
368
    goto done;
  }
  if (!stream->strh || !stream->strf.data) {
    res = FALSE;
    goto done;
369
  }
370
  if (stream->strh->type == GST_RIFF_FCC_vids &&
371
372
373
374
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) {
    res = FALSE;
    goto done;
  }
375

376
377
378
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
379
        case GST_FORMAT_BYTES:
380
381
382
          *dest_value =
              gst_util_uint64_scale_int (src_value, stream->strf.auds->av_bps,
              GST_SECOND);
383
384
          break;
        case GST_FORMAT_DEFAULT:
385
386
          *dest_value = gst_util_uint64_scale (src_value, stream->strh->rate,
              stream->strh->scale * GST_SECOND);
387
388
389
390
          break;
        default:
          res = FALSE;
          break;
391
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
392
      break;
393
394
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
395
        case GST_FORMAT_TIME:
396
          if (stream->strf.auds->av_bps != 0) {
397
398
            *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
                stream->strf.auds->av_bps);
399
400
          } else
            res = FALSE;
401
402
403
404
          break;
        default:
          res = FALSE;
          break;
405
406
      }
      break;
407
408
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
409
        case GST_FORMAT_TIME:
410
411
412
          *dest_value =
              gst_util_uint64_scale (src_value,
              stream->strh->scale * GST_SECOND, stream->strh->rate);
413
414
415
416
          break;
        default:
          res = FALSE;
          break;
417
      }
Wim Taymans's avatar
Wim Taymans committed
418
419
      break;
    default:
420
      res = FALSE;
421
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
422

423
done:
424
425
426
  GST_LOG_OBJECT (avidemux,
      "Returning res:%d dest_format:%s dest_value:%" G_GUINT64_FORMAT, res,
      gst_format_get_name (*dest_format), *dest_value);
427
  gst_object_unref (avidemux);
428
  return res;
429
430
}

431
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
432
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
433
{
434
435
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
436
    GST_QUERY_DURATION,
437
438
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
439

440
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
441
442
}

443
static gboolean
444
gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
445
{
446
  gboolean res = TRUE;
447
  GstAviDemux *demux = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
448

449
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
450

451
  if (!stream->strh || !stream->strf.data)
452
    return gst_pad_query_default (pad, query);
453
454
455

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:{
Wim Taymans's avatar
Wim Taymans committed
456
      gint64 pos = 0;
457
458

      if (stream->strh->type == GST_RIFF_FCC_auds) {
459
460
        if (!stream->is_vbr) {
          /* CBR */
461
462
          pos = gst_util_uint64_scale_int ((gint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
463
        } else if (stream->strf.auds->av_bps != 0) {
464
          /* VBR */
465
466
          pos = gst_util_uint64_scale_int (stream->current_byte, GST_SECOND,
              stream->strf.auds->av_bps);
467
        } else if (stream->total_frames != 0 && stream->total_bytes != 0) {
468
          /* calculate timestamps based on percentage of length */
469
470
471
          guint64 xlen = demux->avih->us_frame *
              demux->avih->tot_frames * GST_USECOND;

472
          if (stream->is_vbr)
473
474
            pos = gst_util_uint64_scale_int (xlen, stream->current_byte,
                stream->total_bytes);
475
476
477
          else
            pos = gst_util_uint64_scale_int (xlen, stream->current_frame,
                stream->total_frames);
478
        } else {
479
          /* we don't know */
480
          res = FALSE;
481
482
483
        }
      } else {
        if (stream->strh->rate != 0) {
484
485
486
          pos =
              gst_util_uint64_scale_int ((guint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
487
488
489
        } else {
          pos = stream->current_frame * demux->avih->us_frame * GST_USECOND;
        }
Wim Taymans's avatar
Wim Taymans committed
490
      }
491
492
      if (res) {
        GST_DEBUG ("pos query : %" GST_TIME_FORMAT, GST_TIME_ARGS (pos));
Wim Taymans's avatar
Wim Taymans committed
493
        gst_query_set_position (query, GST_FORMAT_TIME, pos);
494
495
      } else
        GST_WARNING ("pos query failed");
Wim Taymans's avatar
Wim Taymans committed
496
497
498
499
      break;
    }
    case GST_QUERY_DURATION:
    {
500
501
502
503
504
      if (stream->strh->type != GST_RIFF_FCC_auds &&
          stream->strh->type != GST_RIFF_FCC_vids) {
        res = FALSE;
        break;
      }
505
      gst_query_set_duration (query, GST_FORMAT_TIME, stream->duration);
Wim Taymans's avatar
Wim Taymans committed
506
      break;
507
    }
Wim Taymans's avatar
Wim Taymans committed
508
    default:
509
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
510
511
512
513
514
515
      break;
  }

  return res;
}

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

  return masks;
}
527
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
528

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

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

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

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
623
624
625
/* 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 */

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

641
  if (!gst_riff_parse_file_header (element, buf, &doctype))
642
    return FALSE;
643

644
645
646
647
648
649
650
651
  if (doctype != GST_RIFF_RIFF_AVI)
    goto not_avi;

  return TRUE;

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

659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
/*
 * 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
 */
685
static GstFlowReturn
686
gst_avi_demux_stream_init_pull (GstAviDemux * avi)
687
688
689
690
{
  GstFlowReturn res;
  GstBuffer *buf = NULL;

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

  avi->offset += 12;

  return GST_FLOW_OK;
700
701
702
703
704
705
706

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

709
710
711
/* AVI header handling */

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

730
731
732
733
734
  if (buf == NULL)
    goto no_buffer;

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

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

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

  return TRUE;
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790

  /* 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;
  }
791
792
}

793
/*
794
 * gst_avi_demux_parse_superindex:
795
 * @avi: caller element (used for debugging/errors).
796
797
798
799
800
801
802
803
804
 * @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.
805
806
 */
static gboolean
807
gst_avi_demux_parse_superindex (GstAviDemux * avi,
808
    GstBuffer * buf, guint64 ** _indexes)
809
{
810
  guint8 *data;
811
812
  gint bpe = 16, num, i;
  guint64 *indexes;
813
  guint size;
814

815
816
  *_indexes = NULL;

817
818
  size = buf ? GST_BUFFER_SIZE (buf) : 0;
  if (size < 24)
819
    goto too_small;
820

821
822
  data = GST_BUFFER_DATA (buf);

823
824
825
826
827
  /* 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) {
828
    GST_WARNING_OBJECT (avi,
829
        "Superindex for stream %d has unexpected "
830
831
832
833
834
835
836
837
        "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++) {
838
    if (size < 24 + bpe * (i + 1))
839
840
841
      break;
    indexes[i] = GST_READ_UINT64_LE (&data[24 + bpe * i]);
  }
842
843
  indexes[i] = GST_BUFFER_OFFSET_NONE;
  *_indexes = indexes;
844
845
846
847

  gst_buffer_unref (buf);

  return TRUE;
848
849
850
851

  /* ERRORS */
too_small:
  {
852
853
854
855
    GST_ERROR_OBJECT (avi,
        "Not enough data to parse superindex (%d available, 24 needed)", size);
    if (buf)
      gst_buffer_unref (buf);
856
857
    return FALSE;
  }
858
859
}

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

889
890
  *_entries_list = NULL;

891
  size = buf ? GST_BUFFER_SIZE (buf) : 0;
892

893
894
  /* check size */
  if (size < 24)
895
    goto too_small;
896

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

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

916
  entries = g_new (gst_avi_index_entry, num);
917

918
  for (x = 0; x < num; x++) {
919
920
    gint64 next_ts;

921
    entry = &entries[x];
922

923
    if (size < 24 + bpe * (x + 1))
924
      break;
925

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

934
935
936
937
    /* stream duration unknown, now we can calculate it */
    if (stream->idx_duration == -1)
      stream->idx_duration = 0;

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

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

    stream->total_bytes += entry->size;
957
    stream->total_frames++;
958
    stream->idx_duration = next_ts;
959

960
961
    entries_list = g_list_prepend (entries_list, entry);
  }
962

963
  GST_LOG_OBJECT (element, "Read %d index entries", x);
964

965
966
967
968
969
970
971
  gst_buffer_unref (buf);

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

973
  return TRUE;
974
975
976
977
978

  /* ERRORS */
too_small:
  {
    GST_ERROR_OBJECT (element,
979
980
981
        "Not enough data to parse subindex (%d available, 24 needed)", size);
    if (buf)
      gst_buffer_unref (buf);
982
983
984
985
986
987
988
989
990
    return TRUE;                /* continue *