matroska-mux.c 102 KB
Newer Older
1
2
/* GStreamer Matroska muxer/demuxer
 * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3
 * (c) 2005 Michal Benes <michal.benes@xeris.cz>
4
 * (c) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>
5
 * (c) 2011 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 *
 * matroska-mux.c: matroska file/stream muxer
 *
 * 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
26
27
28
/* TODO: - check everywhere that we don't write invalid values
 *       - make sure timestamps are correctly scaled everywhere
 */

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
 * SECTION:element-matroskamux
 *
 * matroskamux muxes different input streams into a Matroska file.
 *
 * <refsect2>
 * <title>Example launch line</title>
 * |[
 * gst-launch -v filesrc location=/path/to/mp3 ! mp3parse ! matroskamux name=mux ! filesink location=test.mkv  filesrc location=/path/to/theora.ogg ! oggdemux ! theoraparse ! mux.
 * ]| This pipeline muxes an MP3 file and a Ogg Theora video into a Matroska file.
 * |[
 * gst-launch -v audiotestsrc num-buffers=100 ! audioconvert ! vorbisenc ! matroskamux ! filesink location=test.mka
 * ]| This pipeline muxes a 440Hz sine wave encoded with the Vorbis codec into a Matroska file.
 * </refsect2>
 */

45
46
47
48
49
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <math.h>
50
#include <stdio.h>
51
52
#include <string.h>

53
#include <gst/riff/riff-media.h>
54
#include <gst/tag/tag.h>
55

56
57
58
#include "matroska-mux.h"
#include "matroska-ids.h"

59
GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
60
61
#define GST_CAT_DEFAULT matroskamux_debug

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
62
63
enum
{
64
  ARG_0,
65
  ARG_WRITING_APP,
66
  ARG_DOCTYPE_VERSION,
Xavier Queralt's avatar
Xavier Queralt committed
67
  ARG_MIN_INDEX_INTERVAL,
68
  ARG_STREAMABLE
69
70
};

71
#define  DEFAULT_DOCTYPE_VERSION         2
72
#define  DEFAULT_WRITING_APP             "GStreamer Matroska muxer"
73
#define  DEFAULT_MIN_INDEX_INTERVAL      0
74
#define  DEFAULT_STREAMABLE              FALSE
75

Stefan Kost's avatar
Stefan Kost committed
76
77
78
/* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
#define WAVEFORMATEX_SIZE  (2 + sizeof (gst_riff_strf_auds))

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
79
80
81
82
83
static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-matroska")
    );
84

David Schleef's avatar
David Schleef committed
85
86
87
#define COMMON_VIDEO_CAPS \
  "width = (int) [ 16, 4096 ], " \
  "height = (int) [ 16, 4096 ], " \
88
  "framerate = (fraction) [ 0, MAX ]"
89

90
91
92
93
#define COMMON_VIDEO_CAPS_NO_FRAMERATE \
  "width = (int) [ 16, 4096 ], " \
  "height = (int) [ 16, 4096 ] "

94
/* FIXME:
95
96
97
 * * require codec data, etc as needed
 */

