make %u in all request pad templates
[platform/upstream/gst-plugins-good.git] / gst / matroska / matroska-mux.c
index 89294cd..d48d56c 100644 (file)
@@ -46,6 +46,7 @@
 #endif
 
 #include <math.h>
+#include <stdio.h>
 #include <string.h>
 
 #include <gst/riff/riff-media.h>
@@ -61,11 +62,18 @@ enum
 {
   ARG_0,
   ARG_WRITING_APP,
-  ARG_MATROSKA_VERSION
+  ARG_DOCTYPE_VERSION,
+  ARG_MIN_INDEX_INTERVAL,
+  ARG_STREAMABLE
 };
 
-#define  DEFAULT_MATROSKA_VERSION        1
+#define  DEFAULT_DOCTYPE_VERSION         2
 #define  DEFAULT_WRITING_APP             "GStreamer Matroska muxer"
+#define  DEFAULT_MIN_INDEX_INTERVAL      0
+#define  DEFAULT_STREAMABLE              FALSE
+
+/* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
+#define WAVEFORMATEX_SIZE  (2 + sizeof (gst_riff_strf_auds))
 
 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
@@ -82,19 +90,19 @@ static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
   "width = (int) [ 16, 4096 ], " \
   "height = (int) [ 16, 4096 ] "
 
-/* FIXME: 
+/* FIXME:
  * * require codec data, etc as needed
  */
 
 static GstStaticPadTemplate videosink_templ =
-    GST_STATIC_PAD_TEMPLATE ("video_%d",
+    GST_STATIC_PAD_TEMPLATE ("video_%u",
     GST_PAD_SINK,
     GST_PAD_REQUEST,
     GST_STATIC_CAPS ("video/mpeg, "
         "mpegversion = (int) { 1, 2, 4 }, "
         "systemstream = (boolean) false, "
         COMMON_VIDEO_CAPS "; "
-        "video/x-h264, "
+        "video/x-h264, stream-format=avc, alignment=au, "
         COMMON_VIDEO_CAPS "; "
         "video/x-divx, "
         COMMON_VIDEO_CAPS "; "
@@ -116,6 +124,8 @@ static GstStaticPadTemplate videosink_templ =
         "video/x-pn-realvideo, "
         "rmversion = (int) [1, 4], "
         COMMON_VIDEO_CAPS "; "
+        "video/x-vp8, "
+        COMMON_VIDEO_CAPS "; "
         "video/x-raw-yuv, "
         "format = (fourcc) { YUY2, I420, YV12, UYVY, AYUV }, "
         COMMON_VIDEO_CAPS "; "
@@ -130,7 +140,7 @@ static GstStaticPadTemplate videosink_templ =
  * * require codec data, etc as needed
  */
 static GstStaticPadTemplate audiosink_templ =
-    GST_STATIC_PAD_TEMPLATE ("audio_%d",
+    GST_STATIC_PAD_TEMPLATE ("audio_%u",
     GST_PAD_SINK,
     GST_PAD_REQUEST,
     GST_STATIC_CAPS ("audio/mpeg, "
@@ -143,6 +153,10 @@ static GstStaticPadTemplate audiosink_templ =
         COMMON_AUDIO_CAPS "; "
         "audio/x-ac3, "
         COMMON_AUDIO_CAPS "; "
+        "audio/x-eac3, "
+        COMMON_AUDIO_CAPS "; "
+        "audio/x-dts, "
+        COMMON_AUDIO_CAPS "; "
         "audio/x-vorbis, "
         COMMON_AUDIO_CAPS "; "
         "audio/x-flac, "
@@ -183,14 +197,18 @@ static GstStaticPadTemplate audiosink_templ =
         "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
         "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
         "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
-        COMMON_AUDIO_CAPS)
+        COMMON_AUDIO_CAPS ";"
+        "audio/x-alaw, "
+        "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
+        "audio/x-mulaw, "
+        "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]")
     );
 
 static GstStaticPadTemplate subtitlesink_templ =
-GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
+GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
     GST_PAD_SINK,
     GST_PAD_REQUEST,
-    GST_STATIC_CAPS_ANY);
+    GST_STATIC_CAPS ("subtitle/x-kate"));
 
 static GArray *used_uids;
 G_LOCK_DEFINE_STATIC (used_uids);
@@ -240,6 +258,9 @@ static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
     GstMatroskaTrackContext * context);
 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
     GstMatroskaTrackContext * context);
+static void
+gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
+    gpointer data);
 
 static void
 gst_matroska_mux_add_interfaces (GType type)
@@ -252,33 +273,32 @@ gst_matroska_mux_add_interfaces (GType type)
 static void
 gst_matroska_mux_base_init (gpointer g_class)
 {
-  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+}
+
+static void
+gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
 
