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
374
375
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
376

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

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

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

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

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

      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;
409
          else
410
411
            pos = xlen * stream->current_byte / stream->total_bytes;
        } else {
412
          res = FALSE;
413
414
415
416
417
418
419
420
        }
      } 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
421
      }
422
      if (res)
Wim Taymans's avatar
Wim Taymans committed
423
424
425
426
427
428
429
430
431
432
        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
433
      break;
434
    }
Wim Taymans's avatar
Wim Taymans committed
435
436
437
438
439
440
441
442
    default:
      res = FALSE;
      break;
  }

  return res;
}

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

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

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
463
464
465
466
  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");
467
    return FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
468
  }
469

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

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
474
475
476
477
478
479
480
481
    {
      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
482
      GST_DEBUG_OBJECT (avi, "seek format %d, %08x",
483
          format, stream->strh->type);
484

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

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

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

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

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

  gst_event_unref (event);

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

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

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

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

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

581
582
583
  return TRUE;
}

584
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
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).
613
614
 */

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

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

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

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

  return TRUE;
}

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

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

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

  gst_buffer_unref (buf);

  return TRUE;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

851
852
  return TRUE;
}
853

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

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

866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
    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);
      }
884
885
886
887
888
889
890
    }

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

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

static gboolean
906
gst_avi_demux_parse_stream (GstElement * element, GstBuffer * buf)
907
{
908
909
910
911
912
913
914
915
916
  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,</