matroska-demux.c 197 KB
Newer Older
1 2
/* GStreamer Matroska muxer/demuxer
 * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3
 * (c) 2006 Tim-Philipp Müller <tim centricular net>
4
 * (c) 2008 Sebastian Dröge <slomo@circular-chaos.org>
5
 * (c) 2011 Debarshi Ray <rishi@gnu.org>
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * matroska-demux.c: matroska file/stream demuxer
 *
 * 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
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
21 22
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
23 24
 */

25
/* TODO: check CRC32 if present
26 27 28 29 30 31 32 33
 * TODO: there can be a segment after the first segment. Handle like
 *       chained oggs. Fixes #334082
 * TODO: Test samples: http://www.matroska.org/samples/matrix/index.html
 *                     http://samples.mplayerhq.hu/Matroska/
 * TODO: check if demuxing is done correct for all codecs according to spec
 * TODO: seeking with incomplete or without CUE
 */

34 35 36 37 38 39 40 41
/**
 * SECTION:element-matroskademux
 *
 * matroskademux demuxes a Matroska file into the different contained streams.
 *
 * <refsect2>
 * <title>Example launch line</title>
 * |[
42
 * gst-launch-1.0 -v filesrc location=/path/to/mkv ! matroskademux ! vorbisdec ! audioconvert ! audioresample ! autoaudiosink
43 44 45 46 47
 * ]| This pipeline demuxes a Matroska file and outputs the contained Vorbis audio.
 * </refsect2>
 */


48 49 50 51 52 53
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <math.h>
#include <string.h>
54
#include <glib/gprintf.h>
55

56 57
/* For AVI compatibility mode
   and for fourcc stuff */
58
#include <gst/riff/riff-read.h>
59 60
#include <gst/riff/riff-ids.h>
#include <gst/riff/riff-media.h>
61

René Stadler's avatar
René Stadler committed
62
#include <gst/audio/audio.h>
63
#include <gst/tag/tag.h>
64
#include <gst/pbutils/pbutils.h>
65
#include <gst/video/video.h>
66

67 68 69
#include "matroska-demux.h"
#include "matroska-ids.h"

70
GST_DEBUG_CATEGORY_STATIC (matroskademux_debug);
71 72
#define GST_CAT_DEFAULT matroskademux_debug

73 74
#define DEBUG_ELEMENT_START(demux, ebml, element) \
    GST_DEBUG_OBJECT (demux, "Parsing " element " element at offset %" \
75
        G_GUINT64_FORMAT, gst_ebml_read_get_pos (ebml))
76 77

#define DEBUG_ELEMENT_STOP(demux, ebml, element, ret) \
78 79
    GST_DEBUG_OBJECT (demux, "Parsing " element " element " \
        " finished with '%s'", gst_flow_get_name (ret))
80

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
81 82
enum
{
83 84 85 86
  PROP_0,
  PROP_METADATA,
  PROP_STREAMINFO,
  PROP_MAX_GAP_TIME
87 88
};

89 90
#define  DEFAULT_MAX_GAP_TIME      (2 * GST_SECOND)

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
91 92 93
static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
94 95
    GST_STATIC_CAPS ("audio/x-matroska; video/x-matroska; "
        "video/x-matroska-3d; audio/webm; video/webm")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
96
    );
97

98 99
/* TODO: fill in caps! */

100
static GstStaticPadTemplate audio_src_templ =
Wim Taymans's avatar
Wim Taymans committed
101
GST_STATIC_PAD_TEMPLATE ("audio_%u",
102 103 104 105 106 107
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS ("ANY")
    );

static GstStaticPadTemplate video_src_templ =
Wim Taymans's avatar
Wim Taymans committed
108
GST_STATIC_PAD_TEMPLATE ("video_%u",
109 110 111 112 113 114
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS ("ANY")
    );

static GstStaticPadTemplate subtitle_src_templ =
Wim Taymans's avatar
Wim Taymans committed
115
    GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
