From: Tim-Philipp Müller Date: Fri, 14 Oct 2005 12:43:30 +0000 (+0000) Subject: Port matroska muxer to 0.9 (#318847). X-Git-Tag: RELEASE-0_9_4~27 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=22b9a5cd4310a0c4b4ab1508c0ac8fb6a951f061;p=platform%2Fupstream%2Fgst-plugins-good.git Port matroska muxer to 0.9 (#318847). Original commit message from CVS: Reviewed by: Tim-Philipp Müller * 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). --- diff --git a/ChangeLog b/ChangeLog index 8725fa6..f36f5fa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2005-10-14 Michal Benes + + Reviewed by: Tim-Philipp Müller + + * 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 * ext/speex/gstspeexenc.c: (gst_speexenc_get_tag_value), diff --git a/configure.ac b/configure.ac index 3bdcb40..f3ee637 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/gst/matroska/Makefile.am b/gst/matroska/Makefile.am index 78f2c91..1dd6e0c 100644 --- a/gst/matroska/Makefile.am +++ b/gst/matroska/Makefile.am @@ -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) diff --git a/gst/matroska/ebml-ids.h b/gst/matroska/ebml-ids.h index 0dd120c..db547cd 100644 --- a/gst/matroska/ebml-ids.h +++ b/gst/matroska/ebml-ids.h @@ -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 diff --git a/gst/matroska/ebml-write.c b/gst/matroska/ebml-write.c index 139c4d0..27bc6b5 100644 --- a/gst/matroska/ebml-write.c +++ b/gst/matroska/ebml-write.c @@ -1,5 +1,6 @@ /* GStreamer EBML I/O * (c) 2003 Ronald Bultje + * (c) 2005 Michal Benes * * ebml-write.c: write EBML data to file/stream * @@ -28,85 +29,116 @@ #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) { diff --git a/gst/matroska/ebml-write.h b/gst/matroska/ebml-write.h index 43d2f8b..01a9992 100644 --- a/gst/matroska/ebml-write.h +++ b/gst/matroska/ebml-write.h @@ -1,5 +1,6 @@ /* GStreamer EBML I/O * (c) 2003 Ronald Bultje + * (c) 2005 Michal Benes * * 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. diff --git a/gst/matroska/matroska-ids.h b/gst/matroska/matroska-ids.h index ec87fa5..a0895a9 100644 --- a/gst/matroska/matroska-ids.h +++ b/gst/matroska/matroska-ids.h @@ -132,6 +132,7 @@ #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" @@ -151,6 +152,7 @@ #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__ */ diff --git a/gst/matroska/matroska-mux.c b/gst/matroska/matroska-mux.c index ab1fec4..044a62c 100644 --- a/gst/matroska/matroska-mux.c +++ b/gst/matroska/matroska-mux.c @@ -1,5 +1,6 @@ /* GStreamer Matroska muxer/demuxer * (c) 2003 Ronald Bultje + * (c) 2005 Michal Benes * * 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); diff --git a/gst/matroska/matroska-mux.h b/gst/matroska/matroska-mux.h index ce4c327..15f113c 100644 --- a/gst/matroska/matroska-mux.h +++ b/gst/matroska/matroska-mux.h @@ -1,5 +1,6 @@ /* GStreamer Matroska muxer/demuxer * (c) 2003 Ronald Bultje + * (c) 2005 Michal Benes * * matroska-mux.h: matroska file/stream muxer object types * @@ -23,6 +24,7 @@ #define __GST_MATROSKA_MUX_H__ #include +#include #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); diff --git a/gst/matroska/matroska.c b/gst/matroska/matroska.c index 67b5f26..aceb340 100644 --- a/gst/matroska/matroska.c +++ b/gst/matroska/matroska.c @@ -23,14 +23,14 @@ #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,