matroska-demux.c 185 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
  ARG_0,
  ARG_METADATA,
85
86
  ARG_STREAMINFO,
  ARG_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
194
195
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

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

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

205
206
  gobject_class->finalize = gst_matroska_demux_finalize;

207
208
209
210
211
  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
212
          "The demuxer sends out segment events for skipping "
213
214
215
          "gaps longer than this (0 = disabled).", 0, G_MAXUINT64,
          DEFAULT_MAX_GAP_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

216
217
218
219
  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);
220
221
  gstelement_class->query =
      GST_DEBUG_FUNCPTR (gst_matroska_demux_element_query);
222
#if 0
223
224
225
226
  gstelement_class->set_index =
      GST_DEBUG_FUNCPTR (gst_matroska_demux_set_index);
  gstelement_class->get_index =
      GST_DEBUG_FUNCPTR (gst_matroska_demux_get_index);
227
#endif
René Stadler's avatar
René Stadler committed
228
229
230
231
232
233
234
235
236
237

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

238
  gst_element_class_set_static_metadata (gstelement_class, "Matroska demuxer",
René Stadler's avatar
René Stadler committed
239
240
241
      "Codec/Demuxer",
      "Demuxes Matroska/WebM streams into video/audio/subtitles",
      "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
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
269
270
  /* finish off */
  gst_matroska_demux_reset (GST_ELEMENT (demux));
}

271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
/*
 * 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 */
288
289
290
291
  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);
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308

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

309
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
310
gst_matroska_demux_reset (GstElement * element)
311
312
313
{
  GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (element);

314
315
  GST_DEBUG_OBJECT (demux, "Resetting state");

316
  gst_matroska_read_common_reset (GST_ELEMENT (demux), &demux->common);
317

318
319
320
321
  demux->num_a_streams = 0;
  demux->num_t_streams = 0;
  demux->num_v_streams = 0;

322
323
324
  demux->have_group_id = FALSE;
  demux->group_id = G_MAXUINT;

325
326
  demux->clock = NULL;
  demux->tracks_parsed = FALSE;
327

328
329
330
331
332
  if (demux->clusters) {
    g_array_free (demux->clusters, TRUE);
    demux->clusters = NULL;
  }

333
  g_list_foreach (demux->seek_parsed,
334
      (GFunc) gst_matroska_read_common_free_parsed_el, NULL);
335
336
337
  g_list_free (demux->seek_parsed);
  demux->seek_parsed = NULL;

338
  demux->last_stop_end = GST_CLOCK_TIME_NONE;
339
  demux->seek_block = 0;
340
  demux->stream_start_time = GST_CLOCK_TIME_NONE;
341
  demux->to_time = GST_CLOCK_TIME_NONE;
342
343
  demux->cluster_time = GST_CLOCK_TIME_NONE;
  demux->cluster_offset = 0;
344
  demux->next_cluster_offset = 0;
345
346
  demux->index_offset = 0;
  demux->seekable = FALSE;
René Stadler's avatar
René Stadler committed
347
  demux->need_segment = FALSE;
348
  demux->segment_seqnum = 0;
349
350
  demux->requested_seek_time = GST_CLOCK_TIME_NONE;
  demux->seek_offset = -1;
351
352
353
354
355
  demux->building_index = FALSE;
  if (demux->seek_event) {
    gst_event_unref (demux->seek_event);
    demux->seek_event = NULL;
  }
356

357
358
359
  demux->seek_index = NULL;
  demux->seek_entry = 0;

360
361
362
363
  if (demux->new_segment) {
    gst_event_unref (demux->new_segment);
    demux->new_segment = NULL;
  }
364

365
  demux->invalid_duration = FALSE;
366
367
}

