gstavidemux.c 111 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
/**
 * SECTION:element-avidemux
 *
 * <refsect2>
 * <para>
 * Demuxes an .avi file into raw or compressed audio and/or video streams.
 * </para>
 * <para>
Wim Taymans's avatar
Wim Taymans committed
30
 * This element supports both push and pull-based scheduling, depending on the
31
 * capabilities of the upstream elements.
32
33
34
35
 * </para>
 * <title>Example launch line</title>
 * <para>
 * <programlisting>
Wim Taymans's avatar
Wim Taymans committed
36
 * gst-launch filesrc location=test.avi ! avidemux name=demux  demux.audio_00 ! decodebin ! audioconvert ! audioresample ! autoaudiosink   demux.video_00 ! queue ! decodebin ! ffmpegcolorspace ! videoscale ! autovideosink
37
38
39
40
41
42
43
44
 * </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>
 *
Wim Taymans's avatar
Wim Taymans committed
45
 * Last reviewed on 2006-12-29 (0.10.6)
46
47
 */

48
49
50
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
51

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
52
53
#include <string.h>

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

60
61
62
GST_DEBUG_CATEGORY_STATIC (avidemux_debug);
#define GST_CAT_DEFAULT avidemux_debug

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
63
64
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_EVENT);

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

71
72
73
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);
74
static void gst_avi_demux_finalize (GObject * object);
75

76
static void gst_avi_demux_reset (GstAviDemux * avi);
77

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

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

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

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

104
static GstElementClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
105

106
/* GObject methods */
107

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

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

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
131
132
133
  return avi_demux_type;
}

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

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

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

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

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

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

175
  parent_class = g_type_class_peek_parent (klass);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
176

177
  gobject_class->finalize = gst_avi_demux_finalize;
178
179
  gstelement_class->change_state =
      GST_DEBUG_FUNCPTR (gst_avi_demux_change_state);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
180
181
}

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

193
  avi->adapter = gst_adapter_new ();
Wim Taymans's avatar
Wim Taymans committed
194

195
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
196
197
}

198
static void
199
gst_avi_demux_finalize (GObject * object)
200
201
202
{
  GstAviDemux *avi = GST_AVI_DEMUX (object);

203
204
205
  GST_DEBUG ("AVI: finalize");

  g_object_unref (avi->adapter);
206

207
  G_OBJECT_CLASS (parent_class)->finalize (object);
208
209
}

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_pad_set_active (avi->stream[i].pad, FALSE);
226
      gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad);
227
    }
228
229
230
231
    if (avi->stream[i].taglist) {
      gst_tag_list_free (avi->stream[i].taglist);
      avi->stream[i].taglist = NULL;
    }
232
233
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
234

235
236
237
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
238

239
  avi->state = GST_AVI_DEMUX_START;
240
  avi->offset = 0;
241

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

250
251
252
  if (avi->seek_event)
    gst_event_unref (avi->seek_event);
  avi->seek_event = NULL;
253

254
255
256
257
  if (avi->globaltags)
    gst_tag_list_free (avi->globaltags);
  avi->globaltags = NULL;

258
  avi->got_tags = FALSE;
259
260
  avi->have_eos = FALSE;

261
262
  gst_adapter_clear (avi->adapter);

263
  gst_segment_init (&avi->segment, GST_FORMAT_TIME);
264
265
}

266
/* Index helper */
267
268
269
270
271
272
273
274
275
276
277
278
279
280
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;
}
281

282
static gst_avi_index_entry *
283
gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
284
{
285
  gint i;
286
  gst_avi_index_entry *result = NULL;
287

288
  for (i = start; i < avi->index_size; i++) {
289
290
    if (avi->index_entries[i].stream_nr == stream_nr) {
      result = &avi->index_entries[i];
Ronald S. Bultje's avatar
Ronald S. Bultje committed
291
      break;
292
    }
293
  }
294
  return result;
295
}
296

297
298
299
300
301
302
303
304
305
306
307
/*
 * gst_avi_index_entry:
 * @avi: Avi object
 * @stream_nr: stream number
 * @time: seek time position
 * @flags: index entry flags to match
 *
 * Finds the index entry which time is less or equal than the requested time.
 *
 * Returns: the found index entry or %NULL
 */
