gstavidemux.c 67.7 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
177
    gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad);
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
178

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

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

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

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

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

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

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

216
217
  return entry;
}
218

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

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

234
    i = entry->index_nr;
235

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

244
245
  return last_entry;
}
246

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
247

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

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

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

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

279
  avi_stream_context *stream = gst_pad_get_element_private (pad);
280

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

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

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

343
344
345
346
done:
  GST_LOG_OBJECT (avidemux, "Returning res:%d dest_format:%d dest_value:%lld",
      res, *dest_format, *dest_value);
  gst_object_unref (avidemux);
347
  return res;
348
349
}

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

359
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
360
361
}

362
static gboolean
363
gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
364
{
365
  gboolean res = TRUE;
366
  GstAviDemux *demux = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
367

368
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
369

370
371
372
373
374
  if (!stream->strh || !stream->strf.data)
    return FALSE;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:{
Wim Taymans's avatar
Wim Taymans committed
375
      gint64 pos = 0;
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390

      if (stream->strh->type == GST_RIFF_FCC_auds) {
        if (!stream->strh->samplesize) {
          pos = GST_SECOND * stream->current_frame *
              stream->strh->scale / stream->strh->rate;
        } else if (stream->strf.auds->av_bps != 0) {
          pos = ((gfloat) stream->current_byte) * GST_SECOND /
              stream->strf.auds->av_bps;
        } 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)
            pos = xlen * stream->current_frame / stream->total_frames;
391
          else
392
393
            pos = xlen * stream->current_byte / stream->total_bytes;
        } else {
394
          res = FALSE;
395
396
397
398
399
400
401
402
        }
      } else {
        if (stream->strh->rate != 0) {
          pos = ((gfloat) stream->current_frame * stream->strh->scale *
              GST_SECOND / stream->strh->rate);
        } else {
          pos = stream->current_frame * demux->avih->us_frame * GST_USECOND;
        }
Wim Taymans's avatar
Wim Taymans committed
403
      }
404
      if (res)
Wim Taymans's avatar
Wim Taymans committed
405
406
407
408
409
410
411
412
413
414
        gst_query_set_position (query, GST_FORMAT_TIME, pos);
      break;
    }
    case GST_QUERY_DURATION:
    {
      gint64 len;

      len = (((gfloat) stream->strh->scale) * stream->strh->length /
          stream->strh->rate) * GST_SECOND;
      gst_query_set_duration (query, GST_FORMAT_TIME, len);
Wim Taymans's avatar
Wim Taymans committed
415
      break;
416
    }
Wim Taymans's avatar
Wim Taymans committed
417
418
419
420
421
422
423
424
    default:
      res = FALSE;
      break;
  }

  return res;
}

425
#if 0
426
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
427
gst_avi_demux_get_event_mask (GstPad * pad)
428
429
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
430
431
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
432
433
434
435
  };

  return masks;
}
436
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
437

Wim Taymans's avatar
Wim Taymans committed
438
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
439
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
440
441
{
  gboolean res = TRUE;
442
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Wim Taymans's avatar
Wim Taymans committed
443
  avi_stream_context *stream;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
444

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
445
446
447
448
  GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi,
      "have event type %d: %p on src pad", GST_EVENT_TYPE (event), event);
  if (!avi->index_entries) {
    GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi, "no index entries, returning");
449
    return FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
450
  }
451

Wim Taymans's avatar
Wim Taymans committed
452
453
454
455
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
456
457
458
    {
      GstFormat format;
      GstSeekFlags flags;
459
460
461
462
463
464
465
466
467
468
469
470
      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);

471
472
473
      GST_DEBUG_OBJECT (avi,
          "seek format %d, flags:%d, %08x, start:%lld, stop:%lld", format,
          flags, stream->strh->type, start, stop);
474
475
476
477
478
479
480
481
482
483
484

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

486
487
      duration = (((gfloat) stream->strh->scale) * stream->strh->length /
          stream->strh->rate) * GST_SECOND;
488

489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
      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);
504

505
506
507
      switch (stop_type) {
        case GST_SEEK_TYPE_CUR:
          tstop = avi->segment_stop + tstop;
508
          break;
509
510
511
512
513
514
515
516
        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:
517
          break;
Wim Taymans's avatar
Wim Taymans committed
518
      }
519
520
521
522
523
524
525
526
527
      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
528
      break;
529

530
    }
Wim Taymans's avatar
Wim Taymans committed
531
532
533
534
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
535

536
done:
Wim Taymans's avatar
Wim Taymans committed
537
538
  gst_event_unref (event);

Wim Taymans's avatar
Wim Taymans committed
539
540
541
  return res;
}