116 117
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
118
    GST_STATIC_CAPS ("text/x-raw, format=pango-markup; application/x-ssa; "
119
        "application/x-ass;application/x-usf; subpicture/x-dvd; "
120
        "subpicture/x-pgs; subtitle/x-kate; " "application/x-subtitle-unknown")
121 122
    );

123 124
static GstFlowReturn gst_matroska_demux_parse_id (GstMatroskaDemux * demux,
    guint32 id, guint64 length, guint needed);
125

126
/* element functions */
127 128 129
static void gst_matroska_demux_loop (GstPad * pad);

static gboolean gst_matroska_demux_element_send_event (GstElement * element,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
130
    GstEvent * event);
131 132 133
static gboolean gst_matroska_demux_element_query (GstElement * element,
    GstQuery * query);

134
/* pad functions */
René Stadler's avatar
René Stadler committed
135 136 137 138
static gboolean gst_matroska_demux_sink_activate (GstPad * sinkpad,
    GstObject * parent);
static gboolean gst_matroska_demux_sink_activate_mode (GstPad * sinkpad,
    GstObject * parent, GstPadMode mode, gboolean active);
139

140
static gboolean gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux,
141
    GstPad * pad, GstEvent * event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
142
static gboolean gst_matroska_demux_handle_src_event (GstPad * pad,
René Stadler's avatar
René Stadler committed
143
    GstObject * parent, GstEvent * event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
144
static gboolean gst_matroska_demux_handle_src_query (GstPad * pad,
René Stadler's avatar
René Stadler committed
145
    GstObject * parent, GstQuery * query);
146

147
static gboolean gst_matroska_demux_handle_sink_event (GstPad * pad,
René Stadler's avatar
René Stadler committed
148
    GstObject * parent, GstEvent * event);
149
static GstFlowReturn gst_matroska_demux_chain (GstPad * pad,
René Stadler's avatar
René Stadler committed
150
    GstObject * object, GstBuffer * buffer);
151

152 153 154
static GstStateChangeReturn
gst_matroska_demux_change_state (GstElement * element,
    GstStateChange transition);
155
#if 0
156 157 158
static void
gst_matroska_demux_set_index (GstElement * element, GstIndex * index);
static GstIndex *gst_matroska_demux_get_index (GstElement * element);
159
#endif
160 161

/* caps functions */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
162
static GstCaps *gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext
163 164
    * videocontext, const gchar * codec_id, guint8 * data, guint size,
    gchar ** codec_name, guint32 * riff_fourcc);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
165
static GstCaps *gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext
166 167
    * audiocontext, const gchar * codec_id, guint8 * data, guint size,
    gchar ** codec_name, guint16 * riff_audio_fmt);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
168 169 170
static GstCaps
    * gst_matroska_demux_subtitle_caps (GstMatroskaTrackSubtitleContext *
    subtitlecontext, const gchar * codec_id, gpointer data, guint size);
171 172

/* stream methods */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
173
static void gst_matroska_demux_reset (GstElement * element);
174
static gboolean perform_seek_to_offset (GstMatroskaDemux * demux,
175
    gdouble rate, guint64 offset, guint32 seqnum);
176

177 178 179 180 181 182
/* gobject functions */
static void gst_matroska_demux_set_property (GObject * object,
    guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_matroska_demux_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec);

183
GType gst_matroska_demux_get_type (void);
René Stadler's avatar
René Stadler committed
184 185
#define parent_class gst_matroska_demux_parent_class
G_DEFINE_TYPE (GstMatroskaDemux, gst_matroska_demux, GST_TYPE_ELEMENT);
186

