gstavidemux.c 79.5 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
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
 * SECTION:element-avidemux
 *
 * <refsect2>
 * <para>
 * Demuxes an .avi file into raw or compressed audio and/or video streams.
 * </para>
 * <para>
 * This element currently only supports pull-based scheduling. 
 * </para>
 * <title>Example launch line</title>
 * <para>
 * <programlisting>
 * gst-launch filesrc test.avi ! avidemux name=demux  demux.audio_00 ! decodebin ! audioconvert ! audioresample ! autoaudiosink   demux.video_00 ! queue ! decodebin ! ffmpegcolorspace ! videoscale ! autovideosink
 * </programlisting>
 * Play (parse and decode) an .avi file and try to output it to
 * an automatically detected soundcard and videosink. If the AVI file contains
 * compressed audio or video data, this will only work if you have the
 * right decoder elements/plugins installed.
 * </para>
 * </refsect2>
 *
 */

45
46
47
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
48

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
49
50
#include <string.h>

51
#include "gst/riff/riff-media.h"
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
52
#include "gstavidemux.h"
53
#include "avi-ids.h"
54
#include <gst/gst-i18n-plugin.h>
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
55

56
57
58
GST_DEBUG_CATEGORY_STATIC (avidemux_debug);
#define GST_CAT_DEFAULT avidemux_debug

59
#ifndef WIN32
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
60
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_EVENT);
61
62
63
64
65
#else
extern
_declspec (dllimport)
     GstDebugCategory *GST_CAT_EVENT;
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
66

67
     static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
68
69
70
71
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-msvideo")
    );
72

73
74
75
     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);
76

77
     static void gst_avi_demux_reset (GstAviDemux * avi);
78

79
#if 0
80
     static const GstEventMask *gst_avi_demux_get_event_mask (GstPad * pad);
81
#endif
82
83
     static gboolean gst_avi_demux_handle_src_event (GstPad * pad,
    GstEvent * event);
84
85

#if 0
86
     static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad);
87
#endif
88
89
90
91
92
93
94
95
96
97
98
99
100
     static const GstQueryType *gst_avi_demux_get_src_query_types (GstPad *
    pad);
     static gboolean gst_avi_demux_handle_src_query (GstPad * pad,
    GstQuery * query);
     static gboolean gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format, gint64 src_value, GstFormat * dest_format,
    gint64 * dest_value);

     static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi,
    gboolean update);
     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,
101
    gboolean active);
102
103
     static GstStateChangeReturn gst_avi_demux_change_state (GstElement *
    element, GstStateChange transition);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
104

105
     static GstElementClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
106

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
107
GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
108
gst_avi_demux_get_type (void)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
109
110
111
112
113
{
  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
114
      sizeof (GstAviDemuxClass),
115
      (GBaseInitFunc) gst_avi_demux_base_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
116
      NULL,
117
      (GClassInitFunc) gst_avi_demux_class_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
118
119
      NULL,
      NULL,
120
      sizeof (GstAviDemux),
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
121
      0,
122
      (GInstanceInitFunc) gst_avi_demux_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
123
    };
124
125

    avi_demux_type =
126
        g_type_register_static (GST_TYPE_ELEMENT,
127
        "GstAviDemux", &avi_demux_info, 0);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
128
  }
129

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
130
131
132
  return avi_demux_type;
}

133
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
134
gst_avi_demux_base_init (GstAviDemuxClass * klass)
135
{
136
  static const GstElementDetails gst_avi_demux_details =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
137
138
139
140
141
142
      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>");
143
144
145
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstPadTemplate *videosrctempl, *audiosrctempl;
  GstCaps *audcaps, *vidcaps;
146

147
  audcaps = gst_riff_create_audio_template_caps ();
148
  gst_caps_append (audcaps, gst_caps_new_simple ("audio/x-avi-unknown", NULL));
149
  audiosrctempl = gst_pad_template_new ("audio_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
150
      GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps);
151

David Schleef's avatar
David Schleef committed
152
153
  vidcaps = gst_riff_create_video_template_caps ();
  gst_caps_append (vidcaps, gst_riff_create_iavs_template_caps ());
154
  gst_caps_append (vidcaps, gst_caps_new_simple ("video/x-avi-unknown", NULL));
155
  videosrctempl = gst_pad_template_new ("video_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
156
      GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps);
157

158
159
160
  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
161
      gst_static_pad_template_get (&sink_templ));
162
163
164
  gst_element_class_set_details (element_class, &gst_avi_demux_details);
}

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
165
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
166
gst_avi_demux_class_init (GstAviDemuxClass * klass)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
167
{
168
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
169

170
  GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
171
      0, "Demuxer for AVI streams");
