gstavidemux.c 107 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 31
 * This element supports both push and pull-based scheduling, depending on the
 * 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 145 146
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstPadTemplate *videosrctempl, *audiosrctempl;
  GstCaps *audcaps, *vidcaps;
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
  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
162
      gst_static_pad_template_get (&sink_templ));
163 164 165
  gst_element_class_set_details (element_class, &gst_avi_demux_details);
}

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

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

175
  parent_class = g_type_class_peek_parent (klass);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
176

177
  gobject_class->finalize = gst_avi_demux_finalize;
Wim Taymans's avatar
Wim Taymans committed
178
  gstelement_class->change_state = gst_avi_demux_change_state;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
179 180
}

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

192
  avi->adapter = gst_adapter_new ();
Wim Taymans's avatar
Wim Taymans committed
193

194
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
195 196
}

197
static void
198
gst_avi_demux_finalize (GObject * object)
199 200 201
{
  GstAviDemux *avi = GST_AVI_DEMUX (object);

202 203 204
  GST_DEBUG ("AVI: finalize");

  g_object_unref (avi->adapter);
205

206
  G_OBJECT_CLASS (parent_class)->finalize (object);
207 208
}

209
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
210
gst_avi_demux_reset (GstAviDemux * avi)
211
{
212
  gint i;
213

214 215
  for (i = 0; i < avi->num_streams; i++) {
    g_free (avi->stream[i].strh);
216
    g_free (avi->stream[i].strf.data);
217 218
    if (avi->stream[i].name)
      g_free (avi->stream[i].name);
219 220 221 222
    if (avi->stream[i].initdata)
      gst_buffer_unref (avi->stream[i].initdata);
    if (avi->stream[i].extradata)
      gst_buffer_unref (avi->stream[i].extradata);
223 224
    if (avi->stream[i].pad)
      gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad);
225 226 227 228
    if (avi->stream[i].taglist) {
      gst_tag_list_free (avi->stream[i].taglist);
      avi->stream[i].taglist = NULL;
    }
229 230
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
231

232 233 234
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
235

236
  avi->state = GST_AVI_DEMUX_START;
237
  avi->offset = 0;
238

239 240
  g_free (avi->index_entries);
  avi->index_entries = NULL;
241
  avi->index_size = 0;
242
  avi->index_offset = 0;
243
  avi->current_entry = 0;
244 245
  g_free (avi->avih);
  avi->avih = NULL;
246

247 248 249
  if (avi->seek_event)
    gst_event_unref (avi->seek_event);
  avi->seek_event = NULL;
250

251 252 253 254
  if (avi->globaltags)
    gst_tag_list_free (avi->globaltags);
  avi->globaltags = NULL;

255
  avi->got_tags = FALSE;
256 257
  avi->have_eos = FALSE;

258 259
  gst_adapter_clear (avi->adapter);

260
  gst_segment_init (&avi->segment, GST_FORMAT_TIME);
261 262
}

263
/* Index helper */
264 265 266 267 268 269 270 271 272 273 274 275 276 277
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;
}
278

279
static gst_avi_index_entry *
280
gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
281
{
282
  gint i;
283
  gst_avi_index_entry *result = NULL;
284

285
  for (i = start; i < avi->index_size; i++) {
286 287
    if (avi->index_entries[i].stream_nr == stream_nr) {
      result = &avi->index_entries[i];
Ronald S. Bultje's avatar
Ronald S. Bultje committed
288
      break;
289
    }
290
  }
291
  return result;
292
}
293

294 295 296 297 298 299 300 301 302 303 304
/*
 * 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
 */
305
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
306 307
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
    gint stream_nr, guint64 time, guint32 flags)
308 309 310
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
311

312
  GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT " flags:%x",
313
      stream_nr, GST_TIME_ARGS (time), flags);
314

315 316
  i = -1;
  do {
317
    /* get next entry for given stream */
318
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
319
    if (!entry)
320
      break;
321

322
    i = entry->index_nr;
323 324
    if (entry->ts <= time && (entry->flags & flags) == flags)
      last_entry = entry;
325

326 327 328 329
    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);
330
  } while (entry->ts < time);
331

332 333
  return last_entry;
}
334

335 336
/* GstElement methods */

337
#if 0
338
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
339
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
340
{
341
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
342

343 344 345 346 347 348 349 350 351 352 353
  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
354

355
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
356
      src_a_formats : src_v_formats);
357
}
358
#endif
359

