gstavidemux.c 60.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
36
37
38
static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-msvideo")
    );
39

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
44
45
static void gst_avi_demux_reset (GstAviDemux * avi);
static void gst_avi_demux_loop (GstElement * element);
46

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
47
48
static gboolean gst_avi_demux_send_event (GstElement * element,
    GstEvent * event);
49

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
50
51
52
53
54
55
56
57
58
static const GstEventMask *gst_avi_demux_get_event_mask (GstPad * pad);
static gboolean gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event);
static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad);
static const GstQueryType *gst_avi_demux_get_src_query_types (GstPad * pad);
static gboolean gst_avi_demux_handle_src_query (GstPad * pad,
    GstQueryType type, GstFormat * format, gint64 * value);
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
59

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
60
static GstElementStateReturn gst_avi_demux_change_state (GstElement * element);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
61

62
static GstRiffReadClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
63

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
64
GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
65
gst_avi_demux_get_type (void)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
66
67
68
69
70
{
  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
71
      sizeof (GstAviDemuxClass),
72
      (GBaseInitFunc) gst_avi_demux_base_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
73
      NULL,
74
      (GClassInitFunc) gst_avi_demux_class_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
75
76
      NULL,
      NULL,
77
      sizeof (GstAviDemux),
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
78
      0,
79
      (GInstanceInitFunc) gst_avi_demux_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
80
    };
81
82

    avi_demux_type =
83
84
        g_type_register_static (GST_TYPE_RIFF_READ,
        "GstAviDemux", &avi_demux_info, 0);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
85
  }
86

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
87
88
89
  return avi_demux_type;
}

90
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
91
gst_avi_demux_base_init (GstAviDemuxClass * klass)
92
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
93
94
95
96
97
98
99
  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>");
100
101
102
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstPadTemplate *videosrctempl, *audiosrctempl;
  GstCaps *audcaps, *vidcaps;
103

104
  audcaps = gst_riff_create_audio_template_caps ();
105
  audiosrctempl = gst_pad_template_new ("audio_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
106
      GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps);
107

David Schleef's avatar
David Schleef committed
108
109
  vidcaps = gst_riff_create_video_template_caps ();
  gst_caps_append (vidcaps, gst_riff_create_iavs_template_caps ());
110
  videosrctempl = gst_pad_template_new ("video_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
111
      GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps);
112

113
114
115
  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
116
      gst_static_pad_template_get (&sink_templ));
117
118
119
  gst_element_class_set_details (element_class, &gst_avi_demux_details);
}

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
120
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
121
gst_avi_demux_class_init (GstAviDemuxClass * klass)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
122
123
124
125
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
126
127
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
128

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

  parent_class = g_type_class_ref (GST_TYPE_RIFF_READ);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
133

Wim Taymans's avatar
Wim Taymans committed
134
  gstelement_class->change_state = gst_avi_demux_change_state;
135
  gstelement_class->send_event = gst_avi_demux_send_event;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
136
137
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
138
139
static void
gst_avi_demux_init (GstAviDemux * avi)
140
{
141
  GST_FLAG_SET (avi, GST_ELEMENT_EVENT_AWARE);
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
  gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad);
  GST_RIFF_READ (avi)->sinkpad = avi->sinkpad;
Wim Taymans's avatar
Wim Taymans committed
148

149
150
  gst_element_set_loop_function (GST_ELEMENT (avi), gst_avi_demux_loop);
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
151

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

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

161
162
163
  for (i = 0; i < avi->num_streams; i++) {
    g_free (avi->stream[i].strh);
    gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad);
164
    gst_caps_free (avi->stream[i].caps);
165
166
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
167

168
169
170
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
171

172
173
  avi->state = GST_AVI_DEMUX_START;
  avi->level_up = 0;
174

175
176
177
  if (avi->index_entries) {
    g_free (avi->index_entries);
    avi->index_entries = NULL;
178
  }
179
  avi->index_size = 0;
180
  avi->index_offset = 0;
