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, GstSeekFlags flags);
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
  gst_element_class_add_static_pad_template (gstelement_class,
      &video_src_templ);
  gst_element_class_add_static_pad_template (gstelement_class,
      &audio_src_templ);
  gst_element_class_add_static_pad_template (gstelement_class,
      &subtitle_src_templ);
  gst_element_class_add_static_pad_template (gstelement_class, &sink_templ);
René Stadler's avatar
René Stadler committed
237

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

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

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

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

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

267
268
  demux->flowcombiner = gst_flow_combiner_new ();

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

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

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

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

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

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

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

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

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

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

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

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

329
  demux->invalid_duration = FALSE;
330

331
332
  demux->cached_length = G_MAXUINT64;

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

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

  g_return_val_if_fail (GST_IS_BUFFER (buf), NULL);

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

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

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

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

366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
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);
}

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

408
409
410
411
412
413
414
415
  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;
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

              memcpy (&videocontext->fourcc, data, 4);
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoColourSpace: %" GST_FOURCC_FORMAT,
                  GST_FOURCC_ARGS (videocontext->fourcc));
731
              g_free (data);
732
733
              break;
            }
734
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
            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;
            }
784

785
786
787
788
            default:
              GST_WARNING_OBJECT (demux,
                  "Unknown TrackVideo subelement 0x%x - ignoring", id);
              /* fall through */
789
790
791
792
793
794
            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:
795
              ret = gst_ebml_read_skip (ebml);
796
797
798
              break;
          }
        }
799
800

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

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

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

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

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

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

824
825
826
        while (ret == GST_FLOW_OK &&
            gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
          if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
827
828
829
830
831
832
833
            break;

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

834
              if ((ret = gst_ebml_read_float (ebml, &id, &num)) != GST_FLOW_OK)
835
                break;
836

837
838

              if (num <= 0.0) {
839
840
                GST_WARNING_OBJECT (demux,
                    "Invalid TrackAudioSamplingFrequency %lf", num);
841
842
843
                break;
              }

844
              GST_DEBUG_OBJECT (demux, "TrackAudioSamplingFrequency: %lf", num);
845
846
847
848
849
850
851
852
              audiocontext->samplerate = num;
              break;
            }

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

853
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
854
                break;
855
856

              if (num == 0) {
857
                GST_WARNING_OBJECT (demux, "Invalid TrackAudioBitDepth 0");
858
859
860
                break;
              }

861
862
              GST_DEBUG_OBJECT (demux, "TrackAudioBitDepth: %" G_GUINT64_FORMAT,
                  num);
863
864
865
866
867
868
869
870
              audiocontext->bitdepth = num;
              break;
            }

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

871
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
872
                break;
873
874

              if (num == 0) {
875
                GST_WARNING_OBJECT (demux, "Invalid TrackAudioChannels 0");
876
877
878
                break;
              }

879
880
              GST_DEBUG_OBJECT (demux, "TrackAudioChannels: %" G_GUINT64_FORMAT,
                  num);
881
882
883
884
              audiocontext->channels = num;
              break;
            }

885
886
887
888
            default:
              GST_WARNING_OBJECT (demux,
                  "Unknown TrackAudio subelement 0x%x - ignoring", id);
              /* fall through */
889
890
            case GST_MATROSKA_ID_AUDIOCHANNELPOSITIONS:
            case GST_MATROSKA_ID_AUDIOOUTPUTSAMPLINGFREQ:
891
              ret = gst_ebml_read_skip (ebml);
892
893
894
              break;
          }
        }
895
896
897

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

898
        break;
899
900
      }

901
        /* codec identifier */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
902
      case GST_MATROSKA_ID_CODECID:{
903
904
        gchar *text;

905
        if ((ret = gst_ebml_read_ascii (ebml, &id, &text)) != GST_FLOW_OK)
906
          break;
907
908

        GST_DEBUG_OBJECT (demux, "CodecID: %s", GST_STR_NULL (text));
909
910
        context->codec_id = text;
        break;
911
912
      }

913
        /* codec private data */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
914
      case GST_MATROSKA_ID_CODECPRIVATE:{
915
916
917
        guint8 *data;
        guint64 size;

918
        if ((ret =
919
                gst_ebml_read_binary (ebml, &id, &data, &size)) != GST_FLOW_OK)
920
          break;
921

922
923
        context->codec_priv = data;
        context->codec_priv_size = size;
924
925
926

        GST_DEBUG_OBJECT (demux, "CodecPrivate of size %" G_GUINT64_FORMAT,
            size);
927
        break;
928
929
      }

930
        /* name of the codec */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
931
      case GST_MATROSKA_ID_CODECNAME:{
932
933
        gchar *text;

934
        if ((ret = gst_ebml_read_utf8 (ebml, &id, &text)) != GST_FLOW_OK)
935
          break;
936
937

        GST_DEBUG_OBJECT (demux, "CodecName: %s", GST_STR_NULL (text));
938
939
        context->codec_name = text;
        break;
940
941
      }

942
943
944
945
946
947
948
949
950
951