matroska-mux.c 113 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
 *
 * 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
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 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
/**
 * SECTION:element-matroskamux
 *
 * matroskamux muxes different input streams into a Matroska file.
 *
 * <refsect2>
 * <title>Example launch line</title>
 * |[
37
 * gst-launch-1.0 -v filesrc location=/path/to/mp3 ! mpegaudioparse ! matroskamux name=mux ! filesink location=test.mkv  filesrc location=/path/to/theora.ogg ! oggdemux ! theoraparse ! mux.
38 39
 * ]| This pipeline muxes an MP3 file and a Ogg Theora video into a Matroska file.
 * |[
40
 * gst-launch-1.0 -v audiotestsrc num-buffers=100 ! audioconvert ! vorbisenc ! matroskamux ! filesink location=test.mka
41 42 43 44
 * ]| 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>

René Stadler's avatar
René Stadler committed
53
#include <gst/audio/audio.h>
54
#include <gst/riff/riff-media.h>
55
#include <gst/tag/tag.h>
56

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

60 61
#define GST_MATROSKA_MUX_CHAPLANG "und"

62
GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
63 64
#define GST_CAT_DEFAULT matroskamux_debug

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

74
#define  DEFAULT_DOCTYPE_VERSION         2
75
#define  DEFAULT_WRITING_APP             "GStreamer Matroska muxer"
76
#define  DEFAULT_MIN_INDEX_INTERVAL      0
77
#define  DEFAULT_STREAMABLE              FALSE
78

Stefan Kost's avatar
Stefan Kost committed
79 80 81
/* 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
82 83 84
static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
85
    GST_STATIC_CAPS ("video/x-matroska; video/x-matroska-3d; audio/x-matroska")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
86
    );
87

David Schleef's avatar
David Schleef committed
88
#define COMMON_VIDEO_CAPS \
89 90 91
  "width = (int) [ 16, 4096 ], " \
  "height = (int) [ 16, 4096 ] "

92
/* FIXME:
93 94 95
 * * require codec data, etc as needed
 */

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

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

/* FIXME:
139
 * * require codec data, etc as needed
140
 */
David Schleef's avatar
David Schleef committed
141
static GstStaticPadTemplate audiosink_templ =
142
    GST_STATIC_PAD_TEMPLATE ("audio_%u",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
143 144 145
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS ("audio/mpeg, "
146 147 148 149 150
        "mpegversion = (int) 1, "
        "layer = (int) [ 1, 3 ], "
        COMMON_AUDIO_CAPS "; "
        "audio/mpeg, "
        "mpegversion = (int) { 2, 4 }, "
151
        "stream-format = (string) raw, "
152 153 154
        COMMON_AUDIO_CAPS "; "
        "audio/x-ac3, "
        COMMON_AUDIO_CAPS "; "
155 156
        "audio/x-eac3, "
        COMMON_AUDIO_CAPS "; "
157 158
        "audio/x-dts, "
        COMMON_AUDIO_CAPS "; "
159 160
        "audio/x-vorbis, "
        COMMON_AUDIO_CAPS "; "
161 162
        "audio/x-flac, "
        COMMON_AUDIO_CAPS "; "
163
        "audio/x-opus; "
164 165
        "audio/x-speex, "
        COMMON_AUDIO_CAPS "; "
René Stadler's avatar
René Stadler committed
166 167
        "audio/x-raw, "
        "format = (string) { U8, S16BE, S16LE, S24BE, S24LE, S32BE, S32LE, F32LE, F64LE }, "
168
        "layout = (string) interleaved, "
169
        COMMON_AUDIO_CAPS ";"
170
        "audio/x-tta, "
171
        "width = (int) { 8, 16, 24 }, "
172 173
        "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
        "audio/x-pn-realaudio, "
174 175 176
        "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
        "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
        "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
177 178 179 180
        COMMON_AUDIO_CAPS ";"
        "audio/x-alaw, "
        "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
        "audio/x-mulaw, "
181 182 183
        "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
        "audio/x-adpcm, "
        "layout = (string)dvi, "
184
        "block_align = (int)[64, 8192], "
185 186 187
        "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
        "audio/x-adpcm, "
        "layout = (string)g726, " "channels = (int)1," "rate = (int)8000; ")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
188
    );
David Schleef's avatar
David Schleef committed
189 190

static GstStaticPadTemplate subtitlesink_templ =
191
    GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
192 193
    GST_PAD_SINK,
    GST_PAD_REQUEST,