308
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
309
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
310
    gint stream_nr, guint64 time, guchar flags)
311
312
313
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
314

315
  GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT " flags:%x",
316
      stream_nr, GST_TIME_ARGS (time), flags);
317

318
319
  i = -1;
  do {
320
    /* get next entry for given stream */
321
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
322
    if (!entry)
323
      break;
324

325
    i = entry->index_nr;
326
327
    if (entry->ts <= time && (entry->flags & flags) == flags)
      last_entry = entry;
328

329
330
    GST_LOG_OBJECT (avi,
        "looking at entry %d / ts:%" GST_TIME_FORMAT " / dur:%" GST_TIME_FORMAT
331
        " flags:%02x", i, GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (entry->dur),
332
        entry->flags);
333
  } while (entry->ts < time);
334

335
336
  return last_entry;
}
337

338
339
/* GstElement methods */

340
#if 0
341
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
342
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
343
{
344
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
345

346
347
348
349
350
351
352
353
354
355
356
  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
357

358
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
359
      src_a_formats : src_v_formats);
360
}
361
#endif
362

363
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
364
365
366
gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
367
368
{
  gboolean res = TRUE;
369
  GstAviDemux *avidemux = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
370

371
  avi_stream_context *stream = gst_pad_get_element_private (pad);
372

373
  GST_LOG_OBJECT (avidemux,
374
375
376
      "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));
377

378
379
  if (src_format == *dest_format) {
    *dest_value = src_value;
380
381
382
383
384
    goto done;
  }
  if (!stream->strh || !stream->strf.data) {
    res = FALSE;
    goto done;
385
  }
386
  if (stream->strh->type == GST_RIFF_FCC_vids &&
387
388
389
390
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) {
    res = FALSE;
    goto done;
  }
391

392
393
394
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
395
        case GST_FORMAT_BYTES:
396
397
          *dest_value = gst_util_uint64_scale (src_value,
              (guint64) stream->strf.auds->av_bps, GST_SECOND);
398
399
          break;
        case GST_FORMAT_DEFAULT:
400
401
          *dest_value = gst_util_uint64_scale (src_value, stream->strh->rate,
              stream->strh->scale * GST_SECOND);
402
403
404
405
          break;
        default:
          res = FALSE;
          break;
406
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
407
      break;
408
409
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
410
        case GST_FORMAT_TIME:
411
          if (stream->strf.auds->av_bps != 0) {
412
413
            *dest_value = gst_util_uint64_scale (src_value, GST_SECOND,
                (guint64) stream->strf.auds->av_bps);
414
415
          } else
            res = FALSE;
416
417
418
419
          break;
        default:
          res = FALSE;
          break;
420
421
      }
      break;
422
423
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
424
        case GST_FORMAT_TIME:
425
426
          *dest_value = gst_util_uint64_scale (src_value,
              stream->strh->scale * GST_SECOND, (guint64) stream->strh->rate);
427
428
429
430
          break;
        default:
          res = FALSE;
          break;
431
      }
Wim Taymans's avatar
Wim Taymans committed
432
433
      break;
    default:
434
      res = FALSE;
435
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
436

437
done:
438
439
440
  GST_LOG_OBJECT (avidemux,
      "Returning res:%d dest_format:%s dest_value:%" G_GUINT64_FORMAT, res,
      gst_format_get_name (*dest_format), *dest_value);
441
  gst_object_unref (avidemux);
442
  return res;
443
444
}

445
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
446
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
447
{
448
449
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
450
    GST_QUERY_DURATION,
451
452
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
453

454
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
455
456
}

