gstavidemux.c 63.3 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
63
64
65
66
static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi);
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);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
67
static GstElementStateReturn gst_avi_demux_change_state (GstElement * element);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
68

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

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

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

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

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

111
  audcaps = gst_riff_create_audio_template_caps ();
112
  audiosrctempl = gst_pad_template_new ("audio_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
113
      GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps);
114

David Schleef's avatar
David Schleef committed
115
116
  vidcaps = gst_riff_create_video_template_caps ();
  gst_caps_append (vidcaps, gst_riff_create_iavs_template_caps ());
117
  videosrctempl = gst_pad_template_new ("video_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
118
      GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps);
119

120
121
122
  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
123
      gst_static_pad_template_get (&sink_templ));
124
125
126
  gst_element_class_set_details (element_class, &gst_avi_demux_details);
}

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

132
  GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
133
      0, "Demuxer for AVI streams");
134

135
  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
136

Wim Taymans's avatar
Wim Taymans committed
137
  gstelement_class->change_state = gst_avi_demux_change_state;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
138
139
}

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

151
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
152

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

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

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

174
175
176
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
177

178
  avi->state = GST_AVI_DEMUX_START;
179
  avi->offset = 0;
180

181
182
  g_free (avi->index_entries);
  avi->index_entries = NULL;
183
  avi->index_size = 0;
184
  avi->index_offset = 0;
185
  avi->current_entry = 0;
186
187
  g_free (avi->avih);
  avi->avih = NULL;
188

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
189
  avi->seek_offset = (guint64) - 1;
190
191
192
  if (avi->seek_event)
    gst_event_unref (avi->seek_event);
  avi->seek_event = NULL;
193
194
}

195
static gst_avi_index_entry *
196
gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
197
{
198
199
  gint i;
  gst_avi_index_entry *entry = NULL;
200

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

207
208
  return entry;
}
209

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

217
218
  i = -1;
  do {
219
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
220
221
    if (!entry)
      return NULL;
222

223
    i = entry->index_nr;
224

225
    if (entry->ts <= time && (entry->flags & flags) == flags)
226
      last_entry = entry;
227
  } while (entry->ts < time);
228

229
230
  return last_entry;
}
231

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

239
240
  i = -1;
  do {
241
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
242
243
    if (!entry)
      return NULL;
244

245
    i = entry->index_nr;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
246

247
    if (entry->bytes_before <= byte && (entry->flags & flags) == flags)
248
      last_entry = entry;
249
  } while (entry->bytes_before < byte);
250

251
  return last_entry;
252
253
}

254
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
255
256
gst_avi_demux_index_entry_for_frame (GstAviDemux * avi,
    gint stream_nr, guint32 frame, guint32 flags)
257
{
258
259
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
260

261
262
  i = -1;
  do {
263
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
264
265
    if (!entry)
      return NULL;
266

267
    i = entry->index_nr;
268

269
    if (entry->frames_before <= frame && (entry->flags & flags) == flags)
270
      last_entry = entry;
271
  } while (entry->frames_before < frame);
272

273
  return last_entry;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
274
275
}

276
#if 0
277
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
278
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
279
{
280
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
281

282
283
284
285
286
287
288
289
290
291
292
  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
293

294
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
295
      src_a_formats : src_v_formats);
296
}
297
#endif
298

299
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
300
301
302
gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
303
304
{
  gboolean res = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
305

306
  avi_stream_context *stream = gst_pad_get_element_private (pad);
307

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

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

362
  return res;
363
364
}

365
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
366
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
367
{
368
369
370
371
372
  static const GstQueryType src_types[] = {
    GST_QUERY_TOTAL,
    GST_QUERY_POSITION,
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
373

374
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
375
376
}

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

383
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
384

385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
  if (!stream->strh || !stream->strf.data)
    return FALSE;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:{
      gint64 len, pos;

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

  return res;
}

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

  return masks;
}
443
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
444

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
452
453
454
455
  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");
456
    return FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
457
  }
458

Wim Taymans's avatar
Wim Taymans committed
459
460
461
462
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
463
464
      GST_DEBUG_OBJECT (avi, "seek format %d, %08x",
          GST_EVENT_SEEK_FORMAT (event), stream->strh->type);
465

