From: Vincent Penquerc'h Date: Wed, 17 Dec 2014 17:36:18 +0000 (+0000) Subject: matroska: mux/demux the OpusHead header X-Git-Tag: 1.19.3~509^2~3957 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b7413279d9ff2956b04360f40a806557f3c18b9c;p=platform%2Fupstream%2Fgstreamer.git matroska: mux/demux the OpusHead header This is meant to be so (https://wiki.xiph.org/MatroskaOpus - while it is marked as a draft, this part was confirmed to be correct on IRC), and allows one to determine whether a demuxed stream is multistream or not, and thus set the multistream caps field accordingly. In turn, this means downstream does not have to guess. https://bugzilla.gnome.org/show_bug.cgi?id=740744 --- diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index 03d5286..ddca9cd 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -5278,6 +5278,18 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)) { caps = gst_caps_new_empty_simple ("audio/x-opus"); *codec_name = g_strdup ("Opus"); + context->stream_headers = + gst_matroska_parse_opus_stream_headers (context->codec_priv, + context->codec_priv_size); + if (context->stream_headers) { + /* There was a valid header. Multistream headers are more than + * 19 bytes, as they include an extra channel mapping table. */ + gboolean multistream = (context->codec_priv_size > 19); + gst_caps_set_simple (caps, "multistream", G_TYPE_BOOLEAN, multistream, + NULL); + } + /* 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; diff --git a/gst/matroska/matroska-ids.c b/gst/matroska/matroska-ids.c index 60ebd0e..7fdcac8 100644 --- a/gst/matroska/matroska-ids.c +++ b/gst/matroska/matroska-ids.c @@ -221,6 +221,36 @@ gst_matroska_parse_speex_stream_headers (gpointer codec_data, } GstBufferList * +gst_matroska_parse_opus_stream_headers (gpointer codec_data, + gsize codec_data_size) +{ + GstBufferList *list = NULL; + GstBuffer *hdr; + guint8 *pdata = codec_data; + + GST_MEMDUMP ("opus codec data", codec_data, codec_data_size); + + if (codec_data == NULL || codec_data_size < 19) { + GST_WARNING ("not enough codec priv data for opus headers"); + return NULL; + } + + if (memcmp (pdata, "OpusHead", 8) != 0) { + GST_WARNING ("no OpusHead marker at start of stream headers"); + return NULL; + } + + list = gst_buffer_list_new (); + + hdr = + gst_buffer_new_wrapped (g_memdup (pdata, codec_data_size), + codec_data_size); + gst_buffer_list_add (list, hdr); + + return list; +} + +GstBufferList * gst_matroska_parse_flac_stream_headers (gpointer codec_data, gsize codec_data_size) { diff --git a/gst/matroska/matroska-ids.h b/gst/matroska/matroska-ids.h index 68a68d4..d971ba2 100644 --- a/gst/matroska/matroska-ids.h +++ b/gst/matroska/matroska-ids.h @@ -640,6 +640,9 @@ GstBufferList * gst_matroska_parse_xiph_stream_headers (gpointer codec_data, GstBufferList * gst_matroska_parse_speex_stream_headers (gpointer codec_data, gsize codec_data_size); +GstBufferList * gst_matroska_parse_opus_stream_headers (gpointer codec_data, + gsize codec_data_size); + GstBufferList * gst_matroska_parse_flac_stream_headers (gpointer codec_data, gsize codec_data_size); void gst_matroska_track_free (GstMatroskaTrackContext * track); diff --git a/gst/matroska/matroska-mux.c b/gst/matroska/matroska-mux.c index 6c3519e..72a8e2c 100644 --- a/gst/matroska/matroska-mux.c +++ b/gst/matroska/matroska-mux.c @@ -1583,6 +1583,58 @@ speex_streamheader_to_codecdata (const GValue * streamheader, return TRUE; } +static gboolean +opus_streamheader_to_codecdata (const GValue * streamheader, + GstMatroskaTrackContext * context) +{ + GArray *bufarr; + GValue *bufval; + GstBuffer *buf; + + if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) + goto wrong_type; + + bufarr = g_value_peek_pointer (streamheader); + if (bufarr->len <= 0 || bufarr->len > 255) /* one header, and count stored in a byte */ + goto wrong_count; + if (bufarr->len != 1 && bufarr->len != 2) + goto wrong_count; + + context->xiph_headers_to_skip = bufarr->len; + + bufval = &g_array_index (bufarr, GValue, 0); + if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) { + goto wrong_content_type; + } + buf = g_value_peek_pointer (bufval); + + gst_matroska_mux_free_codec_priv (context); + + context->codec_priv_size = gst_buffer_get_size (buf); + context->codec_priv = g_malloc0 (context->codec_priv_size); + gst_buffer_extract (buf, 0, context->codec_priv, -1); + + return TRUE; + +/* ERRORS */ +wrong_type: + { + GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s", + G_VALUE_TYPE_NAME (streamheader)); + return FALSE; + } +wrong_count: + { + GST_WARNING ("got %u streamheaders, not 1 or 2 as expected", bufarr->len); + return FALSE; + } +wrong_content_type: + { + GST_WARNING ("streamheaders array does not contain GstBuffers"); + return FALSE; + } +} + static const gchar * aac_codec_data_to_codec_id (GstBuffer * buf) { @@ -1834,7 +1886,19 @@ gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps) goto refuse_caps; } } else if (!strcmp (mimetype, "audio/x-opus")) { + const GValue *streamheader; + gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_OPUS); + + streamheader = gst_structure_get_value (structure, "streamheader"); + if (streamheader) { + gst_matroska_mux_free_codec_priv (context); + if (!opus_streamheader_to_codecdata (streamheader, context)) { + GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL), + ("opus stream headers missing or malformed")); + goto refuse_caps; + } + } } else if (!strcmp (mimetype, "audio/x-ac3")) { gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3); } else if (!strcmp (mimetype, "audio/x-eac3")) {