542
543
544
545
546
547
548
549
550
551
/**
 * 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).
552
553
 */

554
static gboolean
555
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
556
{
557
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
558

559
  if (!gst_riff_parse_file_header (element, buf, &doctype))
560
    return FALSE;
561

562
  if (doctype != GST_RIFF_RIFF_AVI) {
563
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
564
        ("File is not an AVI file: %" GST_FOURCC_FORMAT,
565
            GST_FOURCC_ARGS (doctype)));
566
567
    return FALSE;
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
568

569
570
571
  return TRUE;
}

572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
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))
    return GST_FLOW_ERROR;

  avi->offset += 12;

  return GST_FLOW_OK;
}

/**
 * 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).
601
602
 */

603
static gboolean
604
605
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
606
{
607
  gst_riff_avih *avih;
608

609
610
611
612
613
614
  if (!buf || GST_BUFFER_SIZE (buf) < sizeof (gst_riff_avih)) {
    GST_ELEMENT_ERROR (element, STREAM, DEMUX, (NULL),
        ("Too small avih (%d available, %d needed)",
            GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_avih)));
    if (buf)
      gst_buffer_unref (buf);
615
616
617
    return FALSE;
  }

618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
  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
636
637

  /* debug stuff */
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
  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;
655
656
657
658
659
  gst_buffer_unref (buf);

  return TRUE;
}

660
661
662
663
664
665
666
667
668
669
670
671
/**
 * 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.
672
673
674
 */

static gboolean
675
676
gst_avi_demux_parse_superindex (GstElement * element,
    GstBuffer * buf, guint64 ** _indexes)
677
{
678
  guint8 *data = GST_BUFFER_DATA (buf);
679
680
681
  gint bpe = 16, num, i;
  guint64 *indexes;

682
683
684
685
686
687
688
689
  *_indexes = NULL;

  if (!buf || GST_BUFFER_SIZE (buf) < 24) {
    GST_ERROR_OBJECT (element,
        "Not enough data to parse superindex (%d available, %d needed)",
        GST_BUFFER_SIZE (buf), 24);
    if (buf)
      gst_buffer_unref (buf);
690
691
692
693
694
695
696
697
    return FALSE;
  }

  /* 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) {
698
699
    GST_WARNING_OBJECT (element,
        "Superindex for stream %d has unexpected "
700
701
702
703
704
705
706
707
708
709
710
711
        "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]);
  }
712
713
  indexes[i] = GST_BUFFER_OFFSET_NONE;
  *_indexes = indexes;
714
715
716
717
718
719

  gst_buffer_unref (buf);

  return TRUE;
}

720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
/**
 * 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.
 */

738
static gboolean
739
740
gst_avi_demux_parse_subindex (GstElement * element,
    GstBuffer * buf, avi_stream_context * stream, GList ** _entries_list)
741
{
742
743
744
  guint8 *data = GST_BUFFER_DATA (buf);
  gint bpe, num, x;
  guint64 baseoff;
745
  gst_avi_index_entry *entries, *entry;
746
  GList *entries_list = NULL;
747
  GstFormat format = GST_FORMAT_TIME;
748
  gint64 tmp;
749

750
751
752
753
754
755
756
757
758
759
  /* check size */
  if (!buf || GST_BUFFER_SIZE (buf) < 24) {
    GST_ERROR_OBJECT (element,
        "Not enough data to parse subindex (%d available, %d needed)",
        GST_BUFFER_SIZE (buf), 24);
    if (buf)
      gst_buffer_unref (buf);
    *_entries_list = NULL;
    return TRUE;                /* continue */
  }
760

761
762
763
764
765
766
767
  /* We don't support index-data yet */
  if (data[3] & 0x80) {
    GST_ELEMENT_ERROR (element, STREAM, NOT_IMPLEMENTED, (NULL),
        ("Subindex-is-data is not implemented"));
    gst_buffer_unref (buf);
    return FALSE;
  }
768

769
770
771
772
773
774
775
776
777
778
779
780
781
782
  /* 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]);
783

784
785
786
  entries = g_new (gst_avi_index_entry, num);
  for (x = 0; x < num; x++) {
    entry = &entries[x];
787

788
789
    if (GST_BUFFER_SIZE (buf) < 24 + bpe * (x + 1))
      break;
790

791
792
793
794
795
796
797
    /* 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;
798

799
800
801
802
    /* 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,
803
804
          stream->total_bytes, &format, &tmp);
      entry->ts = tmp;
805
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES,
806
807
          stream->total_bytes + entry->size, &format, &tmp);
      entry->dur = tmp;
808
809
810
    } else {
      /* VBR stream */
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
811
812
          stream->total_frames, &format, &tmp);
      entry->ts = tmp;
813
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
814
815
          stream->total_frames + 1, &format, &tmp);
      entry->dur = tmp;
