gstavidemux.c 113 KB
Newer Older
1
2
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@temple-baptist.com>
3
 * Copyright (C) <2006> Nokia Corporation (contact <stefan.kost@nokia.com>)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 *
 * 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.
 */
20
/* Element-Checklist-Version: 5 */
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
21

22
23
24
25
26
27
28
29
/**
 * SECTION:element-avidemux
 *
 * <refsect2>
 * <para>
 * Demuxes an .avi file into raw or compressed audio and/or video streams.
 * </para>
 * <para>
Wim Taymans's avatar
Wim Taymans committed
30
 * This element supports both push and pull-based scheduling, depending on the
31
 * capabilities of the upstream elements.
32
33
34
35
 * </para>
 * <title>Example launch line</title>
 * <para>
 * <programlisting>
Wim Taymans's avatar
Wim Taymans committed
36
 * gst-launch filesrc location=test.avi ! avidemux name=demux  demux.audio_00 ! decodebin ! audioconvert ! audioresample ! autoaudiosink   demux.video_00 ! queue ! decodebin ! ffmpegcolorspace ! videoscale ! autovideosink
37
38
39
40
41
42
43
44
 * </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>
 *
Wim Taymans's avatar
Wim Taymans committed
45
 * Last reviewed on 2006-12-29 (0.10.6)
46
47
 */

48
49
50
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
51

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
52
53
#include <string.h>

54
#include "gst/riff/riff-media.h"
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
55
#include "gstavidemux.h"
56
#include "avi-ids.h"
57
#include <gst/gst-i18n-plugin.h>
58
#include <gst/base/gstadapter.h>
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
59

60
61
62
GST_DEBUG_CATEGORY_STATIC (avidemux_debug);
#define GST_CAT_DEFAULT avidemux_debug

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
63
64
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_EVENT);

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

71
72
73
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);
74
static void gst_avi_demux_finalize (GObject * object);
75

76
static void gst_avi_demux_reset (GstAviDemux * avi);
77

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

#if 0
84
static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad);
85
#endif
86
87
88
89
90
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);

91
92
93
static gboolean gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment);
static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad,
    GstEvent * event);
94
95
96
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,
97
    gboolean active);
98
99
100
static gboolean gst_avi_demux_activate_push (GstPad * pad, gboolean active);
static GstFlowReturn gst_avi_demux_chain (GstPad * pad, GstBuffer * buf);

101
102
static GstStateChangeReturn gst_avi_demux_change_state (GstElement * element,
    GstStateChange transition);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
103

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

106
/* GObject methods */
107

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

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

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

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

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

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

159
160
161
  subcaps = gst_caps_new_simple ("application/x-subtitle-avi", NULL);
  subsrctempl = gst_pad_template_new ("subtitle_%02d",
      GST_PAD_SRC, GST_PAD_SOMETIMES, subcaps);
162
163
  gst_element_class_add_pad_template (element_class, audiosrctempl);
  gst_element_class_add_pad_template (element_class, videosrctempl);
164
  gst_element_class_add_pad_template (element_class, subsrctempl);
165
  gst_element_class_add_pad_template (element_class,
David Schleef's avatar
David Schleef committed
166
      gst_static_pad_template_get (&sink_templ));
167
168
169
  gst_element_class_set_details (element_class, &gst_avi_demux_details);
}

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
170
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
171
gst_avi_demux_class_init (GstAviDemuxClass * klass)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
172
{
173
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
174
  GObjectClass *gobject_class = (GObjectClass *) klass;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
175

176
  GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
177
      0, "Demuxer for AVI streams");
178

179
  parent_class = g_type_class_peek_parent (klass);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
180

181
  gobject_class->finalize = gst_avi_demux_finalize;
182
183
  gstelement_class->change_state =
      GST_DEBUG_FUNCPTR (gst_avi_demux_change_state);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
184
185
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
186
187
static void
gst_avi_demux_init (GstAviDemux * avi)
188
{
189
  avi->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink");
190
191
192
  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);
193
194
  gst_pad_set_activatepush_function (avi->sinkpad, gst_avi_demux_activate_push);
  gst_pad_set_chain_function (avi->sinkpad, gst_avi_demux_chain);
195
  gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad);
Wim Taymans's avatar
Wim Taymans committed
196

197
  avi->adapter = gst_adapter_new ();
Wim Taymans's avatar
Wim Taymans committed
198

199
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
200
201
}

