gstavidemux.c 78 KB
Newer Older
1 2
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@temple-baptist.com>
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
19
/* Element-Checklist-Version: 5 */
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
20

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

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

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

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

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

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

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

73 74 75
     static void gst_avi_demux_base_init (GstAviDemuxClass * klass);
     static void gst_avi_demux_class_init (GstAviDemuxClass * klass);
     static void gst_avi_demux_init (GstAviDemux * avi);
76

77
     static void gst_avi_demux_reset (GstAviDemux * avi);
78

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

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

     static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi,
    gboolean update);
     static void gst_avi_demux_loop (GstPad * pad);
     static gboolean gst_avi_demux_sink_activate (GstPad * sinkpad);
     static gboolean gst_avi_demux_sink_activate_pull (GstPad * sinkpad,
101
    gboolean active);
102 103
     static GstStateChangeReturn gst_avi_demux_change_state (GstElement *
    element, GstStateChange transition);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
104

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

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
107
GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
108
gst_avi_demux_get_type (void)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
109 110 111 112 113
{
  static GType avi_demux_type = 0;

  if (!avi_demux_type) {
    static const GTypeInfo avi_demux_info = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
114
      sizeof (GstAviDemuxClass),
115
      (GBaseInitFunc) gst_avi_demux_base_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
116
      NULL,
117
      (GClassInitFunc) gst_avi_demux_class_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
118 119
      NULL,
      NULL,
120
      sizeof (GstAviDemux),
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
121
      0,
122
      (GInstanceInitFunc) gst_avi_demux_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
123
    };
124 125

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

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

133
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
134
gst_avi_demux_base_init (GstAviDemuxClass * klass)
135
{
136
  static const GstElementDetails gst_avi_demux_details =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
137 138 139 140 141 142
      GST_ELEMENT_DETAILS ("Avi demuxer",
      "Codec/Demuxer",
      "Demultiplex an avi file into audio and video",
      "Erik Walthinsen <omega@cse.ogi.edu>\n"
      "Wim Taymans <wim.taymans@chello.be>\n"
      "Ronald Bultje <rbultje@ronald.bitfreak.net>");
143 144 145
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstPadTemplate *videosrctempl, *audiosrctempl;
  GstCaps *audcaps, *vidcaps;
146

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

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

158 159 160
  gst_element_class_add_pad_template (element_class, audiosrctempl);
  gst_element_class_add_pad_template (element_class, videosrctempl);
  gst_element_class_add_pad_template (element_class,
David Schleef's avatar
David Schleef committed
161
      gst_static_pad_template_get (&sink_templ));
162 163 164
  gst_element_class_set_details (element_class, &gst_avi_demux_details);
}

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

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

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

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

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

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

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

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

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

212 213 214
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
215

216
  avi->state = GST_AVI_DEMUX_START;
217
  avi->offset = 0;
218

219 220
  g_free (avi->index_entries);
  avi->index_entries = NULL;
221
  avi->index_size = 0;
222
  avi->index_offset = 0;
223
  avi->current_entry = 0;
224 225
  g_free (avi->avih);
  avi->avih = NULL;
226

227 228 229
  if (avi->seek_event)
    gst_event_unref (avi->seek_event);
  avi->seek_event = NULL;
230

231
  gst_segment_init (&avi->segment, GST_FORMAT_TIME);
232 233
}

234
static gst_avi_index_entry *
235
gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
236
{
237
  gint i;
238
  gst_avi_index_entry *result = NULL;
239

240
  for (i = start; i < avi->index_size; i++) {
241 242 243 244
    gst_avi_index_entry *entry = &avi->index_entries[i];

    if (entry->stream_nr == stream_nr) {
      result = entry;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
245
      break;
246
    }
247
  }
248

249
  return result;
250
}
251

252
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
253 254
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
    gint stream_nr, guint64 time, guint32 flags)
255 256 257
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
258

259 260
  GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT " flags:%d",
      stream_nr, GST_TIME_ARGS (time), flags);
261 262
  i = -1;
  do {
263
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
264 265
    if (!entry)
      return NULL;
266

267
    i = entry->index_nr;
268

269 270 271 272
    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);
273
    if (entry->ts <= time && (entry->flags & flags) == flags)
274
      last_entry = entry;
275
  } while (entry->ts < time);
276

277 278
  return last_entry;
}
279

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
280

