gstavidemux.c 65.1 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"
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
30

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

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

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

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

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

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

#if 0
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
54
static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad);
55
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
56
static const GstQueryType *gst_avi_demux_get_src_query_types (GstPad * pad);
57
static gboolean gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
58
59
60
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
61

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
193
  avi->seek_offset = (guint64) - 1;
194
195
196
  if (avi->seek_event)
    gst_event_unref (avi->seek_event);
  avi->seek_event = NULL;
197
198
}

199
static gst_avi_index_entry *
200
gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
201
{
202
203
  gint i;
  gst_avi_index_entry *entry = NULL;
204

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

211
212
  return entry;
}
213

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

221
222
  i = -1;
  do {
223
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
224
225
    if (!entry)
      return NULL;
226

227
    i = entry->index_nr;
228

229
    if (entry->ts <= time && (entry->flags & flags) == flags)
230
      last_entry = entry;
231
  } while (entry->ts < time);
232

233
234
  return last_entry;
}
235

236
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
237
238
gst_avi_demux_index_entry_for_byte (GstAviDemux * avi,
    gint stream_nr, guint64 byte, guint32 flags)
239
240
241
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
242

243
244
  i = -1;
  do {
245
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
246
247
    if (!entry)
      return NULL;
248

249
    i = entry->index_nr;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
250

251
    if (entry->bytes_before <= byte && (entry->flags & flags) == flags)
252
      last_entry = entry;
253
  } while (entry->bytes_before < byte);
254

255
  return last_entry;
256
257
}

258
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
259
260
gst_avi_demux_index_entry_for_frame (GstAviDemux * avi,
    gint stream_nr, guint32 frame, guint32 flags)
261
{
262
263
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
264

265
266
  i = -1;
  do {
267
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
268
269
    if (!entry)
      return NULL;
270

271
    i = entry->index_nr;
272

273
    if (entry->frames_before <= frame && (entry->flags & flags) == flags)
274
      last_entry = entry;
275
  } while (entry->frames_before < frame);
276

277
  return last_entry;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
278
279
}

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

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

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

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

310
  avi_stream_context *stream = gst_pad_get_element_private (pad);
311

312
313
314
315
316
317
  if (src_format == *dest_format) {
    *dest_value = src_value;
    return TRUE;
  }
  if (!stream->strh || !stream->strf.data)
    return FALSE;
318
  if (stream->strh->type == GST_RIFF_FCC_vids &&
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
319
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
320
    return FALSE;
321

322
323
324
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
325
        case GST_FORMAT_BYTES:
326
          *dest_value = src_value * stream->strf.auds->av_bps / GST_SECOND;
327
328
329
330
331
332
333
334
          break;
        case GST_FORMAT_DEFAULT:
          *dest_value = src_value * stream->strh->rate /
              (stream->strh->scale * GST_SECOND);
          break;
        default:
          res = FALSE;
          break;
335
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
336
      break;
337
338
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
339
        case GST_FORMAT_TIME:
340
341
342
343
344
          if (stream->strf.auds->av_bps != 0) {
            *dest_value = ((gfloat) src_value) * GST_SECOND /
                stream->strf.auds->av_bps;
          } else
            res = FALSE;
345
346
347
348
          break;
        default:
          res = FALSE;
          break;
349
350
      }
      break;
351
352
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
353
354
355
356
357
358
359
        case GST_FORMAT_TIME:
          *dest_value = ((((gfloat) src_value) * stream->strh->scale) /
              stream->strh->rate) * GST_SECOND;
          break;
        default:
          res = FALSE;
          break;
360
      }
Wim Taymans's avatar
Wim Taymans committed
361
362
      break;
    default:
363
      res = FALSE;
364
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
365

366
  return res;
367
368
}

369
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
370
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
371
{
372
373
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
374
    GST_QUERY_DURATION,
375
376
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
377

378
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
379
380
}