-  gst_element_class_add_pad_template (element_class,
+  gst_element_class_add_pad_template (gstelement_class,
       gst_static_pad_template_get (&videosink_templ));
-  gst_element_class_add_pad_template (element_class,
+  gst_element_class_add_pad_template (gstelement_class,
       gst_static_pad_template_get (&audiosink_templ));
-  gst_element_class_add_pad_template (element_class,
+  gst_element_class_add_pad_template (gstelement_class,
       gst_static_pad_template_get (&subtitlesink_templ));
-  gst_element_class_add_pad_template (element_class,
+  gst_element_class_add_pad_template (gstelement_class,
       gst_static_pad_template_get (&src_templ));
-  gst_element_class_set_details_simple (element_class, "Matroska muxer",
+  gst_element_class_set_details_simple (gstelement_class, "Matroska muxer",
       "Codec/Muxer",
       "Muxes video/audio/subtitle streams into a matroska stream",
       "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
 
   GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
       "Matroska muxer");
-}
-
-static void
-gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
-{
-  GObjectClass *gobject_class;
-  GstElementClass *gstelement_class;
-
-  gobject_class = (GObjectClass *) klass;
-  gstelement_class = (GstElementClass *) klass;
 
   gobject_class->finalize = gst_matroska_mux_finalize;
 
@@ -288,11 +308,23 @@ gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
   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.",
-          NULL, G_PARAM_READWRITE));
-  g_object_class_install_property (gobject_class, ARG_MATROSKA_VERSION,
-      g_param_spec_int ("version", "Matroska version",
-          "This parameter determines what matroska features can be used.",
-          1, 2, DEFAULT_MATROSKA_VERSION, G_PARAM_READWRITE));
+          NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
+      g_param_spec_int ("version", "DocType version",
+          "This parameter determines what Matroska features can be used.",
+          1, 2, DEFAULT_DOCTYPE_VERSION,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  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.",
+          0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  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.",
+          DEFAULT_STREAMABLE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
 
   gstelement_class->change_state =
       GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
@@ -313,7 +345,12 @@ gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
 static void
 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
 {
-  mux->srcpad = gst_pad_new_from_static_template (&src_templ, "src");
+  GstPadTemplate *templ;
+
+  templ =
+      gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
+  mux->srcpad = gst_pad_new_from_template (templ, "src");
+
   gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
   gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
 
@@ -323,10 +360,13 @@ gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
       mux);
 
   mux->ebml_write = gst_ebml_write_new (mux->srcpad);
+  mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
 
   /* property defaults */
-  mux->matroska_version = DEFAULT_MATROSKA_VERSION;
+  mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
   mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
+  mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
+  mux->streamable = DEFAULT_STREAMABLE;
 
   /* initialize internal variables */
   mux->index = NULL;
@@ -351,6 +391,8 @@ gst_matroska_mux_finalize (GObject * object)
 {
   GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
 
+  gst_event_replace (&mux->force_key_unit_event, NULL);
+
   gst_object_unref (mux->collect);
   gst_object_unref (mux->ebml_write);
   if (mux->writing_app)
@@ -457,7 +499,7 @@ gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
         break;
       default:
         g_assert_not_reached ();
-        break;
+        return;
     }
 
     context->type = type;
@@ -521,12 +563,14 @@ gst_matroska_mux_reset (GstElement * element)
 
   /* reset timers */
   mux->time_scale = GST_MSECOND;
+  mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
   mux->duration = 0;
 
   /* reset cluster */
   mux->cluster = 0;
   mux->cluster_time = 0;
   mux->cluster_pos = 0;
+  mux->prev_cluster_size = 0;
 
   /* reset tags */
   gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
@@ -537,7 +581,7 @@ gst_matroska_mux_reset (GstElement * element)
  * @pad: Pad which received the event.
  * @event: Received event.
  *
- * handle events - copied from oggmux without understanding 
+ * handle events - copied from oggmux without understanding
  *
  * Returns: #TRUE on success.
  */
@@ -579,7 +623,6 @@ gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
 
   mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
 
-  /* FIXME: aren't we either leaking events here or doing a wrong unref? */
   switch (GST_EVENT_TYPE (event)) {
     case GST_EVENT_TAG:{
       gchar *lang = NULL;
@@ -606,22 +649,46 @@ gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
         g_free (lang);
       }
 
+      /* FIXME: what about stream-specific tags? */
       gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
           gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
+
+      gst_event_unref (event);
+      /* handled this, don't want collectpads to forward it downstream */
+      event = NULL;
       break;
     }
-    case GST_EVENT_NEWSEGMENT:
-      /* We don't support NEWSEGMENT events */
-      ret = FALSE;
-      gst_event_unref (event);
+    case GST_EVENT_NEWSEGMENT:{
+      GstFormat format;
+
+      gst_event_parse_new_segment (event, NULL, NULL, &format, NULL, NULL,
+          NULL);
+      if (format != GST_FORMAT_TIME) {
+        ret = FALSE;
+        gst_event_unref (event);
+        event = NULL;
+      }
       break;
+    }
+    case GST_EVENT_CUSTOM_DOWNSTREAM:{
+      const GstStructure *structure;
+
+      structure = gst_event_get_structure (event);
+      if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
+        gst_event_replace (&mux->force_key_unit_event, NULL);
+        mux->force_key_unit_event = event;
+        event = NULL;
+      }
+      break;
+    }
     default:
       break;
   }
 
   /* now GstCollectPads can take care of the rest, e.g. EOS */
-  if (ret)
+  if (event)
     ret = mux->collect_event (pad, event);
+
   gst_object_unref (mux);
 
   return ret;
@@ -650,6 +717,7 @@ gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
   const GstBuffer *codec_buf = NULL;
   gint width, height, pixel_width, pixel_height;
   gint fps_d, fps_n;
+  gboolean interlaced = FALSE;
 
   mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
 
@@ -666,6 +734,10 @@ gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
 
   mimetype = gst_structure_get_name (structure);
 
+  if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
+      && interlaced)
+    context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
+
   if (!strcmp (mimetype, "video/x-theora")) {
     /* we'll extract the details later from the theora identification header */
     goto skip_details;
@@ -724,17 +796,16 @@ skip_details:
   if (!strcmp (mimetype, "video/x-raw-yuv")) {
     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
     gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
-  } else if (!strcmp (mimetype, "image/jpeg")) {
-    context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
   } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
       ||!strcmp (mimetype, "video/x-huffyuv")
       || !strcmp (mimetype, "video/x-divx")
       || !strcmp (mimetype, "video/x-dv")
       || !strcmp (mimetype, "video/x-h263")
       || !strcmp (mimetype, "video/x-msmpeg")
-      || !strcmp (mimetype, "video/x-wmv")) {
-    BITMAPINFOHEADER *bih;
-    gint size = sizeof (BITMAPINFOHEADER);
+      || !strcmp (mimetype, "video/x-wmv")
+      || !strcmp (mimetype, "image/jpeg")) {
+    gst_riff_strf_vids *bih;
+    gint size = sizeof (gst_riff_strf_vids);
     guint32 fourcc = 0;
 
     if (!strcmp (mimetype, "video/x-xvid"))
@@ -789,27 +860,29 @@ skip_details:
           fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
         }
       }
