From 2609c394d7c63abfe807701e97e765de4151e91f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 3 Nov 2015 19:51:03 +0200 Subject: [PATCH] tsmux: Simplify Opus caps parsing by using codecutils helpers https://bugzilla.gnome.org/show_bug.cgi?id=757152 --- gst/mpegtsdemux/tsdemux.c | 146 +++++++++++++++++--------------------- gst/mpegtsmux/Makefile.am | 1 + gst/mpegtsmux/mpegtsmux.c | 74 +++++++------------ 3 files changed, 91 insertions(+), 130 deletions(-) diff --git a/gst/mpegtsdemux/tsdemux.c b/gst/mpegtsdemux/tsdemux.c index bcc3018c77..7e22c3596a 100644 --- a/gst/mpegtsdemux/tsdemux.c +++ b/gst/mpegtsdemux/tsdemux.c @@ -1229,123 +1229,105 @@ create_pad_for_stream (MpegTSBase * base, MpegTSBaseStream * bstream, {0, 1, 2, 3, 4, 5, 6, 7}, }; - guint8 codecdata[22 + 256] = { - 'O', 'p', 'u', 's', - 'H', 'e', 'a', 'd', - 1, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, - }; - GstBuffer *codec_data_buf; - guint channels; - GValue v_arr = G_VALUE_INIT; - GValue v_buf = G_VALUE_INIT; - GstTagList *tags; - gint codecdata_len = -1; + gint channels = -1, stream_count, coupled_count, mapping_family; + guint8 *channel_mapping = NULL; channels = channel_config_code ? (channel_config_code & 0x0f) : 2; - codecdata[9] = channels; if (channel_config_code == 0 || channel_config_code == 0x80) { /* Dual Mono */ - codecdata[18] = 255; + mapping_family = 255; if (channel_config_code == 0) { - codecdata[19] = 1; - codecdata[20] = 1; + stream_count = 1; + coupled_count = 1; } else { - codecdata[19] = 2; - codecdata[20] = 0; + stream_count = 2; + coupled_count = 0; } - memcpy (&codecdata[21], channel_map_a[1], channels); - codecdata_len = 24; + channel_mapping = g_new0 (guint8, channels); + memcpy (channel_mapping, &channel_map_a[1], channels); } else if (channel_config_code <= 8) { - codecdata[18] = (channels > 2) ? 1 : 0; - codecdata[19] = + mapping_family = (channels > 2) ? 1 : 0; + stream_count = channel_config_code - coupled_stream_counts[channel_config_code]; - codecdata[20] = coupled_stream_counts[channel_config_code]; - memcpy (&codecdata[21], channel_map_a[channels - 1], channels); - if (codecdata[18] == 0) - codecdata_len = 19; - else - codecdata_len = 21 + channels; + coupled_count = coupled_stream_counts[channel_config_code]; + if (mapping_family != 0) { + channel_mapping = g_new0 (guint8, channels); + memcpy (channel_mapping, &channel_map_a[channels - 1], + channels); + } } else if (channel_config_code >= 0x82 && channel_config_code <= 0x88) { - codecdata[18] = 1; - codecdata[19] = channels; - codecdata[20] = 0; - memcpy (&codecdata[21], channel_map_b[channels - 1], channels); - codecdata_len = 21 + channels; + mapping_family = 1; + stream_count = channels; + coupled_count = 0; + channel_mapping = g_new0 (guint8, channels); + memcpy (channel_mapping, &channel_map_b[channels - 1], + channels); } else if (channel_config_code == 0x81) { - guint8 channel_count, mapping_family; - if (gst_byte_reader_get_remaining (&br) < 2) { GST_WARNING_OBJECT (demux, "Invalid Opus descriptor with extended channel configuration"); + channels = -1; break; } - channel_count = gst_byte_reader_get_uint8_unchecked (&br); + channels = gst_byte_reader_get_uint8_unchecked (&br); mapping_family = gst_byte_reader_get_uint8_unchecked (&br); /* Overwrite values from above */ - if (channel_count == 0) { + if (channels == 0) { GST_WARNING_OBJECT (demux, "Invalid Opus descriptor with extended channel configuration"); + channels = -1; break; } - channels = channel_count; - codecdata[9] = channels; - if (mapping_family == 0 && channel_count <= 2) { - codecdata[18] = 0; - codecdata[19] = - channel_count - coupled_stream_counts[channel_count]; - codecdata[20] = coupled_stream_counts[channel_count]; - codecdata_len = 19; + if (mapping_family == 0 && channels <= 2) { + stream_count = channels - coupled_stream_counts[channels]; + coupled_count = coupled_stream_counts[channels]; } else { GstBitReader breader; guint8 stream_count_minus_one, coupled_stream_count; gint stream_count_minus_one_len, coupled_stream_count_len; gint channel_mapping_len, i; - codecdata[18] = mapping_family; - gst_bit_reader_init (&breader, gst_byte_reader_get_data_unchecked (&br, gst_byte_reader_get_remaining (&br)), gst_byte_reader_get_remaining (&br)); - stream_count_minus_one_len = ceil (log2 (channel_count)); + stream_count_minus_one_len = ceil (log2 (channels)); if (!gst_bit_reader_get_bits_uint8 (&breader, &stream_count_minus_one, stream_count_minus_one_len)) { GST_WARNING_OBJECT (demux, "Invalid Opus descriptor with extended channel configuration"); + channels = -1; break; } - codecdata[19] = stream_count_minus_one + 1; + stream_count = stream_count_minus_one + 1; coupled_stream_count_len = - ceil (log2 (stream_count_minus_one_len + 2)); + ceil (log2 (stream_count_minus_one + 2)); if (!gst_bit_reader_get_bits_uint8 (&breader, &coupled_stream_count, coupled_stream_count_len)) { GST_WARNING_OBJECT (demux, "Invalid Opus descriptor with extended channel configuration"); + channels = -1; break; } - codecdata[20] = coupled_stream_count; + coupled_count = coupled_stream_count; channel_mapping_len = ceil (log2 (stream_count_minus_one + 1 + coupled_stream_count + 1)); - for (i = 0; i < channel_count; i++) { + channel_mapping = g_new0 (guint8, channels); + for (i = 0; i < channels; i++) { if (!gst_bit_reader_get_bits_uint8 (&breader, - &codecdata[21 + i], channel_mapping_len)) { + &channel_mapping[i], channel_mapping_len)) { GST_WARNING_OBJECT (demux, "Invalid Opus descriptor with extended channel configuration"); break; @@ -1353,42 +1335,44 @@ create_pad_for_stream (MpegTSBase * base, MpegTSBaseStream * bstream, } /* error above */ - if (i != channel_count) + if (i != channels) { + channels = -1; + g_free (channel_mapping); + channel_mapping = NULL; break; - - codecdata_len = 22 + channel_count; + } } } else { g_assert_not_reached (); } - if (codecdata_len != -1) { + if (channels != -1) { is_audio = TRUE; template = gst_static_pad_template_get (&audio_template); name = g_strdup_printf ("audio_%04x", bstream->pid); - caps = gst_caps_new_empty_simple ("audio/x-opus"); + caps = gst_caps_new_simple ("audio/x-opus", + "channels", G_TYPE_INT, channels, + "channel-mapping-family", G_TYPE_INT, mapping_family, + "stream-count", G_TYPE_INT, stream_count, + "coupled-count", G_TYPE_INT, coupled_count, NULL); - g_value_init (&v_arr, GST_TYPE_ARRAY); - g_value_init (&v_buf, GST_TYPE_BUFFER); - codec_data_buf = - gst_buffer_new_wrapped (g_memdup (codecdata, codecdata_len), - codecdata_len); - gst_value_take_buffer (&v_buf, codec_data_buf); - gst_value_array_append_and_take_value (&v_arr, &v_buf); + if (channel_mapping) { + GValue v_arr = G_VALUE_INIT; + GValue v = G_VALUE_INIT; + gint i; + g_value_init (&v_arr, GST_TYPE_ARRAY); + g_value_init (&v, G_TYPE_INT); + for (i = 0; i < channels; i++) { + g_value_set_int (&v, channel_mapping[i]); + gst_value_array_append_value (&v_arr, &v); + } - tags = gst_tag_list_new_empty (); - g_value_init (&v_buf, GST_TYPE_BUFFER); - codec_data_buf = - gst_tag_list_to_vorbiscomment_buffer (tags, - (const guint8 *) "OpusTags", 8, "No comments"); - gst_tag_list_unref (tags); - gst_value_take_buffer (&v_buf, codec_data_buf); - gst_value_array_append_and_take_value (&v_arr, &v_buf); - - gst_caps_set_value (caps, "streamheader", &v_arr); - - g_value_unset (&v_arr); + gst_caps_set_value (caps, "channel-mapping", &v_arr); + g_value_unset (&v_arr); + g_value_unset (&v); + g_free (channel_mapping); + } } } else { GST_WARNING_OBJECT (demux, diff --git a/gst/mpegtsmux/Makefile.am b/gst/mpegtsmux/Makefile.am index 37fc935de0..0fa78d7a17 100644 --- a/gst/mpegtsmux/Makefile.am +++ b/gst/mpegtsmux/Makefile.am @@ -13,6 +13,7 @@ libgstmpegtsmux_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) libgstmpegtsmux_la_LIBADD = $(top_builddir)/gst/mpegtsmux/tsmux/libtsmux.la \ $(GST_PLUGINS_BASE_LIBS) -lgstvideo-@GST_API_VERSION@ \ -lgstaudio-@GST_API_VERSION@ -lgsttag-@GST_API_VERSION@ \ + -lgstpbutils-@GST_API_VERSION@ \ $(GST_BASE_LIBS) $(GST_LIBS) libgstmpegtsmux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstmpegtsmux_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) diff --git a/gst/mpegtsmux/mpegtsmux.c b/gst/mpegtsmux/mpegtsmux.c index 0f6346b80a..e18aa75a96 100644 --- a/gst/mpegtsmux/mpegtsmux.c +++ b/gst/mpegtsmux/mpegtsmux.c @@ -91,6 +91,7 @@ #include #include #include +#include #include "mpegtsmux.h" @@ -143,7 +144,9 @@ static GstStaticPadTemplate mpegtsmux_sink_factory = "mute = (boolean) { FALSE, TRUE }; " "audio/x-ac3, framed = (boolean) TRUE;" "audio/x-dts, framed = (boolean) TRUE;" - "audio/x-opus;" + "audio/x-opus, " + "channels = (int) [1, 8], " + "channel-mapping-family = (int) {0, 1};" "subpicture/x-dvb; application/x-teletext; meta/x-klv, parsed=true")); static GstStaticPadTemplate mpegtsmux_src_factory = @@ -671,43 +674,22 @@ mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data) /* needs a particularly sized layout */ ts_data->prepare_func = mpegtsmux_prepare_teletext; } else if (strcmp (mt, "audio/x-opus") == 0) { - GstBuffer *streamheader = NULL; - const GValue *v; - GstMapInfo map; + guint8 channels, mapping_family, stream_count, coupled_count; + guint8 channel_mapping[256]; - v = gst_structure_get_value (s, "streamheader"); - if (v && G_VALUE_HOLDS (v, GST_TYPE_ARRAY) - && gst_value_array_get_size (v) >= 1) { - const GValue *h = gst_value_array_get_value (v, 0); - - streamheader = gst_value_get_buffer (h); + if (!gst_codec_utils_opus_parse_caps (caps, NULL, &channels, + &mapping_family, &stream_count, &coupled_count, channel_mapping)) { + GST_ERROR_OBJECT (pad, "Incomplete Opus caps"); + goto not_negotiated; } - /* FIXME: We need to either map all values for the OpusHead header - * to caps, or always require/generate an OpusHead streamheader for the - * caps. E.g. in rtpopusdepay */ - if (!streamheader || gst_buffer_get_size (streamheader) < 22) { - gint channels; - - if (gst_structure_get_int (s, "channels", &channels) && channels <= 2) { - opus_channel_config_code = channels; - } else { - GST_FIXME_OBJECT (pad, - "Multichannel Opus without streamheader not handled"); - goto not_negotiated; - } - } - - gst_buffer_map (streamheader, &map, GST_MAP_READ); - if (map.data[9] == 2 && map.data[18] == 255 && map.data[19] == 1 - && map.data[20] == 1) { + if (channels <= 2 && mapping_family == 0) { + opus_channel_config_code = channels; + } else if (channels == 2 && mapping_family == 255 && stream_count == 1 + && coupled_count == 1) { /* Dual mono */ opus_channel_config_code = 0; - } else if (map.data[9] >= 1 && map.data[9] <= 2 && map.data[18] == 0) { - /* RTP mapping */ - opus_channel_config_code = map.data[9]; - } else if (map.data[9] >= 2 && map.data[9] <= 8 && map.data[18] == 1 - && map.size >= 21 + map.data[9]) { + } else if (channels >= 2 && channels <= 8 && mapping_family == 1) { static const guint8 coupled_stream_counts[9] = { 1, 0, 1, 1, 2, 2, 2, 3, 3 }; @@ -733,27 +715,21 @@ mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data) }; /* Vorbis mapping */ - if (map.data[19] == map.data[9] - coupled_stream_counts[map.data[9]] && - map.data[20] == coupled_stream_counts[map.data[9]] && - memcmp (&map.data[21], channel_map_a[map.data[9] - 1], - map.data[9]) == 0) { - opus_channel_config_code = map.data[9]; - } else if (map.data[19] == map.data[9] && - map.data[20] == 0 && - memcmp (&map.data[21], channel_map_b[map.data[9] - 1], - map.data[9]) == 0) { - opus_channel_config_code = map.data[9] | 0x80; + if (stream_count == channels - coupled_stream_counts[channels] && + coupled_count == coupled_stream_counts[channels] && + memcmp (channel_mapping, channel_map_a[channels - 1], + channels) == 0) { + opus_channel_config_code = channels; + } else if (stream_count == channels - coupled_stream_counts[channels] && + coupled_count == coupled_stream_counts[channels] && + memcmp (channel_mapping, channel_map_b[channels - 1], + channels) == 0) { + opus_channel_config_code = channels | 0x80; } else { - gst_buffer_unmap (streamheader, &map); GST_FIXME_OBJECT (pad, "Opus channel mapping not handled"); goto not_negotiated; } - } else { - gst_buffer_unmap (streamheader, &map); - GST_FIXME_OBJECT (pad, "Opus channel mapping not handled"); - goto not_negotiated; } - gst_buffer_unmap (streamheader, &map); st = TSMUX_ST_PS_OPUS; ts_data->prepare_func = mpegtsmux_prepare_opus;