gstavidemux.c 79.4 KB
Newer Older
1
2
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@temple-baptist.com>
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
19
/* Element-Checklist-Version: 5 */
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
20

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

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

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

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

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

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

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

67
68
69
static void gst_avi_demux_base_init (GstAviDemuxClass * klass);
static void gst_avi_demux_class_init (GstAviDemuxClass * klass);
static void gst_avi_demux_init (GstAviDemux * avi);
70

71
static void gst_avi_demux_reset (GstAviDemux * avi);
72

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

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

static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi, gboolean update);
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,
90
    gboolean active);
91
92
static GstStateChangeReturn gst_avi_demux_change_state (GstElement * element,
    GstStateChange transition);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
93

94
static GstElementClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
95

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

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

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
119
120
121
  return avi_demux_type;
}

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

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

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

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

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

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

162
  parent_class = g_type_class_peek_parent (klass);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
163

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
167
168
static void
gst_avi_demux_init (GstAviDemux * avi)
169
{
170
  avi->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink");
171
172
173
  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);
174
  gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad);
Wim Taymans's avatar
Wim Taymans committed
175

176
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
177

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

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

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

205
206
207
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
208

209
  avi->state = GST_AVI_DEMUX_START;
210
  avi->offset = 0;
211

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

220
221
222
  if (avi->seek_event)
    gst_event_unref (avi->seek_event);
  avi->seek_event = NULL;
223

224
225
226
227
  if (avi->globaltags)
    gst_tag_list_free (avi->globaltags);
  avi->globaltags = NULL;

228
229
  avi->got_tags = FALSE;

230
  gst_segment_init (&avi->segment, GST_FORMAT_TIME);
231
232
}

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

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

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

248
  return result;
249
}
250

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

258
259
  GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT " flags:%d",
      stream_nr, GST_TIME_ARGS (time), flags);
260
261
  i = -1;
  do {
262
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
263
264
    if (!entry)
      return NULL;
265

266
    i = entry->index_nr;
267

268
269
270
271
    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);
272
    if (entry->ts <= time && (entry->flags & flags) == flags)
273
      last_entry = entry;
274
  } while (entry->ts < time);
275

276
277
  return last_entry;
}
278

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

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

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

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

310
  avi_stream_context *stream = gst_pad_get_element_private (pad);
311

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

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

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

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

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

393
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
394
395
}

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

402
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
403

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

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

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

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

      /* 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;
Wim Taymans's avatar
Wim Taymans committed
461

462
463
464
465
466
467
        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
468
      break;
469
    }
Wim Taymans's avatar
Wim Taymans committed
470
    default:
471
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
472
473
474
475
476
477
      break;
  }

  return res;
}

478
#if 0
479
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
480
gst_avi_demux_get_event_mask (GstPad * pad)
481
482
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
483
484
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
485
486
487
488
  };

  return masks;
}
489
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
490

Wim Taymans's avatar
Wim Taymans committed
491
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
492
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
493
494
{
  gboolean res = TRUE;
495
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Wim Taymans's avatar
Wim Taymans committed
496
  avi_stream_context *stream;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
497

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
498
  GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi,
499
      "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
500
501
  if (!avi->index_entries) {
    GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi, "no index entries, returning");
502
    return FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
503
  }
504

Wim Taymans's avatar
Wim Taymans committed
505
506
507
508
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
509
    {
510
511
      /* FIXME, this seeking code is not correct, look at wavparse for 
       * a better example */
512
513
      GstFormat format;
      GstSeekFlags flags;
514
515
516
517
518
519
520
521
522
523
524
525
      gdouble rate;
      gint64 start, stop;
      gint64 tstart, tstop;
      gint64 duration;
      GstFormat tformat = GST_FORMAT_TIME;
      GstSeekType start_type, stop_type;
      gboolean update_start = TRUE;
      gboolean update_stop = TRUE;

      gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
          &stop_type, &stop);

526
527
528
      GST_DEBUG_OBJECT (avi,
          "seek format %d, flags:%d, %08x, start:%lld, stop:%lld", format,
          flags, stream->strh->type, start, stop);
529
530
531
532
533
534
535
536
537
538
539

      if (format != GST_FORMAT_TIME) {
        res &=
            gst_avi_demux_src_convert (pad, format, start, &tformat, &tstart);
        res &= gst_avi_demux_src_convert (pad, format, stop, &tformat, &tstop);
        if (!res)
          goto done;
      } else {
        tstart = start;
        tstop = stop;
      }
540

541
542
543
544
      if (!gst_pad_query_duration (stream->pad, &tformat, &duration)) {
        res = FALSE;
        goto done;
      }
545