194
    GST_STATIC_CAPS ("subtitle/x-kate; "
195
        "text/x-raw, format=utf8; application/x-ssa; application/x-ass; "
196
        "application/x-usf; subpicture/x-dvd; "
197 198
        "application/x-subtitle-unknown")
    );
199

200
static gpointer parent_class;   /* NULL */
201 202

/* Matroska muxer destructor */
203 204
static void gst_matroska_mux_class_init (GstMatroskaMuxClass * klass);
static void gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class);
205
static void gst_matroska_mux_finalize (GObject * object);
206

207
/* Pads collected callback */
208 209 210 211
static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads * pads,
    GstCollectData * data, GstBuffer * buf, gpointer user_data);
static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
    GstCollectData * data, GstEvent * event, gpointer user_data);
212 213

/* pad functions */
214
static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
René Stadler's avatar
René Stadler committed
215
    GstObject * parent, GstEvent * event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
216
static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
René Stadler's avatar
René Stadler committed
217
    GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
218
static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
219 220

/* gst internal change state handler */
221 222
static GstStateChangeReturn
gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
223 224

/* gobject bla bla */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
225 226 227 228
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);
229 230

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

233
/* uid generation */
234
static guint64 gst_matroska_mux_create_uid (GstMatroskaMux * mux);
235

236 237 238 239
static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
    GstMatroskaTrackContext * context);
static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
    GstMatroskaTrackContext * context);
240 241
static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
    GstMatroskaTrackContext * context);
242 243
static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
    GstMatroskaTrackContext * context);
244 245
static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
    GstMatroskaTrackContext * context);
246 247 248
static void
gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
    gpointer data);
249

250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
/* Cannot use boilerplate macros here because we need the full init function
 * signature with the additional class argument, so we use the right template
 * for the sink caps */
GType
gst_matroska_mux_get_type (void)
{
  static GType object_type;     /* 0 */

  if (object_type == 0) {
    static const GTypeInfo object_info = {
      sizeof (GstMatroskaMuxClass),
      NULL,                     /* base_init */
      NULL,                     /* base_finalize */
      (GClassInitFunc) gst_matroska_mux_class_init,
      NULL,                     /* class_finalize */
      NULL,                     /* class_data */
      sizeof (GstMatroskaMux),
      0,                        /* n_preallocs */
      (GInstanceInitFunc) gst_matroska_mux_init
    };
    const GInterfaceInfo iface_info = { NULL };

    object_type = g_type_register_static (GST_TYPE_ELEMENT,
        "GstMatroskaMux", &object_info, (GTypeFlags) 0);

    g_type_add_interface_static (object_type, GST_TYPE_TAG_SETTER, &iface_info);
    g_type_add_interface_static (object_type, GST_TYPE_TOC_SETTER, &iface_info);
  }

  return object_type;
}

282 283 284 285 286
static void
gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;
287

288 289 290 291
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;

  gst_element_class_add_pad_template (gstelement_class,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
292
      gst_static_pad_template_get (&videosink_templ));
293
  gst_element_class_add_pad_template (gstelement_class,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
294
      gst_static_pad_template_get (&audiosink_templ));
295
  gst_element_class_add_pad_template (gstelement_class,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
296
      gst_static_pad_template_get (&subtitlesink_templ));
297
  gst_element_class_add_pad_template (gstelement_class,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
298
      gst_static_pad_template_get (&src_templ));
299
  gst_element_class_set_static_metadata (gstelement_class, "Matroska muxer",
300 301
      "Codec/Muxer",
      "Muxes video/audio/subtitle streams into a matroska stream",
302
      "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
303 304 305

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

307
  gobject_class->finalize = gst_matroska_mux_finalize;
308 309 310 311

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

312 313 314
  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.",
315
          NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
316 317
  g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
      g_param_spec_int ("version", "DocType version",
318
          "This parameter determines what Matroska features can be used.",
319 320
          1, 2, DEFAULT_DOCTYPE_VERSION,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
321 322 323
  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.",
324 325
          0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
326 327 328 329
  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.",
330 331
          DEFAULT_STREAMABLE,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
332

333 334 335 336 337 338
  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);
339 340

  parent_class = g_type_class_peek_parent (klass);
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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
441
static void
442
gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class)
443
{
444 445 446
  GstPadTemplate *templ;

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

450
  gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
451
  gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
Wim Taymans's avatar
Wim Taymans committed
452
  gst_pad_use_fixed_caps (mux->srcpad);
453

454 455 456 457
  mux->collect = gst_collect_pads_new ();
  gst_collect_pads_set_clip_function (mux->collect,
      GST_DEBUG_FUNCPTR (gst_collect_pads_clip_running_time), mux);
  gst_collect_pads_set_buffer_function (mux->collect,
458
      GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux);
459
  gst_collect_pads_set_event_function (mux->collect,
460
      GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux);
461

462
  mux->ebml_write = gst_ebml_write_new (mux->srcpad);
463
  mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
464

465
  /* property defaults */
466
  mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
467
  mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
468
  mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
469
  mux->streamable = DEFAULT_STREAMABLE;
470

471
  /* initialize internal variables */
472
  mux->index = NULL;
473 474 475 476
  mux->num_streams = 0;
  mux->num_a_streams = 0;
  mux->num_t_streams = 0;
  mux->num_v_streams = 0;
477

478 479 480
  /* create used uid list */
  mux->used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);

