matroska-demux.c 184 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 21 22 23 24
 *
 * 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
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

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 42 43 44 45 46 47
/**
 * SECTION:element-matroskademux
 *
 * matroskademux demuxes a Matroska file into the different contained streams.
 *
 * <refsect2>
 * <title>Example launch line</title>
 * |[
 * gst-launch -v filesrc location=/path/to/mkv ! matroskademux ! vorbisdec ! audioconvert ! audioresample ! autoaudiosink
 * ]| 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 65
#include <gst/pbutils/pbutils.h>

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

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

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

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
80 81
enum
{
82 83
  ARG_0,
  ARG_METADATA,
84 85
  ARG_STREAMINFO,
  ARG_MAX_GAP_TIME
86 87
};

88 89
#define  DEFAULT_MAX_GAP_TIME      (2 * GST_SECOND)

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
90 91 92
static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
93 94
    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
95
    );
96

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

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

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

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

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

125
/* element functions */
126 127 128
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
129
    GstEvent * event);
130 131 132
static gboolean gst_matroska_demux_element_query (GstElement * element,
    GstQuery * query);

133
/* pad functions */
René Stadler's avatar
René Stadler committed
134 135 136 137
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);
138

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

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

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

/* caps functions */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
161
static GstCaps *gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext
162 163
    * videocontext, const gchar * codec_id, guint8 * data, guint size,
    gchar ** codec_name, guint32 * riff_fourcc);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
164
static GstCaps *gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext
165 166
    * 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
167 168 169
static GstCaps
    * gst_matroska_demux_subtitle_caps (GstMatroskaTrackSubtitleContext *
    subtitlecontext, const gchar * codec_id, gpointer data, guint size);
170 171

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

176 177 178 179 180 181
/* 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);

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

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

191 192 193
  if (demux->common.src) {
    g_ptr_array_free (demux->common.src, TRUE);
    demux->common.src = NULL;
194 195
  }

196 197 198
  if (demux->common.global_tags) {
    gst_tag_list_free (demux->common.global_tags);
    demux->common.global_tags = NULL;
199 200
  }

201
  g_object_unref (demux->common.adapter);
202

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

206
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
207
gst_matroska_demux_class_init (GstMatroskaDemuxClass * klass)
208
{
209 210
  GObjectClass *gobject_class = (GObjectClass *) klass;
  GstElementClass *gstelement_class = (GstElementClass *) klass;
211

212 213
  GST_DEBUG_CATEGORY_INIT (matroskademux_debug, "matroskademux", 0,
      "Matroska demuxer");
214

215 216
  gobject_class->finalize = gst_matroska_demux_finalize;

217 218 219 220 221
  gobject_class->get_property = gst_matroska_demux_get_property;
  gobject_class->set_property = gst_matroska_demux_set_property;

  g_object_class_install_property (gobject_class, ARG_MAX_GAP_TIME,
      g_param_spec_uint64 ("max-gap-time", "Maximum gap time",
René Stadler's avatar
René Stadler committed
222
          "The demuxer sends out segment events for skipping "
223 224 225
          "gaps longer than this (0 = disabled).", 0, G_MAXUINT64,
          DEFAULT_MAX_GAP_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

226 227 228 229
  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);
230 231
  gstelement_class->query =
      GST_DEBUG_FUNCPTR (gst_matroska_demux_element_query);
232
#if 0
233 234 235 236
  gstelement_class->set_index =
      GST_DEBUG_FUNCPTR (gst_matroska_demux_set_index);
  gstelement_class->get_index =
      GST_DEBUG_FUNCPTR (gst_matroska_demux_get_index);
237
#endif
René Stadler's avatar
René Stadler committed
238 239 240 241 242 243 244 245 246 247

  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));

248
  gst_element_class_set_static_metadata (gstelement_class, "Matroska demuxer",
René Stadler's avatar
René Stadler committed
249 250 251
      "Codec/Demuxer",
      "Demuxes Matroska/WebM streams into video/audio/subtitles",
      "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
252 253
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
254
static void
René Stadler's avatar
René Stadler committed
255
gst_matroska_demux_init (GstMatroskaDemux * demux)
256
{
257 258 259
  demux->common.sinkpad = gst_pad_new_from_static_template (&sink_templ,
      "sink");
  gst_pad_set_activate_function (demux->common.sinkpad,
260
      GST_DEBUG_FUNCPTR (gst_matroska_demux_sink_activate));
René Stadler's avatar
René Stadler committed
261 262
  gst_pad_set_activatemode_function (demux->common.sinkpad,
      GST_DEBUG_FUNCPTR (gst_matroska_demux_sink_activate_mode));
263
  gst_pad_set_chain_function (demux->common.sinkpad,
264
      GST_DEBUG_FUNCPTR (gst_matroska_demux_chain));
265
  gst_pad_set_event_function (demux->common.sinkpad,
266
      GST_DEBUG_FUNCPTR (gst_matroska_demux_handle_sink_event));
267
  gst_element_add_pad (GST_ELEMENT (demux), demux->common.sinkpad);
268 269

  /* initial stream no. */
