Port matroska muxer to 0.9 (#318847).
authorTim-Philipp Müller <tim@centricular.net>
Fri, 14 Oct 2005 12:43:30 +0000 (12:43 +0000)
committerTim-Philipp Müller <tim@centricular.net>
Fri, 14 Oct 2005 12:43:30 +0000 (12:43 +0000)
Original commit message from CVS:
Reviewed by: Tim-Philipp Müller  <tim at centricular dot net>
* configure.ac:
* gst/matroska/Makefile.am:
* gst/matroska/ebml-ids.h:
* gst/matroska/ebml-write.c:
* gst/matroska/ebml-write.h:
* gst/matroska/matroska-ids.h:
* gst/matroska/matroska-mux.c:
* gst/matroska/matroska-mux.h:
* gst/matroska/matroska.c: (plugin_init):
Port matroska muxer to 0.9 (#318847).

ChangeLog
configure.ac
gst/matroska/Makefile.am
gst/matroska/ebml-ids.h
gst/matroska/ebml-write.c
gst/matroska/ebml-write.h
gst/matroska/matroska-ids.h
gst/matroska/matroska-mux.c
gst/matroska/matroska-mux.h
gst/matroska/matroska.c

index 8725fa6..f36f5fa 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2005-10-14  Michal Benes  <michal dot benes at xeris dot cz>
+
+       Reviewed by: Tim-Philipp Müller  <tim at centricular dot net>
+
+       * configure.ac:
+       * gst/matroska/Makefile.am:
+       * gst/matroska/ebml-ids.h:
+       * gst/matroska/ebml-write.c:
+       * gst/matroska/ebml-write.h:
+       * gst/matroska/matroska-ids.h:
+       * gst/matroska/matroska-mux.c:
+       * gst/matroska/matroska-mux.h:
+       * gst/matroska/matroska.c: (plugin_init):
+         Port matroska muxer to 0.9 (#318847).
+
 2005-10-13  Tim-Philipp Müller  <tim at centricular dot net>
 
        * ext/speex/gstspeexenc.c: (gst_speexenc_get_tag_value),
index 3bdcb40..f3ee637 100644 (file)
@@ -275,6 +275,7 @@ GST_PLUGINS_ALL="\
                goom \
                law \
                level \
+               matroska \
                rtp     \
                rtsp    \
                smpte   \
@@ -593,6 +594,7 @@ gst/effectv/Makefile
 gst/goom/Makefile
 gst/law/Makefile
 gst/level/Makefile
+gst/matroska/Makefile
 gst/rtp/Makefile
 gst/rtsp/Makefile
 gst/smpte/Makefile
index 78f2c91..1dd6e0c 100644 (file)
@@ -1,12 +1,13 @@
 plugin_LTLIBRARIES = libgstmatroska.la
 
 libgstmatroska_la_SOURCES = \
-       ebml-read.c \
        ebml-write.c \
        matroska.c \
-       matroska-demux.c \
        matroska-mux.c
 
+#      ebml-read.c
+#      matroska-demux.c
+
 noinst_HEADERS = \
        ebml-ids.h \
        ebml-read.h \
@@ -15,6 +16,14 @@ noinst_HEADERS = \
        matroska-ids.h \
        matroska-mux.h
 
-libgstmatroska_la_CFLAGS = $(GST_CFLAGS)
-libgstmatroska_la_LIBADD = $(GST_LIBS)
+libgstmatroska_la_CFLAGS = \
+       $(GST_BASE_CFLAGS) \
+       $(GST_PLUGINS_BASE_CFLAGS) \
+       $(GST_CFLAGS) \
+       -I$(top_srcdir)/gst-libs
+libgstmatroska_la_LIBADD = \
+       $(GST_BASE_LIBS) \
+       $(GST_PLUGINS_BASE_LIBS) \
+       $(GST_LIBS) \
+       -lgstriff-@GST_MAJORMINOR@
 libgstmatroska_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
index 0dd120c..db547cd 100644 (file)
@@ -27,6 +27,9 @@ G_BEGIN_DECLS
 /* EBML version supported */
 #define GST_EBML_VERSION 1
 
+/* Unknown size (all bits set to 1) */
+#define GST_EBML_SIZE_UNKNOWN          G_GINT64_CONSTANT(0x00ffffffffffffff)
+
 /* top-level master-IDs */
 #define GST_EBML_ID_HEADER             0x1A45DFA3
 
index 139c4d0..27bc6b5 100644 (file)
@@ -1,5 +1,6 @@
 /* GStreamer EBML I/O
  * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * (c) 2005 Michal Benes <michal.benes@xeris.cz>
  *
  * ebml-write.c: write EBML data to file/stream
  *
 #include "ebml-write.h"
 #include "ebml-ids.h"
 
-enum
-{
-  /* FILL ME */
-  LAST_SIGNAL
-};
 
-static void gst_ebml_write_class_init (GstEbmlWriteClass * klass);
-static void gst_ebml_write_init (GstEbmlWrite * ebml);
-static GstStateChangeReturn gst_ebml_write_change_state (GstElement * element,
-    GstStateChange transition);
+GST_DEBUG_CATEGORY_STATIC (gst_ebml_write_debug);
+#define GST_CAT_DEFAULT gst_ebml_write_debug
 
-static GstElementClass *parent_class = NULL;
+#define _do_init(thing) \
+      GST_DEBUG_CATEGORY_INIT (gst_ebml_write_debug, "GstEbmlWrite", 0, "Write EBML structured data")
+GST_BOILERPLATE_FULL (GstEbmlWrite, gst_ebml_write, GstObject, GST_TYPE_OBJECT,
+    _do_init)
+
+     static void gst_ebml_write_finalize (GObject * object);
 
-GType
-gst_ebml_write_get_type (void)
-{
-  static GType gst_ebml_write_type = 0;
-
-  if (!gst_ebml_write_type) {
-    static const GTypeInfo gst_ebml_write_info = {
-      sizeof (GstEbmlWriteClass),
-      NULL,
-      NULL,
-      (GClassInitFunc) gst_ebml_write_class_init,
-      NULL,
-      NULL,
-      sizeof (GstEbmlWrite),
-      0,
-      (GInstanceInitFunc) gst_ebml_write_init,
-    };
-
-    gst_ebml_write_type =
-        g_type_register_static (GST_TYPE_ELEMENT, "GstEbmlWrite",
-        &gst_ebml_write_info, 0);
-  }
 
-  return gst_ebml_write_type;
+     static void gst_ebml_write_base_init (gpointer g_class)
+{
 }
 
 static void
 gst_ebml_write_class_init (GstEbmlWriteClass * klass)
 {
-  GstElementClass *gstelement_class = (GstElementClass *) klass;
-
-  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+  GObjectClass *object = G_OBJECT_CLASS (klass);
 
-  gstelement_class->change_state = gst_ebml_write_change_state;
+  object->finalize = gst_ebml_write_finalize;
 }
 
 static void
-gst_ebml_write_init (GstEbmlWrite * ebml)
+gst_ebml_write_init (GstEbmlWrite * ebml, GstEbmlWriteClass * klass)
 {
   ebml->srcpad = NULL;
   ebml->pos = 0;
 
   ebml->cache = NULL;
+  ebml->cache_size = 0;
+}
+
+static void
+gst_ebml_write_finalize (GObject * object)
+{
+  GstEbmlWrite *ebml = GST_EBML_WRITE (object);
+
+  gst_object_unref (ebml->srcpad);
+
+  GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
+}
+
+
+/**
+ * gst_ebml_write_new:
+ * @srcpad: Source pad to which the output will be pushed.
+ *
+ * Creates a new #GstEbmlWrite.
+ *
+ * Returns: a new #GstEbmlWrite
+ */
+GstEbmlWrite *
+gst_ebml_write_new (GstPad * srcpad)
+{
+  GstEbmlWrite *ebml =
+      GST_EBML_WRITE (g_object_new (GST_TYPE_EBML_WRITE, NULL));
+
+  ebml->srcpad = gst_object_ref (srcpad);
+
+  gst_ebml_write_reset (ebml);
+
+  return ebml;
 }
 
-static GstStateChangeReturn
-gst_ebml_write_change_state (GstElement * element, GstStateChange transition)
+
+/**
+ * gst_ebml_write_reset:
+ * @ebml: a #GstEbmlWrite.
+ *
+ * Reset internal state of #GstEbmlWrite.
+ */
+void
+gst_ebml_write_reset (GstEbmlWrite * ebml)
 {
-  GstEbmlWrite *ebml = GST_EBML_WRITE (element);
-
-  switch (transition) {
-    case GST_STATE_CHANGE_PAUSED_TO_READY:
-      ebml->pos = 0;
-      break;
-    default:
-      break;
+  ebml->pos = 0;
+
+  if (ebml->cache) {
+    gst_buffer_unref (ebml->cache);
+    ebml->cache = NULL;
   }
+  ebml->cache_size = 0;
+  ebml->last_write_result = GST_FLOW_OK;
+}
 
-  if (GST_ELEMENT_CLASS (parent_class)->change_state)
-    return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
 
-  return GST_STATE_CHANGE_SUCCESS;
+/**
+ * gst_ebml_last_write_result:
+ * @ebml: a #GstEbmlWrite.
+ *
+ * Returns: GST_FLOW_OK if there was not write error since the last call of
+ *          gst_ebml_last_write_result or code of the error.
+ */
+GstFlowReturn
+gst_ebml_last_write_result (GstEbmlWrite * ebml)
+{
+  GstFlowReturn res = ebml->last_write_result;
+
+  ebml->last_write_result = GST_FLOW_OK;
+
+  return res;
 }
 
-/*
- * Caching.
+
+/**
+ * gst_ebml_write_set_cache:
+ * @ebml: a #GstEbmlWrite.
+ * @size: size of the cache.
+ * Create a cache.
  *
  * The idea is that you use this for writing a lot
  * of small elements. This will just "queue" all of
@@ -114,7 +146,6 @@ gst_ebml_write_change_state (GstElement * element, GstStateChange transition)
  * at once. This saves memory and time for buffer
  * allocation and init, and it looks better.
  */
-
 void
 gst_ebml_write_set_cache (GstEbmlWrite * ebml, guint size)
 {
@@ -124,11 +155,18 @@ gst_ebml_write_set_cache (GstEbmlWrite * ebml, guint size)
   g_return_if_fail (ebml->cache == NULL);
 
   ebml->cache = gst_buffer_new_and_alloc (size);
+  ebml->cache_size = size;
   GST_BUFFER_SIZE (ebml->cache) = 0;
   GST_BUFFER_OFFSET (ebml->cache) = ebml->pos;
   ebml->handled = 0;
 }
 
+/**
+ * gst_ebml_write_flush_cache:
+ * @ebml: a #GstEbmlWrite.
+ *
+ * Flush the cache.
+ */
 void
 gst_ebml_write_flush_cache (GstEbmlWrite * ebml)
 {
@@ -141,16 +179,25 @@ gst_ebml_write_flush_cache (GstEbmlWrite * ebml)
   g_assert (GST_BUFFER_SIZE (ebml->cache) +
       GST_BUFFER_OFFSET (ebml->cache) == ebml->pos);
 
-  gst_pad_push (ebml->srcpad, GST_DATA (ebml->cache));
+  if (ebml->last_write_result == GST_FLOW_OK)
+    ebml->last_write_result = gst_pad_push (ebml->srcpad, ebml->cache);
+
   ebml->cache = NULL;
+  ebml->cache_size = 0;
   ebml->handled = 0;
 }
 
-/*
- * One-element buffer, in case of no cache. If there is
+
+/**
+ * gst_ebml_write_element_new:
+ * @ebml: a #GstEbmlWrite.
+ * @size: size of the requested buffer.
+ *
+ * Create a buffer for one element. If there is
  * a cache, use that instead.
+ *
+ * Returns: A new #GstBuffer.
  */
-
 static GstBuffer *
 gst_ebml_write_element_new (GstEbmlWrite * ebml, guint size)
 {
@@ -162,7 +209,7 @@ gst_ebml_write_element_new (GstEbmlWrite * ebml, guint size)
 
   /* prefer cache */
   if (ebml->cache) {
-    if (GST_BUFFER_MAXSIZE (ebml->cache) - GST_BUFFER_SIZE (ebml->cache) < size) {
+    if (ebml->cache_size - GST_BUFFER_SIZE (ebml->cache) < size) {
       GST_LOG ("Cache available, but too small. Clearing...");
       gst_ebml_write_flush_cache (ebml);
     } else {
@@ -177,10 +224,14 @@ gst_ebml_write_element_new (GstEbmlWrite * ebml, guint size)
   return buf;
 }
 
-/*
+
+/**
+ * gst_ebml_write_element_id:
+ * @buf: Buffer to which id should be written.
+ * @id: Element ID that should be written.
+ * 
  * Write element ID into a buffer.
  */
-
 static void
 gst_ebml_write_element_id (GstBuffer * buf, guint32 id)
 {
@@ -208,29 +259,38 @@ gst_ebml_write_element_id (GstBuffer * buf, guint32 id)
   }
 }
 
-/*
+
+/**
+ * gst_ebml_write_element_size:
+ * @buf: #GstBuffer to which size should be written.
+ * @size: Element length.
+ * 
  * Write element length into a buffer.
  */
-
 static void
 gst_ebml_write_element_size (GstBuffer * buf, guint64 size)
 {
   guint8 *data = GST_BUFFER_DATA (buf) + GST_BUFFER_SIZE (buf);
   guint bytes = 1, mask = 0x80;
 
-  /* how many bytes? */
-  while ((size >> ((bytes - 1) * 8)) >= mask && bytes <= 8) {
-    mask >>= 1;
-    bytes++;
-  }
+  if (size != GST_EBML_SIZE_UNKNOWN) {
+    /* how many bytes? - use mask-1 because an all-1 bitset is not allowed */
+    while ((size >> ((bytes - 1) * 8)) >= (mask - 1) && bytes <= 8) {
+      mask >>= 1;
+      bytes++;
+    }
 
-  /* if invalid size, use max. */
-  if (bytes > 8) {
-    GST_WARNING ("Invalid size, maximizing");
+    /* if invalid size, use max. */
+    if (bytes > 8) {
+      GST_WARNING ("Invalid size, writing size unknown");
+      mask = 0x01;
+      bytes = 8;
+      /* Now here's a real FIXME: we cannot read those yet! */
+      size = GST_EBML_SIZE_UNKNOWN;
+    }
+  } else {
     mask = 0x01;
     bytes = 8;
-    /* Now here's a real FIXME: we cannot read those yet! */
-    size = G_GINT64_CONSTANT (0x00ffffffffffffff);
   }
 
   /* write out, BE, with length size marker */
@@ -243,10 +303,15 @@ gst_ebml_write_element_size (GstBuffer * buf, guint64 size)
   }
 }
 
-/*
+
+/**
+ * gst_ebml_write_element_data:
+ * @buf: #GstBuffer to which data should be written.
+ * @write: Data that should be written.
+ * @length: Length of the data.
+ *
  * Write element data into a buffer.
  */
-
 static void
 gst_ebml_write_element_data (GstBuffer * buf, guint8 * write, guint64 length)
 {
@@ -256,10 +321,14 @@ gst_ebml_write_element_data (GstBuffer * buf, guint8 * write, guint64 length)
   GST_BUFFER_SIZE (buf) += length;
 }
 
-/*
+
+/**
+ * gst_ebml_write_element_push:
+ * @ebml: #GstEbmlWrite
+ * @buf: #GstBuffer to be written.
+ * 
  * Write out buffer by moving it to the next element.
  */
-
 static void
 gst_ebml_write_element_push (GstEbmlWrite * ebml, GstBuffer * buf)
 {
@@ -276,25 +345,30 @@ gst_ebml_write_element_push (GstEbmlWrite * ebml, GstBuffer * buf)
     return;
   }
 
-  gst_pad_push (ebml->srcpad, GST_DATA (buf));
+  if (ebml->last_write_result == GST_FLOW_OK)
+    ebml->last_write_result = gst_pad_push (ebml->srcpad, buf);
 }
 
-/*
+
+/**
+ * gst_ebml_write_seek:
+ * @ebml: #GstEbmlWrite
+ * @pos: Seek position.
+ * 
  * Seek.
  */
-
 void
 gst_ebml_write_seek (GstEbmlWrite * ebml, guint64 pos)
 {
   GstEvent *seek;
+  GstPad *peer_pad;
 
   /* Cache seeking. A bit dangerous, we assume the client writer
    * knows what he's doing... */
   if (ebml->cache) {
     /* within bounds? */
     if (pos >= GST_BUFFER_OFFSET (ebml->cache) &&
-        pos <
-        GST_BUFFER_OFFSET (ebml->cache) + GST_BUFFER_MAXSIZE (ebml->cache)) {
+        pos < GST_BUFFER_OFFSET (ebml->cache) + ebml->cache_size) {
       GST_BUFFER_SIZE (ebml->cache) = pos - GST_BUFFER_OFFSET (ebml->cache);
       if (ebml->pos > pos)
         ebml->handled -= ebml->pos - pos;
@@ -307,15 +381,27 @@ gst_ebml_write_seek (GstEbmlWrite * ebml, guint64 pos)
     }
   }
 
-  seek = gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_SET, pos);
-  gst_pad_push (ebml->srcpad, GST_DATA (seek));
+  seek = gst_event_new_newsegment (FALSE, 1.0, GST_FORMAT_BYTES,
+      pos, -1, GST_CLOCK_TIME_NONE);
+  peer_pad = GST_PAD_PEER (ebml->srcpad);
+  if (peer_pad) {
+    gst_pad_send_event (peer_pad, seek);
+  } else {
+    GST_WARNING_OBJECT (ebml, "Can not seek: no peer pad");
+  }
+
   ebml->pos = pos;
 }
 
-/*
- * Get no. bytes needed to write a uint.
- */
 
+/**
+ * gst_ebml_write_get_uint_size:
+ * @num: Number to be encoded.
+ * 
+ * Get number of bytes needed to write a uint.
+ *
+ * Returns: Encoded uint length.
+ */
 static guint
 gst_ebml_write_get_uint_size (guint64 num)
 {
@@ -330,10 +416,14 @@ gst_ebml_write_get_uint_size (guint64 num)
 }
 
 
-/*
+/**
+ * gst_ebml_write_set_uint:
+ * @buf: #GstBuffer to which ithe number should be written.
+ * @num: Number to be written.
+ * @size: Encoded number length.
+ *
  * Write an uint into a buffer.
  */
-
 static void
 gst_ebml_write_set_uint (GstBuffer * buf, guint64 num, guint size)
 {
@@ -347,10 +437,15 @@ gst_ebml_write_set_uint (GstBuffer * buf, guint64 num, guint size)
   }
 }
 
-/*
- * Data type wrappers.
- */
 
+/**
+ * gst_ebml_write_uint:
+ * @ebml: #GstEbmlWrite
+ * @id: Element ID.
+ * @num: Number to be written.
+ *
+ * Write uint element.
+ */
 void
 gst_ebml_write_uint (GstEbmlWrite * ebml, guint32 id, guint64 num)
 {
@@ -364,6 +459,15 @@ gst_ebml_write_uint (GstEbmlWrite * ebml, guint32 id, guint64 num)
   gst_ebml_write_element_push (ebml, buf);
 }
 
+
+/**
+ * gst_ebml_write_sint:
+ * @ebml: #GstEbmlWrite
+ * @id: Element ID.
+ * @num: Number to be written.
+ *
+ * Write sint element.
+ */
 void
 gst_ebml_write_sint (GstEbmlWrite * ebml, guint32 id, gint64 num)
 {
@@ -392,6 +496,15 @@ gst_ebml_write_sint (GstEbmlWrite * ebml, guint32 id, gint64 num)
   gst_ebml_write_element_push (ebml, buf);
 }
 
+
+/**
+ * gst_ebml_write_float:
+ * @ebml: #GstEbmlWrite
+ * @id: Element ID.
+ * @num: Number to be written.
+ *
+ * Write float element.
+ */
 void
 gst_ebml_write_float (GstEbmlWrite * ebml, guint32 id, gdouble num)
 {
@@ -413,6 +526,15 @@ gst_ebml_write_float (GstEbmlWrite * ebml, guint32 id, gdouble num)
   gst_ebml_write_element_push (ebml, buf);
 }
 
+
+/**
+ * gst_ebml_write_ascii:
+ * @ebml: #GstEbmlWrite
+ * @id: Element ID.
+ * @str: String to be written.
+ *
+ * Write string element.
+ */
 void
 gst_ebml_write_ascii (GstEbmlWrite * ebml, guint32 id, const gchar * str)
 {
@@ -425,25 +547,49 @@ gst_ebml_write_ascii (GstEbmlWrite * ebml, guint32 id, const gchar * str)
   gst_ebml_write_element_push (ebml, buf);
 }
 
+
+/**
+ * gst_ebml_write_utf8:
+ * @ebml: #GstEbmlWrite
+ * @id: Element ID.
+ * @str: String to be written.
+ *
+ * Write utf8 encoded string element.
+ */
 void
 gst_ebml_write_utf8 (GstEbmlWrite * ebml, guint32 id, const gchar * str)
 {
   gst_ebml_write_ascii (ebml, id, str);
 }
 
-/* date should be in seconds since the unix epoch */
+
+/**
+ * gst_ebml_write_date:
+ * @ebml: #GstEbmlWrite
+ * @id: Element ID.
+ * @date: Date in seconds since the unix epoch.
+ *
+ * Write date element.
+ */
 void
 gst_ebml_write_date (GstEbmlWrite * ebml, guint32 id, gint64 date)
 {
   gst_ebml_write_sint (ebml, id, (date - GST_EBML_DATE_OFFSET) * GST_SECOND);
 }
 
-/*
+/**
+ * gst_ebml_write_master_start:
+ * @ebml: #GstEbmlWrite
+ * @id: Element ID.
+ *
+ * Start wiriting mater element.
+ *
  * Master writing is annoying. We use a size marker of
  * the max. allowed length, so that we can later fill it
  * in validly. 
+ *
+ * Returns: Master starting position.
  */
-
 guint64
 gst_ebml_write_master_start (GstEbmlWrite * ebml, guint32 id)
 {
@@ -453,12 +599,20 @@ gst_ebml_write_master_start (GstEbmlWrite * ebml, guint32 id)
   t = GST_BUFFER_SIZE (buf);
   gst_ebml_write_element_id (buf, id);
   pos += GST_BUFFER_SIZE (buf) - t;
-  gst_ebml_write_element_size (buf, -1);
+  gst_ebml_write_element_size (buf, GST_EBML_SIZE_UNKNOWN);
   gst_ebml_write_element_push (ebml, buf);
 
   return pos;
 }
 
+
+/**
+ * gst_ebml_write_master_finish:
+ * @ebml: #GstEbmlWrite
+ * @startpos: Master starting position.
+ *
+ * Finish writing master element.
+ */
 void
 gst_ebml_write_master_finish (GstEbmlWrite * ebml, guint64 startpos)
 {
@@ -476,6 +630,16 @@ gst_ebml_write_master_finish (GstEbmlWrite * ebml, guint64 startpos)
   gst_ebml_write_seek (ebml, pos);
 }
 
+
+/**
+ * gst_ebml_write_binary:
+ * @ebml: #GstEbmlWrite
+ * @id: Element ID.
+ * @binary: Data to be written.
+ * @length: Length of the data
+ *
+ * Write an element with binary data.
+ */
 void
 gst_ebml_write_binary (GstEbmlWrite * ebml,
     guint32 id, guint8 * binary, guint64 length)
@@ -488,13 +652,20 @@ gst_ebml_write_binary (GstEbmlWrite * ebml,
   gst_ebml_write_element_push (ebml, buf);
 }
 
-/*
+
+/**
+ * gst_ebml_write_buffer_header:
+ * @ebml: #GstEbmlWrite
+ * @id: Element ID.
+ * @length: Length of the data
+ * 
+ * Write header of the binary element (use with gst_ebml_write_buffer function).
+ * 
  * For things like video frames and audio samples,
  * you want to use this function, as it doesn't have
  * the overhead of memcpy() that other functions
  * such as write_binary() do have.
  */
-
 void
 gst_ebml_write_buffer_header (GstEbmlWrite * ebml, guint32 id, guint64 length)
 {
@@ -505,13 +676,29 @@ gst_ebml_write_buffer_header (GstEbmlWrite * ebml, guint32 id, guint64 length)
   gst_ebml_write_element_push (ebml, buf);
 }
 
+
+/**
+ * gst_ebml_write_buffer:
+ * @ebml: #GstEbmlWrite
+ * @data: #GstBuffer cointaining the data.
+ *
+ * Write  binary element (see gst_ebml_write_buffer_header).
+ */
 void
 gst_ebml_write_buffer (GstEbmlWrite * ebml, GstBuffer * data)
 {
   gst_ebml_write_element_push (ebml, data);
 }
 
-/*
+
+/**
+ * gst_ebml_replace_uint:
+ * @ebml: #GstEbmlWrite
+ * @pos: Position of the uint that should be replaced.
+ * @num: New value.
+ *
+ * Replace uint with a new value.
+ * 
  * When replacing a uint, we assume that it is *always*
  * 8-byte, since that's the safest guess we can do. This
  * is just for simplicity.
@@ -519,7 +706,6 @@ gst_ebml_write_buffer (GstEbmlWrite * ebml, GstBuffer * data)
  * FIXME: this function needs to be replaced with something
  * proper. This is a crude hack.
  */
-
 void
 gst_ebml_replace_uint (GstEbmlWrite * ebml, guint64 pos, guint64 num)
 {
@@ -533,10 +719,14 @@ gst_ebml_replace_uint (GstEbmlWrite * ebml, guint64 pos, guint64 num)
   gst_ebml_write_seek (ebml, oldpos);
 }
 
-/*
+/**
+ * gst_ebml_write_header:
+ * @ebml: #GstEbmlWrite
+ * @doctype: Document type.
+ * @version: Document type version.
+ * 
  * Write EBML header.
  */
-
 void
 gst_ebml_write_header (GstEbmlWrite * ebml, gchar * doctype, guint version)
 {
index 43d2f8b..01a9992 100644 (file)
@@ -1,5 +1,6 @@
 /* GStreamer EBML I/O
  * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * (c) 2005 Michal Benes <michal.benes@xeris.cz>
  *
  * ebml-write.c: write EBML data to file/stream
  *
@@ -41,21 +42,35 @@ G_BEGIN_DECLS
   (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_EBML_WRITE, GstEbmlWriteClass))
 
 typedef struct _GstEbmlWrite {
-  GstElement parent;
+  GstObject object;
 
   GstPad *srcpad;
   guint64 pos;
 
   GstBuffer *cache;
+  guint cache_size;
   guint handled;
+
+  GstFlowReturn last_write_result;
+
+  /*< private >*/
+  gpointer _gst_reserved[GST_PADDING];
 } GstEbmlWrite;
 
 typedef struct _GstEbmlWriteClass {
-  GstElementClass parent;
+  GstObjectClass parent;
+
+  /*< private >*/
+  gpointer _gst_reserved[GST_PADDING];
 } GstEbmlWriteClass;
 
 GType   gst_ebml_write_get_type      (void);
 
+GstEbmlWrite *gst_ebml_write_new     (GstPad *srcpad);
+void    gst_ebml_write_reset         (GstEbmlWrite *ebml);
+
+GstFlowReturn gst_ebml_last_write_result (GstEbmlWrite *ebml);
+
 /*
  * Caching means that we do not push one buffer for
  * each element, but fill this one until a flush.
index ec87fa5..a0895a9 100644 (file)
 #define GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_SP     "V_MPEG4/ISO/SP"
 #define GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP    "V_MPEG4/ISO/ASP"
 #define GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AP     "V_MPEG4/ISO/AP"
+#define GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC    "V_MPEG4/ISO/AVC"
 #define GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3    "V_MPEG4/MS/V3"
 #define GST_MATROSKA_CODEC_ID_VIDEO_MPEG1        "V_MPEG1"
 #define GST_MATROSKA_CODEC_ID_VIDEO_MPEG2        "V_MPEG2"
 #define GST_MATROSKA_CODEC_ID_AUDIO_MPEG2        "A_AAC/MPEG2/"
 #define GST_MATROSKA_CODEC_ID_AUDIO_MPEG4        "A_AAC/MPEG4/"
 #define GST_MATROSKA_CODEC_ID_AUDIO_TTA          "A_TTA1"
+#define GST_MATROSKA_CODEC_ID_AUDIO_WAVPACK4     "A_WAVPACK4"
 /* TODO: AC3-9/10 (?), Real, Musepack, Quicktime */
 
 #define GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8      "S_TEXT/UTF8"
@@ -267,4 +269,17 @@ typedef struct _GstMatroskaIndex {
   guint64        time;  /* in nanoseconds */
 } GstMatroskaIndex;
 
+typedef struct _Wavpack4Header {
+  guchar  ck_id [4];     /* "wvpk"                                         */
+  guint32 ck_size;       /* size of entire frame (minus 8, of course)      */
+  guint16 version;       /* 0x403 for now                                  */
+  guint8  track_no;      /* track number (0 if not used, like now)         */
+  guint8  index_no;      /* remember these? (0 if not used, like now)      */
+  guint32 total_samples; /* for entire file (-1 if unknown)                */
+  guint32 block_index;   /* index of first sample in block (to file begin) */
+  guint32 block_samples; /* # samples in this block                        */
+  guint32 flags;         /* various flags for id and decoding              */
+  guint32 crc;           /* crc for actual decoded data                    */
+} Wavpack4Header;
+
 #endif /* __GST_MATROSKA_IDS_H__ */
index ab1fec4..044a62c 100644 (file)
@@ -1,5 +1,6 @@
 /* GStreamer Matroska muxer/demuxer
  * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * (c) 2005 Michal Benes <michal.benes@xeris.cz>
  *
  * matroska-mux.c: matroska file/stream muxer
  *
@@ -29,7 +30,7 @@
 #include "matroska-mux.h"
 #include "matroska-ids.h"
 
-GST_DEBUG_CATEGORY (matroskamux_debug);
+GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
 #define GST_CAT_DEFAULT matroskamux_debug
 
 enum
@@ -41,7 +42,7 @@ enum
 enum
 {
   ARG_0,
-  ARG_METADATA
+  ARG_WRITING_APP
       /* FILL ME */
 };
 
@@ -64,6 +65,8 @@ static GstStaticPadTemplate videosink_templ =
         "mpegversion = (int) { 1, 2, 4 }, "
         "systemstream = (boolean) false, "
         COMMON_VIDEO_CAPS "; "
+        "video/x-h264, "
+        COMMON_VIDEO_CAPS "; "
         "video/x-divx, "
         COMMON_VIDEO_CAPS "; "
         "video/x-xvid, "
@@ -82,7 +85,6 @@ static GstStaticPadTemplate videosink_templ =
 
 /* FIXME:
  * * audio/x-raw-float: endianness needs defining.
- * * audio/x-vorbis: private data setup needs work.
  */
 static GstStaticPadTemplate audiosink_templ =
     GST_STATIC_PAD_TEMPLATE ("audio_%d",
@@ -97,6 +99,8 @@ static GstStaticPadTemplate audiosink_templ =
         COMMON_AUDIO_CAPS "; "
         "audio/x-ac3, "
         COMMON_AUDIO_CAPS "; "
+        "audio/x-vorbis, "
+        COMMON_AUDIO_CAPS "; "
         "audio/x-raw-int, "
         "width = (int) { 8, 16, 24 }, "
         "depth = (int) { 8, 16, 24 }, "
@@ -116,15 +120,26 @@ GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
 
 static GArray *used_uids;
 
-/* gobject magic foo */
-static void gst_matroska_mux_base_init (GstMatroskaMuxClass * klass);
-static void gst_matroska_mux_class_init (GstMatroskaMuxClass * klass);
-static void gst_matroska_mux_init (GstMatroskaMux * mux);
+static void
+_do_init (GType matroskamux_type)
+{
+  GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
+      "Matroska muxer");
+}
+
+GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
+    GST_TYPE_ELEMENT, _do_init);
+
+/* Matroska muxer destructor */
+static void gst_matroska_mux_finalize (GObject * object);
 
-/* element functions */
-static void gst_matroska_mux_loop (GstElement * element);
+/* Pads collected callback */
+static GstFlowReturn
+gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data);
 
 /* pad functions */