481
  /* initialize remaining variables */
482 483 484
  gst_matroska_mux_reset (GST_ELEMENT (mux));
}

485 486 487 488 489 490 491 492 493 494 495 496

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

497 498
  gst_event_replace (&mux->force_key_unit_event, NULL);

499 500
  gst_object_unref (mux->collect);
  gst_object_unref (mux->ebml_write);
501 502
  if (mux->writing_app)
    g_free (mux->writing_app);
503

504 505
  g_array_free (mux->used_uids, TRUE);

506 507 508 509 510 511
  G_OBJECT_CLASS (parent_class)->finalize (object);
}


/**
 * gst_matroska_mux_create_uid:
512
 * @mux: #GstMatroskaMux to generate UID for.
513 514 515 516 517
 *
 * Generate new unused track UID.
 *
 * Returns: New track UID.
 */
518
static guint64
519
gst_matroska_mux_create_uid (GstMatroskaMux * mux)
520
{
521
  guint64 uid = 0;
522

523 524 525
  while (!uid) {
    guint i;

526
    uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
527 528
    for (i = 0; i < mux->used_uids->len; i++) {
      if (g_array_index (mux->used_uids, guint64, i) == uid) {
529 530 531 532
        uid = 0;
        break;
      }
    }
533
    g_array_append_val (mux->used_uids, uid);
534
  }
535

536 537 538
  return uid;
}

539

540
/**
541
 * gst_matroska_pad_reset:
542 543
 * @collect_pad: the #GstMatroskaPad
 *
544
 * Reset and/or release resources of a matroska collect pad.
545 546
 */
static void
547
gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
548
{
549 550
  gchar *name = NULL;
  GstMatroskaTrackType type = 0;
551

552
  /* free track information */
553
  if (collect_pad->track != NULL) {
554 555 556 557 558 559 560 561 562 563 564 565 566
    /* 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;
      }
    }
567 568
    g_free (collect_pad->track->codec_id);
    g_free (collect_pad->track->codec_name);
569 570
    if (full)
      g_free (collect_pad->track->name);
571 572 573 574 575 576
    g_free (collect_pad->track->language);
    g_free (collect_pad->track->codec_priv);
    g_free (collect_pad->track);
    collect_pad->track = NULL;
  }

577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
  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 ();
596
        return;
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
    }

    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
617
gst_matroska_pad_free (GstPad * collect_pad)
618
{
619
  gst_matroska_pad_reset ((GstMatroskaPad *) collect_pad, TRUE);
620 621
}

622 623 624 625 626 627 628

/**
 * gst_matroska_mux_reset:
 * @element: #GstMatroskaMux that should be reseted.
 *
 * Reset matroska muxer back to initial state.
 */
629
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
630
gst_matroska_mux_reset (GstElement * element)
631 632
{
  GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
633 634 635 636
  GSList *walk;

  /* reset EBML write */
  gst_ebml_write_reset (mux->ebml_write);
637 638 639 640 641

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

  /* clean up existing streams */
642 643

  for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
644 645 646 647
    GstMatroskaPad *collect_pad;

    collect_pad = (GstMatroskaPad *) walk->data;

648 649
    /* reset collect pad to pristine state */
    gst_matroska_pad_reset (collect_pad, FALSE);
650
  }
651 652 653 654 655 656 657

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

  /* reset timers */
658
  mux->time_scale = GST_MSECOND;
659
  mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
660
  mux->duration = 0;
661

662 663 664 665
  /* reset cluster */
  mux->cluster = 0;
  mux->cluster_time = 0;
  mux->cluster_pos = 0;
666
  mux->prev_cluster_size = 0;
667

668
  /* reset tags */
669
  gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
670 671 672 673

  mux->tags_pos = 0;

  /* reset chapters */
674
  gst_toc_setter_reset (GST_TOC_SETTER (mux));
675 676

  mux->chapters_pos = 0;
677 678 679 680 681

  /* clear used uids */
  if (mux->used_uids->len > 0) {
    g_array_remove_range (mux->used_uids, 0, mux->used_uids->len);
  }
682 683
}

684 685 686 687 688
/**
 * gst_matroska_mux_handle_src_event:
 * @pad: Pad which received the event.
 * @event: Received event.
 *
689
 * handle events - copied from oggmux without understanding
690 691 692 693
 *
 * Returns: #TRUE on success.
 */
static gboolean
René Stadler's avatar
René Stadler committed
694 695
gst_matroska_mux_handle_src_event (GstPad * pad, GstObject * parent,
    GstEvent * event)
696 697 698 699 700 701 702 703 704 705 706 707 708
{
  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;
  }

René Stadler's avatar
René Stadler committed
709
  return gst_pad_event_default (pad, parent, event);
710 711
}