181
  avi->current_entry = 0;
182

183
184
  avi->num_frames = 0;
  avi->us_per_frame = 0;
185

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
186
  avi->seek_offset = (guint64) - 1;
187
188
}

189
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
190
191
gst_avi_demux_index_next (GstAviDemux * avi,
    gint stream_nr, gint start, guint32 flags)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
192
{
193
194
  gint i;
  gst_avi_index_entry *entry = NULL;
195

196
197
  for (i = start; i < avi->index_size; i++) {
    entry = &avi->index_entries[i];
198

199
    if (entry->stream_nr == stream_nr && (entry->flags & flags) == flags) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
200
      break;
201
202
    }
  }
203

204
205
  return entry;
}
206

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

214
215
216
217
218
  i = -1;
  do {
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1, flags);
    if (!entry)
      return NULL;
219

220
    i = entry->index_nr;
221

222
223
224
225
    if (entry->ts <= time) {
      last_entry = entry;
    }
  } while (entry->ts <= time);
226

227
228
  return last_entry;
}
229

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

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

243
    i = entry->index_nr;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
244

245
246
247
248
    if (entry->bytes_before <= byte) {
      last_entry = entry;
    }
  } while (entry->bytes_before <= byte);
249

250
  return last_entry;
251
252
}

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

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

266
    i = entry->index_nr;
267

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

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

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

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

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

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

  /*GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad)); */
305
  avi_stream_context *stream = gst_pad_get_element_private (pad);
306

307
  if (stream->strh->type == GST_RIFF_FCC_vids &&
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
308
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
309
    return FALSE;
310

311
312
313
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
314
        case GST_FORMAT_BYTES:
315
          *dest_value = src_value * stream->bitrate / GST_SECOND;
316
317
318
319
320
321
322
323
          break;
        case GST_FORMAT_DEFAULT:
          *dest_value = src_value * stream->strh->rate /
              (stream->strh->scale * GST_SECOND);
          break;
        default:
          res = FALSE;
          break;
324
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
325
      break;
326
327
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
328
        case GST_FORMAT_TIME:
329
          *dest_value = ((gfloat) src_value) * GST_SECOND / stream->bitrate;
330
331
332
333
          break;
        default:
          res = FALSE;
          break;
334
335
      }
      break;
336
337
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
338
339
340
341
342
343
344
        case GST_FORMAT_TIME:
          *dest_value = ((((gfloat) src_value) * stream->strh->scale) /
              stream->strh->rate) * GST_SECOND;
          break;
        default:
          res = FALSE;
          break;
345
      }
Wim Taymans's avatar
Wim Taymans committed
346
347
      break;
    default:
348
      res = FALSE;
349
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
350

351
  return res;
352
353
}

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

363
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
364
365
}

366
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
367
368
gst_avi_demux_handle_src_query (GstPad * pad,
    GstQueryType type, GstFormat * format, gint64 * value)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