David Schleef's avatar
David Schleef committed
98
static GstStaticPadTemplate videosink_templ =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
99
100
101
102
    GST_STATIC_PAD_TEMPLATE ("video_%d",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS ("video/mpeg, "
103
104
105
        "mpegversion = (int) { 1, 2, 4 }, "
        "systemstream = (boolean) false, "
        COMMON_VIDEO_CAPS "; "
106
        "video/x-h264, stream-format=avc, alignment=au, "
107
        COMMON_VIDEO_CAPS "; "
108
109
110
111
        "video/x-divx, "
        COMMON_VIDEO_CAPS "; "
        "video/x-xvid, "
        COMMON_VIDEO_CAPS "; "
112
113
114
115
116
117
        "video/x-huffyuv, "
        COMMON_VIDEO_CAPS "; "
        "video/x-dv, "
        COMMON_VIDEO_CAPS "; "
        "video/x-h263, "
        COMMON_VIDEO_CAPS "; "
118
119
        "video/x-msmpeg, "
        COMMON_VIDEO_CAPS "; "
120
        "image/jpeg, "
121
        COMMON_VIDEO_CAPS_NO_FRAMERATE "; "
122
        "video/x-theora; "
123
124
        "video/x-dirac, "
        COMMON_VIDEO_CAPS "; "
125
126
127
        "video/x-pn-realvideo, "
        "rmversion = (int) [1, 4], "
        COMMON_VIDEO_CAPS "; "
128
129
        "video/x-vp8, "
        COMMON_VIDEO_CAPS "; "
130
        "video/x-raw-yuv, "
131
        "format = (fourcc) { YUY2, I420, YV12, UYVY, AYUV }, "
132
133
        COMMON_VIDEO_CAPS "; "
        "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
134
    );
David Schleef's avatar
David Schleef committed
135
136

#define COMMON_AUDIO_CAPS \
137
138
  "channels = (int) [ 1, MAX ], " \
  "rate = (int) [ 1, MAX ]"
139
140

/* FIXME:
141
 * * require codec data, etc as needed
142
 */
David Schleef's avatar
David Schleef committed
143
static GstStaticPadTemplate audiosink_templ =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
144
145
146
147
    GST_STATIC_PAD_TEMPLATE ("audio_%d",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS ("audio/mpeg, "
148
149
150
151
152
        "mpegversion = (int) 1, "
        "layer = (int) [ 1, 3 ], "
        COMMON_AUDIO_CAPS "; "
        "audio/mpeg, "
        "mpegversion = (int) { 2, 4 }, "
153
        "stream-format = (string) raw, "
154
155
156
        COMMON_AUDIO_CAPS "; "
        "audio/x-ac3, "
        COMMON_AUDIO_CAPS "; "
157
158
        "audio/x-eac3, "
        COMMON_AUDIO_CAPS "; "
159
160
        "audio/x-dts, "
        COMMON_AUDIO_CAPS "; "
161
162
        "audio/x-vorbis, "
        COMMON_AUDIO_CAPS "; "
163
164
        "audio/x-flac, "
        COMMON_AUDIO_CAPS "; "
165
166
        "audio/x-speex, "
        COMMON_AUDIO_CAPS "; "
167
        "audio/x-raw-int, "
168
169
170
171
172
173
174
        "width = (int) 8, "
        "depth = (int) 8, "
        "signed = (boolean) false, "
        COMMON_AUDIO_CAPS ";"
        "audio/x-raw-int, "
        "width = (int) 16, "
        "depth = (int) 16, "
175
        "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
176
        "signed = (boolean) true, "
177
        COMMON_AUDIO_CAPS ";"
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
        "audio/x-raw-int, "
        "width = (int) 24, "
        "depth = (int) 24, "
        "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
        "signed = (boolean) true, "
        COMMON_AUDIO_CAPS ";"
        "audio/x-raw-int, "
        "width = (int) 32, "
        "depth = (int) 32, "
        "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
        "signed = (boolean) true, "
        COMMON_AUDIO_CAPS ";"
        "audio/x-raw-float, "
        "width = (int) [ 32, 64 ], "
        "endianness = (int) LITTLE_ENDIAN, "
        COMMON_AUDIO_CAPS ";"
194
        "audio/x-tta, "
195
        "width = (int) { 8, 16, 24 }, "
196
197
        "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
        "audio/x-pn-realaudio, "
198
199
200
        "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
        "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
        "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
201
202
203
204
205
        COMMON_AUDIO_CAPS ";"
        "audio/x-alaw, "
        "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
        "audio/x-mulaw, "
        "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
206
    );
David Schleef's avatar
David Schleef committed
207
208

static GstStaticPadTemplate subtitlesink_templ =
209
    GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
210
211
    GST_PAD_SINK,
    GST_PAD_REQUEST,
212
213
214
215
216
    GST_STATIC_CAPS ("subtitle/x-kate; "
        "text/plain; application/x-ssa; application/x-ass; "
        "application/x-usf; video/x-dvd-subpicture; "
        "application/x-subtitle-unknown")
    );
217

218
static GArray *used_uids;
219
G_LOCK_DEFINE_STATIC (used_uids);
220

221
222
223
224
static void gst_matroska_mux_add_interfaces (GType type);

GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
    GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
225
226
227

/* Matroska muxer destructor */
static void gst_matroska_mux_finalize (GObject * object);
228

229
/* Pads collected callback */
230
231
static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads2 * pads,
    GstCollectData2 * data, GstBuffer * buf, gpointer user_data);
232
233
static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads,
    GstCollectData2 * data, GstEvent * event, gpointer user_data);
234
235

/* pad functions */
236
237
static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
    GstEvent * event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
238
239
static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
    GstPadTemplate * templ, const gchar * name);
240
static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
241
242

/* gst internal change state handler */
243
244
static GstStateChangeReturn
gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
245
246

/* gobject bla bla */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
247
248
249
250
static void gst_matroska_mux_set_property (GObject * object,
    guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_matroska_mux_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec);
251
252

/* reset muxer */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
253
static void gst_matroska_mux_reset (GstElement * element);
254

255
/* uid generation */
256
static guint64 gst_matroska_mux_create_uid ();
257

258
259
260
261
static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
    GstMatroskaTrackContext * context);
static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
    GstMatroskaTrackContext * context);
262
263
static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
    GstMatroskaTrackContext * context);
264
265
static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
    GstMatroskaTrackContext * context);
266
267
static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
    GstMatroskaTrackContext * context);