368
369
370
static GstBuffer *
gst_matroska_decode_buffer (GstMatroskaTrackContext * context, GstBuffer * buf)
{
Wim Taymans's avatar
Wim Taymans committed
371
372
373
  GstMapInfo map;
  gpointer data;
  gsize size;
374
375
376

  g_return_val_if_fail (GST_IS_BUFFER (buf), NULL);

377
378
  GST_DEBUG ("decoding buffer %p", buf);

Wim Taymans's avatar
Wim Taymans committed
379
380
381
  gst_buffer_map (buf, &map, GST_MAP_READ);
  data = map.data;
  size = map.size;
René Stadler's avatar
René Stadler committed
382

Wim Taymans's avatar
Wim Taymans committed
383
  g_return_val_if_fail (size > 0, buf);
384
385
386

  if (gst_matroska_decode_data (context->encodings, &data, &size,
          GST_MATROSKA_TRACK_ENCODING_SCOPE_FRAME, FALSE)) {
Wim Taymans's avatar
Wim Taymans committed
387
    gst_buffer_unmap (buf, &map);
388
    gst_buffer_unref (buf);
René Stadler's avatar
René Stadler committed
389
    return gst_buffer_new_wrapped (data, size);
390
  } else {
391
    GST_DEBUG ("decode data failed");
Wim Taymans's avatar
Wim Taymans committed
392
    gst_buffer_unmap (buf, &map);
393
394
395
396
397
    gst_buffer_unref (buf);
    return NULL;
  }
}

398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
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);
}

423
static GstFlowReturn
424
gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
425
426
427
428
{
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (demux);
  GstMatroskaTrackContext *context;
  GstPadTemplate *templ = NULL;
429
  GstStreamFlags stream_flags;
430
431
  GstCaps *caps = NULL;
  gchar *padname = NULL;
432
  GstFlowReturn ret;
433
434
  guint32 id, riff_fourcc = 0;
  guint16 riff_audio_fmt = 0;
435
  GstTagList *list = NULL;
436
  GstEvent *stream_start;
437
  gchar *codec = NULL;
438
  gchar *stream_id;
439

440
441
442
443
444
445
446
447
  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;
  }

448
449
450
  /* allocate generic... if we know the type, we'll g_renew()
   * with the precise type */
  context = g_new0 (GstMatroskaTrackContext, 1);
451
452
  g_ptr_array_add (demux->common.src, context);
  context->index = demux->common.num_streams;
453
  context->index_writer_id = -1;
454
  context->type = 0;            /* no type yet */
455
  context->default_duration = 0;
456
  context->pos = 0;
457
  context->set_discont = TRUE;
458
459
460
461
  context->timecodescale = 1.0;
  context->flags =
      GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT |
      GST_MATROSKA_TRACK_LACING;
462
  context->last_flow = GST_FLOW_OK;
463
464
  context->from_time = GST_CLOCK_TIME_NONE;
  context->from_offset = -1;
465
  context->to_offset = G_MAXINT64;
466
  context->alignment = 1;
467
468
  demux->common.num_streams++;
  g_assert (demux->common.src->len == demux->common.num_streams);
469

470
  GST_DEBUG_OBJECT (demux, "Stream number %d", context->index);
471
472

  /* try reading the trackentry headers */
473
474
  while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
    if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
475
      break;
476