Wim Taymans's avatar
Wim Taymans committed
466
      switch (GST_EVENT_SEEK_FORMAT (event)) {
467
468
469
        case GST_FORMAT_BYTES:
        case GST_FORMAT_DEFAULT:
        case GST_FORMAT_TIME:{
470
          gst_avi_index_entry *entry = NULL, *real;
471
472
473
          gint64 desired_offset = GST_EVENT_SEEK_OFFSET (event);
          guint32 flags;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
474
475
          GST_DEBUG_OBJECT (avi, "seeking to %" G_GINT64_FORMAT,
              desired_offset);
476
477
478
479

          flags = GST_RIFF_IF_KEYFRAME;
          switch (GST_EVENT_SEEK_FORMAT (event)) {
            case GST_FORMAT_BYTES:
480
              entry = gst_avi_demux_index_entry_for_byte (avi, 0,       //stream->num,
481
                  desired_offset, flags);
482
483
              real = gst_avi_demux_index_entry_for_byte (avi, stream->num,
                  desired_offset, 0);
484
485
              break;
            case GST_FORMAT_DEFAULT:
486
              entry = gst_avi_demux_index_entry_for_frame (avi, 0,      //stream->num,
487
                  desired_offset, flags);
488
489
              real = gst_avi_demux_index_entry_for_frame (avi, stream->num,
                  desired_offset, 0);
490
491
              break;
            case GST_FORMAT_TIME:
492
              entry = gst_avi_demux_index_entry_for_time (avi, 0,       //stream->num,
493
                  desired_offset, flags);
494
495
              real = gst_avi_demux_index_entry_for_time (avi, stream->num,
                  desired_offset, 0);
496
497
498
              break;
          }

499
500
501
          if (!(flags & GST_SEEK_FLAG_ACCURATE))
            real = entry;

502
          if (entry) {
503
            avi->seek_offset = entry->offset + avi->index_offset;
504
            avi->last_seek = real->ts;
505
506
507
            avi->seek_flush =
                (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH);
            avi->seek_entry = entry->index_nr;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
508
            GST_DEBUG_OBJECT (avi, "Will seek to entry %d", avi->seek_entry);
509
            res = gst_avi_demux_handle_seek (avi);
510
          } else {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
511
            GST_DEBUG_OBJECT (avi, "no index entry found for format=%d value=%"
512
513
514
                G_GINT64_FORMAT, GST_EVENT_SEEK_FORMAT (event), desired_offset);
            res = FALSE;
          }
515
          GST_LOG ("seek done");
516
517
518
519
520
          break;
        }
        default:
          res = FALSE;
          break;
Wim Taymans's avatar
Wim Taymans committed
521
522
523
524
525
526
      }
      break;
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
527
528
529

  gst_event_unref (event);

Wim Taymans's avatar
Wim Taymans committed
530
531
532
  return res;
}

533
534
535
536
537
538
539
540
541
542
/**
 * 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).
543
544
 */

545
static gboolean
546
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
547
{
548
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
549

550
  if (!gst_riff_parse_file_header (element, buf, &doctype))
551
    return FALSE;
552

553
  if (doctype != GST_RIFF_RIFF_AVI) {
554
555
556
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
        ("File is not an AVI file: " GST_FOURCC_FORMAT,
            GST_FOURCC_ARGS (doctype)));
557
558
    return FALSE;
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
559

560
561
562
  return TRUE;
}

563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
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).
592
593
 */

594
static gboolean
595
596
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
597
{
598
  gst_riff_avih *avih;
599

600
601
602
603
604
605
  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);
606
607
608
    return FALSE;
  }

609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
  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
627
628

  /* debug stuff */
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
  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;
646
647
648
649
650
  gst_buffer_unref (buf);

  return TRUE;
}

651
652
653
654
655
656
657
658
659
660
661
662
/**
 * 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.
663
664
665
 */

static gboolean
666
667
gst_avi_demux_parse_superindex (GstElement * element,
    GstBuffer * buf, guint64 ** _indexes)
668
{
669
  guint8 *data = GST_BUFFER_DATA (buf);
670
671
672
  gint bpe = 16, num, i;
  guint64 *indexes;

673
674
675
676
677
678
679
680
  *_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);