369
{
370
  gboolean res = TRUE;
371
  GstAviDemux *demux = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
372
373

  /*GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad)); */
374
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
375
376

  switch (type) {
Wim Taymans's avatar
Wim Taymans committed
377
    case GST_QUERY_TOTAL:
Wim Taymans's avatar
Wim Taymans committed
378
      switch (*format) {
379
380
381
382
383
384
385
386
387
388
389
        case GST_FORMAT_TIME:
          *value = (((gfloat) stream->strh->scale) * stream->strh->length /
              stream->strh->rate) * GST_SECOND;
          break;
        case GST_FORMAT_BYTES:
          if (stream->strh->type == GST_RIFF_FCC_auds) {
            *value = stream->total_bytes;
          } else
            res = FALSE;
          break;
        case GST_FORMAT_DEFAULT:
390
391
392
393
394
395
          if (stream->strh->type == GST_RIFF_FCC_auds) {
            if (!stream->strh->samplesize)
              *value = stream->total_frames;
            else
              *value = stream->total_bytes / stream->strh->samplesize;
          } else if (stream->strh->type == GST_RIFF_FCC_vids)
396
397
398
399
400
401
402
            *value = stream->strh->length;
          else
            res = FALSE;
          break;
        default:
          res = FALSE;
          break;
Wim Taymans's avatar
Wim Taymans committed
403
404
      }
      break;
Wim Taymans's avatar
Wim Taymans committed
405
    case GST_QUERY_POSITION:
Wim Taymans's avatar
Wim Taymans committed
406
      switch (*format) {
407
        case GST_FORMAT_TIME:
408
          if (stream->strh->type == GST_RIFF_FCC_auds) {
409
410
411
            if (!stream->strh->samplesize) {
              *value = GST_SECOND * stream->current_frame *
                  stream->strh->scale / stream->strh->rate;
412
            } else if (stream->bitrate != 0) {
413
414
              *value = ((gfloat) stream->current_byte) * GST_SECOND /
                  stream->bitrate;
415
            } else if (stream->total_frames != 0 && stream->total_bytes != 0) {
416
417
418
419
              /* calculate timestamps based on video size */
              guint64 len = demux->us_per_frame * demux->num_frames *
                  GST_USECOND;

420
421
422
423
              if (!stream->strh->samplesize)
                *value = len * stream->current_frame / stream->total_frames;
              else
                *value = len * stream->current_byte / stream->total_bytes;
424
            } else {
425
              res = FALSE;
426
            }
427
          } else {
428
429
430
431
432
433
434
            if (stream->strh->rate != 0) {
              *value = ((gfloat) stream->current_frame * stream->strh->scale *
                  GST_SECOND / stream->strh->rate);
            } else {
              *value = stream->current_frame * demux->us_per_frame *
                  GST_USECOND;
            }
435
436
437
438
439
440
441
442
          }
          break;
        case GST_FORMAT_BYTES:
          *value = stream->current_byte;
          break;
        case GST_FORMAT_DEFAULT:
          if (stream->strh->samplesize &&
              stream->strh->type == GST_RIFF_FCC_auds)
443
            *value = stream->current_byte / stream->strh->samplesize;
444
445
446
447
448
449
          else
            *value = stream->current_frame;
          break;
        default:
          res = FALSE;
          break;
Wim Taymans's avatar
Wim Taymans committed
450
451
452
453
454
455
456
457
458
459
      }
      break;
    default:
      res = FALSE;
      break;
  }

  return res;
}

460
static GstCaps *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
461
gst_avi_demux_src_getcaps (GstPad * pad)
462
463
464
465
466
467
{
  avi_stream_context *stream = gst_pad_get_element_private (pad);

  return gst_caps_copy (stream->caps);
}

468
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
469
gst_avi_demux_send_event (GstElement * element, GstEvent * event)
470
{
Wim Taymans's avatar
Wim Taymans committed
471
  const GList *pads;
472
473
474

  pads = gst_element_get_pad_list (element);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
475
  while (pads) {
476
477
478
    GstPad *pad = GST_PAD (pads->data);

    if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
Wim Taymans's avatar
Wim Taymans committed
479
480
481
482
      /* we ref the event here as we might have to try again if the event
       * failed on this pad */
      gst_event_ref (event);
      if (gst_avi_demux_handle_src_event (pad, event)) {
483
        gst_event_unref (event);
484

485
        return TRUE;
Wim Taymans's avatar
Wim Taymans committed
486
      }
487
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
488

489
490
    pads = g_list_next (pads);
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
491

Wim Taymans's avatar
Wim Taymans committed
492
  gst_event_unref (event);
493

494
495
496
  return FALSE;
}

497
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
498
gst_avi_demux_get_event_mask (GstPad * pad)
499
500
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
501
502
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
503
504
505
506
  };

  return masks;
}
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
507