546
547
      switch (start_type) {
        case GST_SEEK_TYPE_CUR:
548
          tstart = avi->segment.start + tstart;
549
550
551
552
553
          break;
        case GST_SEEK_TYPE_END:
          tstart = duration + tstart;
          break;
        case GST_SEEK_TYPE_NONE:
554
          tstart = avi->segment.start;
555
556
557
558
559
560
          update_start = FALSE;
          break;
        case GST_SEEK_TYPE_SET:
          break;
      }
      tstart = CLAMP (tstart, 0, duration);
561

562
563
      switch (stop_type) {
        case GST_SEEK_TYPE_CUR:
564
          tstop = avi->segment.stop + tstop;
565
          break;
566
567
568
569
        case GST_SEEK_TYPE_END:
          tstop = duration + tstop;
          break;
        case GST_SEEK_TYPE_NONE:
570
          tstop = avi->segment.stop;
571
572
573
          update_stop = FALSE;
          break;
        case GST_SEEK_TYPE_SET:
574
          break;
Wim Taymans's avatar
Wim Taymans committed
575
      }
576
577
578
      tstop = CLAMP (tstop, 0, duration);

      /* now store the values */
579
580
581
582
      avi->segment.rate = rate;
      avi->segment.flags = flags;
      avi->segment.start = tstart;
      avi->segment.stop = tstop;
583
584

      gst_avi_demux_handle_seek (avi, update_start || update_stop);
Wim Taymans's avatar
Wim Taymans committed
585
      break;
586

587
    }
Wim Taymans's avatar
Wim Taymans committed
588
589
590
591
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
592

593
done:
Wim Taymans's avatar
Wim Taymans committed
594
595
  gst_event_unref (event);

596
  GST_DEBUG_OBJECT (avi, "returning %d", res);
Wim Taymans's avatar
Wim Taymans committed
597
598
599
  return res;
}

600
601
602
603
604
605
606
607
608
609
/**
 * 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
 * bytes long. Discards buffer after use.
 *
 * Returns: TRUE if the file is a RIFF/AVI file, FALSE otherwise.
 *          Throws an error, caller should error out (fatal).
610
611
 */

612
static gboolean
613
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
614
{
615
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
616

617
  if (!gst_riff_parse_file_header (element, buf, &doctype))
618
    return FALSE;
619

620
621
622
623
624
625
626
627
  if (doctype != GST_RIFF_RIFF_AVI)
    goto not_avi;

  return TRUE;

  /* ERRORS */
not_avi:
  {
628
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
629
        ("File is not an AVI file: %" GST_FOURCC_FORMAT,
630
            GST_FOURCC_ARGS (doctype)));
631
632
633
634
    return FALSE;
  }
}

635
636
637
638
639
640
641
642
643
644
static GstFlowReturn
gst_avi_demux_stream_init (GstAviDemux * avi)
{
  GstFlowReturn res;
  GstBuffer *buf = NULL;

  if ((res = gst_pad_pull_range (avi->sinkpad,
              avi->offset, 12, &buf)) != GST_FLOW_OK)
    return res;
  else if (!gst_avi_demux_parse_file_header (GST_ELEMENT (avi), buf))
645
    goto wrong_header;
646
647
648
649

  avi->offset += 12;

  return GST_FLOW_OK;
650
651
652
653
654
655
656
657

  /* ERRORS */
wrong_header:
  {
    GST_DEBUG_OBJECT (avi, "error parsing file header");
    gst_buffer_unref (buf);
    return GST_FLOW_ERROR;
  }
658
659
660
661
662
663
664
665
666
667
668
669
670
671
}

/**
 * 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).
672
673
 */