268
269
270
static void
gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
    gpointer data);
271

272
273
274
275
276
277
278
279
static void
gst_matroska_mux_add_interfaces (GType type)
{
  static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };

  g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
}

280
static void
281
gst_matroska_mux_base_init (gpointer g_class)
282
{
283
284
285
286
287
288
289
}

static void
gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;
290

291
292
293
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;

294
295
296
297
298
299
300
  gst_element_class_add_static_pad_template (gstelement_class,
      &videosink_templ);
  gst_element_class_add_static_pad_template (gstelement_class,
      &audiosink_templ);
  gst_element_class_add_static_pad_template (gstelement_class,
      &subtitlesink_templ);
  gst_element_class_add_static_pad_template (gstelement_class, &src_templ);
301
  gst_element_class_set_details_simple (gstelement_class, "Matroska muxer",
302
303
      "Codec/Muxer",
      "Muxes video/audio/subtitle streams into a matroska stream",
304
      "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
305
306
307

  GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
      "Matroska muxer");
308

309
  gobject_class->finalize = gst_matroska_mux_finalize;
310
311
312
313

  gobject_class->get_property = gst_matroska_mux_get_property;
  gobject_class->set_property = gst_matroska_mux_set_property;

314
315
316
  g_object_class_install_property (gobject_class, ARG_WRITING_APP,
      g_param_spec_string ("writing-app", "Writing application.",
          "The name the application that creates the matroska file.",
317
          NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
318
319
  g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
      g_param_spec_int ("version", "DocType version",
320
          "This parameter determines what Matroska features can be used.",
321
322
          1, 2, DEFAULT_DOCTYPE_VERSION,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
323
324
325
  g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
      g_param_spec_int64 ("min-index-interval", "Minimum time between index "
          "entries", "An index entry is created every so many nanoseconds.",
326
327
          0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
328
329
330
331
  g_object_class_install_property (gobject_class, ARG_STREAMABLE,
      g_param_spec_boolean ("streamable", "Determines whether output should "
          "be streamable", "If set to true, the output should be as if it is "
          "to be streamed and hence no indexes written or duration written.",
332
333
          DEFAULT_STREAMABLE,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
334

335
336
337
338
339
340
  gstelement_class->change_state =
      GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
  gstelement_class->request_new_pad =
      GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
  gstelement_class->release_pad =
      GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
341
342
}

343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
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
391
392
393
394
395
396
397
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
423
424
/**
 * Start of pad option handler code
 */
#define DEFAULT_PAD_FRAME_DURATION TRUE
#define DEFAULT_PAD_FRAME_DURATION_VP8 FALSE

enum
{
  PROP_PAD_0,
  PROP_PAD_FRAME_DURATION
};

typedef struct
{
  GstPad parent;
  gboolean frame_duration;
  gboolean frame_duration_user;
} GstMatroskamuxPad;

static void gst_matroskamux_pad_class_init (GstPadClass * klass);

static GType
gst_matroskamux_pad_get_type (void)
{
  static GType type = 0;

  if (G_UNLIKELY (type == 0)) {
    type = g_type_register_static_simple (GST_TYPE_PAD,
        g_intern_static_string ("GstMatroskamuxPad"), sizeof (GstPadClass),
        (GClassInitFunc) gst_matroskamux_pad_class_init,
        sizeof (GstMatroskamuxPad), NULL, 0);
  }
  return type;
}

#define GST_TYPE_MATROSKAMUX_PAD (gst_matroskamux_pad_get_type())
#define GST_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_MATROSKAMUX_PAD,GstMatroskamuxPad))
#define GST_MATROSKAMUX_PAD_CAST(pad) ((GstMatroskamuxPad *) pad)
#define GST_IS_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_MATROSKAMUX_PAD))

static void
gst_matroskamux_pad_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);

  switch (prop_id) {
    case PROP_PAD_FRAME_DURATION:
      g_value_set_boolean (value, pad->frame_duration);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_matroskamux_pad_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);

  switch (prop_id) {
    case PROP_PAD_FRAME_DURATION:
      pad->frame_duration = g_value_get_boolean (value);
      pad->frame_duration_user = TRUE;
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_matroskamux_pad_class_init (GstPadClass * klass)
{
  GObjectClass *gobject_class = (GObjectClass *) klass;

  gobject_class->set_property = gst_matroskamux_pad_set_property;
  gobject_class->get_property = gst_matroskamux_pad_get_property;

  g_object_class_install_property (gobject_class, PROP_PAD_FRAME_DURATION,
425
      g_param_spec_boolean ("frame-duration", "Frame duration",
426
427
428
429
430
431
432
433
434
435
436
437
438
439
          "Default frame duration", DEFAULT_PAD_FRAME_DURATION,
          G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
}

static void
gst_matroskamux_pad_init (GstMatroskamuxPad * pad)
{
  pad->frame_duration = DEFAULT_PAD_FRAME_DURATION;
  pad->frame_duration_user = FALSE;
}

/*
 * End of pad option handler code
 **/
440
441
442
443
444
445
446
447

/**
 * gst_matroska_mux_init:
 * @mux: #GstMatroskaMux that should be initialized.
 * @g_class: Class of the muxer.
 *
 * Matroska muxer constructor.
 */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
448
static void
449
gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
450
{
451
452
453
454
455
456
  GstPadTemplate *templ;

  templ =
      gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
  mux->srcpad = gst_pad_new_from_template (templ, "src");

457
  gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
458
459
  gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);

460
  mux->collect = gst_collect_pads2_new ();
461
462
463
464
  gst_collect_pads2_set_clip_function (mux->collect,
      GST_DEBUG_FUNCPTR (gst_collect_pads2_clip_running_time), mux);
  gst_collect_pads2_set_buffer_function (mux->collect,
      GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux);
465
  gst_collect_pads2_set_event_function (mux->collect,
466
      GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux);
467

468
  mux->ebml_write = gst_ebml_write_new (mux->srcpad);
469
  mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
470

471
  /* property defaults */
472
  mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
473
  mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
474
  mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
475
  mux->streamable = DEFAULT_STREAMABLE;
476

477
  /* initialize internal variables */
478
  mux->index = NULL;
479
480
481
482
  mux->num_streams = 0;
  mux->num_a_streams = 0;
  mux->num_t_streams = 0;
  mux->num_v_streams = 0;
483

484
  /* initialize remaining variables */
485
486
487
  gst_matroska_mux_reset (GST_ELEMENT (mux));
}

488
489
490
491
492
493
494
495
496
497
498
499

/**
 * gst_matroska_mux_finalize:
 * @object: #GstMatroskaMux that should be finalized.
 *
 * Finalize matroska muxer.
 */
static void
gst_matroska_mux_finalize (GObject * object)
{
  GstMatroskaMux *mux = GST_MATROSKA_MUX (object);

500
501
  gst_event_replace (&mux->force_key_unit_event, NULL);

502
503
  gst_object_unref (mux->collect);
  gst_object_unref (mux->ebml_write);
504
505
  if (mux->writing_app)
    g_free (mux->writing_app);
506
507
508
509
510
511
512
513
514
515
516
517

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


/**
 * gst_matroska_mux_create_uid:
 *
 * Generate new unused track UID.
 *
 * Returns: New track UID.
 */
518
static guint64
519
gst_matroska_mux_create_uid (void)
520
{
521
  guint64 uid = 0;
522

523
524
525
526
  G_LOCK (used_uids);

  if (!used_uids)
    used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
527
528
529
530

  while (!uid) {
    guint i;

531
    uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
532
    for (i = 0; i < used_uids->len; i++) {
533
      if (g_array_index (used_uids, guint64, i) == uid) {
534
535
536
537
538
539
        uid = 0;
        break;
      }
    }
    g_array_append_val (used_uids, uid);
  }
540
541

  G_UNLOCK (used_uids);
542
543
544
  return uid;
}

545

546
/**
547
 * gst_matroska_pad_reset:
548
549
 * @collect_pad: the #GstMatroskaPad
 *
550
 * Reset and/or release resources of a matroska collect pad.
551
552
 */
static void
553
gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
554
{
555
556
  gchar *name = NULL;
  GstMatroskaTrackType type = 0;
557

558
  /* free track information */
559
  if (collect_pad->track != NULL) {
560
561
562
563
564
565
566
567
568
569
570
571
572
    /* retrieve for optional later use */
    name = collect_pad->track->name;
    type = collect_pad->track->type;
    /* extra for video */
    if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
      GstMatroskaTrackVideoContext *ctx =
          (GstMatroskaTrackVideoContext *) collect_pad->track;

      if (ctx->dirac_unit) {
        gst_buffer_unref (ctx->dirac_unit);
        ctx->dirac_unit = NULL;
      }
    }
573
574
    g_free (collect_pad->track->codec_id);
    g_free (collect_pad->track->codec_name);
575
576
    if (full)
      g_free (collect_pad->track->name);
577
578
579
580
581
582
    g_free (collect_pad->track->language);
    g_free (collect_pad->track->codec_priv);
    g_free (collect_pad->track);
    collect_pad->track = NULL;
  }

583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
  if (!full && type != 0) {
    GstMatroskaTrackContext *context;

    /* create a fresh context */
    switch (type) {
      case GST_MATROSKA_TRACK_TYPE_VIDEO:
        context = (GstMatroskaTrackContext *)
            g_new0 (GstMatroskaTrackVideoContext, 1);
        break;
      case GST_MATROSKA_TRACK_TYPE_AUDIO:
        context = (GstMatroskaTrackContext *)
            g_new0 (GstMatroskaTrackAudioContext, 1);
        break;
      case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
        context = (GstMatroskaTrackContext *)
            g_new0 (GstMatroskaTrackSubtitleContext, 1);
        break;
      default:
        g_assert_not_reached ();
602
        return;
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
    }

    context->type = type;
    context->name = name;
    /* TODO: check default values for the context */
    context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
    collect_pad->track = context;
    collect_pad->duration = 0;
    collect_pad->start_ts = GST_CLOCK_TIME_NONE;
    collect_pad->end_ts = GST_CLOCK_TIME_NONE;
  }
}

/**
 * gst_matroska_pad_free:
 * @collect_pad: the #GstMatroskaPad
 *
 * Release resources of a matroska collect pad.
 */
static void
gst_matroska_pad_free (GstMatroskaPad * collect_pad)
{
  gst_matroska_pad_reset (collect_pad, TRUE);
626
627
}

628
629
630
631
632
633
634

/**
 * gst_matroska_mux_reset:
 * @element: #GstMatroskaMux that should be reseted.
 *
 * Reset matroska muxer back to initial state.
 */
635
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
636
gst_matroska_mux_reset (GstElement * element)
637
638
{
  GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
639
640
641
642
  GSList *walk;

  /* reset EBML write */
  gst_ebml_write_reset (mux->ebml_write);
643
644
645
646
647

  /* reset input */
  mux->state = GST_MATROSKA_MUX_STATE_START;

  /* clean up existing streams */
648
649

  for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
650
651
652
653
    GstMatroskaPad *collect_pad;

    collect_pad = (GstMatroskaPad *) walk->data;

654
655
    /* reset collect pad to pristine state */
    gst_matroska_pad_reset (collect_pad, FALSE);
656
  }
657
658
659
660
661
662
663

  /* reset indexes */
  mux->num_indexes = 0;
  g_free (mux->index);
  mux->index = NULL;

  /* reset timers */
664
  mux->time_scale = GST_MSECOND;
665
  mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
666
  mux->duration = 0;
667

668
669
670
671
  /* reset cluster */
  mux->cluster = 0;
  mux->cluster_time = 0;
  mux->cluster_pos = 0;
672
  mux->prev_cluster_size = 0;
673

674
  /* reset tags */
675
  gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
676
677
}

678
679
680
681
682
/**
 * gst_matroska_mux_handle_src_event:
 * @pad: Pad which received the event.
 * @event: Received event.
 *
683
 * handle events - copied from oggmux without understanding
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
 *
 * Returns: #TRUE on success.
 */
static gboolean
gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
{
  GstEventType type;

  type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;

  switch (type) {
    case GST_EVENT_SEEK:
      /* disable seeking for now */
      return FALSE;
    default:
      break;
  }

  return gst_pad_event_default (pad, event);
}

705
706
707
708
709
710
711
712
713
714
715

static void
gst_matroska_mux_free_codec_priv (GstMatroskaTrackContext * context)
{
  if (context->codec_priv != NULL) {
    g_free (context->codec_priv);
    context->codec_priv = NULL;
    context->codec_priv_size = 0;
  }
}

716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
static void
gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context,
    const guint * clut)
{
  gchar *clutv[17];
  gchar *sclut;
  gint i;
  guint32 col;
  gdouble y, u, v;
  guint8 r, g, b;

  /* produce comma-separated list in hex format */
  for (i = 0; i < 16; ++i) {
    col = clut[i];
    /* replicate vobsub's slightly off RGB conversion calculation */
    y = (((col >> 16) & 0xff) - 16) * 255 / 219;
    u = ((col >> 8) & 0xff) - 128;
    v = (col & 0xff) - 128;
    r = CLAMP (1.0 * y + 1.4022 * u, 0, 255);
    g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255);
    b = CLAMP (1.0 * y + 1.7710 * v, 0, 255);
    clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b);
  }
  clutv[i] = NULL;
  sclut = g_strjoinv (",", clutv);

  /* build codec private; only palette for now */
743
  gst_matroska_mux_free_codec_priv (context);
744
745
746
747
748
749
750
751
752
753
  context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut);
  /* include terminating 0 */
  context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1;
  g_free (sclut);
  for (i = 0; i < 16; ++i) {
    g_free (clutv[i]);
  }
}


754
755
756
757
758
759
760
761
762
763
/**
 * gst_matroska_mux_handle_sink_event:
 * @pad: Pad which received the event.
 * @event: Received event.
 *
 * handle events - informational ones like tags
 *
 * Returns: #TRUE on success.
 */
static gboolean
764
765
gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads,
    GstCollectData2 * data, GstEvent * event, gpointer user_data)