816
817
818
819
820
821
822
823
    }
    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++;
824

825
826
    entries_list = g_list_prepend (entries_list, entry);
  }
827

828
  GST_LOG_OBJECT (element, "Read %d index entries", x);
829

830
831
832
833
834
835
836
837
  gst_buffer_unref (buf);

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

839
840
  return TRUE;
}
841

842
843
844
845
846
847
848
849
static void
gst_avi_demux_read_subindexes (GstAviDemux * avi,
    GList ** index, GList ** alloc_list)
{
  GList *list;
  guint32 tag;
  GstBuffer *buf;
  gint i, n;
850

851
852
  for (n = 0; n < avi->num_streams; n++) {
    avi_stream_context *stream = &avi->stream[n];
853

854
855
856
857
858
859
860
    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),
861
862
            "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
            GST_FOURCC_ARGS (tag));
863
864
865
866
867
868
869
870
871
872
        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);
      }
873
874
875
876
877
878
879
    }

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

880
881
882
883
884
885
886
887
888
889
890
891
/**
 * 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.
892
893
894
 */

static gboolean
895
gst_avi_demux_parse_stream (GstElement * element, GstBuffer * buf)
896
{
897
898
899
900
901
902
903
904
905
  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;
906
907
908
  GstCaps *caps = NULL;
  GstPad *pad;

909
910
911
912
913
914
  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,
915
        "Failed to find strh chunk (tag: %" GST_FOURCC_FORMAT ")",
916
917
918
919
920
921
922
923
924
        buf ? GST_BUFFER_SIZE (buf) : 0, GST_FOURCC_ARGS (tag));
    goto fail;
  } else if (!gst_riff_parse_strh (element, sub, &stream->strh))
    goto fail;

  /* read strf */
  if (!gst_riff_parse_chunk (element, buf, &offset, &tag, &sub) ||
      tag != GST_RIFF_TAG_strf) {
    GST_ERROR_OBJECT (element,
925
        "Failed to find strh chunk (size: %d, tag: %"
926
927
928
929
930
        GST_FOURCC_FORMAT ")", buf ? GST_BUFFER_SIZE (buf) : 0,
        GST_FOURCC_ARGS (tag));
    goto fail;
  } else {
    gboolean res = FALSE;
Wim Taymans's avatar
Wim Taymans committed
931

932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
    switch (stream->strh->type) {
      case GST_RIFF_FCC_vids:
        res = gst_riff_parse_strf_vids (element, sub,
            &stream->strf.vids, &stream->extradata);
        break;
      case GST_RIFF_FCC_auds:
        res = gst_riff_parse_strf_auds (element, sub,
            &stream->strf.auds, &stream->extradata);
        break;
      case GST_RIFF_FCC_iavs:
        res = gst_riff_parse_strf_iavs (element, sub,
            &stream->strf.iavs, &stream->extradata);
        break;
      default:
        GST_ERROR_OBJECT (element,
947
            "Don´t know how to handle stream type %" GST_FOURCC_FORMAT,
948
949
            GST_FOURCC_ARGS (stream->strh->type));
        break;
950
    }
Wim Taymans's avatar
Wim Taymans committed
951

952
953
954
955
956
957
    if (!res)
      goto fail;
  }

  /* read strd/strn */
  while (gst_riff_parse_chunk (element, buf, &offset, &tag, &sub)) {
958
    switch (tag) {
959
      case GST_RIFF_TAG_strd:
960
961
962
        if (stream->initdata)
          gst_buffer_unref (stream->initdata);
        stream->initdata = sub;
963
        break;
964
      case GST_RIFF_TAG_strn:
965
        g_free (stream->name);
966
        stream->name = g_new (gchar, GST_BUFFER_SIZE (sub) + 1);
967
968
969
        memcpy (stream->name, GST_BUFFER_DATA (sub), GST_BUFFER_SIZE (sub));
        stream->name[GST_BUFFER_SIZE (sub)] = '\0';
        gst_buffer_unref (sub);
970
        break;
971
      default:
972
        if (tag == GST_MAKE_FOURCC ('i', 'n', 'd', 'x') ||
973
974
975
976
            tag == GST_MAKE_FOURCC ('i', 'x', '0' + avi->num_streams / 10,
                '0' + avi->num_streams % 10)) {
          g_free (stream->indexes);
          gst_avi_demux_parse_superindex (element, sub, &stream->indexes);
977
978
          break;
        }
<