457
static gboolean
458
gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
459
{
460
  gboolean res = TRUE;
461
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
462

463
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
464

465
  if (!stream->strh || !stream->strf.data)
466
    return gst_pad_query_default (pad, query);
467
468
469

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

472
473
474
      GST_DEBUG ("pos query for stream %d: frames %d, bytes %" G_GUINT64_FORMAT,
          stream->num, stream->current_frame, stream->current_byte);

475
      if (stream->strh->type == GST_RIFF_FCC_auds) {
476
477
        if (stream->is_vbr) {
          /* VBR */
478
479
480
          pos = gst_util_uint64_scale ((gint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, (guint64) stream->strh->rate);
          GST_DEBUG_OBJECT (avi, "VBR convert frame %u, time %"
481
              GST_TIME_FORMAT, stream->current_frame, GST_TIME_ARGS (pos));
482
        } else if (stream->strf.auds->av_bps != 0) {
483
          /* CBR */
484
485
486
          pos = gst_util_uint64_scale (stream->current_byte, GST_SECOND,
              (guint64) stream->strf.auds->av_bps);
          GST_DEBUG_OBJECT (avi,
487
488
              "CBR convert bytes %" G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT,
              stream->current_byte, GST_TIME_ARGS (pos));
489
        } else if (stream->total_frames != 0 && stream->total_bytes != 0) {
490
          /* calculate timestamps based on percentage of length */
491
492
          guint64 xlen = avi->avih->us_frame *
              avi->avih->tot_frames * GST_USECOND;
493

494
          if (stream->is_vbr) {
495
            pos = gst_util_uint64_scale (xlen, stream->current_frame,
496
                stream->total_frames);
497
            GST_DEBUG_OBJECT (avi, "VBR perc convert frame %u, time %"
498
499
                GST_TIME_FORMAT, stream->current_frame, GST_TIME_ARGS (pos));
          } else {
500
            pos = gst_util_uint64_scale (xlen, stream->current_byte,
501
                stream->total_bytes);
502
            GST_DEBUG_OBJECT (avi, "CBR perc convert bytes %" G_GUINT64_FORMAT
503
504
505
                ", time %" GST_TIME_FORMAT, stream->current_byte,
                GST_TIME_ARGS (pos));
          }
506
        } else {
507
          /* we don't know */
508
          res = FALSE;
509
510
511
        }
      } else {
        if (stream->strh->rate != 0) {
512
513
          pos = gst_util_uint64_scale ((guint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, (guint64) stream->strh->rate);
514
        } else {
515
          pos = stream->current_frame * avi->avih->us_frame * GST_USECOND;
516
        }
Wim Taymans's avatar
Wim Taymans committed
517
      }
518
519
      if (res) {
        GST_DEBUG ("pos query : %" GST_TIME_FORMAT, GST_TIME_ARGS (pos));
Wim Taymans's avatar
Wim Taymans committed
520
        gst_query_set_position (query, GST_FORMAT_TIME, pos);
521
522
      } else
        GST_WARNING ("pos query failed");
Wim Taymans's avatar
Wim Taymans committed
523
524
525
526
      break;
    }
    case GST_QUERY_DURATION:
    {
527
528
529
530
531
      if (stream->strh->type != GST_RIFF_FCC_auds &&
          stream->strh->type != GST_RIFF_FCC_vids) {
        res = FALSE;
        break;
      }
532
      gst_query_set_duration (query, GST_FORMAT_TIME, stream->duration);
Wim Taymans's avatar
Wim Taymans committed
533
      break;
534
    }
Wim Taymans's avatar
Wim Taymans committed
535
    default:
536
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
537
538
539
540
541
542
      break;
  }

  return res;
}

543
#if 0
544
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
545
gst_avi_demux_get_event_mask (GstPad * pad)
546
547
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
548
549
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
550
551
552
553
  };

  return masks;
}
554
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
555

Wim Taymans's avatar
Wim Taymans committed
556
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
557
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
558
559
{
  gboolean res = TRUE;
560
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
561

562
  GST_DEBUG_OBJECT (avi,
563
      "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
Wim Taymans's avatar
Wim Taymans committed
564
565
566

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
567
568
569
570
571
572
573
      /* 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
574
575
      break;
    default:
576
      /* most other events are not very usefull */
Wim Taymans's avatar
Wim Taymans committed
577
578
579
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
580
  gst_event_unref (event);
Wim Taymans's avatar
Wim Taymans committed
581
582
583
  return res;
}

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)
{
  guint32 peek_size = 0;
626
  gint available;
627

628
  if (!gst_avi_demux_peek_chunk_info (avi, tag, size)) {
629
630
    return FALSE;
  }
631
  /* FIXME: shouldn't this check go to gst_avi_demux_peek_chunk_info() already */
632
  if (!(*size) || (*size) == -1) {
633
634
    GST_INFO ("Invalid chunk size %d for tag %" GST_FOURCC_FORMAT,
        *size, GST_FOURCC_ARGS (*tag));
635
636
637
    return FALSE;
  }
  peek_size = (*size + 1) & ~1;
638
  available = gst_adapter_available (avi->adapter);
639

640
641
642
643
  GST_DEBUG ("Need to peek chunk of %d bytes to read chunk %" GST_FOURCC_FORMAT
      ", %d bytes available", *size, GST_FOURCC_ARGS (*tag), available);

  if (available >= (8 + peek_size)) {
644
645
646
647
648
649
650
651
652
    return TRUE;
  } else {
    return FALSE;
  }
}