674
static gboolean
675
676
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
677
{
678
  gst_riff_avih *avih;
679

680
681
682
683
684
  if (buf == NULL)
    goto no_buffer;

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

686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
  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
704
705

  /* debug stuff */
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
  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;
723
724
725
  gst_buffer_unref (buf);

  return TRUE;
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740

  /* 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;
  }
741
742
}

743
744
745
746
747
748
749
750
751
752
753
754
/**
 * 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.
755
756
757
 */

static gboolean
758
759
gst_avi_demux_parse_superindex (GstElement * element,
    GstBuffer * buf, guint64 ** _indexes)
760
{
761
  guint8 *data;
762
763
764
  gint bpe = 16, num, i;
  guint64 *indexes;

765
766
  *_indexes = NULL;

767
768
769
  if (buf == NULL)
    goto no_buffer;

770
  if (GST_BUFFER_SIZE (buf) < 24)
771
    goto too_small;
772

773
774
  data = GST_BUFFER_DATA (buf);

775
776
777
778
779
  /* 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) {
780
781
    GST_WARNING_OBJECT (element,
        "Superindex for stream %d has unexpected "
782
783
784
785
786
787
788
789
790
791
792
793
        "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]);
  }
794
795
  indexes[i] = GST_BUFFER_OFFSET_NONE;
  *_indexes = indexes;
796
797
798
799

  gst_buffer_unref (buf);

  return TRUE;
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814

  /* 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;
  }
815
816
}

817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
/**
 * 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.
 */

835
static gboolean
836
837
gst_avi_demux_parse_subindex (GstElement * element,
    GstBuffer * buf, avi_stream_context * stream, GList ** _entries_list)
838
{
839
840
841
  guint8 *data = GST_BUFFER_DATA (buf);
  gint bpe, num, x;
  guint64 baseoff;
842
  gst_avi_index_entry *entries, *entry;
843
  GList *entries_list = NULL;
844
845
  GstFormat format = GST_FORMAT_TIME;

846
  /* check size */
847
848
849
850
851
  if (buf == NULL)
    goto no_buffer;

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

853
  /* We don't support index-data yet */
854
855
  if (data[3] & 0x80)
    goto not_implemented;
856

857
858
859
860
861
862
863
864
865
866
867
868
869
870
  /* 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]);
871

872
873
  entries = g_new (gst_avi_index_entry, num);
  for (x = 0; x < num; x++) {
874
875
    gint64 next_ts;

876
    entry = &entries[x];
877

878
879
    if (GST_BUFFER_SIZE (buf) < 24 + bpe * (x + 1))
      break;
880

881
882
883
884
885
886
887
    /* 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;
888

889
    /* timestamps */
890
891
892
    entry->ts = stream->total_time;
    if (stream->is_vbr) {
      /* VBR get next timestamp */
893
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
894
895
896
897
898
          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);
899
    }
900
901
    /* duration is next - current */
    entry->dur = next_ts - entry->ts;
902
903
904
905

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

    stream->total_bytes += entry->size;
908
    stream->total_frames++;
909
    stream->total_time = next_ts;
910

911
912
    entries_list = g_list_prepend (entries_list, entry);
  }
913

914
  GST_LOG_OBJECT (element, "Read %d index entries", x);
915

916
917
918
919
920
921
922
923
  gst_buffer_unref (buf);

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

925
  return TRUE;
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948

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

951
952
953
954
955
956
957
958
static void
gst_avi_demux_read_subindexes (GstAviDemux * avi,
    GList ** index, GList ** alloc_list)
{
  GList *list;
  guint32 tag;
  GstBuffer *buf;
  gint i, n;
959

960
961
  GST_DEBUG_OBJECT (avi, "gst_avi_demux_read_subindexes");

962
963
  for (n = 0; n < avi->num_streams; n++) {
    avi_stream_context *stream = &avi->stream[n];
964

965
966
967
968
969
970
    for (i = 0; stream->indexes[i] != GST_BUFFER_OFFSET_NONE; i++) {
      if (gst_riff_read_chunk (GST_ELEMENT (avi), avi->sinkpad,
              &stream->indexes[i], &tag, &buf) != GST_FLOW_OK)
        continue;
      else if (tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream->num / 10,
              '0' + stream->num % 10)) {
971
        GST_WARNING_OBJECT (GST_ELEMENT (avi),
972
973
            "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
            GST_FOURCC_ARGS (tag));
974
975
976
977
978
979
980
981
982
983
        gst_buffer_unref (buf);
        continue;
      }

      if (!gst_avi_demux_parse_subindex (GST_ELEMENT (avi), buf, stream, &list))
        continue;
      if (list) {
        *alloc_list = g_list_append (*alloc_list, list->data);
        *index = g_list_concat (*index, list);
      }
984
985
986
987
988
989
990
    }

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

991
992
993
994
995
996
997
998
999
1000
1001
1002
/**
 * gst_avi_demux_parse_stream:
 * @element: calling element (used for debugging/errors).
 * @buf: input buffer used to parse the stream.
 *
 * Parses all subchunks in a strl chunk (which defines a single
 * stream). Discards the buffer after use. This function will
 * increment the stream counter internally.
 *
 * Returns: whether the stream was identified successfully.
 *          Errors are not fatal. It does indicate the stream
 *          was skipped.
1003
1004
1005
 */

static gboolean
1006
gst_avi_demux_parse_stream (GstElement * element, GstBuffer * buf)
1007
{
1008
1009
1010
1011
1012
1013
1014
1015
1016
  GstAviDemux *avi = GST_AVI_DEMUX (element);
  avi_stream_context *stream = &avi->stream[avi->num_streams];
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
  GstPadTemplate *templ;
  GstBuffer *sub = NULL;
  guint offset = 4;
  guint32 tag = 0;
  gchar *codec_name = NULL, *padname = NULL;
  const gchar *tag_name;
1017
1018
1019
  GstCaps *caps = NULL;
  GstPad *pad;

1020
1021
1022
1023
1024
1025
  GST_DEBUG_OBJECT (element, "Parsing stream");

  /* read strh */
  if (!buf || !gst_riff_parse_chunk (element, buf, &offset, &tag, &sub) ||
      tag != GST_RIFF_TAG_strh) {
    GST_ERROR_OBJECT (element,
1026
        "Failed to find strh chunk (tag: %" GST_FOURCC_FORMAT ")",
1027
1028
1029