270
  demux->common.src = NULL;
271

272 273
  demux->common.writing_app = NULL;
  demux->common.muxing_app = NULL;
274
  demux->common.index = NULL;
275
  demux->common.global_tags = NULL;
276

277
  demux->common.adapter = gst_adapter_new ();
278

279 280 281
  /* property defaults */
  demux->max_gap_time = DEFAULT_MAX_GAP_TIME;

Wim Taymans's avatar
Wim Taymans committed
282 283
  GST_OBJECT_FLAG_SET (demux, GST_ELEMENT_FLAG_INDEXABLE);

284 285 286 287
  /* finish off */
  gst_matroska_demux_reset (GST_ELEMENT (demux));
}

288 289 290 291 292 293 294 295
static void
gst_matroska_track_free (GstMatroskaTrackContext * track)
{
  g_free (track->codec_id);
  g_free (track->codec_name);
  g_free (track->name);
  g_free (track->language);
  g_free (track->codec_priv);
296
  g_free (track->codec_state);
297 298 299 300 301 302 303 304 305 306 307 308 309 310

  if (track->encodings != NULL) {
    int i;

    for (i = 0; i < track->encodings->len; ++i) {
      GstMatroskaTrackEncoding *enc = &g_array_index (track->encodings,
          GstMatroskaTrackEncoding,
          i);

      g_free (enc->comp_settings);
    }
    g_array_free (track->encodings, TRUE);
  }

311 312 313
  if (track->pending_tags)
    gst_tag_list_free (track->pending_tags);

314 315 316
  if (track->index_table)
    g_array_free (track->index_table, TRUE);

317 318 319
  g_free (track);
}

320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
/*
 * Returns the aggregated GstFlowReturn.
 */
static GstFlowReturn
gst_matroska_demux_combine_flows (GstMatroskaDemux * demux,
    GstMatroskaTrackContext * track, GstFlowReturn ret)
{
  guint i;

  /* store the value */
  track->last_flow = ret;

  /* any other error that is not-linked can be returned right away */
  if (ret != GST_FLOW_NOT_LINKED)
    goto done;

  /* only return NOT_LINKED if all other pads returned NOT_LINKED */
337 338 339 340
  g_assert (demux->common.src->len == demux->common.num_streams);
  for (i = 0; i < demux->common.src->len; i++) {
    GstMatroskaTrackContext *ostream = g_ptr_array_index (demux->common.src,
        i);
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357

    if (ostream == NULL)
      continue;

    ret = ostream->last_flow;
    /* some other return value (must be SUCCESS but we can return
     * other values as well) */
    if (ret != GST_FLOW_NOT_LINKED)
      goto done;
  }
  /* if we get here, all other pads were unlinked and we return
   * NOT_LINKED then */
done:
  GST_LOG_OBJECT (demux, "combined return %s", gst_flow_get_name (ret));
  return ret;
}

358 359 360 361 362 363
static void
gst_matroska_demux_free_parsed_el (gpointer mem, gpointer user_data)
{
  g_slice_free (guint64, mem);
}