766
767
768
769
{
  GstMatroskaTrackContext *context;
  GstMatroskaPad *collect_pad;
  GstMatroskaMux *mux;
770
  GstPad *pad;
771
772
  GstTagList *list;

773
774
775
  mux = GST_MATROSKA_MUX (user_data);
  collect_pad = (GstMatroskaPad *) data;
  pad = data->pad;
776
777
  context = collect_pad->track;
  g_assert (context);
778
779

  switch (GST_EVENT_TYPE (event)) {
780
781
782
    case GST_EVENT_TAG:{
      gchar *lang = NULL;

783
      GST_DEBUG_OBJECT (mux, "received tag event");
784
      gst_event_parse_tag (event, &list);
785

786
787
788
789
790
791
792
793
794
795
796
797
798
      /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
      if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
        const gchar *lang_code;

        lang_code = gst_tag_get_language_code_iso_639_2B (lang);
        if (lang_code) {
          GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
          context->language = g_strdup (lang_code);
        } else {
          GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
        }
        g_free (lang);
      }
799

800
      /* FIXME: what about stream-specific tags? */
801
802
      gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
          gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
803
804

      gst_event_unref (event);
805
806
      /* handled this, don't want collectpads to forward it downstream */
      event = NULL;
807
      break;
808
    }
809
810
811
812
813
814
815
816
817
    case GST_EVENT_NEWSEGMENT:{
      GstFormat format;

      gst_event_parse_new_segment (event, NULL, NULL, &format, NULL, NULL,
          NULL);
      if (format != GST_FORMAT_TIME) {
        gst_event_unref (event);
        event = NULL;
      }
818
      break;
819
    }
820
821
822
823
824
825
826
827
    case GST_EVENT_CUSTOM_DOWNSTREAM:{
      const GstStructure *structure;

      structure = gst_event_get_structure (event);
      if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
        gst_event_replace (&mux->force_key_unit_event, NULL);
        mux->force_key_unit_event = event;
        event = NULL;
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
      } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
          !strcmp ("dvd-spu-clut-change",
              gst_structure_get_string (structure, "event"))) {
        gchar name[16];
        gint i, value;
        guint clut[16];

        GST_DEBUG_OBJECT (pad, "New DVD colour table received");
        if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
          GST_DEBUG_OBJECT (pad, "... discarding");
          break;
        }
        /* first transform event data into table form */
        for (i = 0; i < 16; i++) {
          g_snprintf (name, sizeof (name), "clut%02d", i);
          if (!gst_structure_get_int (structure, name, &value)) {
            GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
                "contain %s field", name);
            break;
          }
          clut[i] = value;
        }

        /* transform into private data for stream; text form */
        gst_matroska_mux_build_vobsub_private (context, clut);