172

173
  parent_class = g_type_class_peek_parent (klass);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
174

Wim Taymans's avatar
Wim Taymans committed
175
  gstelement_class->change_state = gst_avi_demux_change_state;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
176
177
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
178
179
static void
gst_avi_demux_init (GstAviDemux * avi)
180
{
181
  avi->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink");
182
183
184
  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);
185
  gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad);
Wim Taymans's avatar
Wim Taymans committed
186

187
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
188

189
190
  avi->index_entries = NULL;
  memset (&avi->stream, 0, sizeof (avi->stream));
Wim Taymans's avatar
Wim Taymans committed
191
192
}

193
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
194
gst_avi_demux_reset (GstAviDemux * avi)
195
{
196
  gint i;
197

198
199
  for (i = 0; i < avi->num_streams; i++) {
    g_free (avi->stream[i].strh);
200
    g_free (avi->stream[i].strf.data);
201
202
    if (avi->stream[i].name)
      g_free (avi->stream[i].name);
203
204
205
206
    if (avi->stream[i].initdata)
      gst_buffer_unref (avi->stream[i].initdata);
    if (avi->stream[i].extradata)
      gst_buffer_unref (avi->stream[i].extradata);
207
208
    if (avi->stream[i].pad)
      gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad);
209
210
211
212
    if (avi->stream[i].taglist) {
      gst_tag_list_free (avi->stream[i].taglist);
      avi->stream[i].taglist = NULL;
    }
213
214
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
215

216
217
218
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
219

220
  avi->state = GST_AVI_DEMUX_START;
221
  avi->offset = 0;
222

223
224
  g_free (avi->index_entries);
  avi->index_entries = NULL;
225
  avi->index_size = 0;
226
  avi->index_offset = 0;
227
  avi->current_entry = 0;
228
229
  g_free (avi->avih);
  avi->avih = NULL;
230

231
232
233
  if (avi->seek_event)
    gst_event_unref (avi->seek_event);
  avi->seek_event = NULL;
234

235
236
237
238
  if (avi->globaltags)
    gst_tag_list_free (avi->globaltags);
  avi->globaltags = NULL;

239
240
  avi->got_tags = FALSE;

241
  gst_segment_init (&avi->segment, GST_FORMAT_TIME);
242
243
}

244
static gst_avi_index_entry *
245
gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
246
{
247
  gint i;
248
  gst_avi_index_entry *result = NULL;
249

250
  for (i = start; i < avi->index_size; i++) {
251
252
253
254
    gst_avi_index_entry *entry = &avi->index_entries[i];

    if (entry->stream_nr == stream_nr) {
      result = entry;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
255
      break;
256
    }
257
  }
258

259
  return result;
260
}
261

262
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
263
264
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
    gint stream_nr, guint64 time, guint32 flags)
265
266
267
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
268

269
270
  GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT " flags:%d",
      stream_nr, GST_TIME_ARGS (time), flags);
271
272
  i = -1;
  do {
273
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
274
275
    if (!entry)
      return NULL;
276

277
    i = entry->index_nr;
278

279
280
281
282
    GST_LOG_OBJECT (avi,
        "looking at entry %d / ts:%" GST_TIME_FORMAT " / dur:%" GST_TIME_FORMAT
        " flags:%d", i, GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (entry->dur),
        entry->flags);
283
    if (entry->ts <= time && (entry->flags & flags) == flags)
284
      last_entry = entry;
285
  } while (entry->ts < time);
286

287
288
  return last_entry;
}
289

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
290

291
#if 0
292
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
293
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
294
{
295
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
296

297
298
299
300
301
302
303
304
305
306
307
  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
308

309
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
310
      src_a_formats : src_v_formats);
311
}
312
#endif
313

314
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
315
316
317
gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
318
319
{
  gboolean res = TRUE;
320
  GstAviDemux *avidemux = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
321

322
  avi_stream_context *stream = gst_pad_get_element_private (pad);
323

324
325
326
327
  GST_LOG_OBJECT (avidemux,
      "Received  src_format:%d, src_value:%lld, dest_format:%d", src_format,
      src_value, *dest_format);

328
329
  if (src_format == *dest_format) {
    *dest_value = src_value;
330
331
332
333
334
    goto done;
  }
  if (!stream->strh || !stream->strf.data) {
    res = FALSE;
    goto done;
335
  }
336
  if (stream->strh->type == GST_RIFF_FCC_vids &&
337
338
339
340
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) {
    res = FALSE;
    goto done;
  }
341

342
343
344
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
345
        case GST_FORMAT_BYTES:
346
347
348
          *dest_value =
              gst_util_uint64_scale_int (src_value, stream->strf.auds->av_bps,
              GST_SECOND);
349
350
          break;
        case GST_FORMAT_DEFAULT:
351
352
          *dest_value = gst_util_uint64_scale (src_value, stream->strh->rate,
              stream->strh->scale * GST_SECOND);
353
354
355
356
          break;
        default:
          res = FALSE;
          break;
357
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
358
      break;
359
360
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
361
        case GST_FORMAT_TIME:
362
          if (stream->strf.auds->av_bps != 0) {
363
364
            *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
                stream->strf.auds->av_bps);
365
366
          } else
            res = FALSE;