187 188 189 190 191
static void
gst_matroska_demux_finalize (GObject * object)
{
  GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (object);

192
  gst_matroska_read_common_finalize (&demux->common);
193
  gst_flow_combiner_free (demux->flowcombiner);
194 195 196
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

197
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
198
gst_matroska_demux_class_init (GstMatroskaDemuxClass * klass)
199
{
200 201
  GObjectClass *gobject_class = (GObjectClass *) klass;
  GstElementClass *gstelement_class = (GstElementClass *) klass;
202

203 204
  GST_DEBUG_CATEGORY_INIT (matroskademux_debug, "matroskademux", 0,
      "Matroska demuxer");
205

206 207
  gobject_class->finalize = gst_matroska_demux_finalize;

208 209 210
  gobject_class->get_property = gst_matroska_demux_get_property;
  gobject_class->set_property = gst_matroska_demux_set_property;

211
  g_object_class_install_property (gobject_class, PROP_MAX_GAP_TIME,
212
      g_param_spec_uint64 ("max-gap-time", "Maximum gap time",
René Stadler's avatar
René Stadler committed
213
          "The demuxer sends out segment events for skipping "
214 215 216
          "gaps longer than this (0 = disabled).", 0, G_MAXUINT64,
          DEFAULT_MAX_GAP_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

217 218 219 220
  gstelement_class->change_state =
      GST_DEBUG_FUNCPTR (gst_matroska_demux_change_state);
  gstelement_class->send_event =
      GST_DEBUG_FUNCPTR (gst_matroska_demux_element_send_event);
221 222
  gstelement_class->query =
      GST_DEBUG_FUNCPTR (gst_matroska_demux_element_query);
223
#if 0
224 225 226 227
  gstelement_class->set_index =
      GST_DEBUG_FUNCPTR (gst_matroska_demux_set_index);
  gstelement_class->get_index =
      GST_DEBUG_FUNCPTR (gst_matroska_demux_get_index);
228
#endif
René Stadler's avatar
René Stadler committed
229 230 231 232 233 234 235 236 237 238

  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&video_src_templ));
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&audio_src_templ));
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&subtitle_src_templ));
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&sink_templ));

239
  gst_element_class_set_static_metadata (gstelement_class, "Matroska demuxer",
René Stadler's avatar
René Stadler committed
240 241
      "Codec/Demuxer",
      "Demuxes Matroska/WebM streams into video/audio/subtitles",
242
      "GStreamer maintainers <gstreamer-devel@lists.freedesktop.org>");
243 244
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
245
static void
René Stadler's avatar
René Stadler committed
246
gst_matroska_demux_init (GstMatroskaDemux * demux)
247
{
248 249 250
  demux->common.sinkpad = gst_pad_new_from_static_template (&sink_templ,
      "sink");
  gst_pad_set_activate_function (demux->common.sinkpad,
251
      GST_DEBUG_FUNCPTR (gst_matroska_demux_sink_activate));
René Stadler's avatar
René Stadler committed
252 253
  gst_pad_set_activatemode_function (demux->common.sinkpad,
      GST_DEBUG_FUNCPTR (gst_matroska_demux_sink_activate_mode));
254
  gst_pad_set_chain_function (demux->common.sinkpad,
255
      GST_DEBUG_FUNCPTR (gst_matroska_demux_chain));
256
  gst_pad_set_event_function (demux->common.sinkpad,
257
      GST_DEBUG_FUNCPTR (gst_matroska_demux_handle_sink_event));
258
  gst_element_add_pad (GST_ELEMENT (demux), demux->common.sinkpad);
259

260 261
  /* init defaults for common read context */
  gst_matroska_read_common_init (&demux->common);
262

263 264 265
  /* property defaults */
  demux->max_gap_time = DEFAULT_MAX_GAP_TIME;

Wim Taymans's avatar
Wim Taymans committed
266 267
  GST_OBJECT_FLAG_SET (demux, GST_ELEMENT_FLAG_INDEXABLE);

268 269
  demux->flowcombiner = gst_flow_combiner_new ();

270 271
  /* finish off */
  gst_matroska_demux_reset (GST_ELEMENT (demux));
272 273
}