364
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
365
gst_matroska_demux_reset (GstElement * element)
366 367 368 369
{
  GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (element);
  guint i;

370 371
  GST_DEBUG_OBJECT (demux, "Resetting state");

372
  /* reset input */
373
  demux->common.state = GST_MATROSKA_READ_STATE_START;
374 375

  /* clean up existing streams */
376 377 378 379 380
  if (demux->common.src) {
    g_assert (demux->common.src->len == demux->common.num_streams);
    for (i = 0; i < demux->common.src->len; i++) {
      GstMatroskaTrackContext *context = g_ptr_array_index (demux->common.src,
          i);
381 382 383 384 385 386

      if (context->pad != NULL)
        gst_element_remove_pad (GST_ELEMENT (demux), context->pad);

      gst_caps_replace (&context->caps, NULL);
      gst_matroska_track_free (context);
387
    }
388
    g_ptr_array_free (demux->common.src, TRUE);
389
  }
390
  demux->common.src = g_ptr_array_new ();
391

392
  demux->common.num_streams = 0;
393 394 395 396 397
  demux->num_a_streams = 0;
  demux->num_t_streams = 0;
  demux->num_v_streams = 0;

  /* reset media info */
398 399 400 401
  g_free (demux->common.writing_app);
  demux->common.writing_app = NULL;
  g_free (demux->common.muxing_app);
  demux->common.muxing_app = NULL;
402 403

  /* reset indexes */
404 405 406
  if (demux->common.index) {
    g_array_free (demux->common.index, TRUE);
    demux->common.index = NULL;
407
  }
408

409 410 411 412 413
  if (demux->clusters) {
    g_array_free (demux->clusters, TRUE);
    demux->clusters = NULL;
  }

414
  /* reset timers */
415
  demux->clock = NULL;
416
  demux->common.time_scale = 1000000;
417
  demux->common.created = G_MININT64;
418

419
  demux->common.index_parsed = FALSE;
420
  demux->tracks_parsed = FALSE;
421
  demux->common.segmentinfo_parsed = FALSE;
422
  demux->common.attachments_parsed = FALSE;
423
  demux->common.chapters_parsed = FALSE;
424

425
  g_list_foreach (demux->common.tags_parsed,
426
      (GFunc) gst_matroska_demux_free_parsed_el, NULL);
427 428
  g_list_free (demux->common.tags_parsed);
  demux->common.tags_parsed = NULL;
429

430 431 432 433 434
  g_list_foreach (demux->seek_parsed,
      (GFunc) gst_matroska_demux_free_parsed_el, NULL);
  g_list_free (demux->seek_parsed);
  demux->seek_parsed = NULL;

435
  gst_segment_init (&demux->common.segment, GST_FORMAT_TIME);
436
  demux->last_stop_end = GST_CLOCK_TIME_NONE;
437
  demux->seek_block = 0;
438
  demux->stream_start_time = GST_CLOCK_TIME_NONE;
439
  demux->to_time = GST_CLOCK_TIME_NONE;
440

441
  demux->common.offset = 0;
442 443
  demux->cluster_time = GST_CLOCK_TIME_NONE;
  demux->cluster_offset = 0;
444
  demux->next_cluster_offset = 0;
445 446
  demux->index_offset = 0;
  demux->seekable = FALSE;
René Stadler's avatar
René Stadler committed
447
  demux->need_segment = FALSE;
448 449 450 451 452
  demux->building_index = FALSE;
  if (demux->seek_event) {
    gst_event_unref (demux->seek_event);
    demux->seek_event = NULL;
  }
453

454 455 456
  demux->seek_index = NULL;
  demux->seek_entry = 0;

457 458 459 460
  if (demux->new_segment) {
    gst_event_unref (demux->new_segment);
    demux->new_segment = NULL;
  }
461
#if 0
462 463 464
  if (demux->common.element_index) {
    gst_object_unref (demux->common.element_index);
    demux->common.element_index = NULL;
465
  }
466
  demux->common.element_index_writer_id = -1;
467
#endif
468

469 470
  if (demux->common.global_tags) {
    gst_tag_list_free (demux->common.global_tags);
471
  }
René Stadler's avatar
René Stadler committed
472
  demux->common.global_tags = gst_tag_list_new_empty ();
473

474
  if (demux->common.cached_buffer) {
René Stadler's avatar
René Stadler committed
475
    if (demux->common.cached_data) {
Wim Taymans's avatar
Wim Taymans committed
476
      gst_buffer_unmap (demux->common.cached_buffer, &demux->common.cached_map);
René Stadler's avatar
René Stadler committed
477 478
      demux->common.cached_data = NULL;
    }
479 480
    gst_buffer_unref (demux->common.cached_buffer);
    demux->common.cached_buffer = NULL;
481
  }
482

483 484
  /* free chapters TOC if any */
  if (demux->common.toc) {
485
    gst_toc_unref (demux->common.toc);
486 487 488
    demux->common.toc = NULL;
  }

489
  demux->invalid_duration = FALSE;
490 491
}