Wim Taymans's avatar
Wim Taymans committed
508
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
509
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
510
511
{
  gboolean res = TRUE;
512
  GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Wim Taymans's avatar
Wim Taymans committed
513
  avi_stream_context *stream;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
514

515
516
517
  if (!avi->index_entries)
    return FALSE;

Wim Taymans's avatar
Wim Taymans committed
518
519
520
521
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
522
      GST_DEBUG ("seek format %d, %08x", GST_EVENT_SEEK_FORMAT (event),
523
          stream->strh->type);
524

Wim Taymans's avatar
Wim Taymans committed
525
      switch (GST_EVENT_SEEK_FORMAT (event)) {
526
527
528
        case GST_FORMAT_BYTES:
        case GST_FORMAT_DEFAULT:
        case GST_FORMAT_TIME:{
529
          gst_avi_index_entry *entry = NULL;
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
          gint64 desired_offset = GST_EVENT_SEEK_OFFSET (event);
          guint32 flags;

          /* no seek on audio yet */
          if (stream->strh->type == GST_RIFF_FCC_auds) {
            res = FALSE;
            goto done;
          }
          GST_DEBUG ("seeking to %" G_GINT64_FORMAT, desired_offset);

          flags = GST_RIFF_IF_KEYFRAME;
          switch (GST_EVENT_SEEK_FORMAT (event)) {
            case GST_FORMAT_BYTES:
              entry = gst_avi_demux_index_entry_for_byte (avi, stream->num,
                  desired_offset, flags);
              break;
            case GST_FORMAT_DEFAULT:
              entry = gst_avi_demux_index_entry_for_frame (avi, stream->num,
                  desired_offset, flags);
              break;
            case GST_FORMAT_TIME:
              entry = gst_avi_demux_index_entry_for_time (avi, stream->num,
                  desired_offset, flags);
              break;
          }

          if (entry) {
557
            avi->seek_offset = entry->offset + avi->index_offset;
558
            avi->last_seek = entry->ts;
559
560
561
            avi->seek_flush =
                (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH);
            avi->seek_entry = entry->index_nr;
562
            GST_DEBUG ("Will seek to entry %d", avi->seek_entry);
563
564
565
566
567
          } else {
            GST_DEBUG ("no index entry found for format=%d value=%"
                G_GINT64_FORMAT, GST_EVENT_SEEK_FORMAT (event), desired_offset);
            res = FALSE;
          }
568
          GST_LOG ("seek done\n");
569
570
571
572
573
          break;
        }
        default:
          res = FALSE;
          break;
Wim Taymans's avatar
Wim Taymans committed
574
575
576
577
578
579
      }
      break;
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
580
581
582
583

done:
  gst_event_unref (event);

Wim Taymans's avatar
Wim Taymans committed
584
585
586
  return res;
}

587
588
589
590
/*
 * "Open" a RIFF file.
 */

591
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
592
gst_avi_demux_stream_init (GstAviDemux * avi)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
593
{
594
595
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
596

597
598
599
  if (!gst_riff_read_header (riff, &doctype))
    return FALSE;
  if (doctype != GST_RIFF_RIFF_AVI) {
600
    GST_ELEMENT_ERROR (avi, STREAM, WRONG_TYPE, (NULL), (NULL));
601
602
    return FALSE;
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
603

604
605
606
607
608
609
610
  return TRUE;
}

/*
 * Read 'avih' header.
 */

611
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
612
613
gst_avi_demux_stream_avih (GstAviDemux * avi,
    guint32 * flags, guint32 * streams)
614
615
616
617
{
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 tag;
  GstBuffer *buf;
618
  gst_riff_avih avih, *_avih;
619
620
621
622
623
624
625
626
627
628
629

  if (!gst_riff_read_data (riff, &tag, &buf))
    return FALSE;

  if (tag != GST_RIFF_TAG_avih) {
    g_warning ("Not a avih chunk");
    gst_buffer_unref (buf);
    return FALSE;
  }
  if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_avih)) {
    g_warning ("Too small avih (%d available, %d needed)",
630
        GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_avih));
631
632
633
634
    gst_buffer_unref (buf);
    return FALSE;
  }

635
  _avih = (gst_riff_avih *) GST_BUFFER_DATA (buf);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