+static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
+    GstEvent * event);
 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
     GstPadTemplate * templ, const gchar * name);
 
@@ -144,40 +159,12 @@ static void gst_matroska_mux_reset (GstElement * element);
 /* uid generation */
 static guint32 gst_matroska_mux_create_uid ();
 
-static GstEbmlWriteClass *parent_class = NULL;
-
 /*static guint gst_matroska_mux_signals[LAST_SIGNAL] = { 0 };*/
 
-GType
-gst_matroska_mux_get_type (void)
-{
-  static GType gst_matroska_mux_type = 0;
-
-  if (!gst_matroska_mux_type) {
-    static const GTypeInfo gst_matroska_mux_info = {
-      sizeof (GstMatroskaMuxClass),
-      (GBaseInitFunc) gst_matroska_mux_base_init,
-      NULL,
-      (GClassInitFunc) gst_matroska_mux_class_init,
-      NULL,
-      NULL,
-      sizeof (GstMatroskaMux),
-      0,
-      (GInstanceInitFunc) gst_matroska_mux_init,
-    };
-
-    gst_matroska_mux_type =
-        g_type_register_static (GST_TYPE_EBML_WRITE,
-        "GstMatroskaMux", &gst_matroska_mux_info, 0);
-  }
-
-  return gst_matroska_mux_type;
-}
-
 static void
