From ef0805ea14c3ab6ceebf8cbe8b572fca079b1290 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tim-Philipp=20M=C3=BCller?= Date: Fri, 28 Sep 2012 00:43:38 +0100 Subject: [PATCH] matroskademux: put streamheaders on vorbis/speex/flac/theora caps to make remuxing work https://bugzilla.gnome.org/show_bug.cgi?id=640589 --- gst/matroska/matroska-demux.c | 257 ++++++++++++++---------------------------- gst/matroska/matroska-ids.c | 150 ++++++++++++++++++++++++ gst/matroska/matroska-ids.h | 26 ++--- 3 files changed, 248 insertions(+), 185 deletions(-) diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index 049f4b4..316c02d 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -314,6 +314,9 @@ gst_matroska_track_free (GstMatroskaTrackContext * track) if (track->index_table) g_array_free (track->index_table, TRUE); + if (track->stream_headers) + gst_buffer_list_unref (track->stream_headers); + g_free (track); } @@ -522,6 +525,31 @@ gst_matroska_decode_buffer (GstMatroskaTrackContext * context, GstBuffer * buf) } } +static void +gst_matroska_demux_add_stream_headers_to_caps (GstMatroskaDemux * demux, + GstBufferList * list, GstCaps * caps) +{ + GstStructure *s; + GValue arr_val = G_VALUE_INIT; + GValue buf_val = G_VALUE_INIT; + gint i, num; + + g_assert (gst_caps_is_writable (caps)); + + g_value_init (&arr_val, GST_TYPE_ARRAY); + g_value_init (&buf_val, GST_TYPE_BUFFER); + + num = gst_buffer_list_length (list); + for (i = 0; i < num; ++i) { + g_value_set_boxed (&buf_val, gst_buffer_list_get (list, i)); + gst_value_array_append_value (&arr_val, &buf_val); + } + + s = gst_caps_get_structure (caps, 0); + gst_structure_take_value (s, "streamheader", &arr_val); + g_value_unset (&buf_val); +} + static GstFlowReturn gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) { @@ -1316,6 +1344,9 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) gst_caps_set_simple (caps, "fourcc", G_TYPE_STRING, fstr, NULL); g_free (fstr); } + } else if (context->stream_headers != NULL) { + gst_matroska_demux_add_stream_headers_to_caps (demux, + context->stream_headers, caps); } /* the pad in here */ @@ -2553,161 +2584,38 @@ gst_matroska_demux_sync_streams (GstMatroskaDemux * demux) } static GstFlowReturn -gst_matroska_demux_push_hdr_buf (GstMatroskaDemux * demux, - GstMatroskaTrackContext * stream, guint8 * data, guint len) -{ - GstFlowReturn ret, cret; - GstBuffer *header_buf; - - header_buf = gst_buffer_new_wrapped (g_memdup (data, len), len); - - if (stream->set_discont) { - GST_BUFFER_FLAG_SET (header_buf, GST_BUFFER_FLAG_DISCONT); - stream->set_discont = FALSE; - } - - ret = gst_pad_push (stream->pad, header_buf); - - /* combine flows */ - cret = gst_matroska_demux_combine_flows (demux, stream, ret); - - return cret; -} - -static GstFlowReturn -gst_matroska_demux_push_flac_codec_priv_data (GstMatroskaDemux * demux, +gst_matroska_demux_push_stream_headers (GstMatroskaDemux * demux, GstMatroskaTrackContext * stream) { - GstFlowReturn ret; - guint8 *pdata; - guint off, len; - - GST_LOG_OBJECT (demux, "priv data size = %" G_GSIZE_FORMAT, - stream->codec_priv_size); - - pdata = (guint8 *) stream->codec_priv; - - /* need at least 'fLaC' marker + STREAMINFO metadata block */ - if (stream->codec_priv_size < ((4) + (4 + 34))) { - GST_WARNING_OBJECT (demux, "not enough codec priv data for flac headers"); - return GST_FLOW_ERROR; - } - - if (memcmp (pdata, "fLaC", 4) != 0) { - GST_WARNING_OBJECT (demux, "no flac marker at start of stream headers"); - return GST_FLOW_ERROR; - } - - ret = gst_matroska_demux_push_hdr_buf (demux, stream, pdata, 4); - if (ret != GST_FLOW_OK) - return ret; - - off = 4; /* skip fLaC marker */ - while (off < stream->codec_priv_size) { - len = GST_READ_UINT8 (pdata + off + 1) << 16; - len |= GST_READ_UINT8 (pdata + off + 2) << 8; - len |= GST_READ_UINT8 (pdata + off + 3); - - GST_DEBUG_OBJECT (demux, "header packet: len=%u bytes, flags=0x%02x", - len, (guint) pdata[off]); - - ret = gst_matroska_demux_push_hdr_buf (demux, stream, pdata + off, len + 4); - if (ret != GST_FLOW_OK) - return ret; - - off += 4 + len; - } - return GST_FLOW_OK; -} - -static GstFlowReturn -gst_matroska_demux_push_speex_codec_priv_data (GstMatroskaDemux * demux, - GstMatroskaTrackContext * stream) -{ - GstFlowReturn ret; - guint8 *pdata = stream->codec_priv; - - GST_LOG_OBJECT (demux, "priv data size = %" G_GSIZE_FORMAT, - stream->codec_priv_size); - - /* need at least 'fLaC' marker + STREAMINFO metadata block */ - if (stream->codec_priv_size < 80) { - GST_WARNING_OBJECT (demux, "not enough codec priv data for speex headers"); - return GST_FLOW_ERROR; - } - - if (memcmp (pdata, "Speex ", 8) != 0) { - GST_WARNING_OBJECT (demux, "no Speex marker at start of stream headers"); - return GST_FLOW_ERROR; - } + GstFlowReturn ret = GST_FLOW_OK; + gint i, num; - ret = gst_matroska_demux_push_hdr_buf (demux, stream, pdata, 80); - if (ret != GST_FLOW_OK) - return ret; + num = gst_buffer_list_length (stream->stream_headers); + for (i = 0; i < num; ++i) { + GstBuffer *buf; - if (stream->codec_priv_size == 80) - return ret; - else - return gst_matroska_demux_push_hdr_buf (demux, stream, pdata + 80, - stream->codec_priv_size - 80); -} + buf = gst_buffer_list_get (stream->stream_headers, i); + buf = gst_buffer_copy (buf); -static GstFlowReturn -gst_matroska_demux_push_xiph_codec_priv_data (GstMatroskaDemux * demux, - GstMatroskaTrackContext * stream) -{ - GstFlowReturn ret; - guint8 *p = stream->codec_priv; - gint i, offset, num_packets; - guint *length, last; + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER); - if (stream->codec_priv == NULL || stream->codec_priv_size == 0) { - GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), - ("Missing codec private data for xiph headers, broken file")); - return GST_FLOW_ERROR; - } - - /* start of the stream and vorbis audio or theora video, need to - * send the codec_priv data as first three packets */ - num_packets = p[0] + 1; - GST_DEBUG_OBJECT (demux, - "%u stream headers, total length=%" G_GSIZE_FORMAT " bytes", - (guint) num_packets, stream->codec_priv_size); - - length = g_alloca (num_packets * sizeof (guint)); - last = 0; - offset = 1; - - /* first packets, read length values */ - for (i = 0; i < num_packets - 1; i++) { - length[i] = 0; - while (offset < stream->codec_priv_size) { - length[i] += p[offset]; - if (p[offset++] != 0xff) - break; + if (stream->set_discont) { + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); + stream->set_discont = FALSE; } - last += length[i]; - } - if (offset + last > stream->codec_priv_size) - return GST_FLOW_ERROR; - /* last packet is the remaining size */ - length[i] = stream->codec_priv_size - offset - last; + /* push out all headers in one go and use last flow return */ + ret = gst_pad_push (stream->pad, buf); + } - for (i = 0; i < num_packets; i++) { - GST_DEBUG_OBJECT (demux, "buffer %d: length=%u bytes", i, - (guint) length[i]); - if (offset + length[i] > stream->codec_priv_size) - return GST_FLOW_ERROR; + /* don't need these any longer */ + gst_buffer_list_unref (stream->stream_headers); + stream->stream_headers = NULL; - ret = - gst_matroska_demux_push_hdr_buf (demux, stream, p + offset, length[i]); - if (ret != GST_FLOW_OK) - return ret; + /* combine flows */ + ret = gst_matroska_demux_combine_flows (demux, stream, ret); - offset += length[i]; - } - return GST_FLOW_OK; + return ret; } static void @@ -3330,19 +3238,15 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, break; } - if (stream->send_xiph_headers) { - ret = gst_matroska_demux_push_xiph_codec_priv_data (demux, stream); - stream->send_xiph_headers = FALSE; - } - - if (stream->send_flac_headers) { - ret = gst_matroska_demux_push_flac_codec_priv_data (demux, stream); - stream->send_flac_headers = FALSE; - } - - if (stream->send_speex_headers) { - ret = gst_matroska_demux_push_speex_codec_priv_data (demux, stream); - stream->send_speex_headers = FALSE; + if (stream->send_stream_headers) { + if (stream->stream_headers != NULL) { + ret = gst_matroska_demux_push_stream_headers (demux, stream); + } else { + /* FIXME: perhaps we can just disable and skip this stream then */ + GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL), + ("Failed to extract stream headers from codec private data")); + } + stream->send_stream_headers = FALSE; } if (stream->send_dvd_event) { @@ -4931,10 +4835,6 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * g_assert (videocontext != NULL); g_assert (codec_name != NULL); - context->send_xiph_headers = FALSE; - context->send_flac_headers = FALSE; - context->send_speex_headers = FALSE; - if (riff_fourcc) *riff_fourcc = 0; @@ -5143,7 +5043,11 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * *codec_name = g_strdup_printf ("RealVideo %d.0", rmversion); } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_THEORA)) { caps = gst_caps_new_empty_simple ("video/x-theora"); - context->send_xiph_headers = TRUE; + context->stream_headers = + gst_matroska_parse_xiph_stream_headers (context->codec_priv, + context->codec_priv_size); + /* FIXME: mark stream as broken and skip if there are no stream headers */ + context->send_stream_headers = TRUE; } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC)) { caps = gst_caps_new_empty_simple ("video/x-dirac"); *codec_name = g_strdup_printf ("Dirac"); @@ -5307,10 +5211,6 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * if (riff_audio_fmt) *riff_audio_fmt = 0; - context->send_xiph_headers = FALSE; - context->send_flac_headers = FALSE; - context->send_speex_headers = FALSE; - /* TODO: check if we have all codec types from matroska-ids.h * check if we have to do more special things with codec_private * check if we need bitdepth in different places too @@ -5388,14 +5288,25 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * *codec_name = g_strdup ("DTS audio"); } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS)) { caps = gst_caps_new_empty_simple ("audio/x-vorbis"); - context->send_xiph_headers = TRUE; - /* vorbis decoder does tags */ + context->stream_headers = + gst_matroska_parse_xiph_stream_headers (context->codec_priv, + context->codec_priv_size); + /* FIXME: mark stream as broken and skip if there are no stream headers */ + context->send_stream_headers = TRUE; } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_FLAC)) { caps = gst_caps_new_empty_simple ("audio/x-flac"); - context->send_flac_headers = TRUE; + context->stream_headers = + gst_matroska_parse_flac_stream_headers (context->codec_priv, + context->codec_priv_size); + /* FIXME: mark stream as broken and skip if there are no stream headers */ + context->send_stream_headers = TRUE; } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX)) { caps = gst_caps_new_empty_simple ("audio/x-speex"); - context->send_speex_headers = TRUE; + context->stream_headers = + gst_matroska_parse_speex_stream_headers (context->codec_priv, + context->codec_priv_size); + /* FIXME: mark stream as broken and skip if there are no stream headers */ + context->send_stream_headers = TRUE; } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_ACM)) { gst_riff_strf_auds auds; @@ -5652,7 +5563,11 @@ gst_matroska_demux_subtitle_caps (GstMatroskaTrackSubtitleContext * caps = gst_caps_new_empty_simple ("subpicture/x-pgs"); } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_SUBTITLE_KATE)) { caps = gst_caps_new_empty_simple ("subtitle/x-kate"); - context->send_xiph_headers = TRUE; + context->stream_headers = + gst_matroska_parse_xiph_stream_headers (context->codec_priv, + context->codec_priv_size); + /* FIXME: mark stream as broken and skip if there are no stream headers */ + context->send_stream_headers = TRUE; } else { GST_DEBUG ("Unknown subtitle stream: codec_id='%s'", codec_id); caps = gst_caps_new_empty_simple ("application/x-subtitle-unknown"); diff --git a/gst/matroska/matroska-ids.c b/gst/matroska/matroska-ids.c index 8e62395..06c3fd7 100644 --- a/gst/matroska/matroska-ids.c +++ b/gst/matroska/matroska-ids.c @@ -26,6 +26,8 @@ #include "matroska-ids.h" +#include + gboolean gst_matroska_track_init_video_context (GstMatroskaTrackContext ** p_context) { @@ -116,3 +118,151 @@ gst_matroska_register_tags (void) { /* TODO: register other custom tags */ } + +GstBufferList * +gst_matroska_parse_xiph_stream_headers (gpointer codec_data, + gsize codec_data_size) +{ + GstBufferList *list = NULL; + guint8 *p = codec_data; + gint i, offset, num_packets; + guint *length, last; + + GST_MEMDUMP ("xiph codec data", codec_data, codec_data_size); + + if (codec_data == NULL || codec_data_size == 0) + goto error; + + /* start of the stream and vorbis audio or theora video, need to + * send the codec_priv data as first three packets */ + num_packets = p[0] + 1; + GST_DEBUG ("%u stream headers, total length=%" G_GSIZE_FORMAT " bytes", + (guint) num_packets, codec_data_size); + + length = g_alloca (num_packets * sizeof (guint)); + last = 0; + offset = 1; + + /* first packets, read length values */ + for (i = 0; i < num_packets - 1; i++) { + length[i] = 0; + while (offset < codec_data_size) { + length[i] += p[offset]; + if (p[offset++] != 0xff) + break; + } + last += length[i]; + } + if (offset + last > codec_data_size) + goto error; + + /* last packet is the remaining size */ + length[i] = codec_data_size - offset - last; + + list = gst_buffer_list_new (); + + for (i = 0; i < num_packets; i++) { + GstBuffer *hdr; + + GST_DEBUG ("buffer %d: %u bytes", i, (guint) length[i]); + + if (offset + length[i] > codec_data_size) + goto error; + + hdr = gst_buffer_new_wrapped (g_memdup (p + offset, length[i]), length[i]); + gst_buffer_list_add (list, hdr); + + offset += length[i]; + } + + return list; + +/* ERRORS */ +error: + { + if (list != NULL) + gst_buffer_list_unref (list); + return NULL; + } +} + +GstBufferList * +gst_matroska_parse_speex_stream_headers (gpointer codec_data, + gsize codec_data_size) +{ + GstBufferList *list = NULL; + GstBuffer *hdr; + guint8 *pdata = codec_data; + + GST_MEMDUMP ("speex codec data", codec_data, codec_data_size); + + if (codec_data == NULL || codec_data_size < 80) { + GST_WARNING ("not enough codec priv data for speex headers"); + return NULL; + } + + if (memcmp (pdata, "Speex ", 8) != 0) { + GST_WARNING ("no Speex marker at start of stream headers"); + return NULL; + } + + list = gst_buffer_list_new (); + + hdr = gst_buffer_new_wrapped (g_memdup (pdata, 80), 80); + gst_buffer_list_add (list, hdr); + + if (codec_data_size > 80) { + hdr = gst_buffer_new_wrapped (g_memdup (pdata + 80, codec_data_size - 80), + codec_data_size - 80); + gst_buffer_list_add (list, hdr); + } + + return list; +} + +GstBufferList * +gst_matroska_parse_flac_stream_headers (gpointer codec_data, + gsize codec_data_size) +{ + GstBufferList *list = NULL; + GstBuffer *hdr; + guint8 *pdata = codec_data; + guint len, off; + + GST_MEMDUMP ("flac codec data", codec_data, codec_data_size); + + /* need at least 'fLaC' marker + STREAMINFO metadata block */ + if (codec_data == NULL || codec_data_size < ((4) + (4 + 34))) { + GST_WARNING ("not enough codec priv data for flac headers"); + return NULL; + } + + if (memcmp (pdata, "fLaC", 4) != 0) { + GST_WARNING ("no flac marker at start of stream headers"); + return NULL; + } + + list = gst_buffer_list_new (); + + hdr = gst_buffer_new_wrapped (g_memdup (pdata, 4), 4); + gst_buffer_list_add (list, hdr); + + /* skip fLaC marker */ + off = 4; + + /* FIXME: check size remaining */ + while (off < codec_data_size) { + len = GST_READ_UINT8 (pdata + off + 1) << 16; + len |= GST_READ_UINT8 (pdata + off + 2) << 8; + len |= GST_READ_UINT8 (pdata + off + 3); + + GST_DEBUG ("header packet: len=%u bytes, flags=0x%02x", len, pdata[off]); + + /* FIXME: check size remaining */ + hdr = gst_buffer_new_wrapped (g_memdup (pdata + off, len + 4), len + 4); + gst_buffer_list_add (list, hdr); + + off += 4 + len; + } + return list; +} diff --git a/gst/matroska/matroska-ids.h b/gst/matroska/matroska-ids.h index eb30932..c04d569 100644 --- a/gst/matroska/matroska-ids.h +++ b/gst/matroska/matroska-ids.h @@ -518,20 +518,9 @@ struct _GstMatroskaTrackContext { gboolean set_discont; /* TRUE = set DISCONT flag on next buffer */ - /* Special flag for Vorbis and Theora, for which we need to send - * codec_priv first before sending any data, and just testing - * for time == 0 is not enough to detect that. Used by demuxer */ - gboolean send_xiph_headers; - - /* Special flag for Flac, for which we need to reconstruct the header - * buffer from the codec_priv data before sending any data, and just - * testing for time == 0 is not enough to detect that. Used by demuxer */ - gboolean send_flac_headers; - - /* Special flag for Speex, for which we need to reconstruct the header - * buffer from the codec_priv data before sending any data, and just - * testing for time == 0 is not enough to detect that. Used by demuxer */ - gboolean send_speex_headers; + /* Stream header buffer, to put into caps and send before any other buffers */ + GstBufferList * stream_headers; + gboolean send_stream_headers; /* Special flag for VobSub, for which we have to send colour table info * (if available) first before sending any data, and just testing @@ -642,4 +631,13 @@ gboolean gst_matroska_track_init_subtitle_context (GstMatroskaTrackContext ** p_ void gst_matroska_register_tags (void); +GstBufferList * gst_matroska_parse_xiph_stream_headers (gpointer codec_data, + gsize codec_data_size); + +GstBufferList * gst_matroska_parse_speex_stream_headers (gpointer codec_data, + gsize codec_data_size); + +GstBufferList * gst_matroska_parse_flac_stream_headers (gpointer codec_data, + gsize codec_data_size); + #endif /* __GST_MATROSKA_IDS_H__ */ -- 2.7.4