274
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
275
gst_matroska_demux_reset (GstElement * element)
276 277 278
{
  GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (element);

279 280
  GST_DEBUG_OBJECT (demux, "Resetting state");

281
  gst_matroska_read_common_reset (GST_ELEMENT (demux), &demux->common);
282

283 284 285 286
  demux->num_a_streams = 0;
  demux->num_t_streams = 0;
  demux->num_v_streams = 0;

287 288 289
  demux->have_group_id = FALSE;
  demux->group_id = G_MAXUINT;

290 291
  demux->clock = NULL;
  demux->tracks_parsed = FALSE;
292

293 294 295 296 297
  if (demux->clusters) {
    g_array_free (demux->clusters, TRUE);
    demux->clusters = NULL;
  }

298
  g_list_foreach (demux->seek_parsed,
299
      (GFunc) gst_matroska_read_common_free_parsed_el, NULL);
300 301 302
  g_list_free (demux->seek_parsed);
  demux->seek_parsed = NULL;

303
  demux->last_stop_end = GST_CLOCK_TIME_NONE;
304
  demux->seek_block = 0;
305
  demux->stream_start_time = GST_CLOCK_TIME_NONE;
306
  demux->to_time = GST_CLOCK_TIME_NONE;
307 308
  demux->cluster_time = GST_CLOCK_TIME_NONE;
  demux->cluster_offset = 0;
309
  demux->next_cluster_offset = 0;
310 311
  demux->index_offset = 0;
  demux->seekable = FALSE;
René Stadler's avatar
René Stadler committed
312
  demux->need_segment = FALSE;
313
  demux->segment_seqnum = 0;
314 315
  demux->requested_seek_time = GST_CLOCK_TIME_NONE;
  demux->seek_offset = -1;
316 317 318 319 320
  demux->building_index = FALSE;
  if (demux->seek_event) {
    gst_event_unref (demux->seek_event);
    demux->seek_event = NULL;
  }
321

322 323 324
  demux->seek_index = NULL;
  demux->seek_entry = 0;

325 326 327 328
  if (demux->new_segment) {
    gst_event_unref (demux->new_segment);
    demux->new_segment = NULL;
  }
329

330
  demux->invalid_duration = FALSE;
331

332 333
  demux->cached_length = G_MAXUINT64;

334
  gst_flow_combiner_clear (demux->flowcombiner);
335 336
}

337 338 339
static GstBuffer *
gst_matroska_decode_buffer (GstMatroskaTrackContext * context, GstBuffer * buf)
{
Wim Taymans's avatar
Wim Taymans committed
340 341 342
  GstMapInfo map;
  gpointer data;
  gsize size;
343 344 345

  g_return_val_if_fail (GST_IS_BUFFER (buf), NULL);

346 347
  GST_DEBUG ("decoding buffer %p", buf);

Wim Taymans's avatar
Wim Taymans committed
348 349 350
  gst_buffer_map (buf, &map, GST_MAP_READ);
  data = map.data;
  size = map.size;
René Stadler's avatar
René Stadler committed
351

Wim Taymans's avatar
Wim Taymans committed
352
  g_return_val_if_fail (size > 0, buf);
353 354 355

  if (gst_matroska_decode_data (context->encodings, &data, &size,
          GST_MATROSKA_TRACK_ENCODING_SCOPE_FRAME, FALSE)) {
Wim Taymans's avatar
Wim Taymans committed
356
    gst_buffer_unmap (buf, &map);
357
    gst_buffer_unref (buf);
René Stadler's avatar
René Stadler committed
358
    return gst_buffer_new_wrapped (data, size);
359
  } else {
360
    GST_DEBUG ("decode data failed");
Wim Taymans's avatar
Wim Taymans committed
361
    gst_buffer_unmap (buf, &map);
362 363 364 365 366
    gst_buffer_unref (buf);
    return NULL;
  }
}

367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
static void
gst_matroska_demux_add_stream_headers_to_caps (GstMatroskaDemux * demux,
    GstBufferList * list, GstCaps * caps)
{
  GstStructure *s;
  GValue arr_val = G_VALUE_INIT;
  GValue buf_val = G_VALUE_INIT;
  gint i, num;

  g_assert (gst_caps_is_writable (caps));

  g_value_init (&arr_val, GST_TYPE_ARRAY);
  g_value_init (&buf_val, GST_TYPE_BUFFER);

  num = gst_buffer_list_length (list);
  for (i = 0; i < num; ++i) {
    g_value_set_boxed (&buf_val, gst_buffer_list_get (list, i));
    gst_value_array_append_value (&arr_val, &buf_val);
  }

  s = gst_caps_get_structure (caps, 0);
  gst_structure_take_value (s, "streamheader", &arr_val);
  g_value_unset (&buf_val);
}

