gstavidemux.c 74.8 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
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
24

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
25
26
#include <string.h>

27
#include "gst/riff/riff-media.h"
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
28
#include "gstavidemux.h"
29
#include "avi-ids.h"
30
#include <gst/gst-i18n-plugin.h>
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
31

32
33
34
GST_DEBUG_CATEGORY_STATIC (avidemux_debug);
#define GST_CAT_DEFAULT avidemux_debug

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
35
36
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_EVENT);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
37
38
39
40
41
static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-msvideo")
    );
42

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
43
44
45
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);
46

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
47
static void gst_avi_demux_reset (GstAviDemux * avi);
48

49
#if 0
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
50
static const GstEventMask *gst_avi_demux_get_event_mask (GstPad * pad);
51
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
52
static gboolean gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event);
53
54

#if 0
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
55
static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad);
56
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
57
static const GstQueryType *gst_avi_demux_get_src_query_types (GstPad * pad);
58
static gboolean gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
59
60
61
static gboolean gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
Wim Taymans's avatar
Wim Taymans committed
62

63
static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi, gboolean update);
64
65
66
67
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,
    gboolean active);
68
69
static GstStateChangeReturn gst_avi_demux_change_state (GstElement * element,
    GstStateChange transition);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
70

71
static GstElementClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
72

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
73
GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
74
gst_avi_demux_get_type (void)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
75
76
77
78
79
{
  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
80
      sizeof (GstAviDemuxClass),
81
      (GBaseInitFunc) gst_avi_demux_base_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
82
      NULL,
83
      (GClassInitFunc) gst_avi_demux_class_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
84
85
      NULL,
      NULL,
86
      sizeof (GstAviDemux),
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
87
      0,
88
      (GInstanceInitFunc) gst_avi_demux_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
89
    };
90
91

    avi_demux_type =
92
        g_type_register_static (GST_TYPE_ELEMENT,
93
        "GstAviDemux", &avi_demux_info, 0);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
94
  }
95

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
96
97
98
  return avi_demux_type;
}

99
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
100
gst_avi_demux_base_init (GstAviDemuxClass * klass)
101
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
102
103
104
105
106
107
108
  static GstElementDetails gst_avi_demux_details =
      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>");
109
110
111
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstPadTemplate *videosrctempl, *audiosrctempl;
  GstCaps *audcaps, *vidcaps;
112

113
  audcaps = gst_riff_create_audio_template_caps ();
114
  gst_caps_append (audcaps, gst_caps_new_simple ("audio/x-avi-unknown", NULL));
115
  audiosrctempl = gst_pad_template_new ("audio_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
116
      GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps);
117

David Schleef's avatar
David Schleef committed
118
119
  vidcaps = gst_riff_create_video_template_caps ();
  gst_caps_append (vidcaps, gst_riff_create_iavs_template_caps ());
120
  gst_caps_append (vidcaps, gst_caps_new_simple ("video/x-avi-unknown", NULL));
121
  videosrctempl = gst_pad_template_new ("video_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
122
      GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps);
123

124
125
126
  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
127
      gst_static_pad_template_get (&sink_templ));
128
129
130
  gst_element_class_set_details (element_class, &gst_avi_demux_details);
}

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
131
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
132
gst_avi_demux_class_init (GstAviDemuxClass * klass)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
133
{
134
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
135

136
  GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
137
      0, "Demuxer for AVI streams");
138

139
  parent_class = g_type_class_peek_parent (klass);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
140

Wim Taymans's avatar
Wim Taymans committed
141
  gstelement_class->change_state = gst_avi_demux_change_state;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
142
143
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
144
145
static void
gst_avi_demux_init (GstAviDemux * avi)
146
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
147
148
149
  avi->sinkpad =
      gst_pad_new_from_template (gst_static_pad_template_get (&sink_templ),
      "sink");
150
151
152
  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);
153
  gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad);
Wim Taymans's avatar
Wim Taymans committed
154

155
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
156

157
158
  avi->index_entries = NULL;
  memset (&avi->stream, 0, sizeof (avi->stream));
Wim Taymans's avatar
Wim Taymans committed
159
160
}