-gst_matroska_mux_base_init (GstMatroskaMuxClass * klass)
+gst_matroska_mux_base_init (gpointer g_class)
 {
-  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
   static GstElementDetails gst_matroska_mux_details = {
     "Matroska muxer",
     "Codec/Muxer",
@@ -205,50 +192,80 @@ gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
   gobject_class = (GObjectClass *) klass;
   gstelement_class = (GstElementClass *) klass;
 
-  g_object_class_install_property (gobject_class, ARG_METADATA,
-      g_param_spec_boxed ("metadata", "Metadata", "Metadata",
-          GST_TYPE_CAPS, G_PARAM_READWRITE));
-
-  parent_class = g_type_class_ref (GST_TYPE_EBML_WRITE);
+  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_matroska_mux_finalize);
 
   gobject_class->get_property = gst_matroska_mux_get_property;
   gobject_class->set_property = gst_matroska_mux_set_property;
 
+  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));
+
   gstelement_class->change_state = gst_matroska_mux_change_state;
   gstelement_class->request_new_pad = gst_matroska_mux_request_new_pad;
-
-  GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
-      "Matroska muxer");
 }
 
+
+/**
+ * gst_matroska_mux_init:
+ * @mux: #GstMatroskaMux that should be initialized.
+ * @g_class: Class of the muxer.
+ *
+ * Matroska muxer constructor.
+ */
 static void