+    } else if (!strcmp (mimetype, "image/jpeg")) {
+      fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
     }
 
     if (!fourcc)
       goto refuse_caps;
 
-    bih = g_new0 (BITMAPINFOHEADER, 1);
-    GST_WRITE_UINT32_LE (&bih->bi_size, size);
-    GST_WRITE_UINT32_LE (&bih->bi_width, videocontext->pixel_width);
-    GST_WRITE_UINT32_LE (&bih->bi_height, videocontext->pixel_height);
-    GST_WRITE_UINT32_LE (&bih->bi_compression, fourcc);
-    GST_WRITE_UINT16_LE (&bih->bi_planes, (guint16) 1);
-    GST_WRITE_UINT16_LE (&bih->bi_bit_count, (guint16) 24);
-    GST_WRITE_UINT32_LE (&bih->bi_size_image, videocontext->pixel_width *
+    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 *
         videocontext->pixel_height * 3);
 
     /* process codec private/initialization data, if any */
     if (codec_buf) {
       size += GST_BUFFER_SIZE (codec_buf);
       bih = g_realloc (bih, size);
-      GST_WRITE_UINT32_LE (&bih->bi_size, size);
-      memcpy ((guint8 *) bih + sizeof (BITMAPINFOHEADER),
+      GST_WRITE_UINT32_LE (&bih->size, size);
+      memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
           GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
     }
 
@@ -851,6 +924,8 @@ skip_details:
     }
   } else if (!strcmp (mimetype, "video/x-dirac")) {
     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
+  } else if (!strcmp (mimetype, "video/x-vp8")) {
+    context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
   } else if (!strcmp (mimetype, "video/mpeg")) {
     gint mpegversion;
 
@@ -1270,10 +1345,10 @@ speex_streamheader_to_codecdata (const GValue * streamheader,
   return TRUE;
 }
 
-static gchar *
+static const gchar *
 aac_codec_data_to_codec_id (const GstBuffer * buf)
 {
-  gchar *result;
+  const gchar *result;
   gint profile;
 
   /* default to MAIN */
@@ -1535,6 +1610,10 @@ gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
     }
   } else if (!strcmp (mimetype, "audio/x-ac3")) {
     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
+  } else if (!strcmp (mimetype, "audio/x-eac3")) {
+    context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
+  } else if (!strcmp (mimetype, "audio/x-dts")) {
+    context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_DTS);
   } else if (!strcmp (mimetype, "audio/x-tta")) {
     gint width;
 
@@ -1580,41 +1659,61 @@ gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
       context->codec_priv_size = priv_data_size;
     }
 
-  } else if (!strcmp (mimetype, "audio/x-wma")) {
+  } else if (!strcmp (mimetype, "audio/x-wma")
+      || !strcmp (mimetype, "audio/x-alaw")
+      || !strcmp (mimetype, "audio/x-mulaw")) {
     guint8 *codec_priv;
     guint codec_priv_size;
-    guint16 format;
+    guint16 format = 0;
     gint block_align;
     gint bitrate;
-    gint wmaversion;
-    gint depth;
-
-    if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
-        || !gst_structure_get_int (structure, "block_align", &block_align)
-        || !gst_structure_get_int (structure, "bitrate", &bitrate)
-        || samplerate == 0 || channels == 0) {
-      GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
-          "channels/rate on WMA caps");
+
+    if (samplerate == 0 || channels == 0) {
+      GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
       goto refuse_caps;
     }
 
