gstavidemux.c 65 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:{
393
      gint64 len, pos = 0;
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410

      len = (((gfloat) stream->strh->scale) * stream->strh->length /
          stream->strh->rate) * GST_SECOND;
      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;
411
          else
412
413
            pos = xlen * stream->current_byte / stream->total_bytes;
        } else {
414
          res = FALSE;
415
416
417
418
419
420
421
422
        }
      } 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
423
      }
424
425
      if (res)
        gst_query_set_position (query, GST_FORMAT_TIME, pos, len);
Wim Taymans's avatar
Wim Taymans committed
426
      break;
427
    }
Wim Taymans's avatar
Wim Taymans committed
428
429
430
431
432
433
434
435
    default:
      res = FALSE;
      break;
  }

  return res;
}

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

  return masks;
}
447
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
448

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
456
457
458
459
  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");
460
    return FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
461
  }
462

Wim Taymans's avatar
Wim Taymans committed
463
464
465
466
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
467
468
469
470
471
472
473
474
    {
      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
475
      GST_DEBUG_OBJECT (avi, "seek format %d, %08x",
476
          format, stream->strh->type);
477

478
      switch (format) {
479
480
481
        case GST_FORMAT_BYTES:
        case GST_FORMAT_DEFAULT:
        case GST_FORMAT_TIME:{
482
          gst_avi_index_entry *entry = NULL, *real;
483
          gint64 desired_offset = cur;
484
485
          guint32 flags;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
486
487
          GST_DEBUG_OBJECT (avi, "seeking to %" G_GINT64_FORMAT,
              desired_offset);
488
489

          flags = GST_RIFF_IF_KEYFRAME;
490
          switch (format) {
491
            case GST_FORMAT_BYTES:
492
              entry = gst_avi_demux_index_entry_for_byte (avi, 0,       //stream->num,
493
                  desired_offset, flags);
494
495
              real = gst_avi_demux_index_entry_for_byte (avi, stream->num,
                  desired_offset, 0);
496
497
              break;
            case GST_FORMAT_DEFAULT:
498
              entry = gst_avi_demux_index_entry_for_frame (avi, 0,      //stream->num,
499
                  desired_offset, flags);
500
501
              real = gst_avi_demux_index_entry_for_frame (avi, stream->num,
                  desired_offset, 0);
502
503
              break;
            case GST_FORMAT_TIME:
504
              entry = gst_avi_demux_index_entry_for_time (avi, 0,       //stream->num,
505
                  desired_offset, flags);
506
507
              real = gst_avi_demux_index_entry_for_time (avi, stream->num,
                  desired_offset, 0);
508
              break;
509
510
            default:
              break;
511
512
          }

513
514
515
          if (!(flags & GST_SEEK_FLAG_ACCURATE))
            real = entry;

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

  gst_event_unref (event);

Wim Taymans's avatar
Wim Taymans committed
544
545
546
  return res;
}

547
548
549
550
551
552
553
554
555
556
/**
 * 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).
557
558
 */

559
static gboolean
560
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
561
{
562
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
563

564
  if (!gst_riff_parse_file_header (element, buf, &doctype))
565
    return FALSE;
566

567
  if (doctype != GST_RIFF_RIFF_AVI) {
568
569
570
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
        ("File is not an AVI file: " GST_FOURCC_FORMAT,
            GST_FOURCC_ARGS (doctype)));
571
572
    return FALSE;
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
573

574
575
576
  return TRUE;
}

577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
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).
606
607
 */

608
static gboolean
609
610
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
611
{
612
  gst_riff_avih *avih;
613

614
615
616
617
618
619
  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);
620
621
622
    return FALSE;
  }

623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
  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
641
642

  /* debug stuff */
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
  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;
660
661
662
663
664
  gst_buffer_unref (buf);

  return TRUE;
}

665
666
667
668
669
670
671
672
673
674
675
676
/**
 * 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.
677
678
679
 */

static gboolean
680
681
gst_avi_demux_parse_superindex (GstElement * element,
    GstBuffer * buf, guint64 ** _indexes)
682
{
683
  guint8 *data = GST_BUFFER_DATA (buf);
684
685
686
  gint bpe = 16, num, i;
  guint64 *indexes;

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

  gst_buffer_unref (buf);

  return TRUE;
}

725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
/**
 * 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.
 */

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

755
756
757
758
759
760
761
762
763
764
  /* 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 */
  }
765

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

774
775
776
777
778
779
780
781
782
783
784
785
786
787
  /* 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]);
788

789
790
791
  entries = g_new (gst_avi_index_entry, num);
  for (x = 0; x < num; x++) {
    entry = &entries[x];
792

793
794
    if (GST_BUFFER_SIZE (buf) < 24 + bpe * (x + 1))
      break;
795

796
797
798
799
800
801
802
    /* 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;
803

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

830
831
    entries_list = g_list_prepend (entries_list, entry);
  }
832

833
  GST_LOG_OBJECT (element, "Read %d index entries", x);
834

835
836
837
838
839
840
841
842
  gst_buffer_unref (buf);

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

844
845
  return TRUE;
}
846

847
848
849
850
851
852
853
854
static void
gst_avi_demux_read_subindexes (GstAviDemux * avi,
    GList ** index, GList ** alloc_list)
{
  GList *list;
  guint32 tag;
  GstBuffer *buf;
  gint i, n;
855

856
857
  for (n = 0; n < avi->num_streams; n++) {
    avi_stream_context *stream = &avi->stream[n];
858

859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
    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);
      }
877
878
879
880
881
882
883
    }

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

884
885
886
887
888
889
890
891
892
893
894
895
/**
 * 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.
896
897
898
 */

static gboolean
899
gst_avi_demux_parse_stream (GstElement * element, GstBuffer * buf)
900
{
901
902
903
904
905
906
907
908
909
  GstAviDemux *avi = GST_AVI_DEMUX (element);
  avi_stream_context *stream = &avi->stream[avi->num_streams];
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
  GstPadTemplate *templ;
  GstBuffer *sub = NULL;
  guint offset = 4;
  guint32 tag = 0;
  gchar *codec_name = NULL, *padname = NULL;
  const gchar *tag_name;
910
911
912
  GstCaps *caps = NULL;
  GstPad *pad;

Ronald S. Bultje's avatar