202
static void
203
gst_avi_demux_finalize (GObject * object)
204
205
206
{
  GstAviDemux *avi = GST_AVI_DEMUX (object);

207
208
209
  GST_DEBUG ("AVI: finalize");

  g_object_unref (avi->adapter);
210

211
  G_OBJECT_CLASS (parent_class)->finalize (object);
212
213
}

214
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
215
gst_avi_demux_reset (GstAviDemux * avi)
216
{
217
  gint i;
218

219
220
  GST_DEBUG ("AVI: reset");

221
222
  for (i = 0; i < avi->num_streams; i++) {
    g_free (avi->stream[i].strh);
223
    g_free (avi->stream[i].strf.data);
224
225
    if (avi->stream[i].name)
      g_free (avi->stream[i].name);
226
227
228
229
    if (avi->stream[i].initdata)
      gst_buffer_unref (avi->stream[i].initdata);
    if (avi->stream[i].extradata)
      gst_buffer_unref (avi->stream[i].extradata);
230
231
    if (avi->stream[i].pad) {
      gst_pad_set_active (avi->stream[i].pad, FALSE);
232
      gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad);
233
    }
234
235
236
237
    if (avi->stream[i].taglist) {
      gst_tag_list_free (avi->stream[i].taglist);
      avi->stream[i].taglist = NULL;
    }
238
239
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
240

241
242
243
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
244
  avi->num_t_streams = 0;
245

246
  avi->state = GST_AVI_DEMUX_START;
247
  avi->offset = 0;
248

249
250
  g_free (avi->index_entries);
  avi->index_entries = NULL;
251
  avi->index_size = 0;
252
  avi->index_offset = 0;
253
  avi->current_entry = 0;
254
255
  g_free (avi->avih);
  avi->avih = NULL;
256

257
  if (avi->seek_event) {
258
    gst_event_unref (avi->seek_event);
259
260
    avi->seek_event = NULL;
  }
261

262
263
264
265
  if (avi->globaltags)
    gst_tag_list_free (avi->globaltags);
  avi->globaltags = NULL;

266
  avi->got_tags = FALSE;
267
268
  avi->have_eos = FALSE;

269
270
  gst_adapter_clear (avi->adapter);

271
  gst_segment_init (&avi->segment, GST_FORMAT_TIME);
272
273
}

274
/* Index helper */
275
276
277
278
279
280
281
282
283
284
285
286
287
288
static gst_avi_index_entry *
gst_avi_demux_index_last (GstAviDemux * avi, gint stream_nr)
{
  gint i;
  gst_avi_index_entry *result = NULL;

  for (i = avi->index_size - 1; i >= 0; i--) {
    if (avi->index_entries[i].stream_nr == stream_nr) {
      result = &avi->index_entries[i];
      break;
    }
  }
  return result;
}
289

290
static gst_avi_index_entry *
291
gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
292
{
293
  gint i;
294
  gst_avi_index_entry *result = NULL;
295

296
  for (i = start; i < avi->index_size; i++) {
297
298
    if (avi->index_entries[i].stream_nr == stream_nr) {
      result = &avi->index_entries[i];
Ronald S. Bultje's avatar
Ronald S. Bultje committed
299
      break;
300
    }
301
  }
302
  return result;
303
}
304

305
306
307
308
309
310
311
312
313
314
315
/*
 * gst_avi_index_entry:
 * @avi: Avi object
 * @stream_nr: stream number
 * @time: seek time position
 * @flags: index entry flags to match
 *
 * Finds the index entry which time is less or equal than the requested time.
 *
 * Returns: the found index entry or %NULL
 */
316
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
317
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
318
    gint stream_nr, guint64 time, guchar flags)
319
320
321
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
322

323
  GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT " flags:%x",
324
      stream_nr, GST_TIME_ARGS (time), flags);
325

326
327
  i = -1;
  do {
328
    /* get next entry for given stream */
329
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
330
    if (!entry)
331
      break;
332

333
    i = entry->index_nr;
334
335
    if (entry->ts <= time && (entry->flags & flags) == flags)
      last_entry = entry;
336

337
338
    GST_LOG_OBJECT (avi,
        "looking at entry %d / ts:%" GST_TIME_FORMAT " / dur:%" GST_TIME_FORMAT
339
        " flags:%02x", i, GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (entry->dur),
340
        entry->flags);
341
  } while (entry->ts < time);
342

343
344
  return last_entry;
}
345

346
347
/* GstElement methods */

348
#if 0
349
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
350
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
351
{
352
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
353

354
355
356
357
358
359
360
361
362
363
364
  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
365

366
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
367
      src_a_formats : src_v_formats);