367
368
369
370
          break;
        default:
          res = FALSE;
          break;
371
372
      }
      break;
373
374
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
375
        case GST_FORMAT_TIME:
376
377
378
          *dest_value =
              gst_util_uint64_scale (src_value,
              stream->strh->scale * GST_SECOND, stream->strh->rate);
379
380
381
382
          break;
        default:
          res = FALSE;
          break;
383
      }
Wim Taymans's avatar
Wim Taymans committed
384
385
      break;
    default:
386
      res = FALSE;
387
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
388

389
390
391
392
done:
  GST_LOG_OBJECT (avidemux, "Returning res:%d dest_format:%d dest_value:%lld",
      res, *dest_format, *dest_value);
  gst_object_unref (avidemux);
393
  return res;
394
395
}

396
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
397
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
398
{
399
400
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
401
    GST_QUERY_DURATION,
402
403
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
404

405
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
406
407
}

408
static gboolean
409
gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
410
{
411
  gboolean res = TRUE;
412
  GstAviDemux *demux = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
413

414
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
415

416
417
418
419
420
  if (!stream->strh || !stream->strf.data)
    return FALSE;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:{
Wim Taymans's avatar
Wim Taymans committed
421
      gint64 pos = 0;
422
423

      if (stream->strh->type == GST_RIFF_FCC_auds) {
424
425
        if (!stream->is_vbr) {
          /* CBR */
426
427
          pos = gst_util_uint64_scale_int ((gint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
428
        } else if (stream->strf.auds->av_bps != 0) {
429
          /* VBR */
430
431
          pos = gst_util_uint64_scale_int (stream->current_byte, GST_SECOND,
              stream->strf.auds->av_bps);
432
        } else if (stream->total_frames != 0 && stream->total_bytes != 0) {
433
          /* calculate timestamps based on percentage of length */
434
435
436
          guint64 xlen = demux->avih->us_frame *
              demux->avih->tot_frames * GST_USECOND;

437
          if (stream->is_vbr)
438
439
            pos = gst_util_uint64_scale_int (xlen, stream->current_byte,
                stream->total_bytes);
440
441
442
          else
            pos = gst_util_uint64_scale_int (xlen, stream->current_frame,
                stream->total_frames);
443
        } else {
444
          res = FALSE;
445
446
447
        }
      } else {
        if (stream->strh->rate != 0) {
448
449
450
          pos =
              gst_util_uint64_scale_int ((guint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
451
452
453
        } else {
          pos = stream->current_frame * demux->avih->us_frame * GST_USECOND;
        }
Wim Taymans's avatar
Wim Taymans committed
454
      }
455
      if (res)
Wim Taymans's avatar
Wim Taymans committed
456
457
458
459
460
        gst_query_set_position (query, GST_FORMAT_TIME, pos);
      break;
    }
    case GST_QUERY_DURATION:
    {
461
462
463
464
465
466
467
468
469
470
471
472
      if (stream->strh->type != GST_RIFF_FCC_auds &&
          stream->strh->type != GST_RIFF_FCC_vids) {
        res = FALSE;
        break;
      }

      /* use duration from the index if we have an
       * index instead of trusting the stream header */
      if (GST_CLOCK_TIME_IS_VALID (stream->idx_duration)) {
        gst_query_set_duration (query, GST_FORMAT_TIME, stream->idx_duration);
      } else {
        gint64 len;
Wim Taymans's avatar
Wim Taymans committed
473

474
475
476
477
478
479
        len =
            gst_util_uint64_scale ((guint64) stream->strh->length *
            stream->strh->scale, GST_SECOND, stream->strh->rate);

        gst_query_set_duration (query, GST_FORMAT_TIME, len);
      }
Wim Taymans's avatar
Wim Taymans committed
480
      break;
481
    }
Wim Taymans's avatar
Wim Taymans committed
482
    default:
483
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
484
485
486
487
488
489
      break;
  }

  return res;
}

490
#if 0
491
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
492
gst_avi_demux_get_event_mask (GstPad * pad)
493
494
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
495
496
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
497
498
499
500
  };

  return masks;
}
501
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
502

Wim Taymans's avatar
Wim Taymans committed
503
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
504
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
505
506
{
  gboolean res = TRUE;
507
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Wim Taymans's avatar
Wim Taymans committed
508
  avi_stream_context *stream;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
509

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
510
  GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi,
511
      "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
512
513
  if (!avi->index_entries) {
    GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi, "no index entries, returning");
514
    return FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
515
  }
516

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

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
521
    {
522
523
      /* FIXME, this seeking code is not correct, look at wavparse for 
       * a better example */
524
525
      GstFormat format;
      GstSeekFlags flags;
526
527
528
529
530
531
532
533
534
535
536
537
      gdouble rate;
      gint64 start, stop;
      gint64 tstart, tstop;
      gint64 duration;
      GstFormat tformat = GST_FORMAT_TIME;
      GstSeekType start_type, stop_type;
      gboolean update_start = TRUE;
      gboolean update_stop = TRUE;

      gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
          &stop_type, &stop);

538
539
540
      GST_DEBUG_OBJECT (avi,
          "seek format %d, flags:%d, %08x, start:%lld, stop:%lld", format,
          flags, stream->strh->type, start, stop);
541
542
543
544
545
546
547
548
549
550
551

      if (format != GST_FORMAT_TIME) {
        res &=
            gst_avi_demux_src_convert (pad, format, start, &tformat, &tstart);
        res &= gst_avi_demux_src_convert (pad, format, stop, &tformat, &tstop);
        if (!res)
          goto done;
      } else {
        tstart = start;
        tstop = stop;
      }
552

553
554
555
556
      if (!gst_pad_query_duration (stream->pad, &tformat, &duration)) {
        res = FALSE;
        goto done;
      }
557

558
559
      switch (start_type) {
        case GST_SEEK_TYPE_CUR:
560
          tstart = avi->segment.start + tstart;
561
562
563
564
565
          break;
        case GST_SEEK_TYPE_END:
          tstart = duration + tstart;
          break;
        case GST_SEEK_TYPE_NONE:
566
          tstart = avi->segment.start;
567
568
569
570
571
572
          update_start = FALSE;
          break;
        case GST_SEEK_TYPE_SET:
          break;
      }
      tstart = CLAMP (tstart, 0, duration);
573

574
575
      switch (stop_type) {
        case GST_SEEK_TYPE_CUR:
576
          tstop = avi->segment.stop + tstop;
577
          break;
578
579
580
581
        case GST_SEEK_TYPE_END:
          tstop = duration + tstop;
          break;
        case GST_SEEK_TYPE_NONE:
582
          tstop = avi->segment.stop;
583
584
585
          update_stop = FALSE;
          break;
        case GST_SEEK_TYPE_SET:
586
          break;
Wim Taymans's avatar
Wim Taymans committed
587
      }
588
589
590
      tstop = CLAMP (tstop, 0, duration);

      /* now store the values */
591
592
593
594
      avi->segment.rate = rate;
      avi->segment.flags = flags;
      avi->segment.start = tstart;
      avi->segment.stop = tstop;
595
596

      gst_avi_demux_handle_seek (avi, update_start || update_stop);
Wim Taymans's avatar
Wim Taymans committed
597
      break;
598

599
    }