-    switch (wmaversion) {
-      case 1:
-        format = GST_RIFF_WAVE_FORMAT_WMAV1;
-        break;
-      case 2:
-        format = GST_RIFF_WAVE_FORMAT_WMAV2;
-        break;
-      case 3:
-        format = GST_RIFF_WAVE_FORMAT_WMAV3;
-        break;
-      default:
-        GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
+    if (!strcmp (mimetype, "audio/x-wma")) {
+      gint wmaversion;
+      gint depth;
+
+      if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
+          || !gst_structure_get_int (structure, "block_align", &block_align)
+          || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
+        GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
+            " on WMA caps");
         goto refuse_caps;
-    }
+      }
 
-    if (gst_structure_get_int (structure, "depth", &depth))
-      audiocontext->bitdepth = depth;
+      switch (wmaversion) {
+        case 1:
+          format = GST_RIFF_WAVE_FORMAT_WMAV1;
+          break;
+        case 2:
+          format = GST_RIFF_WAVE_FORMAT_WMAV2;
+          break;
+        case 3:
+          format = GST_RIFF_WAVE_FORMAT_WMAV3;
+          break;
+        default:
+          GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
+          goto refuse_caps;
+      }
+
+      if (gst_structure_get_int (structure, "depth", &depth))
+        audiocontext->bitdepth = depth;
+    } else if (!strcmp (mimetype, "audio/x-alaw")
+        || !strcmp (mimetype, "audio/x-mulaw")) {
+      audiocontext->bitdepth = 8;
+      if (!strcmp (mimetype, "audio/x-alaw"))
+        format = GST_RIFF_WAVE_FORMAT_ALAW;
+      else
+        format = GST_RIFF_WAVE_FORMAT_MULAW;
+
+      block_align = channels;
+      bitrate = block_align * samplerate;
+    }
+    g_assert (format != 0);
 
     codec_priv_size = WAVEFORMATEX_SIZE;
     if (buf)
@@ -1740,32 +1839,55 @@ gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
  */
 static GstPad *
 gst_matroska_mux_request_new_pad (GstElement * element,
-    GstPadTemplate * templ, const gchar * pad_name)
+    GstPadTemplate * templ, const gchar * req_name)
 {
   GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
   GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
   GstMatroskaPad *collect_pad;
   GstPad *newpad = NULL;
   gchar *name = NULL;
+  const gchar *pad_name = NULL;
   GstPadSetCapsFunction setcapsfunc = NULL;
   GstMatroskaTrackContext *context = NULL;
+  gint pad_id;
 
-  if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
-    name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
+  if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
+    /* don't mix named and unnamed pads, if the pad already exists we fail when
+     * trying to add it */
+    if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
+      pad_name = req_name;
+    } else {
+      name = g_strdup_printf ("audio_%u", mux->num_a_streams++);
+      pad_name = name;
+    }
     setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
     context = (GstMatroskaTrackContext *)
         g_new0 (GstMatroskaTrackAudioContext, 1);
     context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
     context->name = g_strdup ("Audio");
-  } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
-    name = g_strdup_printf ("video_%d", mux->num_v_streams++);
+  } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
+    /* don't mix named and unnamed pads, if the pad already exists we fail when
+     * trying to add it */
+    if (req_name != NULL && sscanf (req_name, "video_%u", &pad_id) == 1) {
+      pad_name = req_name;
+    } else {
+      name = g_strdup_printf ("video_%u", mux->num_v_streams++);
+      pad_name = name;
+    }
     setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
     context = (GstMatroskaTrackContext *)
         g_new0 (GstMatroskaTrackVideoContext, 1);
     context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
     context->name = g_strdup ("Video");
-  } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
-    name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
+  } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%u")) {
+    /* don't mix named and unnamed pads, if the pad already exists we fail when
+     * trying to add it */
+    if (req_name != NULL && sscanf (req_name, "subtitle_%u", &pad_id) == 1) {
+      pad_name = req_name;
+    } else {
+      name = g_strdup_printf ("subtitle_%u", mux->num_t_streams++);
+      pad_name = name;
+    }
     setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
     context = (GstMatroskaTrackContext *)
         g_new0 (GstMatroskaTrackSubtitleContext, 1);
@@ -1776,7 +1898,7 @@ gst_matroska_mux_request_new_pad (GstElement * element,
     return NULL;
   }
 