-gst_matroska_mux_init (GstMatroskaMux * mux)
+gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
 {
-  GstElementClass *klass = GST_ELEMENT_GET_CLASS (mux);
-  gint i;
+  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
 
   mux->srcpad =
-      gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
-          "src"), "src");
+      gst_pad_new_from_template (gst_element_class_get_pad_template
+      (gstelement_class, "src"), "src");
+  gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
   gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
-  GST_EBML_WRITE (mux)->srcpad = mux->srcpad;
 
-  gst_element_set_loop_function (GST_ELEMENT (mux), gst_matroska_mux_loop);
+  mux->collect = gst_collectpads_new ();
+  gst_collectpads_set_function (mux->collect,
+      (GstCollectPadsFunction) gst_matroska_mux_collected, mux);
 
-  /* initial stream no. */
-  for (i = 0; i < GST_MATROSKA_MUX_MAX_STREAMS; i++) {
-    mux->sink[i].buffer = NULL;
-    mux->sink[i].track = NULL;
-    mux->sink[i].duration = 0;
-  }
+  mux->ebml_write = gst_ebml_write_new (mux->srcpad);
+
+  /* initialize internal variables */
   mux->index = NULL;
 
-  /* finish off */
+  /* Initialize all variables */
   gst_matroska_mux_reset (GST_ELEMENT (mux));
 }
 