492 493 494
static GstBuffer *
gst_matroska_decode_buffer (GstMatroskaTrackContext * context, GstBuffer * buf)
{
Wim Taymans's avatar
Wim Taymans committed
495 496 497
  GstMapInfo map;
  gpointer data;
  gsize size;
498 499 500

  g_return_val_if_fail (GST_IS_BUFFER (buf), NULL);

501 502
  GST_DEBUG ("decoding buffer %p", buf);

Wim Taymans's avatar
Wim Taymans committed
503 504 505
  gst_buffer_map (buf, &map, GST_MAP_READ);
  data = map.data;
  size = map.size;
René Stadler's avatar
René Stadler committed
506

Wim Taymans's avatar
Wim Taymans committed
507
  g_return_val_if_fail (size > 0, buf);
508 509 510

  if (gst_matroska_decode_data (context->encodings, &data, &size,
          GST_MATROSKA_TRACK_ENCODING_SCOPE_FRAME, FALSE)) {
Wim Taymans's avatar
Wim Taymans committed
511
    gst_buffer_unmap (buf, &map);
512
    gst_buffer_unref (buf);
René Stadler's avatar
René Stadler committed
513
    return gst_buffer_new_wrapped (data, size);
514
  } else {
515
    GST_DEBUG ("decode data failed");
Wim Taymans's avatar
Wim Taymans committed
516
    gst_buffer_unmap (buf, &map);
517 518 519 520 521
    gst_buffer_unref (buf);
    return NULL;
  }
}

522
static GstFlowReturn
523
gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
524 525 526 527 528 529
{
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (demux);
  GstMatroskaTrackContext *context;
  GstPadTemplate *templ = NULL;
  GstCaps *caps = NULL;
  gchar *padname = NULL;
530
  GstFlowReturn ret;
531 532
  guint32 id, riff_fourcc = 0;
  guint16 riff_audio_fmt = 0;
533 534
  GstTagList *list = NULL;
  gchar *codec = NULL;
535

536 537 538 539 540 541 542 543
  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;
  }

544 545 546
  /* allocate generic... if we know the type, we'll g_renew()
   * with the precise type */
  context = g_new0 (GstMatroskaTrackContext, 1);
547 548
  g_ptr_array_add (demux->common.src, context);
  context->index = demux->common.num_streams;
549
  context->index_writer_id = -1;
550
  context->type = 0;            /* no type yet */
551
  context->default_duration = 0;
552
  context->pos = 0;
553
  context->set_discont = TRUE;
554 555 556 557
  context->timecodescale = 1.0;
  context->flags =
      GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT |
      GST_MATROSKA_TRACK_LACING;
558
  context->last_flow = GST_FLOW_OK;
559 560
  context->from_time = GST_CLOCK_TIME_NONE;
  context->from_offset = -1;
561
  context->to_offset = G_MAXINT64;
562
  context->alignment = 1;
563 564
  demux->common.num_streams++;
  g_assert (demux->common.src->len == demux->common.num_streams);
565

566
  GST_DEBUG_OBJECT (demux, "Stream number %d", context->index);
567 568

  /* try reading the trackentry headers */
569 570
  while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
    if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
571
      break;
572