-  newpad = gst_pad_new_from_template (templ, name);
+  newpad = gst_pad_new_from_template (templ, pad_name);
   g_free (name);
   collect_pad = (GstMatroskaPad *)
       gst_collect_pads_add_pad_full (mux->collect, newpad,
@@ -1800,10 +1922,22 @@ gst_matroska_mux_request_new_pad (GstElement * element,
 
   gst_pad_set_setcaps_function (newpad, setcapsfunc);
   gst_pad_set_active (newpad, TRUE);
-  gst_element_add_pad (element, newpad);
+  if (!gst_element_add_pad (element, newpad))
+    goto pad_add_failed;
+
   mux->num_streams++;
 
+  GST_DEBUG_OBJECT (newpad, "Added new request pad");
+
   return newpad;
+
+  /* ERROR cases */
+pad_add_failed:
+  {
+    GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
+    gst_object_unref (newpad);
+    return NULL;
+  }
 }
 
 /**
@@ -1959,6 +2093,7 @@ static void
 gst_matroska_mux_start (GstMatroskaMux * mux)
 {
   GstEbmlWrite *ebml = mux->ebml_write;
+  const gchar *doctype;
   guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
     GST_MATROSKA_ID_TRACKS,
     GST_MATROSKA_ID_CUES,
@@ -1973,27 +2108,58 @@ gst_matroska_mux_start (GstMatroskaMux * mux)
   guint32 segment_uid[4];
   GTimeVal time = { 0, 0 };
 
+  if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
+    ebml->caps = gst_caps_new_simple ("video/webm", NULL);
+  } else {
+    ebml->caps = gst_caps_new_simple ("video/x-matroska", NULL);
+  }
   /* we start with a EBML header */
-  gst_ebml_write_header (ebml, "matroska", mux->matroska_version);
+  doctype = mux->doctype;
+  GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
+      doctype, mux->doctype_version);
+  gst_ebml_write_header (ebml, doctype, mux->doctype_version);
+
+  /* the rest of the header is cached */
+  gst_ebml_write_set_cache (ebml, 0x1000);
 
   /* start a segment */
   mux->segment_pos =
       gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
   mux->segment_master = ebml->pos;
 
-  /* the rest of the header is cached */
-  gst_ebml_write_set_cache (ebml, 0x1000);
+  if (!mux->streamable) {
+    /* seekhead (table of contents) - we set the positions later */
+    mux->seekhead_pos = ebml->pos;
+    master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
+    for (i = 0; seekhead_id[i] != 0; i++) {
+      child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
+      gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
+      gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
+      gst_ebml_write_master_finish (ebml, child);
+    }
+    gst_ebml_write_master_finish (ebml, master);
+  }
+
+  if (mux->streamable) {
+    const GstTagList *tags;
+
+    /* tags */
+    tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
+
+    if (tags != NULL && !gst_tag_list_is_empty (tags)) {
+      guint64 master_tags, master_tag;
 
-  /* seekhead (table of contents) - we set the positions later */
-  mux->seekhead_pos = ebml->pos;
-  master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
-  for (i = 0; seekhead_id[i] != 0; i++) {
-    child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
-    gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
-    gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
-    gst_ebml_write_master_finish (ebml, child);
+      GST_DEBUG ("Writing tags");
+
+      /* TODO: maybe limit via the TARGETS id by looking at the source pad */
+      mux->tags_pos = ebml->pos;
+      master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
+      master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
+      gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
+      gst_ebml_write_master_finish (ebml, master_tag);
+      gst_ebml_write_master_finish (ebml, master_tags);
+    }
   }
-  gst_ebml_write_master_finish (ebml, master);
 
   /* segment info */
   mux->info_pos = ebml->pos;
@@ -2006,30 +2172,31 @@ gst_matroska_mux_start (GstMatroskaMux * mux)
   gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
   mux->duration_pos = ebml->pos;
   /* get duration */
-  for (collected = mux->collect->data; collected;
-      collected = g_slist_next (collected)) {
-    GstMatroskaPad *collect_pad;
-    GstFormat format = GST_FORMAT_TIME;
-    GstPad *thepad;
-    gint64 trackduration;
-
-    collect_pad = (GstMatroskaPad *) collected->data;
-    thepad = collect_pad->collect.pad;
-
-    /* Query the total length of the track. */
-    GST_DEBUG_OBJECT (thepad, "querying peer duration");
-    if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
-      GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
-          GST_TIME_ARGS (trackduration));
-      if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
-        duration = (GstClockTime) trackduration;
+  if (!mux->streamable) {
+    for (collected = mux->collect->data; collected;
+        collected = g_slist_next (collected)) {
+      GstMatroskaPad *collect_pad;
+      GstFormat format = GST_FORMAT_TIME;
+      GstPad *thepad;
+      gint64 trackduration;
+
+      collect_pad = (GstMatroskaPad *) collected->data;
+      thepad = collect_pad->collect.pad;
+
+      /* Query the total length of the track. */
+      GST_DEBUG_OBJECT (thepad, "querying peer duration");
+      if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
+        GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (trackduration));
+        if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
+          duration = (GstClockTime) trackduration;
+        }
       }
     }
+    gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
+        gst_guint64_to_gdouble (duration) /
+        gst_guint64_to_gdouble (mux->time_scale));
   }
-  gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
-      gst_guint64_to_gdouble (duration) /
-      gst_guint64_to_gdouble (mux->time_scale));
-
   gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
       "GStreamer plugin version " PACKAGE_VERSION);
   if (mux->writing_app && mux->writing_app[0]) {
@@ -2057,12 +2224,16 @@ gst_matroska_mux_start (GstMatroskaMux * mux)
       child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
       gst_matroska_mux_track_header (mux, collect_pad->track);
       gst_ebml_write_master_finish (ebml, child);
+      /* some remaing pad/track setup */
+      collect_pad->default_duration_scaled =
+          gst_util_uint64_scale (collect_pad->track->default_duration,
+          1, mux->time_scale);
     }
   }
   gst_ebml_write_master_finish (ebml, master);
 
   /* lastly, flush the cache */
