gstavidemux.c 79.2 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 211 212
    if (avi->stream[i].taglist) {
      gst_tag_list_free (avi->stream[i].taglist);
      avi->stream[i].taglist = NULL;
    }
213 214
  }
  memset (&avi->stream, 0, sizeof (avi->stream));
215

216 217 218
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
219

220
  avi->state = GST_AVI_DEMUX_START;
221
  avi->offset = 0;
222

223 224
  g_free (avi->index_entries);
  avi->index_entries = NULL;
225
  avi->index_size = 0;
226
  avi->index_offset = 0;
227
  avi->current_entry = 0;
228 229
  g_free (avi->avih);
  avi->avih = NULL;
230

231 232 233
  if (avi->seek_event)
    gst_event_unref (avi->seek_event);
  avi->seek_event = NULL;
234

235 236
  avi->got_tags = FALSE;

237
  gst_segment_init (&avi->segment, GST_FORMAT_TIME);
238 239
}

240
static gst_avi_index_entry *
241
gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
242
{
243
  gint i;
244
  gst_avi_index_entry *result = NULL;
245

246
  for (i = start; i < avi->index_size; i++) {
247 248 249 250
    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
251
      break;
252
    }
253
  }
254

255
  return result;
256
}
257

258
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
259 260
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
    gint stream_nr, guint64 time, guint32 flags)
261 262 263
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
264

265 266
  GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT " flags:%d",
      stream_nr, GST_TIME_ARGS (time), flags);
267 268
  i = -1;
  do {
269
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
270 271
    if (!entry)
      return NULL;
272

273
    i = entry->index_nr;
274

275 276 277 278
    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);
279
    if (entry->ts <= time && (entry->flags & flags) == flags)
280
      last_entry = entry;
281
  } while (entry->ts < time);
282

283 284
  return last_entry;
}
285

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
286

287
#if 0
288
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
289
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
290
{
291
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
292

293 294 295 296 297 298 299 300 301 302 303
  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
304

305
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
306
      src_a_formats : src_v_formats);
307
}
308
#endif
309

310
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
311 312 313
gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
314 315
{
  gboolean res = TRUE;
316
  GstAviDemux *avidemux = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
317

318
  avi_stream_context *stream = gst_pad_get_element_private (pad);
319

320 321 322 323
  GST_LOG_OBJECT (avidemux,
      "Received  src_format:%d, src_value:%lld, dest_format:%d", src_format,
      src_value, *dest_format);

324 325
  if (src_format == *dest_format) {
    *dest_value = src_value;
326 327 328 329 330
    goto done;
  }
  if (!stream->strh || !stream->strf.data) {
    res = FALSE;
    goto done;
331
  }
332
  if (stream->strh->type == GST_RIFF_FCC_vids &&
333 334 335 336
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) {
    res = FALSE;
    goto done;
  }
337

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

385 386 387 388
done:
  GST_LOG_OBJECT (avidemux, "Returning res:%d dest_format:%d dest_value:%lld",
      res, *dest_format, *dest_value);
  gst_object_unref (avidemux);
389
  return res;
390 391
}

392
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
393
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
394
{
395 396
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
397
    GST_QUERY_DURATION,
398 399
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
400

401
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
402 403
}

404
static gboolean
405
gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
406
{
407
  gboolean res = TRUE;
408
  GstAviDemux *demux = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
409

410
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
411

412 413 414 415 416
  if (!stream->strh || !stream->strf.data)
    return FALSE;

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

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

433
          if (stream->is_vbr)
434 435
            pos = gst_util_uint64_scale_int (xlen, stream->current_byte,
                stream->total_bytes);
436 437 438
          else
            pos = gst_util_uint64_scale_int (xlen, stream->current_frame,
                stream->total_frames);
439
        } else {
440
          res = FALSE;
441 442 443
        }
      } else {
        if (stream->strh->rate != 0) {
444 445 446
          pos =
              gst_util_uint64_scale_int ((guint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
447 448 449
        } else {
          pos = stream->current_frame * demux->avih->us_frame * GST_USECOND;
        }
Wim Taymans's avatar
Wim Taymans committed
450
      }
451
      if (res)
Wim Taymans's avatar
Wim Taymans committed
452 453 454 455 456
        gst_query_set_position (query, GST_FORMAT_TIME, pos);
      break;
    }
    case GST_QUERY_DURATION:
    {
457 458 459 460 461 462 463 464 465 466 467 468
      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
469

470 471 472 473 474 475
        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
476
      break;
477
    }
Wim Taymans's avatar
Wim Taymans committed
478
    default:
479
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
480 481 482 483 484 485
      break;
  }

  return res;
}

486
#if 0
487
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
488
gst_avi_demux_get_event_mask (GstPad * pad)
489 490
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
491 492
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
493 494 495 496
  };

  return masks;
}
497
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
498

Wim Taymans's avatar
Wim Taymans committed
499
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
500
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
501 502
{
  gboolean res = TRUE;
503
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Wim Taymans's avatar
Wim Taymans committed
504
  avi_stream_context *stream;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
505

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
506
  GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi,
507
      "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
508 509
  if (!avi->index_entries) {
    GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi, "no index entries, returning");
510
    return FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
511
  }
512

Wim Taymans's avatar
Wim Taymans committed
513 514 515 516
  stream = gst_pad_get_element_private (pad);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