381
static gboolean
382
gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
383
{
384
  gboolean res = TRUE;
385
  GstAviDemux *demux = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
386

387
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
388

389
390
391
392
393
  if (!stream->strh || !stream->strf.data)
    return FALSE;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:{
Wim Taymans's avatar
Wim Taymans committed
394
      gint64 pos = 0;
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409

      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;
410
          else
411
412
            pos = xlen * stream->current_byte / stream->total_bytes;
        } else {
413
          res = FALSE;
414
415
416
417
418
419
420
421
        }
      } 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
422
      }
423
      if (res)
Wim Taymans's avatar
Wim Taymans committed
424
425
426
427
428
429
430
431
432
433
        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
434
      break;
435
    }
Wim Taymans's avatar
Wim Taymans committed
436
437
438
439
440
441
442
443
    default:
      res = FALSE;
      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
465
466
467
  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");
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
478
479
480
481
482
    {
      GstFormat format;
      GstSeekFlags flags;
      gint64 cur, stop;

      gst_event_parse_seek (event, NULL, &format, &flags, NULL, &cur, NULL,
          &stop);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
483
      GST_DEBUG_OBJECT (avi, "seek format %d, %08x",
484
          format, stream->strh->type);
485

486
      switch (format) {
487
488
489
        case GST_FORMAT_BYTES:
        case GST_FORMAT_DEFAULT:
        case GST_FORMAT_TIME:{
490
          gst_avi_index_entry *entry = NULL, *real;
491
          gint64 desired_offset = cur;
492
493
          guint32 flags;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
494
495
          GST_DEBUG_OBJECT (avi, "seeking to %" G_GINT64_FORMAT,
              desired_offset);
496
497

          flags = GST_RIFF_IF_KEYFRAME;
498
          switch (format) {
499
            case GST_FORMAT_BYTES:
500
              entry = gst_avi_demux_index_entry_for_byte (avi, 0,       //stream->num,
501
                  desired_offset, flags);
502
503
              real = gst_avi_demux_index_entry_for_byte (avi, stream->num,
                  desired_offset, 0);
504
505
              break;
            case GST_FORMAT_DEFAULT:
506
              entry = gst_avi_demux_index_entry_for_frame (avi, 0,      //stream->num,
507
                  desired_offset, flags);
508
509
              real = gst_avi_demux_index_entry_for_frame (avi, stream->num,
                  desired_offset, 0);
510
511
              break;
            case GST_FORMAT_TIME:
512
              entry = gst_avi_demux_index_entry_for_time (avi, 0,       //stream->num,
513
                  desired_offset, flags);
514
515
              real = gst_avi_demux_index_entry_for_time (avi, stream->num,
                  desired_offset, 0);
516
              break;
517
518
            default:
              break;
519
520
          }

521
522
523
          if (!(flags & GST_SEEK_FLAG_ACCURATE))
            real = entry;

524
          if (entry) {
525
            avi->seek_offset = entry->offset + avi->index_offset;
526
            avi->last_seek = real->ts;
527
            avi->seek_flush = flags & GST_SEEK_FLAG_FLUSH;
528
            avi->seek_entry = entry->index_nr;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
529
            GST_DEBUG_OBJECT (avi, "Will seek to entry %d", avi->seek_entry);
530
            res = gst_avi_demux_handle_seek (avi, real->ts);
531
          } else {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
532
            GST_DEBUG_OBJECT (avi, "no index entry found for format=%d value=%"
533
                G_GINT64_FORMAT, format, desired_offset);
534
535
            res = FALSE;
          }
536
          GST_LOG ("seek done");
537
538
539
540
541
          break;
        }
        default:
          res = FALSE;
          break;
Wim Taymans's avatar
Wim Taymans committed
542
543
      }
      break;
544
    }
Wim Taymans's avatar
Wim Taymans committed
545
546
547
548
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
549
550
551

  gst_event_unref (event);

Wim Taymans's avatar
Wim Taymans committed
552
553
554
  return res;
}

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

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

572
  if (!gst_riff_parse_file_header (element, buf, &doctype))
573
    return FALSE;
574

575
  if (doctype != GST_RIFF_RIFF_AVI) {
576
577
578
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
        ("File is not an AVI file: " GST_FOURCC_FORMAT,
            GST_FOURCC_ARGS (doctype)));