853
854
855
      }
      break;
    }
856
857
858
859
    default:
      break;
  }

860
  /* now GstCollectPads2 can take care of the rest, e.g. EOS */
861
  if (event)
862
863
864
    return FALSE;
  else
    return TRUE;
865
866
}

867
868
869
870
871
872
873
874
875
static void
gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
    const char *id)
{
  g_assert (context && id);
  if (context->codec_id)
    g_free (context->codec_id);
  context->codec_id = g_strdup (id);
}
876
877
878
879
880
881
882
883
884
885
886
887

/**
 * gst_matroska_mux_video_pad_setcaps:
 * @pad: Pad which got the caps.
 * @caps: New caps.
 *
 * Setcaps function for video sink pad.
 *
 * Returns: #TRUE on success.
 */
static gboolean
gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
888
889
890
{
  GstMatroskaTrackContext *context = NULL;
  GstMatroskaTrackVideoContext *videocontext;
891
  GstMatroskaMux *mux;
892
  GstMatroskaPad *collect_pad;
893
  GstStructure *structure;
894
  const gchar *mimetype;
895
896
  const GValue *value = NULL;
  const GstBuffer *codec_buf = NULL;
897
  gint width, height, pixel_width, pixel_height;
898
  gint fps_d, fps_n;
899
  gboolean interlaced = FALSE;
900

901
902
  mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));