161
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
162
gst_avi_demux_reset (GstAviDemux * avi)
163
{
164
  gint i;
165

166
167
  for (i = 0; i < avi->num_streams; i++) {
    g_free (avi->stream[i].strh);
168
    g_free (avi->stream[i].strf.data);
169
170
    if (avi->stream[i].name)
      g_free (avi->stream[i].name);
171
172
173
174
    if (avi->stream[i].initdata)
      gst_buffer_unref (avi->stream[i].initdata);
    if (avi->stream[i].extradata)
      gst_buffer_unref (avi->stream[i].extradata);
175
176
    if (avi->stream[i].pad)
      gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad);
177
178
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
179

180
181
182
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
183

184
  avi->state = GST_AVI_DEMUX_START;
185
  avi->offset = 0;
186

187
188
  g_free (avi->index_entries);
  avi->index_entries = NULL;
189
  avi->index_size = 0;
190
  avi->index_offset = 0;
191
  avi->current_entry = 0;
192
193
  g_free (avi->avih);
  avi->avih = NULL;
194

195
196
197
  if (avi->seek_event)
    gst_event_unref (avi->seek_event);
  avi->seek_event = NULL;
198
199
200
201
202

  avi->segment_rate = 1.0;
  avi->segment_flags = 0;
  avi->segment_start = -1;
  avi->segment_stop = -1;
203
204
}

205
static gst_avi_index_entry *
206
gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
207
{
208
209
  gint i;
  gst_avi_index_entry *entry = NULL;
210

211
212
  for (i = start; i < avi->index_size; i++) {
    entry = &avi->index_entries[i];
213
    if (entry->stream_nr == stream_nr)
Ronald S. Bultje's avatar
Ronald S. Bultje committed
214
      break;
215
  }
216

217
218
  return entry;
}
219

220
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
221
222
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
    gint stream_nr, guint64 time, guint32 flags)
223
224
225
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
226

227
228
  GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT " flags:%d",
      stream_nr, GST_TIME_ARGS (time), flags);
229
230
  i = -1;
  do {
231
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
232
233
    if (!entry)
      return NULL;
234

235
    i = entry->index_nr;
236

237
238
239
240
    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);
241
    if (entry->ts <= time && (entry->flags & flags) == flags)
242
      last_entry = entry;
243
  } while (entry->ts < time);
244

245
246
  return last_entry;
}
247

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
248

249
#if 0
250
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
251
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
252
{
253
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
254

255
256
257
258
259
260
261
262
263
264
265
  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
266

267
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
268
      src_a_formats : src_v_formats);
269
}
270
#endif
271

272
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
273
274
275
gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
276
277
{
  gboolean res = TRUE;
278
  GstAviDemux *avidemux = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
279

280
  avi_stream_context *stream = gst_pad_get_element_private (pad);
281

282
283
284
285
  GST_LOG_OBJECT (avidemux,
      "Received  src_format:%d, src_value:%lld, dest_format:%d", src_format,
      src_value, *dest_format);

286
287
  if (src_format == *dest_format) {
    *dest_value = src_value;
288
289
290
291
292
    goto done;
  }
  if (!stream->strh || !stream->strf.data) {
    res = FALSE;
    goto done;
293
  }
294
  if (stream->strh->type == GST_RIFF_FCC_vids &&
295
296
297
298
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) {
    res = FALSE;
    goto done;
  }
299

300
301
302
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
303
        case GST_FORMAT_BYTES:
304
305
306
          *dest_value =
              gst_util_uint64_scale_int (src_value, stream->strf.auds->av_bps,
              GST_SECOND);
307
308
          break;
        case GST_FORMAT_DEFAULT:
309
310
          *dest_value = gst_util_uint64_scale (src_value, stream->strh->rate,
              stream->strh->scale * GST_SECOND);
311
312
313
314
          break;
        default:
          res = FALSE;
          break;
315
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
316
      break;
317
318
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
319
        case GST_FORMAT_TIME:
320
          if (stream->strf.auds->av_bps != 0) {
321
322
            *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
                stream->strf.auds->av_bps);
323
324
          } else
            res = FALSE;