477
    switch (id) {
478
        /* track number (unique stream ID) */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
479
      case GST_MATROSKA_ID_TRACKNUMBER:{
480
481
        guint64 num;

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

485
        if (num == 0) {
486
487
488
          GST_ERROR_OBJECT (demux, "Invalid TrackNumber 0");
          ret = GST_FLOW_ERROR;
          break;
489
490
        } else if (!gst_matroska_read_common_tracknumber_unique (&demux->common,
                num)) {
491
492
          GST_ERROR_OBJECT (demux, "TrackNumber %" G_GUINT64_FORMAT
              " is not unique", num);
493
494
495
496
          ret = GST_FLOW_ERROR;
          break;
        }

497
        GST_DEBUG_OBJECT (demux, "TrackNumber: %" G_GUINT64_FORMAT, num);
498
499
        context->num = num;
        break;
500
      }
501
        /* track UID (unique identifier) */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
502
      case GST_MATROSKA_ID_TRACKUID:{
503
504
        guint64 num;

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

508
        if (num == 0) {
509
          GST_ERROR_OBJECT (demux, "Invalid TrackUID 0");
510
511
512
513
          ret = GST_FLOW_ERROR;
          break;
        }

514
        GST_DEBUG_OBJECT (demux, "TrackUID: %" G_GUINT64_FORMAT, num);
515
516
        context->uid = num;
        break;
517
518
      }

519
        /* track type (video, audio, combined, subtitle, etc.) */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
520
      case GST_MATROSKA_ID_TRACKTYPE:{
521
        guint64 track_type;
522

523
        if ((ret = gst_ebml_read_uint (ebml, &id, &track_type)) != GST_FLOW_OK) {
524
525
          break;
        }
526
527

        if (context->type != 0 && context->type != track_type) {
528
529
          GST_WARNING_OBJECT (demux,
              "More than one tracktype defined in a TrackEntry - skipping");
530
          break;
531
        } else if (track_type < 1 || track_type > 254) {
532
533
          GST_WARNING_OBJECT (demux, "Invalid TrackType %" G_GUINT64_FORMAT,
              track_type);
534
          break;
535
536
        }

537
538
        GST_DEBUG_OBJECT (demux, "TrackType: %" G_GUINT64_FORMAT, track_type);

539
        /* ok, so we're actually going to reallocate this thing */
540
        switch (track_type) {
541
          case GST_MATROSKA_TRACK_TYPE_VIDEO:
542
            gst_matroska_track_init_video_context (&context);
543
544
            break;
          case GST_MATROSKA_TRACK_TYPE_AUDIO:
545
            gst_matroska_track_init_audio_context (&context);
546
547
            break;
          case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
548
            gst_matroska_track_init_subtitle_context (&context);
549
            break;
550
          case GST_MATROSKA_TRACK_TYPE_COMPLEX:
551
          case GST_MATROSKA_TRACK_TYPE_LOGO:
552
          case GST_MATROSKA_TRACK_TYPE_BUTTONS:
553
554
          case GST_MATROSKA_TRACK_TYPE_CONTROL:
          default:
555
556
557
            GST_WARNING_OBJECT (demux,
                "Unknown or unsupported TrackType %" G_GUINT64_FORMAT,
                track_type);
558
559
560
            context->type = 0;
            break;
        }
561
562
        g_ptr_array_index (demux->common.src, demux->common.num_streams - 1)
            = context;
563
        break;
564
565
      }

566
        /* tracktype specific stuff for video */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
567
      case GST_MATROSKA_ID_TRACKVIDEO:{
568
569
        GstMatroskaTrackVideoContext *videocontext;

570
571
        DEBUG_ELEMENT_START (demux, ebml, "TrackVideo");

572
        if (!gst_matroska_track_init_video_context (&context)) {
573
574
          GST_WARNING_OBJECT (demux,
              "TrackVideo element in non-video track - ignoring track");
575
          ret = GST_FLOW_ERROR;
576
          break;
577
        } else if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) {
578
579
580
          break;
        }
        videocontext = (GstMatroskaTrackVideoContext *) context;
581
582
        g_ptr_array_index (demux->common.src, demux->common.num_streams - 1)
            = context;
583

584
585
586
        while (ret == GST_FLOW_OK &&
            gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
          if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
587
588
589
            break;

          switch (id) {
590
              /* Should be one level up but some broken muxers write it here. */
591
592
593
            case GST_MATROSKA_ID_TRACKDEFAULTDURATION:{
              guint64 num;

594
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
595
                break;
596
597

              if (num == 0) {
598
                GST_WARNING_OBJECT (demux, "Invalid TrackDefaultDuration 0");
599
600
601
                break;
              }

602
603
              GST_DEBUG_OBJECT (demux,
                  "TrackDefaultDuration: %" G_GUINT64_FORMAT, num);
604
605
606
607
608
              context->default_duration = num;
              break;
            }

              /* video framerate */
609
610
              /* NOTE: This one is here only for backward compatibility.
               * Use _TRACKDEFAULDURATION one level up. */
611
612
613
            case GST_MATROSKA_ID_VIDEOFRAMERATE:{
              gdouble num;

614
              if ((ret = gst_ebml_read_float (ebml, &id, &num)) != GST_FLOW_OK)
615
                break;
616
617

              if (num <= 0.0) {
618
                GST_WARNING_OBJECT (demux, "Invalid TrackVideoFPS %lf", num);
619
                break;
620
              }
621

622
              GST_DEBUG_OBJECT (demux, "TrackVideoFrameRate: %lf", num);
623
624
625
626
              if (context->default_duration == 0)
                context->default_duration =
                    gst_gdouble_to_guint64 ((gdouble) GST_SECOND * (1.0 / num));
              videocontext->default_fps = num;
627
628
629
630
631
632
633
              break;
            }

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

634
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
635
                break;
636
637

              if (num == 0) {
638
                GST_WARNING_OBJECT (demux, "Invalid TrackVideoDisplayWidth 0");
639
640
641
                break;
              }

642
643
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoDisplayWidth: %" G_GUINT64_FORMAT, num);
644
645
646
647
648
649
650
651
              videocontext->display_width = num;
              break;
            }

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

652
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
653
                break;
654
655

              if (num == 0) {
656
                GST_WARNING_OBJECT (demux, "Invalid TrackVideoDisplayHeight 0");
657
658
659
                break;
              }

660
661
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoDisplayHeight: %" G_GUINT64_FORMAT, num);
662
663
664
665
666
667
668
669
              videocontext->display_height = num;
              break;
            }

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

670
              if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
671
                break;
672
673

              if (num == 0) {
674
                GST_WARNING_OBJECT (demux, "Invalid TrackVideoPixelWidth 0");
675
676
677
                break;
              }

678
679
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoPixelWidth: %" G_GUINT64_FORMAT, num);
680
681
682
683
684
685
686
687
              videocontext->pixel_width = num;
              break;
            }

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

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

              if (num == 0) {
692
                GST_WARNING_OBJECT (demux, "Invalid TrackVideoPixelHeight 0");
693
694
695
                break;
              }

