gstavidemux.c 77 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
{
147
  avi->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink");
148
149
150
  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);
151
  gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad);
Wim Taymans's avatar
Wim Taymans committed
152

153
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
154

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

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

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

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

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

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

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

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

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

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

215
216
  return entry;
}
217

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

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

233
    i = entry->index_nr;
234

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

243
244
  return last_entry;
}
245

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
246

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

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

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

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

278
  avi_stream_context *stream = gst_pad_get_element_private (pad);
279

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

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

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

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

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

361
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
362
363
}

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

370
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
371

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

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

      if (stream->strh->type == GST_RIFF_FCC_auds) {
        if (!stream->strh->samplesize) {
381
382
          pos = gst_util_uint64_scale_int ((gint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
383
        } else if (stream->strf.auds->av_bps != 0) {
384
385
          pos = gst_util_uint64_scale_int (stream->current_byte, GST_SECOND,
              stream->strf.auds->av_bps);
386
387
388
389
390
391
        } 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)
392
393
            pos = gst_util_uint64_scale_int (xlen, stream->current_frame,
                stream->total_frames);
394
          else
395
396
            pos = gst_util_uint64_scale_int (xlen, stream->current_byte,
                stream->total_bytes);
397
        } else {
398
          res = FALSE;
399
400
401
        }
      } else {
        if (stream->strh->rate != 0) {
402
403
404
          pos =
              gst_util_uint64_scale_int ((guint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
405
406
407
        } else {
          pos = stream->current_frame * demux->avih->us_frame * GST_USECOND;
        }
Wim Taymans's avatar
Wim Taymans committed
408
      }
409
      if (res)
Wim Taymans's avatar
Wim Taymans committed
410
411
412
413
414
        gst_query_set_position (query, GST_FORMAT_TIME, pos);
      break;
    }
    case GST_QUERY_DURATION:
    {
415
416
417
418
419
420
421
422
423
424
425
426
      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
427

428
429
430
431
432
433
        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
434
      break;
435
    }
Wim Taymans's avatar
Wim Taymans committed
436
    default:
437
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
438
439
440
441
442
443
      break;
  }

  return res;
}

444
#if 0
445
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
446
gst_avi_demux_get_event_mask (GstPad * pad)
447
448
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
449
450
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
451
452
453
454
  };

  return masks;
}
455
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
456

Wim Taymans's avatar
Wim Taymans committed
457
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
458
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
459
460
{
  gboolean res = TRUE;
461
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Wim Taymans's avatar
Wim Taymans committed
462
  avi_stream_context *stream;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
463

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
464
  GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi,
465
      "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
466
467
  if (!avi->index_entries) {
    GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi, "no index entries, returning");
468
    return FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
469
  }
470

Wim Taymans's avatar
Wim Taymans committed
471
472
473
474
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
475
    {
476
477
      /* FIXME, this seeking code is not correct, look at wavparse for 
       * a better example */
478
479
      GstFormat format;
      GstSeekFlags flags;
480
481
482
483
484
485
486
487
488
489
490
491
      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);

492
493
494
      GST_DEBUG_OBJECT (avi,
          "seek format %d, flags:%d, %08x, start:%lld, stop:%lld", format,
          flags, stream->strh->type, start, stop);
495
496
497
498
499
500
501
502
503
504
505

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

507
508
509
510
      if (!gst_pad_query_duration (stream->pad, &tformat, &duration)) {
        res = FALSE;
        goto done;
      }
511

512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
      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);
527

528
529
530
      switch (stop_type) {
        case GST_SEEK_TYPE_CUR:
          tstop = avi->segment_stop + tstop;
531
          break;
532
533
534
535
536
537
538
539
        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:
540
          break;
Wim Taymans's avatar
Wim Taymans committed
541
      }
542
543
544
545
546
547
548
549
550
      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
551
      break;
552

553
    }
Wim Taymans's avatar
Wim Taymans committed
554
555
556
557
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
558

559
done:
Wim Taymans's avatar
Wim Taymans committed
560
561
  gst_event_unref (event);

562
  GST_DEBUG_OBJECT (avi, "returning %d", res);
Wim Taymans's avatar
Wim Taymans committed
563
564
565
  return res;
}

566
567
568
569
570
571
572
573
574
575
/**
 * 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).
576
577
 */

578
static gboolean
579
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
580
{
581
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
582

583
  if (!gst_riff_parse_file_header (element, buf, &doctype))
584
    return FALSE;
585

586
587
588
589
590
591
592
593
  if (doctype != GST_RIFF_RIFF_AVI)
    goto not_avi;

  return TRUE;

  /* ERRORS */
not_avi:
  {
594
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
595
        ("File is not an AVI file: %" GST_FOURCC_FORMAT,
596
            GST_FOURCC_ARGS (doctype)));
597
598
599
600
    return FALSE;
  }
}