368
}
369
#endif
370

371
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
372
373
374
gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
375
376
{
  gboolean res = TRUE;
377
  GstAviDemux *avidemux = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
378

379
  avi_stream_context *stream = gst_pad_get_element_private (pad);
380

381
  GST_LOG_OBJECT (avidemux,
382
383
384
      "Received  src_format:%s, src_value:%" G_GUINT64_FORMAT
      ", dest_format:%s", gst_format_get_name (src_format), src_value,
      gst_format_get_name (*dest_format));
385

386
387
  if (src_format == *dest_format) {
    *dest_value = src_value;
388
389
390
391
392
    goto done;
  }
  if (!stream->strh || !stream->strf.data) {
    res = FALSE;
    goto done;
393
  }
394
  if (stream->strh->type == GST_RIFF_FCC_vids &&
395
396
397
398
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) {
    res = FALSE;
    goto done;
  }
399

400
401
402
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
403
        case GST_FORMAT_BYTES:
404
405
          *dest_value = gst_util_uint64_scale (src_value,
              (guint64) stream->strf.auds->av_bps, GST_SECOND);
406
407
          break;
        case GST_FORMAT_DEFAULT:
408
409
          *dest_value = gst_util_uint64_scale (src_value, stream->strh->rate,
              stream->strh->scale * GST_SECOND);
410
411
412
413
          break;
        default:
          res = FALSE;
          break;
414
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
415
      break;
416
417
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
418
        case GST_FORMAT_TIME:
419
          if (stream->strf.auds->av_bps != 0) {
420
421
            *dest_value = gst_util_uint64_scale (src_value, GST_SECOND,
                (guint64) stream->strf.auds->av_bps);
422
423
          } else
            res = FALSE;
424
425
426
427
          break;
        default:
          res = FALSE;
          break;
428
429
      }
      break;
430
431
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
432
        case GST_FORMAT_TIME:
433
434
          *dest_value = gst_util_uint64_scale (src_value,
              stream->strh->scale * GST_SECOND, (guint64) stream->strh->rate);
435
436
437
438
          break;
        default:
          res = FALSE;
          break;
439
      }
Wim Taymans's avatar
Wim Taymans committed
440
441
      break;
    default:
442
      res = FALSE;
443
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
444

445
done:
446
447
448
  GST_LOG_OBJECT (avidemux,
      "Returning res:%d dest_format:%s dest_value:%" G_GUINT64_FORMAT, res,
      gst_format_get_name (*dest_format), *dest_value);
449
  return res;
450
451
}

452
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
453
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
454
{
455
456
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
457
    GST_QUERY_DURATION,
458
    GST_QUERY_SEEKING,
459
460
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
461

462
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
463
464
}

