#define NAN (0.0 / 0.0)
#endif
-GST_DEBUG_CATEGORY_STATIC (ebmlread_debug);
+GST_DEBUG_CATEGORY (ebmlread_debug);
#define GST_CAT_DEFAULT ebmlread_debug
-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);
+/* Peeks following element id and element length in datastream provided
+ * by @peek with @ctx as user data.
+ * Returns GST_FLOW_UNEXPECTED if not enough data to read id and length.
+ * Otherwise, @needed provides the prefix length (id + length), and
+ * @length provides element length.
+ *
+ * @object and @offset are provided for informative messaging/debug purposes.
+ */
+GstFlowReturn
+gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed,
+ GstPeekData peek, gpointer * ctx, GstElement * el, guint64 offset)
+{
+ guint needed;
+ const guint8 *buf;
+ gint len_mask = 0x80, read = 1, n = 1, num_ffs = 0;
+ guint64 total;
+ guint8 b;
-/* convenience functions */
-static GstFlowReturn gst_ebml_read_peek_bytes (GstEbmlRead * ebml, guint size,
- GstBuffer ** p_buf, guint8 ** bytes);
-static GstFlowReturn gst_ebml_read_pull_bytes (GstEbmlRead * ebml, guint size,
- GstBuffer ** p_buf, guint8 ** bytes);
+ g_return_val_if_fail (_id != NULL, GST_FLOW_ERROR);
+ g_return_val_if_fail (_length != NULL, GST_FLOW_ERROR);
+ g_return_val_if_fail (_needed != NULL, GST_FLOW_ERROR);
+ /* well ... */
+ *_id = (guint32) GST_EBML_SIZE_UNKNOWN;
+ *_length = GST_EBML_SIZE_UNKNOWN;
-static GstElementClass *parent_class; /* NULL */
+ /* read element id */
+ needed = 2;
+ buf = peek (ctx, needed);
+ if (!buf)
+ goto not_enough_data;
-GType
-gst_ebml_read_get_type (void)
-{
- static GType gst_ebml_read_type; /* 0 */
-
- if (!gst_ebml_read_type) {
- static const GTypeInfo gst_ebml_read_info = {
- sizeof (GstEbmlReadClass),
- NULL,
- NULL,
- (GClassInitFunc) gst_ebml_read_class_init,
- NULL,
- NULL,
- sizeof (GstEbmlRead),
- 0,
- (GInstanceInitFunc) gst_ebml_read_init,
- };
-
- gst_ebml_read_type =
- g_type_register_static (GST_TYPE_ELEMENT, "GstEbmlRead",
- &gst_ebml_read_info, 0);
+ b = GST_READ_UINT8 (buf);
+ total = (guint64) b;
+ while (read <= 4 && !(total & len_mask)) {
+ read++;
+ len_mask >>= 1;
}
+ if (G_UNLIKELY (read > 4))
+ goto invalid_id;
- return gst_ebml_read_type;
-}
+ /* need id and at least something for subsequent length */
+ needed = read + 1;
+ buf = peek (ctx, needed);
+ if (!buf)
+ goto not_enough_data;
-void
-gst_ebml_level_free (GstEbmlLevel * level)
-{
- g_slice_free (GstEbmlLevel, level);
-}
+ while (n < read) {
+ b = GST_READ_UINT8 (buf + n);
+ total = (total << 8) | b;
+ ++n;
+ }
+ *_id = (guint32) total;
-static void
-gst_ebml_finalize (GObject * obj)
-{
- GstEbmlRead *ebml = GST_EBML_READ (obj);
-
- g_list_foreach (ebml->level, (GFunc) gst_ebml_level_free, NULL);
- g_list_free (ebml->level);
- ebml->level = NULL;
- if (ebml->cached_buffer) {
- gst_buffer_unref (ebml->cached_buffer);
- ebml->cached_buffer = NULL;
+ /* read element length */
+ b = GST_READ_UINT8 (buf + n);
+ total = (guint64) b;
+ len_mask = 0x80;
+ read = 1;
+ while (read <= 8 && !(total & len_mask)) {
+ read++;
+ len_mask >>= 1;
}
+ if (G_UNLIKELY (read > 8))
+ goto invalid_length;
+ if ((total &= (len_mask - 1)) == len_mask - 1)
+ num_ffs++;
- G_OBJECT_CLASS (parent_class)->finalize (obj);
-}
+ needed += read - 1;
+ buf = peek (ctx, needed);
+ if (!buf)
+ goto not_enough_data;
-static void
-gst_ebml_read_class_init (GstEbmlReadClass * klass)
-{
- GstElementClass *gstelement_class = (GstElementClass *) klass;
- GObjectClass *gobject_class = (GObjectClass *) klass;
-
- parent_class = g_type_class_peek_parent (klass);
+ buf += (needed - read);
+ n = 1;
+ while (n < read) {
+ guint8 b = GST_READ_UINT8 (buf + n);
- GST_DEBUG_CATEGORY_INIT (ebmlread_debug, "ebmlread",
- 0, "EBML stream helper class");
+ if (G_UNLIKELY (b == 0xff))
+ num_ffs++;
+ total = (total << 8) | b;
+ ++n;
+ }
- gobject_class->finalize = gst_ebml_finalize;
+ if (G_UNLIKELY (read == num_ffs))
+ *_length = G_MAXUINT64;
+ else
+ *_length = total;
+ *_length = total;
- gstelement_class->change_state =
- GST_DEBUG_FUNCPTR (gst_ebml_read_change_state);
-}
+ *_needed = needed;
-static void
-gst_ebml_read_init (GstEbmlRead * ebml)
-{
- ebml->sinkpad = NULL;
- ebml->level = NULL;
-}
+ return GST_FLOW_OK;
-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) {
- g_return_val_if_reached (GST_STATE_CHANGE_FAILURE);
- }
- break;
- default:
- break;
+ /* ERRORS */
+not_enough_data:
+ {
+ *_needed = needed;
+ return GST_FLOW_UNEXPECTED;
}
-
- 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) gst_ebml_level_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:
- break;
+invalid_id:
+ {
+ GST_ERROR_OBJECT (el,
+ "Invalid EBML ID size tag (0x%x) at position %" G_GUINT64_FORMAT " (0x%"
+ G_GINT64_MODIFIER "x)", (guint) b, offset, offset);
+ return GST_FLOW_ERROR;
+ }
+invalid_length:
+ {
+ GST_ERROR_OBJECT (el,
+ "Invalid EBML length size tag (0x%x) at position %" G_GUINT64_FORMAT
+ " (0x%" G_GINT64_MODIFIER "x)", (guint) b, offset, offset);
+ return GST_FLOW_ERROR;
}
-
- return ret;
}
-/*
- * Used in push mode.
- * Provided buffer is used as cache, based on offset 0, and no further reads
- * will be issued.
- */
-
+/* setup for parsing @buf at position @offset on behalf of @el.
+ * Takes ownership of @buf. */
void
-gst_ebml_read_reset_cache (GstEbmlRead * ebml, GstBuffer * buffer,
+gst_ebml_read_init (GstEbmlRead * ebml, GstElement * el, GstBuffer * buf,
guint64 offset)
{
- if (ebml->cached_buffer)
- gst_buffer_unref (ebml->cached_buffer);
-
- ebml->cached_buffer = buffer;
- ebml->push_cache = TRUE;
- buffer = gst_buffer_make_metadata_writable (buffer);
- GST_BUFFER_OFFSET (buffer) = offset;
- ebml->offset = offset;
- g_list_foreach (ebml->level, (GFunc) gst_ebml_level_free, NULL);
- g_list_free (ebml->level);
- ebml->level = NULL;
-}
+ GstEbmlMaster m;
-/*
- * Return: the amount of levels in the hierarchy that the
- * current element lies higher than the previous one.
- * The opposite isn't done - that's auto-done using master
- * element reading.
- */
+ g_return_if_fail (el);
+ g_return_if_fail (buf);
-static guint
-gst_ebml_read_element_level_up (GstEbmlRead * ebml)
-{
- guint num = 0;
- guint64 pos = ebml->offset;
-
- while (ebml->level != NULL) {
- GstEbmlLevel *level = ebml->level->data;
-
- if (pos >= level->start + level->length) {
- ebml->level = g_list_delete_link (ebml->level, ebml->level);
- gst_ebml_level_free (level);
- num++;
- } else {
- break;
- }
- }
-
- return num;
+ ebml->el = el;
+ ebml->offset = offset;
+ ebml->buf = buf;
+ ebml->readers = g_array_sized_new (FALSE, FALSE, sizeof (GstEbmlMaster), 10);
+ m.offset = ebml->offset;
+ gst_byte_reader_init (&m.br, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
+ g_array_append_val (ebml->readers, m);
}
-/*
- * Calls pull_range for (offset,size) without advancing our offset
- */
-static GstFlowReturn
-gst_ebml_read_peek_bytes (GstEbmlRead * ebml, guint size, GstBuffer ** p_buf,
- guint8 ** bytes)
+void
+gst_ebml_read_clear (GstEbmlRead * ebml)
{
- 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)) {
- if (p_buf)
- *p_buf = gst_buffer_create_sub (ebml->cached_buffer,
- ebml->offset - cache_offset, size);
- if (bytes)
- *bytes =
- GST_BUFFER_DATA (ebml->cached_buffer) + ebml->offset - cache_offset;
- return GST_FLOW_OK;
- }
- /* not enough data in the cache, free cache and get a new one */
- /* never drop pushed cache */
- if (ebml->push_cache) {
- if (ebml->offset == cache_offset + cache_size)
- return GST_FLOW_END;
- else
- return GST_FLOW_UNEXPECTED;
- }
- gst_buffer_unref (ebml->cached_buffer);
- ebml->cached_buffer = NULL;
- }
-
- /* refill the cache */
- ret = gst_pad_pull_range (ebml->sinkpad, ebml->offset, MAX (size, 64 * 1024),
- &ebml->cached_buffer);
- if (ret != GST_FLOW_OK) {
- ebml->cached_buffer = NULL;
- return ret;
- }
-
- if (GST_BUFFER_SIZE (ebml->cached_buffer) >= size) {
- if (p_buf)
- *p_buf = gst_buffer_create_sub (ebml->cached_buffer, 0, size);
- if (bytes)
- *bytes = GST_BUFFER_DATA (ebml->cached_buffer);
- return GST_FLOW_OK;
- }
-
- /* Not possible to get enough data, try a last time with
- * requesting exactly the size we need */
- gst_buffer_unref (ebml->cached_buffer);
- ebml->cached_buffer = NULL;
-
- ret =
- gst_pad_pull_range (ebml->sinkpad, ebml->offset, size,
- &ebml->cached_buffer);
- if (ret != GST_FLOW_OK) {
- GST_DEBUG_OBJECT (ebml, "pull_range returned %d", ret);
- if (p_buf)
- *p_buf = NULL;
- if (bytes)
- *bytes = NULL;
- return ret;
- }
-
- if (GST_BUFFER_SIZE (ebml->cached_buffer) < 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 (ebml->cached_buffer));
-
- gst_buffer_unref (ebml->cached_buffer);
- ebml->cached_buffer = NULL;
- if (p_buf)
- *p_buf = NULL;
- if (bytes)
- *bytes = NULL;
- return GST_FLOW_UNEXPECTED;
- }
-
- if (p_buf)
- *p_buf = gst_buffer_create_sub (ebml->cached_buffer, 0, size);
- if (bytes)
- *bytes = GST_BUFFER_DATA (*p_buf);
-
- return GST_FLOW_OK;
+ if (ebml->readers)
+ g_array_free (ebml->readers, TRUE);
+ ebml->readers = NULL;
+ if (ebml->buf)
+ gst_buffer_unref (ebml->buf);
+ ebml->buf = NULL;
+ ebml->el = NULL;
}
-/*
- * Calls pull_range for (offset,size) and advances our offset by size
- */
-static GstFlowReturn
-gst_ebml_read_pull_bytes (GstEbmlRead * ebml, guint size, GstBuffer ** p_buf,
- guint8 ** bytes)
+static const guint8 *
+gst_ebml_read_peek (GstByteReader * br, guint peek)
{
- GstFlowReturn ret;
-
- ret = gst_ebml_read_peek_bytes (ebml, size, p_buf, bytes);
- if (ret != GST_FLOW_OK)
- return ret;
+ const guint8 *data;
- ebml->offset += size;
- return GST_FLOW_OK;
+ if (G_LIKELY (gst_byte_reader_peek_data (br, peek, &data)))
+ return data;
+ else
+ return NULL;
}
-/*
- * Read: the element content data ID.
- * Return: FALSE on error.
- */
-
static GstFlowReturn
-gst_ebml_read_element_id (GstEbmlRead * ebml, guint32 * id, guint * level_up)
+gst_ebml_peek_id_full (GstEbmlRead * ebml, guint32 * id, guint64 * length,
+ guint * prefix)
{
- guint8 *buf;
- gint len_mask = 0x80, read = 1, n = 1;
- guint32 total;
- guint8 b;
GstFlowReturn ret;
+ const guint8 *data = NULL;
- ret = gst_ebml_read_peek_bytes (ebml, 1, NULL, &buf);
+ ret = gst_ebml_peek_id_length (id, length, prefix,
+ (GstPeekData) gst_ebml_read_peek, (gpointer) gst_ebml_read_br (ebml),
+ ebml->el, gst_ebml_read_get_pos (ebml));
if (ret != GST_FLOW_OK)
return ret;
- b = GST_READ_UINT8 (buf);
-
- total = (guint32) b;
-
- while (read <= 4 && !(total & len_mask)) {
- read++;
- len_mask >>= 1;
- }
- if (read > 4) {
- GST_ERROR_OBJECT (ebml,
- "Invalid EBML ID size tag (0x%x) at position %" G_GUINT64_FORMAT " (0x%"
- G_GINT64_MODIFIER "x)", (guint) b, ebml->offset, ebml->offset);
- return GST_FLOW_ERROR;
- }
+ GST_LOG_OBJECT (ebml->el, "id 0x%x at offset 0x%" G_GINT64_MODIFIER "x"
+ " of length %" G_GUINT64_FORMAT ", prefix %d", *id,
+ gst_ebml_read_get_pos (ebml), *length, *prefix);
- ret = gst_ebml_read_peek_bytes (ebml, read, NULL, &buf);
- if (ret != GST_FLOW_OK)
- return ret;
+#ifndef GST_DISABLE_GST_DEBUG
+ {
+ GstByteReader *br = gst_ebml_read_br (ebml);
+ guint size = gst_byte_reader_get_remaining (br);
+ gst_byte_reader_peek_data (br, size, &data);
- while (n < read) {
- b = GST_READ_UINT8 (buf + n);
- total = (total << 8) | b;
- ++n;
+ GST_LOG_OBJECT (ebml->el, "current br %p; remaining %d", br, size);
+ if (data)
+ GST_MEMDUMP_OBJECT (ebml->el, "element", data, MIN (size, *length));
}
+#endif
- *id = total;
+ return ret;
+}
- /* level */
- if (level_up)
- *level_up = gst_ebml_read_element_level_up (ebml);
+GstFlowReturn
+gst_ebml_peek_id (GstEbmlRead * ebml, guint32 * id)
+{
+ guint64 length;
+ guint needed;
- ebml->offset += read;
- return GST_FLOW_OK;
+ return gst_ebml_peek_id_full (ebml, id, &length, &needed);
}
/*
- * Read: element content length.
- * Return: the number of bytes read or -1 on error.
+ * Read the next element, the contents are supposed to be sub-elements which
+ * can be read separately. A new bytereader is setup for doing so.
*/
-
-static GstFlowReturn
-gst_ebml_read_element_length (GstEbmlRead * ebml, guint64 * length,
- gint * rread)
+GstFlowReturn
+gst_ebml_read_master (GstEbmlRead * ebml, guint32 * id)
{
+ guint64 length;
+ guint prefix;
+ const guint8 *data;
GstFlowReturn ret;
- guint8 *buf;
- gint len_mask = 0x80, read = 1, n = 1, num_ffs = 0;
- guint64 total;
- guint8 b;
+ GstEbmlMaster m;
- ret = gst_ebml_read_peek_bytes (ebml, 1, NULL, &buf);
+ ret = gst_ebml_peek_id_full (ebml, id, &length, &prefix);
if (ret != GST_FLOW_OK)
return ret;
- b = GST_READ_UINT8 (buf);
+ /* we just at least peeked the id */
+ g_assert (gst_byte_reader_skip (gst_ebml_read_br (ebml), prefix));
- total = (guint64) b;
+ m.offset = gst_ebml_read_get_pos (ebml);
+ if (!gst_byte_reader_get_data (gst_ebml_read_br (ebml), length, &data))
+ return GST_FLOW_PARSE;
- while (read <= 8 && !(total & len_mask)) {
- read++;
- len_mask >>= 1;
- }
- if (read > 8) {
- GST_ERROR_OBJECT (ebml,
- "Invalid EBML length size tag (0x%x) at position %" G_GUINT64_FORMAT
- " (0x%" G_GINT64_MODIFIER "x)", (guint) b, ebml->offset, ebml->offset);
- return GST_FLOW_ERROR;
- }
-
- if ((total &= (len_mask - 1)) == len_mask - 1)
- num_ffs++;
-
- ret = gst_ebml_read_peek_bytes (ebml, read, NULL, &buf);
- if (ret != GST_FLOW_OK)
- return ret;
-
- while (n < read) {
- guint8 b = GST_READ_UINT8 (buf + n);
-
- if (b == 0xff)
- num_ffs++;
- total = (total << 8) | b;
- ++n;
- }
-
- if (read == num_ffs)
- *length = G_MAXUINT64;
- else
- *length = total;
-
- if (rread)
- *rread = read;
-
- ebml->offset += read;
+ GST_LOG_OBJECT (ebml->el, "pushing level %d at offset %" G_GUINT64_FORMAT,
+ ebml->readers->len, m.offset);
+ gst_byte_reader_init (&m.br, data, length);
+ g_array_append_val (ebml->readers, m);
return GST_FLOW_OK;
}
-/*
- * Return: the ID of the next element.
- * Level_up contains the amount of levels that this
- * next element lies higher than the previous one.
- */
-
+/* explicitly pop a bytereader from stack. Usually invoked automagically. */
GstFlowReturn
-gst_ebml_peek_id (GstEbmlRead * ebml, guint * level_up, guint32 * id)
+gst_ebml_read_pop_master (GstEbmlRead * ebml)
{
- guint64 off;
- guint level_up_tmp = 0;
- GstFlowReturn ret;
-
- g_assert (level_up);
- g_assert (id);
-
- *level_up = 0;
-
-next:
- off = ebml->offset; /* save offset */
+ g_return_val_if_fail (ebml->readers, GST_FLOW_ERROR);
- if ((ret = gst_ebml_read_element_id (ebml, id, &level_up_tmp)) != GST_FLOW_OK) {
- if (ret != GST_FLOW_END)
- return ret;
- else {
- /* simulate dummy VOID element,
- * and have the call stack bail out all the way */
- *id = GST_EBML_ID_VOID;
- *level_up = G_MAXUINT32 >> 2;
- return GST_FLOW_OK;
- }
- }
-
- ebml->offset = off; /* restore offset */
-
- *level_up += level_up_tmp;
- level_up_tmp = 0;
-
- switch (*id) {
- case GST_EBML_ID_VOID:
- GST_DEBUG_OBJECT (ebml, "Skipping EBML Void element");
- if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK)
- return ret;
- goto next;
- break;
- case GST_EBML_ID_CRC32:
- GST_DEBUG_OBJECT (ebml, "Skipping EBML CRC32 element");
- if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK)
- return ret;
- goto next;
- break;
+ /* never remove initial bytereader */
+ if (ebml->readers->len > 1) {
+ GST_LOG_OBJECT (ebml->el, "popping level %d", ebml->readers->len - 1);
+ g_array_remove_index (ebml->readers, ebml->readers->len - 1);
}
- return ret;
-}
-
-/*
- * Return the length of the stream in bytes
- */
-
-gint64
-gst_ebml_read_get_length (GstEbmlRead * ebml)
-{
- GstFormat fmt = GST_FORMAT_BYTES;
- gint64 end;
-
- /* FIXME: what to do if we don't get the upstream length */
- if (!gst_pad_query_peer_duration (ebml->sinkpad, &fmt, &end) ||
- fmt != GST_FORMAT_BYTES || end < 0)
- g_return_val_if_reached (0);
-
- return end;
-}
-
-/*
- * Seek to a given offset.
- */
-
-GstFlowReturn
-gst_ebml_read_seek (GstEbmlRead * ebml, guint64 offset)
-{
- if (offset >= gst_ebml_read_get_length (ebml))
- return GST_FLOW_UNEXPECTED;
-
- ebml->offset = offset;
-
return GST_FLOW_OK;
}
{
guint64 length;
guint32 id;
+ guint prefix;
GstFlowReturn ret;
- ret = gst_ebml_read_element_id (ebml, &id, NULL);
+ ret = gst_ebml_peek_id_full (ebml, &id, &length, &prefix);
if (ret != GST_FLOW_OK)
return ret;
- ret = gst_ebml_read_element_length (ebml, &length, NULL);
- if (ret != GST_FLOW_OK)
- return ret;
+ if (!gst_byte_reader_skip (gst_ebml_read_br (ebml), length + prefix))
+ return GST_FLOW_PARSE;
- ebml->offset += length;
return ret;
}
gst_ebml_read_buffer (GstEbmlRead * ebml, guint32 * id, GstBuffer ** buf)
{
guint64 length;
+ guint prefix;
GstFlowReturn ret;
- ret = gst_ebml_read_element_id (ebml, id, NULL);
+ ret = gst_ebml_peek_id_full (ebml, id, &length, &prefix);
if (ret != GST_FLOW_OK)
return ret;
- ret = gst_ebml_read_element_length (ebml, &length, NULL);
- if (ret != GST_FLOW_OK)
- return ret;
+ /* we just at least peeked the id */
+ g_assert (gst_byte_reader_skip (gst_ebml_read_br (ebml), prefix));
+
+ if (G_LIKELY (length > 0)) {
+ guint offset;
- if (length == 0) {
+ offset = gst_ebml_read_get_pos (ebml) - ebml->offset;
+ if (G_LIKELY (gst_byte_reader_skip (gst_ebml_read_br (ebml), length))) {
+ *buf = gst_buffer_create_sub (ebml->buf, offset, length);
+ } else {
+ *buf = NULL;
+ return GST_FLOW_PARSE;
+ }
+ } else {
*buf = gst_buffer_new ();
- return GST_FLOW_OK;
}
- *buf = NULL;
- ret = gst_ebml_read_pull_bytes (ebml, (guint) length, buf, NULL);
-
return ret;
}
*/
static GstFlowReturn
-gst_ebml_read_bytes (GstEbmlRead * ebml, guint32 * id, guint8 ** data,
+gst_ebml_read_bytes (GstEbmlRead * ebml, guint32 * id, const guint8 ** data,
guint * size)
{
guint64 length;
+ guint prefix;
GstFlowReturn ret;
*size = 0;
- ret = gst_ebml_read_element_id (ebml, id, NULL);
+ ret = gst_ebml_peek_id_full (ebml, id, &length, &prefix);
if (ret != GST_FLOW_OK)
return ret;
- ret = gst_ebml_read_element_length (ebml, &length, NULL);
- if (ret != GST_FLOW_OK)
- return ret;
-
- if (length == 0) {
- *data = NULL;
- return ret;
- }
+ /* we just at least peeked the id */
+ g_assert (gst_byte_reader_skip (gst_ebml_read_br (ebml), prefix));
*data = NULL;
- ret = gst_ebml_read_pull_bytes (ebml, (guint) length, NULL, data);
- if (ret != GST_FLOW_OK)
- return ret;
+ if (G_LIKELY (length >= 0)) {
+ if (!gst_byte_reader_get_data (gst_ebml_read_br (ebml), length, data))
+ return GST_FLOW_PARSE;
+ }
- *size = (guint) length;
+ *size = length;
return ret;
}
GstFlowReturn
gst_ebml_read_uint (GstEbmlRead * ebml, guint32 * id, guint64 * num)
{
- guint8 *data;
+ const guint8 *data;
guint size;
GstFlowReturn ret;
return ret;
if (size < 1 || size > 8) {
- GST_ERROR_OBJECT (ebml,
+ GST_ERROR_OBJECT (ebml->el,
"Invalid integer element size %d at position %" G_GUINT64_FORMAT " (0x%"
- G_GINT64_MODIFIER "x)", size, ebml->offset - size, ebml->offset - size);
+ G_GINT64_MODIFIER "x)", size, gst_ebml_read_get_pos (ebml) - size,
+ gst_ebml_read_get_pos (ebml) - size);
return GST_FLOW_ERROR;
}
*num = 0;
GstFlowReturn
gst_ebml_read_sint (GstEbmlRead * ebml, guint32 * id, gint64 * num)
{
- guint8 *data;
+ const guint8 *data;
guint size;
gboolean negative = 0;
GstFlowReturn ret;
return ret;
if (size < 1 || size > 8) {
- GST_ERROR_OBJECT (ebml,
+ GST_ERROR_OBJECT (ebml->el,
"Invalid integer element size %d at position %" G_GUINT64_FORMAT " (0x%"
- G_GINT64_MODIFIER "x)", size, ebml->offset - size, ebml->offset - size);
+ G_GINT64_MODIFIER "x)", size, gst_ebml_read_get_pos (ebml) - size,
+ gst_ebml_read_get_pos (ebml) - size);
return GST_FLOW_ERROR;
}
};
static gdouble
-_ext2dbl (guint8 * data)
+_ext2dbl (const guint8 * data)
{
struct _ext_float ext;
guint64 m = 0;
GstFlowReturn
gst_ebml_read_float (GstEbmlRead * ebml, guint32 * id, gdouble * num)
{
- guint8 *data;
+ const guint8 *data;
guint size;
GstFlowReturn ret;
return ret;
if (size != 4 && size != 8 && size != 10) {
- GST_ERROR_OBJECT (ebml,
+ GST_ERROR_OBJECT (ebml->el,
"Invalid float element size %d at position %" G_GUINT64_FORMAT " (0x%"
- G_GINT64_MODIFIER "x)", size, ebml->offset - size, ebml->offset - size);
+ G_GINT64_MODIFIER "x)", size, gst_ebml_read_get_pos (ebml) - size,
+ gst_ebml_read_get_pos (ebml) - size);
return GST_FLOW_ERROR;
}
static GstFlowReturn
gst_ebml_read_string (GstEbmlRead * ebml, guint32 * id, gchar ** str)
{
- guint8 *data;
+ const guint8 *data;
guint size;
GstFlowReturn ret;
GstFlowReturn ret;
#ifndef GST_DISABLE_GST_DEBUG
- guint64 oldoff = ebml->offset;
+ guint64 oldoff = gst_ebml_read_get_pos (ebml);
#endif
ret = gst_ebml_read_string (ebml, id, str);
if (str != NULL && *str != NULL && **str != '\0' &&
!g_utf8_validate (*str, -1, NULL)) {
- GST_WARNING_OBJECT (ebml,
+ GST_WARNING_OBJECT (ebml->el,
"Invalid UTF-8 string at offset %" G_GUINT64_FORMAT, oldoff);
}
return ret;
}
-/*
- * Read the next element, but only the header. The contents
- * are supposed to be sub-elements which can be read separately.
- */
-
-GstFlowReturn
-gst_ebml_read_master (GstEbmlRead * ebml, guint32 * id)
-{
- GstEbmlLevel *level;
- guint64 length;
- GstFlowReturn ret;
-
- ret = gst_ebml_read_element_id (ebml, id, NULL);
- if (ret != GST_FLOW_OK)
- return ret;
-
- ret = gst_ebml_read_element_length (ebml, &length, NULL);
- if (ret != GST_FLOW_OK)
- return ret;
-
- /* remember level */
- level = g_slice_new (GstEbmlLevel);
- level->start = ebml->offset;
- level->length = length;
- ebml->level = g_list_prepend (ebml->level, level);
-
- return GST_FLOW_OK;
-}
-
/*
* Read the next element as binary data.
*/
gst_ebml_read_binary (GstEbmlRead * ebml,
guint32 * id, guint8 ** binary, guint64 * length)
{
- guint8 *data;
+ const guint8 *data;
guint size;
GstFlowReturn ret;
return GST_FLOW_OK;
}
-
-/*
- * Read an EBML header.
- */
-
-GstFlowReturn
-gst_ebml_read_header (GstEbmlRead * ebml, gchar ** doctype, guint * version)
-{
- /* this function is the first to be called */
- guint32 id;
- guint level_up;
- GstFlowReturn ret;
-
- /* default init */
- if (doctype)
- *doctype = NULL;
- if (version)
- *version = 1;
-
- ret = gst_ebml_peek_id (ebml, &level_up, &id);
- if (ret != GST_FLOW_OK)
- return ret;
-
- GST_DEBUG_OBJECT (ebml, "id: %08x", GST_READ_UINT32_BE (&id));
-
- if (level_up != 0 || id != GST_EBML_ID_HEADER) {
- GST_ERROR_OBJECT (ebml, "Failed to read header");
- return GST_FLOW_ERROR;
- }
- ret = gst_ebml_read_master (ebml, &id);
- if (ret != GST_FLOW_OK)
- return ret;
-
- while (TRUE) {
- ret = gst_ebml_peek_id (ebml, &level_up, &id);
- if (ret != GST_FLOW_OK)
- return ret;
-
- /* end-of-header */
- if (level_up)
- break;
-
- switch (id) {
- /* is our read version uptodate? */
- case GST_EBML_ID_EBMLREADVERSION:{
- guint64 num;
-
- ret = gst_ebml_read_uint (ebml, &id, &num);
- if (ret != GST_FLOW_OK)
- return ret;
- g_assert (id == GST_EBML_ID_EBMLREADVERSION);
- if (num != GST_EBML_VERSION) {
- GST_ERROR_OBJECT (ebml, "Unsupported EBML version %" G_GUINT64_FORMAT,
- num);
- return GST_FLOW_ERROR;
- }
-
- GST_DEBUG_OBJECT (ebml, "EbmlReadVersion: %" G_GUINT64_FORMAT, num);
- break;
- }
-
- /* we only handle 8 byte lengths at max */
- case GST_EBML_ID_EBMLMAXSIZELENGTH:{
- guint64 num;
-
- ret = gst_ebml_read_uint (ebml, &id, &num);
- if (ret != GST_FLOW_OK)
- return ret;
- g_assert (id == GST_EBML_ID_EBMLMAXSIZELENGTH);
- if (num > sizeof (guint64)) {
- GST_ERROR_OBJECT (ebml,
- "Unsupported EBML maximum size %" G_GUINT64_FORMAT, num);
- return GST_FLOW_ERROR;
- }
- GST_DEBUG_OBJECT (ebml, "EbmlMaxSizeLength: %" G_GUINT64_FORMAT, num);
- break;
- }
-
- /* we handle 4 byte IDs at max */
- case GST_EBML_ID_EBMLMAXIDLENGTH:{
- guint64 num;
-
- ret = gst_ebml_read_uint (ebml, &id, &num);
- if (ret != GST_FLOW_OK)
- return ret;
- g_assert (id == GST_EBML_ID_EBMLMAXIDLENGTH);
- if (num > sizeof (guint32)) {
- GST_ERROR_OBJECT (ebml,
- "Unsupported EBML maximum ID %" G_GUINT64_FORMAT, num);
- return GST_FLOW_ERROR;
- }
- GST_DEBUG_OBJECT (ebml, "EbmlMaxIdLength: %" G_GUINT64_FORMAT, num);
- break;
- }
-
- case GST_EBML_ID_DOCTYPE:{
- gchar *text;
-
- ret = gst_ebml_read_ascii (ebml, &id, &text);
- if (ret != GST_FLOW_OK)
- return ret;
- g_assert (id == GST_EBML_ID_DOCTYPE);
-
- GST_DEBUG_OBJECT (ebml, "EbmlDocType: %s", GST_STR_NULL (text));
-
- if (doctype) {
- g_free (*doctype);
- *doctype = text;
- } else
- g_free (text);
- break;
- }
-
- case GST_EBML_ID_DOCTYPEREADVERSION:{
- guint64 num;
-
- ret = gst_ebml_read_uint (ebml, &id, &num);
- if (ret != GST_FLOW_OK)
- return ret;
- g_assert (id == GST_EBML_ID_DOCTYPEREADVERSION);
- if (version)
- *version = num;
- GST_DEBUG_OBJECT (ebml, "EbmlReadVersion: %" G_GUINT64_FORMAT, num);
- break;
- }
-
- default:
- GST_WARNING_OBJECT (ebml,
- "Unknown data type 0x%x in EBML header (ignored)", id);
- /* pass-through */
-
- /* we ignore these two, as they don't tell us anything we care about */
- case GST_EBML_ID_EBMLVERSION:
- case GST_EBML_ID_DOCTYPEVERSION:
- ret = gst_ebml_read_skip (ebml);
- if (ret != GST_FLOW_OK)
- return ret;
- break;
- }
- }
-
- return GST_FLOW_OK;
-}
#define DEBUG_ELEMENT_START(demux, ebml, element) \
GST_DEBUG_OBJECT (demux, "Parsing " element " element at offset %" \
- G_GUINT64_FORMAT, ebml->offset)
+ G_GUINT64_FORMAT, gst_ebml_read_get_pos (ebml))
#define DEBUG_ELEMENT_STOP(demux, ebml, element, ret) \
- GST_DEBUG_OBJECT (demux, "Parsing " element " element at offset %" \
- G_GUINT64_FORMAT " finished with '%s'", ebml->offset, \
- gst_flow_get_name (ret))
+ GST_DEBUG_OBJECT (demux, "Parsing " element " element " \
+ " finished with '%s'", gst_flow_get_name (ret))
enum
{
"subpicture/x-pgs; subtitle/x-kate; " "application/x-subtitle-unknown")
);
-static GstFlowReturn gst_matroska_demux_parse_contents (GstMatroskaDemux *
- demux);
+static GstFlowReturn gst_matroska_demux_parse_id (GstMatroskaDemux * demux,
+ guint32 id, guint64 length, guint needed);
/* element functions */
static void gst_matroska_demux_loop (GstPad * pad);
guint64 offset);
GType gst_matroska_demux_get_type (void);
-GST_BOILERPLATE (GstMatroskaDemux, gst_matroska_demux, GstEbmlRead,
- GST_TYPE_EBML_READ);
+GST_BOILERPLATE (GstMatroskaDemux, gst_matroska_demux, GstElement,
+ GST_TYPE_ELEMENT);
static void
gst_matroska_demux_base_init (gpointer klass)
GObjectClass *gobject_class = (GObjectClass *) klass;
GstElementClass *gstelement_class = (GstElementClass *) klass;
+ /* parser helper separate debug */
+ GST_DEBUG_CATEGORY_INIT (ebmlread_debug, "ebmlread",
+ 0, "EBML stream helper class");
+
GST_DEBUG_CATEGORY_INIT (matroskademux_debug, "matroskademux", 0,
"Matroska demuxer");
gst_pad_set_event_function (demux->sinkpad,
GST_DEBUG_FUNCPTR (gst_matroska_demux_handle_sink_event));
gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
- GST_EBML_READ (demux)->sinkpad = demux->sinkpad;
/* initial stream no. */
demux->src = NULL;
return ret;
}
+static void
+gst_matroska_demux_free_parsed_el (gpointer mem, gpointer user_data)
+{
+ g_slice_free (guint64, mem);
+}
+
static void
gst_matroska_demux_reset (GstElement * element)
{
demux->segmentinfo_parsed = FALSE;
demux->attachments_parsed = FALSE;
- g_list_foreach (demux->tags_parsed, (GFunc) gst_ebml_level_free, NULL);
+ g_list_foreach (demux->tags_parsed,
+ (GFunc) gst_matroska_demux_free_parsed_el, NULL);
g_list_free (demux->tags_parsed);
demux->tags_parsed = NULL;
+ g_list_foreach (demux->seek_parsed,
+ (GFunc) gst_matroska_demux_free_parsed_el, NULL);
+ g_list_free (demux->seek_parsed);
+ demux->seek_parsed = NULL;
+
gst_segment_init (&demux->segment, GST_FORMAT_TIME);
demux->last_stop_end = GST_CLOCK_TIME_NONE;
demux->seek_block = 0;
gst_tag_list_free (demux->global_tags);
}
demux->global_tags = gst_tag_list_new ();
+
+ if (demux->cached_buffer) {
+ gst_buffer_unref (demux->cached_buffer);
+ demux->cached_buffer = NULL;
+ }
+}
+
+/*
+ * Calls pull_range for (offset,size) without advancing our offset
+ */
+static GstFlowReturn
+gst_matroska_demux_peek_bytes (GstMatroskaDemux * demux, guint64 offset,
+ guint size, GstBuffer ** p_buf, guint8 ** bytes)
+{
+ 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 (demux->cached_buffer) {
+ guint64 cache_offset = GST_BUFFER_OFFSET (demux->cached_buffer);
+ guint cache_size = GST_BUFFER_SIZE (demux->cached_buffer);
+
+ if (cache_offset <= demux->offset &&
+ (demux->offset + size) <= (cache_offset + cache_size)) {
+ if (p_buf)
+ *p_buf = gst_buffer_create_sub (demux->cached_buffer,
+ demux->offset - cache_offset, size);
+ if (bytes)
+ *bytes = GST_BUFFER_DATA (demux->cached_buffer) + demux->offset -
+ cache_offset;
+ return GST_FLOW_OK;
+ }
+ /* not enough data in the cache, free cache and get a new one */
+ gst_buffer_unref (demux->cached_buffer);
+ demux->cached_buffer = NULL;
+ }
+
+ /* refill the cache */
+ ret = gst_pad_pull_range (demux->sinkpad, demux->offset,
+ MAX (size, 64 * 1024), &demux->cached_buffer);
+ if (ret != GST_FLOW_OK) {
+ demux->cached_buffer = NULL;
+ return ret;
+ }
+
+ if (GST_BUFFER_SIZE (demux->cached_buffer) >= size) {
+ if (p_buf)
+ *p_buf = gst_buffer_create_sub (demux->cached_buffer, 0, size);
+ if (bytes)
+ *bytes = GST_BUFFER_DATA (demux->cached_buffer);
+ return GST_FLOW_OK;
+ }
+
+ /* Not possible to get enough data, try a last time with
+ * requesting exactly the size we need */
+ gst_buffer_unref (demux->cached_buffer);
+ demux->cached_buffer = NULL;
+
+ ret =
+ gst_pad_pull_range (demux->sinkpad, demux->offset, size,
+ &demux->cached_buffer);
+ if (ret != GST_FLOW_OK) {
+ GST_DEBUG_OBJECT (demux, "pull_range returned %d", ret);
+ if (p_buf)
+ *p_buf = NULL;
+ if (bytes)
+ *bytes = NULL;
+ return ret;
+ }
+
+ if (GST_BUFFER_SIZE (demux->cached_buffer) < size) {
+ GST_WARNING_OBJECT (demux, "Dropping short buffer at offset %"
+ G_GUINT64_FORMAT ": wanted %u bytes, got %u bytes", demux->offset,
+ size, GST_BUFFER_SIZE (demux->cached_buffer));
+
+ gst_buffer_unref (demux->cached_buffer);
+ demux->cached_buffer = NULL;
+ if (p_buf)
+ *p_buf = NULL;
+ if (bytes)
+ *bytes = NULL;
+ return GST_FLOW_UNEXPECTED;
+ }
+
+ if (p_buf)
+ *p_buf = gst_buffer_create_sub (demux->cached_buffer, 0, size);
+ if (bytes)
+ *bytes = GST_BUFFER_DATA (demux->cached_buffer);
+
+ return GST_FLOW_OK;
+}
+
+static const guint8 *
+gst_matroska_demux_peek_pull (GstMatroskaDemux * demux, guint peek)
+{
+ guint8 *data = NULL;
+
+ gst_matroska_demux_peek_bytes (demux, demux->offset, peek, NULL, &data);
+ return data;
+}
+
+static GstFlowReturn
+gst_matroska_demux_peek_id_length_pull (GstMatroskaDemux * demux, guint32 * _id,
+ guint64 * _length, guint * _needed)
+{
+ return gst_ebml_peek_id_length (_id, _length, _needed,
+ (GstPeekData) gst_matroska_demux_peek_pull, (gpointer) demux,
+ GST_ELEMENT_CAST (demux), demux->offset);
+}
+
+static gint64
+gst_matroska_demux_get_length (GstMatroskaDemux * demux)
+{
+ GstFormat fmt = GST_FORMAT_BYTES;
+ gint64 end;
+
+ /* FIXME: what to do if we don't get the upstream length */
+ if (!gst_pad_query_peer_duration (demux->sinkpad, &fmt, &end) ||
+ fmt != GST_FORMAT_BYTES || end < 0)
+ g_return_val_if_reached (0);
+
+ return end;
}
static gint
static GstFlowReturn
gst_matroska_demux_read_track_encoding (GstMatroskaDemux * demux,
- GstMatroskaTrackContext * context)
+ GstEbmlRead * ebml, GstMatroskaTrackContext * context)
{
GstMatroskaTrackEncoding enc = { 0, };
- GstEbmlRead *ebml = GST_EBML_READ (demux);
GstFlowReturn ret;
guint32 id;
return ret;
}
- while (ret == GST_FLOW_OK) {
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
- break;
-
- if (demux->level_up) {
- demux->level_up--;
+ while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- }
switch (id) {
case GST_MATROSKA_ID_CONTENTENCODINGORDER:{
if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
break;
- while (ret == GST_FLOW_OK) {
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up,
- &id)) != GST_FLOW_OK)
- break;
-
- if (demux->level_up) {
- demux->level_up--;
+ while (ret == GST_FLOW_OK &&
+ gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- }
switch (id) {
case GST_MATROSKA_ID_CONTENTCOMPALGO:{
ret = gst_ebml_read_skip (ebml);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
DEBUG_ELEMENT_STOP (demux, ebml, "ContentCompression", ret);
break;
ret = gst_ebml_read_skip (ebml);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
DEBUG_ELEMENT_STOP (demux, ebml, "ContentEncoding", ret);
static GstFlowReturn
gst_matroska_demux_read_track_encodings (GstMatroskaDemux * demux,
- GstMatroskaTrackContext * context)
+ GstEbmlRead * ebml, GstMatroskaTrackContext * context)
{
GstFlowReturn ret;
- GstEbmlRead *ebml = GST_EBML_READ (demux);
guint32 id;
DEBUG_ELEMENT_START (demux, ebml, "ContentEncodings");
context->encodings =
g_array_sized_new (FALSE, FALSE, sizeof (GstMatroskaTrackEncoding), 1);
- while (ret == GST_FLOW_OK) {
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
- break;
-
- if (demux->level_up) {
- demux->level_up--;
+ while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- }
switch (id) {
case GST_MATROSKA_ID_CONTENTENCODING:
- ret = gst_matroska_demux_read_track_encoding (demux, context);
+ ret = gst_matroska_demux_read_track_encoding (demux, ebml, context);
break;
default:
GST_WARNING_OBJECT (demux,
ret = gst_ebml_read_skip (ebml);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
DEBUG_ELEMENT_STOP (demux, ebml, "ContentEncodings", ret);
}
static GstFlowReturn
-gst_matroska_demux_add_stream (GstMatroskaDemux * demux)
+gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
{
GstElementClass *klass = GST_ELEMENT_GET_CLASS (demux);
- GstEbmlRead *ebml = GST_EBML_READ (demux);
GstMatroskaTrackContext *context;
GstPadTemplate *templ = NULL;
GstCaps *caps = NULL;
GST_DEBUG_OBJECT (demux, "Stream number %d", context->index);
/* try reading the trackentry headers */
- while (ret == GST_FLOW_OK) {
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
+ while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
-
switch (id) {
/* track number (unique stream ID) */
case GST_MATROSKA_ID_TRACKNUMBER:{
videocontext = (GstMatroskaTrackVideoContext *) context;
g_ptr_array_index (demux->src, demux->num_streams - 1) = context;
- while (ret == GST_FLOW_OK) {
- if ((ret =
- gst_ebml_peek_id (ebml, &demux->level_up,
- &id)) != GST_FLOW_OK)
- break;
-
- if (demux->level_up) {
- demux->level_up--;
+ while (ret == GST_FLOW_OK &&
+ gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- }
switch (id) {
/* Should be one level up but some broken muxers write it here. */
ret = gst_ebml_read_skip (ebml);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
DEBUG_ELEMENT_STOP (demux, ebml, "TrackVideo", ret);
audiocontext = (GstMatroskaTrackAudioContext *) context;
g_ptr_array_index (demux->src, demux->num_streams - 1) = context;
- while (ret == GST_FLOW_OK) {
- if ((ret =
- gst_ebml_peek_id (ebml, &demux->level_up,
- &id)) != GST_FLOW_OK)
- break;
-
- if (demux->level_up) {
- demux->level_up--;
+ while (ret == GST_FLOW_OK &&
+ gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- }
switch (id) {
/* samplerate */
ret = gst_ebml_read_skip (ebml);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
DEBUG_ELEMENT_STOP (demux, ebml, "TrackAudio", ret);
}
case GST_MATROSKA_ID_CONTENTENCODINGS:{
- ret = gst_matroska_demux_read_track_encodings (demux, context);
+ ret = gst_matroska_demux_read_track_encodings (demux, ebml, context);
break;
}
ret = gst_ebml_read_skip (ebml);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
DEBUG_ELEMENT_STOP (demux, ebml, "TrackEntry", ret);
GST_OBJECT_LOCK (demux);
/* seek (relative to matroska segment) */
- if (gst_ebml_read_seek (GST_EBML_READ (demux),
- entry->pos + demux->ebml_segment_start) != GST_FLOW_OK) {
- GST_DEBUG_OBJECT (demux, "Failed to seek to offset %" G_GUINT64_FORMAT,
- entry->pos + demux->ebml_segment_start);
- goto seek_error;
- }
+ /* position might be invalid; will error when streaming resumes ... */
+ demux->offset = entry->pos + demux->ebml_segment_start;
GST_DEBUG_OBJECT (demux, "Seeked to offset %" G_GUINT64_FORMAT ", block %d, "
"time %" GST_TIME_FORMAT, entry->pos + demux->ebml_segment_start,
gst_matroska_demux_reset_streams (demux, entry->time, TRUE);
demux->segment.last_stop = entry->time;
demux->seek_block = entry->block;
+ demux->seek_first = TRUE;
demux->last_stop_end = GST_CLOCK_TIME_NONE;
if (reset) {
GST_OBJECT_UNLOCK (demux);
return TRUE;
-
-seek_error:
- {
- GST_OBJECT_UNLOCK (demux);
- GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), ("Got a seek error"));
- return FALSE;
- }
}
static gboolean
return ret;
}
+/* skip unknown or alike element */
static GstFlowReturn
-gst_matroska_demux_parse_header (GstMatroskaDemux * demux)
+gst_matroska_demux_parse_skip (GstMatroskaDemux * demux, GstEbmlRead * ebml,
+ const gchar * parent_name, guint id)
+{
+ if (id == GST_EBML_ID_VOID) {
+ GST_DEBUG_OBJECT (demux, "Skipping EBML Void element");
+ } else if (id == GST_EBML_ID_CRC32) {
+ GST_DEBUG_OBJECT (demux, "Skipping EBML CRC32 element");
+ } else {
+ GST_WARNING_OBJECT (demux,
+ "Unknown %s subelement 0x%x - ignoring", parent_name, id);
+ }
+
+ return gst_ebml_read_skip (ebml);
+}
+
+static GstFlowReturn
+gst_matroska_demux_parse_header (GstMatroskaDemux * demux, GstEbmlRead * ebml)
{
- GstEbmlRead *ebml = GST_EBML_READ (demux);
GstFlowReturn ret;
gchar *doctype;
guint version;
+ guint32 id;
+
+ /* this function is the first to be called */
+
+ /* default init */
+ doctype = NULL;
+ version = 1;
+
+ ret = gst_ebml_peek_id (ebml, &id);
+ if (ret != GST_FLOW_OK)
+ return ret;
+
+ GST_DEBUG_OBJECT (demux, "id: %08x", id);
+
+ if (id != GST_EBML_ID_HEADER) {
+ GST_ERROR_OBJECT (demux, "Failed to read header");
+ goto exit;
+ }
- if ((ret = gst_ebml_read_header (ebml, &doctype, &version)) != GST_FLOW_OK)
+ ret = gst_ebml_read_master (ebml, &id);
+ if (ret != GST_FLOW_OK)
return ret;
+ while (gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+ ret = gst_ebml_peek_id (ebml, &id);
+ if (ret != GST_FLOW_OK)
+ return ret;
+
+ switch (id) {
+ /* is our read version uptodate? */
+ case GST_EBML_ID_EBMLREADVERSION:{
+ guint64 num;
+
+ ret = gst_ebml_read_uint (ebml, &id, &num);
+ if (ret != GST_FLOW_OK)
+ return ret;
+ g_assert (id == GST_EBML_ID_EBMLREADVERSION);
+ if (num != GST_EBML_VERSION) {
+ GST_ERROR_OBJECT (ebml, "Unsupported EBML version %" G_GUINT64_FORMAT,
+ num);
+ return GST_FLOW_ERROR;
+ }
+
+ GST_DEBUG_OBJECT (ebml, "EbmlReadVersion: %" G_GUINT64_FORMAT, num);
+ break;
+ }
+
+ /* we only handle 8 byte lengths at max */
+ case GST_EBML_ID_EBMLMAXSIZELENGTH:{
+ guint64 num;
+
+ ret = gst_ebml_read_uint (ebml, &id, &num);
+ if (ret != GST_FLOW_OK)
+ return ret;
+ g_assert (id == GST_EBML_ID_EBMLMAXSIZELENGTH);
+ if (num > sizeof (guint64)) {
+ GST_ERROR_OBJECT (ebml,
+ "Unsupported EBML maximum size %" G_GUINT64_FORMAT, num);
+ return GST_FLOW_ERROR;
+ }
+ GST_DEBUG_OBJECT (ebml, "EbmlMaxSizeLength: %" G_GUINT64_FORMAT, num);
+ break;
+ }
+
+ /* we handle 4 byte IDs at max */
+ case GST_EBML_ID_EBMLMAXIDLENGTH:{
+ guint64 num;
+
+ ret = gst_ebml_read_uint (ebml, &id, &num);
+ if (ret != GST_FLOW_OK)
+ return ret;
+ g_assert (id == GST_EBML_ID_EBMLMAXIDLENGTH);
+ if (num > sizeof (guint32)) {
+ GST_ERROR_OBJECT (ebml,
+ "Unsupported EBML maximum ID %" G_GUINT64_FORMAT, num);
+ return GST_FLOW_ERROR;
+ }
+ GST_DEBUG_OBJECT (ebml, "EbmlMaxIdLength: %" G_GUINT64_FORMAT, num);
+ break;
+ }
+
+ case GST_EBML_ID_DOCTYPE:{
+ gchar *text;
+
+ ret = gst_ebml_read_ascii (ebml, &id, &text);
+ if (ret != GST_FLOW_OK)
+ return ret;
+ g_assert (id == GST_EBML_ID_DOCTYPE);
+
+ GST_DEBUG_OBJECT (ebml, "EbmlDocType: %s", GST_STR_NULL (text));
+
+ if (doctype)
+ g_free (doctype);
+ doctype = text;
+ break;
+ }
+
+ case GST_EBML_ID_DOCTYPEREADVERSION:{
+ guint64 num;
+
+ ret = gst_ebml_read_uint (ebml, &id, &num);
+ if (ret != GST_FLOW_OK)
+ return ret;
+ g_assert (id == GST_EBML_ID_DOCTYPEREADVERSION);
+ version = num;
+ GST_DEBUG_OBJECT (ebml, "EbmlReadVersion: %" G_GUINT64_FORMAT, num);
+ break;
+ }
+
+ default:
+ ret = gst_matroska_demux_parse_skip (demux, ebml, "EBML header", id);
+ if (ret != GST_FLOW_OK)
+ return ret;
+ break;
+
+ /* we ignore these two, as they don't tell us anything we care about */
+ case GST_EBML_ID_EBMLVERSION:
+ case GST_EBML_ID_DOCTYPEVERSION:
+ ret = gst_ebml_read_skip (ebml);
+ if (ret != GST_FLOW_OK)
+ return ret;
+ break;
+ }
+ }
+
+exit:
ret = GST_FLOW_ERROR;
if (doctype) {
if (g_str_equal (doctype, GST_MATROSKA_DOCTYPE_MATROSKA) ||
}
static GstFlowReturn
-gst_matroska_demux_init_stream (GstMatroskaDemux * demux)
-{
- GstEbmlRead *ebml = GST_EBML_READ (demux);
- guint32 id;
- GstFlowReturn ret;
-
- GST_DEBUG_OBJECT (demux, "Init stream");
-
- if ((ret = gst_matroska_demux_parse_header (demux)) != GST_FLOW_OK)
- return ret;
-
- /* find segment, must be the next element but search as long as
- * we find it anyway */
- while (TRUE) {
- guint last_level;
-
- if ((ret = gst_ebml_peek_id (ebml, &last_level, &id)) != GST_FLOW_OK) {
- GST_DEBUG_OBJECT (demux, "gst_ebml_peek_id() failed!");
- return ret;
- }
-
- if (id == GST_MATROSKA_ID_SEGMENT)
- break;
-
- /* oi! */
- GST_WARNING_OBJECT (demux,
- "Expected a Segment ID (0x%x), but received 0x%x!",
- GST_MATROSKA_ID_SEGMENT, id);
-
- if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK)
- return ret;
- }
-
- /* we now have a EBML segment */
- if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) {
- GST_DEBUG_OBJECT (demux, "gst_ebml_read_master() failed!");
- return ret;
- }
-
- GST_DEBUG_OBJECT (demux, "Found Segment start at offset %" G_GUINT64_FORMAT,
- ebml->offset);
- /* seeks are from the beginning of the segment,
- * after the segment ID/length */
- demux->ebml_segment_start = ebml->offset;
-
- return ret;
-}
-
-static GstFlowReturn
-gst_matroska_demux_parse_tracks (GstMatroskaDemux * demux)
+gst_matroska_demux_parse_tracks (GstMatroskaDemux * demux, GstEbmlRead * ebml)
{
- GstEbmlRead *ebml = GST_EBML_READ (demux);
GstFlowReturn ret = GST_FLOW_OK;
guint32 id;
return ret;
}
- while (ret == GST_FLOW_OK) {
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
- break;
-
- if (demux->level_up) {
- demux->level_up--;
+ while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- }
switch (id) {
/* one track within the "all-tracks" header */
case GST_MATROSKA_ID_TRACKENTRY:
- ret = gst_matroska_demux_add_stream (demux);
+ ret = gst_matroska_demux_add_stream (demux, ebml);
break;
default:
- GST_WARNING ("Unknown Track subelement 0x%x - ignoring", id);
- ret = gst_ebml_read_skip (ebml);
+ ret = gst_matroska_demux_parse_skip (demux, ebml, "Track", id);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
DEBUG_ELEMENT_STOP (demux, ebml, "Tracks", ret);
static GstFlowReturn
gst_matroska_demux_parse_index_cuetrack (GstMatroskaDemux * demux,
- guint * nentries)
+ GstEbmlRead * ebml, guint * nentries)
{
- GstEbmlRead *ebml = GST_EBML_READ (demux);
guint32 id;
GstFlowReturn ret;
GstMatroskaIndex idx;
return ret;
}
- while (ret == GST_FLOW_OK) {
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
+ while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
-
switch (id) {
/* track number */
case GST_MATROSKA_ID_CUETRACK:
}
default:
- GST_WARNING ("Unknown CueTrackPositions subelement 0x%x - ignoring",
+ ret = gst_matroska_demux_parse_skip (demux, ebml, "CueTrackPositions",
id);
- /* fall-through */
+ break;
case GST_MATROSKA_ID_CUECODECSTATE:
case GST_MATROSKA_ID_CUEREFERENCE:
- if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK)
- break;
+ ret = gst_ebml_read_skip (ebml);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
DEBUG_ELEMENT_STOP (demux, ebml, "CueTrackPositions", ret);
}
static GstFlowReturn
-gst_matroska_demux_parse_index_pointentry (GstMatroskaDemux * demux)
+gst_matroska_demux_parse_index_pointentry (GstMatroskaDemux * demux,
+ GstEbmlRead * ebml)
{
- GstEbmlRead *ebml = GST_EBML_READ (demux);
guint32 id;
GstFlowReturn ret;
GstClockTime time = GST_CLOCK_TIME_NONE;
return ret;
}
- while (ret == GST_FLOW_OK) {
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
- break;
-
- if (demux->level_up) {
- demux->level_up--;
+ while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- }
switch (id) {
/* one single index entry ('point') */
case GST_MATROSKA_ID_CUETRACKPOSITIONS:
{
if ((ret =
- gst_matroska_demux_parse_index_cuetrack (demux,
+ gst_matroska_demux_parse_index_cuetrack (demux, ebml,
&nentries)) != GST_FLOW_OK)
break;
break;
}
default:
- GST_WARNING_OBJECT (demux,
- "Unknown CuePoint subelement 0x%x - ignoring", id);
- if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK)
- break;
+ ret = gst_matroska_demux_parse_skip (demux, ebml, "CuePoint", id);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
DEBUG_ELEMENT_STOP (demux, ebml, "CuePoint", ret);
}
static GstFlowReturn
-gst_matroska_demux_parse_index (GstMatroskaDemux * demux)
+gst_matroska_demux_parse_index (GstMatroskaDemux * demux, GstEbmlRead * ebml)
{
- GstEbmlRead *ebml = GST_EBML_READ (demux);
guint32 id;
GstFlowReturn ret = GST_FLOW_OK;
guint i;
return ret;
}
- while (ret == GST_FLOW_OK) {
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
- break;
-
- if (demux->level_up) {
- demux->level_up--;
+ while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- }
switch (id) {
/* one single index entry ('point') */
case GST_MATROSKA_ID_POINTENTRY:
- ret = gst_matroska_demux_parse_index_pointentry (demux);
+ ret = gst_matroska_demux_parse_index_pointentry (demux, ebml);
break;
default:
- GST_WARNING ("Unknown Cues subelement 0x%x - ignoring", id);
- ret = gst_ebml_read_skip (ebml);
+ ret = gst_matroska_demux_parse_skip (demux, ebml, "Cues", id);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
DEBUG_ELEMENT_STOP (demux, ebml, "Cues", ret);
}
static GstFlowReturn
-gst_matroska_demux_parse_info (GstMatroskaDemux * demux)
+gst_matroska_demux_parse_info (GstMatroskaDemux * demux, GstEbmlRead * ebml)
{
- GstEbmlRead *ebml = GST_EBML_READ (demux);
GstFlowReturn ret = GST_FLOW_OK;
guint32 id;
return ret;
}
- while (ret == GST_FLOW_OK) {
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
- break;
-
- if (demux->level_up) {
- demux->level_up--;
+ while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- }
switch (id) {
/* cluster timecode */
}
default:
- GST_WARNING_OBJECT (demux,
- "Unknown SegmentInfo subelement 0x%x - ignoring", id);
+ ret = gst_matroska_demux_parse_skip (demux, ebml, "SegmentInfo", id);
+ break;
/* fall through */
case GST_MATROSKA_ID_SEGMENTUID:
ret = gst_ebml_read_skip (ebml);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
DEBUG_ELEMENT_STOP (demux, ebml, "SegmentInfo", ret);
static GstFlowReturn
gst_matroska_demux_parse_metadata_id_simple_tag (GstMatroskaDemux * demux,
- GstTagList ** p_taglist)
+ GstEbmlRead * ebml, GstTagList ** p_taglist)
{
/* FIXME: check if there are more useful mappings */
struct
GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
};
- GstEbmlRead *ebml = GST_EBML_READ (demux);
GstFlowReturn ret;
guint32 id;
gchar *value = NULL;
return ret;
}
- while (ret == GST_FLOW_OK) {
+ while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
/* read all sub-entries */
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
- break;
-
- if (demux->level_up) {
- demux->level_up--;
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- }
switch (id) {
case GST_MATROSKA_ID_TAGNAME:
break;
default:
- GST_WARNING_OBJECT (demux,
- "Unknown SimpleTag subelement 0x%x - ignoring", id);
+ ret = gst_matroska_demux_parse_skip (demux, ebml, "SimpleTag", id);
+ break;
/* fall-through */
case GST_MATROSKA_ID_TAGLANGUAGE:
ret = gst_ebml_read_skip (ebml);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
DEBUG_ELEMENT_STOP (demux, ebml, "SimpleTag", ret);
static GstFlowReturn
gst_matroska_demux_parse_metadata_id_tag (GstMatroskaDemux * demux,
- GstTagList ** p_taglist)
+ GstEbmlRead * ebml, GstTagList ** p_taglist)
{
- GstEbmlRead *ebml = GST_EBML_READ (demux);
guint32 id;
GstFlowReturn ret;
return ret;
}
- while (ret == GST_FLOW_OK) {
+ while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
/* read all sub-entries */
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
- break;
-
- if (demux->level_up) {
- demux->level_up--;
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- }
switch (id) {
case GST_MATROSKA_ID_SIMPLETAG:
- ret =
- gst_matroska_demux_parse_metadata_id_simple_tag (demux, p_taglist);
+ ret = gst_matroska_demux_parse_metadata_id_simple_tag (demux, ebml,
+ p_taglist);
break;
default:
- GST_WARNING_OBJECT (demux, "Unknown Tag subelement 0x%x - ignoring",
- id);
- ret = gst_ebml_read_skip (ebml);
+ ret = gst_matroska_demux_parse_skip (demux, ebml, "Tag", id);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
- }
+ }
DEBUG_ELEMENT_STOP (demux, ebml, "Tag", ret);
}
static GstFlowReturn
-gst_matroska_demux_parse_metadata (GstMatroskaDemux * demux)
+gst_matroska_demux_parse_metadata (GstMatroskaDemux * demux, GstEbmlRead * ebml)
{
- GstEbmlRead *ebml = GST_EBML_READ (demux);
GstTagList *taglist;
GstFlowReturn ret = GST_FLOW_OK;
guint32 id;
GList *l;
- GstEbmlLevel *curlevel;
+ guint64 curpos;
- if (ebml->level == NULL) {
- GST_ERROR_OBJECT (demux, "Unexpected metadata, bailing");
- return GST_FLOW_ERROR;
- }
- curlevel = ebml->level->data;
+ curpos = gst_ebml_read_get_pos (ebml);
/* Make sure we don't parse a tags element twice and
* post it's tags twice */
+ curpos = gst_ebml_read_get_pos (ebml);
for (l = demux->tags_parsed; l; l = l->next) {
- GstEbmlLevel *level = l->data;
-
- if (ebml->level)
- curlevel = ebml->level->data;
- else
- break;
+ guint64 *pos = l->data;
- if (level->start == curlevel->start && level->length == curlevel->length) {
+ if (*pos == curpos) {
GST_DEBUG_OBJECT (demux, "Skipping already parsed Tags at offset %"
- G_GUINT64_FORMAT, ebml->offset);
- ret = gst_ebml_read_skip (ebml);
- return ret;
+ G_GUINT64_FORMAT, curpos);
+ return GST_FLOW_OK;
}
}
- DEBUG_ELEMENT_START (demux, ebml, "Tags");
+ demux->tags_parsed =
+ g_list_prepend (demux->tags_parsed, g_slice_new (guint64));
+ *((guint64 *) demux->tags_parsed->data) = curpos;
+ /* fall-through */
if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) {
DEBUG_ELEMENT_STOP (demux, ebml, "Tags", ret);
taglist = gst_tag_list_new ();
- /* TODO: g_slice_dup() if we depend on GLib 2.14 */
- curlevel = g_slice_new (GstEbmlLevel);
- memcpy (curlevel, ebml->level->data, sizeof (GstEbmlLevel));
- demux->tags_parsed = g_list_prepend (demux->tags_parsed, curlevel);
-
- while (ret == GST_FLOW_OK) {
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
- break;
-
- if (demux->level_up) {
- demux->level_up--;
+ while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- }
switch (id) {
case GST_MATROSKA_ID_TAG:
- ret = gst_matroska_demux_parse_metadata_id_tag (demux, &taglist);
+ ret = gst_matroska_demux_parse_metadata_id_tag (demux, ebml, &taglist);
break;
default:
- GST_WARNING_OBJECT (demux, "Unknown Tags subelement 0x%x - ignoring",
- id);
+ ret = gst_matroska_demux_parse_skip (demux, ebml, "Tags", id);
+ break;
/* FIXME: Use to limit the tags to specific pads */
case GST_MATROSKA_ID_TARGETS:
ret = gst_ebml_read_skip (ebml);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
DEBUG_ELEMENT_STOP (demux, ebml, "Tags", ret);
static GstFlowReturn
gst_matroska_demux_parse_attached_file (GstMatroskaDemux * demux,
- GstTagList * taglist)
+ GstEbmlRead * ebml, GstTagList * taglist)
{
- GstEbmlRead *ebml = GST_EBML_READ (demux);
guint32 id;
GstFlowReturn ret;
gchar *description = NULL;
return ret;
}
- while (ret == GST_FLOW_OK) {
+ while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
/* read all sub-entries */
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
-
switch (id) {
case GST_MATROSKA_ID_FILEDESCRIPTION:
if (description) {
break;
default:
- GST_WARNING_OBJECT (demux,
- "Unknown AttachedFile subelement 0x%x - ignoring", id);
- /* fall through */
+ ret = gst_matroska_demux_parse_skip (demux, ebml, "AttachedFile", id);
+ break;
case GST_MATROSKA_ID_FILEUID:
ret = gst_ebml_read_skip (ebml);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
DEBUG_ELEMENT_STOP (demux, ebml, "AttachedFile", ret);
}
static GstFlowReturn
-gst_matroska_demux_parse_attachments (GstMatroskaDemux * demux)
+gst_matroska_demux_parse_attachments (GstMatroskaDemux * demux,
+ GstEbmlRead * ebml)
{
- GstEbmlRead *ebml = GST_EBML_READ (demux);
guint32 id;
GstFlowReturn ret = GST_FLOW_OK;
GstTagList *taglist;
taglist = gst_tag_list_new ();
- while (ret == GST_FLOW_OK) {
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
- break;
-
- if (demux->level_up) {
- demux->level_up--;
+ while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- }
switch (id) {
case GST_MATROSKA_ID_ATTACHEDFILE:
- ret = gst_matroska_demux_parse_attached_file (demux, taglist);
+ ret = gst_matroska_demux_parse_attached_file (demux, ebml, taglist);
break;
default:
- GST_WARNING ("Unknown Attachments subelement 0x%x - ignoring", id);
- ret = gst_ebml_read_skip (ebml);
+ ret = gst_matroska_demux_parse_skip (demux, ebml, "Attachments", id);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
DEBUG_ELEMENT_STOP (demux, ebml, "Attachments", ret);
}
static GstFlowReturn
-gst_matroska_demux_parse_chapters (GstMatroskaDemux * demux)
+gst_matroska_demux_parse_chapters (GstMatroskaDemux * demux, GstEbmlRead * ebml)
{
- GstEbmlRead *ebml = GST_EBML_READ (demux);
guint32 id;
GstFlowReturn ret = GST_FLOW_OK;
return ret;
}
- while (ret == GST_FLOW_OK) {
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
+ while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
-
switch (id) {
default:
ret = gst_ebml_read_skip (ebml);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
DEBUG_ELEMENT_STOP (demux, ebml, "Chapters", ret);
static GstFlowReturn
gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
- guint64 cluster_time, guint64 cluster_offset, gboolean is_simpleblock)
+ GstEbmlRead * ebml, guint64 cluster_time, guint64 cluster_offset,
+ gboolean is_simpleblock)
{
GstMatroskaTrackContext *stream = NULL;
- GstEbmlRead *ebml = GST_EBML_READ (demux);
GstFlowReturn ret = GST_FLOW_OK;
gboolean readblock = FALSE;
guint32 id;
gint64 referenceblock = 0;
gint64 offset;
- offset = demux->parent.offset;
+ offset = gst_ebml_read_get_offset (ebml);
- while (ret == GST_FLOW_OK) {
+ while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
if (!is_simpleblock) {
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
- break;
-
- if (demux->level_up) {
- demux->level_up--;
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- }
} else {
id = GST_MATROSKA_ID_SIMPLEBLOCK;
}
}
default:
- GST_WARNING_OBJECT (demux,
- "Unknown BlockGroup subelement 0x%x - ignoring", id);
- /* fall-through */
+ ret = gst_matroska_demux_parse_skip (demux, ebml, "BlockGroup", id);
+ break;
case GST_MATROSKA_ID_BLOCKVIRTUAL:
case GST_MATROSKA_ID_BLOCKADDITIONS:
if (is_simpleblock)
break;
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
if (ret == GST_FLOW_OK && readblock) {
}
static GstFlowReturn
-gst_matroska_demux_parse_cluster (GstMatroskaDemux * demux)
-{
- GstEbmlRead *ebml = GST_EBML_READ (demux);
- GstFlowReturn ret = GST_FLOW_OK;
- guint64 cluster_time = GST_CLOCK_TIME_NONE;
- guint32 id;
- guint64 cluster_offset = demux->parent.offset;
-
- DEBUG_ELEMENT_START (demux, ebml, "Cluster");
-
- if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) {
- DEBUG_ELEMENT_STOP (demux, ebml, "Cluster", ret);
- return ret;
- }
-
- while (ret == GST_FLOW_OK) {
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
- break;
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
-
- switch (id) {
- /* cluster timecode */
- case GST_MATROSKA_ID_CLUSTERTIMECODE:
- {
- guint64 num;
-
- if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
- break;
-
- GST_DEBUG_OBJECT (demux, "ClusterTimeCode: %" G_GUINT64_FORMAT, num);
- cluster_time = num;
- break;
- }
-
- /* a group of blocks inside a cluster */
- case GST_MATROSKA_ID_BLOCKGROUP:
- if (!gst_matroska_demux_seek_block (demux))
- goto skip;
- DEBUG_ELEMENT_START (demux, ebml, "BlockGroup");
- if ((ret = gst_ebml_read_master (ebml, &id)) == GST_FLOW_OK) {
- ret = gst_matroska_demux_parse_blockgroup_or_simpleblock (demux,
- cluster_time, cluster_offset, FALSE);
- }
- DEBUG_ELEMENT_STOP (demux, ebml, "BlockGroup", ret);
- break;
-
- case GST_MATROSKA_ID_SIMPLEBLOCK:
- {
- if (!gst_matroska_demux_seek_block (demux))
- goto skip;
- DEBUG_ELEMENT_START (demux, ebml, "SimpleBlock");
- ret = gst_matroska_demux_parse_blockgroup_or_simpleblock (demux,
- cluster_time, cluster_offset, TRUE);
- DEBUG_ELEMENT_STOP (demux, ebml, "SimpleBlock", ret);
- break;
- }
-
- default:
- GST_WARNING ("Unknown Cluster subelement 0x%x - ignoring", id);
- /* fall-through */
- case GST_MATROSKA_ID_POSITION:
- case GST_MATROSKA_ID_PREVSIZE:
- case GST_MATROSKA_ID_ENCRYPTEDBLOCK:
- case GST_MATROSKA_ID_SILENTTRACKS:
- skip:
- GST_DEBUG ("Skipping Cluster subelement 0x%x - ignoring", id);
- ret = gst_ebml_read_skip (ebml);
- break;
- }
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
- }
-
- if (demux->element_index) {
- if (demux->element_index_writer_id == -1)
- gst_index_get_writer_id (demux->element_index,
- GST_OBJECT (demux), &demux->element_index_writer_id);
-
- GST_LOG_OBJECT (demux, "adding association %" GST_TIME_FORMAT "-> %"
- G_GUINT64_FORMAT " for writer id %d",
- GST_TIME_ARGS (cluster_time), cluster_offset,
- demux->element_index_writer_id);
- gst_index_add_association (demux->element_index,
- demux->element_index_writer_id, GST_ASSOCIATION_FLAG_KEY_UNIT,
- GST_FORMAT_TIME, cluster_time, GST_FORMAT_BYTES, cluster_offset, NULL);
- }
-
- DEBUG_ELEMENT_STOP (demux, ebml, "Cluster", ret);
-
- if (G_UNLIKELY (demux->seek_block)) {
- GST_DEBUG_OBJECT (demux, "seek target block %" G_GUINT64_FORMAT
- " not found in Cluster, trying next Cluster's first block instead",
- demux->seek_block);
- demux->seek_block = 0;
- }
-
- return ret;
-}
-
-static GstFlowReturn
-gst_matroska_demux_parse_contents_seekentry (GstMatroskaDemux * demux)
+gst_matroska_demux_parse_contents_seekentry (GstMatroskaDemux * demux,
+ GstEbmlRead * ebml)
{
- GstEbmlRead *ebml = GST_EBML_READ (demux);
GstFlowReturn ret;
guint64 seek_pos = (guint64) - 1;
guint32 seek_id = 0;
return ret;
}
- while (ret == GST_FLOW_OK) {
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
- break;
-
- if (demux->level_up) {
- demux->level_up--;
+ while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- }
switch (id) {
case GST_MATROSKA_ID_SEEKID:
}
default:
- GST_WARNING_OBJECT (demux,
- "Unknown SeekHead subelement 0x%x - ignoring", id);
- ret = gst_ebml_read_skip (ebml);
+ ret = gst_matroska_demux_parse_skip (demux, ebml, "SeekHead", id);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
if (ret != GST_FLOW_OK && ret != GST_FLOW_UNEXPECTED)
}
switch (seek_id) {
+ case GST_MATROSKA_ID_SEEKHEAD:
+ {
+ }
case GST_MATROSKA_ID_CUES:
case GST_MATROSKA_ID_TAGS:
case GST_MATROSKA_ID_TRACKS:
- case GST_MATROSKA_ID_SEEKHEAD:
case GST_MATROSKA_ID_SEGMENTINFO:
case GST_MATROSKA_ID_ATTACHMENTS:
case GST_MATROSKA_ID_CHAPTERS:
{
- guint level_up = demux->level_up;
guint64 before_pos, length;
- GstEbmlLevel *level;
+ guint needed;
/* remember */
- length = gst_ebml_read_get_length (ebml);
- before_pos = ebml->offset;
+ length = gst_matroska_demux_get_length (demux);
+ before_pos = demux->offset;
/* check for validity */
if (seek_pos + demux->ebml_segment_start + 12 >= length) {
}
/* seek */
- if (gst_ebml_read_seek (ebml, seek_pos + demux->ebml_segment_start) !=
- GST_FLOW_OK)
- break;
-
- /* we don't want to lose our seekhead level, so we add
- * a dummy. This is a crude hack. */
- level = g_slice_new (GstEbmlLevel);
- level->start = 0;
- level->length = G_MAXUINT64;
- ebml->level = g_list_prepend (ebml->level, level);
+ demux->offset = seek_pos + demux->ebml_segment_start;
/* check ID */
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
+ if ((ret = gst_matroska_demux_peek_id_length_pull (demux, &id, &length,
+ &needed)) != GST_FLOW_OK)
goto finish;
if (id != seek_id) {
GST_WARNING_OBJECT (demux,
"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 (!demux->index_parsed) {
- ret = gst_matroska_demux_parse_index (demux);
- }
- break;
- case GST_MATROSKA_ID_TAGS:
- ret = gst_matroska_demux_parse_metadata (demux);
- break;
- case GST_MATROSKA_ID_TRACKS:
- if (!demux->tracks_parsed) {
- ret = gst_matroska_demux_parse_tracks (demux);
- }
- break;
-
- case GST_MATROSKA_ID_SEGMENTINFO:
- if (!demux->segmentinfo_parsed) {
- ret = gst_matroska_demux_parse_info (demux);
- }
- break;
- case GST_MATROSKA_ID_SEEKHEAD:
- {
- GList *l;
-
- DEBUG_ELEMENT_START (demux, ebml, "SeekHead");
- if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
- goto finish;
-
- /* Prevent infinite recursion if there's a cycle from
- * one seekhead to the same again. Simply break if
- * we already had this seekhead, finish will clean up
- * everything. */
- for (l = ebml->level; l; l = l->next) {
- GstEbmlLevel *level = (GstEbmlLevel *) l->data;
-
- if (level->start == ebml->offset && l->prev)
- goto finish;
- }
-
- ret = gst_matroska_demux_parse_contents (demux);
- break;
- }
- case GST_MATROSKA_ID_ATTACHMENTS:
- if (!demux->attachments_parsed) {
- ret = gst_matroska_demux_parse_attachments (demux);
- }
- break;
- case GST_MATROSKA_ID_CHAPTERS:
- ret = gst_matroska_demux_parse_chapters (demux);
- break;
+ } else {
+ /* now parse */
+ ret = gst_matroska_demux_parse_id (demux, id, length, needed);
}
finish:
- /* remove dummy level */
- while (ebml->level) {
- guint64 length;
-
- level = ebml->level->data;
- ebml->level = g_list_delete_link (ebml->level, ebml->level);
- length = level->length;
- g_slice_free (GstEbmlLevel, level);
- if (length == G_MAXUINT64)
- break;
- }
-
/* seek back */
- (void) gst_ebml_read_seek (ebml, before_pos);
- demux->level_up = level_up;
+ demux->offset = before_pos;
break;
}
}
static GstFlowReturn
-gst_matroska_demux_parse_contents (GstMatroskaDemux * demux)
+gst_matroska_demux_parse_contents (GstMatroskaDemux * demux, GstEbmlRead * ebml)
{
- GstEbmlRead *ebml = GST_EBML_READ (demux);
GstFlowReturn ret = GST_FLOW_OK;
guint32 id;
- while (ret == GST_FLOW_OK) {
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
- break;
+ DEBUG_ELEMENT_START (demux, ebml, "SeekHead");
+
+ if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) {
+ DEBUG_ELEMENT_STOP (demux, ebml, "SeekHead", ret);
+ return ret;
+ }
- if (demux->level_up) {
- demux->level_up--;
+ while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
+ if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
break;
- }
switch (id) {
case GST_MATROSKA_ID_SEEKENTRY:
{
- ret = gst_matroska_demux_parse_contents_seekentry (demux);
+ ret = gst_matroska_demux_parse_contents_seekentry (demux, ebml);
/* Ignore EOS and errors here */
if (ret != GST_FLOW_OK) {
GST_DEBUG_OBJECT (demux, "Ignoring %s", gst_flow_get_name (ret));
}
default:
- GST_WARNING ("Unknown SeekHead subelement 0x%x - ignoring", id);
- ret = gst_ebml_read_skip (ebml);
+ ret = gst_matroska_demux_parse_skip (demux, ebml, "SeekHead", id);
break;
}
-
- if (demux->level_up) {
- demux->level_up--;
- break;
- }
}
DEBUG_ELEMENT_STOP (demux, ebml, "SeekHead", ret);
return ret;
}
-/* returns FALSE on error, otherwise TRUE */
-static GstFlowReturn
-gst_matroska_demux_loop_stream_parse_id (GstMatroskaDemux * demux,
- guint32 id, gboolean * p_run_loop)
+static inline GstFlowReturn
+gst_matroska_demux_check_read_size (GstMatroskaDemux * demux, guint64 bytes)
{
- GstEbmlRead *ebml = GST_EBML_READ (demux);
- GstFlowReturn ret = GST_FLOW_OK;
+ if (G_UNLIKELY (bytes > 10 * 1024 * 1024)) {
+ /* only a few blocks are expected/allowed to be large,
+ * and will be recursed into, whereas others will be read and must fit */
+ GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL),
+ ("reading large block of size %" G_GUINT64_FORMAT " not supported; "
+ "file might be corrupt.", bytes));
+ return GST_FLOW_ERROR;
+ } else {
+ return GST_FLOW_OK;
+ }
+}
- switch (id) {
- /* stream info
- * Can exist more than once but following occurences
- * must have the same content so ignore them */
- case GST_MATROSKA_ID_SEGMENTINFO:
- if (!demux->segmentinfo_parsed) {
- if ((ret = gst_matroska_demux_parse_info (demux)) != GST_FLOW_OK)
- return ret;
- } else {
- if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK)
- return ret;
- }
- break;
+/* initializes @ebml with @bytes from input stream at current offset.
+ * Returns UNEXPECTED if insufficient available,
+ * ERROR if too much was attempted to read. */
+static inline GstFlowReturn
+gst_matroska_demux_take (GstMatroskaDemux * demux, guint64 bytes,
+ GstEbmlRead * ebml)
+{
+ GstBuffer *buffer = NULL;
+ GstFlowReturn ret = GST_FLOW_OK;
- /* track info headers
- * Can exist more than once but following occurences
- * must have the same content so ignore them */
- case GST_MATROSKA_ID_TRACKS:
- {
- if (!demux->tracks_parsed) {
- if ((ret = gst_matroska_demux_parse_tracks (demux)) != GST_FLOW_OK)
- return ret;
- } else {
- if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK)
- return ret;
- }
- break;
- }
+ GST_LOG_OBJECT (demux, "taking %" G_GUINT64_FORMAT " bytes for parsing",
+ bytes);
+ ret = gst_matroska_demux_check_read_size (demux, bytes);
+ if (ret != GST_FLOW_OK)
+ goto exit;
+ if (demux->streaming) {
+ if (gst_adapter_available (demux->adapter) >= bytes)
+ buffer = gst_adapter_take_buffer (demux->adapter, bytes);
+ else
+ ret = GST_FLOW_UNEXPECTED;
+ } else
+ ret = gst_matroska_demux_peek_bytes (demux, demux->offset, bytes, &buffer,
+ NULL);
+ if (G_LIKELY (buffer)) {
+ gst_ebml_read_init (ebml, GST_ELEMENT_CAST (demux), buffer, demux->offset);
+ demux->offset += bytes;
+ }
+exit:
+ return ret;
+}
- /* cues - seek table
- * Either exists exactly one time or never but ignore
- * following occurences for the sake of sanity */
- case GST_MATROSKA_ID_CUES:
- {
- if (!demux->index_parsed) {
- if ((ret = gst_matroska_demux_parse_index (demux)) != GST_FLOW_OK)
- return ret;
- } else {
- if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK)
- return ret;
- }
- break;
- }
+static inline GstFlowReturn
+gst_matroska_demux_flush (GstMatroskaDemux * demux, guint flush)
+{
+ GST_LOG_OBJECT (demux, "skipping %d bytes", flush);
+ demux->offset += flush;
+ if (demux->streaming) {
+ GstFlowReturn ret;
- /* metadata
- * can exist more than one time with different content */
- case GST_MATROSKA_ID_TAGS:
- {
- if ((ret = gst_matroska_demux_parse_metadata (demux)) != GST_FLOW_OK)
- return ret;
- break;
- }
+ /* hard to skip large blocks when streaming */
+ ret = gst_matroska_demux_check_read_size (demux, flush);
+ if (ret != GST_FLOW_OK)
+ return ret;
+ if (flush <= gst_adapter_available (demux->adapter))
+ gst_adapter_flush (demux->adapter, flush);
+ else
+ return GST_FLOW_UNEXPECTED;
+ }
+ return GST_FLOW_OK;
+}
- /* file index (if seekable, seek to Cues/Tags/etc to parse it) */
- case GST_MATROSKA_ID_SEEKHEAD:
- {
- DEBUG_ELEMENT_START (demux, ebml, "SeekHead");
- if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
- return ret;
- if ((ret = gst_matroska_demux_parse_contents (demux)) != GST_FLOW_OK)
- return ret;
- break;
- }
+static void
+gst_matroska_demux_check_seekability (GstMatroskaDemux * demux)
+{
+ GstQuery *query;
+ gboolean seekable = FALSE;
+ gint64 start = -1, stop = -1;
- /* cluster - contains the payload */
- case GST_MATROSKA_ID_CLUSTER:
- {
- if (demux->state != GST_MATROSKA_DEMUX_STATE_DATA) {
- /* We need a Tracks element first before we can output anything.
- * Search it!
- */
- if (!demux->tracks_parsed) {
- GstEbmlLevel *level;
- guint32 iid;
- guint level_up;
- guint64 before_pos;
+ query = gst_query_new_seeking (GST_FORMAT_BYTES);
+ if (!gst_pad_peer_query (demux->sinkpad, query)) {
+ GST_DEBUG_OBJECT (demux, "seeking query failed");
+ goto done;
+ }
- GST_WARNING_OBJECT (demux,
- "Found Cluster element before Tracks, searching Tracks");
+ gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
- /* remember */
- level_up = demux->level_up;
- before_pos = ebml->offset;
+ /* try harder to query upstream size if we didn't get it the first time */
+ if (seekable && stop == -1) {
+ GstFormat fmt = GST_FORMAT_BYTES;
- /* we don't want to lose our seekhead level, so we add
- * a dummy. This is a crude hack. */
+ GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
+ gst_pad_query_peer_duration (demux->sinkpad, &fmt, &stop);
+ }
- level = g_slice_new (GstEbmlLevel);
- level->start = 0;
- level->length = G_MAXUINT64;
- ebml->level = g_list_prepend (ebml->level, level);
+ /* if upstream doesn't know the size, it's likely that it's not seekable in
+ * practice even if it technically may be seekable */
+ if (seekable && (start != 0 || stop <= start)) {
+ GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
+ seekable = FALSE;
+ }
- /* Search Tracks element */
- while (TRUE) {
- if (gst_ebml_read_skip (ebml) != GST_FLOW_OK)
- break;
-
- if (gst_ebml_peek_id (ebml, &demux->level_up, &iid) != GST_FLOW_OK)
- break;
-
- if (iid != GST_MATROSKA_ID_TRACKS)
- continue;
-
- gst_matroska_demux_parse_tracks (demux);
- break;
- }
-
- if (!demux->tracks_parsed) {
- GST_ERROR_OBJECT (demux, "No Tracks element found");
- ret = GST_FLOW_ERROR;
- }
+done:
+ GST_INFO_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
+ G_GUINT64_FORMAT ")", seekable, start, stop);
+ demux->seekable = seekable;
- /* remove dummy level */
- while (ebml->level) {
- guint64 length;
+ gst_query_unref (query);
+}
- level = ebml->level->data;
- ebml->level = g_list_delete_link (ebml->level, ebml->level);
- length = level->length;
- g_slice_free (GstEbmlLevel, level);
- if (length == G_MAXUINT64)
- break;
- }
+static GstFlowReturn
+gst_matroska_demux_find_tracks (GstMatroskaDemux * demux)
+{
+ guint32 id;
+ guint64 before_pos;
+ guint64 length;
+ guint needed;
+ GstFlowReturn ret = GST_FLOW_OK;
- /* seek back */
- gst_ebml_read_seek (ebml, before_pos);
- demux->level_up = level_up;
- }
+ GST_WARNING_OBJECT (demux,
+ "Found Cluster element before Tracks, searching Tracks");
- if (ret != GST_FLOW_OK)
- return ret;
+ /* remember */
+ before_pos = demux->offset;
- demux->state = GST_MATROSKA_DEMUX_STATE_DATA;
- GST_DEBUG_OBJECT (demux, "signaling no more pads");
- gst_element_no_more_pads (GST_ELEMENT (demux));
- /* send initial discont */
- gst_matroska_demux_send_event (demux,
- gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0));
- } else {
- /* 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 ((ret = gst_matroska_demux_parse_cluster (demux)) != GST_FLOW_OK)
- return ret;
- *p_run_loop = FALSE;
- }
+ /* Search Tracks element */
+ while (TRUE) {
+ ret = gst_matroska_demux_peek_id_length_pull (demux, &id, &length, &needed);
+ if (ret != GST_FLOW_OK)
break;
- }
- /* attachments - contains files attached to the mkv container
- * like album art, etc */
- case GST_MATROSKA_ID_ATTACHMENTS:{
- if (!demux->attachments_parsed) {
- if ((ret = gst_matroska_demux_parse_attachments (demux)) != GST_FLOW_OK)
- return ret;
+ if (id != GST_MATROSKA_ID_TRACKS) {
+ /* we may be skipping large cluster here, so forego size check etc */
+ /* ... but we can't skip undefined size; force error */
+ if (length == G_MAXUINT64) {
+ ret = gst_matroska_demux_check_read_size (demux, length);
+ break;
} else {
- if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK)
- return ret;
+ demux->offset += needed;
+ demux->offset += length;
}
- break;
- }
-
- /* chapters - contains meta information about how to group
- * the file into chapters, similar to DVD */
- case GST_MATROSKA_ID_CHAPTERS:{
- if ((ret = gst_matroska_demux_parse_chapters (demux)) != GST_FLOW_OK)
- return ret;
- break;
+ continue;
}
- default:
- GST_WARNING_OBJECT (demux, "Unknown Segment subelement 0x%x at %"
- G_GUINT64_FORMAT " - ignoring", id, GST_EBML_READ (demux)->offset);
- if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK)
- return ret;
- break;
+ /* will lead to track parsing ... */
+ ret = gst_matroska_demux_parse_id (demux, id, length, needed);
+ break;
}
+
+ /* seek back */
+ demux->offset = before_pos;
+
return ret;
}
+#define GST_READ_CHECK(stmt) \
+G_STMT_START { \
+ if (G_UNLIKELY ((ret = (stmt)) != GST_FLOW_OK)) \
+ goto read_error; \
+} G_STMT_END
+
static GstFlowReturn
-gst_matroska_demux_loop_stream (GstMatroskaDemux * demux)
+gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id,
+ guint64 length, guint needed)
{
- GstEbmlRead *ebml = GST_EBML_READ (demux);
+ GstEbmlRead ebml = { 0, };
GstFlowReturn ret = GST_FLOW_OK;
- gboolean run_loop = TRUE;
- guint32 id;
+ guint64 read;
- /* we've found our segment, start reading the different contents in here */
- while (run_loop && ret == GST_FLOW_OK) {
- if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
- return ret;
+ /* if we plan to read and parse this element, we need prefix (id + length)
+ * and the contents */
+ /* mind about overflow wrap-around when dealing with undefined size */
+ read = length;
+ if (G_LIKELY (length != G_MAXUINT64))
+ read += needed;
- if (demux->level_up) {
- demux->level_up--;
+ switch (demux->state) {
+ case GST_MATROSKA_DEMUX_STATE_START:
+ switch (id) {
+ case GST_EBML_ID_HEADER:
+ GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
+ ret = gst_matroska_demux_parse_header (demux, &ebml);
+ if (ret != GST_FLOW_OK)
+ goto parse_failed;
+ demux->state = GST_MATROSKA_DEMUX_STATE_SEGMENT;
+ gst_matroska_demux_check_seekability (demux);
+ break;
+ default:
+ goto invalid_header;
+ break;
+ }
break;
- }
+ case GST_MATROSKA_DEMUX_STATE_SEGMENT:
+ switch (id) {
+ case GST_MATROSKA_ID_SEGMENT:
+ /* eat segment prefix */
+ GST_READ_CHECK (gst_matroska_demux_flush (demux, needed));
+ GST_DEBUG_OBJECT (demux,
+ "Found Segment start at offset %" G_GUINT64_FORMAT,
+ demux->offset);
+ /* seeks are from the beginning of the segment,
+ * after the segment ID/length */
+ demux->ebml_segment_start = demux->offset;
+ demux->state = GST_MATROSKA_DEMUX_STATE_HEADER;
+ break;
+ default:
+ GST_WARNING_OBJECT (demux,
+ "Expected a Segment ID (0x%x), but received 0x%x!",
+ GST_MATROSKA_ID_SEGMENT, id);
+ GST_READ_CHECK (gst_matroska_demux_flush (demux, read));
+ break;
+ }
+ break;
+ case GST_MATROSKA_DEMUX_STATE_HEADER:
+ case GST_MATROSKA_DEMUX_STATE_DATA:
+ case GST_MATROSKA_DEMUX_STATE_SEEK:
+ switch (id) {
+ case GST_MATROSKA_ID_SEGMENTINFO:
+ if (!demux->segmentinfo_parsed) {
+ GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
+ ret = gst_matroska_demux_parse_info (demux, &ebml);
+ } else {
+ GST_READ_CHECK (gst_matroska_demux_flush (demux, read));
+ }
+ break;
+ case GST_MATROSKA_ID_TRACKS:
+ if (!demux->tracks_parsed) {
+ GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
+ ret = gst_matroska_demux_parse_tracks (demux, &ebml);
+ } else {
+ GST_READ_CHECK (gst_matroska_demux_flush (demux, read));
+ }
+ break;
+ case GST_MATROSKA_ID_CLUSTER:
+ if (G_UNLIKELY (!demux->tracks_parsed)) {
+ if (demux->streaming) {
+ GST_DEBUG_OBJECT (demux, "Cluster before Track");
+ goto not_streamable;
+ } else {
+ ret = gst_matroska_demux_find_tracks (demux);
+ if (!demux->tracks_parsed)
+ goto no_tracks;
+ }
+ }
+ if (G_UNLIKELY (demux->state == GST_MATROSKA_DEMUX_STATE_HEADER)) {
+ demux->state = GST_MATROSKA_DEMUX_STATE_DATA;
+ GST_DEBUG_OBJECT (demux, "signaling no more pads");
+ gst_element_no_more_pads (GST_ELEMENT (demux));
+ /* send initial newsegment */
+ gst_matroska_demux_send_event (demux,
+ gst_event_new_new_segment (FALSE, 1.0,
+ GST_FORMAT_TIME, 0,
+ (demux->segment.duration >
+ 0) ? demux->segment.duration : -1, 0));
+ }
+ demux->cluster_time = GST_CLOCK_TIME_NONE;
+ demux->cluster_offset = demux->offset;
+ if (G_UNLIKELY (!demux->seek_first && demux->seek_block)) {
+ GST_DEBUG_OBJECT (demux, "seek target block %" G_GUINT64_FORMAT
+ " not found in Cluster, trying next Cluster's first block instead",
+ demux->seek_block);
+ demux->seek_block = 0;
+ }
+ demux->seek_first = FALSE;
+ /* eat cluster prefix */
+ gst_matroska_demux_flush (demux, needed);
+ break;
+ case GST_MATROSKA_ID_CLUSTERTIMECODE:
+ {
+ guint64 num;
- ret = gst_matroska_demux_loop_stream_parse_id (demux, id, &run_loop);
+ GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
+ if ((ret = gst_ebml_read_uint (&ebml, &id, &num)) != GST_FLOW_OK)
+ goto parse_failed;
+ GST_DEBUG_OBJECT (demux, "ClusterTimeCode: %" G_GUINT64_FORMAT, num);
+ demux->cluster_time = num;
+ if (demux->element_index) {
+ if (demux->element_index_writer_id == -1)
+ gst_index_get_writer_id (demux->element_index,
+ GST_OBJECT (demux), &demux->element_index_writer_id);
+ GST_LOG_OBJECT (demux, "adding association %" GST_TIME_FORMAT "-> %"
+ G_GUINT64_FORMAT " for writer id %d",
+ GST_TIME_ARGS (demux->cluster_time), demux->cluster_offset,
+ demux->element_index_writer_id);
+ gst_index_add_association (demux->element_index,
+ demux->element_index_writer_id, GST_ASSOCIATION_FLAG_KEY_UNIT,
+ GST_FORMAT_TIME, demux->cluster_time,
+ GST_FORMAT_BYTES, demux->cluster_offset, NULL);
+ }
+ break;
+ }
+ case GST_MATROSKA_ID_BLOCKGROUP:
+ if (!gst_matroska_demux_seek_block (demux))
+ goto skip;
+ GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
+ DEBUG_ELEMENT_START (demux, &ebml, "BlockGroup");
+ if ((ret = gst_ebml_read_master (&ebml, &id)) == GST_FLOW_OK) {
+ ret = gst_matroska_demux_parse_blockgroup_or_simpleblock (demux,
+ &ebml, demux->cluster_time, demux->cluster_offset, FALSE);
+ }
+ DEBUG_ELEMENT_STOP (demux, &ebml, "BlockGroup", ret);
+ break;
+ case GST_MATROSKA_ID_SIMPLEBLOCK:
+ if (!gst_matroska_demux_seek_block (demux))
+ goto skip;
+ GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
+ DEBUG_ELEMENT_START (demux, &ebml, "SimpleBlock");
+ ret = gst_matroska_demux_parse_blockgroup_or_simpleblock (demux,
+ &ebml, demux->cluster_time, demux->cluster_offset, TRUE);
+ DEBUG_ELEMENT_STOP (demux, &ebml, "SimpleBlock", ret);
+ break;
+ case GST_MATROSKA_ID_ATTACHMENTS:
+ if (!demux->attachments_parsed) {
+ GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
+ ret = gst_matroska_demux_parse_attachments (demux, &ebml);
+ } else {
+ GST_READ_CHECK (gst_matroska_demux_flush (demux, read));
+ }
+ break;
+ case GST_MATROSKA_ID_TAGS:
+ GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
+ ret = gst_matroska_demux_parse_metadata (demux, &ebml);
+ break;
+ case GST_MATROSKA_ID_CHAPTERS:
+ GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
+ ret = gst_matroska_demux_parse_chapters (demux, &ebml);
+ break;
+ case GST_MATROSKA_ID_SEEKHEAD:
+ GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
+ ret = gst_matroska_demux_parse_contents (demux, &ebml);
+ break;
+ case GST_MATROSKA_ID_CUES:
+ if (demux->index_parsed) {
+ GST_READ_CHECK (gst_matroska_demux_flush (demux, read));
+ break;
+ }
+ GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
+ ret = gst_matroska_demux_parse_index (demux, &ebml);
+ /* only push based; delayed index building */
+ if (demux->state == GST_MATROSKA_DEMUX_STATE_SEEK) {
+ GstEvent *event;
- if (demux->level_up) {
- demux->level_up--;
+ GST_OBJECT_LOCK (demux);
+ event = demux->seek_event;
+ demux->seek_event = NULL;
+ GST_OBJECT_UNLOCK (demux);
+
+ g_assert (event);
+ /* unlikely to fail, since we managed to seek to this point */
+ if (!gst_matroska_demux_handle_seek_event (demux, NULL, event))
+ goto seek_failed;
+ /* resume data handling, main thread clear to seek again */
+ GST_OBJECT_LOCK (demux);
+ demux->state = GST_MATROSKA_DEMUX_STATE_DATA;
+ GST_OBJECT_UNLOCK (demux);
+ }
+ break;
+ case GST_MATROSKA_ID_POSITION:
+ case GST_MATROSKA_ID_PREVSIZE:
+ case GST_MATROSKA_ID_ENCRYPTEDBLOCK:
+ case GST_MATROSKA_ID_SILENTTRACKS:
+ GST_DEBUG_OBJECT (demux,
+ "Skipping Cluster subelement 0x%x - ignoring", id);
+ /* fall-through */
+ default:
+ skip:
+ GST_DEBUG_OBJECT (demux, "skipping Element 0x%x", id);
+ GST_READ_CHECK (gst_matroska_demux_flush (demux, read));
+ break;
+ }
break;
- }
}
+
+ if (ret == GST_FLOW_PARSE)
+ goto parse_failed;
+
+exit:
+ gst_ebml_read_clear (&ebml);
return ret;
+
+ /* ERRORS */
+read_error:
+ {
+ /* simply exit, maybe not enough data yet */
+ /* no ebml to clear if read error */
+ return ret;
+ }
+parse_failed:
+ {
+ GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL),
+ ("Failed to parse Element 0x%x", id));
+ ret = GST_FLOW_ERROR;
+ goto exit;
+ }
+not_streamable:
+ {
+ GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL),
+ ("File layout does not permit streaming"));
+ ret = GST_FLOW_ERROR;
+ goto exit;
+ }
+no_tracks:
+ {
+ GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL),
+ ("No Tracks element found"));
+ ret = GST_FLOW_ERROR;
+ goto exit;
+ }
+invalid_header:
+ {
+ GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), ("Invalid header"));
+ ret = GST_FLOW_ERROR;
+ goto exit;
+ }
+seek_failed:
+ {
+ GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), ("Failed to seek"));
+ ret = GST_FLOW_ERROR;
+ goto exit;
+ }
}
static void
gst_matroska_demux_loop (GstPad * pad)
{
GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (GST_PAD_PARENT (pad));
- GstEbmlRead *ebml = GST_EBML_READ (demux);
GstFlowReturn ret;
-
- /* first, if we're to start, let's actually get starting */
- if (G_UNLIKELY (demux->state == GST_MATROSKA_DEMUX_STATE_START)) {
- ret = gst_matroska_demux_init_stream (demux);
- if (ret != GST_FLOW_OK) {
- GST_WARNING_OBJECT (demux, "init stream failed!");
- goto pause;
- }
- demux->state = GST_MATROSKA_DEMUX_STATE_HEADER;
- }
+ guint32 id;
+ guint64 length;
+ guint needed;
/* If we have to close a segment, send a new segment to do this now */
-
if (G_LIKELY (demux->state == GST_MATROSKA_DEMUX_STATE_DATA)) {
if (G_UNLIKELY (demux->close_segment)) {
gst_matroska_demux_send_event (demux, demux->close_segment);
}
}
- ret = gst_matroska_demux_loop_stream (demux);
+ ret = gst_matroska_demux_peek_id_length_pull (demux, &id, &length, &needed);
+ if (ret == GST_FLOW_UNEXPECTED)
+ goto eos;
+ if (ret != GST_FLOW_OK)
+ goto pause;
+
+ GST_LOG_OBJECT (demux, "Offset %" G_GUINT64_FORMAT ", Element id 0x%x, "
+ "size %" G_GUINT64_FORMAT ", needed %d", demux->offset, id,
+ length, needed);
+
+ ret = gst_matroska_demux_parse_id (demux, id, length, needed);
if (ret == GST_FLOW_UNEXPECTED)
goto eos;
if (ret != GST_FLOW_OK)
}
next:
- if (G_UNLIKELY (ebml->offset == gst_ebml_read_get_length (ebml))) {
+ if (G_UNLIKELY (demux->offset == gst_matroska_demux_get_length (demux))) {
GST_LOG_OBJECT (demux, "Reached end of stream, sending EOS");
ret = GST_FLOW_UNEXPECTED;
goto pause;
return res;
}
-static void
-gst_matroska_demux_check_seekability (GstMatroskaDemux * demux)
-{
- GstQuery *query;
- gboolean seekable = FALSE;
- gint64 start = -1, stop = -1;
-
- query = gst_query_new_seeking (GST_FORMAT_BYTES);
- if (!gst_pad_peer_query (demux->sinkpad, query)) {
- GST_DEBUG_OBJECT (demux, "seeking query failed");
- goto done;
- }
-
- gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
-
- /* try harder to query upstream size if we didn't get it the first time */
- if (seekable && stop == -1) {
- GstFormat fmt = GST_FORMAT_BYTES;
-
- GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
- gst_pad_query_peer_duration (demux->sinkpad, &fmt, &stop);
- }
-
- /* if upstream doesn't know the size, it's likely that it's not seekable in
- * practice even if it technically may be seekable */
- if (seekable && (start != 0 || stop <= start)) {
- GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable");
- seekable = FALSE;
- }
-
-done:
- GST_INFO_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %"
- G_GUINT64_FORMAT ")", seekable, start, stop);
- demux->seekable = seekable;
-
- gst_query_unref (query);
-}
-
-static inline void
-gst_matroska_demux_take (GstMatroskaDemux * demux, guint bytes)
-{
- GstBuffer *buffer;
-
- GST_LOG_OBJECT (demux, "caching %d bytes for parsing", bytes);
- buffer = gst_adapter_take_buffer (demux->adapter, bytes);
- gst_ebml_read_reset_cache (GST_EBML_READ (demux), buffer, demux->offset);
- demux->offset += bytes;
-}
-
-static inline void
-gst_matroska_demux_flush (GstMatroskaDemux * demux, guint flush)
+static const guint8 *
+gst_matroska_demux_peek_adapter (GstMatroskaDemux * demux, guint peek)
{
- GST_LOG_OBJECT (demux, "skipping %d bytes", flush);
- gst_adapter_flush (demux->adapter, flush);
- demux->offset += flush;
+ return gst_adapter_peek (demux->adapter, peek);
}
static GstFlowReturn
-gst_matroska_demux_peek_id_length (GstMatroskaDemux * demux, guint32 * _id,
+gst_matroska_demux_peek_id_length_push (GstMatroskaDemux * demux, guint32 * _id,
guint64 * _length, guint * _needed)
{
- guint avail, needed;
- const guint8 *buf;
- gint len_mask = 0x80, read = 1, n = 1, num_ffs = 0;
- guint64 total;
- guint8 b;
-
- g_return_val_if_fail (_id != NULL, GST_FLOW_ERROR);
- g_return_val_if_fail (_length != NULL, GST_FLOW_ERROR);
- g_return_val_if_fail (_needed != NULL, GST_FLOW_ERROR);
-
- /* well ... */
- *_id = (guint32) GST_EBML_SIZE_UNKNOWN;
- *_length = GST_EBML_SIZE_UNKNOWN;
-
- /* read element id */
- needed = 2;
- avail = gst_adapter_available (demux->adapter);
- if (avail < needed)
- goto exit;
-
- buf = gst_adapter_peek (demux->adapter, 1);
- b = GST_READ_UINT8 (buf);
-
- total = (guint64) b;
- while (read <= 4 && !(total & len_mask)) {
- read++;
- len_mask >>= 1;
- }
- if (G_UNLIKELY (read > 4))
- goto invalid_id;
- /* need id and at least something for subsequent length */
- if ((needed = read + 1) > avail)
- goto exit;
-
- buf = gst_adapter_peek (demux->adapter, needed);
- while (n < read) {
- b = GST_READ_UINT8 (buf + n);
- total = (total << 8) | b;
- ++n;
- }
- *_id = (guint32) total;
-
- /* read element length */
- b = GST_READ_UINT8 (buf + n);
- total = (guint64) b;
- len_mask = 0x80;
- read = 1;
- while (read <= 8 && !(total & len_mask)) {
- read++;
- len_mask >>= 1;
- }
- if (G_UNLIKELY (read > 8))
- goto invalid_length;
- if ((needed += read - 1) > avail)
- goto exit;
- if ((total &= (len_mask - 1)) == len_mask - 1)
- num_ffs++;
-
- buf = gst_adapter_peek (demux->adapter, needed);
- buf += (needed - read);
- n = 1;
- while (n < read) {
- guint8 b = GST_READ_UINT8 (buf + n);
-
- if (G_UNLIKELY (b == 0xff))
- num_ffs++;
- total = (total << 8) | b;
- ++n;
- }
-
- if (G_UNLIKELY (read == num_ffs))
- *_length = G_MAXUINT64;
- else
- *_length = total;
- *_length = total;
-
-exit:
- *_needed = needed;
-
- return GST_FLOW_OK;
-
- /* ERRORS */
-invalid_id:
- {
- GST_ERROR_OBJECT (demux,
- "Invalid EBML ID size tag (0x%x) at position %" G_GUINT64_FORMAT " (0x%"
- G_GINT64_MODIFIER "x)", (guint) b, demux->offset, demux->offset);
- return GST_FLOW_ERROR;
- }
-invalid_length:
- {
- GST_ERROR_OBJECT (demux,
- "Invalid EBML length size tag (0x%x) at position %" G_GUINT64_FORMAT
- " (0x%" G_GINT64_MODIFIER "x)", (guint) b, demux->offset,
- demux->offset);
- return GST_FLOW_ERROR;
- }
+ return gst_ebml_peek_id_length (_id, _length, _needed,
+ (GstPeekData) gst_matroska_demux_peek_adapter, (gpointer) demux,
+ GST_ELEMENT_CAST (demux), demux->offset);
}
static GstFlowReturn
gst_matroska_demux_chain (GstPad * pad, GstBuffer * buffer)
{
GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (GST_PAD_PARENT (pad));
- GstEbmlRead *ebml = GST_EBML_READ (demux);
guint available;
GstFlowReturn ret = GST_FLOW_OK;
guint needed = 0;
guint32 id;
guint64 length;
- const gchar *name;
if (G_UNLIKELY (GST_BUFFER_IS_DISCONT (buffer))) {
GST_DEBUG_OBJECT (demux, "got DISCONT");
next:
available = gst_adapter_available (demux->adapter);
- ret = gst_matroska_demux_peek_id_length (demux, &id, &length, &needed);
- if (G_UNLIKELY (ret != GST_FLOW_OK))
- goto parse_failed;
+ ret = gst_matroska_demux_peek_id_length_push (demux, &id, &length, &needed);
+ if (G_UNLIKELY (ret != GST_FLOW_OK && ret != GST_FLOW_UNEXPECTED))
+ return ret;
GST_LOG_OBJECT (demux, "Offset %" G_GUINT64_FORMAT ", Element id 0x%x, "
"size %" G_GUINT64_FORMAT ", needed %d, available %d", demux->offset, id,
if (needed > available)
return GST_FLOW_OK;
- /* only a few blocks are expected/allowed to be large,
- * and will be recursed into, whereas others must fit */
- if (G_LIKELY (id != GST_MATROSKA_ID_SEGMENT && id != GST_MATROSKA_ID_CLUSTER)) {
- if (needed + length > available)
- return GST_FLOW_OK;
- /* probably happens with 'large pieces' at the end, so consider it EOS */
- if (G_UNLIKELY (length > 10 * 1024 * 1024)) {
- GST_WARNING_OBJECT (demux, "forcing EOS due to size %" G_GUINT64_FORMAT,
- length);
- return GST_FLOW_UNEXPECTED;
- }
- }
-
- switch (demux->state) {
- case GST_MATROSKA_DEMUX_STATE_START:
- switch (id) {
- case GST_EBML_ID_HEADER:
- gst_matroska_demux_take (demux, length + needed);
- ret = gst_matroska_demux_parse_header (demux);
- if (ret != GST_FLOW_OK)
- goto parse_failed;
- demux->state = GST_MATROSKA_DEMUX_STATE_HEADER;
- gst_matroska_demux_check_seekability (demux);
- break;
- default:
- goto invalid_header;
- break;
- }
- break;
- case GST_MATROSKA_DEMUX_STATE_HEADER:
- case GST_MATROSKA_DEMUX_STATE_DATA:
- case GST_MATROSKA_DEMUX_STATE_SEEK:
- switch (id) {
- case GST_MATROSKA_ID_SEGMENT:
- /* eat segment prefix */
- gst_matroska_demux_flush (demux, needed);
- GST_DEBUG_OBJECT (demux,
- "Found Segment start at offset %" G_GUINT64_FORMAT, ebml->offset);
- /* seeks are from the beginning of the segment,
- * after the segment ID/length */
- demux->ebml_segment_start = demux->offset;
- break;
- case GST_MATROSKA_ID_SEGMENTINFO:
- if (!demux->segmentinfo_parsed) {
- gst_matroska_demux_take (demux, length + needed);
- ret = gst_matroska_demux_parse_info (demux);
- } else {
- gst_matroska_demux_flush (demux, needed + length);
- }
- break;
- case GST_MATROSKA_ID_TRACKS:
- if (!demux->tracks_parsed) {
- gst_matroska_demux_take (demux, length + needed);
- ret = gst_matroska_demux_parse_tracks (demux);
- } else {
- gst_matroska_demux_flush (demux, needed + length);
- }
- break;
- case GST_MATROSKA_ID_CLUSTER:
- if (G_UNLIKELY (!demux->tracks_parsed)) {
- GST_DEBUG_OBJECT (demux, "Cluster before Track");
- goto not_streamable;
- } else if (demux->state == GST_MATROSKA_DEMUX_STATE_HEADER) {
- demux->state = GST_MATROSKA_DEMUX_STATE_DATA;
- GST_DEBUG_OBJECT (demux, "signaling no more pads");
- gst_element_no_more_pads (GST_ELEMENT (demux));
- /* send initial newsegment */
- gst_matroska_demux_send_event (demux,
- gst_event_new_new_segment (FALSE, 1.0,
- GST_FORMAT_TIME, 0,
- (demux->segment.duration >
- 0) ? demux->segment.duration : -1, 0));
- }
- demux->cluster_time = GST_CLOCK_TIME_NONE;
- demux->cluster_offset = demux->parent.offset;
- /* eat cluster prefix */
- gst_matroska_demux_flush (demux, needed);
- break;
- case GST_MATROSKA_ID_CLUSTERTIMECODE:
- {
- guint64 num;
-
- gst_matroska_demux_take (demux, length + needed);
- if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
- goto parse_failed;
- GST_DEBUG_OBJECT (demux, "ClusterTimeCode: %" G_GUINT64_FORMAT, num);
- demux->cluster_time = num;
- break;
- }
- case GST_MATROSKA_ID_BLOCKGROUP:
- gst_matroska_demux_take (demux, length + needed);
- DEBUG_ELEMENT_START (demux, ebml, "BlockGroup");
- if ((ret = gst_ebml_read_master (ebml, &id)) == GST_FLOW_OK) {
- ret = gst_matroska_demux_parse_blockgroup_or_simpleblock (demux,
- demux->cluster_time, demux->cluster_offset, FALSE);
- }
- DEBUG_ELEMENT_STOP (demux, ebml, "BlockGroup", ret);
- if (ret != GST_FLOW_OK)
- goto exit;
- break;
- case GST_MATROSKA_ID_SIMPLEBLOCK:
- gst_matroska_demux_take (demux, length + needed);
- DEBUG_ELEMENT_START (demux, ebml, "SimpleBlock");
- ret = gst_matroska_demux_parse_blockgroup_or_simpleblock (demux,
- demux->cluster_time, demux->cluster_offset, TRUE);
- DEBUG_ELEMENT_STOP (demux, ebml, "SimpleBlock", ret);
- if (ret != GST_FLOW_OK)
- goto exit;
- break;
- case GST_MATROSKA_ID_ATTACHMENTS:
- if (!demux->attachments_parsed) {
- gst_matroska_demux_take (demux, length + needed);
- ret = gst_matroska_demux_parse_attachments (demux);
- } else {
- gst_matroska_demux_flush (demux, needed + length);
- }
- break;
- case GST_MATROSKA_ID_TAGS:
- gst_matroska_demux_take (demux, length + needed);
- ret = gst_matroska_demux_parse_metadata (demux);
- break;
- case GST_MATROSKA_ID_CHAPTERS:
- name = "Cues";
- goto skip;
- case GST_MATROSKA_ID_SEEKHEAD:
- gst_matroska_demux_take (demux, length + needed);
- DEBUG_ELEMENT_START (demux, ebml, "SeekHead");
- if ((ret = gst_ebml_read_master (ebml, &id)) == GST_FLOW_OK)
- ret = gst_matroska_demux_parse_contents (demux);
- if (ret != GST_FLOW_OK)
- goto exit;
- break;
- case GST_MATROSKA_ID_CUES:
- if (demux->index_parsed) {
- gst_matroska_demux_flush (demux, needed + length);
- break;
- }
- gst_matroska_demux_take (demux, length + needed);
- ret = gst_matroska_demux_parse_index (demux);
- if (demux->state == GST_MATROSKA_DEMUX_STATE_SEEK) {
- GstEvent *event;
-
- GST_OBJECT_LOCK (demux);
- event = demux->seek_event;
- demux->seek_event = NULL;
- GST_OBJECT_UNLOCK (demux);
-
- g_assert (event);
- /* unlikely to fail, since we managed to seek to this point */
- if (!gst_matroska_demux_handle_seek_event (demux, NULL, event))
- goto seek_failed;
- /* resume data handling, main thread clear to seek again */
- GST_OBJECT_LOCK (demux);
- demux->state = GST_MATROSKA_DEMUX_STATE_DATA;
- GST_OBJECT_UNLOCK (demux);
- }
- break;
- default:
- name = "Unknown";
- skip:
- GST_DEBUG_OBJECT (demux, "skipping Element 0x%x (%s)", id, name);
- gst_matroska_demux_flush (demux, needed + length);
- break;
- }
- break;
- }
-
- if (ret != GST_FLOW_OK)
- goto parse_failed;
- else
+ ret = gst_matroska_demux_parse_id (demux, id, length, needed);
+ if (ret == GST_FLOW_UNEXPECTED) {
+ /* need more data */
+ return GST_FLOW_OK;
+ } else if (ret != GST_FLOW_OK) {
+ return ret;
+ } else
goto next;
-
-exit:
- return ret;
-
- /* ERRORS */
-parse_failed:
- {
- GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL),
- ("Failed to parse Element 0x%x", id));
- return GST_FLOW_ERROR;
- }
-not_streamable:
- {
- GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL),
- ("File layout does not permit streaming"));
- return GST_FLOW_ERROR;
- }
-invalid_header:
- {
- GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), ("Invalid header"));
- return GST_FLOW_ERROR;
- }
-seek_failed:
- {
- GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), ("Failed to seek"));
- return GST_FLOW_ERROR;
- }
}
static gboolean