-  gst_ebml_write_flush_cache (ebml);
+  gst_ebml_write_flush_cache (ebml, FALSE, 0);
 }
 
 static void
@@ -2070,15 +2241,15 @@ gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
     gpointer data)
 {
   /* TODO: more sensible tag mappings */
-  struct
+  static const struct
   {
-    gchar *matroska_tagname;
-    gchar *gstreamer_tagname;
+    const gchar *matroska_tagname;
+    const gchar *gstreamer_tagname;
   }
   tag_conv[] = {
     {
     GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
-    GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
+    GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
     GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
     GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
     GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
@@ -2171,7 +2342,7 @@ gst_matroska_mux_finish (GstMatroskaMux * mux)
     }
 
     gst_ebml_write_master_finish (ebml, master);
-    gst_ebml_write_flush_cache (ebml);
+    gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
   }
 
   /* tags */
@@ -2239,7 +2410,8 @@ gst_matroska_mux_finish (GstMatroskaMux * mux)
 
     collect_pad = (GstMatroskaPad *) collected->data;
 
-    GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
+    GST_DEBUG_OBJECT (mux,
+        "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
         " end ts %" GST_TIME_FORMAT, collect_pad,
         GST_TIME_ARGS (collect_pad->start_ts),
         GST_TIME_ARGS (collect_pad->end_ts));
@@ -2250,7 +2422,8 @@ gst_matroska_mux_finish (GstMatroskaMux * mux)
           GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
       if (collect_pad->duration < min_duration)
         collect_pad->duration = min_duration;
-      GST_DEBUG_OBJECT (collect_pad, "final track duration: %" GST_TIME_FORMAT,
+      GST_DEBUG_OBJECT (collect_pad,
+          "final track duration: %" GST_TIME_FORMAT,
           GST_TIME_ARGS (collect_pad->duration));
     }
 
@@ -2275,7 +2448,7 @@ gst_matroska_mux_finish (GstMatroskaMux * mux)
     gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
     gst_ebml_write_seek (ebml, my_pos);
   }
-
+  GST_DEBUG_OBJECT (mux, "finishing segment");
   /* finish segment - this also writes element length */
   gst_ebml_write_master_finish (ebml, mux->segment_pos);
 }
@@ -2286,7 +2459,7 @@ gst_matroska_mux_finish (GstMatroskaMux * mux)
  * @mux: #GstMatroskaMux
  * @popped: True if at least one buffer was popped from #GstCollectPads
  *
- * Find a pad with the oldest data 
+ * Find a pad with the oldest data
  * (data from this pad should be written first).
  *
  * Returns: Selected pad.
@@ -2308,8 +2481,33 @@ gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
       collect_pad->buffer = gst_collect_pads_pop (mux->collect,
           (GstCollectData *) collect_pad);
 
-      if (collect_pad->buffer != NULL)
+      if (collect_pad->buffer != NULL) {
+        GstClockTime time;
+
         *popped = TRUE;
+        /* convert to running time */
+        time = GST_BUFFER_TIMESTAMP (collect_pad->buffer);
+        /* invalid should pass */
+        if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) {
+          time = gst_segment_to_running_time (&collect_pad->collect.segment,
+              GST_FORMAT_TIME, time);
+          if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
+            GST_DEBUG_OBJECT (mux, "clipping buffer on pad %s outside segment",
+                GST_PAD_NAME (collect_pad->collect.pad));
+            gst_buffer_unref (collect_pad->buffer);
+            collect_pad->buffer = NULL;
+            return NULL;
+          } else {
+            GST_LOG_OBJECT (mux, "buffer ts %" GST_TIME_FORMAT " -> %"
+                GST_TIME_FORMAT " running time",
+                GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (collect_pad->buffer)),
+                GST_TIME_ARGS (time));
+            collect_pad->buffer =
+                gst_buffer_make_metadata_writable (collect_pad->buffer);
+            GST_BUFFER_TIMESTAMP (collect_pad->buffer) = time;
+          }
+        }
+      }
     }
 
     /* if we have a buffer check if it is better then the current best one */
@@ -2333,10 +2531,10 @@ gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
  * @flags: Buffer flags.
  *
  * Create a buffer containing buffer header.
- * 
+ *
  * Returns: New buffer.
  */
-GstBuffer *
+static GstBuffer *
 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
     gint16 relative_timestamp, int flags)
 {
@@ -2397,7 +2595,7 @@ gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
 
     next_parse_offset = GST_READ_UINT32_BE (data + 5);
 
-    if (G_UNLIKELY (next_parse_offset == 0))
+    if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
       break;
 
     data += next_parse_offset;
@@ -2424,6 +2622,36 @@ gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
   return ret;
 }
 
