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_dispose (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 105
/* GObject mathods */

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->dispose = gst_avi_demux_dispose;
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
  gst_avi_demux_reset (avi);
Wim Taymans's avatar
Wim Taymans committed
191

192 193
  avi->index_entries = NULL;
  memset (&avi->stream, 0, sizeof (avi->stream));
Wim Taymans's avatar
Wim Taymans committed
194 195
}

196 197 198 199 200 201 202 203 204 205 206 207 208 209
static void
gst_avi_demux_dispose (GObject * object)
{
  GstAviDemux *avi = GST_AVI_DEMUX (object);

  GST_DEBUG ("AVI: Dispose");
  if (avi->adapter) {
    g_object_unref (avi->adapter);
    avi->adapter = NULL;
  }

  G_OBJECT_CLASS (parent_class)->dispose (object);
}

210
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
211
gst_avi_demux_reset (GstAviDemux * avi)
212
{
213
  gint i;
214

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

233 234 235
  avi->num_streams = 0;
  avi->num_v_streams = 0;
  avi->num_a_streams = 0;
236

237
  avi->state = GST_AVI_DEMUX_START;
238
  avi->offset = 0;
239

240 241
  g_free (avi->index_entries);
  avi->index_entries = NULL;
242
  avi->index_size = 0;
243
  avi->index_offset = 0;
244
  avi->current_entry = 0;
245 246
  g_free (avi->avih);
  avi->avih = NULL;
247

248 249 250
  if (avi->seek_event)
    gst_event_unref (avi->seek_event);
  avi->seek_event = NULL;
251

252 253 254 255
  if (avi->globaltags)
    gst_tag_list_free (avi->globaltags);
  avi->globaltags = NULL;

256
  avi->got_tags = FALSE;
257 258 259
  avi->have_eos = FALSE;

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

262 263
/* Index helper */

264
static gst_avi_index_entry *
265
gst_avi_demux_index_next (GstAviDemux * avi, gint stream_nr, gint start)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
266
{
267
  gint i;
268
  gst_avi_index_entry *result = NULL;
269

270
  for (i = start; i < avi->index_size; i++) {
271 272
    if (avi->index_entries[i].stream_nr == stream_nr) {
      result = &avi->index_entries[i];
Ronald S. Bultje's avatar
Ronald S. Bultje committed
273
      break;
274
    }
275
  }
276

277
  return result;
278
}
279

280
static gst_avi_index_entry *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
281 282
gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
    gint stream_nr, guint64 time, guint32 flags)
283 284 285
{
  gst_avi_index_entry *entry = NULL, *last_entry = NULL;
  gint i;
286

287 288
  GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT " flags:%d",
      stream_nr, GST_TIME_ARGS (time), flags);
289

290 291
  i = -1;
  do {
292
    entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
293
    if (!entry)
294
      return last_entry;
295

296
    i = entry->index_nr;
297

298 299 300 301
    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);
302 303
    if (entry->ts <= time &&
        (entry->flags & flags) == flags && stream_nr == entry->stream_nr)
304
      last_entry = entry;
305
  } while (entry->ts < time);
306

307 308
  return last_entry;
}
309

310 311
/* GstElement methods */