/* AVI init */

/*
653
654
655
656
657
 * 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
658
 * bytes long. Takes ownership of @buf.
659
660
661
 *
 * Returns: TRUE if the file is a RIFF/AVI file, FALSE otherwise.
 *          Throws an error, caller should error out (fatal).
662
 */
663
static gboolean
664
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
665
{
666
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
667

668
  /* riff_parse posts an error */
669
  if (!gst_riff_parse_file_header (element, buf, &doctype))
670
    return FALSE;
671

672
673
674
675
676
677
678
679
  if (doctype != GST_RIFF_RIFF_AVI)
    goto not_avi;

  return TRUE;

  /* ERRORS */
not_avi:
  {
680
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
681
        ("File is not an AVI file: %" GST_FOURCC_FORMAT,
682
            GST_FOURCC_ARGS (doctype)));
683
684
685
686
    return FALSE;
  }
}

687
688
689
690
691
692
693
/*
 * Read AVI file tag when streaming
 */
static GstFlowReturn
gst_avi_demux_stream_init_push (GstAviDemux * avi)
{
  if (gst_adapter_available (avi->adapter) >= 12) {
694
    GstBuffer *tmp;
695

696
    tmp = gst_adapter_take_buffer (avi->adapter, 12);
697
698
699
700
701
702
703

    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;
704
705

    avi->state = GST_AVI_DEMUX_HEADER;
706
707
708
709
710
711
712
  }
  return GST_FLOW_OK;
}

/*
 * Read AVI file tag
 */
713
static GstFlowReturn
714
gst_avi_demux_stream_init_pull (GstAviDemux * avi)
715
716
717
718
{
  GstFlowReturn res;
  GstBuffer *buf = NULL;

719
720
  res = gst_pad_pull_range (avi->sinkpad, avi->offset, 12, &buf);
  if (res != GST_FLOW_OK)
721
    return res;
722
  else if (!gst_avi_demux_parse_file_header (GST_ELEMENT_CAST (avi), buf))
723
    goto wrong_header;
724
725
726
727

  avi->offset += 12;

  return GST_FLOW_OK;
728
729
730
731
732
733
734

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

737
738
739
/* AVI header handling */

/*
740
741
742
743
744
745
746
747
748
749
750
 * 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).
751
 */
752
static gboolean
753
754
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
755
{
756
  gst_riff_avih *avih;
757

758
759
760
761
762
  if (buf == NULL)
    goto no_buffer;

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

764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
  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
782
783

  /* debug stuff */
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
  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;
801
802
803
  gst_buffer_unref (buf);

  return TRUE;
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818

  /* 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;
  }
819
820
}

821
/*
822
 * gst_avi_demux_parse_superindex:
823
 * @avi: caller element (used for debugging/errors).
824
825
826
827
828
829
830
831
832
 * @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.
833
834
 */
static gboolean
835
gst_avi_demux_parse_superindex (GstAviDemux * avi,
836
    GstBuffer * buf, guint64 ** _indexes)
837
{
838
  guint8 *data;
839
840
  guint16 bpe = 16;
  guint32 num, i;
841
  guint64 *indexes;
842
  guint size;
843

844
845
  *_indexes = NULL;

846
847
  size = buf ? GST_BUFFER_SIZE (buf) : 0;
  if (size < 24)
848
    goto too_small;
849

850
851
  data = GST_BUFFER_DATA (buf);