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

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

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

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

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

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

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

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

69 70 71
static void gst_avi_demux_base_init (GstAviDemuxClass * klass);
static void gst_avi_demux_class_init (GstAviDemuxClass * klass);
static void gst_avi_demux_init (GstAviDemux * avi);
72
static void gst_avi_demux_finalize (GObject * object);
73

74
static void gst_avi_demux_reset (GstAviDemux * avi);
75

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

#if 0
82
static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad);
83
#endif
84 85 86 87 88
static const GstQueryType *gst_avi_demux_get_src_query_types (GstPad * pad);
static gboolean gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query);
static gboolean gst_avi_demux_src_convert (GstPad * pad, GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value);

89 90 91
static gboolean gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment);
static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad,
    GstEvent * event);
92 93 94
static void gst_avi_demux_loop (GstPad * pad);
static gboolean gst_avi_demux_sink_activate (GstPad * sinkpad);
static gboolean gst_avi_demux_sink_activate_pull (GstPad * sinkpad,
95
    gboolean active);
96 97 98
static gboolean gst_avi_demux_activate_push (GstPad * pad, gboolean active);
static GstFlowReturn gst_avi_demux_chain (GstPad * pad, GstBuffer * buf);

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

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

104
/* GObject methods */
105

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  g_object_unref (avi->adapter);
203

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

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

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

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

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

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

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

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

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

256 257
  gst_adapter_clear (avi->adapter);

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

261
/* Index helper */
262 263 264 265 266 267 268 269 270 271 272 273 274 275
static gst_avi_index_entry *
gst_avi_demux_index_last (GstAviDemux * avi, gint stream_nr)
{
  gint i;
  gst_avi_index_entry *result = NULL;

  for (i = avi->index_size - 1; i >= 0; i--) {
    if (avi->index_entries[i].stream_nr == stream_nr) {
      result = &avi->index_entries[i];
      break;
    }
  }
  return result;
}
276

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

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

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

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

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

308
    i = entry->index_nr;
309

310 311 312 313
    GST_LOG_OBJECT (avi,
        "looking at entry %d / ts:%" GST_TIME_FORMAT " / dur:%" GST_TIME_FORMAT
        " flags:%d", i, GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (entry->dur),
        entry->flags);
314 315
    if (entry->ts <= time &&
        (entry->flags & flags) == flags && stream_nr == entry->stream_nr)
316
      last_entry = entry;
317
  } while (entry->ts < time);
318

319 320
  return last_entry;
}
321

322 323
/* GstElement methods */

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

330 331 332 333 334 335 336 337 338 339 340
  static const GstFormat src_a_formats[] = {
    GST_FORMAT_TIME,
    GST_FORMAT_BYTES,
    GST_FORMAT_DEFAULT,
    0
  };
  static const GstFormat src_v_formats[] = {
    GST_FORMAT_TIME,
    GST_FORMAT_DEFAULT,
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
341

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

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

355
  avi_stream_context *stream = gst_pad_get_element_private (pad);
356

357
  GST_LOG_OBJECT (avidemux,
358 359 360
      "Received  src_format:%s, src_value:%" G_GUINT64_FORMAT
      ", dest_format:%s", gst_format_get_name (src_format), src_value,
      gst_format_get_name (*dest_format));
361

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

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

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

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

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

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

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

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

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

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

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

  return res;
}

516
#if 0
517
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
518
gst_avi_demux_get_event_mask (GstPad * pad)
519 520
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
521 522
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
523 524 525 526
  };

  return masks;
}
527
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
528