312
#if 0
313
static const GstFormat *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
314
gst_avi_demux_get_src_formats (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
315
{
316
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
317

318 319 320 321 322 323 324 325 326 327 328
  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
329

330
  return (stream->strh->type == GST_RIFF_FCC_auds ?
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
331
      src_a_formats : src_v_formats);
332
}
333
#endif
334

335
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
336 337 338
gst_avi_demux_src_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
339 340
{
  gboolean res = TRUE;
341
  GstAviDemux *avidemux = GST_AVI_DEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
342

343
  avi_stream_context *stream = gst_pad_get_element_private (pad);
344

345
  GST_LOG_OBJECT (avidemux,
346 347 348
      "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));
349

350 351
  if (src_format == *dest_format) {
    *dest_value = src_value;
352 353 354 355 356
    goto done;
  }
  if (!stream->strh || !stream->strf.data) {
    res = FALSE;
    goto done;
357
  }
358
  if (stream->strh->type == GST_RIFF_FCC_vids &&
359 360 361 362
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) {
    res = FALSE;
    goto done;
  }
363

364 365 366
  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
367
        case GST_FORMAT_BYTES:
368 369 370
          *dest_value =
              gst_util_uint64_scale_int (src_value, stream->strf.auds->av_bps,
              GST_SECOND);
371 372
          break;
        case GST_FORMAT_DEFAULT:
373 374
          *dest_value = gst_util_uint64_scale (src_value, stream->strh->rate,
              stream->strh->scale * GST_SECOND);
375 376 377 378
          break;
        default:
          res = FALSE;
          break;
379
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
380
      break;
381 382
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
383
        case GST_FORMAT_TIME:
384
          if (stream->strf.auds->av_bps != 0) {
385 386
            *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
                stream->strf.auds->av_bps);
387 388
          } else
            res = FALSE;
389 390 391 392
          break;
        default:
          res = FALSE;
          break;
393 394
      }
      break;
395 396
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
397
        case GST_FORMAT_TIME:
398 399 400
          *dest_value =
              gst_util_uint64_scale (src_value,
              stream->strh->scale * GST_SECOND, stream->strh->rate);
401 402 403 404
          break;
        default:
          res = FALSE;
          break;
405
      }
Wim Taymans's avatar
Wim Taymans committed
406 407
      break;
    default:
408
      res = FALSE;
409
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
410

411
done:
412 413 414
  GST_LOG_OBJECT (avidemux,
      "Returning res:%d dest_format:%s dest_value:%" G_GUINT64_FORMAT, res,
      gst_format_get_name (*dest_format), *dest_value);
415
  gst_object_unref (avidemux);
416
  return res;
417 418
}

419
static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
420
gst_avi_demux_get_src_query_types (GstPad * pad)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
421
{
422 423
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
424
    GST_QUERY_DURATION,
425 426
    0
  };
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
427

428
  return src_types;
Wim Taymans's avatar
Wim Taymans committed
429 430
}