903
  /* find context */
904
905
906
907
  collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
  g_assert (collect_pad);
  context = collect_pad->track;
  g_assert (context);
908
909
910
911
  g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
  videocontext = (GstMatroskaTrackVideoContext *) context;

  /* gst -> matroska ID'ing */
David Schleef's avatar
David Schleef committed
912
913
914
915
  structure = gst_caps_get_structure (caps, 0);

  mimetype = gst_structure_get_name (structure);

916
917
918
919
  if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
      && interlaced)
    context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;

920
921
922
923
924
  if (!strcmp (mimetype, "video/x-theora")) {
    /* we'll extract the details later from the theora identification header */
    goto skip_details;
  }

David Schleef's avatar
David Schleef committed
925
  /* get general properties */
926
927
928
929
930
  /* spec says it is mandatory */
  if (!gst_structure_get_int (structure, "width", &width) ||
      !gst_structure_get_int (structure, "height", &height))
    goto refuse_caps;

David Schleef's avatar
David Schleef committed
931
932
  videocontext->pixel_width = width;
  videocontext->pixel_height = height;
933
934
935
936
937
938
939
940
941

  /* set vp8 defaults or let user override it */
  if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration_user == FALSE
      && (!strcmp (mimetype, "video/x-vp8")))
    GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration =
        DEFAULT_PAD_FRAME_DURATION_VP8;

  if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
      && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