281
#if 0
282
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
283
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
284
{
285
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
286

287 288 289 290 291 292 293 294 295 296 297
  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
298

299
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
300
      src_a_formats : src_v_formats);
301
}
302
#endif
303

304
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
305 306 307
gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
308 309
{
  gboolean res = TRUE;
310
  GstAviDemux *avidemux = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
311

312
  avi_stream_context *stream = gst_pad_get_element_private (pad);
313

314 315 316 317
  GST_LOG_OBJECT (avidemux,
      "Received  src_format:%d, src_value:%lld, dest_format:%d", src_format,
      src_value, *dest_format);

318 319
  if (src_format == *dest_format) {
    *dest_value = src_value;
320 321 322 323 324
    goto done;
  }
  if (!stream->strh || !stream->strf.data) {
    res = FALSE;
    goto done;
325
  }
326
  if (stream->strh->type == GST_RIFF_FCC_vids &&
327 328 329 330
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) {
    res = FALSE;
    goto done;
  }
331

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

379 380 381 382
done:
  GST_LOG_OBJECT (avidemux, "Returning res:%d dest_format:%d dest_value:%lld",
      res, *dest_format, *dest_value);
  gst_object_unref (avidemux);
383
  return res;
384 385
}

386
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
387
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
388
{
389 390
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
391
    GST_QUERY_DURATION,
392 393
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
394

395
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
396 397
}

398
static gboolean
399
gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
400
{
401
  gboolean res = TRUE;
402
  GstAviDemux *demux = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
403

404
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
405

406 407 408 409 410
  if (!stream->strh || !stream->strf.data)
    return FALSE;

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

      if (stream->strh->type == GST_RIFF_FCC_auds) {
414 415
        if (!stream->is_vbr) {
          /* CBR */
416 417
          pos = gst_util_uint64_scale_int ((gint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
418
        } else if (stream->strf.auds->av_bps != 0) {
419
          /* VBR */
420 421
          pos = gst_util_uint64_scale_int (stream->current_byte, GST_SECOND,
              stream->strf.auds->av_bps);
422
        } else if (stream->total_frames != 0 && stream->total_bytes != 0) {
423
          /* calculate timestamps based on percentage of length */
424 425 426
          guint64 xlen = demux->avih->us_frame *
              demux->avih->tot_frames * GST_USECOND;

427
          if (stream->is_vbr)
428 429
            pos = gst_util_uint64_scale_int (xlen, stream->current_byte,
                stream->total_bytes);
430 431 432
          else
            pos = gst_util_uint64_scale_int (xlen, stream->current_frame,
                stream->total_frames);
433
        } else {
434
          res = FALSE;
435 436 437
        }
      } else {
        if (stream->strh->rate != 0) {
438 439 440
          pos =
              gst_util_uint64_scale_int ((guint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
441 442 443
        } else {
          pos = stream->current_frame * demux->avih->us_frame * GST_USECOND;
        }
Wim Taymans's avatar
Wim Taymans committed
444
      }
445
      if (res)
Wim Taymans's avatar
Wim Taymans committed
446 447 448 449 450
        gst_query_set_position (query, GST_FORMAT_TIME, pos);
      break;
    }
    case GST_QUERY_DURATION:
    {
451 452 453 454 455 456 457 458 459 460 461 462
      if (stream->strh->type != GST_RIFF_FCC_auds &&
          stream->strh->type != GST_RIFF_FCC_vids) {
        res = FALSE;
        break;
      }

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

464 465 466 467 468 469
        len =
            gst_util_uint64_scale ((guint64) stream->strh->length *
            stream->strh->scale, GST_SECOND, stream->strh->rate);

        gst_query_set_duration (query, GST_FORMAT_TIME, len);
      }
Wim Taymans's avatar
Wim Taymans committed
470
      break;
471
    }
Wim Taymans's avatar
Wim Taymans committed
472
    default:
473
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
474 475 476 477 478 479
      break;
  }

  return res;
}

480
#if 0
481
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
482
gst_avi_demux_get_event_mask (GstPad * pad)
483 484
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
485 486
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
487 488 489 490
  };

  return masks;
}
491
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
492

Wim Taymans's avatar
Wim Taymans committed
493
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
494
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
495 496
{
  gboolean res = TRUE;
497
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Wim Taymans's avatar
Wim Taymans committed
498
  avi_stream_context *stream;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
499

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
500
  GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi,
501
      "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
502 503
  if (!avi->index_entries) {
    GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi, "no index entries, returning");
504
    return FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
505
  }