681
682
683
684
685
686
687
688
    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) {
689
690
    GST_WARNING_OBJECT (element,
        "Superindex for stream %d has unexpected "
691
692
693
694
695
696
697
698
699
700
701
702
        "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]);
  }
703
704
  indexes[i] = GST_BUFFER_OFFSET_NONE;
  *_indexes = indexes;
705
706
707
708
709
710

  gst_buffer_unref (buf);

  return TRUE;
}

711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
/**
 * 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.
 */

729
static gboolean
730
731
gst_avi_demux_parse_subindex (GstElement * element,
    GstBuffer * buf, avi_stream_context * stream, GList ** _entries_list)
732
{
733
734
735
  guint8 *data = GST_BUFFER_DATA (buf);
  gint bpe, num, x;
  guint64 baseoff;
736
  gst_avi_index_entry *entries, *entry;
737
  GList *entries_list = NULL;
738
739
  GstFormat format = GST_FORMAT_TIME;

740
741
742
743
744
745
746
747
748
749
  /* 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 */
  }
750

751
752
753
754
755
756
757
  /* 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;
  }
758

759
760
761
762
763
764
765
766
767
768
769
770
771
772
  /* 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]);
773

774
775
776
  entries = g_new (gst_avi_index_entry, num);
  for (x = 0; x < num; x++) {
    entry = &entries[x];
777

778
779
    if (GST_BUFFER_SIZE (buf) < 24 + bpe * (x + 1))
      break;
780

781
782
783
784
785
786
787
    /* 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;
788

789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
    /* 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,
          stream->total_bytes, &format, &entry->ts);
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES,
          stream->total_bytes + entry->size, &format, &entry->dur);
    } else {
      /* VBR stream */
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
          stream->total_frames, &format, &entry->ts);
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
          stream->total_frames + 1, &format, &entry->dur);
    }
    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++;
810

811
812
    entries_list = g_list_prepend (entries_list, entry);
  }
813

814
  GST_LOG_OBJECT (element, "Read %d index entries", x);
815

816
817
818
819
820
821
822
823
  gst_buffer_unref (buf);

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

825
826
  return TRUE;
}
827

828
829
830
831
832
833
834
835
static void
gst_avi_demux_read_subindexes (GstAviDemux * avi,
    GList ** index, GList ** alloc_list)
{
  GList *list;
  guint32 tag;
  GstBuffer *buf;
  gint i, n;
836

837
838
  for (n = 0; n < avi->num_streams; n++) {
    avi_stream_context *stream = &avi->stream[n];
839

840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
    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);
      }
858
859
860
861
862
863
864
    }

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

865
866
867
868
869
870
871
872
873
874
875
876
/**
 * 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.
877
878
879
 */

static gboolean
880
gst_avi_demux_parse_stream (GstElement * element, GstBuffer * buf)
881
{
882
883
884
885
886
887
888
889
890
  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;
891
892
893
  GstCaps *caps = NULL;
  GstPad *pad;

894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
  GST_DEBUG_OBJECT (element, "Parsing stream");

  /* read strh */
  if (!buf || !gst_riff_parse_chunk (element, buf, &offset, &tag, &sub) ||
      tag != GST_RIFF_TAG_strh) {
    GST_ERROR_OBJECT (element,
        "Failed to find strh chunk (tag: " GST_FOURCC_FORMAT ")",
        buf ? GST_BUFFER_SIZE (buf) : 0, GST_FOURCC_ARGS (tag));
    goto fail;
  } else if (!gst_riff_parse_strh (element, sub, &stream->strh))
    goto fail;

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

917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
    switch (stream->strh->type) {
      case GST_RIFF_FCC_vids:
        res = gst_riff_parse_strf_vids (element, sub,
            &stream->strf.vids, &stream->extradata);
        break;
      case GST_RIFF_FCC_auds:
        res = gst_riff_parse_strf_auds (element, sub,
            &stream->strf.auds, &stream->extradata);
        break;
      case GST_RIFF_FCC_iavs:
        res = gst_riff_parse_strf_iavs (element, sub,
            &stream->strf.iavs, &stream->extradata);
        break;
      default:
        GST_ERROR_OBJECT (element,
            "Don´t know how to handle stream type " GST_FOURCC_FORMAT,
            GST_FOURCC_ARGS (stream->strh->type));
        break;
935
    }
Wim Taymans's avatar
Wim Taymans committed
936

937
938
939
940
941
942
    if (!res)
      goto fail;
  }

  /* read strd/strn */
  while (gst_riff_parse_chunk (element, buf, &offset, &tag, &sub)) {
943
    switch (tag) {
944
      case GST_RIFF_TAG_strd:
945
946
947
        if (stream->initdata)
          gst_buffer_unref (stream->initdata);
        stream->initdata = sub;
948
        break;
949
      case GST_RIFF_TAG_strn:
950
951
952
953
954
        g_free (stream->name);
        stream->name = g_new (gchar, GST_BUFFER_SIZE (sub));
        memcpy (stream->name, GST_BUFFER_DATA (sub), GST_BUFFER_SIZE (sub));
        stream->name[GST_BUFFER_SIZE (sub)] = '\0';
        gst_buffer_unref (sub);
955
        break;
956
      default:
957
        if (tag == GST_MAKE_FOURCC ('i', 'n', 'd', 'x') ||
958
959
960
961
            tag == GST_MAKE_FOURCC ('i', 'x', '0' + avi->num_streams / 10,
                '0' + avi->num_streams % 10)) {
          g_free (stream->indexes);
          gst_avi_demux_parse_superindex (element, sub, &stream->indexes);
962
963
          break;
        }
964
965
        GST_WARNING_OBJECT (element,
            "Unknown stream header tag " GST_FOURCC_FORMAT ", ignoring",
966
967
            GST_FOURCC_ARGS (tag));
        /* fall-through */
968
      case GST_RIFF_TAG_JUNK:
969
        gst_buffer_unref (sub);
970
        break;
971
972
973
    }
  }