+static void
+gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
+{
+  GstCaps *caps;
+  GstStructure *s;
+  GValue streamheader = { 0 };
+  GValue bufval = { 0 };
+  GstBuffer *streamheader_buffer;
+  GstEbmlWrite *ebml = mux->ebml_write;
+
+  streamheader_buffer = gst_ebml_stop_streamheader (ebml);
+  if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
+    caps = gst_caps_new_simple ("video/webm", NULL);
+  } else {
+    caps = gst_caps_new_simple ("video/x-matroska", NULL);
+  }
+  s = gst_caps_get_structure (caps, 0);
+  g_value_init (&streamheader, GST_TYPE_ARRAY);
+  g_value_init (&bufval, GST_TYPE_BUFFER);
+  GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_IN_CAPS);
+  gst_value_set_buffer (&bufval, streamheader_buffer);
+  gst_value_array_append_value (&streamheader, &bufval);
+  g_value_unset (&bufval);
+  gst_structure_set_value (s, "streamheader", &streamheader);
+  g_value_unset (&streamheader);
+  gst_caps_replace (&ebml->caps, caps);
+  gst_buffer_unref (streamheader_buffer);
+  gst_caps_unref (caps);
+}
+
 /**
  * gst_matroska_mux_write_data:
  * @mux: #GstMatroskaMux
@@ -2488,25 +2716,45 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
   }
 
   if (mux->cluster) {
-    /* start a new cluster every two seconds or at keyframe */
-    if (mux->cluster_time + GST_SECOND * 2 < GST_BUFFER_TIMESTAMP (buf)
-        || is_video_keyframe) {
+    /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
+     * or when we may be reaching the limit of the relative timestamp */
+    if (mux->cluster_time +
+        mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
+        || is_video_keyframe || mux->force_key_unit_event) {
+      if (!mux->streamable)
+        gst_ebml_write_master_finish (ebml, mux->cluster);
+
+      /* Forward the GstForceKeyUnit event after finishing the cluster */
+      if (mux->force_key_unit_event) {
+        gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
+        mux->force_key_unit_event = NULL;
+      }
 
-      gst_ebml_write_master_finish (ebml, mux->cluster);
+      mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
       mux->cluster_pos = ebml->pos;
+      gst_ebml_write_set_cache (ebml, 0x20);
       mux->cluster =
           gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
-          GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
+          gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
+              mux->time_scale));
+      GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
+          gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
+              mux->time_scale));
+      gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
       mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
+      gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
+          mux->prev_cluster_size);
     }
   } else {
     /* first cluster */
 
     mux->cluster_pos = ebml->pos;
+    gst_ebml_write_set_cache (ebml, 0x20);
     mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
     gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
-        GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
+        gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
+    gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
     mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
   }
 
@@ -2514,90 +2762,103 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
   if (GST_BUFFER_DURATION_IS_VALID (buf))
     collect_pad->duration += GST_BUFFER_DURATION (buf);
 
-  /* We currently write an index entry for each keyframe in a
-   * video track or one entry for each cluster in an audio track
-   * for audio only files. This can be largely improved, such as doing
-   * one for each keyframe or each second (for all-keyframe
-   * streams), only the *first* video track. But that'll come later... */
+  /* We currently write index entries for all video tracks or for the audio
+   * track in a single-track audio file.  This could be improved by keeping the
+   * index only for the *first* video track. */
 
   /* TODO: index is useful for every track, should contain the number of
-   * the block in the cluster which contains the timestamp
+   * the block in the cluster which contains the timestamp, should also work
+   * for files with multiple audio tracks.
    */
-  if (is_video_keyframe) {
-    GstMatroskaIndex *idx;
-
-    if (mux->num_indexes % 32 == 0) {
-      mux->index = g_renew (GstMatroskaIndex, mux->index,
-          mux->num_indexes + 32);
+  if (!mux->streamable &&
+      (is_video_keyframe ||
+          ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
+              (mux->num_streams == 1)))) {
+    gint last_idx = -1;
+
+    if (mux->min_index_interval != 0) {
+      for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
+        if (mux->index[last_idx].track == collect_pad->track->num)
+          break;
+      }
     }
-    idx = &mux->index[mux->num_indexes++];
 
-    idx->pos = mux->cluster_pos;
-    idx->time = GST_BUFFER_TIMESTAMP (buf);
-    idx->track = collect_pad->track->num;
-  } else if ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
-      (mux->num_streams == 1)) {
-    GstMatroskaIndex *idx;
+    if (last_idx < 0 || mux->min_index_interval == 0 ||
+        (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
+            >= mux->min_index_interval)) {
+      GstMatroskaIndex *idx;
 
-    if (mux->num_indexes % 32 == 0) {
-      mux->index = g_renew (GstMatroskaIndex, mux->index,
-          mux->num_indexes + 32);
-    }
-    idx = &mux->index[mux->num_indexes++];
+      if (mux->num_indexes % 32 == 0) {
+        mux->index = g_renew (GstMatroskaIndex, mux->index,
+            mux->num_indexes + 32);
+      }
+      idx = &mux->index[mux->num_indexes++];
 
-    idx->pos = mux->cluster_pos;
-    idx->time = GST_BUFFER_TIMESTAMP (buf);
-    idx->track = collect_pad->track->num;
+      idx->pos = mux->cluster_pos;
+      idx->time = GST_BUFFER_TIMESTAMP (buf);
+      idx->track = collect_pad->track->num;
+    }
   }
 
   /* Check if the duration differs from the default duration. */
   write_duration = FALSE;
