gst-indent
[platform/upstream/gst-plugins-good.git] / gst / matroska / matroska-mux.c
1 /* GStreamer Matroska muxer/demuxer
2  * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3  *
4  * matroska-mux.c: matroska file/stream muxer
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <math.h>
27 #include <string.h>
28
29 #include "matroska-mux.h"
30 #include "matroska-ids.h"
31
32 enum
33 {
34   /* FILL ME */
35   LAST_SIGNAL
36 };
37
38 enum
39 {
40   ARG_0,
41   ARG_METADATA,
42   /* FILL ME */
43 };
44
45 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
46     GST_PAD_SRC,
47     GST_PAD_ALWAYS,
48     GST_STATIC_CAPS ("video/x-matroska")
49     );
50
51 #define COMMON_VIDEO_CAPS \
52   "width = (int) [ 16, 4096 ], " \
53   "height = (int) [ 16, 4096 ], " \
54   "framerate = (double) [ 0, MAX ]"
55
56 static GstStaticPadTemplate videosink_templ =
57     GST_STATIC_PAD_TEMPLATE ("video_%d",
58     GST_PAD_SINK,
59     GST_PAD_REQUEST,
60     GST_STATIC_CAPS ("video/mpeg, "
61         "mpegversion = (int) { 1, 2, 4 }, "
62         "systemstream = (boolean) false, "
63         COMMON_VIDEO_CAPS "; "
64         "video/x-divx, "
65         COMMON_VIDEO_CAPS "; "
66         "video/x-xvid, "
67         COMMON_VIDEO_CAPS "; "
68         "video/x-msmpeg, "
69         COMMON_VIDEO_CAPS "; "
70         "video/x-jpeg, "
71         COMMON_VIDEO_CAPS "; "
72         "video/x-raw-yuv, "
73         "format = (fourcc) { YUY2, I420 }, " COMMON_VIDEO_CAPS)
74     );
75
76 #define COMMON_AUDIO_CAPS \
77   "channels = (int) [ 1, 8 ], " \
78   "rate = (int) [ 8000, 96000 ]"
79
80 /* FIXME:
81  * * audio/x-raw-float: endianness needs defining.
82  * * audio/x-vorbis: private data setup needs work.
83  */
84 static GstStaticPadTemplate audiosink_templ =
85     GST_STATIC_PAD_TEMPLATE ("audio_%d",
86     GST_PAD_SINK,
87     GST_PAD_REQUEST,
88     GST_STATIC_CAPS ("audio/mpeg, "
89         "mpegversion = (int) 1, "
90         "layer = (int) [ 1, 3 ], "
91         COMMON_AUDIO_CAPS "; "
92         "audio/mpeg, "
93         "mpegversion = (int) { 2, 4 }, "
94         COMMON_AUDIO_CAPS "; "
95         "audio/x-ac3, "
96         COMMON_AUDIO_CAPS "; "
97         "audio/x-raw-int, "
98         "width = (int) { 8, 16, 24 }, "
99         "depth = (int) { 8, 16, 24 }, "
100         "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
101         "signed = (boolean) { true, false }, " COMMON_AUDIO_CAPS)
102     );
103
104 static GstStaticPadTemplate subtitlesink_templ =
105 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
106     GST_PAD_SINK,
107     GST_PAD_REQUEST,
108     GST_STATIC_CAPS_ANY);
109
110 /* gobject magic foo */
111 static void gst_matroska_mux_base_init (GstMatroskaMuxClass * klass);
112 static void gst_matroska_mux_class_init (GstMatroskaMuxClass * klass);
113 static void gst_matroska_mux_init (GstMatroskaMux * mux);
114
115 /* element functions */
116 static void gst_matroska_mux_loop (GstElement * element);
117
118 /* pad functions */
119 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
120     GstPadTemplate * templ, const gchar * name);
121
122 /* gst internal change state handler */
123 static GstElementStateReturn
124 gst_matroska_mux_change_state (GstElement * element);
125
126 /* gobject bla bla */
127 static void gst_matroska_mux_set_property (GObject * object,
128     guint prop_id, const GValue * value, GParamSpec * pspec);
129 static void gst_matroska_mux_get_property (GObject * object,
130     guint prop_id, GValue * value, GParamSpec * pspec);
131
132 /* reset muxer */
133 static void gst_matroska_mux_reset (GstElement * element);
134
135 static GstEbmlWriteClass *parent_class = NULL;
136
137 /*static guint gst_matroska_mux_signals[LAST_SIGNAL] = { 0 };*/
138
139 GType
140 gst_matroska_mux_get_type (void)
141 {
142   static GType gst_matroska_mux_type = 0;
143
144   if (!gst_matroska_mux_type) {
145     static const GTypeInfo gst_matroska_mux_info = {
146       sizeof (GstMatroskaMuxClass),
147       (GBaseInitFunc) gst_matroska_mux_base_init,
148       NULL,
149       (GClassInitFunc) gst_matroska_mux_class_init,
150       NULL,
151       NULL,
152       sizeof (GstMatroskaMux),
153       0,
154       (GInstanceInitFunc) gst_matroska_mux_init,
155     };
156
157     gst_matroska_mux_type =
158         g_type_register_static (GST_TYPE_EBML_WRITE,
159         "GstMatroskaMmux", &gst_matroska_mux_info, 0);
160   }
161
162   return gst_matroska_mux_type;
163 }
164
165 static void
166 gst_matroska_mux_base_init (GstMatroskaMuxClass * klass)
167 {
168   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
169   static GstElementDetails gst_matroska_mux_details = {
170     "Matroska muxer",
171     "Codec/Muxer",
172     "Muxes video/audio/subtitle streams into a matroska stream",
173     "Ronald Bultje <rbultje@ronald.bitfreak.net>"
174   };
175
176   gst_element_class_add_pad_template (element_class,
177       gst_static_pad_template_get (&videosink_templ));
178   gst_element_class_add_pad_template (element_class,
179       gst_static_pad_template_get (&audiosink_templ));
180   gst_element_class_add_pad_template (element_class,
181       gst_static_pad_template_get (&subtitlesink_templ));
182   gst_element_class_add_pad_template (element_class,
183       gst_static_pad_template_get (&src_templ));
184   gst_element_class_set_details (element_class, &gst_matroska_mux_details);
185 }
186
187 static void
188 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
189 {
190   GObjectClass *gobject_class;
191   GstElementClass *gstelement_class;
192
193   gobject_class = (GObjectClass *) klass;
194   gstelement_class = (GstElementClass *) klass;
195
196   g_object_class_install_property (gobject_class, ARG_METADATA,
197       g_param_spec_boxed ("metadata", "Metadata", "Metadata",
198           GST_TYPE_CAPS, G_PARAM_READWRITE));
199
200   parent_class = g_type_class_ref (GST_TYPE_EBML_WRITE);
201
202   gobject_class->get_property = gst_matroska_mux_get_property;
203   gobject_class->set_property = gst_matroska_mux_set_property;
204
205   gstelement_class->change_state = gst_matroska_mux_change_state;
206   gstelement_class->request_new_pad = gst_matroska_mux_request_new_pad;
207 }
208
209 static void
210 gst_matroska_mux_init (GstMatroskaMux * mux)
211 {
212   GstElementClass *klass = GST_ELEMENT_GET_CLASS (mux);
213   gint i;
214
215   mux->srcpad =
216       gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
217           "src"), "src");
218   gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
219   GST_EBML_WRITE (mux)->srcpad = mux->srcpad;
220
221   gst_element_set_loop_function (GST_ELEMENT (mux), gst_matroska_mux_loop);
222
223   /* initial stream no. */
224   for (i = 0; i < GST_MATROSKA_MUX_MAX_STREAMS; i++) {
225     mux->sink[i].buffer = NULL;
226     mux->sink[i].track = NULL;
227   }
228   mux->index = NULL;
229
230   /* finish off */
231   gst_matroska_mux_reset (GST_ELEMENT (mux));
232 }
233
234 static void
235 gst_matroska_mux_reset (GstElement * element)
236 {
237   GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
238   guint i;
239
240   /* reset input */
241   mux->state = GST_MATROSKA_MUX_STATE_START;
242
243   /* clean up existing streams */
244   for (i = 0; i < GST_MATROSKA_MUX_MAX_STREAMS; i++) {
245     if (mux->sink[i].track != NULL) {
246       if (mux->sink[i].track->pad != NULL) {
247         gst_element_remove_pad (GST_ELEMENT (mux), mux->sink[i].track->pad);
248       }
249       g_free (mux->sink[i].track->codec_id);
250       g_free (mux->sink[i].track->codec_name);
251       g_free (mux->sink[i].track->name);
252       g_free (mux->sink[i].track->language);
253       g_free (mux->sink[i].track->codec_priv);
254       g_free (mux->sink[i].track);
255       mux->sink[i].track = NULL;
256     }
257     if (mux->sink[i].buffer != NULL) {
258       gst_buffer_unref (mux->sink[i].buffer);
259       mux->sink[i].buffer = NULL;
260     }
261     mux->sink[i].eos = FALSE;
262   }
263   mux->num_streams = 0;
264   mux->num_a_streams = 0;
265   mux->num_t_streams = 0;
266   mux->num_v_streams = 0;
267
268   /* reset media info  (to default) */
269   gst_caps_replace (&mux->metadata,
270       gst_caps_new_simple ("application/x-gst-metadata",
271           "application", G_TYPE_STRING, "", "date", G_TYPE_STRING, "", NULL));
272
273   /* reset indexes */
274   mux->num_indexes = 0;
275   g_free (mux->index);
276   mux->index = NULL;
277
278   /* reset timers */
279   mux->time_scale = 1000000;
280   mux->duration = 0;
281 }
282
283 static GstPadLinkReturn
284 gst_matroska_mux_video_pad_link (GstPad * pad, const GstCaps * caps)
285 {
286   GstMatroskaTrackContext *context = NULL;
287   GstMatroskaTrackVideoContext *videocontext;
288   GstMatroskaMux *mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
289   const gchar *mimetype;
290   gint width, height, pixel_width, pixel_height, i;
291   gdouble framerate;
292   GstStructure *structure;
293   gboolean ret;
294
295   /* find context */
296   for (i = 0; i < mux->num_streams; i++) {
297     if (mux->sink[i].track && mux->sink[i].track->pad &&
298         mux->sink[i].track->pad == pad) {
299       context = mux->sink[i].track;
300       break;
301     }
302   }
303   g_assert (i < mux->num_streams);
304   g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
305   videocontext = (GstMatroskaTrackVideoContext *) context;
306
307   /* gst -> matroska ID'ing */
308   structure = gst_caps_get_structure (caps, 0);
309
310   mimetype = gst_structure_get_name (structure);
311
312   /* get general properties */
313   gst_structure_get_int (structure, "width", &width);
314   gst_structure_get_int (structure, "height", &height);
315   gst_structure_get_double (structure, "framerate", &framerate);
316
317   videocontext->pixel_width = width;
318   videocontext->pixel_height = height;
319   context->default_duration = GST_SECOND / framerate;
320
321   ret = gst_structure_get_int (structure, "pixel_width", &pixel_width);
322   ret &= gst_structure_get_int (structure, "pixel_height", &pixel_height);
323   if (ret) {
324     if (pixel_width > pixel_height) {
325       videocontext->display_width = width * pixel_width / pixel_height;
326       videocontext->display_height = height;
327     } else if (pixel_width < pixel_height) {
328       videocontext->display_width = width;
329       videocontext->display_height = height * pixel_height / pixel_width;
330     } else {
331       videocontext->display_width = 0;
332       videocontext->display_height = 0;
333     }
334   } else {
335     videocontext->display_width = 0;
336     videocontext->display_height = 0;
337   }
338
339   videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
340   videocontext->eye_mode = GST_MATROSKA_EYE_MODE_MONO;
341   videocontext->fourcc = 0;
342
343   /* find type */
344   if (!strcmp (mimetype, "video/x-raw-yuv")) {
345     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
346     gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
347
348     return GST_PAD_LINK_OK;
349   } else if (!strcmp (mimetype, "video/x-jpeg")) {
350     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
351
352     return GST_PAD_LINK_OK;
353   } else if (!strcmp (mimetype, "video/x-divx")) {
354     gint divxversion;
355
356     gst_structure_get_int (structure, "divxversion", &divxversion);
357     switch (divxversion) {
358       case 3:
359         context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
360         break;
361       case 4:
362         context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_SP);
363         break;
364       case 5:
365         context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
366         break;
367     }
368
369     return GST_PAD_LINK_OK;
370   } else if (!strcmp (mimetype, "video/x-xvid")) {
371     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
372
373     return GST_PAD_LINK_OK;
374   } else if (!strcmp (mimetype, "video/mpeg")) {
375     gint mpegversion;
376
377     gst_structure_get_int (structure, "mpegversion", &mpegversion);
378     switch (mpegversion) {
379       case 1:
380         context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
381         break;
382       case 2:
383         context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
384         break;
385       case 3:
386         context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
387         break;
388     }
389
390     return GST_PAD_LINK_OK;
391   } else if (!strcmp (mimetype, "video/x-msmpeg")) {
392     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
393
394     return GST_PAD_LINK_OK;
395   }
396
397   return GST_PAD_LINK_REFUSED;
398 }
399
400 static GstPadLinkReturn
401 gst_matroska_mux_audio_pad_link (GstPad * pad, const GstCaps * caps)
402 {
403   GstMatroskaTrackContext *context = NULL;
404   GstMatroskaTrackAudioContext *audiocontext;
405   GstMatroskaMux *mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
406   const gchar *mimetype;
407   gint samplerate, channels, i;
408   GstStructure *structure;
409
410   /* find context */
411   for (i = 0; i < mux->num_streams; i++) {
412     if (mux->sink[i].track && mux->sink[i].track->pad &&
413         mux->sink[i].track->pad == pad) {
414       context = mux->sink[i].track;
415       break;
416     }
417   }
418   g_assert (i < mux->num_streams);
419   g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
420   audiocontext = (GstMatroskaTrackAudioContext *) context;
421
422   structure = gst_caps_get_structure (caps, 0);
423   mimetype = gst_structure_get_name (structure);
424
425   /* general setup */
426   gst_structure_get_int (structure, "rate", &samplerate);
427   gst_structure_get_int (structure, "channels", &channels);
428
429   audiocontext->samplerate = samplerate;
430   audiocontext->channels = channels;
431   audiocontext->bitdepth = 0;
432
433   if (!strcmp (mimetype, "audio/mpeg")) {
434     gint mpegversion = 0;
435
436     gst_structure_get_int (structure, "mpegversion", &mpegversion);
437     switch (mpegversion) {
438       case 1:{
439         gint layer;
440
441         gst_structure_get_int (structure, "layer", &layer);
442         switch (layer) {
443           case 1:
444             context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
445             break;
446           case 2:
447             context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
448             break;
449           case 3:
450             context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
451             break;
452         }
453         break;
454       }
455       case 2:
456         context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG2 "MAIN");
457         break;
458       case 4:
459         context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG4 "MAIN");
460         break;
461     }
462
463     return GST_PAD_LINK_OK;
464   } else if (!strcmp (mimetype, "audio/x-raw-int")) {
465     gint endianness, width, depth;
466     gboolean signedness;
467
468     gst_structure_get_int (structure, "endianness", &endianness);
469     gst_structure_get_int (structure, "width", &width);
470     gst_structure_get_int (structure, "depth", &depth);
471     gst_structure_get_int (structure, "signed", &signedness);
472     if (width != depth ||
473         (width == 8 && signedness) || (width == 16 && !signedness))
474       return GST_PAD_LINK_REFUSED;
475
476     audiocontext->bitdepth = depth;
477     if (endianness == G_BIG_ENDIAN)
478       context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
479     else
480       context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
481
482     return GST_PAD_LINK_OK;
483   } else if (!strcmp (mimetype, "audio/x-raw-float")) {
484     /* FIXME: endianness is undefined */
485   } else if (!strcmp (mimetype, "audio/x-vorbis")) {
486     /* FIXME: private data setup needs work */
487   } else if (!strcmp (mimetype, "audio/x-ac3")) {
488     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
489
490     return GST_PAD_LINK_OK;
491   }
492
493   return GST_PAD_LINK_REFUSED;
494 }
495
496 static GstPadLinkReturn
497 gst_matroska_mux_subtitle_pad_link (GstPad * pad, const GstCaps * caps)
498 {
499   /* Consider this as boilerplate code for now. There is
500    * no single subtitle creation element in GStreamer,
501    * neither do I know how subtitling works at all. */
502
503   return GST_PAD_LINK_REFUSED;
504 }
505
506 static GstPad *
507 gst_matroska_mux_request_new_pad (GstElement * element,
508     GstPadTemplate * templ, const gchar * pad_name)
509 {
510   GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
511   GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
512   GstPad *pad = NULL;
513   gchar *name = NULL;
514   GstPadLinkFunction linkfunc = NULL;
515   GstMatroskaTrackContext *context = NULL;
516
517   if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
518     name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
519     linkfunc = gst_matroska_mux_audio_pad_link;
520     context = (GstMatroskaTrackContext *)
521         g_new0 (GstMatroskaTrackAudioContext, 1);
522     context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
523     context->name = g_strdup ("Audio");
524   } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
525     name = g_strdup_printf ("video_%d", mux->num_v_streams++);
526     linkfunc = gst_matroska_mux_video_pad_link;
527     context = (GstMatroskaTrackContext *)
528         g_new0 (GstMatroskaTrackVideoContext, 1);
529     context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
530     context->name = g_strdup ("Video");
531   } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
532     name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
533     linkfunc = gst_matroska_mux_subtitle_pad_link;
534     context = (GstMatroskaTrackContext *)
535         g_new0 (GstMatroskaTrackSubtitleContext, 1);
536     context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
537     context->name = g_strdup ("Subtitle");
538   } else {
539     g_warning ("matroskamux: this is not our template!");
540     return NULL;
541   }
542
543   pad = gst_pad_new_from_template (templ, name);
544   g_free (name);
545   gst_element_add_pad (element, pad);
546   gst_pad_set_link_function (pad, linkfunc);
547   context->index = mux->num_streams++;
548   mux->sink[context->index].track = context;
549   context->pad = pad;
550   context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
551
552   return pad;
553 }
554
555 static void
556 gst_matroska_mux_track_header (GstMatroskaMux * mux,
557     GstMatroskaTrackContext * context)
558 {
559   GstEbmlWrite *ebml = GST_EBML_WRITE (mux);
560   guint64 master;
561
562   /* track type goes before the type-specific stuff */
563   gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
564   gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
565
566   /* type-specific stuff */
567   switch (context->type) {
568     case GST_MATROSKA_TRACK_TYPE_VIDEO:{
569       GstMatroskaTrackVideoContext *videocontext =
570           (GstMatroskaTrackVideoContext *) context;
571
572       /* framerate, but not in the video part */
573       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
574           context->default_duration);
575
576       master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
577       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
578           videocontext->pixel_width);
579       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
580           videocontext->pixel_height);
581       if (videocontext->display_width && videocontext->display_height) {
582         gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
583             videocontext->display_width);
584         gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
585             videocontext->display_height);
586       }
587       if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
588         gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
589       if (videocontext->fourcc) {
590         guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
591
592         gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
593             (gpointer) & fcc_le, 4);
594       }
595       gst_ebml_write_master_finish (ebml, master);
596
597       break;
598     }
599
600     case GST_MATROSKA_TRACK_TYPE_AUDIO:{
601       GstMatroskaTrackAudioContext *audiocontext =
602           (GstMatroskaTrackAudioContext *) context;
603
604       master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
605       if (audiocontext->samplerate != 8000)
606         gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
607             audiocontext->samplerate);
608       if (audiocontext->channels != 1)
609         gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
610             audiocontext->channels);
611       if (audiocontext->bitdepth) {
612         gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
613             audiocontext->bitdepth);
614       }
615       gst_ebml_write_master_finish (ebml, master);
616
617       break;
618     }
619
620     default:
621       /* doesn't need type-specific data */
622       break;
623   }
624
625   gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
626   if (context->codec_priv)
627     gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
628         context->codec_priv, context->codec_priv_size);
629   /* FIXME: until we have a nice way of getting the codecname
630    * out of the caps, I'm not going to enable this. Too much
631    * (useless, double, boring) work... */
632   /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
633      context->codec_name); */
634   gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
635 }
636
637 static void
638 gst_matroska_mux_start (GstMatroskaMux * mux)
639 {
640   GstEbmlWrite *ebml = GST_EBML_WRITE (mux);
641   guint32 seekhead_id[] = { GST_MATROSKA_ID_INFO,
642     GST_MATROSKA_ID_TRACKS,
643     GST_MATROSKA_ID_CUES,
644 #if 0
645     GST_MATROSKA_ID_TAGS,
646 #endif
647     0
648   };
649   guint64 master, child;
650   gint i;
651   guint tracknum = 1;
652
653   /* we start with a EBML header */
654   gst_ebml_write_header (ebml, "matroska", 1);
655
656   /* start a segment */
657   mux->segment_pos =
658       gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
659   mux->segment_master = ebml->pos;
660
661   /* the rest of the header is cached */
662   gst_ebml_write_set_cache (ebml, 0x1000);
663
664   /* seekhead (table of contents) - we set the positions later */
665   mux->seekhead_pos = ebml->pos;
666   master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
667   for (i = 0; seekhead_id[i] != 0; i++) {
668     child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
669     gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
670     gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
671     gst_ebml_write_master_finish (ebml, child);
672   }
673   gst_ebml_write_master_finish (ebml, master);
674
675   /* segment info */
676   mux->info_pos = ebml->pos;
677   master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_INFO);
678   gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
679   mux->duration_pos = ebml->pos;
680   gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION, 0);
681   gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP, "GStreamer");
682   if (mux->metadata &&
683       gst_structure_has_field (gst_caps_get_structure (mux->metadata, 0),
684           "application")) {
685     const gchar *app;
686
687     app = gst_structure_get_string (gst_caps_get_structure (mux->metadata, 0),
688         "application");
689     if (app && app[0]) {
690       gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, app);
691     }
692   }
693   /* FIXME: how do I get this? Automatic? Via tags? */
694   /*gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, 0); */
695   gst_ebml_write_master_finish (ebml, master);
696
697   /* tracks */
698   mux->tracks_pos = ebml->pos;
699   master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
700   for (i = 0; i < mux->num_streams; i++) {
701     if (GST_PAD_IS_USABLE (mux->sink[i].track->pad)) {
702       mux->sink[i].track->num = tracknum++;
703       child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
704       gst_matroska_mux_track_header (mux, mux->sink[i].track);
705       gst_ebml_write_master_finish (ebml, child);
706     }
707   }
708   gst_ebml_write_master_finish (ebml, master);
709
710   /* lastly, flush the cache */
711   gst_ebml_write_flush_cache (ebml);
712 }
713
714 static void
715 gst_matroska_mux_finish (GstMatroskaMux * mux)
716 {
717   GstEbmlWrite *ebml = GST_EBML_WRITE (mux);
718   guint64 pos;
719
720   /* cues */
721   if (mux->index != NULL) {
722     guint n;
723     guint64 master, pointentry_master, trackpos_master;
724
725     mux->cues_pos = ebml->pos;
726     gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
727     master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
728
729     for (n = 0; n < mux->num_indexes; n++) {
730       GstMatroskaIndex *idx = &mux->index[n];
731
732       pointentry_master = gst_ebml_write_master_start (ebml,
733           GST_MATROSKA_ID_POINTENTRY);
734       gst_ebml_write_date (ebml, GST_MATROSKA_ID_CUETIME,
735           idx->time / mux->time_scale);
736       trackpos_master = gst_ebml_write_master_start (ebml,
737           GST_MATROSKA_ID_CUETRACKPOSITION);
738       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
739       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
740           idx->pos - mux->segment_master);
741       gst_ebml_write_master_finish (ebml, trackpos_master);
742       gst_ebml_write_master_finish (ebml, pointentry_master);
743     }
744
745     gst_ebml_write_master_finish (ebml, master);
746     gst_ebml_write_flush_cache (ebml);
747   }
748
749   /* FIXME: tags */
750
751   /* update seekhead. We know that:
752    * - a seekhead contains 4 entries.
753    * - order of entries is as above.
754    * - a seekhead has a 4-byte header + 8-byte length
755    * - each entry is 2-byte master, 2-byte ID pointer,
756    *     2-byte length pointer, all 8/1-byte length, 4-
757    *     byte ID and 8-byte length pointer, where the
758    *     length pointer starts at 20.
759    * - all entries are local to the segment (so pos - segment_master).
760    * - so each entry is at 12 + 20 + num * 28. */
761   gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
762       mux->info_pos - mux->segment_master);
763   gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
764       mux->tracks_pos - mux->segment_master);
765   if (mux->index != NULL) {
766     gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
767         mux->cues_pos - mux->segment_master);
768   } else {
769     /* void'ify */
770     guint64 my_pos = ebml->pos;
771
772     gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
773     gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
774     gst_ebml_write_seek (ebml, my_pos);
775   }
776 #if 0
777   gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
778       mux->tags_pos - mux->segment_master);
779 #endif
780
781   /* update duration */
782   pos = GST_EBML_WRITE (mux)->pos;
783   gst_ebml_write_seek (ebml, mux->duration_pos);
784   gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
785       mux->duration / mux->time_scale);
786   gst_ebml_write_seek (ebml, pos);
787
788   /* finish segment - this also writes element length */
789   gst_ebml_write_master_finish (ebml, mux->segment_pos);
790 }
791
792 static gint
793 gst_matroska_mux_prepare_data (GstMatroskaMux * mux)
794 {
795   gint i, first = -1;
796
797   for (i = 0; i < mux->num_streams; i++) {
798     while (!mux->sink[i].eos && !mux->sink[i].buffer &&
799         mux->sink[i].track->num > 0 &&
800         GST_PAD_IS_USABLE (mux->sink[i].track->pad)) {
801       GstData *data;
802
803       data = gst_pad_pull (mux->sink[i].track->pad);
804       if (GST_IS_EVENT (data)) {
805         if (GST_EVENT_TYPE (GST_EVENT (data)) == GST_EVENT_EOS)
806           mux->sink[i].eos = TRUE;
807         gst_event_unref (GST_EVENT (data));
808       } else {
809         mux->sink[i].buffer = GST_BUFFER (data);
810       }
811     }
812
813     if (mux->sink[i].buffer) {
814       if (first < 0 || GST_BUFFER_TIMESTAMP (mux->sink[i].buffer) <
815           GST_BUFFER_TIMESTAMP (mux->sink[first].buffer))
816         first = i;
817     }
818   }
819
820   return first;
821 }
822
823 static void
824 gst_matroska_mux_write_data (GstMatroskaMux * mux)
825 {
826   GstEbmlWrite *ebml = GST_EBML_WRITE (mux);
827   GstBuffer *buf, *hdr;
828   gint i;
829   guint64 cluster, blockgroup;
830
831   /* which stream-num to write from? */
832   if ((i = gst_matroska_mux_prepare_data (mux)) < 0) {
833     GstEvent *event = gst_event_new (GST_EVENT_EOS);
834
835     gst_matroska_mux_finish (mux);
836     gst_pad_push (mux->srcpad, GST_DATA (event));
837     gst_element_set_eos (GST_ELEMENT (mux));
838
839     return;
840   }
841
842   /* write data */
843   buf = mux->sink[i].buffer;
844   mux->sink[i].buffer = NULL;
845
846   /* We currently write an index entry for each keyframe in a
847    * video track. This can be largely improved, such as doing
848    * one for each keyframe or each second (for all-keyframe
849    * streams), only the *first* video track or the audio track
850    * if we have no video tracks. But that'll come later... */
851   if (mux->sink[i].track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
852       GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_KEY_UNIT)) {
853     GstMatroskaIndex *idx;
854
855     if (mux->num_indexes % 32 == 0) {
856       mux->index = g_renew (GstMatroskaIndex, mux->index,
857           mux->num_indexes + 32);
858     }
859     idx = &mux->index[mux->num_indexes++];
860
861     idx->pos = ebml->pos;
862     idx->time = GST_BUFFER_TIMESTAMP (buf);
863     idx->track = mux->sink[i].track->num;
864   }
865
866   /* write one cluster with one blockgroup with one block with
867    * one slice (*breath*).
868    * FIXME: lacing, multiple frames/cluster, etc. */
869   cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
870   gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
871       GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
872   blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
873   gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
874       GST_BUFFER_SIZE (buf) + 4);
875   hdr = gst_buffer_new_and_alloc (4);
876   /* track num - FIXME: what if num >= 0x80 (unlikely)? */
877   GST_BUFFER_DATA (hdr)[0] = mux->sink[i].track->num | 0x80;
878   /* time relative to clustertime - we don't use this yet */
879   *(guint16 *) & GST_BUFFER_DATA (hdr)[1] = GUINT16_TO_BE (0);
880   /* flags - no lacing (yet) */
881   GST_BUFFER_DATA (hdr)[3] = 0;
882   gst_ebml_write_buffer (ebml, hdr);
883   gst_ebml_write_buffer (ebml, buf);
884   gst_ebml_write_master_finish (ebml, blockgroup);
885   gst_ebml_write_master_finish (ebml, cluster);
886 }
887
888 static void
889 gst_matroska_mux_loop (GstElement * element)
890 {
891   GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
892
893   /* start with a header */
894   if (mux->state == GST_MATROSKA_MUX_STATE_START) {
895     mux->state = GST_MATROSKA_MUX_STATE_HEADER;
896     gst_matroska_mux_start (mux);
897     mux->state = GST_MATROSKA_MUX_STATE_DATA;
898   }
899
900   /* do one single buffer */
901   gst_matroska_mux_write_data (mux);
902 }
903
904 static GstElementStateReturn
905 gst_matroska_mux_change_state (GstElement * element)
906 {
907   GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
908
909   switch (GST_STATE_TRANSITION (element)) {
910     case GST_STATE_PAUSED_TO_READY:
911       gst_matroska_mux_reset (GST_ELEMENT (mux));
912       break;
913     default:
914       break;
915   }
916
917   if (((GstElementClass *) parent_class)->change_state)
918     return ((GstElementClass *) parent_class)->change_state (element);
919
920   return GST_STATE_SUCCESS;
921 }
922
923 static void
924 gst_matroska_mux_set_property (GObject * object,
925     guint prop_id, const GValue * value, GParamSpec * pspec)
926 {
927   GstMatroskaMux *mux;
928
929   g_return_if_fail (GST_IS_MATROSKA_MUX (object));
930   mux = GST_MATROSKA_MUX (object);
931
932   switch (prop_id) {
933     case ARG_METADATA:
934       gst_caps_replace (&mux->metadata, g_value_get_boxed (value));
935       break;
936     default:
937       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
938       break;
939   }
940 }
941
942 static void
943 gst_matroska_mux_get_property (GObject * object,
944     guint prop_id, GValue * value, GParamSpec * pspec)
945 {
946   GstMatroskaMux *mux;
947
948   g_return_if_fail (GST_IS_MATROSKA_MUX (object));
949   mux = GST_MATROSKA_MUX (object);
950
951   switch (prop_id) {
952     case ARG_METADATA:
953       g_value_set_boxed (value, mux->metadata);
954       break;
955     default:
956       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
957       break;
958   }
959 }
960
961 gboolean
962 gst_matroska_mux_plugin_init (GstPlugin * plugin)
963 {
964   return gst_element_register (plugin, "matroskamux",
965       GST_RANK_NONE, GST_TYPE_MATROSKA_MUX);
966 }