392
static GstFlowReturn
393
gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
394 395 396 397
{
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (demux);
  GstMatroskaTrackContext *context;
  GstPadTemplate *templ = NULL;
398
  GstStreamFlags stream_flags;
399
  GstCaps *caps = NULL;
400
  GstTagList *cached_taglist;
401
  gchar *padname = NULL;
402
  GstFlowReturn ret;
403 404
  guint32 id, riff_fourcc = 0;
  guint16 riff_audio_fmt = 0;
405
  GstEvent *stream_start;
406
  gchar *codec = NULL;
407
  gchar *stream_id;
408

409 410 411 412 413 414 415 416
  DEBUG_ELEMENT_START (demux, ebml, "TrackEntry");

  /* start with the master */
  if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) {
    DEBUG_ELEMENT_STOP (demux, ebml, "TrackEntry", ret);
    return ret;
  }

417 418 419
  /* allocate generic... if we know the type, we'll g_renew()
   * with the precise type */
  context = g_new0 (GstMatroskaTrackContext, 1);
420 421
  g_ptr_array_add (demux->common.src, context);
  context->index = demux->common.num_streams;
422
  context->index_writer_id = -1;
423
  context->type = 0;            /* no type yet */
424
  context->default_duration = 0;
425
  context->pos = 0;
426
  context->set_discont = TRUE;
427 428 429 430
  context->timecodescale = 1.0;
  context->flags =
      GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT |
      GST_MATROSKA_TRACK_LACING;
431 432
  context->from_time = GST_CLOCK_TIME_NONE;
  context->from_offset = -1;
433
  context->to_offset = G_MAXINT64;
434
  context->alignment = 1;
435
  context->dts_only = FALSE;
436
  context->intra_only = FALSE;
437
  context->tags = gst_tag_list_new_empty ();
438 439
  demux->common.num_streams++;
  g_assert (demux->common.src->len == demux->common.num_streams);
440

441
  GST_DEBUG_OBJECT (demux, "Stream number %d", context->index);
442 443

  /* try reading the trackentry headers */
444 445
  while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
    if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
446
      break;
447