517
    {
518 519
      /* FIXME, this seeking code is not correct, look at wavparse for 
       * a better example */
520 521
      GstFormat format;
      GstSeekFlags flags;
522 523 524 525 526 527 528 529 530 531 532 533
      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);

534 535 536
      GST_DEBUG_OBJECT (avi,
          "seek format %d, flags:%d, %08x, start:%lld, stop:%lld", format,
          flags, stream->strh->type, start, stop);
537 538 539 540 541 542 543 544 545 546 547

      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;
      }
548

549 550 551 552
      if (!gst_pad_query_duration (stream->pad, &tformat, &duration)) {
        res = FALSE;
        goto done;
      }
553

554 555
      switch (start_type) {
        case GST_SEEK_TYPE_CUR:
556
          tstart = avi->segment.start + tstart;
557 558 559 560 561
          break;
        case GST_SEEK_TYPE_END:
          tstart = duration + tstart;
          break;
        case GST_SEEK_TYPE_NONE:
562
          tstart = avi->segment.start;
563 564 565 566 567 568
          update_start = FALSE;
          break;
        case GST_SEEK_TYPE_SET:
          break;
      }
      tstart = CLAMP (tstart, 0, duration);
569

570 571
      switch (stop_type) {
        case GST_SEEK_TYPE_CUR:
572
          tstop = avi->segment.stop + tstop;
573
          break;
574 575 576 577
        case GST_SEEK_TYPE_END:
          tstop = duration + tstop;
          break;
        case GST_SEEK_TYPE_NONE:
578
          tstop = avi->segment.stop;
579 580 581
          update_stop = FALSE;
          break;
        case GST_SEEK_TYPE_SET:
582
          break;
Wim Taymans's avatar
Wim Taymans committed
583
      }
584 585 586
      tstop = CLAMP (tstop, 0, duration);

      /* now store the values */
587 588 589 590
      avi->segment.rate = rate;
      avi->segment.flags = flags;
      avi->segment.start = tstart;
      avi->segment.stop = tstop;
591 592

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

595
    }
Wim Taymans's avatar
Wim Taymans committed
596 597 598 599
    default:
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
600

601
done:
Wim Taymans's avatar
Wim Taymans committed
602 603
  gst_event_unref (event);

604
  GST_DEBUG_OBJECT (avi, "returning %d", res);
Wim Taymans's avatar
Wim Taymans committed
605 606 607
  return res;
}

608 609 610 611 612 613 614 615 616 617
/**
 * 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).
618 619
 */

620
static gboolean
621
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
622
{
623
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
624

625
  if (!gst_riff_parse_file_header (element, buf, &doctype))
626
    return FALSE;
627

628 629 630 631 632 633 634 635
  if (doctype != GST_RIFF_RIFF_AVI)
    goto not_avi;

  return TRUE;

  /* ERRORS */
not_avi:
  {
636
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
637
        ("File is not an AVI file: %" GST_FOURCC_FORMAT,
638
            GST_FOURCC_ARGS (doctype)));
639 640 641 642
    return FALSE;
  }
}

643 644 645 646 647 648 649 650 651 652
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))
653
    goto wrong_header;
654 655 656 657

  avi->offset += 12;

  return GST_FLOW_OK;
658 659 660 661 662 663 664 665

  /* ERRORS */
wrong_header:
  {
    GST_DEBUG_OBJECT (avi, "error parsing file header");
    gst_buffer_unref (buf);
    return GST_FLOW_ERROR;
  }
666 667 668 669 670 671 672 673 674 675 676 677 678 679
}

/**
 * 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).
680 681
 */

682
static gboolean
683 684
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
685
{
686
  gst_riff_avih *avih;
687

688 689 690 691 692
  if (buf == NULL)
    goto no_buffer;

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

694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
  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
712 713

  /* debug stuff */
714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
  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;
731 732 733
  gst_buffer_unref (buf);

  return TRUE;
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748

  /* 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;
  }
749 750
}

751 752 753 754 755 756 757 758 759 760 761 762
/**
 * 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.
763 764 765
 */

static gboolean
766 767
gst_avi_demux_parse_superindex (GstElement * element,
    GstBuffer * buf, guint64 ** _indexes)
768
{
769
  guint8 *data;
770 771 772
  gint bpe = 16, num, i;
  guint64 *indexes;

773 774
  *_indexes = NULL;

775 776 777
  if (buf == NULL)
    goto no_buffer;

778
  if (GST_BUFFER_SIZE (buf) < 24)
779
    goto too_small;
780

781 782
  data = GST_BUFFER_DATA (buf);

783 784 785 786 787
  /* 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) {
788 789
    GST_WARNING_OBJECT (element,
        "Superindex for stream %d has unexpected "
790 791 792 793 794 795 796 797 798 799 800 801
        "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]);
  }
802 803
  indexes[i] = GST_BUFFER_OFFSET_NONE;
  *_indexes = indexes;
804 805 806 807

  gst_buffer_unref (buf);

  return TRUE;
808 809 810 811 812 813 814 815 816 817 818 819 820 821 822

  /* 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;
  }
823 824
}

825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842
/**
 * 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.
 */

843
static gboolean
844 845
gst_avi_demux_parse_subindex (GstElement * element,
    GstBuffer * buf, avi_stream_context * stream, GList ** _entries_list)
846
{
847 848 849
  guint8 *data = GST_BUFFER_DATA (buf);
  gint bpe, num, x;
  guint64 baseoff;
850
  gst_avi_index_entry *entries, *entry;