Wim Taymans's avatar
Wim Taymans committed
529
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
530
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
531 532
{
  gboolean res = TRUE;
533
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
534

535
  GST_DEBUG_OBJECT (avi,
536
      "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
Wim Taymans's avatar
Wim Taymans committed
537 538 539

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
540 541 542 543 544 545 546
      /* 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
547 548
      break;
    default:
549
      /* most other events are not very usefull */
Wim Taymans's avatar
Wim Taymans committed
550 551 552
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
553
  gst_event_unref (event);
Wim Taymans's avatar
Wim Taymans committed
554 555 556
  return res;
}

557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
/* 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)
{
  const guint8 *data = NULL;
  guint32 peek_size = 0;

  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);
  if (!(*size) || (*size) == -1) {
    GST_DEBUG ("Invalid chunk size");
    return FALSE;
  }
  GST_DEBUG ("Need to peek chunk of %d bytes to read chunk %" GST_FOURCC_FORMAT,
      *size, GST_FOURCC_ARGS (*tag));
  peek_size = (*size + 1) & ~1;

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

/* AVI init */

/*
626 627 628 629 630
 * 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
631
 * bytes long. Takes ownership of @buf.
632 633 634
 *
 * Returns: TRUE if the file is a RIFF/AVI file, FALSE otherwise.
 *          Throws an error, caller should error out (fatal).
635
 */
636
static gboolean
637
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
638
{
639
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
640

641
  if (!gst_riff_parse_file_header (element, buf, &doctype))
642
    return FALSE;
643

644 645 646 647 648 649 650 651
  if (doctype != GST_RIFF_RIFF_AVI)
    goto not_avi;

  return TRUE;

  /* ERRORS */
not_avi:
  {
652
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
653
        ("File is not an AVI file: %" GST_FOURCC_FORMAT,
654
            GST_FOURCC_ARGS (doctype)));
655 656 657 658
    return FALSE;
  }
}

659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684
/*
 * Read AVI file tag when streaming
 */
static GstFlowReturn
gst_avi_demux_stream_init_push (GstAviDemux * avi)
{
  if (gst_adapter_available (avi->adapter) >= 12) {
    GstBuffer *tmp = gst_buffer_new ();

    /* _take flushes the data */
    GST_BUFFER_DATA (tmp) = gst_adapter_take (avi->adapter, 12);
    GST_BUFFER_SIZE (tmp) = 12;

    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;
  }
  return GST_FLOW_OK;
}

/*
 * Read AVI file tag
 */
685
static GstFlowReturn
686
gst_avi_demux_stream_init_pull (GstAviDemux * avi)
687 688 689 690
{
  GstFlowReturn res;
  GstBuffer *buf = NULL;

691 692
  res = gst_pad_pull_range (avi->sinkpad, avi->offset, 12, &buf);
  if (res != GST_FLOW_OK)
693
    return res;
694
  else if (!gst_avi_demux_parse_file_header (GST_ELEMENT_CAST (avi), buf))
695
    goto wrong_header;
696 697 698 699

  avi->offset += 12;

  return GST_FLOW_OK;
700 701 702 703 704 705 706

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

709 710 711
/* AVI header handling */

/*
712 713 714 715 716 717 718 719 720 721 722
 * 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).
723
 */
724
static gboolean
725 726
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
727
{
728
  gst_riff_avih *avih;
729

730 731 732 733 734
  if (buf == NULL)
    goto no_buffer;

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

736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
  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
754 755

  /* debug stuff */
756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
  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;
773 774 775
  gst_buffer_unref (buf);

  return TRUE;
776 777 778 779 780 781 782 783 784 785 786 787 788 789 790

  /* 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;
  }
791 792
}

793
/*
794
 * gst_avi_demux_parse_superindex:
795
 * @avi: caller element (used for debugging/errors).
796 797 798 799 800 801 802 803 804
 * @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.
805 806
 */
static gboolean
807
gst_avi_demux_parse_superindex (GstAviDemux * avi,
808
    GstBuffer * buf, guint64 ** _indexes)
809
{
810
  guint8 *data;
811 812
  gint bpe = 16, num, i;
  guint64 *indexes;
813
  guint size;
814

815 816
  *_indexes = NULL;

817 818
  size = buf ? GST_BUFFER_SIZE (buf) : 0;
  if (size < 24)
819
    goto too_small;
820

821 822
  data = GST_BUFFER_DATA (buf);

823 824 825 826 827
  /* 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) {
828
    GST_WARNING_OBJECT (avi,
829
        "Superindex for stream %d has unexpected "
830 831 832 833 834 835