712 713 714 715 716 717 718 719 720 721 722

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

723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
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 */
750
  gst_matroska_mux_free_codec_priv (context);
751 752 753 754 755 756 757 758 759 760
  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]);
  }
}


761 762 763 764 765 766 767 768 769 770
/**
 * 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
771 772
gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
    GstCollectData * data, GstEvent * event, gpointer user_data)
773 774
{
  GstMatroskaPad *collect_pad;
775
  GstMatroskaTrackContext *context;
776
  GstMatroskaMux *mux;
777
  GstPad *pad;
778
  GstTagList *list;
779
  gboolean ret = TRUE;
780

781 782 783
  mux = GST_MATROSKA_MUX (user_data);
  collect_pad = (GstMatroskaPad *) data;
  pad = data->pad;
784 785
  context = collect_pad->track;
  g_assert (context);
786 787

  switch (GST_EVENT_TYPE (event)) {
René Stadler's avatar
René Stadler committed
788 789 790 791 792 793
    case GST_EVENT_CAPS:{
      GstCaps *caps;

      collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
      gst_event_parse_caps (event, &caps);

794
      ret = collect_pad->capsfunc (pad, caps);
René Stadler's avatar
René Stadler committed
795 796 797 798
      gst_event_unref (event);
      event = NULL;
      break;
    }
799 800 801
    case GST_EVENT_TAG:{
      gchar *lang = NULL;

802
      GST_DEBUG_OBJECT (mux, "received tag event");
803
      gst_event_parse_tag (event, &list);
804

805 806 807 808 809 810 811
      /* 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);
812
          g_free (context->language);
813 814 815 816 817 818
          context->language = g_strdup (lang_code);
        } else {
          GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
        }
        g_free (lang);
      }
819

820
      /* FIXME: what about stream-specific tags? */
821 822
      gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
          gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
823 824

      gst_event_unref (event);
825 826
      /* handled this, don't want collectpads to forward it downstream */
      event = NULL;
827
      ret = TRUE;
828
      break;
829
    }
830
    case GST_EVENT_TOC:{
831
      GstToc *toc, *old_toc;
832 833 834 835 836 837 838 839

      if (mux->chapters_pos > 0)
        break;

      GST_DEBUG_OBJECT (mux, "received toc event");
      gst_event_parse_toc (event, &toc, NULL);

      if (toc != NULL) {
840 841 842 843 844
        old_toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
        if (old_toc != NULL) {
          if (old_toc != toc)
            GST_INFO_OBJECT (pad, "Replacing TOC with a new one");
          gst_toc_unref (old_toc);
845 846 847
        }

        gst_toc_setter_set_toc (GST_TOC_SETTER (mux), toc);
848
        gst_toc_unref (toc);
849 850 851 852 853 854 855
      }

      gst_event_unref (event);
      /* handled this, don't want collectpads to forward it downstream */
      event = NULL;
      break;
    }
856 857
    case GST_EVENT_CUSTOM_DOWNSTREAM:
    case GST_EVENT_CUSTOM_DOWNSTREAM_STICKY:{
858 859 860 861 862 863 864
      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;
865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882
      } 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);