+
+/**
+ * gst_matroska_mux_finalize:
+ * @object: #GstMatroskaMux that should be finalized.
+ *
+ * Finalize matroska muxer.
+ */
+static void
+gst_matroska_mux_finalize (GObject * object)
+{
+  GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
+
+  gst_object_unref (mux->collect);
+  gst_object_unref (mux->ebml_write);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+/**
+ * gst_matroska_mux_create_uid:
+ *
+ * Generate new unused track UID.
+ *
+ * Returns: New track UID.
+ */
 static guint32
-gst_matroska_mux_create_uid ()
+gst_matroska_mux_create_uid (void)
 {
   guint32 uid = 0;
   GRand *rand = g_rand_new ();
@@ -269,46 +286,63 @@ gst_matroska_mux_create_uid ()
   return uid;
 }
 
+
+/**
+ * gst_matroska_mux_reset:
+ * @element: #GstMatroskaMux that should be reseted.
+ *
+ * Reset matroska muxer back to initial state.
+ */
 static void
 gst_matroska_mux_reset (GstElement * element)
 {
   GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
-  guint i;
+  GSList *walk;
+
+  /* reset EBML write */
+  gst_ebml_write_reset (mux->ebml_write);
 
   /* reset input */
   mux->state = GST_MATROSKA_MUX_STATE_START;
 
   /* clean up existing streams */
-  for (i = 0; i < GST_MATROSKA_MUX_MAX_STREAMS; i++) {
-    if (mux->sink[i].track != NULL) {
-      if (mux->sink[i].track->pad != NULL) {
-        gst_element_remove_pad (GST_ELEMENT (mux), mux->sink[i].track->pad);
-      }
-      g_free (mux->sink[i].track->codec_id);
-      g_free (mux->sink[i].track->codec_name);
-      g_free (mux->sink[i].track->name);
-      g_free (mux->sink[i].track->language);
-      g_free (mux->sink[i].track->codec_priv);
-      g_free (mux->sink[i].track);
-      mux->sink[i].track = NULL;
+  while ((walk = mux->collect->data) != NULL) {
+    GstMatroskaPad *collect_pad;
+    GstPad *thepad;
+
+    collect_pad = (GstMatroskaPad *) walk->data;
+    thepad = collect_pad->collect.pad;
+
+    /* free track information */
+    if (collect_pad->track != NULL) {
+      g_free (collect_pad->track->codec_id);
+      g_free (collect_pad->track->codec_name);
+      g_free (collect_pad->track->name);
+      g_free (collect_pad->track->language);
+      g_free (collect_pad->track->codec_priv);
+      g_free (collect_pad->track);
+      collect_pad->track = NULL;
     }
-    if (mux->sink[i].buffer != NULL) {
-      gst_buffer_unref (mux->sink[i].buffer);
-      mux->sink[i].buffer = NULL;
+
+    /* free cached buffer */
+    if (collect_pad->buffer != NULL) {
+      gst_buffer_unref (collect_pad->buffer);
+      collect_pad->buffer = NULL;
     }
-    mux->sink[i].eos = FALSE;
+
+    /* remove from collectpads */
+    gst_collectpads_remove_pad (mux->collect, thepad);
   }
   mux->num_streams = 0;
   mux->num_a_streams = 0;
   mux->num_t_streams = 0;
   mux->num_v_streams = 0;
 
-/* FIXME x-gst-metadata : prehistoric way to do metadata in caps. is done via signals actually */
-
-  /* reset media info  (to default) */
-  gst_caps_replace (&mux->metadata,
-      gst_caps_new_simple ("application/x-gst-metadata",
-          "application", G_TYPE_STRING, "", "date", G_TYPE_STRING, "", NULL));
+  /* reset writing_app */
+  if (mux->writing_app) {
+    free (mux->writing_app);
+  }
+  mux->writing_app = g_strdup ("GStreamer Matroska muxer");
 
   /* reset indexes */
   mux->num_indexes = 0;
@@ -321,7 +355,7 @@ gst_matroska_mux_reset (GstElement * element)
 
   /* reset uid array */
   if (used_uids) {
-    g_free (used_uids);
+    g_array_free (used_uids, TRUE);
   }
   /* arbitrary size, 10 should be enough in most cases */
   used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint32), 10);
@@ -337,27 +371,63 @@ gst_matroska_mux_reset (GstElement * element)
   mux->meta_index = NULL;
 }
 
-static GstPadLinkReturn
-gst_matroska_mux_video_pad_link (GstPad * pad, const GstCaps * caps)
+/**
+ * gst_matroska_mux_handle_src_event:
+ * @pad: Pad which received the event.
+ * @event: Received event.
+ *
+ * handle events - copied from oggmux without understanding 
+ *
+ * Returns: #TRUE on success.
+ */
+static gboolean
+gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
+{
+  GstMatroskaMux *mux;
+  GstEventType type;
+
+  mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
+
+  type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
+
+  switch (type) {
+    case GST_EVENT_SEEK:
+      /* disable seeking for now */
+      return FALSE;
+    default:
+      break;
+  }
+
+  return gst_pad_event_default (pad, event);
+}
+
+
+/**
+ * gst_matroska_mux_video_pad_setcaps:
+ * @pad: Pad which got the caps.
+ * @caps: New caps.
+ *
+ * Setcaps function for video sink pad.
+ *
+ * Returns: #TRUE on success.
+ */
+static gboolean
+gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
 {
   GstMatroskaTrackContext *context = NULL;
   GstMatroskaTrackVideoContext *videocontext;
-  GstMatroskaMux *mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
+  GstMatroskaPad *collect_pad;
   const gchar *mimetype;
-  gint width, height, pixel_width, pixel_height, i;
+  gint width, height, pixel_width, pixel_height;
   gdouble framerate;
   GstStructure *structure;
   gboolean ret;
 
   /* find context */
-  for (i = 0; i < mux->num_streams; i++) {
-    if (mux->sink[i].track && mux->sink[i].track->pad &&
-        mux->sink[i].track->pad == pad) {
-      context = mux->sink[i].track;
-      break;
-    }
-  }
-  g_assert (i < mux->num_streams);
+  collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
+  g_assert (collect_pad);
+  context = collect_pad->track;
+  g_assert (context);
   g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
   videocontext = (GstMatroskaTrackVideoContext *) context;
 
@@ -402,11 +472,11 @@ gst_matroska_mux_video_pad_link (GstPad * pad, const GstCaps * caps)
     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
     gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
 
-    return GST_PAD_LINK_OK;
+    return TRUE;
   } else if (!strcmp (mimetype, "video/x-jpeg")) {
     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
 
-    return GST_PAD_LINK_OK;
+    return TRUE;
   } else if (!strcmp (mimetype, "video/x-divx")) {
     gint divxversion;
     BITMAPINFOHEADER *bih;
@@ -437,7 +507,7 @@ gst_matroska_mux_video_pad_link (GstPad * pad, const GstCaps * caps)
     context->codec_priv = (gpointer) bih;
     context->codec_priv_size = sizeof (BITMAPINFOHEADER);
 
-    return GST_PAD_LINK_OK;
+    return TRUE;
   } else if (!strcmp (mimetype, "video/x-xvid")) {
     BITMAPINFOHEADER *bih;
 
@@ -455,7 +525,38 @@ gst_matroska_mux_video_pad_link (GstPad * pad, const GstCaps * caps)
     context->codec_priv = (gpointer) bih;
     context->codec_priv_size = sizeof (BITMAPINFOHEADER);
 
-    return GST_PAD_LINK_OK;
+    return TRUE;
+  } else if (!strcmp (mimetype, "video/x-h264")) {
+    const GValue *codec_data;
+
+    context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
+
+    if (context->codec_priv != NULL) {
+      g_free (context->codec_priv);
+      context->codec_priv = NULL;
+      context->codec_priv_size = 0;
+    }
+
+
+    /* Create avcC header */
+    codec_data = gst_structure_get_value (structure, "codec_data");
+
+    if (codec_data != NULL) {
+      guint8 *priv_data = NULL;
+      guint priv_data_size = 0;
+
+      GstBuffer *codec_data_buf = g_value_peek_pointer (codec_data);
+
+      priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
+      priv_data = g_malloc0 (priv_data_size);
+
+      memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
+
+      context->codec_priv = priv_data;
+      context->codec_priv_size = priv_data_size;
+    }
+
+    return TRUE;
   } else if (!strcmp (mimetype, "video/mpeg")) {
     gint mpegversion;
 
@@ -472,35 +573,42 @@ gst_matroska_mux_video_pad_link (GstPad * pad, const GstCaps * caps)
         break;
     }
 
-    return GST_PAD_LINK_OK;
+    return TRUE;
   } else if (!strcmp (mimetype, "video/x-msmpeg")) {
     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
 
-    return GST_PAD_LINK_OK;
+    return TRUE;
   }
 
-  return GST_PAD_LINK_REFUSED;
+  return FALSE;
 }
 
