gstavidemux.c 104 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
/**
 * 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>
 *
 */

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

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

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

58 59 60
GST_DEBUG_CATEGORY_STATIC (avidemux_debug);
#define GST_CAT_DEFAULT avidemux_debug

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
61 62
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_EVENT);

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

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

74
static void gst_avi_demux_reset (GstAviDemux * avi);
75

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

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

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

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

102
static GstElementClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
103

104
/* GObject methods */
105

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

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

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

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

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

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

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

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
164
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
165
gst_avi_demux_class_init (GstAviDemuxClass * klass)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
166
{
167
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
168
  GObjectClass *gobject_class = (GObjectClass *) 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

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

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

190
  avi->adapter = gst_adapter_new ();
Wim Taymans's avatar
Wim Taymans committed
191

192
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
193 194
}

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

200 201 202
  GST_DEBUG ("AVI: finalize");

  g_object_unref (avi->adapter);
203

204
  G_OBJECT_CLASS (parent_class)->finalize (object);
205 206
}

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

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

230 231 232
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
233

234
  avi->state = GST_AVI_DEMUX_START;
235
  avi->offset = 0;
236

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

245 246 247
  if (avi->seek_event)
    gst_event_unref (avi->seek_event);
  avi->seek_event = NULL;
248

249 250 251 252
  if (avi->globaltags)
    gst_tag_list_free (avi->globaltags);
  avi->globaltags = NULL;

253
  avi->got_tags = FALSE;
254 255
  avi->have_eos = FALSE;

256 257
  gst_adapter_clear (avi->adapter);

258
  gst_segment_init (&avi->segment, GST_FORMAT_TIME);
259 260
}

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

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

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

292
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
293 294
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
    gint stream_nr, guint64 time, guint32 flags)
295 296 297
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
298

299 300
  GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT " flags:%d",
      stream_nr, GST_TIME_ARGS (time), flags);
301

302 303
  i = -1;
  do {
304
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
305
    if (!entry)
306
      break;
307

308
    i = entry->index_nr;
309

310 311 312 313
    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);
314 315
    if (entry->ts <= time &&
        (entry->flags & flags) == flags && stream_nr == entry->stream_nr)
316
      last_entry = entry;
317
  } while (entry->ts < time);
318

319 320
  return last_entry;
}
321

322 323
/* GstElement methods */

324
#if 0
325
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
326
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
327
{
328
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
329

330 331 332 333 334 335 336 337 338 339 340
  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
341

342
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
343
      src_a_formats : src_v_formats);
344
}
345
#endif
346

347
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
348 349 350
gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
351 352
{
  gboolean res = TRUE;
353
  GstAviDemux *avidemux = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
354

355
  avi_stream_context *stream = gst_pad_get_element_private (pad);
356

357
  GST_LOG_OBJECT (avidemux,
358 359 360
      "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));
361

362 363
  if (src_format == *dest_format) {
    *dest_value = src_value;
364 365 366 367 368
    goto done;
  }
  if (!stream->strh || !stream->strf.data) {
    res = FALSE;
    goto done;
369
  }
370
  if (stream->strh->type == GST_RIFF_FCC_vids &&
371 372 373 374
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) {
    res = FALSE;
    goto done;
  }
375

376 377 378
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
379
        case GST_FORMAT_BYTES:
380 381 382
          *dest_value =
              gst_util_uint64_scale_int (src_value, stream->strf.auds->av_bps,
              GST_SECOND);
383 384
          break;
        case GST_FORMAT_DEFAULT:
385 386
          *dest_value = gst_util_uint64_scale (src_value, stream->strh->rate,
              stream->strh->scale * GST_SECOND);
387 388 389 390
          break;
        default:
          res = FALSE;
          break;
391
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
392
      break;
393 394
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
395
        case GST_FORMAT_TIME:
396
          if (stream->strf.auds->av_bps != 0) {
397 398
            *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
                stream->strf.auds->av_bps);
399 400
          } else
            res = FALSE;
401 402 403 404
          break;
        default:
          res = FALSE;
          break;
405 406
      }
      break;
407 408
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
409
        case GST_FORMAT_TIME:
410 411 412
          *dest_value =
              gst_util_uint64_scale (src_value,
              stream->strh->scale * GST_SECOND, stream->strh->rate);
413 414 415 416
          break;
        default:
          res = FALSE;
          break;
417
      }
Wim Taymans's avatar
Wim Taymans committed
418 419
      break;
    default:
420
      res = FALSE;
421
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
422

423
done:
424 425 426
  GST_LOG_OBJECT (avidemux,
      "Returning res:%d dest_format:%s dest_value:%" G_GUINT64_FORMAT, res,
      gst_format_get_name (*dest_format), *dest_value);
427
  gst_object_unref (avidemux);
428
  return res;
429 430
}

431
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
432
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
433
{
434 435
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
436
    GST_QUERY_DURATION,
437 438
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
439

440
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
441 442
}

443
static gboolean
444
gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
445
{
446
  gboolean res = TRUE;
447
  GstAviDemux *demux = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
448

449
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
450

451
  if (!stream->strh || !stream->strf.data)
452
    return gst_pad_query_default (pad, query);
453 454 455

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

      if (stream->strh->type == GST_RIFF_FCC_auds) {
459 460
        if (stream->is_vbr) {
          /* VBR */
461 462
          pos = gst_util_uint64_scale_int ((gint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
463 464
          GST_DEBUG_OBJECT (demux, "VBR convert frame %u, time %"
              GST_TIME_FORMAT, stream->current_frame, GST_TIME_ARGS (pos));
465
        } else if (stream->strf.auds->av_bps != 0) {
466
          /* CBR */
467 468
          pos = gst_util_uint64_scale_int (stream->current_byte, GST_SECOND,
              stream->strf.auds->av_bps);
469 470 471
          GST_DEBUG_OBJECT (demux,
              "CBR convert bytes %" G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT,
              stream->current_byte, GST_TIME_ARGS (pos));
472
        } else if (stream->total_frames != 0 && stream->total_bytes != 0) {
473
          /* calculate timestamps based on percentage of length */
474 475 476
          guint64 xlen = demux->avih->us_frame *
              demux->avih->tot_frames * GST_USECOND;

477
          if (stream->is_vbr) {
478 479
            pos = gst_util_uint64_scale_int (xlen, stream->current_frame,
                stream->total_frames);
480 481 482 483 484 485 486 487 488
            GST_DEBUG_OBJECT (demux, "VBR perc convert frame %u, time %"
                GST_TIME_FORMAT, stream->current_frame, GST_TIME_ARGS (pos));
          } else {
            pos = gst_util_uint64_scale_int (xlen, stream->current_byte,
                stream->total_bytes);
            GST_DEBUG_OBJECT (demux, "CBR perc convert bytes %" G_GUINT64_FORMAT
                ", time %" GST_TIME_FORMAT, stream->current_byte,
                GST_TIME_ARGS (pos));
          }
489
        } else {
490
          /* we don't know */
491
          res = FALSE;
492 493 494
        }
      } else {
        if (stream->strh->rate != 0) {
495 496 497
          pos =
              gst_util_uint64_scale_int ((guint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
498 499 500
        } else {
          pos = stream->current_frame * demux->avih->us_frame * GST_USECOND;
        }
Wim Taymans's avatar
Wim Taymans committed
501
      }
502 503
      if (res) {
        GST_DEBUG ("pos query : %" GST_TIME_FORMAT, GST_TIME_ARGS (pos));
Wim Taymans's avatar
Wim Taymans committed
504
        gst_query_set_position (query, GST_FORMAT_TIME, pos);
505 506
      } else
        GST_WARNING ("pos query failed");
Wim Taymans's avatar
Wim Taymans committed
507 508 509 510
      break;
    }
    case GST_QUERY_DURATION:
    {
511 512 513 514 515
      if (stream->strh->type != GST_RIFF_FCC_auds &&
          stream->strh->type != GST_RIFF_FCC_vids) {
        res = FALSE;
        break;
      }
516
      gst_query_set_duration (query, GST_FORMAT_TIME, stream->duration);
Wim Taymans's avatar
Wim Taymans committed
517
      break;
518
    }
Wim Taymans's avatar
Wim Taymans committed
519
    default:
520
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
521 522 523 524 525 526
      break;
  }

  return res;
}

527
#if 0
528
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
529
gst_avi_demux_get_event_mask (GstPad * pad)
530 531
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
532 533
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
534 535 536 537
  };

  return masks;
}
538
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
539