325
326
327
328
          break;
        default:
          res = FALSE;
          break;
329
330
      }
      break;
331
332
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
333
        case GST_FORMAT_TIME:
334
335
336
          *dest_value =
              gst_util_uint64_scale (src_value,
              stream->strh->scale * GST_SECOND, stream->strh->rate);
337
338
339
340
          break;
        default:
          res = FALSE;
          break;
341
      }
Wim Taymans's avatar
Wim Taymans committed
342
343
      break;
    default:
344
      res = FALSE;
345
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
346

347
348
349
350
done:
  GST_LOG_OBJECT (avidemux, "Returning res:%d dest_format:%d dest_value:%lld",
      res, *dest_format, *dest_value);
  gst_object_unref (avidemux);
351
  return res;
352
353
}

354
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
355
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
356
{
357
358
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
359
    GST_QUERY_DURATION,
360
361
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
362

363
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
364
365
}

366
static gboolean
367
gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
368
{
369
  gboolean res = TRUE;
370
  GstAviDemux *demux = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
371

372
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
373

374
375
376
377
378
  if (!stream->strh || !stream->strf.data)
    return FALSE;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:{
Wim Taymans's avatar
Wim Taymans committed
379
      gint64 pos = 0;
380
381
382

      if (stream->strh->type == GST_RIFF_FCC_auds) {
        if (!stream->strh->samplesize) {
383
384
          pos = gst_util_uint64_scale_int ((gint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
385
        } else if (stream->strf.auds->av_bps != 0) {
386
387
          pos = gst_util_uint64_scale_int (stream->current_byte, GST_SECOND,
              stream->strf.auds->av_bps);
388
389
390
391
392
393
        } else if (stream->total_frames != 0 && stream->total_bytes != 0) {
          /* calculate timestamps based on video size */
          guint64 xlen = demux->avih->us_frame *
              demux->avih->tot_frames * GST_USECOND;

          if (!stream->strh->samplesize)
394
395
            pos = gst_util_uint64_scale_int (xlen, stream->current_frame,
                stream->total_frames);
396
          else
397
398
            pos = gst_util_uint64_scale_int (xlen, stream->current_byte,
                stream->total_bytes);
399
        } else {
400
          res = FALSE;
401
402
403
        }
      } else {
        if (stream->strh->rate != 0) {
404
405
406
          pos =
              gst_util_uint64_scale_int ((guint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
407
408
409
        } else {
          pos = stream->current_frame * demux->avih->us_frame * GST_USECOND;
        }
Wim Taymans's avatar
Wim Taymans committed
410
      }
411
      if (res)
Wim Taymans's avatar
Wim Taymans committed
412
413
414
415
416
417
418
        gst_query_set_position (query, GST_FORMAT_TIME, pos);
      break;
    }
    case GST_QUERY_DURATION:
    {
      gint64 len;

419
420
421
      len =
          gst_util_uint64_scale ((guint64) stream->strh->length *
          stream->strh->scale, GST_SECOND, stream->strh->rate);
Wim Taymans's avatar
Wim Taymans committed
422
      gst_query_set_duration (query, GST_FORMAT_TIME, len);
Wim Taymans's avatar
Wim Taymans committed
423
      break;
424
    }
Wim Taymans's avatar
Wim Taymans committed
425
    default:
426
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
427
428
429
430
431
432
      break;
  }

  return res;
}

433
#if 0
434
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
435
gst_avi_demux_get_event_mask (GstPad * pad)
436
437
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
438
439
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
440
441
442
443
  };

  return masks;
}
444
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
445

Wim Taymans's avatar
Wim Taymans committed
446
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
447
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
448
449
{
  gboolean res = TRUE;
450
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Wim Taymans's avatar
Wim Taymans committed
451
  avi_stream_context *stream;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
452

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
453
  GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi,
454
      "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
455
456
  if (!avi->index_entries) {
    GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi, "no index entries, returning");
457
    return FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
458
  }
459

Wim Taymans's avatar
Wim Taymans committed
460
461
462
463
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
464
    {
465
466
      /* FIXME, this seeking code is not correct, look at wavparse for 
       * a better example */
467
468
      GstFormat format;
      GstSeekFlags flags;
469
470
471
472
473
474
475
476
477
478
479
480
      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);

481
482
483
      GST_DEBUG_OBJECT (avi,
          "seek format %d, flags:%d, %08x, start:%lld, stop:%lld", format,
          flags, stream->strh->type, start, stop);
484
485
486
487
488
489
490
491
492
493
494

      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;
      }