Wim Taymans's avatar
Wim Taymans committed
600
601
602
603
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
604

605
done:
Wim Taymans's avatar
Wim Taymans committed
606
607
  gst_event_unref (event);

608
  GST_DEBUG_OBJECT (avi, "returning %d", res);
Wim Taymans's avatar
Wim Taymans committed
609
610
611
  return res;
}

612
613
614
615
616
617
618
619
620
621
/**
 * 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).
622
623
 */

624
static gboolean
625
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
626
{
627
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
628

629
  if (!gst_riff_parse_file_header (element, buf, &doctype))
630
    return FALSE;
631

632
633
634
635
636
637
638
639
  if (doctype != GST_RIFF_RIFF_AVI)
    goto not_avi;

  return TRUE;

  /* ERRORS */
not_avi:
  {
640
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
641
        ("File is not an AVI file: %" GST_FOURCC_FORMAT,
642
            GST_FOURCC_ARGS (doctype)));
643
644
645
646
    return FALSE;
  }
}

647
648
649
650
651
652
653
654
655
656
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))
657
    goto wrong_header;
658
659
660
661

  avi->offset += 12;

  return GST_FLOW_OK;
662
663
664
665
666
667
668
669

  /* ERRORS */
wrong_header:
  {
    GST_DEBUG_OBJECT (avi, "error parsing file header");
    gst_buffer_unref (buf);
    return GST_FLOW_ERROR;
  }