360
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
361 362 363
gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
364 365
{
  gboolean res = TRUE;
366
  GstAviDemux *avidemux = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
367

368
  avi_stream_context *stream = gst_pad_get_element_private (pad);
369

370
  GST_LOG_OBJECT (avidemux,
371 372 373
      "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));
374

375 376
  if (src_format == *dest_format) {
    *dest_value = src_value;
377 378 379 380 381
    goto done;
  }
  if (!stream->strh || !stream->strf.data) {
    res = FALSE;
    goto done;
382
  }
383
  if (stream->strh->type == GST_RIFF_FCC_vids &&
384 385 386 387
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) {
    res = FALSE;
    goto done;
  }
388

389 390 391
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
392
        case GST_FORMAT_BYTES:
393 394
          *dest_value = gst_util_uint64_scale (src_value,
              (guint64) stream->strf.auds->av_bps, GST_SECOND);
395 396
          break;
        case GST_FORMAT_DEFAULT:
397 398
          *dest_value = gst_util_uint64_scale (src_value, stream->strh->rate,
              stream->strh->scale * GST_SECOND);
399 400 401 402
          break;
        default:
          res = FALSE;
          break;
403
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
404
      break;
405 406
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
407
        case GST_FORMAT_TIME:
408
          if (stream->strf.auds->av_bps != 0) {
409 410
            *dest_value = gst_util_uint64_scale (src_value, GST_SECOND,
                (guint64) stream->strf.auds->av_bps);
411 412
          } else
            res = FALSE;
413 414 415 416
          break;
        default:
          res = FALSE;
          break;
417 418
      }
      break;
419 420
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
421
        case GST_FORMAT_TIME:
422 423
          *dest_value = gst_util_uint64_scale (src_value,
              stream->strh->scale * GST_SECOND, (guint64) stream->strh->rate);
424 425 426 427
          break;
        default:
          res = FALSE;
          break;
428
      }
Wim Taymans's avatar
Wim Taymans committed
429 430
      break;
    default:
431
      res = FALSE;
432
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
433

434
done:
435 436 437
  GST_LOG_OBJECT (avidemux,
      "Returning res:%d dest_format:%s dest_value:%" G_GUINT64_FORMAT, res,
      gst_format_get_name (*dest_format), *dest_value);
438
  gst_object_unref (avidemux);
439
  return res;
440 441
}

442
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
443
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
444
{
445 446
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
447
    GST_QUERY_DURATION,
448 449
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
450

451
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
452 453
}

454
static gboolean
455
gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
456
{
457
  gboolean res = TRUE;
458
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
459

460
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
461

462
  if (!stream->strh || !stream->strf.data)
463
    return gst_pad_query_default (pad, query);
464 465 466

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

469 470 471
      GST_DEBUG ("pos query for stream %d: frames %d, bytes %" G_GUINT64_FORMAT,
          stream->num, stream->current_frame, stream->current_byte);

472
      if (stream->strh->type == GST_RIFF_FCC_auds) {
473 474
        if (stream->is_vbr) {
          /* VBR */
475 476 477
          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 %"
478
              GST_TIME_FORMAT, stream->current_frame, GST_TIME_ARGS (pos));
479
        } else if (stream->strf.auds->av_bps != 0) {
480
          /* CBR */
481 482 483
          pos = gst_util_uint64_scale (stream->current_byte, GST_SECOND,
              (guint64) stream->strf.auds->av_bps);
          GST_DEBUG_OBJECT (avi,
484 485
              "CBR convert bytes %" G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT,
              stream->current_byte, GST_TIME_ARGS (pos));
486
        } else if (stream->total_frames != 0 && stream->total_bytes != 0) {
487
          /* calculate timestamps based on percentage of length */
488 489
          guint64 xlen = avi->avih->us_frame *
              avi->avih->tot_frames * GST_USECOND;
490

491
          if (stream->is_vbr) {
492
            pos = gst_util_uint64_scale (xlen, stream->current_frame,
493
                stream->total_frames);
494
            GST_DEBUG_OBJECT (avi, "VBR perc convert frame %u, time %"
495 496
                GST_TIME_FORMAT, stream->current_frame, GST_TIME_ARGS (pos));
          } else {
497
            pos = gst_util_uint64_scale (xlen, stream->current_byte,
498
                stream->total_bytes);
499
            GST_DEBUG_OBJECT (avi, "CBR perc convert bytes %" G_GUINT64_FORMAT
500 501 502
                ", time %" GST_TIME_FORMAT, stream->current_byte,
                GST_TIME_ARGS (pos));
          }
503
        } else {
504
          /* we don't know */
505
          res = FALSE;
506 507 508
        }
      } else {
        if (stream->strh->rate != 0) {
509 510
          pos = gst_util_uint64_scale ((guint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, (guint64) stream->strh->rate);
511
        } else {
512
          pos = stream->current_frame * avi->avih->us_frame * GST_USECOND;
513
        }
Wim Taymans's avatar
Wim Taymans committed
514
      }
515 516
      if (res) {
        GST_DEBUG ("pos query : %" GST_TIME_FORMAT, GST_TIME_ARGS (pos));
Wim Taymans's avatar
Wim Taymans committed
517
        gst_query_set_position (query, GST_FORMAT_TIME, pos);
518 519
      } else
        GST_WARNING ("pos query failed");
Wim Taymans's avatar
Wim Taymans committed
520 521 522 523
      break;
    }
    case GST_QUERY_DURATION:
    {
524 525 526 527 528
      if (stream->strh->type != GST_RIFF_FCC_auds &&
          stream->strh->type != GST_RIFF_FCC_vids) {
        res = FALSE;
        break;
      }
529
      gst_query_set_duration (query, GST_FORMAT_TIME, stream->duration);
Wim Taymans's avatar
Wim Taymans committed
530
      break;
531
    }