Wim Taymans's avatar
Wim Taymans committed
540
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
541
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
542 543
{
  gboolean res = TRUE;
544
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
545

546
  GST_DEBUG_OBJECT (avi,
547
      "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
Wim Taymans's avatar
Wim Taymans committed
548 549 550

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
551 552 553 554 555 556 557
      /* 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
558 559
      break;
    default:
560
      /* most other events are not very usefull */
Wim Taymans's avatar
Wim Taymans committed
561 562 563
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
564
  gst_event_unref (event);
Wim Taymans's avatar
Wim Taymans committed
565 566 567
  return res;
}

568 569 570 571 572 573 574 575 576 577 578 579 580 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
/* 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;

611
  if (!gst_avi_demux_peek_chunk_info (avi, tag, size)) {
612 613
    return FALSE;
  }
614
  /* FIXME: shouldn't this check go to gst_avi_demux_peek_chunk_info() already */
615
  if (!(*size) || (*size) == -1) {
616 617
    GST_INFO ("Invalid chunk size %d for tag %" GST_FOURCC_FORMAT,
        *size, GST_FOURCC_ARGS (*tag));
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
    return FALSE;
  }
  GST_DEBUG ("Need to peek chunk of %d bytes to read chunk %" GST_FOURCC_FORMAT,
      *size, GST_FOURCC_ARGS (*tag));
  peek_size = (*size + 1) & ~1;

  if (gst_adapter_available (avi->adapter) >= (8 + peek_size)) {
    return TRUE;
  } else {
    return FALSE;
  }
}

/* AVI init */

/*
634 635 636 637 638
 * 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
639
 * bytes long. Takes ownership of @buf.
640 641 642
 *
 * Returns: TRUE if the file is a RIFF/AVI file, FALSE otherwise.
 *          Throws an error, caller should error out (fatal).
643
 */
644
static gboolean
645
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
646
{
647
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
648

649
  /* riff_parse posts an error */
650
  if (!gst_riff_parse_file_header (element, buf, &doctype))
651
    return FALSE;
652

653 654 655 656 657 658 659 660
  if (doctype != GST_RIFF_RIFF_AVI)
    goto not_avi;

  return TRUE;

  /* ERRORS */
not_avi:
  {
661
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
662
        ("File is not an AVI file: %" GST_FOURCC_FORMAT,
663
            GST_FOURCC_ARGS (doctype)));
664 665 666 667
    return FALSE;
  }
}

668 669 670 671 672 673 674
/*
 * Read AVI file tag when streaming
 */
static GstFlowReturn
gst_avi_demux_stream_init_push (GstAviDemux * avi)
{
  if (gst_adapter_available (avi->adapter) >= 12) {
675
    GstBuffer *tmp;
676

677
    tmp = gst_adapter_take_buffer (avi->adapter, 12);
678 679 680 681 682 683 684

    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;
685 686

    avi->state = GST_AVI_DEMUX_HEADER;
687 688 689 690 691 692 693
  }
  return GST_FLOW_OK;
}

/*
 * Read AVI file tag
 */
694
static GstFlowReturn
695
gst_avi_demux_stream_init_pull (GstAviDemux * avi)
696 697 698 699
{
  GstFlowReturn res;
  GstBuffer *buf = NULL;

700 701
  res = gst_pad_pull_range (avi->sinkpad, avi->offset, 12, &buf);
  if (res != GST_FLOW_OK)
702
    return res;
703
  else if (!gst_avi_demux_parse_file_header (GST_ELEMENT_CAST (avi), buf))
704
    goto wrong_header;
705 706 707 708

  avi->offset += 12;

  return GST_FLOW_OK;
709 710 711 712 713 714 715

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

718 719 720
/* AVI header handling */

/*
721 722 723 724 725 726 727 728 729 730 731
 * 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).
732
 */
733
static gboolean
734 735
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
736
{
737
  gst_riff_avih *avih;
738

739 740 741 742 743
  if (buf == NULL)
    goto no_buffer;

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

745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
  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
763 764

  /* debug stuff */
765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
  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;
782 783 784
  gst_buffer_unref (buf);

  return TRUE;
785 786 787 788 789 790 791 792 793 794 795 796 797 798 799

  /* 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;
  }
800 801
}

802
/*
803
 * gst_avi_demux_parse_superindex:
804
 * @avi: caller element (used for debugging/errors).
805 806 807 808 809 810 811 812 813
 * @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.
814 815
 */
static gboolean
816
gst_avi_demux_parse_superindex (GstAviDemux * avi,
817
    GstBuffer * buf, guint64 ** _indexes)
818
{
819
  guint8 *data;
Ronald S. Bultje's avatar