431
static gboolean
432
gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
433
{
434
  gboolean res = TRUE;
435
  GstAviDemux *demux = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
436

437
  avi_stream_context *stream = gst_pad_get_element_private (pad);
Wim Taymans's avatar
Wim Taymans committed
438

439
  if (!stream->strh || !stream->strf.data)
440
    return gst_pad_query_default (pad, query);
441 442 443

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

      if (stream->strh->type == GST_RIFF_FCC_auds) {
447 448
        if (!stream->is_vbr) {
          /* CBR */
449 450
          pos = gst_util_uint64_scale_int ((gint64) stream->current_frame *
              stream->strh->scale, GST_SECOND, stream->strh->rate);
451
        } else if (stream->strf.auds->av_bps != 0) {
452
          /* VBR */
453 454
          pos = gst_util_uint64_scale_int (stream->current_byte, GST_SECOND,
              stream->strf.auds->av_bps);
455
        } else if (stream->total_frames != 0 && stream->total_bytes != 0) {
456
          /* calculate timestamps based on percentage of length */
457 458 459
          guint64 xlen = demux->avih->us_frame *
              demux->avih->tot_frames * GST_USECOND;

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

  return res;
}

504
#if 0
505
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
506
gst_avi_demux_get_event_mask (GstPad * pad)
507 508
{
  static const GstEventMask masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
509 510
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
    {0,}
511 512 513 514
  };

  return masks;
}
515
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
516

Wim Taymans's avatar
Wim Taymans committed
517
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
518
gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
519 520
{
  gboolean res = TRUE;
521
  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
522

523
  GST_DEBUG_OBJECT (avi,
524
      "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
Wim Taymans's avatar
Wim Taymans committed
525 526 527

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
528 529 530 531 532 533 534
      /* 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
535 536
      break;
    default:
537
      /* most other events are not very usefull */
Wim Taymans's avatar
Wim Taymans committed
538 539 540
      res = FALSE;
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
541
  gst_event_unref (event);
Wim Taymans's avatar
Wim Taymans committed
542 543 544
  return res;
}

545 546 547 548 549 550 551 552 553 554 555 556 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
/* 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 */

/*
614 615 616 617 618
 * 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
619
 * bytes long. Takes ownership of @buf.
620 621 622
 *
 * Returns: TRUE if the file is a RIFF/AVI file, FALSE otherwise.
 *          Throws an error, caller should error out (fatal).
623
 */
624
static gboolean
625
gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
626
{
627
  guint32 doctype;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
628

629
  if (!gst_riff_parse_file_header (element, buf, &doctype))
630
    return FALSE;
631

632 633 634 635 636 637 638 639
  if (doctype != GST_RIFF_RIFF_AVI)
    goto not_avi;

  return TRUE;

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

647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
/*
 * 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
 */
673
static GstFlowReturn
674
gst_avi_demux_stream_init_pull (GstAviDemux * avi)
675 676 677 678
{
  GstFlowReturn res;
  GstBuffer *buf = NULL;

679 680
  res = gst_pad_pull_range (avi->sinkpad, avi->offset, 12, &buf);
  if (res != GST_FLOW_OK)
681
    return res;
682
  else if (!gst_avi_demux_parse_file_header (GST_ELEMENT_CAST (avi), buf))
683
    goto wrong_header;
684 685 686 687

  avi->offset += 12;

  return GST_FLOW_OK;
688 689 690 691 692 693 694

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

697 698 699
/* AVI header handling */

/*
700 701 702 703 704 705 706 707 708 709 710
 * 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).
711
 */
712
static gboolean
713 714
gst_avi_demux_parse_avih (GstElement * element,
    GstBuffer * buf, gst_riff_avih ** _avih)
715
{
716
  gst_riff_avih *avih;
717

718 719 720 721 722
  if (buf == NULL)
    goto no_buffer;

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

724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741
  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
742 743

  /* debug stuff */
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760
  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;
761 762 763
  gst_buffer_unref (buf);

  return TRUE;
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778

  /* 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;
  }
779 780
}

781
/*
782
 * gst_avi_demux_parse_superindex:
783
 * @avi: caller element (used for debugging/errors).
784 785 786 787 788 789 790 791 792
 * @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.
793 794
 */
static gboolean
795
gst_avi_demux_parse_superindex (GstAviDemux * avi,
796
    GstBuffer * buf, guint64 ** _indexes)
797
{
798
  guint8 *data;
799 800
  gint bpe = 16, num, i;
  guint64 *indexes;
801
  guint size;
802

803 804
  *_indexes = NULL;

805 806
  size = buf ? GST_BUFFER_SIZE (buf) : 0;
  if (size < 24)
807
    goto too_small;
808

809 810
  data = GST_BUFFER_DATA (buf);

811 812 813 814 815
  /* 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) {
816
    GST_WARNING_OBJECT (avi,
817
        "Superindex for stream %d has unexpected "
818 819 820 821 822 823 824 825
        "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++) {
826
    if (size < 24 + bpe * (i + 1))
827 828 829
      break;
    indexes[i] = GST_READ_UINT64_LE (&data[24 + bpe * i]);
  }
830 831
  indexes[i] = GST_BUFFER_OFFSET_NONE;
  *_indexes = indexes;
832 833 834 835

  gst_buffer_unref (buf);

  return TRUE;
836 837 838 839

  /* ERRORS */
too_small:
  {
840 841 842 843
    GST_ERROR_OBJECT (avi,
        "Not enough data to parse superindex (%d available, 24 needed)", size);
    if (buf)
      gst_buffer_unref (buf);