670
671
672
673
674
675
676
677
678
679
680
681
682
683
}

/**
 * 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).
684
685
 */

686
static gboolean
687
688
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
689
{
690
  gst_riff_avih *avih;
691

692
693
694
695
696
  if (buf == NULL)
    goto no_buffer;

  if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_avih))
    goto avih_too_small;
697

698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
  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
716
717

  /* debug stuff */
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
  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;
735
736
737
  gst_buffer_unref (buf);

  return TRUE;
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752

  /* ERRORS */
no_buffer:
  {
    GST_ELEMENT_ERROR (element, STREAM, DEMUX, (NULL), ("No buffer"));
    return FALSE;
  }
avih_too_small:
  {
    GST_ELEMENT_ERROR (element, STREAM, DEMUX, (NULL),
        ("Too small avih (%d available, %d needed)",
            GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_avih)));
    gst_buffer_unref (buf);
    return FALSE;
  }
753
754
}

755
756
757
758
759
760
761
762
763
764
765
766
/**
 * 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.
767
768
769
 */

static gboolean
770
771
gst_avi_demux_parse_superindex (GstElement * element,
    GstBuffer * buf, guint64 ** _indexes)
772
{
773
  guint8 *data;
774
775
776
  gint bpe = 16, num, i;
  guint64 *indexes;

777
778
  *_indexes = NULL;

779
780
781
  if (buf == NULL)
    goto no_buffer;

782
  if (GST_BUFFER_SIZE (buf) < 24)
783
    goto too_small;
784

785
786
  data = GST_BUFFER_DATA (buf);

787
788
789
790
791
  /* 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) {
792
793
    GST_WARNING_OBJECT (element,
        "Superindex for stream %d has unexpected "
794
795
796
797
798
799
800
801
802
803
804
805
        "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]);
  }
806
807
  indexes[i] = GST_BUFFER_OFFSET_NONE;
  *_indexes = indexes;
808
809
810
811

  gst_buffer_unref (buf);

  return TRUE;
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826

  /* ERRORS */
no_buffer:
  {
    GST_ERROR_OBJECT (element, "No buffer");
    return FALSE;
  }
too_small:
  {
    GST_ERROR_OBJECT (element,
        "Not enough data to parse superindex (%d available, %d needed)",
        GST_BUFFER_SIZE (buf), 24);
    gst_buffer_unref (buf);
    return FALSE;
  }
827
828
}

829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
/**
 * 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.
 */

847
static gboolean
848
849
gst_avi_demux_parse_subindex (GstElement * element,
    GstBuffer * buf, avi_stream_context * stream, GList ** _entries_list)
850
{
851
852
853
  guint8 *data = GST_BUFFER_DATA (buf);
  gint bpe, num, x;
  guint64 baseoff;
854
  gst_avi_index_entry *entries, *entry;
855
  GList *entries_list = NULL;
856
857
  GstFormat format = GST_FORMAT_TIME;

858
  /* check size */
859
860
861
862
863
  if (buf == NULL)
    goto no_buffer;

  if (GST_BUFFER_SIZE (buf) < 24)
    goto too_small;
864

865
  /* We don't support index-data yet */
866
867
  if (data[3] & 0x80)
    goto not_implemented;
868

869
870
871
872
873
874
875
876
877
878
879
880
881
882
  /* 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]);
883

884
885
  entries = g_new (gst_avi_index_entry, num);
  for (x = 0; x < num; x++) {
886
887
    gint64 next_ts;

888
    entry = &entries[x];
889

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

893
894
895
896
897
898
899
    /* 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;
900

901
    /* timestamps */
902
903
904
    entry->ts = stream->total_time;
    if (stream->is_vbr) {
      /* VBR get next timestamp */
905
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
906
907
908
909
910
          stream->total_frames + 1, &format, &next_ts);
    } else {
      /* CBR get next timestamp */
      gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES,
          stream->total_bytes + entry->size, &format, &next_ts);
911
    }
912
913
    /* duration is next - current */
    entry->dur = next_ts - entry->ts;
914
915
916
917

    /* stream position */
    entry->bytes_before = stream->total_bytes;
    entry->frames_before = stream->total_frames;
918
919

    stream->total_bytes += entry->size;
920
    stream->total_frames++;
921
    stream->total_time = next_ts;
922

923
924
    entries_list = g_list_prepend (entries_list, entry);
  }
925