573
    switch (id) {
574
        /* track number (unique stream ID) */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
575
      case GST_MATROSKA_ID_TRACKNUMBER:{
576 577
        guint64 num;

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

581
        if (num == 0) {
582 583 584
          GST_ERROR_OBJECT (demux, "Invalid TrackNumber 0");
          ret = GST_FLOW_ERROR;
          break;
585 586
        } else if (!gst_matroska_read_common_tracknumber_unique (&demux->common,
                num)) {
587 588
          GST_ERROR_OBJECT (demux, "TrackNumber %" G_GUINT64_FORMAT
              " is not unique", num);
589 590 591 592
          ret = GST_FLOW_ERROR;
          break;
        }

593
        GST_DEBUG_OBJECT (demux, "TrackNumber: %" G_GUINT64_FORMAT, num);
594 595
        context->num = num;
        break;
596
      }
597
        /* track UID (unique identifier) */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
598
      case GST_MATROSKA_ID_TRACKUID:{
599 600
        guint64 num;

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

604
        if (num == 0) {
605
          GST_ERROR_OBJECT (demux, "Invalid TrackUID 0");
606 607 608 609
          ret = GST_FLOW_ERROR;
          break;
        }

610
        GST_DEBUG_OBJECT (demux, "TrackUID: %" G_GUINT64_FORMAT, num);
611 612
        context->uid = num;
        break;
613 614
      }

615
        /* track type (video, audio, combined, subtitle, etc.) */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
616
      case GST_MATROSKA_ID_TRACKTYPE:{
617
        guint64 track_type;
618

619
        if ((ret = gst_ebml_read_uint (ebml, &id, &track_type)) != GST_FLOW_OK) {
620 621
          break;
        }
622 623

        if (context->type != 0 && context->type != track_type) {
624 625
          GST_WARNING_OBJECT (demux,
              "More than one tracktype defined in a TrackEntry - skipping");
626
          break;
627
        } else if (track_type < 1 || track_type > 254) {
628 629
          GST_WARNING_OBJECT (demux, "Invalid TrackType %" G_GUINT64_FORMAT,
              track_type);
630
          break;
631 632
        }

633 634
        GST_DEBUG_OBJECT (demux, "TrackType: %" G_GUINT64_FORMAT, track_type);

635
        /* ok, so we're actually going to reallocate this thing */
636
        switch (track_type) {
637
          case GST_MATROSKA_TRACK_TYPE_VIDEO:
638
            gst_matroska_track_init_video_context (&context);
639 640
            break;
          case GST_MATROSKA_TRACK_TYPE_AUDIO:
641
            gst_matroska_track_init_audio_context (&context);
642 643
            break;
          case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
644
            gst_matroska_track_init_subtitle_context (&context);
645
            break;
646
          case GST_MATROSKA_TRACK_TYPE_COMPLEX:
647
          case GST_MATROSKA_TRACK_TYPE_LOGO:
648
          case GST_MATROSKA_TRACK_TYPE_BUTTONS:
649 650
          case GST_MATROSKA_TRACK_TYPE_CONTROL:
          default:
651 652 653
            GST_WARNING_OBJECT (demux,
                "Unknown or unsupported TrackType %" G_GUINT64_FORMAT,
                track_type);
654 655 656
            context->type = 0;
            break;
        }
657 658
        g_ptr_array_index (demux->common.src, demux->common.num_streams - 1)
            = context;
659
        break;
660 661
      }

662
        /* tracktype specific stuff for video */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
663
      case GST_MATROSKA_ID_TRACKVIDEO:{
664 665
        GstMatroskaTrackVideoContext *videocontext;

666 667
        DEBUG_ELEMENT_START (demux, ebml, "TrackVideo");

668
        if (!gst_matroska_track_init_video_context (&context)) {
669 670
          GST_WARNING_OBJECT (demux,
              "TrackVideo element in non-video track - ignoring track");
671
          ret = GST_FLOW_ERROR;
672
          break;
673
        } else if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) {
674 675 676
          break;
        }
        videocontext = (GstMatroskaTrackVideoContext *) context;
677 678
        g_ptr_array_index (demux->common.src, demux->common.num_streams - 1)
            = context;
679

680 681 682
        while (ret == GST_FLOW_OK &&
            gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
          if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
683 684 685
            break;

          switch (id) {
686
              /* Should be one level up but some broken muxers write it here. */
687 688 689
            case GST_MATROSKA_ID_TRACKDEFAULTDURATION:{
              guint64 num;

690
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
691
                break;
692 693

              if (num == 0) {
694
                GST_WARNING_OBJECT (demux, "Invalid TrackDefaultDuration 0");
695 696 697
                break;
              }

698 699
              GST_DEBUG_OBJECT (demux,
                  "TrackDefaultDuration: %" G_GUINT64_FORMAT, num);
700 701 702 703 704
              context->default_duration = num;
              break;
            }

              /* video framerate */
705 706
              /* NOTE: This one is here only for backward compatibility.
               * Use _TRACKDEFAULDURATION one level up. */
707 708 709
            case GST_MATROSKA_ID_VIDEOFRAMERATE:{
              gdouble num;

710
              if ((ret = gst_ebml_read_float (ebml, &id, &num)) != GST_FLOW_OK)
711
                break;
712 713

              if (num <= 0.0) {
714
                GST_WARNING_OBJECT (demux, "Invalid TrackVideoFPS %lf", num);
715
                break;
716
              }
717

718
              GST_DEBUG_OBJECT (demux, "TrackVideoFrameRate: %lf", num);
719 720 721 722
              if (context->default_duration == 0)
                context->default_duration =
                    gst_gdouble_to_guint64 ((gdouble) GST_SECOND * (1.0 / num));
              videocontext->default_fps = num;
723 724 725 726 727 728 729
              break;
            }

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

730
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
731
                break;
732 733

              if (num == 0) {
734
                GST_WARNING_OBJECT (demux, "Invalid TrackVideoDisplayWidth 0");
735 736 737
                break;
              }

738 739
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoDisplayWidth: %" G_GUINT64_FORMAT, num);
740 741 742 743 744 745 746 747
              videocontext->display_width = num;
              break;
            }

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

748
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
749
                break;
750 751

              if (num == 0) {
752
                GST_WARNING_OBJECT (demux, "Invalid TrackVideoDisplayHeight 0");
753 754 755
                break;
              }

756 757
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoDisplayHeight: %" G_GUINT64_FORMAT, num);
758 759 760 761 762 763 764 765
              videocontext->display_height = num;
              break;
            }

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

766
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
767
                break;
768 769

              if (num == 0) {
770
                GST_WARNING_OBJECT (demux, "Invalid TrackVideoPixelWidth 0");
771 772 773
                break;
              }

774 775
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoPixelWidth: %" G_GUINT64_FORMAT, num);
776 777 778 779 780 781 782 783
              videocontext->pixel_width = num;
              break;
            }

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