465
static gboolean
466
gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
467
{
468
  gboolean res = TRUE;
469
  GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
470

471
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
472

473
  if (!stream->strh || !stream->strf.data)
474
    return gst_pad_query_default (pad, query);
475
476
477

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

480
481
482
      GST_DEBUG ("pos query for stream %d: frames %d, bytes %" G_GUINT64_FORMAT,
          stream->num, stream->current_frame, stream->current_byte);

483
      if (stream->strh->type == GST_RIFF_FCC_auds) {
484
485
        if (stream->is_vbr) {
          /* VBR */
486
487
488
          pos = gst_util_uint64_scale ((gint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, (guint64) stream->strh->rate);
          GST_DEBUG_OBJECT (avi, "VBR convert frame %u, time %"
489
              GST_TIME_FORMAT, stream->current_frame, GST_TIME_ARGS (pos));
490
        } else if (stream->strf.auds->av_bps != 0) {
491
          /* CBR */
492
493
494
          pos = gst_util_uint64_scale (stream->current_byte, GST_SECOND,
              (guint64) stream->strf.auds->av_bps);
          GST_DEBUG_OBJECT (avi,
495
496
              "CBR convert bytes %" G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT,
              stream->current_byte, GST_TIME_ARGS (pos));
497
        } else if (stream->total_frames != 0 && stream->total_bytes != 0) {
498
          /* calculate timestamps based on percentage of length */
499
500
          guint64 xlen = avi->avih->us_frame *
              avi->avih->tot_frames * GST_USECOND;
501

502
          if (stream->is_vbr) {
503
            pos = gst_util_uint64_scale (xlen, stream->current_frame,
504
                stream->total_frames);
505
            GST_DEBUG_OBJECT (avi, "VBR perc convert frame %u, time %"
506
507
                GST_TIME_FORMAT, stream->current_frame, GST_TIME_ARGS (pos));
          } else {
508
            pos = gst_util_uint64_scale (xlen, stream->current_byte,
509
                stream->total_bytes);
510
            GST_DEBUG_OBJECT (avi, "CBR perc convert bytes %" G_GUINT64_FORMAT
511
512
513
                ", time %" GST_TIME_FORMAT, stream->current_byte,
                GST_TIME_ARGS (pos));
          }
514
        } else {
515
          /* we don't know */
516
          res = FALSE;
517
518
519
        }
      } else {
        if (stream->strh->rate != 0) {
520
521
          pos = gst_util_uint64_scale ((guint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, (guint64) stream->strh->rate);
522
        } else {
523
          pos = stream->current_frame * avi->avih->us_frame * GST_USECOND;
524
        }
Wim Taymans's avatar
Wim Taymans committed
525
      }
526
527
      if (res) {
        GST_DEBUG ("pos query : %" GST_TIME_FORMAT, GST_TIME_ARGS (pos));
Wim Taymans's avatar
Wim Taymans committed
528
        gst_query_set_position (query, GST_FORMAT_TIME, pos);
529
530
      } else
        GST_WARNING ("pos query failed");
Wim Taymans's avatar
Wim Taymans committed
531
532
533
534
      break;
    }
    case GST_QUERY_DURATION:
    {
535
536
537
538
539
      if (stream->strh->type != GST_RIFF_FCC_auds &&
          stream->strh->type != GST_RIFF_FCC_vids) {
        res = FALSE;
        break;
      }
540
      gst_query_set_duration (query, GST_FORMAT_TIME, stream->duration);
Wim Taymans's avatar
Wim Taymans committed
541
      break;
542
    }
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
    case GST_QUERY_SEEKING:{
      GstFormat fmt;

      gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
      if (fmt == GST_FORMAT_TIME) {
        gboolean seekable = TRUE;

        if (avi->streaming) {
          seekable = FALSE;
        } else {
          if (avi->index_entries == NULL) {
            seekable = FALSE;
            /* FIXME: when building index_entried, count keyframes
               if (!(avi->key_frame_ct > 1))
               seekable = FALSE;
             */
          }
        }

        gst_query_set_seeking (query, GST_FORMAT_TIME, seekable,
            0, stream->duration);
        res = TRUE;
      }
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
568
    default:
569
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
570
571
572
      break;
  }

573
  gst_object_unref (avi);
Wim Taymans's avatar
Wim Taymans committed
574
575
576
  return res;
}

577
#if 0
578
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
579
gst_avi_demux_get_event_mask (GstPad * pad)
580
581
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
582
583
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
584
585
586
587
  };

  return masks;
}
588
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
589

Wim Taymans's avatar
Wim Taymans committed
590
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
591
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
592
593
{
  gboolean res = TRUE;
594
  GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
595

596
  GST_DEBUG_OBJECT (avi,
597
      "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
Wim Taymans's avatar
Wim Taymans committed
598
599
600

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
601
602
      /* handle seeking */
      res = gst_avi_demux_handle_seek (avi, pad, event);
603
      gst_event_unref (event);
Wim Taymans's avatar
Wim Taymans committed
604
605
      break;
    default:
606
      res = gst_pad_event_default (pad, event);
Wim Taymans's avatar
Wim Taymans committed
607
608
      break;
  }
609
610
611

  gst_object_unref (avi);

Wim Taymans's avatar
Wim Taymans committed
612
613
614
  return res;
}

615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
/* streaming helper (push) */

/*
 * gst_avi_demux_peek_chunk_info:
 * @avi: Avi object
 * @tag: holder for tag
 * @size: holder for tag size
 *
 * Peek next chunk info (tag and size)
 *
 * Returns: TRUE when one chunk info has been got
 */
static gboolean
gst_avi_demux_peek_chunk_info (GstAviDemux * avi, guint32 * tag, guint32 * size)
{
  const guint8 *data = NULL;

  if (gst_adapter_available (avi->adapter) < 8) {
    return FALSE;
  }

  data = gst_adapter_peek (avi->adapter, 8);
  *tag = GST_READ_UINT32_LE (data);
  *size = GST_READ_UINT32_LE (data + 4);

  return TRUE;
}

/*
 * gst_avi_demux_peek_chunk:
 * @avi: Avi object
 * @tag: holder for tag
 * @size: holder for tag size
 *
 * Peek enough data for one full chunk
 *
 * Returns: %TRUE when one chunk has been got
 */