942
      && fps_n > 0) {
943
944
945
946
947
948
949
    context->default_duration =
        gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
    GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
        GST_TIME_ARGS (context->default_duration));
  } else {
    context->default_duration = 0;
  }
950
951
  if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
          &pixel_width, &pixel_height)) {
David Schleef's avatar
David Schleef committed
952
953
954
955
956
957
    if (pixel_width > pixel_height) {
      videocontext->display_width = width * pixel_width / pixel_height;
      videocontext->display_height = height;
    } else if (pixel_width < pixel_height) {
      videocontext->display_width = width;
      videocontext->display_height = height * pixel_height / pixel_width;
958
959
960
961
    } else {
      videocontext->display_width = 0;
      videocontext->display_height = 0;
    }
David Schleef's avatar
David Schleef committed
962
963
964
965
  } else {
    videocontext->display_width = 0;
    videocontext->display_height = 0;
  }
966

967
968
skip_details:

David Schleef's avatar
David Schleef committed
969
970
971
  videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
  videocontext->fourcc = 0;

972
973
974
975
976
  /* TODO: - check if we handle all codecs by the spec, i.e. codec private
   *         data and other settings
   *       - add new formats
   */

977
978
979
980
981
  /* extract codec_data, may turn out needed */
  value = gst_structure_get_value (structure, "codec_data");
  if (value)
    codec_buf = gst_value_get_buffer (value);

David Schleef's avatar
David Schleef committed
982
983
  /* find type */
  if (!strcmp (mimetype, "video/x-raw-yuv")) {
984
985
    gst_matroska_mux_set_codec_id (context,
        GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
David Schleef's avatar
David Schleef committed
986
    gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
987
988
989
990
  } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
      ||!strcmp (mimetype, "video/x-huffyuv")
      || !strcmp (mimetype, "video/x-divx")
      || !strcmp (mimetype, "video/x-dv")
991
      || !strcmp (mimetype, "video/x-h263")
992
      || !strcmp (mimetype, "video/x-msmpeg")
993
994
      || !strcmp (mimetype, "video/x-wmv")
      || !strcmp (mimetype, "image/jpeg")) {
Stefan Kost's avatar
Stefan Kost committed
995
996
    gst_riff_strf_vids *bih;
    gint size = sizeof (gst_riff_strf_vids);
997
    guint32 fourcc = 0;
David Schleef's avatar
David Schleef committed
998

999
    if (!strcmp (mimetype, "video/x-xvid"))
1000
      fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
1001
    else if (!strcmp (mimetype, "video/x-huffyuv"))
1002
      fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
1003
    else if (!strcmp (mimetype, "video/x-dv"))
1004
      fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
1005
    else if (!strcmp (mimetype, "video/x-h263"))
1006
      fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
1007
1008
1009
1010
1011
1012
    else if (!strcmp (mimetype, "video/x-divx")) {
      gint divxversion;

      gst_structure_get_int (structure, "divxversion", &divxversion);
      switch (divxversion) {
        case 3:
1013
          fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
1014
1015
          break;
        case 4:
1016
          fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
1017
1018
          break;
        case 5:
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
          fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
          break;
      }
    } else if (!strcmp (mimetype, "video/x-msmpeg")) {
      gint msmpegversion;

      gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
      switch (msmpegversion) {
        case 41:
          fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
          break;
        case 42:
          fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
          break;
        case 43:
          goto msmpeg43;
1035
1036
          break;
      }
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
    } else if (!strcmp (mimetype, "video/x-wmv")) {
      gint wmvversion;
      guint32 format;
      if (gst_structure_get_fourcc (structure, "format", &format)) {
        fourcc = format;
      } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
        if (wmvversion == 2) {
          fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
        } else if (wmvversion == 1) {
          fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
        } else if (wmvversion == 3) {
          fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
        }
      }
1051
1052
    } else if (!strcmp (mimetype, "image/jpeg")) {
      fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
David Schleef's avatar
David Schleef committed
1053
    }
1054

1055
    if (!fourcc)
1056
      goto refuse_caps;
1057

