diff options
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | gst/matroska/Makefile.am | 5 | ||||
-rw-r--r-- | gst/matroska/ebml-read.c | 468 | ||||
-rw-r--r-- | gst/matroska/ebml-read.h | 103 | ||||
-rw-r--r-- | gst/matroska/matroska-demux.c | 2228 | ||||
-rw-r--r-- | gst/matroska/matroska-demux.h | 46 | ||||
-rw-r--r-- | gst/matroska/matroska.c | 4 |
7 files changed, 1656 insertions, 1207 deletions
@@ -1,3 +1,12 @@ +2005-10-18 Tim-Philipp Müller <tim at centricular dot net> + * gst/matroska/Makefile.am: + * gst/matroska/ebml-read.c: + * gst/matroska/ebml-read.h: + * gst/matroska/matroska-demux.c: + * gst/matroska/matroska-demux.h: + * gst/matroska/matroska.c: (plugin_init): + Ported matroska demuxer to 0.9. + 2005-10-18 Michal Benes <michal dot benes at xeris dot cz> Reviewed by: Tim-Philipp Müller <tim at centricular dot net> diff --git a/gst/matroska/Makefile.am b/gst/matroska/Makefile.am index 1dd6e0c7..e4e58bb4 100644 --- a/gst/matroska/Makefile.am +++ b/gst/matroska/Makefile.am @@ -1,13 +1,12 @@ 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 \ diff --git a/gst/matroska/ebml-read.c b/gst/matroska/ebml-read.c index 707910b3..02d36d97 100644 --- a/gst/matroska/ebml-read.c +++ b/gst/matroska/ebml-read.c @@ -31,23 +31,24 @@ GST_DEBUG_CATEGORY_STATIC (ebmlread_debug); #define GST_CAT_DEFAULT ebmlread_debug -enum -{ - /* FILL ME */ - LAST_SIGNAL -}; - static void gst_ebml_read_class_init (GstEbmlReadClass * klass); static void gst_ebml_read_init (GstEbmlRead * ebml); static GstStateChangeReturn gst_ebml_read_change_state (GstElement * element, GstStateChange transition); -static GstElementClass *parent_class = NULL; +/* convenience functions */ +static gboolean gst_ebml_read_peek_bytes (GstEbmlRead * ebml, guint size, + GstBuffer ** p_buf); +static gboolean gst_ebml_read_pull_bytes (GstEbmlRead * ebml, guint size, + GstBuffer ** p_buf); + + +static GstElementClass *parent_class; /* NULL */ GType gst_ebml_read_get_type (void) { - static GType gst_ebml_read_type = 0; + static GType gst_ebml_read_type; /* 0 */ if (!gst_ebml_read_type) { static const GTypeInfo gst_ebml_read_info = { @@ -75,111 +76,58 @@ gst_ebml_read_class_init (GstEbmlReadClass * klass) { GstElementClass *gstelement_class = (GstElementClass *) klass; - parent_class = g_type_class_ref (GST_TYPE_ELEMENT); + parent_class = g_type_class_peek_parent (klass); GST_DEBUG_CATEGORY_INIT (ebmlread_debug, "ebmlread", 0, "EBML stream helper class"); - gstelement_class->change_state = gst_ebml_read_change_state; + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_ebml_read_change_state); } static void gst_ebml_read_init (GstEbmlRead * ebml) { ebml->sinkpad = NULL; - ebml->bs = NULL; ebml->level = NULL; - ebml->id_cache = 0; } static GstStateChangeReturn gst_ebml_read_change_state (GstElement * element, GstStateChange transition) { + GstStateChangeReturn ret; GstEbmlRead *ebml = GST_EBML_READ (element); switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: - if (!ebml->sinkpad) - return GST_STATE_CHANGE_FAILURE; - ebml->bs = gst_bytestream_new (ebml->sinkpad); - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_bytestream_destroy (ebml->bs); - while (ebml->level) { - GstEbmlLevel *level = ebml->level->data; - - ebml->level = g_list_remove (ebml->level, level); - g_free (level); + if (!ebml->sinkpad) { + g_return_val_if_reached (GST_STATE_CHANGE_FAILURE); } break; default: break; } - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - return GST_STATE_CHANGE_SUCCESS; -} - -/* - * Event handler. Basic: - * - EOS: end-of-file, stop processing, forward EOS. - * - Interrupt: stop processing. - * - Discont: shouldn't be handled here but in the seek handler. Error. - * - Flush: ignore, since we check for flush flags manually. Don't forward. - * - Others: warn, ignore. - * Return value indicates whether to continue processing. - */ - -static gboolean -gst_ebml_read_use_event (GstEbmlRead * ebml, GstEvent * event) -{ - if (!event) { - GST_ELEMENT_ERROR (ebml, RESOURCE, READ, (NULL), (NULL)); - return FALSE; - } - - GST_LOG ("Received event of type %d", GST_EVENT_TYPE (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_EOS: - gst_pad_event_default (ebml->sinkpad, event); - return FALSE; - - case GST_EVENT_INTERRUPT: - gst_event_unref (event); - return FALSE; - - case GST_EVENT_DISCONTINUOUS: - GST_WARNING_OBJECT (ebml, "Unexpected discont - might lose sync"); - gst_event_unref (event); - return TRUE; - - case GST_EVENT_FLUSH: - gst_event_unref (event); - return TRUE; + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + { + g_list_foreach (ebml->level, (GFunc) g_free, NULL); + g_list_free (ebml->level); + ebml->level = NULL; + if (ebml->cached_buffer) { + gst_buffer_unref (ebml->cached_buffer); + ebml->cached_buffer = NULL; + } + ebml->offset = 0; + break; + } default: - GST_WARNING ("don't know how to handle event %d", GST_EVENT_TYPE (event)); - gst_pad_event_default (ebml->sinkpad, event); - return FALSE; + break; } - /* happy */ - g_assert_not_reached (); - return FALSE; -} - -static gboolean -gst_ebml_read_handle_event (GstEbmlRead * ebml) -{ - GstEvent *event = NULL; - guint32 remaining; - - gst_bytestream_get_status (ebml->bs, &remaining, &event); - - return gst_ebml_read_use_event (ebml, event); + return ret; } /* @@ -193,7 +141,7 @@ static guint gst_ebml_read_element_level_up (GstEbmlRead * ebml) { guint num = 0; - guint64 pos = gst_bytestream_tell (ebml->bs); + guint64 pos = ebml->offset; while (ebml->level != NULL) { GList *last = g_list_last (ebml->level); @@ -203,67 +151,129 @@ gst_ebml_read_element_level_up (GstEbmlRead * ebml) ebml->level = g_list_remove (ebml->level, level); g_free (level); num++; - } else + } else { break; + } } return num; } /* + * Calls pull_range for (offset,size) without advancing our offset + */ +static gboolean +gst_ebml_read_peek_bytes (GstEbmlRead * ebml, guint size, GstBuffer ** p_buf) +{ + GstFlowReturn ret; + + /* Caching here actually makes much less difference than one would expect. + * We do it mainly to avoid pulling buffers of 1 byte all the time */ + if (ebml->cached_buffer) { + guint64 cache_offset = GST_BUFFER_OFFSET (ebml->cached_buffer); + guint cache_size = GST_BUFFER_SIZE (ebml->cached_buffer); + + if (cache_offset <= ebml->offset && + (ebml->offset + size) < (cache_offset + cache_size)) { + *p_buf = gst_buffer_create_sub (ebml->cached_buffer, + ebml->offset - cache_offset, size); + return TRUE; + } + gst_buffer_unref (ebml->cached_buffer); + ebml->cached_buffer = NULL; + } + + if (gst_pad_pull_range (ebml->sinkpad, ebml->offset, MAX (size, 64 * 1024), + &ebml->cached_buffer) == GST_FLOW_OK && + GST_BUFFER_SIZE (ebml->cached_buffer) >= size) { + *p_buf = gst_buffer_create_sub (ebml->cached_buffer, 0, size); + return TRUE; + } + + ret = gst_pad_pull_range (ebml->sinkpad, ebml->offset, size, p_buf); + if (ret != GST_FLOW_OK) { + GST_DEBUG ("pull_range returned %d", ret); + return FALSE; + } + + if (GST_BUFFER_SIZE (*p_buf) < size) { + GST_WARNING_OBJECT (ebml, "Dropping short buffer at offset %" + G_GUINT64_FORMAT ": wanted %u bytes, got %u bytes", ebml->offset, + size, GST_BUFFER_SIZE (*p_buf)); + gst_buffer_unref (*p_buf); + *p_buf = NULL; + return FALSE; + } + + return TRUE; +} + +/* + * Calls pull_range for (offset,size) and advances our offset by size + */ +static gboolean +gst_ebml_read_pull_bytes (GstEbmlRead * ebml, guint size, GstBuffer ** p_buf) +{ + if (!gst_ebml_read_peek_bytes (ebml, size, p_buf)) + return FALSE; + + ebml->offset += size; + return TRUE; +} + +/* * Read: the element content data ID. - * Return: the number of bytes read or -1 on error. + * Return: FALSE on error. */ -static gint +static gboolean gst_ebml_read_element_id (GstEbmlRead * ebml, guint32 * id, guint * level_up) { - guint8 *data; + GstBuffer *buf; gint len_mask = 0x80, read = 1, n = 1; guint32 total; + guint8 b; - if (ebml->id_cache) { - *id = ebml->id_cache; - if (level_up) - *level_up = 0; + if (!gst_ebml_read_peek_bytes (ebml, 1, &buf)) + return FALSE; - return 0; - } + b = GST_READ_UINT8 (GST_BUFFER_DATA (buf)); + gst_buffer_unref (buf); + + total = (guint32) b; - while (gst_bytestream_peek_bytes (ebml->bs, &data, 1) != 1) { - if (!gst_ebml_read_handle_event (ebml)) - return -1; - } - total = data[0]; while (read <= 4 && !(total & len_mask)) { read++; len_mask >>= 1; } if (read > 4) { - guint64 pos = gst_bytestream_tell (ebml->bs); + guint64 pos = ebml->offset; GST_ELEMENT_ERROR (ebml, STREAM, DEMUX, (NULL), ("Invalid EBML ID size tag (0x%x) at position %llu (0x%llx)", - data[0], pos, pos)); - return -1; + (guint) b, pos, pos)); + return FALSE; } - while (gst_bytestream_peek_bytes (ebml->bs, &data, read) != read) { - if (!gst_ebml_read_handle_event (ebml)) - return -1; + if (!gst_ebml_read_peek_bytes (ebml, read, &buf)) + return FALSE; + + while (n < read) { + b = GST_READ_UINT8 (GST_BUFFER_DATA (buf) + n); + total = (total << 8) | b; + ++n; } - while (n < read) - total = (total << 8) | data[n++]; - *id = ebml->id_cache = total; + *id = total; /* level */ if (level_up) *level_up = gst_ebml_read_element_level_up (ebml); - gst_bytestream_flush_fast (ebml->bs, read); + gst_buffer_unref (buf); - return 0; + ebml->offset += read; + return TRUE; } /* @@ -274,67 +284,56 @@ gst_ebml_read_element_id (GstEbmlRead * ebml, guint32 * id, guint * level_up) static gint gst_ebml_read_element_length (GstEbmlRead * ebml, guint64 * length) { - guint8 *data; + GstBuffer *buf; gint len_mask = 0x80, read = 1, n = 1, num_ffs = 0; guint64 total; + guint8 b; + + if (!gst_ebml_read_peek_bytes (ebml, 1, &buf)) + return -1; + + b = GST_READ_UINT8 (GST_BUFFER_DATA (buf)); + gst_buffer_unref (buf); + + total = (guint64) b; - while (gst_bytestream_peek_bytes (ebml->bs, &data, 1) != 1) { - if (!gst_ebml_read_handle_event (ebml)) - return -1; - } - total = data[0]; while (read <= 8 && !(total & len_mask)) { read++; len_mask >>= 1; } if (read > 8) { - guint64 pos = gst_bytestream_tell (ebml->bs); + guint64 pos = ebml->offset; GST_ELEMENT_ERROR (ebml, STREAM, DEMUX, (NULL), ("Invalid EBML length size tag (0x%x) at position %llu (0x%llx)", - data[0], pos, pos)); + (guint) b, pos, pos)); return -1; } if ((total &= (len_mask - 1)) == len_mask - 1) num_ffs++; - while (gst_bytestream_peek_bytes (ebml->bs, &data, read) != read) { - if (!gst_ebml_read_handle_event (ebml)) - return -1; - } + + if (!gst_ebml_read_peek_bytes (ebml, read, &buf)) + return -1; + while (n < read) { - if (data[n] == 0xff) + guint8 b = GST_READ_UINT8 (GST_BUFFER_DATA (buf) + n); + + if (b == 0xff) num_ffs++; - total = (total << 8) | data[n]; - n++; + total = (total << 8) | b; + ++n; } + gst_buffer_unref (buf); if (read == num_ffs) *length = G_MAXUINT64; else *length = total; - return read; -} + ebml->offset += read; -/* - * Read: the actual data. - * Return: the data, as a GstBuffer. - */ - -static GstBuffer * -gst_ebml_read_element_data (GstEbmlRead * ebml, guint64 length) -{ - GstBuffer *buf = NULL; - - if (gst_bytestream_peek (ebml->bs, &buf, length) != length) { - if (!gst_ebml_read_handle_event (ebml)) - return NULL; - } - - gst_bytestream_flush_fast (ebml->bs, length); - - return buf; + return read; } /* @@ -343,149 +342,73 @@ gst_ebml_read_element_data (GstEbmlRead * ebml, guint64 length) * next element lies higher than the previous one. */ -guint32 -gst_ebml_peek_id (GstEbmlRead * ebml, guint * level_up) +gboolean +gst_ebml_peek_id (GstEbmlRead * ebml, guint * level_up, guint32 * id) { - guint32 id; + guint64 off; g_assert (level_up); - if (gst_ebml_read_element_id (ebml, &id, level_up) < 0) - return 0; + off = ebml->offset; /* save offset */ + + if (!gst_ebml_read_element_id (ebml, id, level_up)) + return FALSE; - return id; + ebml->offset = off; /* restore offset */ + return TRUE; } /* - * Seek to a given offset. + * Return the length of the stream in bytes */ -GstEvent * -gst_ebml_read_seek (GstEbmlRead * ebml, guint64 offset) +gint64 +gst_ebml_read_get_length (GstEbmlRead * ebml) { - guint32 remaining; - GstEvent *event = NULL; - guchar *data; - - /* first, flush remaining buffers */ - gst_bytestream_get_status (ebml->bs, &remaining, &event); - if (event) { - GST_WARNING ("Unexpected event before seek"); - if (!gst_ebml_read_use_event (ebml, event)) - return NULL; - event = NULL; - } - if (remaining) - gst_bytestream_flush_fast (ebml->bs, remaining); - - /* now seek */ - if (!gst_bytestream_seek (ebml->bs, offset, GST_SEEK_METHOD_SET)) { - GST_ELEMENT_ERROR (ebml, RESOURCE, SEEK, (NULL), - ("Seek to position %llu (0x%llx) failed", offset, offset)); - return NULL; - } + GstFormat fmt = GST_FORMAT_BYTES; + gint64 pos, end; - while (!event) { - /* and now, peek a new byte. This will fail because there's a - * pending event. Then, take the event and return it. */ - if (gst_bytestream_peek_bytes (ebml->bs, &data, 1)) { - GST_WARNING ("Unexpected data after seek - this means seek failed"); - break; - } + if (!gst_pad_query_position (GST_PAD_PEER (ebml->sinkpad), &fmt, &pos, &end)) + g_return_val_if_reached (0); ///// FIXME ///////// - /* get the discont event and return */ - gst_bytestream_get_status (ebml->bs, &remaining, &event); - if (!event) { - GST_WARNING ("No discontinuity event after seek - seek failed"); - break; - } else if (GST_EVENT_TYPE (event) != GST_EVENT_DISCONTINUOUS) { - if (!gst_ebml_read_use_event (ebml, event)) - return NULL; - event = NULL; - } - } + if (fmt != GST_FORMAT_BYTES || end < 0) + g_return_val_if_reached (0); ///// FIXME ///////// - /* at the end of a seek, we have no cached ID anymore */ - ebml->id_cache = 0; - - return event; + return end; } /* - * Skip the next element. + * Seek to a given offset. */ gboolean -gst_ebml_read_skip (GstEbmlRead * ebml) +gst_ebml_read_seek (GstEbmlRead * ebml, guint64 offset) { - gint bytes; - guint32 id, remaining; - guint64 length; - GstEvent *event; - - if ((bytes = gst_ebml_read_element_id (ebml, &id, NULL)) < 0) - return FALSE; - gst_bytestream_flush_fast (ebml->bs, bytes); - - if ((bytes = gst_ebml_read_element_length (ebml, &length)) < 0) - return FALSE; - gst_bytestream_flush_fast (ebml->bs, bytes); - - /* do we have enough bytes left to skip? */ - gst_bytestream_get_status (ebml->bs, &remaining, &event); - if (event) { - g_warning ("Unexpected event before skip"); - if (!gst_ebml_read_use_event (ebml, event)) - return FALSE; - } - - if (remaining >= length) { - ebml->id_cache = 0; - gst_bytestream_flush_fast (ebml->bs, length); - - return TRUE; - } - - if (!(event = gst_ebml_read_seek (ebml, - gst_bytestream_tell (ebml->bs) + length))) { + if (offset >= gst_ebml_read_get_length (ebml)) return FALSE; - } - gst_event_unref (event); - ebml->id_cache = 0; + ebml->offset = offset; return TRUE; } /* - * Make sure there's one whole next chunk available - * for uninterrupted reading. + * Skip the next element. */ gboolean -gst_ebml_read_reserve (GstEbmlRead * ebml) +gst_ebml_read_skip (GstEbmlRead * ebml) { guint64 length; - gint bytes; - guint8 *data; guint32 id; - /* make sure the ID is cached */ - if (gst_ebml_read_element_id (ebml, &id, NULL) < 0) + if (!gst_ebml_read_element_id (ebml, &id, NULL)) return FALSE; - /* now we can peek the length */ - if ((bytes = gst_ebml_read_element_length (ebml, &length)) < 0) + if (gst_ebml_read_element_length (ebml, &length) < 0) return FALSE; - length += bytes; - /* now check if we have that amount available */ - while (gst_bytestream_peek_bytes (ebml->bs, &data, length) != length) { - if (!gst_ebml_read_handle_event (ebml)) - return FALSE; - } - - /* yes, available */ + ebml->offset += length; return TRUE; } @@ -496,25 +419,24 @@ gst_ebml_read_reserve (GstEbmlRead * ebml) gboolean gst_ebml_read_buffer (GstEbmlRead * ebml, guint32 * id, GstBuffer ** buf) { - gint bytes; guint64 length; - if ((bytes = gst_ebml_read_element_id (ebml, id, NULL)) < 0) + if (!gst_ebml_read_element_id (ebml, id, NULL)) return FALSE; - gst_bytestream_flush_fast (ebml->bs, bytes); - - if ((bytes = gst_ebml_read_element_length (ebml, &length)) < 0) + if (gst_ebml_read_element_length (ebml, &length) < 0) return FALSE; - gst_bytestream_flush_fast (ebml->bs, bytes); - ebml->id_cache = 0; if (length == 0) { *buf = gst_buffer_new (); return TRUE; } - return ((*buf = gst_ebml_read_element_data (ebml, length)) != NULL); + *buf = NULL; + if (!gst_ebml_read_pull_bytes (ebml, (guint) length, buf)) + return FALSE; + + return TRUE; } /* @@ -686,7 +608,17 @@ gst_ebml_read_ascii (GstEbmlRead * ebml, guint32 * id, gchar ** str) gboolean gst_ebml_read_utf8 (GstEbmlRead * ebml, guint32 * id, gchar ** str) { - return gst_ebml_read_ascii (ebml, id, str); + gboolean ret; + guint64 oldoff = ebml->offset; + + ret = gst_ebml_read_ascii (ebml, id, str); + + if (str != NULL && *str != NULL && **str != '\0' && + !g_utf8_validate (*str, -1, NULL)) { + GST_WARNING ("Invalid UTF-8 string at offset %" G_GUINT64_FORMAT, oldoff); + } + + return ret; } /* @@ -712,24 +644,20 @@ gst_ebml_read_date (GstEbmlRead * ebml, guint32 * id, gint64 * date) gboolean gst_ebml_read_master (GstEbmlRead * ebml, guint32 * id) { - gint bytes; - guint64 length; GstEbmlLevel *level; + guint64 length; - if ((bytes = gst_ebml_read_element_id (ebml, id, NULL)) < 0) + if (!gst_ebml_read_element_id (ebml, id, NULL)) return FALSE; - gst_bytestream_flush_fast (ebml->bs, bytes); - if ((bytes = gst_ebml_read_element_length (ebml, &length)) < 0) + if (gst_ebml_read_element_length (ebml, &length) < 0) return FALSE; - gst_bytestream_flush_fast (ebml->bs, bytes); /* remember level */ level = g_new (GstEbmlLevel, 1); - level->start = gst_bytestream_tell (ebml->bs); + level->start = ebml->offset; level->length = length; ebml->level = g_list_append (ebml->level, level); - ebml->id_cache = 0; return TRUE; } @@ -772,18 +700,20 @@ gst_ebml_read_header (GstEbmlRead * ebml, gchar ** doctype, guint * version) if (version) *version = 1; - if (!(id = gst_ebml_peek_id (ebml, &level_up))) + if (!gst_ebml_peek_id (ebml, &level_up, &id)) return FALSE; + + GST_DEBUG_OBJECT (ebml, "id: %08x", GST_READ_UINT32_BE (&id)); + if (level_up != 0 || id != GST_EBML_ID_HEADER) { GST_ELEMENT_ERROR (ebml, STREAM, WRONG_TYPE, (NULL), (NULL)); return FALSE; } if (!gst_ebml_read_master (ebml, &id)) return FALSE; - g_assert (id == GST_EBML_ID_HEADER); while (TRUE) { - if (!(id = gst_ebml_peek_id (ebml, &level_up))) + if (!gst_ebml_peek_id (ebml, &level_up, &id)) return FALSE; /* end-of-header */ diff --git a/gst/matroska/ebml-read.h b/gst/matroska/ebml-read.h index e9528955..2bd74712 100644 --- a/gst/matroska/ebml-read.h +++ b/gst/matroska/ebml-read.h @@ -22,9 +22,7 @@ #ifndef __GST_EBML_READ_H__ #define __GST_EBML_READ_H__ -#include <glib.h> #include <gst/gst.h> -#include <gst/bytestream/bytestream.h> G_BEGIN_DECLS @@ -42,64 +40,77 @@ G_BEGIN_DECLS (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_EBML_READ, GstEbmlReadClass)) typedef struct _GstEbmlLevel { - guint64 start, - length; + guint64 start; + guint64 length; } GstEbmlLevel; typedef struct _GstEbmlRead { GstElement parent; + GstBuffer *cached_buffer; + GstPad *sinkpad; - GstByteStream *bs; + guint64 offset; GList *level; - - /* cache of ID (peeking) */ - guint32 id_cache; } GstEbmlRead; typedef struct _GstEbmlReadClass { GstElementClass parent; } GstEbmlReadClass; -GType gst_ebml_read_get_type (void); - -guint32 gst_ebml_peek_id (GstEbmlRead *ebml, - guint *level_up); -GstEvent *gst_ebml_read_seek (GstEbmlRead *ebml, - guint64 offset); -gboolean gst_ebml_read_skip (GstEbmlRead *ebml); -gboolean gst_ebml_read_reserve (GstEbmlRead *ebml); -gboolean gst_ebml_read_buffer (GstEbmlRead *ebml, - guint32 *id, - GstBuffer **buf); -gboolean gst_ebml_read_uint (GstEbmlRead *ebml, - guint32 *id, - guint64 *num); -gboolean gst_ebml_read_sint (GstEbmlRead *ebml, - guint32 *id, - gint64 *num); -gboolean gst_ebml_read_float (GstEbmlRead *ebml, - guint32 *id, - gdouble *num); -gboolean gst_ebml_read_ascii (GstEbmlRead *ebml, - guint32 *id, - gchar **str); -gboolean gst_ebml_read_utf8 (GstEbmlRead *ebml, - guint32 *id, - gchar **str); -gboolean gst_ebml_read_date (GstEbmlRead *ebml, - guint32 *id, - gint64 *date); -gboolean gst_ebml_read_master (GstEbmlRead *ebml, - guint32 *id); -gboolean gst_ebml_read_binary (GstEbmlRead *ebml, - guint32 *id, - guint8 **binary, - guint64 *length); -gboolean gst_ebml_read_header (GstEbmlRead *read, - gchar **doctype, - guint *version); +GType gst_ebml_read_get_type (void); + +gboolean gst_ebml_peek_id (GstEbmlRead *ebml, + guint *level_up, + guint32 *id); + +gboolean gst_ebml_read_seek (GstEbmlRead *ebml, + guint64 offset); + +gint64 gst_ebml_read_get_length (GstEbmlRead *ebml); + +gboolean gst_ebml_read_skip (GstEbmlRead *ebml); + +gboolean gst_ebml_read_buffer (GstEbmlRead *ebml, + guint32 *id, + GstBuffer **buf); + +gboolean gst_ebml_read_uint (GstEbmlRead *ebml, + guint32 *id, + guint64 *num); + +gboolean gst_ebml_read_sint (GstEbmlRead *ebml, + guint32 *id, + gint64 *num); + +gboolean gst_ebml_read_float (GstEbmlRead *ebml, + guint32 *id, + gdouble *num); + +gboolean gst_ebml_read_ascii (GstEbmlRead *ebml, + guint32 *id, + gchar **str); + +gboolean gst_ebml_read_utf8 (GstEbmlRead *ebml, + guint32 *id, + gchar **str); + +gboolean gst_ebml_read_date (GstEbmlRead *ebml, + guint32 *id, + gint64 *date); + +gboolean gst_ebml_read_master (GstEbmlRead *ebml, + guint32 *id); + +gboolean gst_ebml_read_binary (GstEbmlRead *ebml, + guint32 *id, + guint8 **binary, + guint64 *length); + +gboolean gst_ebml_read_header (GstEbmlRead *read, + gchar **doctype, + guint *version); G_END_DECLS diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index 1d3eaae4..1b2d72dc 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -27,6 +27,7 @@ #include <string.h> /* For AVI compatibility mode... Who did that? */ +/* and for fourcc stuff */ #include <gst/riff/riff-ids.h> #include <gst/riff/riff-media.h> @@ -38,16 +39,9 @@ GST_DEBUG_CATEGORY (matroskademux_debug); enum { - /* FILL ME */ - LAST_SIGNAL -}; - -enum -{ ARG_0, ARG_METADATA, ARG_STREAMINFO - /* FILL ME */ }; static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink", @@ -56,42 +50,40 @@ static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_CAPS ("video/x-matroska") ); -/* gobject magic foo */ static void gst_matroska_demux_base_init (GstMatroskaDemuxClass * klass); static void gst_matroska_demux_class_init (GstMatroskaDemuxClass * klass); static void gst_matroska_demux_init (GstMatroskaDemux * demux); /* element functions */ -static void gst_matroska_demux_loop (GstElement * element); -static gboolean gst_matroska_demux_send_event (GstElement * element, +static void gst_matroska_demux_loop (GstPad * pad); + +static gboolean gst_matroska_demux_element_send_event (GstElement * element, GstEvent * event); /* pad functions */ -static const GstEventMask *gst_matroska_demux_get_event_mask (GstPad * pad); +static gboolean gst_matroska_demux_sink_activate_pull (GstPad * sinkpad, + gboolean active); +static gboolean gst_matroska_demux_sink_activate (GstPad * sinkpad); +static gboolean gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux, + GstEvent * event); static gboolean gst_matroska_demux_handle_src_event (GstPad * pad, GstEvent * event); -static const GstFormat *gst_matroska_demux_get_src_formats (GstPad * pad); static const GstQueryType *gst_matroska_demux_get_src_query_types (GstPad * pad); static gboolean gst_matroska_demux_handle_src_query (GstPad * pad, - GstQueryType type, GstFormat * format, gint64 * value); + GstQuery * query); -/* gst internal change state handler */ static GstStateChangeReturn gst_matroska_demux_change_state (GstElement * element, GstStateChange transition); -static void gst_matroska_demux_set_clock (GstElement * element, - GstClock * clock); /* caps functions */ static GstCaps *gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * videocontext, - const gchar * codec_id, - gpointer data, guint size, GstMatroskaDemux * demux); + const gchar * codec_id, gpointer data, guint size, gchar ** codec_name); static GstCaps *gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * audiocontext, - const gchar * codec_id, - gpointer data, guint size, GstMatroskaDemux * demux); + const gchar * codec_id, gpointer data, guint size, gchar ** codec_name); static GstCaps *gst_matroska_demux_complex_caps (GstMatroskaTrackComplexContext * complexcontext, const gchar * codec_id, gpointer data, guint size); static GstCaps @@ -101,15 +93,16 @@ static GstCaps /* stream methods */ static void gst_matroska_demux_reset (GstElement * element); -static GstEbmlReadClass *parent_class = NULL; -static GstPadTemplate *videosrctempl, *audiosrctempl, *subtitlesrctempl; +static GstPadTemplate *subtitlesrctempl; /* NULL */ +static GstPadTemplate *videosrctempl; /* NULL */ +static GstPadTemplate *audiosrctempl; /* NULL */ -/*static guint gst_matroska_demux_signals[LAST_SIGNAL] = { 0 };*/ +static GstEbmlReadClass *parent_class; /* NULL; */ -GType +static GType gst_matroska_demux_get_type (void) { - static GType gst_matroska_demux_type = 0; + static GType gst_matroska_demux_type; /* 0 */ if (!gst_matroska_demux_type) { static const GTypeInfo gst_matroska_demux_info = { @@ -149,6 +142,9 @@ gst_matroska_demux_base_init (GstMatroskaDemuxClass * klass) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&sink_templ)); gst_element_class_set_details (element_class, &gst_matroska_demux_details); + + GST_DEBUG_CATEGORY_INIT (matroskademux_debug, "matroskademux", 0, + "Matroska demuxer"); } static void @@ -158,14 +154,12 @@ gst_matroska_demux_class_init (GstMatroskaDemuxClass * klass) gstelement_class = (GstElementClass *) klass; - parent_class = g_type_class_ref (GST_TYPE_EBML_READ); + parent_class = g_type_class_peek_parent (klass); - gstelement_class->change_state = gst_matroska_demux_change_state; - gstelement_class->send_event = gst_matroska_demux_send_event; - gstelement_class->set_clock = gst_matroska_demux_set_clock; - - GST_DEBUG_CATEGORY_INIT (matroskademux_debug, "matroskademux", 0, - "Matroska demuxer"); + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_matroska_demux_change_state); + gstelement_class->send_event = + GST_DEBUG_FUNCPTR (gst_matroska_demux_element_send_event); } static void @@ -174,21 +168,22 @@ gst_matroska_demux_init (GstMatroskaDemux * demux) GstElementClass *klass = GST_ELEMENT_GET_CLASS (demux); gint i; - GST_OBJECT_FLAG_SET (GST_OBJECT (demux), GST_ELEMENT_EVENT_AWARE); - demux->sinkpad = gst_pad_new_from_template (gst_element_class_get_pad_template (klass, "sink"), "sink"); + gst_pad_set_activate_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_matroska_demux_sink_activate)); + gst_pad_set_activatepull_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_matroska_demux_sink_activate_pull)); gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad); GST_EBML_READ (demux)->sinkpad = demux->sinkpad; - gst_element_set_loop_function (GST_ELEMENT (demux), gst_matroska_demux_loop); - /* initial stream no. */ for (i = 0; i < GST_MATROSKA_DEMUX_MAX_STREAMS; i++) { demux->src[i] = NULL; } - demux->writing_app = demux->muxing_app = NULL; + demux->writing_app = NULL; + demux->muxing_app = NULL; demux->index = NULL; /* finish off */ @@ -241,16 +236,15 @@ gst_matroska_demux_reset (GstElement * element) demux->duration = 0; demux->pos = 0; demux->created = G_MININT64; - demux->seek_pending = GST_CLOCK_TIME_NONE; demux->metadata_parsed = FALSE; demux->index_parsed = FALSE; -} -static void -gst_matroska_demux_set_clock (GstElement * element, GstClock * clock) -{ - GST_MATROSKA_DEMUX (element)->clock = clock; + demux->segment_rate = 1.0; + demux->segment_start = GST_CLOCK_TIME_NONE; + demux->segment_stop = GST_CLOCK_TIME_NONE; + demux->segment_play = FALSE; + demux->seek_pending = FALSE; } static gint @@ -272,23 +266,27 @@ gst_matroska_demux_stream_from_num (GstMatroskaDemux * demux, guint track_num) } static GstCaps * -gst_matroska_demux_src_getcaps (GstPad * pad) +gst_matroska_demux_getcaps (GstPad * pad) { - GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (gst_pad_get_parent (pad)); - guint n; + GstMatroskaDemux *demux; + GstCaps *caps = NULL; + guint i; - for (n = 0; n < demux->num_streams; n++) { - if (demux->src[n] != NULL && demux->src[n]->pad == pad) { - break; - } + demux = GST_MATROSKA_DEMUX (gst_pad_get_parent (pad)); + + for (i = 0; caps == NULL && i < demux->num_streams; ++i) { + if (demux->src[i]->pad == pad) + caps = gst_caps_copy (demux->src[i]->caps); } - if (n == demux->num_streams) - return gst_caps_new_empty (); + gst_object_unref (demux); - return gst_caps_copy (demux->src[n]->caps); + g_return_val_if_fail (caps != NULL, NULL); + + return caps; } + static gboolean gst_matroska_demux_add_stream (GstMatroskaDemux * demux) { @@ -300,6 +298,8 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux) gchar *padname = NULL; gboolean res = TRUE; guint32 id; + GstTagList *list = NULL; + gchar *codec = NULL; if (demux->num_streams >= GST_MATROSKA_DEMUX_MAX_STREAMS) { GST_WARNING ("Maximum number of streams (%d) exceeded, skipping", @@ -323,7 +323,7 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux) /* try reading the trackentry headers */ while (res) { - if (!(id = gst_ebml_peek_id (ebml, &demux->level_up))) { + if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) { res = FALSE; break; } else if (demux->level_up > 0) { @@ -376,6 +376,13 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux) case GST_MATROSKA_TRACK_TYPE_VIDEO: context = (GstMatroskaTrackContext *) g_renew (GstMatroskaTrackVideoContext, context, 1); + ((GstMatroskaTrackVideoContext *) context)->display_width = 0; + ((GstMatroskaTrackVideoContext *) context)->display_height = 0; + ((GstMatroskaTrackVideoContext *) context)->pixel_width = 0; + ((GstMatroskaTrackVideoContext *) context)->pixel_height = 0; + ((GstMatroskaTrackVideoContext *) context)->eye_mode = 0; + ((GstMatroskaTrackVideoContext *) context)->asr_mode = 0; + ((GstMatroskaTrackVideoContext *) context)->fourcc = 0; break; case GST_MATROSKA_TRACK_TYPE_AUDIO: context = (GstMatroskaTrackContext *) @@ -420,7 +427,7 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux) videocontext = (GstMatroskaTrackVideoContext *) context; while (res) { - if (!(id = gst_ebml_peek_id (ebml, &demux->level_up))) { + if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) { res = FALSE; break; } else if (demux->level_up > 0) { @@ -607,7 +614,7 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux) audiocontext = (GstMatroskaTrackAudioContext *) context; while (res) { - if (!(id = gst_ebml_peek_id (ebml, &demux->level_up))) { + if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) { res = FALSE; break; } else if (demux->level_up > 0) { @@ -839,7 +846,13 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux) templ = gst_element_class_get_pad_template (klass, "video_%02d"); caps = gst_matroska_demux_video_caps (videocontext, context->codec_id, - context->codec_priv, context->codec_priv_size, demux); + context->codec_priv, context->codec_priv_size, &codec); + if (codec) { + list = gst_tag_list_new (); + gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, + GST_TAG_VIDEO_CODEC, codec, NULL); + g_free (codec); + } break; } @@ -850,8 +863,14 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux) templ = gst_element_class_get_pad_template (klass, "audio_%02d"); caps = gst_matroska_demux_audio_caps (audiocontext, context->codec_id, - context->codec_priv, context->codec_priv_size, demux); + context->codec_priv, context->codec_priv_size, &codec); audiocontext->first_frame = TRUE; + if (codec) { + list = gst_tag_list_new (); + gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, + GST_TAG_AUDIO_CODEC, codec, NULL); + g_free (codec); + } break; } @@ -879,118 +898,121 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux) case GST_MATROSKA_TRACK_TYPE_CONTROL: default: /* we should already have quit by now */ - g_assert (0); + g_assert_not_reached (); + } + + if (context->language) { + if (!list) + list = gst_tag_list_new (); + gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, + GST_TAG_LANGUAGE_CODE, context->language, NULL); } /* the pad in here */ context->pad = gst_pad_new_from_template (templ, padname); context->caps = caps ? caps : gst_caps_new_empty (); - g_free (padname); - - /* set some functions */ - gst_pad_set_formats_function (context->pad, - gst_matroska_demux_get_src_formats); - gst_pad_set_event_mask_function (context->pad, - gst_matroska_demux_get_event_mask); gst_pad_set_event_function (context->pad, - gst_matroska_demux_handle_src_event); + GST_DEBUG_FUNCPTR (gst_matroska_demux_handle_src_event)); gst_pad_set_query_type_function (context->pad, - gst_matroska_demux_get_src_query_types); + GST_DEBUG_FUNCPTR (gst_matroska_demux_get_src_query_types)); gst_pad_set_query_function (context->pad, - gst_matroska_demux_handle_src_query); - gst_pad_set_getcaps_function (context->pad, gst_matroska_demux_src_getcaps); + GST_DEBUG_FUNCPTR (gst_matroska_demux_handle_src_query)); + + if (caps) { + GST_LOG ("Adding pad '%s' with caps %" GST_PTR_FORMAT, padname, caps); + if (gst_caps_is_fixed (caps)) { + GST_LOG ("fixed caps"); + gst_pad_use_fixed_caps (context->pad); + gst_pad_set_caps (context->pad, context->caps); + } else { + GST_LOG ("non-fixed caps"); + gst_pad_set_getcaps_function (context->pad, + GST_DEBUG_FUNCPTR (gst_matroska_demux_getcaps)); + } + gst_pad_set_active (context->pad, TRUE); + gst_element_add_pad (GST_ELEMENT (demux), context->pad); + } else { + /* FIXME: are we leaking the pad here? can this even happen? */ + GST_LOG ("Not adding pad '%s' with empty caps", padname); + } + + /* tags */ + if (list) { + gst_element_found_tags_for_pad (GST_ELEMENT (demux), context->pad, list); + } - gst_element_add_pad (GST_ELEMENT (demux), context->pad); + g_free (padname); /* tadaah! */ return TRUE; } -static const GstFormat * -gst_matroska_demux_get_src_formats (GstPad * pad) -{ - /*GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (gst_pad_get_parent (pad)); */ - - /* we could try to look for units (i.e. samples) in audio streams - * or video streams, but both samplerate and framerate are not - * always constant, and since we only have a time indication, we - * cannot guarantee anything here based purely on index. So, we - * only support time for now. */ - static const GstFormat src_formats[] = { - GST_FORMAT_TIME, - (GstFormat) 0 - }; - - return src_formats; -} - static const GstQueryType * gst_matroska_demux_get_src_query_types (GstPad * pad) { - static const GstQueryType src_types[] = { - GST_QUERY_TOTAL, + static const GstQueryType query_types[] = { GST_QUERY_POSITION, - (GstQueryType) 0 + 0 }; - return src_types; + return query_types; } static gboolean -gst_matroska_demux_handle_src_query (GstPad * pad, - GstQueryType type, GstFormat * format, gint64 * value) +gst_matroska_demux_handle_src_query (GstPad * pad, GstQuery * query) { - gboolean res = TRUE; - GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (gst_pad_get_parent (pad)); + GstMatroskaDemux *demux; + gboolean res = FALSE; - switch (type) { - case GST_QUERY_TOTAL: - switch (*format) { - case GST_FORMAT_TIME: - *value = demux->duration; - break; - default: - res = FALSE; - break; - } - break; + demux = GST_MATROSKA_DEMUX (gst_pad_get_parent (pad)); + switch (GST_QUERY_TYPE (query)) { case GST_QUERY_POSITION: - switch (*format) { - case GST_FORMAT_TIME: - *value = demux->pos; - break; - default: - res = FALSE; - break; + { + GstFormat format; + + gst_query_parse_position (query, &format, NULL, NULL); + + if (format != GST_FORMAT_TIME) { + GST_DEBUG ("only query position on TIME is supported"); + break; } + + GST_LOCK (demux); + + /* mabe we should only fill in the total time and let + * decoders fill in the current position? (like oggdemux) */ + gst_query_set_position (query, GST_FORMAT_TIME, demux->pos, + demux->duration); + + GST_UNLOCK (demux); + + res = TRUE; break; + } default: - res = FALSE; break; } + gst_object_unref (demux); return res; } + static GstMatroskaIndex * -gst_matroskademux_seek (GstMatroskaDemux * demux) +gst_matroskademux_do_index_seek (GstMatroskaDemux * demux, guint64 seek_pos) { guint entry = (guint) - 1; - guint64 offset = demux->seek_pending; guint n; - /* make sure we don't seek twice */ - demux->seek_pending = GST_CLOCK_TIME_NONE; - for (n = 0; n < demux->num_indexes; n++) { if (entry == (guint) - 1) { entry = n; } else { - gfloat diff_old = fabs (1. * (demux->index[entry].time - offset)), - diff_new = fabs (1. * (demux->index[n].time - offset)); + gfloat diff_old = fabs (1. * (demux->index[entry].time - seek_pos)), + diff_new = fabs (1. * (demux->index[n].time - seek_pos)); if (diff_new < diff_old) { entry = n; @@ -1005,49 +1027,197 @@ gst_matroskademux_seek (GstMatroskaDemux * demux) return NULL; } +/* takes ownership of the passed event! */ static gboolean -gst_matroska_demux_send_event (GstElement * element, GstEvent * event) +gst_matroska_demux_send_event (GstMatroskaDemux * demux, GstEvent * event) { - GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (element); - gboolean res = TRUE; + gboolean ret = TRUE; + gint i; - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - switch (GST_EVENT_SEEK_FORMAT (event)) { - case GST_FORMAT_TIME: - demux->seek_pending = GST_EVENT_SEEK_OFFSET (event); - break; + GST_DEBUG_OBJECT (demux, "Sending event of type %s to all source pads", + GST_EVENT_TYPE_NAME (event)); - default: - GST_WARNING ("Only time seek is supported"); - res = FALSE; - break; - } - break; + for (i = 0; i < demux->num_streams; i++) { + GstMatroskaTrackContext *stream; - default: - GST_WARNING ("Unhandled event of type %d", GST_EVENT_TYPE (event)); - res = FALSE; - break; + stream = demux->src[i]; + gst_event_ref (event); + gst_pad_push_event (stream->pad, event); } - gst_event_unref (event); + return ret; +} + +static gboolean +gst_matroska_demux_element_send_event (GstElement * element, GstEvent * event) +{ + GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (element); + gboolean res; + if (GST_EVENT_TYPE (event) == GST_EVENT_SEEK) { + res = gst_matroska_demux_handle_seek_event (demux, event); + } else { + GST_WARNING ("Unhandled event of type %s", GST_EVENT_TYPE_NAME (event)); + res = FALSE; + } + gst_event_unref (event); return res; } -static const GstEventMask * -gst_matroska_demux_get_event_mask (GstPad * pad) +static gboolean +gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux, + GstEvent * event) { - static const GstEventMask masks[] = { - {GST_EVENT_SEEK, (GstEventFlag) ((gint) GST_SEEK_METHOD_SET | - (gint) GST_SEEK_FLAG_KEY_UNIT)}, - {GST_EVENT_SEEK_SEGMENT, (GstEventFlag) ((gint) GST_SEEK_METHOD_SET | - (gint) GST_SEEK_FLAG_KEY_UNIT)}, - {(GstEventType) 0, (GstEventFlag) 0} - }; + GstMatroskaIndex *entry; + GstSeekFlags flags; + GstSeekType cur_type, stop_type; + GstFormat format; + GstEvent *newsegment_event; + gboolean flush; + gdouble rate; + gint64 cur, stop; + gint64 segment_start, segment_stop; + gint i; + + gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, + &stop_type, &stop); + + /* we can only seek on time */ + if (format != GST_FORMAT_TIME) { + GST_DEBUG ("Can only seek on TIME"); + return FALSE; + } - return masks; + /* cannot yet do backwards playback */ + if (rate <= 0.0) { + GST_DEBUG ("Can only seek with positive rate"); + return FALSE; + } + + /* check sanity before we start flushing and all that */ + if (cur_type == GST_SEEK_TYPE_SET) { + GST_LOCK (demux); + if (!gst_matroskademux_do_index_seek (demux, cur)) { + GST_DEBUG ("No matching seek entry in index"); + GST_UNLOCK (demux); + return FALSE; + } + GST_DEBUG ("Seek position looks sane"); + GST_UNLOCK (demux); + } + + flush = !!(flags & GST_SEEK_FLAG_FLUSH); + + if (flush) { + GST_DEBUG ("Starting flush"); + gst_pad_push_event (demux->sinkpad, gst_event_new_flush_start ()); + gst_matroska_demux_send_event (demux, gst_event_new_flush_start ()); + } else { + gst_pad_pause_task (demux->sinkpad); + } + + /* now grab the stream lock so that streaming cannot continue, for + * non flushing seeks when the element is in PAUSED this could block + * forever. */ + GST_STREAM_LOCK (demux->sinkpad); + + GST_LOCK (demux); + + /* if nothing configured, play complete file */ + if (cur == GST_CLOCK_TIME_NONE) + cur = 0; + if (stop == GST_CLOCK_TIME_NONE) + stop = demux->duration; + + if (cur_type == GST_SEEK_TYPE_SET) + segment_start = cur; + else if (cur_type == GST_SEEK_TYPE_CUR) + segment_start = demux->segment_start + cur; + else + segment_start = demux->segment_start; + + if (stop_type == GST_SEEK_TYPE_SET) + segment_stop = stop; + else if (stop_type == GST_SEEK_TYPE_CUR) + segment_stop = demux->segment_stop + stop; + else + segment_stop = demux->segment_stop; + + segment_start = CLAMP (segment_start, 0, demux->duration); + segment_stop = CLAMP (segment_stop, 0, demux->duration); + + GST_DEBUG ("New segment positions: %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT, + GST_TIME_ARGS (segment_start), GST_TIME_ARGS (segment_stop)); + + entry = gst_matroskademux_do_index_seek (demux, segment_start); + if (!entry) { + GST_DEBUG ("No matching seek entry in index"); + goto seek_error; + } + + /* seek (relative to matroska segment) */ + if (!gst_ebml_read_seek (GST_EBML_READ (demux), + entry->pos + demux->ebml_segment_start)) { + GST_DEBUG ("Failed to seek to offset %" G_GUINT64_FORMAT, + entry->pos + demux->ebml_segment_start); + goto seek_error; + } + + GST_DEBUG ("Seeked to offset %" G_GUINT64_FORMAT, entry->pos + + demux->ebml_segment_start); + + GST_DEBUG ("Committing new seek segment"); + + demux->segment_rate = rate; + demux->segment_play = !!(flags & GST_SEEK_FLAG_SEGMENT); + + demux->segment_start = segment_start; + demux->segment_stop = segment_stop; + + /* notify start of new segment */ + if (demux->segment_play) { + GstMessage *msg; + + msg = gst_message_new_segment_start (GST_OBJECT (demux), GST_FORMAT_TIME, demux->segment_start); /* or entry->time? */ + gst_element_post_message (GST_ELEMENT (demux), msg); + } + + newsegment_event = gst_event_new_newsegment (FALSE, demux->segment_rate, + GST_FORMAT_TIME, entry->time, demux->segment_stop, 0); + + GST_UNLOCK (demux); + + GST_DEBUG ("Stopping flush"); + if (flush) { + gst_matroska_demux_send_event (demux, gst_event_new_flush_stop ()); + } + gst_pad_push_event (demux->sinkpad, gst_event_new_flush_stop ()); + + /* send newsegment event to all source pads and update the time */ + gst_matroska_demux_send_event (demux, newsegment_event); + for (i = 0; i < demux->num_streams; i++) + demux->src[i]->pos = entry->time; + demux->pos = entry->time; + + /* restart our task since it might have been stopped when we did the + * flush. */ + gst_pad_start_task (demux->sinkpad, (GstTaskFunction) gst_matroska_demux_loop, + demux->sinkpad); + + /* streaming can continue now */ + GST_STREAM_UNLOCK (demux->sinkpad); + + return TRUE; + +seek_error: + + /* FIXME: shouldn't we either make it a real error or start the task + * function again so that things can continue from where they left off? */ + GST_DEBUG ("Got a seek error"); + GST_UNLOCK (demux); + GST_STREAM_UNLOCK (demux->sinkpad); + + return FALSE; } static gboolean @@ -1057,9 +1227,9 @@ gst_matroska_demux_handle_src_event (GstPad * pad, GstEvent * event) gboolean res = TRUE; switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK_SEGMENT: case GST_EVENT_SEEK: - return gst_matroska_demux_send_event (GST_ELEMENT (demux), event); + res = gst_matroska_demux_handle_seek_event (demux, event); + break; /* events we don't need to handle */ case GST_EVENT_NAVIGATION: @@ -1071,46 +1241,13 @@ gst_matroska_demux_handle_src_event (GstPad * pad, GstEvent * event) break; } + gst_object_unref (demux); gst_event_unref (event); return res; } static gboolean -gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux) -{ - GstEbmlRead *ebml = GST_EBML_READ (demux); - GstMatroskaIndex *entry = gst_matroskademux_seek (demux); - GstEvent *event; - guint i; - - if (!entry) - return FALSE; - - /* seek (relative to segment) */ - if (!(event = gst_ebml_read_seek (ebml, entry->pos + demux->segment_start))) - return FALSE; - gst_event_unref (event); /* byte - we want time */ - event = - gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, entry->time, - GST_FORMAT_UNDEFINED); - - /* forward to all src pads */ - for (i = 0; i < demux->num_streams; i++) { - if (GST_PAD_IS_USABLE (demux->src[i]->pad)) { - gst_event_ref (event); - gst_pad_push (demux->src[i]->pad, GST_DATA (event)); - } - demux->src[i]->pos = entry->time; - } - demux->pos = entry->time; - - gst_event_unref (event); - - return TRUE; -} - -static gboolean gst_matroska_demux_init_stream (GstMatroskaDemux * demux) { GstEbmlRead *ebml = GST_EBML_READ (demux); @@ -1139,8 +1276,10 @@ gst_matroska_demux_init_stream (GstMatroskaDemux * demux) while (1) { guint last_level; - if (!(id = gst_ebml_peek_id (ebml, &last_level))) + if (!gst_ebml_peek_id (ebml, &last_level, &id)) { + GST_DEBUG_OBJECT (demux, "gst_ebml_peek_id() failed!"); return FALSE; + } if (id == GST_MATROSKA_ID_SEGMENT) break; @@ -1148,16 +1287,20 @@ gst_matroska_demux_init_stream (GstMatroskaDemux * demux) /* oi! */ GST_WARNING ("Expected a Segment ID (0x%x), but received 0x%x!", GST_MATROSKA_ID_SEGMENT, id); + if (!gst_ebml_read_skip (ebml)) return FALSE; } /* we now have a EBML segment */ - if (!gst_ebml_read_master (ebml, &id)) + if (!gst_ebml_read_master (ebml, &id)) { + GST_DEBUG_OBJECT (demux, "gst_ebml_read_master() failed!"); return FALSE; + } + /* seeks are from the beginning of the segment, * after the segment ID/length */ - demux->segment_start = gst_bytestream_tell (ebml->bs); + demux->ebml_segment_start = ebml->offset; return TRUE; } @@ -1170,7 +1313,7 @@ gst_matroska_demux_parse_tracks (GstMatroskaDemux * demux) guint32 id; while (res) { - if (!(id = gst_ebml_peek_id (ebml, &demux->level_up))) { + if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) { res = FALSE; break; } else if (demux->level_up) { @@ -1205,165 +1348,201 @@ gst_matroska_demux_parse_tracks (GstMatroskaDemux * demux) } static gboolean -gst_matroska_demux_parse_index (GstMatroskaDemux * demux, gboolean prevent_eos) +gst_matroska_demux_parse_index_cuetrack (GstMatroskaDemux * demux, + gboolean prevent_eos, GstMatroskaIndex * idx, guint64 length) { GstEbmlRead *ebml = GST_EBML_READ (demux); - gboolean res = TRUE; + gboolean got_error = FALSE; guint32 id; - GstMatroskaIndex idx; - guint64 length = 0; - if (prevent_eos) { - length = gst_bytestream_length (ebml->bs); - } + if (!gst_ebml_read_master (ebml, &id)) + return FALSE; - while (res) { - /* We're an element that can be seeked to. If we are, then - * we want to prevent EOS, since that'll kill us. So we cache - * file size and seek until there, and don't call EOS upon os. */ - if (prevent_eos && length == gst_bytestream_tell (ebml->bs)) { - res = FALSE; - break; - } else if (!(id = gst_ebml_peek_id (ebml, &demux->level_up))) { - res = FALSE; + while (!got_error) { + if (prevent_eos && length == ebml->offset) break; - } else if (demux->level_up) { + + if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) + return FALSE; + + if (demux->level_up) { demux->level_up--; break; } switch (id) { - /* one single index entry ('point') */ - case GST_MATROSKA_ID_POINTENTRY: - if (!gst_ebml_read_master (ebml, &id)) { - res = FALSE; - break; - } + /* track number */ + case GST_MATROSKA_ID_CUETRACK: + { + guint64 num; - /* in the end, we hope to fill one entry with a - * timestamp, a file position and a tracknum */ - idx.pos = (guint64) - 1; - idx.time = (guint64) - 1; - idx.track = (guint16) - 1; + if (!gst_ebml_read_uint (ebml, &id, &num)) + goto error; - while (res) { - if (prevent_eos && length == gst_bytestream_tell (ebml->bs)) { - res = FALSE; - break; - } else if (!(id = gst_ebml_peek_id (ebml, &demux->level_up))) { - res = FALSE; - break; - } else if (demux->level_up) { - demux->level_up--; - break; - } + idx->track = num; + break; + } - switch (id) { - /* one single index entry ('point') */ - case GST_MATROSKA_ID_CUETIME:{ - gint64 time; + /* position in file */ + case GST_MATROSKA_ID_CUECLUSTERPOSITION: + { + guint64 num; - if (!gst_ebml_read_uint (ebml, &id, &time)) { - res = FALSE; - break; - } - idx.time = time * demux->time_scale; - break; - } + if (!gst_ebml_read_uint (ebml, &id, &num)) + goto error; - /* position in the file + track to which it belongs */ - case GST_MATROSKA_ID_CUETRACKPOSITION: - if (!gst_ebml_read_master (ebml, &id)) { - res = FALSE; - break; - } + idx->pos = num; + break; + } - while (res) { - if (prevent_eos && length == gst_bytestream_tell (ebml->bs)) { - res = FALSE; - break; - } else if (!(id = gst_ebml_peek_id (ebml, &demux->level_up))) { - res = FALSE; - break; - } else if (demux->level_up) { - demux->level_up--; - break; - } + default: + GST_WARNING ("Unknown entry 0x%x in CuesTrackPositions", id); + /* fall-through */ - switch (id) { - /* track number */ - case GST_MATROSKA_ID_CUETRACK:{ - guint64 num; + case GST_EBML_ID_VOID: + if (!gst_ebml_read_skip (ebml)) + goto error; + break; + } - if (!gst_ebml_read_uint (ebml, &id, &num)) { - res = FALSE; - break; - } - idx.track = num; - break; - } + if (demux->level_up) { + demux->level_up--; + break; + } + } - /* position in file */ - case GST_MATROSKA_ID_CUECLUSTERPOSITION:{ - guint64 num; + return TRUE; - if (!gst_ebml_read_uint (ebml, &id, &num)) { - res = FALSE; - break; - } - idx.pos = num; - break; - } +error: + if (demux->level_up) + demux->level_up--; - default: - GST_WARNING ("Unknown entry 0x%x in CuesTrackPositions", - id); - /* fall-through */ + return FALSE; +} - case GST_EBML_ID_VOID: - if (!gst_ebml_read_skip (ebml)) - res = FALSE; - break; - } +static gboolean +gst_matroska_demux_parse_index_pointentry (GstMatroskaDemux * demux, + gboolean prevent_eos, guint64 length) +{ + GstEbmlRead *ebml = GST_EBML_READ (demux); + GstMatroskaIndex idx; + gboolean got_error = FALSE; + guint32 id; - if (demux->level_up) { - demux->level_up--; - break; - } - } + if (!gst_ebml_read_master (ebml, &id)) + return FALSE; - break; + /* in the end, we hope to fill one entry with a + * timestamp, a file position and a tracknum */ + idx.pos = (guint64) - 1; + idx.time = (guint64) - 1; + idx.track = (guint16) - 1; - default: - GST_WARNING ("Unknown entry 0x%x in cuespoint index", id); - /* fall-through */ + while (!got_error) { + if (prevent_eos && length == ebml->offset) + break; - case GST_EBML_ID_VOID: - if (!gst_ebml_read_skip (ebml)) - res = FALSE; - break; - } + if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) + return FALSE; - if (demux->level_up) { - demux->level_up--; - break; - } + if (demux->level_up) { + demux->level_up--; + break; + } + + switch (id) { + /* one single index entry ('point') */ + case GST_MATROSKA_ID_CUETIME: + { + guint64 time; + + if (!gst_ebml_read_uint (ebml, &id, &time)) { + got_error = TRUE; + } else { + idx.time = time * demux->time_scale; } + break; + } - /* so let's see if we got what we wanted */ - if (idx.pos != (guint64) - 1 && - idx.time != (guint64) - 1 && idx.track != (guint16) - 1) { - if (demux->num_indexes % 32 == 0) { - /* re-allocate bigger index */ - demux->index = g_renew (GstMatroskaIndex, demux->index, - demux->num_indexes + 32); - } - demux->index[demux->num_indexes].pos = idx.pos; - demux->index[demux->num_indexes].time = idx.time; - demux->index[demux->num_indexes].track = idx.track; - demux->num_indexes++; + /* position in the file + track to which it belongs */ + case GST_MATROSKA_ID_CUETRACKPOSITION: + { + if (!gst_matroska_demux_parse_index_cuetrack (demux, prevent_eos, &idx, + length)) { + got_error = TRUE; } + break; + } + default: + GST_WARNING ("Unknown entry 0x%x in cuespoint index", id); + /* fall-through */ + + case GST_EBML_ID_VOID: + if (!gst_ebml_read_skip (ebml)) + got_error = TRUE; + break; + } + + if (demux->level_up) { + demux->level_up--; + break; + } + } + + /* so let's see if we got what we wanted */ + if (idx.pos != (guint64) - 1 && + idx.time != (guint64) - 1 && idx.track != (guint16) - 1) { + if (demux->num_indexes % 32 == 0) { + /* re-allocate bigger index */ + demux->index = g_renew (GstMatroskaIndex, demux->index, + demux->num_indexes + 32); + } + GST_DEBUG_OBJECT (demux, "Index entry: pos=%" G_GUINT64_FORMAT + ", time=%" GST_TIME_FORMAT ", track=%u", idx.pos, + GST_TIME_ARGS (idx.time), (guint) idx.track); + demux->index[demux->num_indexes].pos = idx.pos; + demux->index[demux->num_indexes].time = idx.time; + demux->index[demux->num_indexes].track = idx.track; + demux->num_indexes++; + } + + return (!got_error); +} + +static gboolean +gst_matroska_demux_parse_index (GstMatroskaDemux * demux, gboolean prevent_eos) +{ + GstEbmlRead *ebml = GST_EBML_READ (demux); + gboolean got_error = FALSE; + guint32 id; + guint64 length = 0; + + if (prevent_eos) { + length = gst_ebml_read_get_length (ebml); + } + + while (!got_error) { + /* We're an element that can be seeked to. If we are, then + * we want to prevent EOS, since that'll kill us. So we cache + * file size and seek until there, and don't call EOS upon os. */ + if (prevent_eos && length == ebml->offset) + break; + + if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) + return FALSE; + + if (demux->level_up) { + demux->level_up--; + break; + } + + switch (id) { + /* one single index entry ('point') */ + case GST_MATROSKA_ID_POINTENTRY: + if (!gst_matroska_demux_parse_index_pointentry (demux, prevent_eos, + length)) + got_error = TRUE; break; default: @@ -1372,7 +1551,7 @@ gst_matroska_demux_parse_index (GstMatroskaDemux * demux, gboolean prevent_eos) case GST_EBML_ID_VOID: if (!gst_ebml_read_skip (ebml)) - res = FALSE; + got_error = TRUE; break; } @@ -1382,7 +1561,7 @@ gst_matroska_demux_parse_index (GstMatroskaDemux * demux, gboolean prevent_eos) } } - return res; + return (!got_error); } static gboolean @@ -1393,7 +1572,7 @@ gst_matroska_demux_parse_info (GstMatroskaDemux * demux) guint32 id; while (res) { - if (!(id = gst_ebml_peek_id (ebml, &demux->level_up))) { + if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) { res = FALSE; break; } else if (demux->level_up) { @@ -1478,13 +1657,9 @@ gst_matroska_demux_parse_info (GstMatroskaDemux * demux) } static gboolean -gst_matroska_demux_parse_metadata (GstMatroskaDemux * demux, - gboolean prevent_eos) +gst_matroska_demux_parse_metadata_id_simple_tag (GstMatroskaDemux * demux, + gboolean prevent_eos, guint64 length, GstTagList ** p_taglist) { - GstEbmlRead *ebml = GST_EBML_READ (demux); - gboolean res = TRUE; - guint32 id; - guint64 length = 0; struct { gchar *matroska_tagname; @@ -1492,170 +1667,137 @@ gst_matroska_demux_parse_metadata (GstMatroskaDemux * demux, } tag_conv[] = { { - GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE} - , { - GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST} - , { - GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM} - , { - GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT} - , { - GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE} - , { - GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER} - , { - GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE} - , { - GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC} - , { + GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, { + GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, { + GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, { + GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, { + GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, { + GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, { + GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, { + GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, { GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT} - , { - NULL, NULL} }; - gint i; - gboolean have_tags = FALSE; - GstTagList *taglist = gst_tag_list_new (); + GstEbmlRead *ebml = GST_EBML_READ (demux); + gboolean got_error = FALSE; + guint32 id; + gchar *value = NULL; + gchar *tag = NULL; - if (prevent_eos) { - length = gst_bytestream_length (ebml->bs); - } + if (!gst_ebml_read_master (ebml, &id)) + return FALSE; - while (res) { - /* We're an element that can be seeked to. If we are, then - * we want to prevent EOS, since that'll kill us. So we cache - * file size and seek until there, and don't call EOS upon os. */ - if (prevent_eos && length == gst_bytestream_tell (ebml->bs)) { - res = FALSE; + while (!got_error) { + /* read all sub-entries */ + if (prevent_eos && length == ebml->offset) break; - } else if (!(id = gst_ebml_peek_id (ebml, &demux->level_up))) { - res = FALSE; - break; - } else if (demux->level_up) { + + if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) + return FALSE; + + if (demux->level_up) { demux->level_up--; break; } switch (id) { - case GST_MATROSKA_ID_TAG: - if (!gst_ebml_read_master (ebml, &id)) { - res = FALSE; - break; - } + case GST_MATROSKA_ID_TAGNAME: + g_free (tag); + if (!gst_ebml_read_ascii (ebml, &id, &tag)) + got_error = TRUE; + break; - while (res) { - /* read all sub-entries */ - if (prevent_eos && length == gst_bytestream_tell (ebml->bs)) { - res = FALSE; - break; - } else if (!(id = gst_ebml_peek_id (ebml, &demux->level_up))) { - res = FALSE; - break; - } else if (demux->level_up) { - demux->level_up--; - break; - } + case GST_MATROSKA_ID_TAGSTRING: + g_free (value); + if (!gst_ebml_read_utf8 (ebml, &id, &value)) + got_error = TRUE; + break; - switch (id) { - case GST_MATROSKA_ID_SIMPLETAG:{ - gchar *tag = NULL, *value = NULL; + default: + GST_WARNING ("Unknown entry 0x%x in metadata collection", id); + /* fall-through */ - if (!gst_ebml_read_master (ebml, &id)) { - res = FALSE; - break; - } + case GST_EBML_ID_VOID: + if (!gst_ebml_read_skip (ebml)) + got_error = TRUE; + break; + } - while (res) { - /* read all sub-entries */ - if (prevent_eos && length == gst_bytestream_tell (ebml->bs)) { - res = FALSE; - break; - } else if (!(id = gst_ebml_peek_id (ebml, &demux->level_up))) { - res = FALSE; - break; - } else if (demux->level_up) { - demux->level_up--; - break; - } + if (demux->level_up) { + demux->level_up--; + break; + } + } - switch (id) { - case GST_MATROSKA_ID_TAGNAME: - g_free (tag); - res = gst_ebml_read_ascii (ebml, &id, &tag); - break; + if (tag && value) { + guint i; + + for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) { + const gchar *tagname_gst = tag_conv[i].gstreamer_tagname; + const gchar *tagname_mkv = tag_conv[i].matroska_tagname; + + if (strcmp (tagname_mkv, tag) == 0) { + GValue src = { 0, }; + GValue dest = { 0, }; + GType dest_type = gst_tag_get_type (tagname_gst); + + g_value_init (&src, G_TYPE_STRING); + g_value_set_string (&src, value); + g_value_init (&dest, dest_type); + g_value_transform (&src, &dest); + g_value_unset (&src); + gst_tag_list_add_values (*p_taglist, GST_TAG_MERGE_APPEND, + tagname_gst, &dest, NULL); + g_value_unset (&dest); + break; + } + } + } - case GST_MATROSKA_ID_TAGSTRING: - g_free (value); - res = gst_ebml_read_utf8 (ebml, &id, &value); - break; + g_free (tag); + g_free (value); - default: - GST_WARNING ("Unknown entry 0x%x in metadata collection", - id); - /* fall-through */ + return (!got_error); +} - case GST_EBML_ID_VOID: - if (!gst_ebml_read_skip (ebml)) - res = FALSE; - break; - } +static gboolean +gst_matroska_demux_parse_metadata_id_tag (GstMatroskaDemux * demux, + gboolean prevent_eos, guint64 length, GstTagList ** p_taglist) +{ + GstEbmlRead *ebml = GST_EBML_READ (demux); + gboolean got_error = FALSE; + guint32 id; - if (demux->level_up) { - demux->level_up--; - break; - } - } + if (!gst_ebml_read_master (ebml, &id)) + return FALSE; - if (tag && value) { - for (i = 0; tag_conv[i].matroska_tagname != NULL; i++) { - if (!strcmp (tag_conv[i].matroska_tagname, tag)) { - GValue src = { 0 } - , dest = { - 0}; - const gchar *type = tag_conv[i].gstreamer_tagname; - GType dest_type = gst_tag_get_type (type); - - g_value_init (&src, G_TYPE_STRING); - g_value_set_string (&src, value); - g_value_init (&dest, dest_type); - g_value_transform (&src, &dest); - g_value_unset (&src); - gst_tag_list_add_values (taglist, GST_TAG_MERGE_APPEND, - type, &dest, NULL); - g_value_unset (&dest); - have_tags = TRUE; - break; - } - } - } - g_free (tag); - g_free (value); - break; - } + while (!got_error) { + /* read all sub-entries */ + if (prevent_eos && length == ebml->offset) + break; - default: - GST_WARNING ("Unknown entry 0x%x in metadata collection", id); - /* fall-through */ + if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) + return FALSE; - case GST_EBML_ID_VOID: - if (!gst_ebml_read_skip (ebml)) - res = FALSE; - break; - } + if (demux->level_up) { + demux->level_up--; + break; + } - if (demux->level_up) { - demux->level_up--; - break; - } + switch (id) { + case GST_MATROSKA_ID_SIMPLETAG: + if (!gst_matroska_demux_parse_metadata_id_simple_tag (demux, + prevent_eos, length, p_taglist)) { + got_error = TRUE; } break; default: - GST_WARNING ("Unknown entry 0x%x in metadata header", id); + GST_WARNING ("Unknown entry 0x%x in metadata collection", id); /* fall-through */ case GST_EBML_ID_VOID: if (!gst_ebml_read_skip (ebml)) - res = FALSE; + got_error = TRUE; break; } @@ -1665,23 +1807,69 @@ gst_matroska_demux_parse_metadata (GstMatroskaDemux * demux, } } - if (have_tags) { - const GList *padlist; + return (!got_error); +} - /* let the world know about this wonderful thing */ - for (padlist = gst_element_get_pad_list (GST_ELEMENT (ebml)); - padlist != NULL; padlist = padlist->next) { - if (GST_PAD_IS_SRC (padlist->data) && GST_PAD_IS_USABLE (padlist->data)) { - gst_pad_push (GST_PAD (padlist->data), - GST_DATA (gst_event_new_tag (taglist))); - } +static gboolean +gst_matroska_demux_parse_metadata (GstMatroskaDemux * demux, + gboolean prevent_eos) +{ + GstEbmlRead *ebml = GST_EBML_READ (demux); + GstTagList *taglist = gst_tag_list_new (); + gboolean got_error = FALSE; + guint64 length = 0; + guint32 id; + + if (prevent_eos) { + length = gst_ebml_read_get_length (ebml); + } + + while (!got_error) { + /* We're an element that can be seeked to. If we are, then + * we want to prevent EOS, since that'll kill us. So we cache + * file size and seek until there, and don't call EOS upon os. */ + if (prevent_eos && length == ebml->offset) + break; + + if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) + return FALSE; + + if (demux->level_up) { + demux->level_up--; + break; } + + switch (id) { + case GST_MATROSKA_ID_TAG: + if (!gst_matroska_demux_parse_metadata_id_tag (demux, prevent_eos, + length, &taglist)) { + got_error = TRUE; + } + break; + + default: + GST_WARNING ("Unknown entry 0x%x in metadata header", id); + /* fall-through */ + + case GST_EBML_ID_VOID: + if (!gst_ebml_read_skip (ebml)) + got_error = TRUE; + break; + } + + if (demux->level_up) { + demux->level_up--; + break; + } + } + + if (gst_structure_n_fields (GST_STRUCTURE (taglist)) > 0) { gst_element_found_tags (GST_ELEMENT (ebml), taglist); } else { gst_tag_list_free (taglist); } - return res; + return (!got_error); } /* @@ -1767,11 +1955,19 @@ gst_matroska_demux_sync_streams (GstMatroskaDemux * demux) /* does it lag? 1 second is a random treshold... */ if (context->pos + (GST_SECOND / 2) < demux->pos) { - /* send filler */ GstEvent *event; + static gboolean showed_msg = FALSE; /* FIXME */ + + event = gst_event_new_filler (); + + /* FIXME: fillers in 0.9 aren't specified properly yet + event = gst_event_new_filler_stamped (context->pos, + demux->pos - context->pos); */ + if (!showed_msg) { + g_message ("%s: fix filler stuff when spec'ed out in core", G_STRLOC); + showed_msg = TRUE; + } - event = gst_event_new_filler_stamped (context->pos, - demux->pos - context->pos); context->pos = demux->pos; /* sync */ @@ -1781,30 +1977,148 @@ gst_matroska_demux_sync_streams (GstMatroskaDemux * demux) GST_TIME_ARGS (context->pos), GST_TIME_ARGS (demux->pos - context->pos), GST_TIME_ARGS (demux->pos)); - gst_pad_push (context->pad, GST_DATA (event)); + + gst_pad_push_event (context->pad, event); + } + } +} + +static gboolean +gst_matroska_demux_stream_is_first_vorbis_frame (GstMatroskaDemux * demux, + GstMatroskaTrackContext * stream) +{ + if (stream->type == GST_MATROSKA_TRACK_TYPE_AUDIO + && ((GstMatroskaTrackAudioContext *) stream)->first_frame == TRUE) { + return (strcmp (stream->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS) == 0); + } + return FALSE; +} + +static gboolean +gst_matroska_demux_push_vorbis_codec_priv_data (GstMatroskaDemux * demux, + GstMatroskaTrackContext * stream) +{ + GstFlowReturn ret; + GstBuffer *priv; + guint32 offset, length; + guchar *p; + gint i; + + /* start of the stream and vorbis audio, need to send the codec_priv + * data as first three packets */ + ((GstMatroskaTrackAudioContext *) stream)->first_frame = FALSE; + p = (guchar *) stream->codec_priv; + offset = 3; + + for (i = 0; i < 2; i++) { + length = p[i + 1]; + if (gst_pad_alloc_buffer (stream->pad, GST_BUFFER_OFFSET_NONE, + length, stream->caps, &priv) != GST_FLOW_OK) { + return FALSE; } + + memcpy (GST_BUFFER_DATA (priv), &p[offset], length); + + ret = gst_pad_push (stream->pad, priv); + if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) + return FALSE; + + offset += length; + } + length = stream->codec_priv_size - offset; + if (gst_pad_alloc_buffer (stream->pad, GST_BUFFER_OFFSET_NONE, length, + stream->caps, &priv) != GST_FLOW_OK) { + return FALSE; + } + memcpy (GST_BUFFER_DATA (priv), &p[offset], length); + ret = gst_pad_push (stream->pad, priv); + if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) + return FALSE; + + return TRUE; +} + +static gboolean +gst_matroska_demux_stream_is_wavpack (GstMatroskaTrackContext * stream) +{ + if (stream->type == GST_MATROSKA_TRACK_TYPE_AUDIO) { + return (strcmp (stream->codec_id, + GST_MATROSKA_CODEC_ID_AUDIO_WAVPACK4) == 0); } + return FALSE; +} + +static gboolean +gst_matroska_demux_add_wvpk_header (GstMatroskaTrackContext * stream, + gint block_length, GstBuffer ** buf) +{ + GstBuffer *newbuf; + guint8 *data; + guint newlen; + + /* we need to reconstruct the header of the wavpack block */ + Wavpack4Header wvh; + + wvh.ck_id[0] = 'w'; + wvh.ck_id[1] = 'v'; + wvh.ck_id[2] = 'p'; + wvh.ck_id[3] = 'k'; + /* -20 because ck_size is the size of the wavpack block -8 + * and lace_size is the size of the wavpack block + 12 + * (the three guint32 of the header that already are in the buffer) */ + wvh.ck_size = block_length + sizeof (Wavpack4Header) - 20; + wvh.version = GST_READ_UINT16_LE (stream->codec_priv); + wvh.track_no = 0; + wvh.index_no = 0; + wvh.total_samples = -1; + wvh.block_index = 0; + + /* block_samples, flags and crc are already in the buffer */ + newlen = block_length + sizeof (Wavpack4Header) - 12; + if (gst_pad_alloc_buffer (stream->pad, GST_BUFFER_OFFSET_NONE, newlen, + stream->caps, &newbuf) != GST_FLOW_OK) { + return FALSE; + } + + data = GST_BUFFER_DATA (newbuf); + data[0] = 'w'; + data[1] = 'v'; + data[2] = 'p'; + data[3] = 'k'; + GST_WRITE_UINT32_LE (data + 4, wvh.ck_size); + GST_WRITE_UINT16_LE (data + 8, wvh.version); + GST_WRITE_UINT8 (data + 10, wvh.track_no); + GST_WRITE_UINT8 (data + 11, wvh.index_no); + GST_WRITE_UINT32_LE (data + 12, wvh.total_samples); + GST_WRITE_UINT32_LE (data + 16, wvh.block_index); + g_memmove (data + 20, GST_BUFFER_DATA (*buf), block_length); + gst_buffer_stamp (newbuf, *buf); + gst_buffer_unref (*buf); + *buf = newbuf; + return TRUE; } static gboolean gst_matroska_demux_parse_blockgroup (GstMatroskaDemux * demux, guint64 cluster_time) { + GstMatroskaTrackContext *stream = NULL; GstEbmlRead *ebml = GST_EBML_READ (demux); - gboolean res = TRUE; + gboolean got_error = FALSE; gboolean readblock = FALSE; guint32 id; guint64 block_duration = 0; GstBuffer *buf = NULL; - gint stream = 0, n, laces = 0; - guint size = 0, *lace_size = NULL; + gint stream_num = 0, n, laces = 0; + guint size = 0; + gint *lace_size = NULL; gint64 time = 0; - while (res) { - if (!(id = gst_ebml_peek_id (ebml, &demux->level_up))) { - res = FALSE; - break; - } else if (demux->level_up) { + while (!got_error) { + if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) + goto error; + + if (demux->level_up) { demux->level_up--; break; } @@ -1813,15 +2127,17 @@ gst_matroska_demux_parse_blockgroup (GstMatroskaDemux * demux, /* one block inside the group. Note, block parsing is one * of the harder things, so this code is a bit complicated. * See http://www.matroska.org/ for documentation. */ - case GST_MATROSKA_ID_BLOCK:{ + case GST_MATROSKA_ID_BLOCK: + { + guint64 num; guint8 *data; gint flags = 0; - guint64 num; if (!gst_ebml_read_buffer (ebml, &id, &buf)) { - res = FALSE; + got_error = TRUE; break; } + data = GST_BUFFER_DATA (buf); size = GST_BUFFER_SIZE (buf); @@ -1829,31 +2145,30 @@ gst_matroska_demux_parse_blockgroup (GstMatroskaDemux * demux, if ((n = gst_matroska_ebmlnum_uint (data, size, &num)) < 0) { GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), ("Data error")); gst_buffer_unref (buf); - res = FALSE; + got_error = TRUE; break; } data += n; size -= n; /* fetch stream from num */ - stream = gst_matroska_demux_stream_from_num (demux, num); - if (size <= 3 || stream < 0 || stream >= demux->num_streams) { - gst_buffer_unref (buf); - GST_WARNING ("Invalid stream %d or size %u", stream, size); - break; - } - if (!GST_PAD_IS_USABLE (demux->src[stream]->pad)) { + stream_num = gst_matroska_demux_stream_from_num (demux, num); + if (size <= 3 || stream_num < 0 || stream_num >= demux->num_streams) { gst_buffer_unref (buf); + GST_WARNING ("Invalid stream %d or size %u", stream_num, size); break; } + stream = demux->src[stream_num]; + /* time (relative to cluster time) */ - time = (GINT16_FROM_BE (*(gint16 *) data)) * demux->time_scale; + time = ((gint16) GST_READ_UINT16_BE (data)) * demux->time_scale; data += 2; size -= 2; - flags = *(guint8 *) data; + flags = GST_READ_UINT8 (data); data += 1; size -= 1; + switch ((flags & 0x06) >> 1) { case 0x0: /* no lacing */ laces = 1; @@ -1865,10 +2180,10 @@ gst_matroska_demux_parse_blockgroup (GstMatroskaDemux * demux, case 0x2: /* fixed-size lacing */ case 0x3: /* EBML lacing */ if (size == 0) { - res = FALSE; + got_error = TRUE; break; } - laces = (*(guint8 *) data) + 1; + laces = GST_READ_UINT8 (data) + 1; data += 1; size -= 1; lace_size = g_new0 (gint, laces); @@ -1877,13 +2192,13 @@ gst_matroska_demux_parse_blockgroup (GstMatroskaDemux * demux, case 0x1: /* xiph lacing */ { guint temp, total = 0; - for (n = 0; res && n < laces - 1; n++) { + for (n = 0; !got_error && n < laces - 1; n++) { while (1) { if (size == 0) { - res = FALSE; + got_error = TRUE; break; } - temp = *(guint8 *) data; + temp = GST_READ_UINT8 (data); lace_size[n] += temp; data += 1; size -= 1; @@ -1907,20 +2222,20 @@ gst_matroska_demux_parse_blockgroup (GstMatroskaDemux * demux, if ((n = gst_matroska_ebmlnum_uint (data, size, &num)) < 0) { GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), ("Data error")); - res = FALSE; + got_error = TRUE; break; } data += n; size -= n; total = lace_size[0] = num; - for (n = 1; res && n < laces - 1; n++) { + for (n = 1; !got_error && n < laces - 1; n++) { gint64 snum; gint r; if ((r = gst_matroska_ebmlnum_sint (data, size, &snum)) < 0) { GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), ("Data error")); - res = FALSE; + got_error = TRUE; break; } data += r; @@ -1936,44 +2251,24 @@ gst_matroska_demux_parse_blockgroup (GstMatroskaDemux * demux, break; } - if (((GstMatroskaTrackAudioContext *) demux->src[stream])->first_frame - && (!strcmp (demux->src[stream]->codec_id, - GST_MATROSKA_CODEC_ID_AUDIO_VORBIS))) { - /* start of the stream and vorbis audio, need to send the codec_priv - * data as first three packets */ - guchar *p; - guint32 offset, length; - gint i; - GstBuffer *priv; - - ((GstMatroskaTrackAudioContext *) demux->src[stream])->first_frame = - FALSE; - p = (unsigned char *) demux->src[stream]->codec_priv; - offset = 3; - for (i = 0; i < 2; i++) { - length = p[i + 1]; - priv = gst_buffer_new_and_alloc (length); - memcpy (GST_BUFFER_DATA (priv), &p[offset], length); - gst_pad_push (demux->src[stream]->pad, GST_DATA (priv)); - offset += length; - } - length = demux->src[stream]->codec_priv_size - offset; - priv = gst_buffer_new_and_alloc (length); - memcpy (GST_BUFFER_DATA (priv), &p[offset], length); - gst_pad_push (demux->src[stream]->pad, GST_DATA (priv)); + if (gst_matroska_demux_stream_is_first_vorbis_frame (demux, stream)) { + if (!gst_matroska_demux_push_vorbis_codec_priv_data (demux, stream)) + got_error = TRUE; } + if (got_error) + break; + readblock = TRUE; break; } case GST_MATROSKA_ID_BLOCKDURATION:{ - if (!gst_ebml_read_uint (ebml, &id, &block_duration)) { - res = FALSE; - break; - } + if (!gst_ebml_read_uint (ebml, &id, &block_duration)) + got_error = TRUE; break; } + case GST_MATROSKA_ID_REFERENCEBLOCK:{ /* FIXME: this segfaults gint64 num; @@ -1984,18 +2279,17 @@ gst_matroska_demux_parse_blockgroup (GstMatroskaDemux * demux, GST_WARNING ("FIXME: implement support for ReferenceBlock"); */ if (!gst_ebml_read_skip (ebml)) - res = FALSE; + got_error = TRUE; break; } - default: GST_WARNING ("Unknown entry 0x%x in blockgroup data", id); /* fall-through */ case GST_EBML_ID_VOID: if (!gst_ebml_read_skip (ebml)) - res = FALSE; + got_error = TRUE; break; } @@ -2005,15 +2299,19 @@ gst_matroska_demux_parse_blockgroup (GstMatroskaDemux * demux, } } - if (res && readblock) { + if (!got_error && readblock) { guint64 duration = 0; + stream = demux->src[stream_num]; + if (block_duration) { duration = block_duration * demux->time_scale; - } else if (demux->src[stream]->default_duration) { - duration = demux->src[stream]->default_duration; + } else if (stream->default_duration) { + duration = stream->default_duration; } + for (n = 0; n < laces; n++) { + GstFlowReturn ret; GstBuffer *sub; if (lace_size[n] == 0) @@ -2030,77 +2328,87 @@ gst_matroska_demux_parse_blockgroup (GstMatroskaDemux * demux, demux->pos = GST_BUFFER_TIMESTAMP (sub); } - demux->src[stream]->pos = demux->pos; + stream->pos = demux->pos; gst_matroska_demux_sync_streams (demux); + if (gst_matroska_demux_stream_is_wavpack (stream)) { + if (!gst_matroska_demux_add_wvpk_header (stream, lace_size[n], &sub)) { + got_error = TRUE; + } + } + /* FIXME: do all laces have the same lenght? */ if (duration) { GST_BUFFER_DURATION (sub) = duration / laces; - demux->src[stream]->pos += GST_BUFFER_DURATION (sub); + stream->pos += GST_BUFFER_DURATION (sub); } + GST_DEBUG ("Pushing data of size %d for stream %d, time=%" GST_TIME_FORMAT " and duration=%" GST_TIME_FORMAT, - GST_BUFFER_SIZE (sub), stream, + GST_BUFFER_SIZE (sub), stream_num, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (sub)), GST_TIME_ARGS (GST_BUFFER_DURATION (sub))); - gst_pad_push (demux->src[stream]->pad, GST_DATA (sub)); + + gst_buffer_set_caps (sub, GST_PAD_CAPS (stream->pad)); + ret = gst_pad_push (stream->pad, sub); + if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) + got_error = TRUE; size -= lace_size[n]; } } + if (0) { + error: + got_error = TRUE; + } + if (readblock) gst_buffer_unref (buf); g_free (lace_size); - return res; + return (!got_error); } static gboolean gst_matroska_demux_parse_cluster (GstMatroskaDemux * demux) { GstEbmlRead *ebml = GST_EBML_READ (demux); - gboolean res = TRUE; - guint32 id; + gboolean got_error = FALSE; guint64 cluster_time = GST_CLOCK_TIME_NONE; + guint32 id; - /* We seek after index/header parsing before doing a new - * buffer. So here. */ - if (demux->seek_pending != GST_CLOCK_TIME_NONE) { - if (!gst_matroska_demux_handle_seek_event (demux)) + while (!got_error) { + if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) return FALSE; - demux->seek_pending = GST_CLOCK_TIME_NONE; - } - while (res) { - if (!(id = gst_ebml_peek_id (ebml, &demux->level_up))) { - res = FALSE; - break; - } else if (demux->level_up) { + if (demux->level_up) { demux->level_up--; break; } switch (id) { /* cluster timecode */ - case GST_MATROSKA_ID_CLUSTERTIMECODE:{ + case GST_MATROSKA_ID_CLUSTERTIMECODE: + { guint64 num; if (!gst_ebml_read_uint (ebml, &id, &num)) { - res = FALSE; - break; + got_error = TRUE; + } else { + cluster_time = num * demux->time_scale; } - cluster_time = num * demux->time_scale; break; } /* a group of blocks inside a cluster */ case GST_MATROSKA_ID_BLOCKGROUP: if (!gst_ebml_read_master (ebml, &id)) { - res = FALSE; - break; + got_error = TRUE; + } else { + if (!gst_matroska_demux_parse_blockgroup (demux, cluster_time)) + got_error = TRUE; } - res = gst_matroska_demux_parse_blockgroup (demux, cluster_time); break; default: @@ -2109,7 +2417,7 @@ gst_matroska_demux_parse_cluster (GstMatroskaDemux * demux) case GST_EBML_ID_VOID: if (!gst_ebml_read_skip (ebml)) - res = FALSE; + got_error = TRUE; break; } @@ -2119,175 +2427,200 @@ gst_matroska_demux_parse_cluster (GstMatroskaDemux * demux) } } - return res; + return (!got_error); } static gboolean -gst_matroska_demux_parse_contents (GstMatroskaDemux * demux) +gst_matroska_demux_parse_contents_seekentry (GstMatroskaDemux * demux, + gboolean * p_run_loop) { GstEbmlRead *ebml = GST_EBML_READ (demux); - gboolean res = TRUE; + gboolean got_error = FALSE; + guint64 seek_pos = (guint64) - 1; + guint32 seek_id = 0; guint32 id; - while (res) { - if (!(id = gst_ebml_peek_id (ebml, &demux->level_up))) { - res = FALSE; - break; - } else if (demux->level_up) { + if (!gst_ebml_read_master (ebml, &id)) + return FALSE; + + while (!got_error) { + if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) + return FALSE; + + if (demux->level_up) { demux->level_up--; break; } switch (id) { - case GST_MATROSKA_ID_SEEKENTRY:{ - guint32 seek_id = 0; - guint64 seek_pos = (guint64) - 1, t; + case GST_MATROSKA_ID_SEEKID: + { + guint64 t; - if (!gst_ebml_read_master (ebml, &id)) { - res = FALSE; - break; + if (!gst_ebml_read_uint (ebml, &id, &t)) { + got_error = TRUE; + } else { + seek_id = t; } + break; + } - while (res) { - if (!(id = gst_ebml_peek_id (ebml, &demux->level_up))) { - res = FALSE; - break; - } else if (demux->level_up) { - demux->level_up--; - break; - } + case GST_MATROSKA_ID_SEEKPOSITION: + { + guint64 t; - switch (id) { - case GST_MATROSKA_ID_SEEKID: - if (!gst_ebml_read_uint (ebml, &id, &t)) - res = FALSE; - seek_id = t; - break; + if (!gst_ebml_read_uint (ebml, &id, &t)) { + got_error = TRUE; + } else { + seek_pos = t; + } + break; + } - case GST_MATROSKA_ID_SEEKPOSITION: - if (!gst_ebml_read_uint (ebml, &id, &seek_pos)) - res = FALSE; - break; + default: + GST_WARNING ("Unknown seekhead ID 0x%x", id); + /* fall-through */ - default: - GST_WARNING ("Unknown seekhead ID 0x%x", id); - /* fall-through */ + case GST_EBML_ID_VOID: + if (!gst_ebml_read_skip (ebml)) + got_error = TRUE; + break; + } - case GST_EBML_ID_VOID: - if (!gst_ebml_read_skip (ebml)) - res = FALSE; - break; - } + if (demux->level_up) { + demux->level_up--; + break; + } + } - if (demux->level_up) { - demux->level_up--; - break; - } - } + if (got_error) + return FALSE; + + if (!seek_id || seek_pos == (guint64) - 1) { + GST_WARNING ("Incomplete seekhead entry (0x%x/%" + G_GUINT64_FORMAT ")", seek_id, seek_pos); + return TRUE; + } + + switch (seek_id) { + case GST_MATROSKA_ID_CUES: + case GST_MATROSKA_ID_TAGS: + { + guint level_up = demux->level_up; + guint64 before_pos, length; + GstEbmlLevel *level; + + /* remember */ + length = gst_ebml_read_get_length (ebml); + before_pos = ebml->offset; + + /* check for validity */ + if (seek_pos + demux->ebml_segment_start + 12 >= length) { + GST_WARNING_OBJECT (demux, + "Seekhead reference lies outside file!" " (%" + G_GUINT64_FORMAT "+%" G_GUINT64_FORMAT "+12 >= %" + G_GUINT64_FORMAT ")", seek_pos, demux->ebml_segment_start, length); + break; + } - if (!seek_id || seek_pos == (guint64) - 1) { - GST_WARNING ("Incomplete seekhead entry (0x%x/%" - G_GUINT64_FORMAT ")", seek_id, seek_pos); + /* seek */ + if (!gst_ebml_read_seek (ebml, seek_pos + demux->ebml_segment_start)) + return FALSE; + + /* we don't want to lose our seekhead level, so we add + * a dummy. This is a crude hack. */ + level = g_new (GstEbmlLevel, 1); + level->start = 0; + level->length = G_MAXUINT64; + ebml->level = g_list_append (ebml->level, level); + + /* check ID */ + if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) + return FALSE; + + if (id != seek_id) { + g_warning ("We looked for ID=0x%x but got ID=0x%x (pos=%" + G_GUINT64_FORMAT ")", seek_id, id, + seek_pos + demux->ebml_segment_start); + goto finish; + } + + /* read master + parse */ + switch (id) { + case GST_MATROSKA_ID_CUES: + if (!gst_ebml_read_master (ebml, &id)) + return FALSE; + if (!gst_matroska_demux_parse_index (demux, TRUE)) + return FALSE; + if (gst_ebml_read_get_length (ebml) == ebml->offset) + *p_run_loop = FALSE; + else + demux->index_parsed = TRUE; break; - } + case GST_MATROSKA_ID_TAGS: + if (!gst_ebml_read_master (ebml, &id)) + return FALSE; + if (!gst_matroska_demux_parse_metadata (demux, TRUE)) + return FALSE; + if (gst_ebml_read_get_length (ebml) == ebml->offset) + *p_run_loop = FALSE; + else + demux->metadata_parsed = TRUE; + break; + } - switch (seek_id) { - case GST_MATROSKA_ID_CUES: - case GST_MATROSKA_ID_TAGS:{ - guint level_up = demux->level_up; - guint64 before_pos, length; - guint32 id_cache = ebml->id_cache; - GstEbmlLevel *level; - GstEvent *event; - - /* remember */ - length = gst_bytestream_length (ebml->bs); - before_pos = gst_bytestream_tell (ebml->bs); - - /* check for validity */ - if (seek_pos + demux->segment_start + 12 >= length) { - g_warning ("Seekhead reference lies outside file!"); - break; - } + /* used to be here in 0.8 version, but makes mewmew sample not work */ + /* if (*p_run_loop == FALSE) break; */ - /* seek */ - if (!(event = gst_ebml_read_seek (ebml, - seek_pos + demux->segment_start))) - return FALSE; - gst_event_unref (event); - - /* we don't want to lose our seekhead level, so we add - * a dummy. This is a crude hack. */ - level = g_new (GstEbmlLevel, 1); - level->start = 0; - level->length = G_MAXUINT64; - ebml->level = g_list_append (ebml->level, level); - - /* check ID */ - if (!(id = gst_ebml_peek_id (ebml, &demux->level_up))) { - res = FALSE; - break; - } - if (id != seek_id) { - g_warning ("We looked for ID=0x%x but got ID=0x%x (pos=%" - G_GUINT64_FORMAT ")", - seek_id, id, seek_pos + demux->segment_start); - goto finish; - } + finish: + /* remove dummy level */ + while (ebml->level) { + guint64 length; - /* read master + parse */ - switch (id) { - case GST_MATROSKA_ID_CUES: - if (!gst_ebml_read_master (ebml, &id)) - res = FALSE; - else if (!gst_matroska_demux_parse_index (demux, TRUE) && - gst_bytestream_length (ebml->bs) != - gst_bytestream_tell (ebml->bs)) - res = FALSE; - else - demux->index_parsed = TRUE; - break; - case GST_MATROSKA_ID_TAGS: - if (!gst_ebml_read_master (ebml, &id)) - res = FALSE; - else if (!gst_matroska_demux_parse_metadata (demux, TRUE) && - gst_bytestream_length (ebml->bs) != - gst_bytestream_tell (ebml->bs)) - res = FALSE; - else - demux->metadata_parsed = TRUE; - break; - } - if (!res) - break; + level = g_list_last (ebml->level)->data; + ebml->level = g_list_remove (ebml->level, level); + length = level->length; + g_free (level); + if (length == G_MAXUINT64) + break; + } - finish: - /* remove dummy level */ - while (ebml->level) { - guint64 length; + /* seek back */ + (void) gst_ebml_read_seek (ebml, before_pos); + demux->level_up = level_up; + break; + } - level = g_list_last (ebml->level)->data; - ebml->level = g_list_remove (ebml->level, level); - length = level->length; - g_free (level); - if (length == G_MAXUINT64) - break; - } + default: + GST_INFO ("Ignoring seekhead entry for ID=0x%x", seek_id); + break; + } - /* seek back */ - if (!(event = gst_ebml_read_seek (ebml, before_pos))) - return FALSE; - gst_event_unref (event); - demux->level_up = level_up; - ebml->id_cache = id_cache; - break; - } + return (!got_error); +} - default: - GST_INFO ("Ignoring seekhead entry for ID=0x%x", seek_id); - break; - } +static gboolean +gst_matroska_demux_parse_contents (GstMatroskaDemux * demux, + gboolean * p_run_loop) +{ + GstEbmlRead *ebml = GST_EBML_READ (demux); + gboolean got_error = FALSE; + guint32 id; + + while (!got_error) { + if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) + return FALSE; + + if (demux->level_up) { + demux->level_up--; + break; + } + switch (id) { + case GST_MATROSKA_ID_SEEKENTRY: + { + if (!gst_matroska_demux_parse_contents_seekentry (demux, p_run_loop)) + got_error = TRUE; break; } @@ -2297,7 +2630,7 @@ gst_matroska_demux_parse_contents (GstMatroskaDemux * demux) case GST_EBML_ID_VOID: if (!gst_ebml_read_skip (ebml)) - res = FALSE; + got_error = TRUE; break; } @@ -2307,146 +2640,244 @@ gst_matroska_demux_parse_contents (GstMatroskaDemux * demux) } } - return res; + return (!got_error); } +/* returns FALSE on error, otherwise TRUE */ static gboolean -gst_matroska_demux_loop_stream (GstMatroskaDemux * demux) +gst_matroska_demux_loop_stream_parse_id (GstMatroskaDemux * demux, + guint32 id, gboolean * p_run_loop) { GstEbmlRead *ebml = GST_EBML_READ (demux); - gboolean res = TRUE; - guint32 id; - /* we've found our segment, start reading the different contents in here */ - while (res) { - if (!(id = gst_ebml_peek_id (ebml, &demux->level_up))) { - res = FALSE; + switch (id) { + /* stream info */ + case GST_MATROSKA_ID_INFO: + if (!gst_ebml_read_master (ebml, &id)) + return FALSE; + if (!gst_matroska_demux_parse_info (demux)) + return FALSE; break; - } else if (demux->level_up) { - demux->level_up--; + + /* track info headers */ + case GST_MATROSKA_ID_TRACKS: + { + if (!gst_ebml_read_master (ebml, &id)) + return FALSE; + if (!gst_matroska_demux_parse_tracks (demux)) + return FALSE; break; } - switch (id) { - /* stream info */ - case GST_MATROSKA_ID_INFO:{ - if (!gst_ebml_read_master (ebml, &id)) { - res = FALSE; - break; - } - res = gst_matroska_demux_parse_info (demux); - break; + /* stream index */ + case GST_MATROSKA_ID_CUES: + { + if (!demux->index_parsed) { + if (!gst_ebml_read_master (ebml, &id)) + return FALSE; + if (!gst_matroska_demux_parse_index (demux, FALSE)) + return FALSE; + } else { + if (!gst_ebml_read_skip (ebml)) + return FALSE; } + break; + } - /* track info headers */ - case GST_MATROSKA_ID_TRACKS:{ - if (!gst_ebml_read_master (ebml, &id)) { - res = FALSE; - break; - } - res = gst_matroska_demux_parse_tracks (demux); - break; + /* metadata */ + case GST_MATROSKA_ID_TAGS: + { + if (!demux->index_parsed) { + if (!gst_ebml_read_master (ebml, &id)) + return FALSE; + if (!gst_matroska_demux_parse_metadata (demux, FALSE)) + return FALSE; + } else { + if (!gst_ebml_read_skip (ebml)) + return FALSE; } + break; + } - /* stream index */ - case GST_MATROSKA_ID_CUES:{ - if (!demux->index_parsed) { - if (!gst_ebml_read_master (ebml, &id)) { - res = FALSE; - break; - } - res = gst_matroska_demux_parse_index (demux, FALSE); - } else - res = gst_ebml_read_skip (ebml); - break; - } + /* file index (if seekable, seek to Cues/Tags to parse it) */ + case GST_MATROSKA_ID_SEEKHEAD: + { + if (!gst_ebml_read_master (ebml, &id)) + return FALSE; + if (!gst_matroska_demux_parse_contents (demux, p_run_loop)) + return FALSE; + break; + } - /* metadata */ - case GST_MATROSKA_ID_TAGS:{ - if (!demux->index_parsed) { - if (!gst_ebml_read_master (ebml, &id)) { - res = FALSE; - break; - } - res = gst_matroska_demux_parse_metadata (demux, FALSE); - } else - res = gst_ebml_read_skip (ebml); - break; + case GST_MATROSKA_ID_CLUSTER: + { + if (demux->state != GST_MATROSKA_DEMUX_STATE_DATA) { + demux->state = GST_MATROSKA_DEMUX_STATE_DATA; + /* FIXME: different streams might have different lengths! */ + /* send initial discont */ + gst_matroska_demux_send_event (demux, + gst_event_new_newsegment (FALSE, 1.0, + GST_FORMAT_TIME, 0, demux->duration, 0)); + + GST_DEBUG_OBJECT (demux, "signaling no more pads"); + gst_element_no_more_pads (GST_ELEMENT (demux)); + } else { + if (!gst_ebml_read_master (ebml, &id)) + return FALSE; + + /* The idea is that we parse one cluster per loop and + * then break out of the loop here. In the next call + * of the loopfunc, we will get back here with the + * next cluster. If an error occurs, we didn't + * actually push a buffer, but we still want to break + * out of the loop to handle a possible error. We'll + * get back here if it's recoverable. */ + if (!gst_matroska_demux_parse_cluster (demux)) + return FALSE; + *p_run_loop = FALSE; } + break; + } - /* file index (if seekable, seek to Cues/Tags to parse it) */ - case GST_MATROSKA_ID_SEEKHEAD:{ - if (!gst_ebml_read_master (ebml, &id)) { - res = FALSE; - break; - } - res = gst_matroska_demux_parse_contents (demux); - break; - } + default: + GST_WARNING ("Unknown matroska file header ID 0x%x at %" + G_GUINT64_FORMAT, id, GST_EBML_READ (demux)->offset); + /* fall-through */ - case GST_MATROSKA_ID_CLUSTER: - if (demux->state != GST_MATROSKA_DEMUX_STATE_DATA) { - demux->state = GST_MATROSKA_DEMUX_STATE_DATA; - gst_element_no_more_pads (GST_ELEMENT (demux)); - } else { - if (!gst_ebml_read_reserve (ebml)) { - res = FALSE; - break; - } - if (!gst_ebml_read_master (ebml, &id)) { - res = FALSE; - break; - } - /* The idea is that we parse one cluster per loop and - * then break out of the loop here. In the next call - * of the loopfunc, we will get back here with the - * next cluster. If an error occurs, we didn't - * actually push a buffer, but we still want to break - * out of the loop to handle a possible error. We'll - * get back here if it's recoverable. */ - gst_matroska_demux_parse_cluster (demux); - res = FALSE; - } - break; + case GST_EBML_ID_VOID: + { + if (!gst_ebml_read_skip (ebml)) + return FALSE; + break; + } + } - default: - GST_WARNING ("Unknown matroska file header ID 0x%x", id); - /* fall-through */ + return TRUE; +} - case GST_EBML_ID_VOID: - if (!gst_ebml_read_skip (ebml)) - res = FALSE; - break; +static gboolean +gst_matroska_demux_loop_stream (GstMatroskaDemux * demux) +{ + GstEbmlRead *ebml = GST_EBML_READ (demux); + gboolean got_error = FALSE; + gboolean run_loop = TRUE; + guint32 id; + + /* we've found our segment, start reading the different contents in here */ + while (run_loop && !got_error) { + if (!gst_ebml_peek_id (ebml, &demux->level_up, &id)) + return FALSE; + + if (demux->level_up) { + demux->level_up--; + break; } + if (!gst_matroska_demux_loop_stream_parse_id (demux, id, &run_loop)) + got_error = TRUE; + if (demux->level_up) { demux->level_up--; break; } } - return res; + return (!got_error); } static void -gst_matroska_demux_loop (GstElement * element) +gst_matroska_demux_loop (GstPad * pad) { - GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (element); + GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (gst_pad_get_parent (pad)); + GstEbmlRead *ebml = GST_EBML_READ (demux); + gboolean ret; /* first, if we're to start, let's actually get starting */ if (demux->state == GST_MATROSKA_DEMUX_STATE_START) { if (!gst_matroska_demux_init_stream (demux)) { - return; + GST_DEBUG_OBJECT (demux, "init stream failed!"); + goto eos_and_pause; } demux->state = GST_MATROSKA_DEMUX_STATE_HEADER; } - gst_matroska_demux_loop_stream (demux); + ret = gst_matroska_demux_loop_stream (demux); + + /* check if we're at the end of a configured segment */ + if (demux->segment_play && GST_CLOCK_TIME_IS_VALID (demux->segment_stop)) { + guint i; + + for (i = 0; i < demux->num_streams; i++) { + if (demux->src[i]->pos >= demux->segment_stop) { + GST_LOG ("Reached end of segment (%" G_GUINT64_FORMAT "-%" + G_GUINT64_FORMAT ") on pad %s:%s", demux->segment_start, + demux->segment_stop, GST_DEBUG_PAD_NAME (demux->src[i]->pad)); + gst_element_post_message (GST_ELEMENT (demux), + gst_message_new_segment_done (GST_OBJECT (demux), GST_FORMAT_TIME, + demux->segment_stop)); + goto pause; + } + } + } + + if (ebml->offset == gst_ebml_read_get_length (ebml)) { + if (demux->segment_play) { + GST_LOG ("Reached end of stream and segment, posting message"); + gst_element_post_message (GST_ELEMENT (demux), + gst_message_new_segment_done (GST_OBJECT (demux), GST_FORMAT_TIME, + demux->duration)); + goto pause; + } + + GST_LOG ("Reached end of stream, sending EOS"); + goto eos_and_pause; + } + + if (ret == FALSE) { + GST_LOG ("Error processing stream, sending EOS"); + goto eos_and_pause; + } + + /* all is fine */ + gst_object_unref (demux); + return; + +eos_and_pause: + gst_matroska_demux_send_event (demux, gst_event_new_eos ()); + /* fallthrough */ +pause: + GST_LOG_OBJECT (demux, "pausing task"); + gst_pad_pause_task (demux->sinkpad); + gst_object_unref (demux); +} + +static gboolean +gst_matroska_demux_sink_activate (GstPad * sinkpad) +{ + if (gst_pad_check_pull_range (sinkpad)) + return gst_pad_activate_pull (sinkpad, TRUE); + + return FALSE; +} + +static gboolean +gst_matroska_demux_sink_activate_pull (GstPad * sinkpad, gboolean active) +{ + if (active) { + /* if we have a scheduler we can start the task */ + gst_pad_start_task (sinkpad, (GstTaskFunction) gst_matroska_demux_loop, + sinkpad); + } else { + gst_pad_stop_task (sinkpad); + } + + return TRUE; } static GstCaps * -gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * videocontext, - const gchar * codec_id, gpointer data, guint size, GstMatroskaDemux * demux) +gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * + videocontext, const gchar * codec_id, gpointer data, guint size, + gchar ** codec_name) { GstMatroskaTrackContext *context = (GstMatroskaTrackContext *) videocontext; GstCaps *caps = NULL; @@ -2455,9 +2886,6 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * videocontext, gst_riff_strf_vids *vids = NULL; if (data) { - char *codec_name = NULL; - GstTagList *list = gst_tag_list_new (); - vids = (gst_riff_strf_vids *) data; /* assure size is big enough */ @@ -2485,14 +2913,7 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * videocontext, vids->imp_colors = GUINT32_FROM_LE (vids->imp_colors); caps = gst_riff_create_video_caps (vids->compression, NULL, vids, - &codec_name); - gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_VIDEO_CODEC, - codec_name, NULL); - if (GST_IS_ELEMENT (demux)) - gst_element_found_tags (GST_ELEMENT (demux), list); - gst_tag_list_free (list); - if (codec_name) - g_free (codec_name); + NULL, NULL, codec_name); } else { caps = gst_riff_create_video_template_caps (); } @@ -2503,7 +2924,13 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * videocontext, switch (videocontext->fourcc) { case GST_MAKE_FOURCC ('I', '4', '2', '0'): + if (codec_name) + *codec_name = g_strdup ("Raw planar YUV 4:2:0"); + fourcc = videocontext->fourcc; + break; case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'): + if (codec_name) + *codec_name = g_strdup ("Raw packed YUV 4:2:2"); fourcc = videocontext->fourcc; break; @@ -2522,6 +2949,8 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * videocontext, } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_SP)) { caps = gst_caps_new_simple ("video/x-divx", "divxversion", G_TYPE_INT, 4, NULL); + if (codec_name) + *codec_name = g_strdup ("MPEG-4 simple profile"); } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP) || !strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AP)) { caps = gst_caps_new_full (gst_structure_new ("video/x-divx", @@ -2530,11 +2959,19 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * videocontext, gst_structure_new ("video/mpeg", "mpegversion", G_TYPE_INT, 4, "systemstream", G_TYPE_BOOLEAN, FALSE, NULL), NULL); + if (codec_name) { + if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP)) + *codec_name = g_strdup ("MPEG-4 advanced simple profile"); + else + *codec_name = g_strdup ("MPEG-4 advanced profile"); + } } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3)) { caps = gst_caps_new_full (gst_structure_new ("video/x-divx", "divxversion", G_TYPE_INT, 3, NULL), gst_structure_new ("video/x-msmpeg", "msmpegversion", G_TYPE_INT, 43, NULL), NULL); + if (codec_name) + *codec_name = g_strdup ("Microsoft MPEG-4 v.3"); } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_MPEG1) || !strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_MPEG2)) { gint mpegversion = -1; @@ -2549,8 +2986,24 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * videocontext, caps = gst_caps_new_simple ("video/mpeg", "systemstream", G_TYPE_BOOLEAN, FALSE, "mpegversion", G_TYPE_INT, mpegversion, NULL); + if (codec_name) + *codec_name = g_strdup_printf ("MPEG-%d video", mpegversion); } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_MJPEG)) { - caps = gst_caps_new_simple ("video/x-jpeg", NULL); + caps = gst_caps_new_simple ("image/jpeg", NULL); + if (codec_name) + *codec_name = g_strdup ("Motion-JPEG"); + } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC)) { + caps = gst_caps_new_simple ("video/x-h264", NULL); + if (data) { + GstBuffer *priv = gst_buffer_new_and_alloc (size); + + memcpy (GST_BUFFER_DATA (priv), data, size); + gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, priv, NULL); + gst_buffer_unref (priv); + + } + if (codec_name) + *codec_name = g_strdup ("H264"); } else { GST_WARNING ("Unknown codec '%s', cannot build Caps", codec_id); return NULL; @@ -2587,20 +3040,22 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * videocontext, n = videocontext->display_width * videocontext->pixel_height; d = videocontext->display_height * videocontext->pixel_width; GST_DEBUG ("setting PAR to %d/%d", n, d); - gst_structure_set (structure, "pixel-aspect-ratio", GST_TYPE_FRACTION, + gst_structure_set (structure, "pixel-aspect-ratio", + GST_TYPE_FRACTION, videocontext->display_width * videocontext->pixel_height, videocontext->display_height * videocontext->pixel_width, NULL); } if (context->default_duration > 0) { - gfloat framerate = 1. * GST_SECOND / context->default_duration; + gdouble framerate = (gdouble) GST_SECOND / context->default_duration; gst_structure_set (structure, "framerate", G_TYPE_DOUBLE, framerate, NULL); } else { /* sort of a hack to get most codecs to support, * even if the default_duration is missing */ - gst_structure_set (structure, "framerate", G_TYPE_DOUBLE, 25.0, NULL); + gst_structure_set (structure, "framerate", G_TYPE_DOUBLE, + (gdouble) 25.0, NULL); } } else { gst_structure_set (structure, @@ -2669,8 +3124,9 @@ aac_profile_idx (const gchar * codec_id) #define AAC_SYNC_EXTENSION_TYPE 0x02b7 static GstCaps * -gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * audiocontext, - const gchar * codec_id, gpointer data, guint size, GstMatroskaDemux * demux) +gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * + audiocontext, const gchar * codec_id, gpointer data, guint size, + gchar ** codec_name) { GstMatroskaTrackContext *context = (GstMatroskaTrackContext *) audiocontext; GstCaps *caps = NULL; @@ -2691,6 +3147,8 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * audiocontext, caps = gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 1, "layer", G_TYPE_INT, layer, NULL); + if (codec_name) + *codec_name = g_strdup_printf ("MPEG-1 layer %d", layer); } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE) || !strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE)) { gint endianness = -1; @@ -2713,6 +3171,9 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * audiocontext, "depth = (int) { 8, 16 }, " "width = (int) { 8, 16 }"); } gst_caps_set_simple (caps, "endianness", G_TYPE_INT, endianness, NULL); + if (codec_name && audiocontext) + *codec_name = g_strdup_printf ("Raw %d-bits PCM audio", + audiocontext->bitdepth); } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT)) { caps = gst_caps_new_simple ("audio/x-raw-float", "endianness", G_TYPE_INT, G_BYTE_ORDER, @@ -2723,19 +3184,25 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * audiocontext, } else { gst_caps_set_simple (caps, "width", GST_TYPE_INT_RANGE, 32, 64, NULL); } + if (codec_name && audiocontext) + *codec_name = g_strdup_printf ("Raw %d-bits floating-point audio", + audiocontext->bitdepth); } else if (!strncmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_AC3, - strlen (GST_MATROSKA_CODEC_ID_AUDIO_AC3)) || - !strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_DTS)) { + strlen (GST_MATROSKA_CODEC_ID_AUDIO_AC3))) { caps = gst_caps_new_simple ("audio/x-ac3", NULL); + if (codec_name) + *codec_name = g_strdup ("AC-3 audio"); + } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_DTS)) { + caps = gst_caps_new_simple ("audio/x-dts", NULL); + if (codec_name) + *codec_name = g_strdup ("DTS audio"); } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS)) { caps = gst_caps_new_simple ("audio/x-vorbis", NULL); + /* vorbis decoder does tags */ } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_ACM)) { gst_riff_strf_auds *auds = NULL; if (data) { - char *codec_name = NULL; - GstTagList *list = gst_tag_list_new (); - auds = (gst_riff_strf_auds *) data; /* little-endian -> byte-order */ @@ -2746,14 +3213,8 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * audiocontext, auds->blockalign = GUINT16_FROM_LE (auds->blockalign); auds->size = GUINT16_FROM_LE (auds->size); - caps = gst_riff_create_audio_caps (auds->format, NULL, auds, &codec_name); - gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_AUDIO_CODEC, - codec_name, NULL); - if (GST_IS_ELEMENT (demux)) - gst_element_found_tags (GST_ELEMENT (demux), list); - gst_tag_list_free (list); - if (codec_name) - g_free (codec_name); + caps = gst_riff_create_audio_caps (auds->format, NULL, auds, NULL, + NULL, codec_name); } else { caps = gst_riff_create_audio_template_caps (); } @@ -2799,10 +3260,13 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * audiocontext, g_assert (0); caps = gst_caps_new_simple ("audio/mpeg", - "mpegversion", G_TYPE_INT, mpegversion, NULL); + "mpegversion", G_TYPE_INT, mpegversion, + "framed", G_TYPE_BOOLEAN, TRUE, NULL); if (priv) { gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, priv, NULL); } + if (codec_name) + *codec_name = g_strdup_printf ("MPEG-%d AAC audio", mpegversion); } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_TTA)) { if (audiocontext != NULL) { caps = gst_caps_new_simple ("audio/x-tta", @@ -2811,6 +3275,19 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * audiocontext, caps = gst_caps_from_string ("audio/x-tta, " "width = (int) { 8, 16, 24 }"); } + if (codec_name) + *codec_name = g_strdup ("TTA audio"); + } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_WAVPACK4)) { + if (audiocontext != NULL) { + caps = gst_caps_new_simple ("audio/x-wavpack", + "width", G_TYPE_INT, audiocontext->bitdepth, + "framed", G_TYPE_BOOLEAN, TRUE, NULL); + } else { + caps = gst_caps_from_string ("audio/x-wavpack, " + "width = (int) { 8, 16, 24 }, " "framed = (boolean) true"); + } + if (codec_name) + *codec_name = g_strdup ("Wavpack audio"); } else { GST_WARNING ("Unknown codec '%s', cannot build Caps", codec_id); return NULL; @@ -2845,7 +3322,7 @@ gst_matroska_demux_complex_caps (GstMatroskaTrackComplexContext * { GstCaps *caps = NULL; - //.. + GST_DEBUG ("Unknown complex stream: codec_id='%s'", codec_id); return caps; } @@ -2860,6 +3337,9 @@ gst_matroska_demux_subtitle_caps (GstMatroskaTrackSubtitleContext * if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8)) { caps = gst_caps_new_simple ("text/plain", NULL); + } else { + GST_DEBUG ("Unknown subtitle stream: codec_id='%s'", codec_id); + caps = gst_caps_new_simple ("application/x-subtitle-unknown", NULL); } return caps; @@ -2870,7 +3350,17 @@ gst_matroska_demux_change_state (GstElement * element, GstStateChange transition) { GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (element); + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + /* handle upwards state changes here */ + switch (transition) { + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + /* handle downwards state changes */ switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: gst_matroska_demux_reset (GST_ELEMENT (demux)); @@ -2879,56 +3369,58 @@ gst_matroska_demux_change_state (GstElement * element, break; } - if (((GstElementClass *) parent_class)->change_state) - return ((GstElementClass *) parent_class)->change_state (element, - transition); - - return GST_STATE_CHANGE_SUCCESS; + return ret; } gboolean gst_matroska_demux_plugin_init (GstPlugin * plugin) { + GstCaps *videosrccaps; + GstCaps *audiosrccaps; + GstCaps *subtitlesrccaps; + GstCaps *temp; gint i; - GstCaps *videosrccaps, *audiosrccaps, *subtitlesrccaps, *temp; + const gchar *video_id[] = { GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC, GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED, GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_SP, GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP, + GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC, GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3, GST_MATROSKA_CODEC_ID_VIDEO_MPEG1, GST_MATROSKA_CODEC_ID_VIDEO_MPEG2, GST_MATROSKA_CODEC_ID_VIDEO_MJPEG, /* TODO: Real/Quicktime */ /* FILLME */ - NULL, - }, *audio_id[] = { + NULL + }; + const gchar *audio_id[] = { GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1, - GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2, - GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3, - GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE, - GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE, - GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT, - GST_MATROSKA_CODEC_ID_AUDIO_AC3, - GST_MATROSKA_CODEC_ID_AUDIO_ACM, - GST_MATROSKA_CODEC_ID_AUDIO_VORBIS, - GST_MATROSKA_CODEC_ID_AUDIO_TTA, - GST_MATROSKA_CODEC_ID_AUDIO_MPEG2, GST_MATROSKA_CODEC_ID_AUDIO_MPEG4, - /* TODO: AC3-9/10, Real, Musepack, Quicktime */ - /* FILLME */ - NULL,} - , *complex_id[] = { + GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2, + GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3, + GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE, + GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE, + GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT, + GST_MATROSKA_CODEC_ID_AUDIO_AC3, + GST_MATROSKA_CODEC_ID_AUDIO_ACM, + GST_MATROSKA_CODEC_ID_AUDIO_VORBIS, + GST_MATROSKA_CODEC_ID_AUDIO_TTA, + GST_MATROSKA_CODEC_ID_AUDIO_MPEG2, GST_MATROSKA_CODEC_ID_AUDIO_MPEG4, + GST_MATROSKA_CODEC_ID_AUDIO_WAVPACK4, + /* TODO: AC3-9/10, Real, Musepack, Quicktime */ + /* FILLME */ + NULL + }; + const gchar *complex_id[] = { /* FILLME */ - NULL,} - , *subtitle_id[] = { + NULL + }; + const gchar *subtitle_id[] = { GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8, - /* FILLME */ - NULL,}; - - /* this filter needs the riff parser */ - if (!gst_library_load ("gstbytestream") || !gst_library_load ("riff")) /* for fourcc stuff */ - return FALSE; + /* FILLME */ + NULL + }; /* video src template */ videosrccaps = gst_caps_new_empty (); @@ -2943,8 +3435,8 @@ gst_matroska_demux_plugin_init (GstPlugin * plugin) videosrctempl = gst_pad_template_new ("video_%02d", GST_PAD_SRC, GST_PAD_SOMETIMES, videosrccaps); - audiosrccaps = gst_caps_new_empty (); /* audio src template */ + audiosrccaps = gst_caps_new_empty (); for (i = 0; audio_id[i] != NULL; i++) { temp = gst_matroska_demux_audio_caps (NULL, audio_id[i], NULL, 0, NULL); gst_caps_append (audiosrccaps, temp); @@ -2952,12 +3444,14 @@ gst_matroska_demux_plugin_init (GstPlugin * plugin) audiosrctempl = gst_pad_template_new ("audio_%02d", GST_PAD_SRC, GST_PAD_SOMETIMES, audiosrccaps); - subtitlesrccaps = gst_caps_new_empty (); /* subtitle src template */ + subtitlesrccaps = gst_caps_new_empty (); for (i = 0; subtitle_id[i] != NULL; i++) { temp = gst_matroska_demux_subtitle_caps (NULL, subtitle_id[i], NULL, 0); gst_caps_append (subtitlesrccaps, temp); } + temp = gst_caps_new_simple ("application/x-subtitle-unknown", NULL); + gst_caps_append (subtitlesrccaps, temp); subtitlesrctempl = gst_pad_template_new ("subtitle_%02d", GST_PAD_SRC, GST_PAD_SOMETIMES, subtitlesrccaps); diff --git a/gst/matroska/matroska-demux.h b/gst/matroska/matroska-demux.h index 9e21a890..889bb650 100644 --- a/gst/matroska/matroska-demux.h +++ b/gst/matroska/matroska-demux.h @@ -23,7 +23,6 @@ #define __GST_MATROSKA_DEMUX_H__ #include <gst/gst.h> -#include <gst/bytestream/bytestream.h> #include "ebml-read.h" #include "matroska-ids.h" @@ -56,50 +55,57 @@ typedef struct _GstMatroskaDemuxIndex { } GstMatroskaDemuxIndex; typedef struct _GstMatroskaDemux { - GstEbmlRead parent; + GstEbmlRead parent; /* pads */ - GstPad *sinkpad; + GstPad *sinkpad; GstMatroskaTrackContext *src[GST_MATROSKA_DEMUX_MAX_STREAMS]; - guint num_streams, - num_v_streams, num_a_streams, num_t_streams; - GstClock *clock; + GstClock *clock; + guint num_streams; + guint num_v_streams; + guint num_a_streams; + guint num_t_streams; /* metadata */ - gchar *muxing_app, *writing_app; - gint64 created; + gchar *muxing_app; + gchar *writing_app; + gint64 created; /* state */ - GstMatroskaDemuxState state; - guint level_up; + GstMatroskaDemuxState state; + guint level_up; /* did we parse metadata/cues already? */ - gboolean metadata_parsed, - index_parsed; + gboolean metadata_parsed; + gboolean index_parsed; /* start-of-segment */ - guint64 segment_start; + guint64 ebml_segment_start; /* a cue (index) table */ - GstMatroskaIndex *index; - guint num_indexes; + GstMatroskaIndex *index; + guint num_indexes; /* timescale in the file */ - guint64 time_scale; + guint64 time_scale; /* length, position (time, ns) */ - guint64 duration, pos; + guint64 pos; + guint64 duration; /* a possible pending seek */ - guint64 seek_pending; + gboolean seek_pending; + + gdouble segment_rate; + gint64 segment_start; + gint64 segment_stop; + gboolean segment_play; } GstMatroskaDemux; typedef struct _GstMatroskaDemuxClass { GstEbmlReadClass parent; } GstMatroskaDemuxClass; -GType gst_matroska_demux_get_type (void); - gboolean gst_matroska_demux_plugin_init (GstPlugin *plugin); G_END_DECLS diff --git a/gst/matroska/matroska.c b/gst/matroska/matroska.c index aceb3402..b274b945 100644 --- a/gst/matroska/matroska.c +++ b/gst/matroska/matroska.c @@ -23,13 +23,13 @@ #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) && */ + return gst_matroska_demux_plugin_init (plugin) && gst_matroska_mux_plugin_init (plugin); } |