601
602
603
604
605
606
607
608
609
610
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))
611
    goto wrong_header;
612
613
614
615

  avi->offset += 12;

  return GST_FLOW_OK;
616
617
618
619
620
621
622
623

  /* ERRORS */
wrong_header:
  {
    GST_DEBUG_OBJECT (avi, "error parsing file header");
    gst_buffer_unref (buf);
    return GST_FLOW_ERROR;
  }
624
625
626
627
628
629
630
631
632
633
634
635
636
637
}

/**
 * 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).
638
639
 */

640
static gboolean
641
642
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
643
{
644
  gst_riff_avih *avih;
645

646
647
648
649
650
  if (buf == NULL)
    goto no_buffer;

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

652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
  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
670
671

  /* debug stuff */
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
  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;
689
690
691
  gst_buffer_unref (buf);

  return TRUE;
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706

  /* 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;
  }
707
708
}

709
710
711
712
713
714
715
716
717
718
719
720
/**
 * 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.
721
722
723
 */

static gboolean
724
725
gst_avi_demux_parse_superindex (GstElement * element,
    GstBuffer * buf, guint64 ** _indexes)
726
{
727
  guint8 *data;
728
729
730
  gint bpe = 16, num, i;
  guint64 *indexes;

731
732
  *_indexes = NULL;

733
734
735
  if (buf == NULL)
    goto no_buffer;

736
  if (GST_BUFFER_SIZE (buf) < 24)
737
    goto too_small;
738

739
740
  data = GST_BUFFER_DATA (buf);

741
742
743
744
745
  /* 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) {
746
747
    GST_WARNING_OBJECT (element,
        "Superindex for stream %d has unexpected "
748
749
750
751
752
753
754
755
756
757
758
759
        "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]);
  }
760
761
  indexes[i] = GST_BUFFER_OFFSET_NONE;
  *_indexes = indexes;
762
763
764
765

  gst_buffer_unref (buf);

  return TRUE;
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780

  /* 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;
  }
781
782
}

783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
/**
 * 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.
 */

801
static gboolean
802
803
gst_avi_demux_parse_subindex (GstElement * element,
    GstBuffer * buf, avi_stream_context * stream, GList ** _entries_list)
804
{
805
806
807
  guint8 *data = GST_BUFFER_DATA (buf);
  gint bpe, num, x;
  guint64 baseoff;
808
  gst_avi_index_entry *entries, *entry;
809
  GList *entries_list = NULL;
810
  GstFormat format = GST_FORMAT_TIME;
811
  gint64 tmp;
812

813
  /* check size */
814
815
816
817
818
  if (buf == NULL)
    goto no_buffer;

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

820
  /* We don't support index-data yet */
821
822
  if (data[3] & 0x80)
    goto not_implemented;
823

824
825
826
827
828
829
830
831
832
833
834
835
836
837
  /* 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]);
838

839
840
841
  entries = g_new (gst_avi_index_entry, num);
  for (x = 0; x < num; x++) {
    entry = &entries[x];
842

843
844
    if (GST_BUFFER_SIZE (buf) < 24 + bpe * (x + 1))
      break;
845

846
847
848
849
850
851
852
    /* 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;
853

854
855
856
857
    /* 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,
858
859
          stream->total_bytes, &format, &tmp);
      entry->ts = tmp;
860
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES,
861
862
          stream->total_bytes + entry->size, &format, &tmp);
      entry->dur = tmp;
863
864
865
    } else {
      /* VBR stream */
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
866
867
          stream->total_frames, &format, &tmp);
      entry->ts = tmp;
868
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
869
870
          stream->total_frames + 1, &format, &tmp);
      entry->dur = tmp;
871
872
873
874
875
876
877
878
    }
    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++;
879

880
881
    entries_list = g_list_prepend (entries_list, entry);
  }
882

883
  GST_LOG_OBJECT (element, "Read %d index entries", x);
884

885
886
887
888
889
890
891
892
  gst_buffer_unref (buf);

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

894
  return TRUE;
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917

  /* 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;
  }
918
}
919

920
921
922
923
924
925
926
927
static void
gst_avi_demux_read_subindexes (GstAviDemux * avi,
    GList ** index, GList ** alloc_list)
{
  GList *list;
  guint32 tag;
  GstBuffer *buf;
  gint i, n;
928

929
930
  for (n = 0; n < avi->num_streams; n++) {
    avi_stream_context *stream = &avi->stream[n];
931

932
933
934
935
936
937
938
    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),
939
940
            "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
            GST_FOURCC_ARGS (tag));
941
942
943
944
945
946
947
948
949
950
        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);
      }
951
952
953
954
955
956
957
    }

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