784
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
785
                break;
786 787

              if (num == 0) {
788
                GST_WARNING_OBJECT (demux, "Invalid TrackVideoPixelHeight 0");
789 790 791
                break;
              }

792 793
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoPixelHeight: %" G_GUINT64_FORMAT, num);
794 795 796 797 798 799 800 801
              videocontext->pixel_height = num;
              break;
            }

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

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

805 806 807 808
              if (num)
                context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
              else
                context->flags &= ~GST_MATROSKA_VIDEOTRACK_INTERLACED;
809
              GST_DEBUG_OBJECT (demux, "TrackVideoInterlaced: %d",
810 811
                  (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED) ? 1 :
                  0);
812 813 814 815
              break;
            }

              /* aspect ratio behaviour */
816
            case GST_MATROSKA_ID_VIDEOASPECTRATIOTYPE:{
817 818
              guint64 num;

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

822 823 824
              if (num != GST_MATROSKA_ASPECT_RATIO_MODE_FREE &&
                  num != GST_MATROSKA_ASPECT_RATIO_MODE_KEEP &&
                  num != GST_MATROSKA_ASPECT_RATIO_MODE_FIXED) {
825 826
                GST_WARNING_OBJECT (demux,
                    "Unknown TrackVideoAspectRatioType 0x%x", (guint) num);
827 828
                break;
              }
829 830
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoAspectRatioType: %" G_GUINT64_FORMAT, num);
831 832 833 834 835 836
              videocontext->asr_mode = num;
              break;
            }

              /* colourspace (only matters for raw video) fourcc */
            case GST_MATROSKA_ID_VIDEOCOLOURSPACE:{
837 838 839 840 841 842
              guint8 *data;
              guint64 datalen;

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

845
              if (datalen != 4) {
846
                g_free (data);
847 848 849
                GST_WARNING_OBJECT (demux,
                    "Invalid TrackVideoColourSpace length %" G_GUINT64_FORMAT,
                    datalen);
850 851
                break;
              }
852 853 854 855 856

              memcpy (&videocontext->fourcc, data, 4);
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoColourSpace: %" GST_FOURCC_FORMAT,
                  GST_FOURCC_ARGS (videocontext->fourcc));