883
            goto break_hard;
884 885 886 887 888 889
          }
          clut[i] = value;
        }

        /* transform into private data for stream; text form */
        gst_matroska_mux_build_vobsub_private (context, clut);
890 891
      }
    }
892
      /* fall through */
893 894 895 896
    default:
      break;
  }

897
break_hard:
898
  if (event != NULL)
899
    return gst_collect_pads_event_default (pads, data, event, FALSE);
900

901
  return ret;
902 903
}

904 905 906 907 908 909 910 911 912
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);
}
913 914 915 916 917 918 919 920 921 922 923 924

/**
 * 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)
925 926 927
{
  GstMatroskaTrackContext *context = NULL;
  GstMatroskaTrackVideoContext *videocontext;
928
  GstMatroskaMux *mux;
929
  GstMatroskaPad *collect_pad;
930
  GstStructure *structure;
931
  const gchar *mimetype;
932
  const gchar *interlace_mode;
933
  const GValue *value = NULL;
René Stadler's avatar
René Stadler committed
934
  GstBuffer *codec_buf = NULL;
935
  gint width, height, pixel_width, pixel_height;
936
  gint fps_d, fps_n;
937

938 939
  mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));

940
  /* find context */
941 942 943 944
  collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
  g_assert (collect_pad);
  context = collect_pad->track;
  g_assert (context);
945 946 947 948
  g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
  videocontext = (GstMatroskaTrackVideoContext *) context;

  /* gst -> matroska ID'ing */
David Schleef's avatar
David Schleef committed
949 950 951 952
  structure = gst_caps_get_structure (caps, 0);

  mimetype = gst_structure_get_name (structure);

953 954
  interlace_mode = gst_structure_get_string (structure, "interlace-mode");
  if (interlace_mode != NULL && strcmp (interlace_mode, "progressive") != 0)
955 956
    context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;

957 958 959 960 961
  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
962
  /* get general properties */
963 964 965 966 967
  /* 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
968 969
  videocontext->pixel_width = width;
  videocontext->pixel_height = height;
970 971 972

  /* set vp8 defaults or let user override it */
  if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration_user == FALSE
973 974
      && (!strcmp (mimetype, "video/x-vp8")
          || !strcmp (mimetype, "video/x-vp9")))
975 976 977 978 979
    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)
980
      && fps_n > 0) {
981 982 983 984 985 986 987
    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;
  }
988 989
  if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
          &pixel_width, &pixel_height)) {
David Schleef's avatar
David Schleef committed
990 991 992 993 994 995
    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;
996 997 998 999
    } else {
      videocontext->display_width = 0;
      videocontext->display_height = 0;
    }
David Schleef's avatar
David Schleef committed
1000 1001 1002 1003
  } else {
    videocontext->display_width = 0;
    videocontext->display_height = 0;
  }
1004

1005 1006
skip_details:

David Schleef's avatar
David Schleef committed
1007 1008 1009
  videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
  videocontext->fourcc = 0;

1010 1011 1012 1013 1014
  /* TODO: - check if we handle all codecs by the spec, i.e. codec private
   *         data and other settings
   *       - add new formats
   */

1015 1016 1017
  /* extract codec_data, may turn out needed */
  value = gst_structure_get_value (structure, "codec_data");
  if (value)
René Stadler's avatar
René Stadler committed
1018
    codec_buf = (GstBuffer *) gst_value_get_buffer (value);
1019

David Schleef's avatar
David Schleef committed
1020
  /* find type */