974
  /* we now have all info, let´s set up a pad and a caps and be done */
975
  /* create stream name + pad */
976
  switch (stream->strh->type) {
977
978
979
    case GST_RIFF_FCC_vids:
      padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
      templ = gst_element_class_get_pad_template (klass, "video_%02d");
980
981
982
983
984
      caps = gst_riff_create_video_caps (stream->strf.vids->compression ?
          stream->strf.vids->compression : stream->strh->fcc_handler,
          stream->strh, stream->strf.vids,
          stream->extradata, stream->initdata, &codec_name);
      tag_name = GST_TAG_VIDEO_CODEC;
985
986
987
988
989
      avi->num_v_streams++;
      break;
    case GST_RIFF_FCC_auds:
      padname = g_strdup_printf ("audio_%02d", avi->num_a_streams);
      templ = gst_element_class_get_pad_template (klass, "audio_%02d");
990
991
992
993
      caps = gst_riff_create_audio_caps (stream->strf.auds->format,
          stream->strh, stream->strf.auds,
          stream->extradata, stream->initdata, &codec_name);
      tag_name = GST_TAG_AUDIO_CODEC;
994
995
996
997
998
      avi->num_a_streams++;
      break;
    case GST_RIFF_FCC_iavs:
      padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
      templ = gst_element_class_get_pad_template (klass, "video_%02d");
999
1000
1001
1002
      caps = gst_riff_create_iavs_caps (stream->strh->fcc_handler,
          stream->strh, stream->strf.iavs,
          stream->extradata, stream->initdata, &codec_name);
      tag_name = GST_TAG_VIDEO_CODEC;
1003
1004
      avi->num_v_streams++;
      break;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
1005
    default:
1006
1007
1008
1009
1010
1011
1012
      g_assert_not_reached ();
  }

  /* no caps means no stream */
  if (!caps) {
    GST_ERROR_OBJECT (element, "Did not find caps for stream %s", padname);
    goto fail;
1013
1014
1015
  }

  /* set proper settings and add it */
1016
  pad = stream->pad = gst_pad_new_from_template (templ, padname);
1017
1018
  g_free (padname);

1019
1020
  gst_pad_use_fixed_caps (pad);
#if 0
1021
1022
  gst_pad_set_formats_function (pad, gst_avi_demux_get_src_formats);
  gst_pad_set_event_mask_function (pad, gst_avi_demux_get_event_mask);
1023
#endif
1024
1025
1026
  gst_pad_set_event_function (pad, gst_avi_demux_handle_src_event);
  gst_pad_set_query_type_function (pad, gst_avi_demux_get_src_query_types);
  gst_pad_set_query_function (pad, gst_avi_demux_handle_src_query);
1027
#if 0
1028
  gst_pad_set_convert_function (pad, gst_avi_demux_src_convert);
1029
#endif
Ronald S. Bultje's avatar