Stefan Kost's avatar
Stefan Kost committed
1058
1059
1060
1061
1062
1063
1064
1065
    bih = g_new0 (gst_riff_strf_vids, 1);
    GST_WRITE_UINT32_LE (&bih->size, size);
    GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
    GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
    GST_WRITE_UINT32_LE (&bih->compression, fourcc);
    GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
    GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
    GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1066
1067
1068
1069
1070
        videocontext->pixel_height * 3);

    /* process codec private/initialization data, if any */
    if (codec_buf) {
      size += GST_BUFFER_SIZE (codec_buf);
1071
      bih = g_realloc (bih, size);
Stefan Kost's avatar
Stefan Kost committed
1072
1073
      GST_WRITE_UINT32_LE (&bih->size, size);
      memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
1074
          GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
1075
    }
1076

1077
1078
    gst_matroska_mux_set_codec_id (context,
        GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1079
    gst_matroska_mux_free_codec_priv (context);
1080
    context->codec_priv = (gpointer) bih;
1081
    context->codec_priv_size = size;
1082
  } else if (!strcmp (mimetype, "video/x-h264")) {
1083
1084
    gst_matroska_mux_set_codec_id (context,
        GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1085
    gst_matroska_mux_free_codec_priv (context);
1086
    /* Create avcC header */
1087
1088
1089
1090
1091
    if (codec_buf != NULL) {
      context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
      context->codec_priv = g_malloc0 (context->codec_priv_size);
      memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
          context->codec_priv_size);
1092
    }
1093
1094
1095
  } else if (!strcmp (mimetype, "video/x-theora")) {
    const GValue *streamheader;

1096
    gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1097

1098
    gst_matroska_mux_free_codec_priv (context);
1099
1100
1101
1102
1103

    streamheader = gst_structure_get_value (structure, "streamheader");
    if (!theora_streamheader_to_codecdata (streamheader, context)) {
      GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
          ("theora stream headers missing or malformed"));
1104
      goto refuse_caps;
1105
    }
1106
  } else if (!strcmp (mimetype, "video/x-dirac")) {
1107
    gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1108
  } else if (!strcmp (mimetype, "video/x-vp8")) {
1109
    gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
David Schleef's avatar
David Schleef committed
1110
1111
1112
1113
1114
1115
  } else if (!strcmp (mimetype, "video/mpeg")) {
    gint mpegversion;

    gst_structure_get_int (structure, "mpegversion", &mpegversion);
    switch (mpegversion) {
      case 1:
1116
1117
        gst_matroska_mux_set_codec_id (context,
            GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1118
        break;
David Schleef's avatar
David Schleef committed
1119
      case 2:
1120
1121
        gst_matroska_mux_set_codec_id (context,
            GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1122
        break;
1123
      case 4:
1124
1125
        gst_matroska_mux_set_codec_id (context,
            GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1126
        break;
1127
      default:
1128
        goto refuse_caps;
David Schleef's avatar
David Schleef committed
1129
    }
1130

1131
1132
    /* global headers may be in codec data */
    if (codec_buf != NULL) {
1133
      gst_matroska_mux_free_codec_priv (context);
1134
1135
1136
1137
1138
      context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
      context->codec_priv = g_malloc0 (context->codec_priv_size);
      memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
          context->codec_priv_size);
    }
David Schleef's avatar
David Schleef committed
1139
  } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1140
1141
  msmpeg43:
    /* can only make it here if preceding case verified it was version 3 */
David Schleef's avatar
David Schleef committed
1142
    context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1143
1144
1145
1146
1147
1148
1149
  } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
    gint rmversion;
    const GValue *mdpr_data;

    gst_structure_get_int (structure, "rmversion", &rmversion);
    switch (rmversion) {
      case 1:
1150
1151
        gst_matroska_mux_set_codec_id (context,
            GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1152
1153
        break;
      case 2:
1154
1155
        gst_matroska_mux_set_codec_id (context,
            GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1156
1157
        break;
      case 3:
1158
1159
        gst_matroska_mux_set_codec_id (context,
            GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1160
1161
        break;
      case 4:
1162
1163
        gst_matroska_mux_set_codec_id (context,
            GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1164
1165
        break;
      default:
1166
        goto refuse_caps;
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
    }

    mdpr_data = gst_structure_get_value (structure, "mdpr_data");
    if (mdpr_data != NULL) {
      guint8 *priv_data = NULL;
      guint priv_data_size = 0;

      GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);

      priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
      priv_data = g_malloc0 (priv_data_size);

      memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);

1181
      gst_matroska_mux_free_codec_priv (context);
1182
1183
1184
      context->codec_priv = priv_data;
      context->codec_priv_size = priv_data_size;
    }
1185
1186
  }

1187
1188
1189
1190
1191
1192
1193
1194
1195
  return TRUE;

  /* ERRORS */
refuse_caps:
  {
    GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
        GST_PAD_NAME (pad), caps);
    return FALSE;
  }