857
              g_free (data);
858 859 860
              break;
            }

861 862 863 864 865
            default:
              GST_WARNING_OBJECT (demux,
                  "Unknown TrackVideo subelement 0x%x - ignoring", id);
              /* fall through */
            case GST_MATROSKA_ID_VIDEOSTEREOMODE:
866 867 868 869 870 871
            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:
872
              ret = gst_ebml_read_skip (ebml);
873 874 875
              break;
          }
        }
876 877

        DEBUG_ELEMENT_STOP (demux, ebml, "TrackVideo", ret);
878
        break;
879 880
      }

881
        /* tracktype specific stuff for audio */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
882
      case GST_MATROSKA_ID_TRACKAUDIO:{
883 884
        GstMatroskaTrackAudioContext *audiocontext;

885 886
        DEBUG_ELEMENT_START (demux, ebml, "TrackAudio");

887
        if (!gst_matroska_track_init_audio_context (&context)) {
888 889
          GST_WARNING_OBJECT (demux,
              "TrackAudio element in non-audio track - ignoring track");
890
          ret = GST_FLOW_ERROR;
891 892
          break;
        }
893 894 895 896

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

897
        audiocontext = (GstMatroskaTrackAudioContext *) context;
898 899
        g_ptr_array_index (demux->common.src, demux->common.num_streams - 1)
            = context;
900

901 902 903
        while (ret == GST_FLOW_OK &&
            gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
          if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
904 905 906 907 908 909 910
            break;

          switch (id) {
              /* samplerate */
            case GST_MATROSKA_ID_AUDIOSAMPLINGFREQ:{
              gdouble num;

911
              if ((ret = gst_ebml_read_float (ebml, &id, &num)) != GST_FLOW_OK)
912
                break;
913

914 915

              if (num <= 0.0) {
916 917
                GST_WARNING_OBJECT (demux,
                    "Invalid TrackAudioSamplingFrequency %lf", num);
918 919 920
                break;
              }

921
              GST_DEBUG_OBJECT (demux, "TrackAudioSamplingFrequency: %lf", num);
922 923 924 925 926 927 928 929
              audiocontext->samplerate = num;
              break;
            }

              /* bitdepth */
            case GST_MATROSKA_ID_AUDIOBITDEPTH:{
              guint64 num;

930
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
931
                break;
932 933

              if (num == 0) {
934
                GST_WARNING_OBJECT (demux, "Invalid TrackAudioBitDepth 0");
935 936 937
                break;
              }

938 939
              GST_DEBUG_OBJECT (demux, "TrackAudioBitDepth: %" G_GUINT64_FORMAT,
                  num);
940 941 942 943 944 945 946 947
              audiocontext->bitdepth = num;
              break;
            }

              /* channels */
            case GST_MATROSKA_ID_AUDIOCHANNELS:{
              guint64 num;

948
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
949
                break;
950 951

              if (num == 0) {
952
                GST_WARNING_OBJECT (demux, "Invalid TrackAudioChannels 0");
953 954 955
                break;
              }

956 957
              GST_DEBUG_OBJECT (demux, "TrackAudioChannels: %" G_GUINT64_FORMAT,
                  num);
958 959 960 961
              audiocontext->channels = num;
              break;
            }

962 963 964 965
            default:
              GST_WARNING_OBJECT (demux,
                  "Unknown TrackAudio subelement 0x%x - ignoring", id);
              /* fall through */
966 967
            case GST_MATROSKA_ID_AUDIOCHANNELPOSITIONS:
            case GST_MATROSKA_ID_AUDIOOUTPUTSAMPLINGFREQ:
968
              ret = gst_ebml_read_skip (ebml);
969 970 971
              break;
          }
        }
972 973 974

        DEBUG_ELEMENT_STOP (demux, ebml, "TrackAudio", ret);

975
        break;
976 977
      }

978
        /* codec identifier */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
979
      case GST_MATROSKA_ID_CODECID:{