579
580
    return FALSE;
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
581

582
583
584
  return TRUE;
}

585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
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).
614
615
 */

616
static gboolean
617
618
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
619
{
620
  gst_riff_avih *avih;
621

622
623
624
625
626
627
  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);
628
629
630
    return FALSE;
  }

631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
  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
649
650

  /* debug stuff */
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
  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;
668
669
670
671
672
  gst_buffer_unref (buf);

  return TRUE;
}

673
674
675
676
677
678
679
680
681
682
683
684
/**
 * 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.
685
686
687
 */

static gboolean
688
689
gst_avi_demux_parse_superindex (GstElement * element,
    GstBuffer * buf, guint64 ** _indexes)
690
{
691
  guint8 *data = GST_BUFFER_DATA (buf);
692
693
694
  gint bpe = 16, num, i;
  guint64 *indexes;

695
696
697
698
699
700
701
702
  *_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);
703
704
705
706
707
708
709
710
    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) {
711
712
    GST_WARNING_OBJECT (element,
        "Superindex for stream %d has unexpected "
713
714
715
716
717
718
719
720
721
722
723
724
        "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]);
  }
725
726
  indexes[i] = GST_BUFFER_OFFSET_NONE;
  *_indexes = indexes;
727
728
729
730
731
732

  gst_buffer_unref (buf);

  return TRUE;
}

733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
/**
 * 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.
 */

751
static gboolean
752
753
gst_avi_demux_parse_subindex (GstElement * element,
    GstBuffer * buf, avi_stream_context * stream, GList ** _entries_list)
754
{
755
756
757
  guint8 *data = GST_BUFFER_DATA (buf);
  gint bpe, num, x;
  guint64 baseoff;
758
  gst_avi_index_entry *entries, *entry;
759
  GList *entries_list = NULL;
760
  GstFormat format = GST_FORMAT_TIME;
761
  gint64 tmp;
762

763
764
765
766
767
768
769
770
771
772
  /* 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 */
  }
773

774
775
776
777
778
779
780
  /* 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;
  }
781

782
783
784
785
786
787
788
789
790
791
792
793
794
795
  /* 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]);
796

797
798
799
  entries = g_new (gst_avi_index_entry, num);
  for (x = 0; x < num; x++) {
    entry = &entries[x];
800

801
802
    if (GST_BUFFER_SIZE (buf) < 24 + bpe * (x + 1))
      break;
803

804
805
806
807
808
809
810
    /* 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;
811

812
813
814
815
    /* 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,
816
817
          stream->total_bytes, &format, &tmp);
      entry->ts = tmp;
818
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES,
819
820
          stream->total_bytes + entry->size, &format, &tmp);
      entry->dur = tmp;
821
822
823
    } else {
      /* VBR stream */
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
824
825
          stream->total_frames, &format, &tmp);
      entry->ts = tmp;
826
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
827
828
          stream->total_frames + 1, &format, &tmp);
      entry->dur = tmp;
829
830
831
832
833
834
835
836
    }
    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++;
837

838
839
    entries_list = g_list_prepend (entries_list, entry);
  }
840

841
  GST_LOG_OBJECT (element, "Read %d index entries", x);
842

843
844
845
846
847
848
849
850
  gst_buffer_unref (buf);

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

852
853
  return TRUE;
}
854

855
856
857
858
859
860
861
862
static void
gst_avi_demux_read_subindexes (GstAviDemux * avi,
    GList ** index, GList ** alloc_list)
{
  GList *list;
  guint32 tag;
  GstBuffer *buf;
  gint i, n;
863

864
865
  for (n = 0; n < avi->num_streams; n++) {
    avi_stream_context *stream = &avi->stream[n];
866

867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
    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),
            "Not an ix## chunk (" GST_FOURCC_FORMAT ")", GST_FOURCC_ARGS (tag));
        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);
      }
885
886
887
888
889
890
891
    }

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

892
893
894
895
896
897
898
899
900
901
902
903
/**
 * 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.
904
905
906
 */

static gboolean
907
gst_avi_demux_parse_stream (GstElement * element, GstBuffer * buf)
908
{