696
697
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoPixelHeight: %" G_GUINT64_FORMAT, num);
698
699
700
701
702
703
704
705
              videocontext->pixel_height = num;
              break;
            }

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

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

709
710
711
712
              if (num)
                context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
              else
                context->flags &= ~GST_MATROSKA_VIDEOTRACK_INTERLACED;
713
              GST_DEBUG_OBJECT (demux, "TrackVideoInterlaced: %d",
714
715
                  (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED) ? 1 :
                  0);
716
717
718
719
              break;
            }

              /* aspect ratio behaviour */
720
            case GST_MATROSKA_ID_VIDEOASPECTRATIOTYPE:{
721
722
              guint64 num;

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

726
727
728
              if (num != GST_MATROSKA_ASPECT_RATIO_MODE_FREE &&
                  num != GST_MATROSKA_ASPECT_RATIO_MODE_KEEP &&
                  num != GST_MATROSKA_ASPECT_RATIO_MODE_FIXED) {
729
730
                GST_WARNING_OBJECT (demux,
                    "Unknown TrackVideoAspectRatioType 0x%x", (guint) num);
731
732
                break;
              }
733
734
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoAspectRatioType: %" G_GUINT64_FORMAT, num);
735
736
737
738
739
740
              videocontext->asr_mode = num;
              break;
            }

              /* colourspace (only matters for raw video) fourcc */
            case GST_MATROSKA_ID_VIDEOCOLOURSPACE:{
741
742
743
744
745
746
              guint8 *data;
              guint64 datalen;

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

749
              if (datalen != 4) {
750
                g_free (data);
751
752
753
                GST_WARNING_OBJECT (demux,
                    "Invalid TrackVideoColourSpace length %" G_GUINT64_FORMAT,
                    datalen);
754
755
                break;
              }
756
757
758
759
760

              memcpy (&videocontext->fourcc, data, 4);
              GST_DEBUG_OBJECT (demux,
                  "TrackVideoColourSpace: %" GST_FOURCC_FORMAT,
                  GST_FOURCC_ARGS (videocontext->fourcc));
761
              g_free (data);
762
763
764
              break;
            }

765
766
767
768
769
            default:
              GST_WARNING_OBJECT (demux,
                  "Unknown TrackVideo subelement 0x%x - ignoring", id);
              /* fall through */
            case GST_MATROSKA_ID_VIDEOSTEREOMODE:
