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
This commit is contained in:
Vincent Penquerc'h 2014-12-17 17:36:18 +00:00
parent d18b893d28
commit b7413279d9
4 changed files with 109 additions and 0 deletions

View file

@ -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;

View file

@ -220,6 +220,36 @@ gst_matroska_parse_speex_stream_headers (gpointer codec_data,
return list;
}
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)

View file

@ -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);

View file

@ -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")) {