From 4ca84a9b1a0ee1643a698662680aab4cd0d46ef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 3 Nov 2015 14:50:53 +0200 Subject: opus: Add proper support for multichannel audio https://bugzilla.gnome.org/show_bug.cgi?id=757152 --- ext/opus/Makefile.am | 1 + ext/opus/gstopusdec.c | 133 ++++++++++++++--------------- ext/opus/gstopusdec.h | 6 +- ext/opus/gstopusenc.c | 196 ++++++++++++++++++++++++------------------- ext/opus/gstopusheader.c | 212 ----------------------------------------------- ext/opus/gstopusheader.h | 6 -- 6 files changed, 177 insertions(+), 377 deletions(-) diff --git a/ext/opus/Makefile.am b/ext/opus/Makefile.am index d22c664e0..c64369252 100644 --- a/ext/opus/Makefile.am +++ b/ext/opus/Makefile.am @@ -10,6 +10,7 @@ libgstopus_la_CFLAGS = \ libgstopus_la_LIBADD = \ $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_API_VERSION) \ -lgsttag-$(GST_API_VERSION) -lgstrtp-$(GST_API_VERSION) \ + -lgstpbutils-$(GST_API_VERSION) \ $(GST_BASE_LIBS) \ $(GST_LIBS) \ $(OPUS_LIBS) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 23649d2ab..8ca2809a3 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -47,6 +47,7 @@ #include "gstopusheader.h" #include "gstopuscommon.h" #include "gstopusdec.h" +#include GST_DEBUG_CATEGORY_STATIC (opusdec_debug); #define GST_CAT_DEFAULT opusdec_debug @@ -225,6 +226,8 @@ gst_opus_dec_negotiate (GstOpusDec * dec, const GstAudioChannelPosition * pos) GstAudioInfo info; if (caps) { + gint rate, channels; + caps = gst_caps_truncate (caps); caps = gst_caps_make_writable (caps); s = gst_caps_get_structure (caps, 0); @@ -233,13 +236,15 @@ gst_opus_dec_negotiate (GstOpusDec * dec, const GstAudioChannelPosition * pos) gst_structure_fixate_field_nearest_int (s, "rate", dec->sample_rate); else gst_structure_set (s, "rate", G_TYPE_INT, dec->sample_rate, NULL); - gst_structure_get_int (s, "rate", &dec->sample_rate); + gst_structure_get_int (s, "rate", &rate); + dec->sample_rate = rate; if (gst_structure_has_field (s, "channels")) gst_structure_fixate_field_nearest_int (s, "channels", dec->n_channels); else gst_structure_set (s, "channels", G_TYPE_INT, dec->n_channels, NULL); - gst_structure_get_int (s, "channels", &dec->n_channels); + gst_structure_get_int (s, "channels", &channels); + dec->n_channels = channels; gst_caps_unref (caps); } @@ -273,7 +278,6 @@ gst_opus_dec_negotiate (GstOpusDec * dec, const GstAudioChannelPosition * pos) /* but we still need the opus order for later reordering */ if (pos) { memcpy (dec->opus_pos, pos, sizeof (pos[0]) * dec->n_channels); - gst_audio_channel_positions_to_valid_order (dec->opus_pos, dec->n_channels); } else { dec->opus_pos[0] = GST_AUDIO_CHANNEL_POSITION_INVALID; } @@ -284,79 +288,64 @@ gst_opus_dec_negotiate (GstOpusDec * dec, const GstAudioChannelPosition * pos) static GstFlowReturn gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) { - const guint8 *data; GstAudioChannelPosition pos[64]; const GstAudioChannelPosition *posn = NULL; - GstMapInfo map; if (!gst_opus_header_is_id_header (buf)) { GST_ERROR_OBJECT (dec, "Header is not an Opus ID header"); return GST_FLOW_ERROR; } - gst_buffer_map (buf, &map, GST_MAP_READ); - data = map.data; - - dec->n_channels = data[9]; - dec->sample_rate = GST_READ_UINT32_LE (data + 12); - if (dec->sample_rate == 0) - dec->sample_rate = 48000; - dec->pre_skip = GST_READ_UINT16_LE (data + 10); - dec->r128_gain = GST_READ_UINT16_LE (data + 16); + if (!gst_codec_utils_opus_parse_header (buf, + &dec->sample_rate, + &dec->n_channels, + &dec->channel_mapping_family, + &dec->n_streams, + &dec->n_stereo_streams, + dec->channel_mapping, &dec->pre_skip, &dec->r128_gain)) { + GST_ERROR_OBJECT (dec, "Failed to parse Opus ID header"); + return GST_FLOW_ERROR; + } dec->r128_gain_volume = gst_opus_dec_get_r128_volume (dec->r128_gain); + GST_INFO_OBJECT (dec, "Found pre-skip of %u samples, R128 gain %d (volume %f)", dec->pre_skip, dec->r128_gain, dec->r128_gain_volume); - dec->channel_mapping_family = data[18]; - if (dec->channel_mapping_family == 0) { - /* implicit mapping */ - GST_INFO_OBJECT (dec, "Channel mapping family 0, implicit mapping"); - dec->n_streams = dec->n_stereo_streams = 1; - dec->channel_mapping[0] = 0; - dec->channel_mapping[1] = 1; - } else { - dec->n_streams = data[19]; - dec->n_stereo_streams = data[20]; - memcpy (dec->channel_mapping, data + 21, dec->n_channels); - - if (dec->channel_mapping_family == 1) { - GST_INFO_OBJECT (dec, "Channel mapping family 1, Vorbis mapping"); - switch (dec->n_channels) { - case 1: - case 2: - /* nothing */ - break; - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - posn = gst_opus_channel_positions[dec->n_channels - 1]; - break; - default:{ - gint i; - - GST_ELEMENT_WARNING (GST_ELEMENT (dec), STREAM, DECODE, - (NULL), ("Using NONE channel layout for more than 8 channels")); - - for (i = 0; i < dec->n_channels; i++) - pos[i] = GST_AUDIO_CHANNEL_POSITION_NONE; - - posn = pos; - } + if (dec->channel_mapping_family == 1) { + GST_INFO_OBJECT (dec, "Channel mapping family 1, Vorbis mapping"); + switch (dec->n_channels) { + case 1: + case 2: + /* nothing */ + break; + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + posn = gst_opus_channel_positions[dec->n_channels - 1]; + break; + default:{ + gint i; + + GST_ELEMENT_WARNING (GST_ELEMENT (dec), STREAM, DECODE, + (NULL), ("Using NONE channel layout for more than 8 channels")); + + for (i = 0; i < dec->n_channels; i++) + pos[i] = GST_AUDIO_CHANNEL_POSITION_NONE; + + posn = pos; } - } else { - GST_INFO_OBJECT (dec, "Channel mapping family %d", - dec->channel_mapping_family); } + } else { + GST_INFO_OBJECT (dec, "Channel mapping family %d", + dec->channel_mapping_family); } gst_opus_dec_negotiate (dec, posn); - gst_buffer_unmap (buf, &map); - return GST_FLOW_OK; } @@ -660,8 +649,10 @@ gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps) if (header && G_VALUE_HOLDS (header, GST_TYPE_BUFFER)) { buf = gst_value_get_buffer (header); res = gst_opus_dec_parse_header (dec, buf); - if (res != GST_FLOW_OK) + if (res != GST_FLOW_OK) { + ret = FALSE; goto done; + } gst_buffer_replace (&dec->streamheader, buf); } @@ -669,26 +660,26 @@ gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps) if (vorbiscomment && G_VALUE_HOLDS (vorbiscomment, GST_TYPE_BUFFER)) { buf = gst_value_get_buffer (vorbiscomment); res = gst_opus_dec_parse_comments (dec, buf); - if (res != GST_FLOW_OK) + if (res != GST_FLOW_OK) { + ret = FALSE; goto done; + } gst_buffer_replace (&dec->vorbiscomment, buf); } } else { - /* defaults if not in the caps */ - dec->n_channels = 2; - dec->sample_rate = 48000; + const GstAudioChannelPosition *posn = NULL; - gst_structure_get_int (s, "channels", &dec->n_channels); - gst_structure_get_int (s, "rate", &dec->sample_rate); + if (!gst_codec_utils_opus_parse_caps (caps, &dec->sample_rate, + &dec->n_channels, &dec->channel_mapping_family, &dec->n_streams, + &dec->n_stereo_streams, dec->channel_mapping)) { + ret = FALSE; + goto done; + } - /* default stereo mapping */ - dec->channel_mapping_family = 0; - dec->channel_mapping[0] = 0; - dec->channel_mapping[1] = 1; - dec->n_streams = 1; - dec->n_stereo_streams = 1; + if (dec->channel_mapping_family == 1 && dec->n_channels <= 8) + posn = gst_opus_channel_positions[dec->n_channels - 1]; - gst_opus_dec_negotiate (dec, NULL); + gst_opus_dec_negotiate (dec, posn); } done: diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index f8d43a926..df52cfb6f 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -52,9 +52,9 @@ struct _GstOpusDec { GstBuffer *streamheader; GstBuffer *vorbiscomment; - int sample_rate; - int n_channels; - guint32 pre_skip; + guint32 sample_rate; + guint8 n_channels; + guint16 pre_skip; gint16 r128_gain; GstAudioChannelPosition opus_pos[64]; diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 17004ce1b..4196c1c8c 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -48,6 +48,8 @@ #include #include +#include +#include #include #include "gstopusheader.h" #include "gstopuscommon.h" @@ -167,12 +169,12 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", "format = (string) " FORMAT_STR ", " "layout = (string) interleaved, " "rate = (int) 48000, " - "channels = (int) [ 1, 2 ]; " + "channels = (int) [ 1, 8 ]; " "audio/x-raw, " "format = (string) " FORMAT_STR ", " "layout = (string) interleaved, " "rate = (int) { 8000, 12000, 16000, 24000 }, " - "channels = (int) [ 1, 2 ] ") + "channels = (int) [ 1, 8 ] ") ); static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", @@ -545,27 +547,20 @@ gst_opus_enc_setup_channel_mappings (GstOpusEnc * enc, /* For two channels, use the basic RTP mapping if the channels are mapped as left/right. */ if (enc->n_channels == 2) { - if (MAPS (0, FRONT_LEFT) && MAPS (1, FRONT_RIGHT)) { - GST_INFO_OBJECT (enc, "Stereo, canonical mapping"); - enc->channel_mapping_family = 0; - enc->n_stereo_streams = 1; - /* The channel mapping is implicit for family 0, that's why we do not - attempt to create one for right/left - this will be mapped to the - Vorbis mapping below. */ - return; - } else { - GST_DEBUG_OBJECT (enc, "Stereo, but not canonical mapping, continuing"); - } + GST_INFO_OBJECT (enc, "Stereo, trivial RTP mapping"); + enc->channel_mapping_family = 0; + enc->n_stereo_streams = 1; + /* implicit mapping for family 0 */ + return; } - /* For channels between 1 and 8, we use the Vorbis mapping if we can - find a permutation that matches it. Mono will have been taken care - of earlier, but this code also handles it. Same for left/right stereo. - There are two mappings. One maps the input channels to an ordering - which has the natural pairs first so they can benefit from the Opus - stereo channel coupling, and the other maps this ordering to the - Vorbis ordering. */ - if (enc->n_channels >= 1 && enc->n_channels <= 8) { + /* For channels between 3 and 8, we use the Vorbis mapping if we can + find a permutation that matches it. Mono and stereo will have been taken + care of earlier, but this code also handles it. There are two mappings. + One maps the input channels to an ordering which has the natural pairs + first so they can benefit from the Opus stereo channel coupling, and the + other maps this ordering to the Vorbis ordering. */ + if (enc->n_channels >= 3 && enc->n_channels <= 8) { int c0, c1, c0v, c1v; int mapped; gboolean positions_done[256]; @@ -580,6 +575,8 @@ gst_opus_enc_setup_channel_mappings (GstOpusEnc * enc, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, + {GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, }; size_t pair; @@ -633,13 +630,8 @@ gst_opus_enc_setup_channel_mappings (GstOpusEnc * enc, GST_DEBUG_OBJECT (enc, "Channel position %s is not mapped yet, adding", gst_opus_channel_names[position]); cv = gst_opus_enc_find_channel_position_in_vorbis_order (enc, position); - if (cv < 0) { - GST_WARNING_OBJECT (enc, - "Cannot map channel positions to Vorbis order, using unknown mapping"); - enc->channel_mapping_family = 255; - enc->n_stereo_streams = 0; - return; - } + if (cv < 0) + g_assert_not_reached (); enc->encoding_channel_mapping[mapped] = n; enc->decoding_channel_mapping[cv] = mapped; mapped++; @@ -718,6 +710,8 @@ gst_opus_enc_setup (GstOpusEnc * enc) GstCaps *caps; gboolean ret; gint32 lookahead; + const GstTagList *tags; + GstBuffer *header, *comments; #ifndef GST_DISABLE_GST_DEBUG GST_DEBUG_OBJECT (enc, @@ -764,10 +758,17 @@ gst_opus_enc_setup (GstOpusEnc * enc) lookahead = lookahead * 48000 / enc->sample_rate; enc->lookahead = enc->pending_lookahead = lookahead; - gst_opus_header_create_caps (&caps, NULL, lookahead, enc->sample_rate, - enc->n_channels, enc->n_stereo_streams, enc->channel_mapping_family, - enc->decoding_channel_mapping, - gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc))); + header = gst_codec_utils_opus_create_header (enc->sample_rate, + enc->n_channels, enc->channel_mapping_family, + enc->n_channels - enc->n_stereo_streams, enc->n_stereo_streams, + enc->decoding_channel_mapping, lookahead, 0); + tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc)); + comments = + gst_tag_list_to_vorbiscomment_buffer (tags, (const guint8 *) "OpusTags", + 8, "Encoded with GStreamer opusenc"); + caps = gst_codec_utils_opus_create_caps_from_header (header, comments); + gst_buffer_unref (header); + gst_buffer_unref (comments); /* negotiate with these caps */ GST_DEBUG_OBJECT (enc, "here are the caps: %" GST_PTR_FORMAT, caps); @@ -813,71 +814,96 @@ gst_opus_enc_sink_event (GstAudioEncoder * benc, GstEvent * event) return GST_AUDIO_ENCODER_CLASS (parent_class)->sink_event (benc, event); } +static GstCaps * +gst_opus_enc_get_sink_template_caps (void) +{ + static volatile gsize init = 0; + static GstCaps *caps = NULL; + + if (g_once_init_enter (&init)) { + GValue rate_array = G_VALUE_INIT; + GValue v = G_VALUE_INIT; + GstStructure *s1, *s2, *s; + gint i, c; + + caps = gst_caps_new_empty (); + + /* Generate our two template structures */ + g_value_init (&rate_array, GST_TYPE_LIST); + g_value_init (&v, G_TYPE_INT); + g_value_set_int (&v, 8000); + gst_value_list_append_value (&rate_array, &v); + g_value_set_int (&v, 12000); + gst_value_list_append_value (&rate_array, &v); + g_value_set_int (&v, 16000); + gst_value_list_append_value (&rate_array, &v); + g_value_set_int (&v, 24000); + gst_value_list_append_value (&rate_array, &v); + + s1 = gst_structure_new ("audio/x-raw", + "format", G_TYPE_STRING, GST_AUDIO_NE (S16), + "layout", G_TYPE_STRING, "interleaved", + "rate", G_TYPE_INT, 48000, NULL); + s2 = gst_structure_new ("audio/x-raw", + "format", G_TYPE_STRING, GST_AUDIO_NE (S16), + "layout", G_TYPE_STRING, "interleaved", NULL); + gst_structure_set_value (s2, "rate", &rate_array); + g_value_unset (&rate_array); + g_value_unset (&v); + + /* Mono */ + s = gst_structure_copy (s1); + gst_structure_set (s, "channels", G_TYPE_INT, 1, NULL); + gst_caps_append_structure (caps, s); + + s = gst_structure_copy (s2); + gst_structure_set (s, "channels", G_TYPE_INT, 1, NULL); + gst_caps_append_structure (caps, s); + + /* Stereo and further */ + for (i = 2; i <= 8; i++) { + guint64 channel_mask = 0; + const GstAudioChannelPosition *pos = gst_opus_channel_positions[i - 1]; + + for (c = 0; c < i; c++) { + channel_mask |= G_GUINT64_CONSTANT (1) << pos[c]; + } + + s = gst_structure_copy (s1); + gst_structure_set (s, "channels", G_TYPE_INT, i, "channel-mask", + GST_TYPE_BITMASK, channel_mask, NULL); + gst_caps_append_structure (caps, s); + + s = gst_structure_copy (s2); + gst_structure_set (s, "channels", G_TYPE_INT, i, "channel-mask", + GST_TYPE_BITMASK, channel_mask, NULL); + gst_caps_append_structure (caps, s); + } + + gst_structure_free (s1); + gst_structure_free (s2); + + g_once_init_leave (&init, 1); + } + + return caps; +} + static GstCaps * gst_opus_enc_sink_getcaps (GstAudioEncoder * benc, GstCaps * filter) { GstOpusEnc *enc; GstCaps *caps; - GstCaps *tcaps; - GstCaps *peercaps = NULL; - GstCaps *intersect = NULL; - guint i; - gboolean allow_multistream; enc = GST_OPUS_ENC (benc); GST_DEBUG_OBJECT (enc, "sink getcaps"); - peercaps = gst_pad_peer_query_caps (GST_AUDIO_ENCODER_SRC_PAD (benc), NULL); - if (!peercaps) { - GST_DEBUG_OBJECT (benc, "No peercaps, returning template sink caps"); - return gst_pad_get_pad_template_caps (GST_AUDIO_ENCODER_SINK_PAD (benc)); - } - - tcaps = gst_pad_get_pad_template_caps (GST_AUDIO_ENCODER_SRC_PAD (benc)); - intersect = gst_caps_intersect (peercaps, tcaps); - gst_caps_unref (tcaps); - gst_caps_unref (peercaps); - - if (gst_caps_is_empty (intersect)) - return intersect; - - allow_multistream = FALSE; - for (i = 0; i < gst_caps_get_size (intersect); i++) { - GstStructure *s = gst_caps_get_structure (intersect, i); - gboolean multistream; - if (gst_structure_get_boolean (s, "multistream", &multistream)) { - if (multistream) { - allow_multistream = TRUE; - } - } else { - allow_multistream = TRUE; - } - } - - gst_caps_unref (intersect); - - caps = gst_pad_get_pad_template_caps (GST_AUDIO_ENCODER_SINK_PAD (benc)); - caps = gst_caps_make_writable (caps); - if (!allow_multistream) { - GValue range = { 0 }; - g_value_init (&range, GST_TYPE_INT_RANGE); - gst_value_set_int_range (&range, 1, 2); - for (i = 0; i < gst_caps_get_size (caps); i++) { - GstStructure *s = gst_caps_get_structure (caps, i); - gst_structure_set_value (s, "channels", &range); - } - g_value_unset (&range); - } - - if (filter) { - GstCaps *tmp = gst_caps_intersect_full (caps, filter, - GST_CAPS_INTERSECT_FIRST); - gst_caps_unref (caps); - caps = tmp; - } + caps = gst_opus_enc_get_sink_template_caps (); + caps = gst_audio_encoder_proxy_getcaps (benc, caps, filter); GST_DEBUG_OBJECT (enc, "Returning caps: %" GST_PTR_FORMAT, caps); + return caps; } diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 7c1cebe3c..dcc85897d 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -26,218 +26,6 @@ #include #include "gstopusheader.h" -static GstBuffer * -gst_opus_enc_create_id_buffer (guint16 pre_skip, guint sample_rate, - guint8 nchannels, guint8 n_stereo_streams, guint8 channel_mapping_family, - const guint8 * channel_mapping) -{ - GstBuffer *buffer; - GstByteWriter bw; - gboolean hdl = TRUE; - - g_return_val_if_fail (nchannels > 0, NULL); - g_return_val_if_fail (n_stereo_streams <= nchannels - n_stereo_streams, NULL); - - gst_byte_writer_init (&bw); - - /* See http://wiki.xiph.org/OggOpus */ - hdl &= gst_byte_writer_put_data (&bw, (const guint8 *) "OpusHead", 8); - hdl &= gst_byte_writer_put_uint8 (&bw, 0x01); /* version number */ - hdl &= gst_byte_writer_put_uint8 (&bw, nchannels); - hdl &= gst_byte_writer_put_uint16_le (&bw, pre_skip); - hdl &= gst_byte_writer_put_uint32_le (&bw, sample_rate); - hdl &= gst_byte_writer_put_uint16_le (&bw, 0); /* output gain */ - hdl &= gst_byte_writer_put_uint8 (&bw, channel_mapping_family); - if (channel_mapping_family > 0) { - hdl &= gst_byte_writer_put_uint8 (&bw, nchannels - n_stereo_streams); - hdl &= gst_byte_writer_put_uint8 (&bw, n_stereo_streams); - hdl &= gst_byte_writer_put_data (&bw, channel_mapping, nchannels); - } - - if (!hdl) - GST_WARNING ("Error creating header"); - - buffer = gst_byte_writer_reset_and_get_buffer (&bw); - - GST_BUFFER_OFFSET (buffer) = 0; - GST_BUFFER_OFFSET_END (buffer) = 0; - - return buffer; -} - -static GstBuffer * -gst_opus_enc_create_metadata_buffer (const GstTagList * tags) -{ - GstTagList *empty_tags = NULL; - GstBuffer *comments = NULL; - - GST_DEBUG ("tags = %" GST_PTR_FORMAT, tags); - - if (tags == NULL) { - /* FIXME: better fix chain of callers to not write metadata at all, - * if there is none */ - empty_tags = gst_tag_list_new_empty (); - tags = empty_tags; - } - comments = - gst_tag_list_to_vorbiscomment_buffer (tags, (const guint8 *) "OpusTags", - 8, "Encoded with GStreamer Opusenc"); - - GST_BUFFER_OFFSET (comments) = 0; - GST_BUFFER_OFFSET_END (comments) = 0; - - if (empty_tags) - gst_tag_list_unref (empty_tags); - - return comments; -} - -/* - * (really really) FIXME: move into core (dixit tpm) - */ -/* - * _gst_caps_set_buffer_array: - * @caps: (transfer full): a #GstCaps - * @field: field in caps to set - * @buf: header buffers - * - * Adds given buffers to an array of buffers set as the given @field - * on the given @caps. List of buffer arguments must be NULL-terminated. - * - * Returns: (transfer full): input caps with a streamheader field added, or NULL - * if some error occurred - */ -static GstCaps * -_gst_caps_set_buffer_array (GstCaps * caps, const gchar * field, - GstBuffer * buf, ...) -{ - GstStructure *structure = NULL; - va_list va; - GValue array = { 0 }; - GValue value = { 0 }; - - g_return_val_if_fail (caps != NULL, NULL); - g_return_val_if_fail (gst_caps_is_fixed (caps), NULL); - g_return_val_if_fail (field != NULL, NULL); - - caps = gst_caps_make_writable (caps); - structure = gst_caps_get_structure (caps, 0); - - g_value_init (&array, GST_TYPE_ARRAY); - - va_start (va, buf); - /* put buffers in a fixed list */ - while (buf) { - g_assert (gst_buffer_is_writable (buf)); - - /* mark buffer */ - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER); - - g_value_init (&value, GST_TYPE_BUFFER); - buf = gst_buffer_copy (buf); - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER); - gst_value_set_buffer (&value, buf); - gst_buffer_unref (buf); - gst_value_array_append_value (&array, &value); - g_value_unset (&value); - - buf = va_arg (va, GstBuffer *); - } - va_end (va); - - gst_structure_set_value (structure, field, &array); - g_value_unset (&array); - - return caps; -} - -void -gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, - GstBuffer * buf1, GstBuffer * buf2) -{ - int n_streams, family; - gint channels, rate; - gboolean multistream; - GstMapInfo map; - guint8 *data; - - g_return_if_fail (caps); - g_return_if_fail (!headers || !*headers); - g_return_if_fail (gst_buffer_get_size (buf1) >= 19); - - gst_buffer_map (buf1, &map, GST_MAP_READ); - data = map.data; - - channels = data[9]; - rate = GST_READ_UINT32_LE (data + 12); - - /* work out the number of streams */ - family = data[18]; - if (family == 0) { - n_streams = 1; - } else { - /* only included in the header for family > 0 */ - if (map.size >= 20) - n_streams = data[19]; - else { - g_warning ("family > 0 but header buffer size < 20"); - gst_buffer_unmap (buf1, &map); - return; - } - } - - /* TODO: should probably also put the channel mapping into the caps too once - * we actually support multi channel, and pre-skip and other fields */ - - gst_buffer_unmap (buf1, &map); - - /* mark and put on caps */ - multistream = n_streams > 1; - *caps = gst_caps_new_simple ("audio/x-opus", - "multistream", G_TYPE_BOOLEAN, multistream, - "channels", G_TYPE_INT, channels, NULL); - - if (rate > 0) { - gst_caps_set_simple (*caps, "rate", G_TYPE_INT, rate, NULL); - } - - *caps = _gst_caps_set_buffer_array (*caps, "streamheader", buf1, buf2, NULL); - - if (headers) { - *headers = g_slist_prepend (*headers, gst_buffer_ref (buf2)); - *headers = g_slist_prepend (*headers, gst_buffer_ref (buf1)); - } -} - -void -gst_opus_header_create_caps (GstCaps ** caps, GSList ** headers, - guint16 pre_skip, guint sample_rate, guint8 nchannels, - guint8 n_stereo_streams, guint8 channel_mapping_family, - const guint8 * channel_mapping, const GstTagList * tags) -{ - GstBuffer *buf1, *buf2; - - g_return_if_fail (caps); - g_return_if_fail (!headers || !*headers); - g_return_if_fail (nchannels > 0); - g_return_if_fail (channel_mapping_family == 0 || channel_mapping); - - /* Opus streams in Ogg begin with two headers; the initial header (with - most of the codec setup parameters) which is mandated by the Ogg - bitstream spec. The second header holds any comment fields. */ - - /* create header buffers */ - buf1 = - gst_opus_enc_create_id_buffer (pre_skip, sample_rate, nchannels, - n_stereo_streams, channel_mapping_family, channel_mapping); - buf2 = gst_opus_enc_create_metadata_buffer (tags); - - gst_opus_header_create_caps_from_headers (caps, headers, buf1, buf2); - - gst_buffer_unref (buf2); - gst_buffer_unref (buf1); -} - gboolean gst_opus_header_is_header (GstBuffer * buf, const char *magic, guint magic_size) { diff --git a/ext/opus/gstopusheader.h b/ext/opus/gstopusheader.h index 567d88c1f..2a91c03f7 100644 --- a/ext/opus/gstopusheader.h +++ b/ext/opus/gstopusheader.h @@ -26,12 +26,6 @@ G_BEGIN_DECLS -extern void gst_opus_header_create_caps_from_headers (GstCaps **caps, GSList **headers, - GstBuffer *id_header, GstBuffer *comment_header); -extern void gst_opus_header_create_caps (GstCaps **caps, GSList **headers, - guint16 pre_skip, guint sample_rate32, guint8 nchannels, guint8 n_stereo_streams, - guint8 channel_mapping_family, const guint8 *channel_mapping, - const GstTagList *tags); extern gboolean gst_opus_header_is_header (GstBuffer * buf, const char *magic, guint magic_size); extern gboolean gst_opus_header_is_id_header (GstBuffer * buf); -- cgit v1.2.1