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