René Stadler's avatar
René Stadler committed
1021 1022
  if (!strcmp (mimetype, "video/x-raw")) {
    const gchar *fstr;
1023 1024
    gst_matroska_mux_set_codec_id (context,
        GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
René Stadler's avatar
René Stadler committed
1025
    fstr = gst_structure_get_string (structure, "format");
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
    if (fstr) {
      if (strlen (fstr) == 4)
        videocontext->fourcc = GST_STR_FOURCC (fstr);
      else if (!strcmp (fstr, "GRAY8"))
        videocontext->fourcc = GST_MAKE_FOURCC ('Y', '8', '0', '0');
      else if (!strcmp (fstr, "BGR"))
        videocontext->fourcc = GST_MAKE_FOURCC ('B', 'G', 'R', 24);
      else if (!strcmp (fstr, "RGB"))
        videocontext->fourcc = GST_MAKE_FOURCC ('R', 'G', 'B', 24);
    }
1036 1037
  } else if (!strcmp (mimetype, "video/x-huffyuv")      /* MS/VfW compatibility cases */
      ||!strcmp (mimetype, "video/x-divx")
1038
      || !strcmp (mimetype, "video/x-dv")
1039
      || !strcmp (mimetype, "video/x-h263")
1040
      || !strcmp (mimetype, "video/x-msmpeg")
1041 1042
      || !strcmp (mimetype, "video/x-wmv")
      || !strcmp (mimetype, "image/jpeg")) {
Stefan Kost's avatar
Stefan Kost committed
1043 1044
    gst_riff_strf_vids *bih;
    gint size = sizeof (gst_riff_strf_vids);
1045
    guint32 fourcc = 0;
David Schleef's avatar
David Schleef committed
1046

1047
    if (!strcmp (mimetype, "video/x-huffyuv"))
1048
      fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
1049
    else if (!strcmp (mimetype, "video/x-dv"))
1050
      fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
1051
    else if (!strcmp (mimetype, "video/x-h263"))
1052
      fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
1053 1054 1055 1056 1057 1058
    else if (!strcmp (mimetype, "video/x-divx")) {
      gint divxversion;

      gst_structure_get_int (structure, "divxversion", &divxversion);
      switch (divxversion) {
        case 3:
1059
          fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
1060 1061
          break;
        case 4:
1062
          fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
1063 1064
          break;
        case 5:
1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
          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;
1081 1082
          break;
      }
1083 1084
    } else if (!strcmp (mimetype, "video/x-wmv")) {
      gint wmvversion;
René Stadler's avatar
René Stadler committed
1085 1086 1087 1088 1089
      const gchar *fstr;

      fstr = gst_structure_get_string (structure, "format");
      if (fstr && strlen (fstr) == 4) {
        fourcc = GST_STR_FOURCC (fstr);
1090 1091 1092 1093 1094 1095 1096 1097 1098
      } 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');
        }
      }
1099 1100
    } else if (!strcmp (mimetype, "image/jpeg")) {
      fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
David Schleef's avatar
David Schleef committed
1101
    }
1102

1103
    if (!fourcc)
1104
      goto refuse_caps;
1105

Stefan Kost's avatar
Stefan Kost committed
1106 1107 1108 1109 1110 1111 1112 1113
    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 *
1114 1115 1116 1117
        videocontext->pixel_height * 3);

    /* process codec private/initialization data, if any */
    if (codec_buf) {
René Stadler's avatar
René Stadler committed
1118
      size += gst_buffer_get_size (codec_buf);
1119
      bih = g_realloc (bih, size);
Stefan Kost's avatar
Stefan Kost committed
1120
      GST_WRITE_UINT32_LE (&bih->size, size);
René Stadler's avatar
René Stadler committed
1121 1122
      gst_buffer_extract (codec_buf, 0,
          (guint8 *) bih + sizeof (gst_riff_strf_vids), -1);
1123
    }
1124

1125 1126
    gst_matroska_mux_set_codec_id (context,
        GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1127
    gst_matroska_mux_free_codec_priv (context);
1128
    context->codec_priv = (gpointer) bih;
1129
    context->codec_priv_size = size;
1130
  } else if (!strcmp (mimetype, "video/x-h264")) {
1131 1132
    gst_matroska_mux_set_codec_id (context,
        GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1133
    gst_matroska_mux_free_codec_priv (context);
1134
    /* Create avcC header */
1135
    if (codec_buf != NULL) {
René Stadler's avatar
René Stadler committed
1136
      context->codec_priv_size = gst_buffer_get_size (codec_buf);
1137
      context->codec_priv = g_malloc0 (context->codec_priv_size);
René Stadler's avatar
René Stadler committed
1138
      gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1139
    }
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149
  } else if (!strcmp (mimetype, "video/x-h265")) {
    gst_matroska_mux_set_codec_id (context,
        GST_MATROSKA_CODEC_ID_VIDEO_MPEGH_HEVC);
    gst_matroska_mux_free_codec_priv (context);
    /* Create hvcC header */
    if (codec_buf != NULL) {
      context->codec_priv_size = gst_buffer_get_size (codec_buf);
      context->codec_priv = g_malloc0 (context->codec_priv_size);
      gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
    }
1150 1151 1152
  } else if (!strcmp (mimetype, "video/x-theora")) {
    const GValue *streamheader;

1153
    gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1154

1155
    gst_matroska_mux_free_codec_priv (context);