506

Wim Taymans's avatar
Wim Taymans committed
507 508 509 510
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
511
    {
512 513
      /* FIXME, this seeking code is not correct, look at wavparse for 
       * a better example */
514 515
      GstFormat format;
      GstSeekFlags flags;
516 517 518 519 520 521 522 523 524 525 526 527
      gdouble rate;
      gint64 start, stop;
      gint64 tstart, tstop;
      gint64 duration;
      GstFormat tformat = GST_FORMAT_TIME;
      GstSeekType start_type, stop_type;
      gboolean update_start = TRUE;
      gboolean update_stop = TRUE;

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

528 529 530
      GST_DEBUG_OBJECT (avi,
          "seek format %d, flags:%d, %08x, start:%lld, stop:%lld", format,
          flags, stream->strh->type, start, stop);
531 532 533 534 535 536 537 538 539 540 541

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

543 544 545 546
      if (!gst_pad_query_duration (stream->pad, &tformat, &duration)) {
        res = FALSE;
        goto done;
      }
547

548 549
      switch (start_type) {
        case GST_SEEK_TYPE_CUR:
550
          tstart = avi->segment.start + tstart;
551 552 553 554 555
          break;
        case GST_SEEK_TYPE_END:
          tstart = duration + tstart;
          break;
        case GST_SEEK_TYPE_NONE:
556
          tstart = avi->segment.start;
557 558 559 560 561 562
          update_start = FALSE;
          break;
        case GST_SEEK_TYPE_SET:
          break;
      }
      tstart = CLAMP (tstart, 0, duration);
563

564 565
      switch (stop_type) {
        case GST_SEEK_TYPE_CUR:
566
          tstop = avi->segment.stop + tstop;
567
          break;
568 569 570 571
        case GST_SEEK_TYPE_END:
          tstop = duration + tstop;
          break;
        case GST_SEEK_TYPE_NONE:
572
          tstop = avi->segment.stop;
573 574 575
          update_stop = FALSE;
          break;
        case GST_SEEK_TYPE_SET:
576
          break;
Wim Taymans's avatar
Wim Taymans committed
577
      }
578 579 580
      tstop = CLAMP (tstop, 0, duration);

      /* now store the values */
581 582 583 584
      avi->segment.rate = rate;
      avi->segment.flags = flags;
      avi->segment.start = tstart;
      avi->segment.stop = tstop;
585 586

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

589
    }
Wim Taymans's avatar
Wim Taymans committed
590 591 592 593
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
594

595
done:
Wim Taymans's avatar
Wim Taymans committed
596 597
  gst_event_unref (event);

598
  GST_DEBUG_OBJECT (avi, "returning %d", res);
Wim Taymans's avatar
Wim Taymans committed
599 600 601
  return res;
}

602 603 604 605 606 607 608 609 610 611
/**
 * gst_avi_demux_parse_file_header:
 * @element: caller element (used for errors/debug).
 * @buf: input data to be used for parsing.
 *
 * "Open" a RIFF/AVI file. The buffer should be at least 12
 * bytes long. Discards buffer after use.
 *
 * Returns: TRUE if the file is a RIFF/AVI file, FALSE otherwise.
 *          Throws an error, caller should error out (fatal).
612 613
 */

614
static gboolean
615
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
616
{
617
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
618

619
  if (!gst_riff_parse_file_header (element, buf, &doctype))
620
    return FALSE;
621

622 623 624 625 626 627 628 629
  if (doctype != GST_RIFF_RIFF_AVI)
    goto not_avi;

  return TRUE;

  /* ERRORS */
not_avi:
  {
630
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
631
        ("File is not an AVI file: %" GST_FOURCC_FORMAT,
632
            GST_FOURCC_ARGS (doctype)));
633 634 635 636
    return FALSE;
  }
}

637 638 639 640 641 642 643 644 645 646
static GstFlowReturn
gst_avi_demux_stream_init (GstAviDemux * avi)
{
  GstFlowReturn res;
  GstBuffer *buf = NULL;

  if ((res = gst_pad_pull_range (avi->sinkpad,
              avi->offset, 12, &buf)) != GST_FLOW_OK)
    return res;
  else if (!gst_avi_demux_parse_file_header (GST_ELEMENT (avi), buf))
647
    goto wrong_header;
648 649 650 651

  avi->offset += 12;

  return GST_FLOW_OK;
652 653 654 655 656 657 658 659

  /* ERRORS */
wrong_header:
  {
    GST_DEBUG_OBJECT (avi, "error parsing file header");
    gst_buffer_unref (buf);
    return GST_FLOW_ERROR;
  }
660 661 662 663 664 665 666 667 668 669 670 671 672 673
}