-static GstPadLinkReturn
-gst_matroska_mux_audio_pad_link (GstPad * pad, const GstCaps * caps)
+
+/**
+ * gst_matroska_mux_audio_pad_setcaps:
+ * @pad: Pad which got the caps.
+ * @caps: New caps.
+ *
+ * Setcaps function for audio sink pad.
+ *
+ * Returns: #TRUE on success.
+ */
+static gboolean
+gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
 {
   GstMatroskaTrackContext *context = NULL;
   GstMatroskaTrackAudioContext *audiocontext;
+  GstMatroskaPad *collect_pad;
   GstMatroskaMux *mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
   const gchar *mimetype;
-  gint samplerate, channels, i;
+  gint samplerate, channels;
   GstStructure *structure;
 
   /* find context */
-  for (i = 0; i < mux->num_streams; i++) {
-    if (mux->sink[i].track && mux->sink[i].track->pad &&
-        mux->sink[i].track->pad == pad) {
-      context = mux->sink[i].track;
-      break;
-    }
-  }
-  g_assert (i < mux->num_streams);
+  collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
+  g_assert (collect_pad);
+  context = collect_pad->track;
+  g_assert (context);
   g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
   audiocontext = (GstMatroskaTrackAudioContext *) context;
 
@@ -552,7 +660,7 @@ gst_matroska_mux_audio_pad_link (GstPad * pad, const GstCaps * caps)
         break;
     }
 
-    return GST_PAD_LINK_OK;
+    return TRUE;
   } else if (!strcmp (mimetype, "audio/x-raw-int")) {
     gint endianness, width, depth;
     gboolean signedness;
@@ -563,7 +671,7 @@ gst_matroska_mux_audio_pad_link (GstPad * pad, const GstCaps * caps)
     gst_structure_get_int (structure, "signed", &signedness);
     if (width != depth ||
         (width == 8 && signedness) || (width == 16 && !signedness))
-      return GST_PAD_LINK_REFUSED;
+      return FALSE;
 
     audiocontext->bitdepth = depth;
     if (endianness == G_BIG_ENDIAN)
@@ -571,15 +679,90 @@ gst_matroska_mux_audio_pad_link (GstPad * pad, const GstCaps * caps)
     else
       context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
 
-    return GST_PAD_LINK_OK;
+    return TRUE;
   } else if (!strcmp (mimetype, "audio/x-raw-float")) {
     /* FIXME: endianness is undefined */
   } else if (!strcmp (mimetype, "audio/x-vorbis")) {
-    /* FIXME: private data setup needs work */
+    const GValue *streamheader;
+    guint8 *priv_data = NULL;
+    guint priv_data_size = 0;
+
+    context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
+
+    if (context->codec_priv != NULL) {
+      g_free (context->codec_priv);
+      context->codec_priv = NULL;
+      context->codec_priv_size = 0;
+    }
+    streamheader = gst_structure_get_value (structure, "streamheader");
+
+    if (streamheader != NULL) {
+      if (G_VALUE_TYPE (streamheader) == GST_TYPE_ARRAY) {
+        GArray *bufarr = g_value_peek_pointer (streamheader);
+        gint i;
+        gint offset;
+
+        if (bufarr->len == 3) {
+          GstBuffer *buf[3];
+
+          for (i = 0; i < bufarr->len; i++) {
+            GValue *bufval = &g_array_index (bufarr, GValue, i);
+
+            if (G_VALUE_TYPE (bufval) == GST_TYPE_BUFFER) {
+              buf[i] = g_value_peek_pointer (bufval);
+            }
+          }
+
+          priv_data_size = 1;
+          priv_data_size += GST_BUFFER_SIZE (buf[0]) / 0xff + 1;
+          priv_data_size += GST_BUFFER_SIZE (buf[1]) / 0xff + 1;
+
+          for (i = 0; i < 3; ++i) {
+            priv_data_size += GST_BUFFER_SIZE (buf[i]);
+          }
+
+          priv_data = g_malloc0 (priv_data_size);
+
+          priv_data[0] = 2;
+          offset = 1;
+
+          for (i = 0; i < GST_BUFFER_SIZE (buf[0]) / 0xff; ++i) {
+            priv_data[offset++] = 0xff;
+          }
+          priv_data[offset++] = GST_BUFFER_SIZE (buf[0]) % 0xff;
+
+          for (i = 0; i < GST_BUFFER_SIZE (buf[1]) / 0xff; ++i) {
+            priv_data[offset++] = 0xff;
+          }
+          priv_data[offset++] = GST_BUFFER_SIZE (buf[1]) % 0xff;
+
+          for (i = 0; i < 3; ++i) {
+            memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
+                GST_BUFFER_SIZE (buf[i]));
+            offset += GST_BUFFER_SIZE (buf[i]);
+          }
+        } else {
+          GST_WARNING_OBJECT (mux, "Vorbis header does not contain "
+              "three buffers (found %d buffers), Ignoring.", bufarr->len);
+        }
+      }
+    }
+
+    if (priv_data == NULL) {
+      GST_WARNING_OBJECT (mux,
+          "Could not write Vorbis header into codec private data. "
+          "You will probably not be able to play the stream.");
+    }
+
+    context->codec_priv_size = priv_data_size;
+    context->codec_priv = (gpointer) priv_data;
+
+    return TRUE;
+
   } else if (!strcmp (mimetype, "audio/x-ac3")) {
     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
 
-    return GST_PAD_LINK_OK;
+    return TRUE;
   } else if (!strcmp (mimetype, "audio/x-tta")) {
     gint width;
 
@@ -590,76 +773,108 @@ gst_matroska_mux_audio_pad_link (GstPad * pad, const GstCaps * caps)
     audiocontext->bitdepth = width;
     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
 
-    return GST_PAD_LINK_OK;
+    return TRUE;
   }
 
-  return GST_PAD_LINK_REFUSED;
+  return FALSE;
 }
 
-static GstPadLinkReturn
-gst_matroska_mux_subtitle_pad_link (GstPad * pad, const GstCaps * caps)
+
+/**
+ * gst_matroska_mux_subtitle_pad_setcaps:
+ * @pad: Pad which got the caps.
+ * @caps: New caps.
+ *
+ * Setcaps function for subtitle sink pad.
+ *
+ * Returns: #TRUE on success.
+ */
+static gboolean
+gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
 {
   /* Consider this as boilerplate code for now. There is
    * no single subtitle creation element in GStreamer,
    * neither do I know how subtitling works at all. */
 
-  return GST_PAD_LINK_REFUSED;
+  return FALSE;
 }
 
+
+/**
+ * gst_matroska_mux_request_new_pad:
+ * @element: #GstMatroskaMux.
+ * @templ: #GstPadTemplate.
+ * @pad_name: New pad name.
+ *
+ * Request pad function for sink templates.
+ *
+ * Returns: New #GstPad.
+ */
 static GstPad *
 gst_matroska_mux_request_new_pad (GstElement * element,
     GstPadTemplate * templ, const gchar * pad_name)
 {
   GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
   GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
-  GstPad *pad = NULL;
+  GstMatroskaPad *collect_pad;
+  GstPad *newpad = NULL;
   gchar *name = NULL;
-  GstPadLinkFunction linkfunc = NULL;
+  GstPadSetCapsFunction setcapsfunc = NULL;
   GstMatroskaTrackContext *context = NULL;
 
   if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
     name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
-    linkfunc = gst_matroska_mux_audio_pad_link;
+    setcapsfunc = 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++);
-    linkfunc = gst_matroska_mux_video_pad_link;
+    setcapsfunc = 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++);
-    linkfunc = gst_matroska_mux_subtitle_pad_link;
+    setcapsfunc = gst_matroska_mux_subtitle_pad_setcaps;
     context = (GstMatroskaTrackContext *)
         g_new0 (GstMatroskaTrackSubtitleContext, 1);
     context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
     context->name = g_strdup ("Subtitle");
   } else {
-    g_warning ("matroskamux: this is not our template!");
+    GST_WARNING_OBJECT (mux, "This is not our template!");
     return NULL;
   }
 
-  pad = gst_pad_new_from_template (templ, name);
+  newpad = gst_pad_new_from_template (templ, name);
   g_free (name);
-  gst_pad_set_link_function (pad, linkfunc);
-  gst_element_add_pad (element, pad);
-  context->index = mux->num_streams++;
-  mux->sink[context->index].track = context;
-  context->pad = pad;
+  collect_pad = (GstMatroskaPad *)
+      gst_collectpads_add_pad (mux->collect, newpad, sizeof (GstMatroskaPad));
   context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
+  collect_pad->track = context;
+  collect_pad->buffer = NULL;
 
-  return pad;
+  gst_pad_set_setcaps_function (newpad, setcapsfunc);
+  gst_element_add_pad (element, newpad);
+
+  return newpad;
 }
 