448
    switch (id) {
449
        /* track number (unique stream ID) */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
450
      case GST_MATROSKA_ID_TRACKNUMBER:{
451 452
        guint64 num;

453
        if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
454
          break;
455

456
        if (num == 0) {
457 458 459
          GST_ERROR_OBJECT (demux, "Invalid TrackNumber 0");
          ret = GST_FLOW_ERROR;
          break;
460 461
        } else if (!gst_matroska_read_common_tracknumber_unique (&demux->common,
                num)) {
462 463
          GST_ERROR_OBJECT (demux, "TrackNumber %" G_GUINT64_FORMAT
              " is not unique", num);
464 465 466 467
          ret = GST_FLOW_ERROR;
          break;
        }

468
        GST_DEBUG_OBJECT (demux, "TrackNumber: %" G_GUINT64_FORMAT, num);
469 470
        context->num = num;
        break;
471
      }
472
        /* track UID (unique identifier) */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
473
      case GST_MATROSKA_ID_TRACKUID:{
474 475
        guint64 num;

476
        if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
477
          break;
478

479
        if (num == 0) {
480
          GST_ERROR_OBJECT (demux, "Invalid TrackUID 0");
481 482 483 484
          ret = GST_FLOW_ERROR;
          break;
        }

485
        GST_DEBUG_OBJECT (demux, "TrackUID: %" G_GUINT64_FORMAT, num);
486 487
        context->uid = num;
        break;
488 489
      }

490
        /* track type (video, audio, combined, subtitle, etc.) */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
491
      case GST_MATROSKA_ID_TRACKTYPE:{
492
        guint64 track_type;
493

494
        if ((ret = gst_ebml_read_uint (ebml, &id, &track_type)) != GST_FLOW_OK) {
495 496
          break;
        }
497 498

        if (context->type != 0 && context->type != track_type) {
499 500
          GST_WARNING_OBJECT (demux,
              "More than one tracktype defined in a TrackEntry - skipping");
501
          break;
502
        } else if (track_type < 1 || track_type > 254) {
503 504
          GST_WARNING_OBJECT (demux, "Invalid TrackType %" G_GUINT64_FORMAT,
              track_type);
505
          break;
506 507
        }

508 509
        GST_DEBUG_OBJECT (demux, "TrackType: %" G_GUINT64_FORMAT, track_type);

510
        /* ok, so we're actually going to reallocate this thing */
511
        switch (track_type) {
512
          case GST_MATROSKA_TRACK_TYPE_VIDEO:
513
            gst_matroska_track_init_video_context (&context);
514 515
            break;
          case GST_MATROSKA_TRACK_TYPE_AUDIO:
516
            gst_matroska_track_init_audio_context (&context);
517 518
            break;
          case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
519
            gst_matroska_track_init_subtitle_context (&context);
520
            break;
521
          case GST_MATROSKA_TRACK_TYPE_COMPLEX:
522
          case GST_MATROSKA_TRACK_TYPE_LOGO:
523
          case GST_MATROSKA_TRACK_TYPE_BUTTONS:
524 525
          case GST_MATROSKA_TRACK_TYPE_CONTROL:
          default:
526 527 528
            GST_WARNING_OBJECT (demux,
                "Unknown or unsupported TrackType %" G_GUINT64_FORMAT,
                track_type);
529 530 531
            context->type = 0;
            break;
        }
532 533
        g_ptr_array_index (demux->common.src, demux->common.num_streams - 1)
            = context;
534
        break;
535 536
      }

537
        /* tracktype specific stuff for video */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
538
      case GST_MATROSKA_ID_TRACKVIDEO:{
539 540
        GstMatroskaTrackVideoContext *videocontext;

541 542
        DEBUG_ELEMENT_START (demux, ebml, "TrackVideo");

543
        if (!gst_matroska_track_init_video_context (&context)) {
544 545
          GST_WARNING_OBJECT (demux,
              "TrackVideo element in non-video track - ignoring track");
546
          ret = GST_FLOW_ERROR;
547
          break;
548
        } else if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) {
549 550 551
          break;
        }
        videocontext = (GstMatroskaTrackVideoContext *) context;
552 553
        g_ptr_array_index (demux->common.src, demux->common.num_streams - 1)
            = context;
554

555 556 557
        while (ret == GST_FLOW_OK &&
            gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
          if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
558 559 560
            break;

          switch (id) {
561
              /* Should be one level up but some broken muxers write it here. */
562 563 564
            case GST_MATROSKA_ID_TRACKDEFAULTDURATION:{
              guint64 num;

565
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
566
                break;
567 568

              if (num == 0) {
569
                GST_WARNING_OBJECT (demux, "Invalid TrackDefaultDuration 0");
570 571 572
                break;
              }

573 574
              GST_DEBUG_OBJECT (demux,
                  "TrackDefaultDuration: %" G_GUINT64_FORMAT, num);
575 576 577 578 579
              context->default_duration = num;
              break;
            }

              /* video framerate */
580 581
              /* NOTE: This one is here only for backward compatibility.
               * Use _TRACKDEFAULDURATION one level up. */
582 583 584
            case GST_MATROSKA_ID_VIDEOFRAMERATE:{
              gdouble num;

585
              if ((ret = gst_ebml_read_float (ebml, &id, &num)) != GST_FLOW_OK)
586
                break;
587 588

              if (num <= 0.0) {
589
                GST_WARNING_OBJECT (demux, "Invalid TrackVideoFPS %lf", num);
590
                break;
591
              }
592

593
              GST_DEBUG_OBJECT (demux, "TrackVideoFrameRate: %lf", num);
594 595 596 597
              if (context->default_duration == 0)
                context->default_duration =
                    gst_gdouble_to_guint64 ((gdouble) GST_SECOND * (1.0 / num));
              videocontext->default_fps = num;
598 599 600 601 602 603 604
              break;
            }

              /* width of the size to display the video at */
            case GST_MATROSKA_ID_VIDEODISPLAYWIDTH:{
              guint64 num;

605
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
606
                break;
607 608

              if (num == 0) {
609
                GST_WARNING_OBJECT (demux, "Invalid TrackVideoDisplayWidth 0");
610 611 612
                break;
              }

613 614
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoDisplayWidth: %" G_GUINT64_FORMAT, num);
615 616 617 618 619 620 621 622
              videocontext->display_width = num;
              break;
            }

              /* height of the size to display the video at */
            case GST_MATROSKA_ID_VIDEODISPLAYHEIGHT:{
              guint64 num;

623
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
624
                break;
625 626

              if (num == 0) {
627
                GST_WARNING_OBJECT (demux, "Invalid TrackVideoDisplayHeight 0");
628 629 630
                break;
              }

631 632
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoDisplayHeight: %" G_GUINT64_FORMAT, num);
633 634 635 636 637 638 639 640
              videocontext->display_height = num;
              break;
            }

              /* width of the video in the file */
            case GST_MATROSKA_ID_VIDEOPIXELWIDTH:{
              guint64 num;

641
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
642
                break;
643 644

              if (num == 0) {
645
                GST_WARNING_OBJECT (demux, "Invalid TrackVideoPixelWidth 0");
646 647 648
                break;
              }

649 650
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoPixelWidth: %" G_GUINT64_FORMAT, num);
651 652 653 654 655 656 657 658
              videocontext->pixel_width = num;
              break;
            }

              /* height of the video in the file */
            case GST_MATROSKA_ID_VIDEOPIXELHEIGHT:{
              guint64 num;

659
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
660
                break;
661 662

              if (num == 0) {
663
                GST_WARNING_OBJECT (demux, "Invalid TrackVideoPixelHeight 0");
664 665 666
                break;
              }

667 668
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoPixelHeight: %" G_GUINT64_FORMAT, num);
669 670 671 672 673 674 675 676
              videocontext->pixel_height = num;
              break;
            }

              /* whether the video is interlaced */
            case GST_MATROSKA_ID_VIDEOFLAGINTERLACED:{
              guint64 num;

677
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
678
                break;
679

680 681 682 683
              if (num)
                context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
              else
                context->flags &= ~GST_MATROSKA_VIDEOTRACK_INTERLACED;
684
              GST_DEBUG_OBJECT (demux, "TrackVideoInterlaced: %d",
685 686
                  (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED) ? 1 :
                  0);
687 688 689 690
              break;
            }

              /* aspect ratio behaviour */
691
            case GST_MATROSKA_ID_VIDEOASPECTRATIOTYPE:{
692 693
              guint64 num;

694
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
695
                break;
696

697 698 699
              if (num != GST_MATROSKA_ASPECT_RATIO_MODE_FREE &&
                  num != GST_MATROSKA_ASPECT_RATIO_MODE_KEEP &&
                  num != GST_MATROSKA_ASPECT_RATIO_MODE_FIXED) {
700 701
                GST_WARNING_OBJECT (demux,
                    "Unknown TrackVideoAspectRatioType 0x%x", (guint) num);
702 703
                break;
              }
704 705
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoAspectRatioType: %" G_GUINT64_FORMAT, num);
706 707 708 709 710 711
              videocontext->asr_mode = num;
              break;
            }

              /* colourspace (only matters for raw video) fourcc */
            case GST_MATROSKA_ID_VIDEOCOLOURSPACE:{
712 713 714 715 716 717
              guint8 *data;
              guint64 datalen;

              if ((ret =
                      gst_ebml_read_binary (ebml, &id, &data,
                          &datalen)) != GST_FLOW_OK)
718
                break;
719

720
              if (datalen != 4) {
721
                g_free (data);
722 723 724
                GST_WARNING_OBJECT (demux,
                    "Invalid TrackVideoColourSpace length %" G_GUINT64_FORMAT,
                    datalen);
725 726
                break;
              }
727 728 729 730 731

              memcpy (&videocontext->fourcc, data, 4);
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoColourSpace: %" GST_FOURCC_FORMAT,
                  GST_FOURCC_ARGS (videocontext->fourcc));
732
              g_free (data);
733 734
              break;
            }
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
            case GST_MATROSKA_ID_VIDEOSTEREOMODE:
            {
              guint64 num;

              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
                break;

              GST_DEBUG_OBJECT (demux, "StereoMode: %" G_GUINT64_FORMAT, num);

              switch (num) {
                case GST_MATROSKA_STEREO_MODE_SBS_RL:
                  videocontext->multiview_flags =
                      GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
                  /* fall through */
                case GST_MATROSKA_STEREO_MODE_SBS_LR:
                  videocontext->multiview_mode =
                      GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
                  break;
                case GST_MATROSKA_STEREO_MODE_TB_RL:
                  videocontext->multiview_flags =
                      GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
                  /* fall through */
                case GST_MATROSKA_STEREO_MODE_TB_LR:
                  videocontext->multiview_mode =
                      GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM;
                  break;
                case GST_MATROSKA_STEREO_MODE_CHECKER_RL:
                  videocontext->multiview_flags =
                      GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
                  /* fall through */
                case GST_MATROSKA_STEREO_MODE_CHECKER_LR:
                  videocontext->multiview_mode =
                      GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD;
                  break;
                case GST_MATROSKA_STEREO_MODE_FBF_RL:
                  videocontext->multiview_flags =
                      GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
                  /* fall through */
                case GST_MATROSKA_STEREO_MODE_FBF_LR:
                  videocontext->multiview_mode =
                      GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
                  /* FIXME: In frame-by-frame mode, left/right frame buffers are
                   * laced within one block, and we'll need to apply FIRST_IN_BUNDLE
                   * accordingly. See http://www.matroska.org/technical/specs/index.html#StereoMode */
                  GST_FIXME_OBJECT (demux,
                      "Frame-by-frame stereoscopic mode not fully implemented");
                  break;
              }
              break;
            }
785

786 787 788 789
            default:
              GST_WARNING_OBJECT (demux,
                  "Unknown TrackVideo subelement 0x%x - ignoring", id);
              /* fall through */
790 791 792 793 794 795
            case GST_MATROSKA_ID_VIDEODISPLAYUNIT:
            case GST_MATROSKA_ID_VIDEOPIXELCROPBOTTOM:
            case GST_MATROSKA_ID_VIDEOPIXELCROPTOP:
            case GST_MATROSKA_ID_VIDEOPIXELCROPLEFT:
            case GST_MATROSKA_ID_VIDEOPIXELCROPRIGHT:
            case GST_MATROSKA_ID_VIDEOGAMMAVALUE:
796
              ret = gst_ebml_read_skip (ebml);
797 798 799
              break;
          }
        }
800 801

        DEBUG_ELEMENT_STOP (demux, ebml, "TrackVideo", ret);
802
        break;
803 804
      }

805
        /* tracktype specific stuff for audio */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
806
      case GST_MATROSKA_ID_TRACKAUDIO:{
807 808
        GstMatroskaTrackAudioContext *audiocontext;

809 810
        DEBUG_ELEMENT_START (demux, ebml, "TrackAudio");

811
        if (!gst_matroska_track_init_audio_context (&context)) {
812 813
          GST_WARNING_OBJECT (demux,
              "TrackAudio element in non-audio track - ignoring track");
814
          ret = GST_FLOW_ERROR;
815 816
          break;
        }
817 818 819 820

        if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
          break;

821
        audiocontext = (GstMatroskaTrackAudioContext *) context;
822 823
        g_ptr_array_index (demux->common.src, demux->common.num_streams - 1)
            = context;