static gboolean
gst_avi_demux_peek_chunk (GstAviDemux * avi, guint32 * tag, guint32 * size)
{
  guint32 peek_size = 0;
657
  gint available;
658

659
  if (!gst_avi_demux_peek_chunk_info (avi, tag, size)) {
660
661
    return FALSE;
  }
662
  /* FIXME: shouldn't this check go to gst_avi_demux_peek_chunk_info() already */
663
  if (!(*size) || (*size) == -1) {
664
665
    GST_INFO ("Invalid chunk size %d for tag %" GST_FOURCC_FORMAT,
        *size, GST_FOURCC_ARGS (*tag));
666
667
668
    return FALSE;
  }
  peek_size = (*size + 1) & ~1;
669
  available = gst_adapter_available (avi->adapter);
670

671
672
673
674
  GST_DEBUG ("Need to peek chunk of %d bytes to read chunk %" GST_FOURCC_FORMAT
      ", %d bytes available", *size, GST_FOURCC_ARGS (*tag), available);

  if (available >= (8 + peek_size)) {
675
676
677
678
679
680
681
682
683
    return TRUE;
  } else {
    return FALSE;
  }
}

/* AVI init */

/*
684
685
686
687
688
 * 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
689
 * bytes long. Takes ownership of @buf.
690
691
692
 *
 * Returns: TRUE if the file is a RIFF/AVI file, FALSE otherwise.
 *          Throws an error, caller should error out (fatal).
693
 */
694
static gboolean
695
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
696
{
697
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
698

699
  /* riff_parse posts an error */
700
  if (!gst_riff_parse_file_header (element, buf, &doctype))
701
    return FALSE;
702

703
704
705
706
707
708
709
710
  if (doctype != GST_RIFF_RIFF_AVI)
    goto not_avi;

  return TRUE;

  /* ERRORS */
not_avi:
  {
711
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
712
        ("File is not an AVI file: %" GST_FOURCC_FORMAT,
713
            GST_FOURCC_ARGS (doctype)));
714
715
716
717
    return FALSE;
  }
}

718
719
720
721
722
723
724
/*
 * Read AVI file tag when streaming
 */
static GstFlowReturn
gst_avi_demux_stream_init_push (GstAviDemux * avi)
{
  if (gst_adapter_available (avi->adapter) >= 12) {
725
    GstBuffer *tmp;
726

727
    tmp = gst_adapter_take_buffer (avi->adapter, 12);
728
729
730
731
732
733
734

    GST_DEBUG ("Parsing avi header");
    if (!gst_avi_demux_parse_file_header (GST_ELEMENT (avi), tmp)) {
      return GST_FLOW_ERROR;
    }
    GST_DEBUG ("header ok");
    avi->offset += 12;
735
736

    avi->state = GST_AVI_DEMUX_HEADER;
737
738
739
740
741
742
743
  }
  return GST_FLOW_OK;
}

/*
 * Read AVI file tag
 */
744
static GstFlowReturn
745
gst_avi_demux_stream_init_pull (GstAviDemux * avi)
746
747
748
749
{
  GstFlowReturn res;
  GstBuffer *buf = NULL;

750
751
  res = gst_pad_pull_range (avi->sinkpad, avi->offset, 12, &buf);
  if (res != GST_FLOW_OK)
752
    return res;
753
  else if (!gst_avi_demux_parse_file_header (GST_ELEMENT_CAST (avi), buf))
754
    goto wrong_header;
755
756
757
758

  avi->offset += 12;

  return GST_FLOW_OK;
759
760
761
762
763
764
765

  /* ERRORS */
wrong_header:
  {
    GST_DEBUG_OBJECT (avi, "error parsing file header");
    return GST_FLOW_ERROR;
  }
766
767
}

768
769
770
/* AVI header handling */

/*
771
772
773
774
775
776
777
778
779
780
781
 * 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).
782
 */
783
static gboolean
784
785
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
786
{
787
  gst_riff_avih *avih;
788

789
790
791
792
793
  if (buf == NULL)
    goto no_buffer;

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

795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
  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
813
814

  /* debug stuff */
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
  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;
832
833
834
  gst_buffer_unref (buf);

  return TRUE;
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849

  /* 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;
  }
850
851
}

852
/*
853
 * gst_avi_demux_parse_superindex:
854
 * @avi: caller element (used for debugging/errors).
855
856
857
858
859
860
861
862
863
 * @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.