Wim Taymans's avatar
Wim Taymans committed
532
    default:
533
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
534 535 536 537 538 539
      break;
  }

  return res;
}

540
#if 0
541
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
542
gst_avi_demux_get_event_mask (GstPad * pad)
543 544
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
545 546
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
547 548 549 550
  };

  return masks;
}
551
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
552

Wim Taymans's avatar
Wim Taymans committed
553
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
554
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
555 556
{
  gboolean res = TRUE;
557
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
558

559
  GST_DEBUG_OBJECT (avi,
560
      "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
Wim Taymans's avatar
Wim Taymans committed
561 562 563

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
564 565 566 567 568 569 570
      /* handle seeking */
      res = gst_avi_demux_handle_seek (avi, pad, event);
      break;
    case GST_EVENT_QOS:
      /* FIXME, we can do something clever here like skip to the next keyframe
       * based on the QoS values. */
      res = FALSE;
Wim Taymans's avatar
Wim Taymans committed
571 572
      break;
    default:
573
      /* most other events are not very usefull */
Wim Taymans's avatar
Wim Taymans committed
574 575 576
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
577
  gst_event_unref (event);
Wim Taymans's avatar
Wim Taymans committed
578 579 580
  return res;
}

581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
/* 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;
623
  gint available;
624

625
  if (!gst_avi_demux_peek_chunk_info (avi, tag, size)) {
626 627
    return FALSE;
  }
628
  /* FIXME: shouldn't this check go to gst_avi_demux_peek_chunk_info() already */
629
  if (!(*size) || (*size) == -1) {
630 631
    GST_INFO ("Invalid chunk size %d for tag %" GST_FOURCC_FORMAT,
        *size, GST_FOURCC_ARGS (*tag));
632 633 634
    return FALSE;
  }
  peek_size = (*size + 1) & ~1;
635
  available = gst_adapter_available (avi->adapter);
636

637 638 639 640
  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)) {
641 642 643 644 645 646 647 648 649
    return TRUE;
  } else {
    return FALSE;
  }
}

/* AVI init */

/*
650 651 652 653 654
 * 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
655
 * bytes long. Takes ownership of @buf.
656 657 658
 *
 * Returns: TRUE if the file is a RIFF/AVI file, FALSE otherwise.
 *          Throws an error, caller should error out (fatal).
659
 */
660
static gboolean
661
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
662
{
663
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
664

665
  /* riff_parse posts an error */
666
  if (!gst_riff_parse_file_header (element, buf, &doctype))
667
    return FALSE;
668

669 670 671 672 673 674 675 676
  if (doctype != GST_RIFF_RIFF_AVI)
    goto not_avi;

  return TRUE;

  /* ERRORS */
not_avi:
  {
677
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
678
        ("File is not an AVI file: %" GST_FOURCC_FORMAT,
679
            GST_FOURCC_ARGS (doctype)));
680 681 682 683
    return FALSE;
  }
}

684 685 686 687 688 689 690
/*
 * Read AVI file tag when streaming
 */
static GstFlowReturn
gst_avi_demux_stream_init_push (GstAviDemux * avi)
{
  if (gst_adapter_available (avi->adapter) >= 12) {
691
    GstBuffer *tmp;
692

693
    tmp = gst_adapter_take_buffer (avi->adapter, 12);
694 695 696 697 698 699 700

    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;
701 702

    avi->state = GST_AVI_DEMUX_HEADER;
703 704 705 706 707 708 709
  }
  return GST_FLOW_OK;
}

/*
 * Read AVI file tag
 */
710
static GstFlowReturn
711
gst_avi_demux_stream_init_pull (GstAviDemux * avi)
712 713 714 715
{
  GstFlowReturn res;
  GstBuffer *buf = NULL;

716 717
  res = gst_pad_pull_range (avi->sinkpad, avi->offset, 12, &buf);
  if (res != GST_FLOW_OK)
718
    return res;
719
  else if (!gst_avi_demux_parse_file_header (GST_ELEMENT_CAST (avi), buf))
720
    goto wrong_header;
721 722 723 724

  avi->offset += 12;

  return GST_FLOW_OK;
725 726 727 728 729 730 731

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

734 735 736
/* AVI header handling */

/*
737 738 739 740 741 742 743 744 745 746 747
 * 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).
748
 */
749
static gboolean
750 751
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
752
{
753
  gst_riff_avih *avih;
754

755 756 757 758 759
  if (buf == NULL)
    goto no_buffer;

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

761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
  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
779 780

  /* debug stuff */