770
771
772
773
774
775
            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:
776
              ret = gst_ebml_read_skip (ebml);
777
778
779
              break;
          }
        }
780
781

        DEBUG_ELEMENT_STOP (demux, ebml, "TrackVideo", ret);
782
        break;
783
784
      }

785
        /* tracktype specific stuff for audio */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
786
      case GST_MATROSKA_ID_TRACKAUDIO:{
787
788
        GstMatroskaTrackAudioContext *audiocontext;

789
790
        DEBUG_ELEMENT_START (demux, ebml, "TrackAudio");

791
        if (!gst_matroska_track_init_audio_context (&context)) {
792
793
          GST_WARNING_OBJECT (demux,
              "TrackAudio element in non-audio track - ignoring track");
794
          ret = GST_FLOW_ERROR;
795
796
          break;
        }
797
798
799
800

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

801
        audiocontext = (GstMatroskaTrackAudioContext *) context;
802
803
        g_ptr_array_index (demux->common.src, demux->common.num_streams - 1)
            = context;
804

805
806
807
        while (ret == GST_FLOW_OK &&
            gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
          if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
808
809
810
811
812
813
814
            break;

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

815
              if ((ret = gst_ebml_read_float (ebml, &id, &num)) != GST_FLOW_OK)
816
                break;
817

818
819

              if (num <= 0.0) {
820
821
                GST_WARNING_OBJECT (demux,
                    "Invalid TrackAudioSamplingFrequency %lf", num);
822
823
824
                break;
              }

825
              GST_DEBUG_OBJECT (demux, "TrackAudioSamplingFrequency: %lf", num);
826
827
828
829
830
831
832
833
              audiocontext->samplerate = num;
              break;
            }

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

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

              if (num == 0) {
838
                GST_WARNING_OBJECT (demux, "Invalid TrackAudioBitDepth 0");
839
840
841
                break;
              }

842
843
              GST_DEBUG_OBJECT (demux, "TrackAudioBitDepth: %" G_GUINT64_FORMAT,
                  num);
844
845
846
847
848
849
850
851
              audiocontext->bitdepth = num;
              break;
            }

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

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

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

860
861
              GST_DEBUG_OBJECT (demux, "TrackAudioChannels: %" G_GUINT64_FORMAT,
                  num);
862
863
864
865
              audiocontext->channels = num;
              break;
            }

866
867
868
869
            default:
              GST_WARNING_OBJECT (demux,
                  "Unknown TrackAudio subelement 0x%x - ignoring", id);
              /* fall through */
870
871
            case GST_MATROSKA_ID_AUDIOCHANNELPOSITIONS:
            case GST_MATROSKA_ID_AUDIOOUTPUTSAMPLINGFREQ:
872
              ret = gst_ebml_read_skip (ebml);
873
874
875
              break;
          }
        }
876
877
878

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

879
        break;
880
881
      }

882
        /* codec identifier */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
883
      case GST_MATROSKA_ID_CODECID:{
884
885
        gchar *text;

886
        if ((ret = gst_ebml_read_ascii (ebml, &id, &text)) != GST_FLOW_OK)
887
          break;
888
889

        GST_DEBUG_OBJECT (demux, "CodecID: %s", GST_STR_NULL (text));
890
891
        context->codec_id = text;
        break;
892
893
      }

894
        /* codec private data */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
895
      case GST_MATROSKA_ID_CODECPRIVATE:{
896
897
898
        guint8 *data;
        guint64 size;

899
        if ((ret =
900
                gst_ebml_read_binary (ebml, &id, &data, &size)) != GST_FLOW_OK)
901
          break;
902

903
904
        context->codec_priv = data;
        context->codec_priv_size = size;
905
906
907

        GST_DEBUG_OBJECT (demux, "CodecPrivate of size %" G_GUINT64_FORMAT,
            size);
908
        break;
909
910
      }

911
        /* name of the codec */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
912
      case GST_MATROSKA_ID_CODECNAME:{
913
914
        gchar *text;

915
        if ((ret = gst_ebml_read_utf8 (ebml, &id, &text)) != GST_FLOW_OK)
916
          break;