495

496
497
498
      duration =
          gst_util_uint64_scale ((guint64) stream->strh->length *
          stream->strh->scale, GST_SECOND, stream->strh->rate);
499

500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
      switch (start_type) {
        case GST_SEEK_TYPE_CUR:
          tstart = avi->segment_start + tstart;
          break;
        case GST_SEEK_TYPE_END:
          tstart = duration + tstart;
          break;
        case GST_SEEK_TYPE_NONE:
          tstart = avi->segment_start;
          update_start = FALSE;
          break;
        case GST_SEEK_TYPE_SET:
          break;
      }
      tstart = CLAMP (tstart, 0, duration);
515

516
517
518
      switch (stop_type) {
        case GST_SEEK_TYPE_CUR:
          tstop = avi->segment_stop + tstop;
519
          break;
520
521
522
523
524
525
526
527
        case GST_SEEK_TYPE_END:
          tstop = duration + tstop;
          break;
        case GST_SEEK_TYPE_NONE:
          tstop = avi->segment_stop;
          update_stop = FALSE;
          break;
        case GST_SEEK_TYPE_SET:
528
          break;
Wim Taymans's avatar
Wim Taymans committed
529
      }
530
531
532
533
534
535
536
537
538
      tstop = CLAMP (tstop, 0, duration);

      /* now store the values */
      avi->segment_rate = rate;
      avi->segment_flags = flags;
      avi->segment_start = tstart;
      avi->segment_stop = tstop;

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

541
    }
Wim Taymans's avatar
Wim Taymans committed
542
543
544
545
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
546

547
done:
Wim Taymans's avatar
Wim Taymans committed
548
549
  gst_event_unref (event);

Wim Taymans's avatar
Wim Taymans committed
550
551
552
  return res;
}

553
554
555
556
557
558
559
560
561
562
/**
 * 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).
563
564
 */

565
static gboolean
566
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
567
{
568
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
569

570
  if (!gst_riff_parse_file_header (element, buf, &doctype))
571
    return FALSE;
572

573
574
575
576
577
578
579
580
  if (doctype != GST_RIFF_RIFF_AVI)
    goto not_avi;

  return TRUE;

  /* ERRORS */
not_avi:
  {
581
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
582
        ("File is not an AVI file: %" GST_FOURCC_FORMAT,
583
            GST_FOURCC_ARGS (doctype)));
584
585
586
587
    return FALSE;
  }
}

588
589
590
591
592
593
594
595
596
597
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))
598
    goto wrong_header;
599
600
601
602

  avi->offset += 12;

  return GST_FLOW_OK;
603
604
605
606
607
608
609
610

  /* ERRORS */
wrong_header:
  {
    GST_DEBUG_OBJECT (avi, "error parsing file header");
    gst_buffer_unref (buf);
    return GST_FLOW_ERROR;
  }
611
612
613
614
615
616
617
618
619
620
621
622
623
624
}

/**
 * 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).
625
626
 */

