mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-03 15:06:34 +00:00
tsmux: Simplify Opus caps parsing by using codecutils helpers
https://bugzilla.gnome.org/show_bug.cgi?id=757152
This commit is contained in:
parent
51edbeb9d9
commit
2609c394d7
3 changed files with 91 additions and 130 deletions
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -91,6 +91,7 @@
|
|||
#include <gst/tag/tag.h>
|
||||
#include <gst/video/video.h>
|
||||
#include <gst/mpegts/mpegts.h>
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
|
||||
#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;
|
||||
|
|
Loading…
Reference in a new issue