/**
 * 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).
674 675
 */

676
static gboolean
677 678
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
679
{
680
  gst_riff_avih *avih;
681

682 683 684 685 686
  if (buf == NULL)
    goto no_buffer;

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

688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705
  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
706 707

  /* debug stuff */
708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
  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;
725 726 727
  gst_buffer_unref (buf);

  return TRUE;
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742

  /* 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;
  }
743 744
}

745 746 747 748 749 750 751 752 753 754 755 756
/**
 * gst_avi_demux_parse_superindex:
 * @element: caller element (used for debugging/errors).
 * @buf: input data to use for parsing.
 * @locations: locations in the file (byte-offsets) that contain
 *             the actual indexes (see get_avi_demux_parse_subindex()).
 *             The array ends with GST_BUFFER_OFFSET_NONE.
 *
 * Reads superindex (openDML-2 spec stuff) from the provided data.
 *
 * Returns: TRUE on success, FALSE otherwise. Indexes should be skipped
 *          on error, but they are not fatal.
757 758 759
 */

static gboolean
760 761
gst_avi_demux_parse_superindex (GstElement * element,
    GstBuffer * buf, guint64 ** _indexes)
762
{
763
  guint8 *data;
764 765 766
  gint bpe = 16, num, i;
  guint64 *indexes;

767 768
  *_indexes = NULL;

769 770 771
  if (buf == NULL)
    goto no_buffer;

772
  if (GST_BUFFER_SIZE (buf) < 24)
773
    goto too_small;
774

775 776
  data = GST_BUFFER_DATA (buf);

777 778 779 780 781
  /* check type of index. The opendml2 specs state that
   * there should be 4 dwords per array entry. Type can be
   * either frame or field (and we don't care). */
  if (GST_READ_UINT16_LE (data) != 4 ||
      (data[2] & 0xfe) != 0x0 || data[3] != 0x0) {
782 783
    GST_WARNING_OBJECT (element,
        "Superindex for stream %d has unexpected "
784 785 786 787 788 789 790 791 792 793 794 795
        "size_entry %d (bytes) or flags 0x%02x/0x%02x",
        GST_READ_UINT16_LE (data), data[2], data[3]);
    bpe = GST_READ_UINT16_LE (data) * 4;
  }
  num = GST_READ_UINT32_LE (&data[4]);

  indexes = g_new (guint64, num + 1);
  for (i = 0; i < num; i++) {
    if (GST_BUFFER_SIZE (buf) < 24 + bpe * (i + 1))
      break;
    indexes[i] = GST_READ_UINT64_LE (&data[24 + bpe * i]);
  }
796 797
  indexes[i] = GST_BUFFER_OFFSET_NONE;
  *_indexes = indexes;
798 799 800 801

  gst_buffer_unref (buf);

  return TRUE;
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816

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

819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
/**
 * gst_avi_demux_parse_subindex:
 * @element: caller element (used for errors/debug).
 * @buf: input data to use for parsing.
 * @stream: stream context.
 * @entries_list: a list (returned by the function) containing all the
 *           indexes parsed in this specific subindex. The first
 *           entry is also a pointer to allocated memory that needs
 *           to be free´ed. May be NULL if no supported indexes were
 *           found.
 *
 * Reads superindex (openDML-2 spec stuff) from the provided data.
 * The buffer will be discarded after use.
 *
 * Returns: TRUE on success, FALSE otherwise. Errors are fatal, we
 *          throw an error, caller should bail out asap.
 */

837
static gboolean
838 839
gst_avi_demux_parse_subindex (GstElement * element,
    GstBuffer * buf, avi_stream_context * stream, GList ** _entries_list)
840
{
841 842 843
  guint8 *data = GST_BUFFER_DATA (buf);
  gint bpe, num, x;
  guint64 baseoff;
844
  gst_avi_index_entry *entries, *entry;
845
  GList *entries_list = NULL;
846 847
  GstFormat format = GST_FORMAT_TIME;

848
  /* check size */