636
637
638
639
640
  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);
641
  avih.init_frames = GUINT32_FROM_LE (_avih->init_frames);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
642
643
644
645
646
647
648
649
  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);
650
651
652

  /* debug stuff */
  GST_INFO ("avih tag found:");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
653
654
655
  GST_INFO (" us_frame    %u", avih.us_frame);
  GST_INFO (" max_bps     %u", avih.max_bps);
  GST_INFO (" pad_gran    %u", avih.pad_gran);
656
  GST_INFO (" flags       0x%08x", avih.flags);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
657
658
659
660
661
662
663
664
665
666
  GST_INFO (" tot_frames  %u", avih.tot_frames);
  GST_INFO (" init_frames %u", avih.init_frames);
  GST_INFO (" streams     %u", avih.streams);
  GST_INFO (" bufsize     %u", avih.bufsize);
  GST_INFO (" width       %u", avih.width);
  GST_INFO (" height      %u", avih.height);
  GST_INFO (" scale       %u", avih.scale);
  GST_INFO (" rate        %u", avih.rate);
  GST_INFO (" start       %u", avih.start);
  GST_INFO (" length      %u", avih.length);
667
668
669
670
671

  avi->num_frames = avih.tot_frames;
  avi->us_per_frame = avih.us_frame;
  *streams = avih.streams;
  *flags = avih.flags;
672
673
674
675
676
677

  gst_buffer_unref (buf);

  return TRUE;
}

678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
/*
 * Read superindex/subindex (openDML-2).
 */

static gboolean
gst_avi_demux_read_superindex (GstAviDemux * avi,
    gint stream_nr, guint64 ** locations)
{
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 tag;
  GstBuffer *buf;
  guint8 *data;
  gint bpe = 16, num, i;
  guint64 *indexes;

  if (!gst_riff_read_data (riff, &tag, &buf))
    return FALSE;
  data = GST_BUFFER_DATA (buf);
  if (tag != GST_MAKE_FOURCC ('i', 'n', 'd', 'x') &&
      tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream_nr / 10,
          '0' + stream_nr % 10)) {
    g_warning ("Not an indx/ix## chunk");
    gst_buffer_unref (buf);
    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) {
    GST_WARNING ("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]);

  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]);
  }
  indexes[i] = 0;
  *locations = indexes;

  gst_buffer_unref (buf);

  return TRUE;
}