+
+/**
+ * gst_matroska_mux_track_header:
+ * @mux: #GstMatroskaMux
+ * @context: Tack context.
+ *
+ * Write a track header.
+ */
 static void
 gst_matroska_mux_track_header (GstMatroskaMux * mux,
     GstMatroskaTrackContext * context)
 {
-  GstEbmlWrite *ebml = GST_EBML_WRITE (mux);
+  GstEbmlWrite *ebml = mux->ebml_write;
   guint64 master;
 
   /* track type goes before the type-specific stuff */
@@ -740,10 +955,17 @@ gst_matroska_mux_track_header (GstMatroskaMux * mux,
   gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
 }
 
+
+/**
+ * gst_matroska_mux_start:
+ * @mux: #GstMatroskaMux
+ *
+ * Start a new matroska file (write headers etc...)
+ */
 static void
 gst_matroska_mux_start (GstMatroskaMux * mux)
 {
-  GstEbmlWrite *ebml = GST_EBML_WRITE (mux);
+  GstEbmlWrite *ebml = mux->ebml_write;
   guint32 seekhead_id[] = { GST_MATROSKA_ID_INFO,
     GST_MATROSKA_ID_TRACKS,
     GST_MATROSKA_ID_CUES,
@@ -754,7 +976,8 @@ gst_matroska_mux_start (GstMatroskaMux * mux)
     0
   };
   guint64 master, child;
-  gint i;
+  GSList *collected;
+  int i;
   guint tracknum = 1;
   gdouble duration = 0;
   guint32 *segment_uid = (guint32 *) g_malloc (16);
@@ -795,31 +1018,35 @@ 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 (i = 0; i < mux->num_streams; i++) {
-    gint64 trackduration;
-    GstFormat format = GST_FORMAT_TIME;
+  for (collected = mux->collect->data; collected;
+      collected = g_slist_next (collected)) {
+    GstMatroskaPad *collect_pad;
+    GstPad *thepad;
+    GstQuery *query;
+
+    collect_pad = (GstMatroskaPad *) collected->data;
+    thepad = collect_pad->collect.pad;
+
+    /* Query the total length of the track. */
+    query = gst_query_new_position (GST_FORMAT_TIME);
+    if (gst_pad_query (GST_PAD_PEER (thepad), query)) {
+      GstFormat format;
+      gint64 cur, trackduration;
+
+      gst_query_parse_position (query, &format, &cur, &trackduration);
 
-    if (gst_pad_query (GST_PAD_PEER (mux->sink[i].track->pad), GST_QUERY_TOTAL,
-            &format, &trackduration)) {
       if ((gdouble) trackduration > duration) {
         duration = (gdouble) trackduration;
       }
     }
+    gst_query_unref (query);
   }
   gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
       duration / mux->time_scale);
   gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
-      "GStreamer plugin version " GST_PLUGINS_VERSION);
-  if (mux->metadata
-      && gst_structure_has_field (gst_caps_get_structure (mux->metadata, 0),
-          "application")) {
-    const gchar *app;
-
-    app = gst_structure_get_string (gst_caps_get_structure (mux->metadata, 0),
-        "application");
-    if (app && app[0]) {
-      gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, app);
-    }
+      "GStreamer plugin version " GST_PLUGINS_GOOD_VERSION);
+  if (mux->writing_app && mux->writing_app[0]) {
+    gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
   }
   g_get_current_time (&time);
   gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
@@ -828,11 +1055,20 @@ gst_matroska_mux_start (GstMatroskaMux * mux)
   /* tracks */
   mux->tracks_pos = ebml->pos;
   master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
-  for (i = 0; i < mux->num_streams; i++) {
-    if (GST_PAD_IS_USABLE (mux->sink[i].track->pad)) {
-      mux->sink[i].track->num = tracknum++;
+
+  for (collected = mux->collect->data; collected;
+      collected = g_slist_next (collected)) {
+
+    GstMatroskaPad *collect_pad;
+    GstPad *thepad;
+
+    collect_pad = (GstMatroskaPad *) collected->data;
+    thepad = collect_pad->collect.pad;
+
+    if (GST_PAD_IS_USABLE (thepad)) {
+      collect_pad->track->num = tracknum++;
       child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
-      gst_matroska_mux_track_header (mux, mux->sink[i].track);
+      gst_matroska_mux_track_header (mux, collect_pad->track);
       gst_ebml_write_master_finish (ebml, child);
     }
   }
@@ -842,13 +1078,20 @@ gst_matroska_mux_start (GstMatroskaMux * mux)
   gst_ebml_write_flush_cache (ebml);
 }
 
+
+/**
+ * gst_matroska_mux_finish:
+ * @mux: #GstMatroskaMux
+ *
+ * Finish a new matroska file (write index etc...)
+ */
 static void
 gst_matroska_mux_finish (GstMatroskaMux * mux)
 {
-  GstEbmlWrite *ebml = GST_EBML_WRITE (mux);
+  GstEbmlWrite *ebml = mux->ebml_write;
   guint64 pos;
   guint64 duration = 0;
-  gint i;
+  GSList *collected;
 
   /* finish last cluster */
   if (mux->cluster) {
@@ -951,12 +1194,17 @@ gst_matroska_mux_finish (GstMatroskaMux * mux)
 
   /* update duration */
   /* first get the overall duration */
-  for (i = 0; i < mux->num_streams; i++) {
-    if (mux->sink[i].duration > duration)
-      duration = mux->sink[i].duration;
+  for (collected = mux->collect->data; collected;
+      collected = g_slist_next (collected)) {
+    GstMatroskaPad *collect_pad;
+
+    collect_pad = (GstMatroskaPad *) collected->data;
+
+    if (collect_pad->duration > duration)
+      duration = collect_pad->duration;
   }
   if (duration != 0) {
-    pos = GST_EBML_WRITE (mux)->pos;
+    pos = mux->ebml_write->pos;
     gst_ebml_write_seek (ebml, mux->duration_pos);
     gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
         (gdouble) duration / mux->time_scale);
@@ -967,59 +1215,77 @@ gst_matroska_mux_finish (GstMatroskaMux * mux)
   gst_ebml_write_master_finish (ebml, mux->segment_pos);
 }
 
-static gint
-gst_matroska_mux_prepare_data (GstMatroskaMux * mux)
+
+/**
+ * gst_matroska_mux_best_pad:
+ * @mux: #GstMatroskaMux
+ *
+ * Find a pad with the oldest data 
+ * (data from this pad should be written first).
+ *
+ * Returns: Selected pad.
+ */
+static GstMatroskaPad *
+gst_matroska_mux_best_pad (GstMatroskaMux * mux)
 {
-  gint i, first = -1;
-
-  for (i = 0; i < mux->num_streams; i++) {
-    while (!mux->sink[i].eos && !mux->sink[i].buffer &&
-        mux->sink[i].track->num > 0 &&
-        GST_PAD_IS_USABLE (mux->sink[i].track->pad)) {
-      GstData *data;
-
-      data = gst_pad_pull (mux->sink[i].track->pad);
-      if (GST_IS_EVENT (data)) {
-        if (GST_EVENT_TYPE (GST_EVENT (data)) == GST_EVENT_EOS)
-          mux->sink[i].eos = TRUE;
-        gst_event_unref (GST_EVENT (data));
-      } else {
-        mux->sink[i].buffer = GST_BUFFER (data);
-      }
+  GSList *collected;
+  GstMatroskaPad *best = NULL;
+
+  for (collected = mux->collect->data; collected;
+      collected = g_slist_next (collected)) {
+    GstMatroskaPad *collect_pad;
+
+    collect_pad = (GstMatroskaPad *) collected->data;
+    /* fetch a new buffer if needed */
+    if (collect_pad->buffer == NULL) {
+      collect_pad->buffer = gst_collectpads_pop (mux->collect,
+          (GstCollectData *) collect_pad);
     }
 
-    if (mux->sink[i].buffer) {
-      if (first < 0 || GST_BUFFER_TIMESTAMP (mux->sink[i].buffer) <
-          GST_BUFFER_TIMESTAMP (mux->sink[first].buffer))
-        first = i;
+    /* if we have a buffer check if it is better then the current best one */
+    if (collect_pad->buffer != NULL) {
+      if (best == NULL
+          || GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
+          GST_BUFFER_TIMESTAMP (best->buffer)) {
+        best = collect_pad;
+      }
     }
   }
 
-  return first;
+  return best;
 }
 
-static void
+/**
+ * gst_matroska_mux_write_data:
+ * @mux: #GstMatroskaMux
+ *
+ * Write collected data (called from gst_matroska_mux_collected).
+ *
+ * Returns: Result of the gst_pad_push issued to write the data.
+ */
+static GstFlowReturn
 gst_matroska_mux_write_data (GstMatroskaMux * mux)
 {
-  GstEbmlWrite *ebml = GST_EBML_WRITE (mux);
+  GstMatroskaPad *best;
+  GstEbmlWrite *ebml = mux->ebml_write;
   GstBuffer *buf, *hdr;
-  gint i;
   guint64 cluster, blockgroup;
 
-  /* which stream-num to write from? */
-  if ((i = gst_matroska_mux_prepare_data (mux)) < 0) {
-    GstEvent *event = gst_event_new (GST_EVENT_EOS);
+  /* which stream to write from? */
+  best = gst_matroska_mux_best_pad (mux);
 
+  /* if there is no best pad, we have reached EOS */
+  if (best == NULL) {
+    GST_DEBUG ("No best pad finishing...");
     gst_matroska_mux_finish (mux);
-    gst_pad_push (mux->srcpad, GST_DATA (event));
-    gst_element_set_eos (GST_ELEMENT (mux));
-
-    return;
+    gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
+    return GST_FLOW_WRONG_STATE;
   }
+  GST_DEBUG ("Best pad %s.", gst_pad_get_name (best->collect.pad));
 
   /* write data */
-  buf = mux->sink[i].buffer;
-  mux->sink[i].buffer = NULL;
+  buf = best->buffer;
+  best->buffer = NULL;
 
   if (mux->cluster) {
     /* start a new cluster every two seconds */
@@ -1064,15 +1330,15 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux)
 
   /* update duration of this track */
   if (GST_BUFFER_DURATION_IS_VALID (buf))
-    mux->sink[i].duration += GST_BUFFER_DURATION (buf);
+    best->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... */
-  if (mux->sink[i].track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
-      GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_KEY_UNIT)) {
+  if (best->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
+      !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
     GstMatroskaIndex *idx;
 
     if (mux->num_indexes % 32 == 0) {
@@ -1083,8 +1349,8 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux)
 
     idx->pos = mux->cluster_pos;
     idx->time = GST_BUFFER_TIMESTAMP (buf);
-    idx->track = mux->sink[i].track->num;
-  } else if ((mux->sink[i].track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
+    idx->track = best->track->num;
+  } else if ((best->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
       (mux->num_streams == 1)) {
     GstMatroskaIndex *idx;
 
@@ -1096,7 +1362,7 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux)
 
     idx->pos = mux->cluster_pos;
     idx->time = GST_BUFFER_TIMESTAMP (buf);
-    idx->track = mux->sink[i].track->num;
+    idx->track = best->track->num;
   }
 
   /* write one blockgroup with one block with
@@ -1107,7 +1373,7 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux)
       GST_BUFFER_SIZE (buf) + 4);
   hdr = gst_buffer_new_and_alloc (4);
   /* track num - FIXME: what if num >= 0x80 (unlikely)? */
-  GST_BUFFER_DATA (hdr)[0] = mux->sink[i].track->num | 0x80;
+  GST_BUFFER_DATA (hdr)[0] = best->track->num | 0x80;
   /* time relative to clustertime */
   *(guint16 *) & GST_BUFFER_DATA (hdr)[1] = GUINT16_TO_BE (
       (GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time) / mux->time_scale);
@@ -1118,35 +1384,38 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux)
   if (GST_BUFFER_DURATION_IS_VALID (buf)) {
     guint64 block_duration = GST_BUFFER_DURATION (buf);
 
-    if (block_duration != mux->sink[i].track->default_duration) {
+    if (block_duration != best->track->default_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);
 }
 
-static void
-gst_matroska_mux_loop (GstElement * element)
+
+/**
+ * gst_matroska_mux_collected:
+ * @pads: #GstCollectPads
+ * @uuser_data: #GstMatroskaMux
+ *
+ * Collectpads callback.
+ *
+ * Returns: #GstFlowReturn
+ */
+static GstFlowReturn
+gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
 {
-  GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
-  guint i;
+  GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
 
-  if (gst_matroska_mux_prepare_data (mux) == -1) {
-    GST_ELEMENT_ERROR (element, STREAM, MUX, (NULL), ("No data"));
-    return;
-  }
+  GST_DEBUG ("Collected pads");
 
   /* start with a header */
   if (mux->state == GST_MATROSKA_MUX_STATE_START) {
-    if (mux->num_streams == 0) {
-      return;
-    }
-    for (i = 0; i < mux->num_streams; i++) {
-      if (!gst_pad_is_negotiated (mux->sink[i].track->pad)) {
-        return;
-      } else {
-      }
+    if (mux->collect->data == NULL) {
+      GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
+          ("No input streams configured"));
+      return GST_FLOW_ERROR;
     }
     mux->state = GST_MATROSKA_MUX_STATE_HEADER;
     gst_matroska_mux_start (mux);
@@ -1154,27 +1423,53 @@ gst_matroska_mux_loop (GstElement * element)
   }
 
   /* do one single buffer */
-  gst_matroska_mux_write_data (mux);
+  return gst_matroska_mux_write_data (mux);
 }
 
+
+/**
+ * gst_matroska_mux_change_state:
+ * @element: #GstMatroskaMux
+ * @transition: State change transition.
+ *
+ * Change the muxer state.
+ *
+ * Returns: #GstStateChangeReturn
+ */
 static GstStateChangeReturn
 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
 {
+  GstStateChangeReturn ret;
   GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
 
   switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      gst_collectpads_start (mux->collect);
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+      break;
+    default:
+      break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+      break;
     case GST_STATE_CHANGE_PAUSED_TO_READY:
+      gst_collectpads_stop (mux->collect);
       gst_matroska_mux_reset (GST_ELEMENT (mux));
       break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      break;
     default:
       break;
   }
 
-  if (((GstElementClass *) parent_class)->change_state)
-    return ((GstElementClass *) parent_class)->change_state (element,
-        transition);
-
-  return GST_STATE_CHANGE_SUCCESS;
+  return ret;
 }
 
 static void
@@ -1187,8 +1482,13 @@ gst_matroska_mux_set_property (GObject * object,
   mux = GST_MATROSKA_MUX (object);
 
   switch (prop_id) {
-    case ARG_METADATA:
-      gst_caps_replace (&mux->metadata, g_value_get_boxed (value));
+    case ARG_WRITING_APP:
+      if (!g_value_get_string (value)) {
+        GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
+        break;
+      }
+      g_free (mux->writing_app);
+      mux->writing_app = g_strdup (g_value_get_string (value));
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1206,8 +1506,8 @@ gst_matroska_mux_get_property (GObject * object,
   mux = GST_MATROSKA_MUX (object);
 
   switch (prop_id) {
-    case ARG_METADATA:
-      g_value_set_boxed (value, mux->metadata);
+    case ARG_WRITING_APP:
+      g_value_set_string (value, mux->writing_app);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
index ce4c327..15f113c 100644 (file)
@@ -1,5 +1,6 @@
 /* GStreamer Matroska muxer/demuxer
  * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * (c) 2005 Michal Benes <michal.benes@xeris.cz>
  *
  * matroska-mux.h: matroska file/stream muxer object types
  *
@@ -23,6 +24,7 @@
 #define __GST_MATROSKA_MUX_H__
 
 #include <gst/gst.h>
+#include <gst/base/gstcollectpads.h>
 
 #include "ebml-write.h"
 #include "matroska-ids.h"
@@ -40,8 +42,6 @@ G_BEGIN_DECLS
 #define GST_IS_MATROSKA_MUX_CLASS(obj) \
   (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MATROSKA_MUX))
 
-#define GST_MATROSKA_MUX_MAX_STREAMS 64
-
 typedef struct _BITMAPINFOHEADER {
   guint32 bi_size;
   guint32 bi_width;
@@ -67,22 +67,32 @@ typedef struct _GstMatroskaMetaSeekIndex {
   guint64  pos;
 } GstMatroskaMetaSeekIndex;
 
+/* all information needed for one matroska stream */
+typedef struct
+{
+  GstCollectData collect;       /* we extend the CollectData */
+  GstMatroskaTrackContext *track;
+
+  GstBuffer *buffer;            /* the queued buffer for this pad */
+
+  guint64 duration;
+}
+GstMatroskaPad;
+
+
 typedef struct _GstMatroskaMux {
-  GstEbmlWrite   parent;
+  GstElement     element;
 
   /* pads */
   GstPad       *srcpad;
-  struct {
-    GstMatroskaTrackContext *track;
-    GstBuffer   *buffer;
-    gboolean     eos;
-    guint64      duration;
-  } sink[GST_MATROSKA_MUX_MAX_STREAMS];
+  GstCollectPads *collect;
+  GstEbmlWrite *ebml_write;
+
   guint          num_streams,
                  num_v_streams, num_a_streams, num_t_streams;
 
-  /* metadata - includes writing_app and creation_time */
-  GstCaps      *metadata;
+  /* Application name (for the writing application header element) */
+  gchar          *writing_app;
 
   /* state */
   GstMatroskaMuxState state;
@@ -99,15 +109,13 @@ typedef struct _GstMatroskaMux {
 
   /* byte-positions of master-elements (for replacing contents) */
   guint64        segment_pos,
-                seekhead_pos,
-                cues_pos,
-#if 0
-                tags_pos,
-#endif
-                info_pos,
-                tracks_pos,
-                duration_pos,
-     meta_pos;
+                 seekhead_pos,
+                 cues_pos,
+                 /* tags_pos, */
+                 info_pos,
+                 tracks_pos,
+                 duration_pos,
+                 meta_pos;
   guint64        segment_master;
 
   /* current cluster */
@@ -121,7 +129,7 @@ typedef struct _GstMatroskaMux {
 } GstMatroskaMux;
 
 typedef struct _GstMatroskaMuxClass {
-  GstEbmlWriteClass parent;
+  GstElementClass parent;
 } GstMatroskaMuxClass;
 
 GType    gst_matroska_mux_get_type    (void);
index 67b5f26..aceb340 100644 (file)
 #include "config.h"
 #endif
 
-#include "matroska-demux.h"
+/* #include "matroska-demux.h" */
 #include "matroska-mux.h"
 
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
-  return (gst_matroska_demux_plugin_init (plugin) &&
-      gst_matroska_mux_plugin_init (plugin));
+  return                        /* gst_matroska_demux_plugin_init (plugin) && */
+      gst_matroska_mux_plugin_init (plugin);
 }
 
 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,