627
static gboolean
628
629
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
630
{
631
  gst_riff_avih *avih;
632

633
634
635
636
637
  if (buf == NULL)
    goto no_buffer;

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

639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
  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
657
658

  /* debug stuff */
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
  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;
676
677
678
  gst_buffer_unref (buf);

  return TRUE;
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693

  /* 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;
  }
694
695
}

696
697
698
699
700
701
702
703
704
705
706
707
/**
 * 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.
708
709
710
 */

static gboolean
711
712
gst_avi_demux_parse_superindex (GstElement * element,
    GstBuffer * buf, guint64 ** _indexes)
713
{
714
  guint8 *data = GST_BUFFER_DATA (buf);
715
716
717
  gint bpe = 16, num, i;
  guint64 *indexes;

718
719
  *_indexes = NULL;

720
721
722
723
724
  if (buf == NULL)
    goto no_buffer;

  if (!buf || GST_BUFFER_SIZE (buf) < 24)
    goto too_small;
725
726
727
728
729
730

  /* 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) {
731
732
    GST_WARNING_OBJECT (element,
        "Superindex for stream %d has unexpected "
733
734
735
736
737
738
739
740
741
742
743
744
        "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]);
  }
745
746
  indexes[i] = GST_BUFFER_OFFSET_NONE;
  *_indexes = indexes;
747
748
749
750

  gst_buffer_unref (buf);

  return TRUE;
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765

  /* 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;
  }
766
767
}

768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
/**
 * 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.
 */

786
static gboolean
787
788
gst_avi_demux_parse_subindex (GstElement * element,
    GstBuffer * buf, avi_stream_context * stream, GList ** _entries_list)
789
{
790
791
792
  guint8 *data = GST_BUFFER_DATA (buf);
  gint bpe, num, x;
  guint64 baseoff;
793
  gst_avi_index_entry *entries, *entry;
794
  GList *entries_list = NULL;
795
  GstFormat format = GST_FORMAT_TIME;
796
  gint64 tmp;
797

798
  /* check size */
799
800
801
802
803
  if (buf == NULL)
    goto no_buffer;

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

805
  /* We don't support index-data yet */
806
807
  if (data[3] & 0x80)
    goto not_implemented;
808

809
810
811
812
813
814
815
816
817
818
819
820
821
822
  /* 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]);
823

824
825
826
  entries = g_new (gst_avi_index_entry, num);
  for (x = 0; x < num; x++) {
    entry = &entries[x];
827

828
829
    if (GST_BUFFER_SIZE (buf) < 24 + bpe * (x + 1))
      break;
830

831
832
833
834
835
836
837
    /* 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;
838

839
840
841
842
    /* timestamps */
    if (stream->strh->samplesize && stream->strh->type == GST_RIFF_FCC_auds) {
      /* constant rate stream */
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES,
843
844
          stream->total_bytes, &format, &tmp);
      entry->ts = tmp;
845
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES,
846
847
          stream->total_bytes + entry->size, &format, &tmp);
      entry->dur = tmp;
848
849
850
    } else {
      /* VBR stream */
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
851
852
          stream->total_frames, &format, &tmp);
      entry->ts = tmp;
853
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
854
855
          stream->total_frames + 1, &format, &tmp);
      entry->dur = tmp;
856
857
858
859
860
861
862
863
    }
    entry->dur -= entry->ts;

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

865
866
    entries_list = g_list_prepend (entries_list, entry);
  }
867

868
  GST_LOG_OBJECT (element, "Read %d index entries", x);
869

870
871
872
873
874
875
876
877
  gst_buffer_unref (buf);

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

879
  return TRUE;
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902

  /* 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;
  }
903
}
904

905
906
907
908
909
910
911
912
static void
gst_avi_demux_read_subindexes (GstAviDemux * avi,
    GList ** index, GList ** alloc_list)
{
  GList *list;
  guint32 tag;
  GstBuffer *buf;
  gint i, n;
913

914
915
  for (n = 0; n < avi->num_streams; n++) {
    avi_stream_context *stream = &avi->stream[n];
916

917
918
919
920
921
922
923
    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)) {
        GST_ERROR_OBJECT (GST_ELEMENT (avi),
924
925
            "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
            GST_FOURCC_ARGS (tag));
926
927
928
929
930
931
932
933
934
935
        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);
      }
936
937
938
939
940
941
942
    }

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

943
944
945
946
947
948
949
950
951
952
953
954
/**
 * 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.
955
956
957
 */

static gboolean
958
gst_avi_demux_parse_stream (GstElement * element, GstBuffer * buf)
959
{
960
961
962
963
964
965
966
967
968
  GstAviDemux *avi = GST_AVI_DEMUX (element);
  avi_stream_context *stream = &avi->stream[avi->num_streams];
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
  GstP