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
  gst_pad_set_activatepush_function (avi->sinkpad, gst_avi_demux_activate_push);
194
  gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad);
195
  gst_pad_set_chain_function (avi->sinkpad, gst_avi_demux_chain);
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
  for (i = 0; i < avi->num_streams; i++) {
    g_free (avi->stream[i].strh);
221
    g_free (avi->stream[i].strf.data);
222 223
    if (avi->stream[i].name)
      g_free (avi->stream[i].name);
224 225 226 227
    if (avi->stream[i].initdata)
      gst_buffer_unref (avi->stream[i].initdata);
    if (avi->stream[i].extradata)
      gst_buffer_unref (avi->stream[i].extradata);
228 229
    if (avi->stream[i].pad) {
      gst_pad_set_active (avi->stream[i].pad, FALSE);
230
      gst_element_remove_pad (GST_ELEMENT (avi), avi->stream[i].pad);
231
    }
232 233 234 235
    if (avi->stream[i].taglist) {
      gst_tag_list_free (avi->stream[i].taglist);
      avi->stream[i].taglist = NULL;
    }
236 237
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
238

239 240 241
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
242
  avi->num_t_streams = 0;
243

244
  avi->state = GST_AVI_DEMUX_START;
245
  avi->offset = 0;
246

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

255 256 257
  if (avi->seek_event)
    gst_event_unref (avi->seek_event);
  avi->seek_event = NULL;
258

259 260 261 262
  if (avi->globaltags)
    gst_tag_list_free (avi->globaltags);
  avi->globaltags = NULL;

263
  avi->got_tags = FALSE;
264 265
  avi->have_eos = FALSE;

266 267
  gst_adapter_clear (avi->adapter);

268
  gst_segment_init (&avi->segment, GST_FORMAT_TIME);
269 270
}

271
/* Index helper */
272 273 274 275 276 277 278 279 280 281 282 283 284 285
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;
}
286

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

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

302 303 304 305 306 307 308 309 310 311 312
/*
 * 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
 */
313
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
314
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
315
    gint stream_nr, guint64 time, guchar flags)
316 317 318
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
319

320
  GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT " flags:%x",
321
      stream_nr, GST_TIME_ARGS (time), flags);
322

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

330
    i = entry->index_nr;
331 332
    if (entry->ts <= time && (entry->flags & flags) == flags)
      last_entry = entry;
333

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

340 341
  return last_entry;
}
342

343 344
/* GstElement methods */

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

351 352 353 354 355 356 357 358 359 360 361
  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
362

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

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

376
  avi_stream_context *stream = gst_pad_get_element_private (pad);
377

378
  GST_LOG_OBJECT (avidemux,
379 380 381
      "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));
382

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

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

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

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

460
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
461 462
}

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

469
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
470

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

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

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

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

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

  return res;
}

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

  return masks;
}
585
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
586

Wim Taymans's avatar
Wim Taymans committed
587
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
588
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
589 590
{
  gboolean res = TRUE;
591
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
592

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

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
598 599 600 601 602 603 604
      /* 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
605 606
      break;
    default:
607
      /* most other events are not very usefull */
Wim Taymans's avatar
Wim Taymans committed
608 609 610
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
611
  gst_event_unref (event);
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