static gboolean
gst_avi_demux_read_subindexes (GstAviDemux * avi,
    GList ** index, GList ** alloc_list)
{
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint64 pos = gst_bytestream_tell (riff->bs),
      length = gst_bytestream_length (riff->bs), baseoff;
  GstEvent *event;
  GList *list = NULL;
  gst_avi_index_entry *entries, *entry;
  guint32 tag;
  GstBuffer *buf;
  guint8 *data;
  GstFormat format = GST_FORMAT_TIME;
  gint bpe, num, x, i, n;

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

    for (i = 0; stream->indexes[i] != 0; i++) {
      /* eos check again */
      if (stream->indexes[i] + 8 >= length) {
        GST_WARNING ("Subindex %d for stream %d doesn't exist", i, n);
        continue;
      }

      /* seek to that point */
      if (!(event = gst_riff_read_seek (riff, stream->indexes[i]))) {
        g_list_free (list);
        return FALSE;
      }
      gst_event_unref (event);
      if (gst_bytestream_peek_bytes (riff->bs, &data, 8) != 8) {
        g_list_free (list);
        return FALSE;
      }

      /* eos check again */
      if (GST_READ_UINT32_LE (&data[4]) + gst_bytestream_tell (riff->bs) >
          length) {
        GST_WARNING ("Subindex %d for stream %d lies outside file", i, n);
        continue;
      }

      /* now read */
      if (!gst_riff_read_data (riff, &tag, &buf))
        return FALSE;
      data = GST_BUFFER_DATA (buf);
      if (tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream->num / 10,
              '0' + stream->num % 10)) {
        GST_WARNING ("Not an ix## chunk (" GST_FOURCC_FORMAT ")",
            GST_FOURCC_ARGS (tag));
        gst_buffer_unref (buf);
        continue;
      }

      /* We don't support index-data yet */
      if (data[3] & 0x80) {
        GST_ELEMENT_ERROR (avi, STREAM, NOT_IMPLEMENTED, (NULL),
            ("Subindex-is-data is not implemented"));
        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). */
      bpe = (data[2] & 0x01) ? 12 : 8;
      if (GST_READ_UINT16_LE (data) != bpe / 4 ||
          (data[2] & 0xfe) != 0x0 || data[3] != 0x1) {
        GST_WARNING ("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]);

      entries = g_new (gst_avi_index_entry, num);
      *alloc_list = g_list_append (*alloc_list, entries);
      for (x = 0; x < num; x++) {
        entry = &entries[x];

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

        /* 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;

        /* timestamps */
        if (stream->strh->samplesize && stream->strh->type == GST_RIFF_FCC_auds) {
          /* constant rate stream */
          gst_pad_convert (stream->pad, GST_FORMAT_BYTES,
              stream->total_bytes, &format, &entry->ts);
          gst_pad_convert (stream->pad, GST_FORMAT_BYTES,
              stream->total_bytes + entry->size, &format, &entry->dur);
        } else {
          /* VBR stream */
          gst_pad_convert (stream->pad, GST_FORMAT_DEFAULT,
              stream->total_frames, &format, &entry->ts);
          gst_pad_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++;

        list = g_list_prepend (list, entry);
      }

      GST_LOG ("Read %d index entries in subindex %d for stream %d "
          "at location %" G_GUINT64_FORMAT, num, i, n, stream->indexes[i]);

      gst_buffer_unref (buf);
    }

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

  /* seek back */
  if (!(event = gst_riff_read_seek (riff, pos))) {
    g_list_free (list);
    return FALSE;
  }
  gst_event_unref (event);
  *index = g_list_reverse (list);

  return TRUE;
}

869
870
871
872
873
/*
 * Add a stream.
 */

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
874
gst_avi_demux_add_stream (GstAviDemux * avi)
875
876
877
878
879
{
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (avi);
  GstRiffRead *riff = GST_RIFF_READ (avi);
  guint32 tag;
  gst_riff_strh *strh;
880
  GstBuffer *extradata = NULL, *initdata = NULL;
881
882
883
884
885
  gchar *name = NULL, *padname = NULL;
  GstCaps *caps = NULL;
  GstPadTemplate *templ = NULL;
  GstPad *pad;
  avi_stream_context *stream;
886
  gint blockalign = 0, bitrate = 0;
887
  guint64 *locations = NULL;
888
889
  GstTagList *list = gst_tag_list_new ();
  gboolean have_tag = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
890
891
  union
  {
892
893
894
    gst_riff_strf_vids *vids;
    gst_riff_strf_auds *auds;
    gst_riff_strf_iavs *iavs;
895
896
  }
  strf;
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911

  /* the stream starts with a 'strh' header */
  if (!(tag = gst_riff_peek_tag (riff, NULL)))
    return FALSE;
  if (tag != GST_RIFF_TAG_strh) {
    g_warning ("Invalid stream header (no strh at begin)");
    goto skip_stream;
  }
  if (!gst_riff_read_strh (riff, &strh))
    return FALSE;

  /* then comes a 'strf' of that specific type */
  if (!(tag = gst_riff_peek_tag (riff, NULL)))
    return FALSE;
  if (tag != GST_RIFF_TAG_strf) {
912
    GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
913
        ("Invalid AVI header (no strf as second tag)"));
914
915
916
917
    goto skip_stream;
  }
  switch (strh->type) {
    case GST_RIFF_FCC_vids:
918
      if (!gst_riff_read_strf_vids_with_data (riff, &strf.vids, &extradata))
919
        return FALSE;
920
921
      break;
    case GST_RIFF_FCC_auds:
922
      if (!gst_riff_read_strf_auds_with_data (riff, &strf.auds, &extradata))
923
        return FALSE;
924
925
926
      break;
    case GST_RIFF_FCC_iavs:
      if (!gst_riff_read_strf_iavs (riff, &strf.iavs))
927
        return FALSE;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
928
      break;
929
930
    default:
      g_warning ("Unknown stream type " GST_FOURCC_FORMAT,
931
          GST_FOURCC_ARGS (strh->type));
932
933
      goto skip_stream;
  }
Wim Taymans's avatar
Wim Taymans committed
934

935
936
937
938
939
940
941
942
  /* read other things */
  while (TRUE) {
    if (!(tag = gst_riff_peek_tag (riff, &avi->level_up)))
      return FALSE;
    else if (avi->level_up) {
      avi->level_up--;
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
943

944
    switch (tag) {
945
946
947
948
949
950
951
      case GST_RIFF_TAG_strd:
        if (initdata)
          gst_buffer_unref (initdata);
        if (!gst_riff_read_data (riff, &tag, &initdata))
          return FALSE;
        break;

952
      case GST_RIFF_TAG_strn:
953
        g_free (name);
954
955
956
        if (!gst_riff_read_ascii (riff, &tag, &name))
          return FALSE;
        break;
Wim Taymans's avatar
Wim Taymans committed
957

958
      default:
959
960
961
962
963
964
965
966
967
        if (tag == GST_MAKE_FOURCC ('i', 'n', 'd', 'x') ||
            tag == GST_MAKE_FOURCC ('i', 'x', avi->num_streams / 10,
                avi->num_streams % 10)) {
          g_free (locations);
          if (!gst_avi_demux_read_superindex (avi,
                  avi->num_streams, &locations))
            return FALSE;
          break;
        }
968
969
970
        GST_WARNING ("Unknown tag " GST_FOURCC_FORMAT " in AVI header",
            GST_FOURCC_ARGS (tag));
        /* fall-through */
971
972

      case GST_RIFF_TAG_JUNK:
973
974
975
        if (!gst_riff_read_skip (riff))
          return FALSE;
        break;
976
    }
Wim Taymans's avatar
Wim Taymans committed
977

978
979
    if (avi->level_up) {
      avi->level_up--;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
980
      break;
Wim Taymans's avatar
Wim Taymans committed
981
    }
982
983
984
985
986
  }

  /* create stream name + pad */
  switch (strh->type) {
    case GST_RIFF_FCC_vids:
987
988
    {
      char *codec_name = NULL;
989
      guint32 tag;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
990

991
992
      padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
      templ = gst_element_class_get_pad_template (klass, "video_%02d");
993
994
995
996
997
      if (strf.vids->compression)
        tag = strf.vids->compression;
      else
        tag = strh->fcc_handler;
      caps = gst_riff_create_video_caps_with_data (tag,
998
          strh, strf.vids, extradata, initdata, &codec_name);
999
1000
1001
1002
      if (codec_name) {
        gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_VIDEO_CODEC,
            codec_name, NULL);
        have_tag = TRUE;
1003
        g_free (codec_name);
1004
      }
1005
1006
1007
      g_free (strf.vids);
      avi->num_v_streams++;
      break;
1008
    }
1009
    case GST_RIFF_FCC_auds:
1010
1011
    {
      char *codec_name = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1012

1013
1014
      padname = g_strdup_printf ("audio_%02d", avi->num_a_streams);
      templ = gst_element_class_get_pad_template (klass, "audio_%02d");
1015
1016
1017
      caps =
          gst_riff_create_audio_caps_with_data (strf.auds->format, strh,
          strf.auds, extradata, initdata, &codec_name);
1018
1019
1020
1021
      if (codec_name) {
        gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_AUDIO_CODEC,
            codec_name, NULL);
        have_tag = TRUE;
1022
        g_free (codec_name);
1023
      }
1024
1025
      blockalign = strf.auds->blockalign;
      bitrate = strf.auds->av_bps;
1026
1027
1028