-  block_duration = GST_BUFFER_DURATION (buf);
+  block_duration = 0;
   if (GST_BUFFER_DURATION_IS_VALID (buf)) {
-    if (block_duration != collect_pad->track->default_duration) {
+    block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
+        1, mux->time_scale);
+
+    /* small difference should be ok. */
+    if (block_duration > collect_pad->default_duration_scaled + 1 ||
+        block_duration < collect_pad->default_duration_scaled - 1) {
       write_duration = TRUE;
     }
   }
 
-  /* write the block, for matroska v2 use SimpleBlock if possible
+  /* write the block, for doctype v2 use SimpleBlock if possible
    * one slice (*breath*).
    * FIXME: Need to do correct lacing! */
   relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
   if (relative_timestamp64 >= 0) {
     /* round the timestamp */
-    relative_timestamp64 += mux->time_scale / 2;
+    relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
   } else {
     /* round the timestamp */
-    relative_timestamp64 -= mux->time_scale / 2;
+    relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
   }
-  relative_timestamp = relative_timestamp64 / (gint64) mux->time_scale;
-  if (mux->matroska_version > 1 && !write_duration) {
+  relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
+      mux->time_scale);
+  if (mux->doctype_version > 1 && !write_duration) {
     int flags =
         GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
 
     hdr =
         gst_matroska_mux_create_buffer_header (collect_pad->track,
         relative_timestamp, flags);
+    gst_ebml_write_set_cache (ebml, 0x40);
     gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
         GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
     gst_ebml_write_buffer (ebml, hdr);
+    gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
     gst_ebml_write_buffer (ebml, buf);
 
     return gst_ebml_last_write_result (ebml);
   } else {
+    gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
+    /* write and call order slightly unnatural,
+     * but avoids seek and minizes pushing */
     blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
     hdr =
         gst_matroska_mux_create_buffer_header (collect_pad->track,
         relative_timestamp, 0);
+    if (write_duration)
+      gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
     gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
         GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
     gst_ebml_write_buffer (ebml, hdr);
+    gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
+    gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
     gst_ebml_write_buffer (ebml, buf);
-    if (write_duration) {
-      gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
-          block_duration / mux->time_scale);
-    }
-    gst_ebml_write_master_finish (ebml, blockgroup);
+
     return gst_ebml_last_write_result (ebml);
   }
 }
@@ -2616,9 +2877,10 @@ static GstFlowReturn
 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
 {
   GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
+  GstEbmlWrite *ebml = mux->ebml_write;
   GstMatroskaPad *best;
   gboolean popped;
-  GstFlowReturn ret;
+  GstFlowReturn ret = GST_FLOW_OK;
 
   GST_DEBUG_OBJECT (mux, "Collected pads");
 
@@ -2630,7 +2892,9 @@ gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
       return GST_FLOW_ERROR;
     }
     mux->state = GST_MATROSKA_MUX_STATE_HEADER;
+    gst_ebml_start_streamheader (ebml);
     gst_matroska_mux_start (mux);
+    gst_matroska_mux_stop_streamheader (mux);
     mux->state = GST_MATROSKA_MUX_STATE_DATA;
   }
 
@@ -2640,8 +2904,15 @@ gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
 
     /* if there is no best pad, we have reached EOS */
     if (best == NULL) {
+      /* buffer popped, but none returned means it was clipped */
+      if (popped)
+        break;
       GST_DEBUG_OBJECT (mux, "No best pad finishing...");
-      gst_matroska_mux_finish (mux);
+      if (!mux->streamable) {
+        gst_matroska_mux_finish (mux);
+      } else {
+        GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
+      }
       gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
       ret = GST_FLOW_UNEXPECTED;
       break;
@@ -2743,8 +3014,14 @@ gst_matroska_mux_set_property (GObject * object,
       g_free (mux->writing_app);
       mux->writing_app = g_value_dup_string (value);
       break;
-    case ARG_MATROSKA_VERSION:
-      mux->matroska_version = g_value_get_int (value);
+    case ARG_DOCTYPE_VERSION:
+      mux->doctype_version = g_value_get_int (value);
+      break;
+    case ARG_MIN_INDEX_INTERVAL:
+      mux->min_index_interval = g_value_get_int64 (value);
+      break;
+    case ARG_STREAMABLE:
+      mux->streamable = g_value_get_boolean (value);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -2765,18 +3042,17 @@ gst_matroska_mux_get_property (GObject * object,
     case ARG_WRITING_APP:
       g_value_set_string (value, mux->writing_app);
       break;
-    case ARG_MATROSKA_VERSION:
-      g_value_set_int (value, mux->matroska_version);
+    case ARG_DOCTYPE_VERSION:
+      g_value_set_int (value, mux->doctype_version);
+      break;
+    case ARG_MIN_INDEX_INTERVAL:
+      g_value_set_int64 (value, mux->min_index_interval);
+      break;
+    case ARG_STREAMABLE:
+      g_value_set_boolean (value, mux->streamable);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
   }
 }
-
-gboolean
-gst_matroska_mux_plugin_init (GstPlugin * plugin)
-{
-  return gst_element_register (plugin, "matroskamux",
-      GST_RANK_PRIMARY, GST_TYPE_MATROSKA_MUX);
-}