From 51b02f61fce8f0d61d99561a642cd1bbd76aede2 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 28 Nov 2011 19:38:34 +0000 Subject: [PATCH 1/8] opusdec: guard against decoding 0 samples https://bugzilla.gnome.org/show_bug.cgi?id=665078 --- ext/opus/gstopusdec.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 585b38335e..420caf1b5a 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -414,11 +414,11 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) GST_INFO_OBJECT (dec, "Skipping %u samples (%u at 48000 Hz, %u left to skip)", skip, scaled_skip, dec->pre_skip); + } - if (GST_BUFFER_SIZE (outbuf) == 0) { - gst_buffer_unref (outbuf); - outbuf = NULL; - } + if (GST_BUFFER_SIZE (outbuf) == 0) { + gst_buffer_unref (outbuf); + outbuf = NULL; } /* Apply gain */ From f6ebc2de8ec979115419247c86bf323ba4f0f284 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 28 Nov 2011 19:47:34 +0000 Subject: [PATCH 2/8] opusdec: default to stereo 48000 Hz if possible when no headers seen https://bugzilla.gnome.org/show_bug.cgi?id=665078 --- ext/opus/gstopusdec.c | 47 ++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 420caf1b5a..af0f0f3771 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -213,12 +213,27 @@ gst_opus_dec_get_r128_volume (gint16 r128_gain) return DB_TO_LINEAR (gst_opus_dec_get_r128_gain (r128_gain)); } +static GstCaps * +gst_opus_dec_negotiate (GstOpusDec * dec) +{ + GstCaps *caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (dec)); + GstStructure *s = gst_caps_get_structure (caps, 0); + gst_structure_fixate_field_nearest_int (s, "rate", 48000); + gst_structure_get_int (s, "rate", &dec->sample_rate); + gst_structure_fixate_field_nearest_int (s, "channels", dec->n_channels); + gst_structure_get_int (s, "channels", &dec->n_channels); + + GST_INFO_OBJECT (dec, "Negotiated %d channels, %d Hz", dec->n_channels, + dec->sample_rate); + + return caps; +} + static GstFlowReturn gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) { const guint8 *data = GST_BUFFER_DATA (buf); GstCaps *caps; - GstStructure *s; const GstAudioChannelPosition *pos = NULL; g_return_val_if_fail (gst_opus_header_is_id_header (buf), GST_FLOW_ERROR); @@ -280,16 +295,7 @@ gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) } } - /* negotiate width with downstream */ - caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (dec)); - s = gst_caps_get_structure (caps, 0); - gst_structure_fixate_field_nearest_int (s, "rate", 48000); - gst_structure_get_int (s, "rate", &dec->sample_rate); - gst_structure_fixate_field_nearest_int (s, "channels", dec->n_channels); - gst_structure_get_int (s, "channels", &dec->n_channels); - - GST_INFO_OBJECT (dec, "Negotiated %d channels, %d Hz", dec->n_channels, - dec->sample_rate); + caps = gst_opus_dec_negotiate (dec); if (pos) { GST_DEBUG_OBJECT (dec, "Setting channel positions on caps"); @@ -307,7 +313,6 @@ gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) return GST_FLOW_OK; } - static GstFlowReturn gst_opus_dec_parse_comments (GstOpusDec * dec, GstBuffer * buf) { @@ -328,6 +333,24 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) GstBuffer *buf; if (dec->state == NULL) { + /* If we did not get any headers, default to 2 channels */ + if (dec->n_channels == 0) { + GstCaps *caps; + GST_INFO_OBJECT (dec, "No header, assuming single stream"); + dec->n_channels = 2; + dec->sample_rate = 48000; + caps = gst_opus_dec_negotiate (dec); + GST_INFO_OBJECT (dec, "Setting src caps to %" GST_PTR_FORMAT, caps); + gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps); + gst_caps_unref (caps); + /* 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; + } + GST_DEBUG_OBJECT (dec, "Creating decoder with %d channels, %d Hz", dec->n_channels, dec->sample_rate); dec->state = opus_multistream_decoder_create (dec->sample_rate, From b7d53c866b0dc379db62a99c19cd5cca1ca51719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 7 Dec 2011 00:06:11 -0500 Subject: [PATCH 3/8] opusdec: Truncate caps first https://bugzilla.gnome.org/show_bug.cgi?id=665078 --- ext/opus/gstopusdec.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index af0f0f3771..6c57648b8a 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -217,7 +217,12 @@ static GstCaps * gst_opus_dec_negotiate (GstOpusDec * dec) { GstCaps *caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (dec)); - GstStructure *s = gst_caps_get_structure (caps, 0); + GstStructure *s; + + caps = gst_caps_make_writable (caps); + gst_caps_truncate (caps); + + s = gst_caps_get_structure (caps, 0); gst_structure_fixate_field_nearest_int (s, "rate", 48000); gst_structure_get_int (s, "rate", &dec->sample_rate); gst_structure_fixate_field_nearest_int (s, "channels", dec->n_channels); From 16bb45d64ac020758724ea87ddfb80548b3a55af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 7 Dec 2011 00:06:11 -0500 Subject: [PATCH 4/8] opusdec: header cleanup https://bugzilla.gnome.org/show_bug.cgi?id=665078 --- ext/opus/gstopusdec.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 6c57648b8a..625c477975 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -38,12 +38,11 @@ */ #ifdef HAVE_CONFIG_H -# include "config.h" +#include "config.h" #endif #include #include -#include #include "gstopusheader.h" #include "gstopuscommon.h" #include "gstopusdec.h" From f8079057864100db0eb88f88482edc8b7f68a2bd Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 8 Dec 2011 18:45:27 +0000 Subject: [PATCH 5/8] opus: properly create channel mapping tables There are two of them, unintuitively enough; the one passed to the encoder should not be the one that gets written to the file. The former maps the input to an ordering which puts paired channels first, while the latter moves the channels to Vorbis order. So add code to calculate both, and we now have properly paired channels where appropriate. https://bugzilla.gnome.org/show_bug.cgi?id=665078 --- ext/opus/gstopuscommon.c | 18 ++++ ext/opus/gstopuscommon.h | 3 + ext/opus/gstopusdec.c | 13 ++- ext/opus/gstopusenc.c | 207 +++++++++++++++++++++++++++++++-------- ext/opus/gstopusenc.h | 4 +- ext/opus/gstopusheader.c | 17 ++-- ext/opus/gstopusheader.h | 2 +- 7 files changed, 213 insertions(+), 51 deletions(-) diff --git a/ext/opus/gstopuscommon.c b/ext/opus/gstopuscommon.c index 426c5b8973..dbf585a82f 100644 --- a/ext/opus/gstopuscommon.c +++ b/ext/opus/gstopuscommon.c @@ -17,6 +17,8 @@ * Boston, MA 02111-1307, USA. */ +#include +#include #include "gstopuscommon.h" /* http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9 */ @@ -86,3 +88,19 @@ const char *gst_opus_channel_names[] = { "side right", "none" }; + +void +gst_opus_common_log_channel_mapping_table (GstElement * element, + GstDebugCategory * category, const char *msg, int n_channels, + const guint8 * table) +{ + char s[8 + 256 * 4] = "[ "; /* enough for 256 times "255 " at most */ + int n; + + for (n = 0; n < n_channels; ++n) { + size_t len = strlen (s); + snprintf (s + len, sizeof (s) - len, "%d ", table[n]); + } + strcat (s, "]"); + GST_CAT_LEVEL_LOG (category, GST_LEVEL_INFO, element, "%s: %s", msg, s); +} diff --git a/ext/opus/gstopuscommon.h b/ext/opus/gstopuscommon.h index 65b944e9e2..1fba5650d7 100644 --- a/ext/opus/gstopuscommon.h +++ b/ext/opus/gstopuscommon.h @@ -28,6 +28,9 @@ G_BEGIN_DECLS extern const GstAudioChannelPosition gst_opus_channel_positions[][8]; extern const char *gst_opus_channel_names[]; +extern void gst_opus_common_log_channel_mapping_table (GstElement *element, + GstDebugCategory * category, const char *msg, + int n_channels, const guint8 *table); G_END_DECLS diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 625c477975..7776f58122 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -357,9 +357,16 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) GST_DEBUG_OBJECT (dec, "Creating decoder with %d channels, %d Hz", dec->n_channels, dec->sample_rate); - dec->state = opus_multistream_decoder_create (dec->sample_rate, - dec->n_channels, dec->n_streams, dec->n_stereo_streams, - dec->channel_mapping, &err); +#ifndef GST_DISABLE_DEBUG + gst_opus_common_log_channel_mapping_table (GST_ELEMENT (dec), opusdec_debug, + "Mapping table", dec->n_channels, dec->channel_mapping); +#endif + + GST_DEBUG_OBJECT (dec, "%d streams, %d stereo", dec->n_streams, + dec->n_stereo_streams); + dec->state = + opus_multistream_decoder_create (dec->sample_rate, dec->n_channels, + dec->n_streams, dec->n_stereo_streams, dec->channel_mapping, &err); if (!dec->state || err != OPUS_OK) goto creation_failed; } diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index f747a37362..e7d6842a12 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -417,7 +417,50 @@ gst_opus_enc_get_frame_samples (GstOpusEnc * enc) } static void -gst_opus_enc_setup_channel_mapping (GstOpusEnc * enc, const GstAudioInfo * info) +gst_opus_enc_setup_trivial_mapping (GstOpusEnc * enc, guint8 mapping[256]) +{ + int n; + + for (n = 0; n < 255; ++n) + mapping[n] = n; +} + +static int +gst_opus_enc_find_channel_position (GstOpusEnc * enc, const GstAudioInfo * info, + GstAudioChannelPosition position) +{ + int n; + for (n = 0; n < enc->n_channels; ++n) { + if (GST_AUDIO_INFO_POSITION (info, n) == position) { + return n; + } + } + return -1; +} + +static int +gst_opus_enc_find_channel_position_in_vorbis_order (GstOpusEnc * enc, + GstAudioChannelPosition position) +{ + int c; + + for (c = 0; c < enc->n_channels; ++c) { + if (gst_opus_channel_positions[enc->n_channels - 1][c] == position) { + GST_INFO_OBJECT (enc, + "Channel position %s maps to index %d in Vorbis order", + gst_opus_channel_names[position], c); + return c; + } + } + GST_WARNING_OBJECT (enc, + "Channel position %s is not representable in Vorbis order", + gst_opus_channel_names[position]); + return -1; +} + +static void +gst_opus_enc_setup_channel_mappings (GstOpusEnc * enc, + const GstAudioInfo * info) { #define MAPS(idx,pos) (GST_AUDIO_INFO_POSITION (info, (idx)) == GST_AUDIO_CHANNEL_POSITION_##pos) @@ -427,14 +470,15 @@ gst_opus_enc_setup_channel_mapping (GstOpusEnc * enc, const GstAudioInfo * info) enc->n_channels); /* Start by setting up a default trivial mapping */ - for (n = 0; n < 255; ++n) - enc->channel_mapping[n] = n; + enc->n_stereo_streams = 0; + gst_opus_enc_setup_trivial_mapping (enc, enc->encoding_channel_mapping); + gst_opus_enc_setup_trivial_mapping (enc, enc->decoding_channel_mapping); /* For one channel, use the basic RTP mapping */ if (enc->n_channels == 1) { GST_INFO_OBJECT (enc, "Mono, trivial RTP mapping"); enc->channel_mapping_family = 0; - enc->channel_mapping[0] = 0; + /* implicit mapping for family 0 */ return; } @@ -444,9 +488,11 @@ gst_opus_enc_setup_channel_mapping (GstOpusEnc * enc, const GstAudioInfo * info) 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"); } @@ -454,42 +500,115 @@ gst_opus_enc_setup_channel_mapping (GstOpusEnc * enc, const GstAudioInfo * info) /* 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. */ + 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) { - GST_DEBUG_OBJECT (enc, - "In range for the Vorbis mapping, checking channel positions"); - for (n = 0; n < enc->n_channels; ++n) { - GstAudioChannelPosition pos = GST_AUDIO_INFO_POSITION (info, n); - int c; + int c0, c1, c0v, c1v; + int mapped; + gboolean positions_done[256]; + static const GstAudioChannelPosition pairs[][2] = { + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, + {GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, + {GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, + }; + size_t pair; - GST_DEBUG_OBJECT (enc, "Channel %d has position %d (%s)", n, pos, - gst_opus_channel_names[pos]); - for (c = 0; c < enc->n_channels; ++c) { - if (gst_opus_channel_positions[enc->n_channels - 1][c] == pos) { - GST_DEBUG_OBJECT (enc, "Found in Vorbis mapping as channel %d", c); - break; + GST_DEBUG_OBJECT (enc, + "In range for the Vorbis mapping, building channel mapping tables"); + + enc->n_stereo_streams = 0; + mapped = 0; + for (n = 0; n < 256; ++n) + positions_done[n] = FALSE; + + /* First, find any natural pairs, and move them to the front */ + for (pair = 0; pair < G_N_ELEMENTS (pairs); ++pair) { + GstAudioChannelPosition p0 = pairs[pair][0]; + GstAudioChannelPosition p1 = pairs[pair][1]; + c0 = gst_opus_enc_find_channel_position (enc, info, p0); + c1 = gst_opus_enc_find_channel_position (enc, info, p1); + if (c0 >= 0 && c1 >= 0) { + /* We found a natural pair */ + GST_DEBUG_OBJECT (enc, "Natural pair '%s/%s' found at %d %d", + gst_opus_channel_names[p0], gst_opus_channel_names[p1], c0, c1); + /* Find where they map in Vorbis order */ + c0v = gst_opus_enc_find_channel_position_in_vorbis_order (enc, p0); + c1v = gst_opus_enc_find_channel_position_in_vorbis_order (enc, p1); + if (c0v < 0 || c1v < 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; } + + enc->encoding_channel_mapping[mapped] = c0; + enc->encoding_channel_mapping[mapped + 1] = c1; + enc->decoding_channel_mapping[c0v] = mapped; + enc->decoding_channel_mapping[c1v] = mapped + 1; + enc->n_stereo_streams++; + mapped += 2; + positions_done[p0] = positions_done[p1] = TRUE; } - if (c == enc->n_channels) { - /* We did not find that position, so use undefined */ - GST_WARNING_OBJECT (enc, - "Position %d (%s) not found in Vorbis mapping, using unknown mapping", - pos, gst_opus_channel_positions[pos]); - enc->channel_mapping_family = 255; - return; - } - GST_DEBUG_OBJECT (enc, "Mapping output channel %d to %d (%s)", c, n, - gst_opus_channel_names[pos]); - enc->channel_mapping[c] = n; } - GST_INFO_OBJECT (enc, "Permutation found, using Vorbis mapping"); + + /* Now add all other input channels as mono streams */ + for (n = 0; n < enc->n_channels; ++n) { + GstAudioChannelPosition position = GST_AUDIO_INFO_POSITION (info, n); + + /* if we already mapped it while searching for pairs, nothing else + needs to be done */ + if (!positions_done[position]) { + int cv; + 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; + } + enc->encoding_channel_mapping[mapped] = n; + enc->decoding_channel_mapping[cv] = mapped; + mapped++; + } + } + +#ifndef GST_DISABLE_DEBUG + GST_INFO_OBJECT (enc, + "Mapping tables built: %d channels, %d stereo streams", enc->n_channels, + enc->n_stereo_streams); + gst_opus_common_log_channel_mapping_table (GST_ELEMENT (enc), opusenc_debug, + "Encoding mapping table", enc->n_channels, + enc->encoding_channel_mapping); + gst_opus_common_log_channel_mapping_table (GST_ELEMENT (enc), opusenc_debug, + "Decoding mapping table", enc->n_channels, + enc->decoding_channel_mapping); +#endif + enc->channel_mapping_family = 1; return; } - /* For other cases, we use undefined, with the default trivial mapping */ + /* More than 8 channels, if future mappings are added for those */ + + /* For other cases, we use undefined, with the default trivial mapping + and all mono streams */ GST_WARNING_OBJECT (enc, "Unknown mapping"); enc->channel_mapping_family = 255; + enc->n_stereo_streams = 0; #undef MAPS } @@ -505,7 +624,7 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info) enc->n_channels = GST_AUDIO_INFO_CHANNELS (info); enc->sample_rate = GST_AUDIO_INFO_RATE (info); - gst_opus_enc_setup_channel_mapping (enc, info); + gst_opus_enc_setup_channel_mappings (enc, info); GST_DEBUG_OBJECT (benc, "Setup with %d channels, %d Hz", enc->n_channels, enc->sample_rate); @@ -530,17 +649,24 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info) static gboolean gst_opus_enc_setup (GstOpusEnc * enc) { - int error = OPUS_OK, n; - guint8 trivial_mapping[256]; + int error = OPUS_OK; - GST_DEBUG_OBJECT (enc, "setup"); +#ifndef GST_DISABLE_DEBUG + GST_DEBUG_OBJECT (enc, + "setup: %d Hz, %d channels, %d stereo streams, family %d", + enc->sample_rate, enc->n_channels, enc->n_stereo_streams, + enc->channel_mapping_family); + GST_INFO_OBJECT (enc, "Mapping tables built: %d channels, %d stereo streams", + enc->n_channels, enc->n_stereo_streams); + gst_opus_common_log_channel_mapping_table (GST_ELEMENT (enc), opusenc_debug, + "Encoding mapping table", enc->n_channels, enc->encoding_channel_mapping); + gst_opus_common_log_channel_mapping_table (GST_ELEMENT (enc), opusenc_debug, + "Decoding mapping table", enc->n_channels, enc->decoding_channel_mapping); +#endif - for (n = 0; n < 256; ++n) - trivial_mapping[n] = n; - - enc->state = - opus_multistream_encoder_create (enc->sample_rate, enc->n_channels, - enc->n_channels, 0, trivial_mapping, + enc->state = opus_multistream_encoder_create (enc->sample_rate, + enc->n_channels, enc->n_channels - enc->n_stereo_streams, + enc->n_stereo_streams, enc->encoding_channel_mapping, enc->audio_or_voip ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP, &error); if (!enc->state || error != OPUS_OK) @@ -698,7 +824,8 @@ gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf) enc->headers = NULL; gst_opus_header_create_caps (&caps, &enc->headers, enc->n_channels, - enc->sample_rate, enc->channel_mapping_family, enc->channel_mapping, + enc->n_stereo_streams, enc->sample_rate, enc->channel_mapping_family, + enc->decoding_channel_mapping, gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc))); diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index 8c2c3c6e82..1e39ad03d4 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -79,7 +79,9 @@ struct _GstOpusEnc { GstTagList *tags; guint8 channel_mapping_family; - guint8 channel_mapping[256]; + guint8 encoding_channel_mapping[256]; + guint8 decoding_channel_mapping[256]; + guint8 n_stereo_streams; }; struct _GstOpusEncClass { diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 42df7b35fb..7f49e47113 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -27,12 +27,17 @@ #include "gstopusheader.h" static GstBuffer * -gst_opus_enc_create_id_buffer (gint nchannels, gint sample_rate, - guint8 channel_mapping_family, const guint8 * channel_mapping) +gst_opus_enc_create_id_buffer (gint nchannels, gint n_stereo_streams, + gint sample_rate, guint8 channel_mapping_family, + const guint8 * channel_mapping) { GstBuffer *buffer; GstByteWriter bw; + g_return_val_if_fail (nchannels > 0 && nchannels < 256, NULL); + g_return_val_if_fail (n_stereo_streams >= 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 */ @@ -44,8 +49,8 @@ gst_opus_enc_create_id_buffer (gint nchannels, gint sample_rate, gst_byte_writer_put_uint16_le (&bw, 0); /* output gain */ gst_byte_writer_put_uint8 (&bw, channel_mapping_family); if (channel_mapping_family > 0) { - gst_byte_writer_put_uint8 (&bw, nchannels); - gst_byte_writer_put_uint8 (&bw, 0); + gst_byte_writer_put_uint8 (&bw, nchannels - n_stereo_streams); + gst_byte_writer_put_uint8 (&bw, n_stereo_streams); gst_byte_writer_put_data (&bw, channel_mapping, nchannels); } @@ -158,7 +163,7 @@ gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, void gst_opus_header_create_caps (GstCaps ** caps, GSList ** headers, gint nchannels, - gint sample_rate, guint8 channel_mapping_family, + gint n_stereo_streams, gint sample_rate, guint8 channel_mapping_family, const guint8 * channel_mapping, const GstTagList * tags) { GstBuffer *buf1, *buf2; @@ -175,7 +180,7 @@ gst_opus_header_create_caps (GstCaps ** caps, GSList ** headers, gint nchannels, /* create header buffers */ buf1 = - gst_opus_enc_create_id_buffer (nchannels, sample_rate, + gst_opus_enc_create_id_buffer (nchannels, n_stereo_streams, sample_rate, channel_mapping_family, channel_mapping); buf2 = gst_opus_enc_create_metadata_buffer (tags); diff --git a/ext/opus/gstopusheader.h b/ext/opus/gstopusheader.h index 3b2cfc265f..c6278eff30 100644 --- a/ext/opus/gstopusheader.h +++ b/ext/opus/gstopusheader.h @@ -28,7 +28,7 @@ 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, - gint nchannels, gint sample_rate, + gint nchannels, gint n_stereo_streams, gint sample_rate, guint8 channel_mapping_family, const guint8 *channel_mapping, const GstTagList *tags); extern gboolean gst_opus_header_is_header (GstBuffer * buf, From dbca14b23b3a933f3bacec4d6e9d7bb3f151144d Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 8 Dec 2011 19:47:55 +0000 Subject: [PATCH 6/8] opus: include streams count in caps https://bugzilla.gnome.org/show_bug.cgi?id=665078 --- ext/opus/gstopusenc.c | 2 +- ext/opus/gstopusheader.c | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index e7d6842a12..502f4a9d94 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -126,7 +126,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-opus") + GST_STATIC_CAPS ("audio/x-opus, streams = (int) [1, 255 ]") ); #define DEFAULT_AUDIO TRUE diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 7f49e47113..185c85ef18 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -150,11 +150,26 @@ void gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, GstBuffer * buf1, GstBuffer * buf2) { + int n_streams, family; + g_return_if_fail (caps); g_return_if_fail (headers && !*headers); + g_return_if_fail (GST_BUFFER_SIZE (buf1) >= 19); + + /* work out the number of streams */ + family = GST_BUFFER_DATA (buf1)[18]; + if (family == 0) { + n_streams = 1; + } else { + /* only included in the header for family > 0 */ + g_return_if_fail (GST_BUFFER_SIZE (buf1) >= 20); + n_streams = GST_BUFFER_DATA (buf1)[19]; + } /* mark and put on caps */ - *caps = gst_caps_from_string ("audio/x-opus"); + *caps = + gst_caps_new_simple ("audio/x-opus", "streams", G_TYPE_INT, n_streams, + NULL); *caps = _gst_caps_set_buffer_array (*caps, "streamheader", buf1, buf2, NULL); *headers = g_slist_prepend (*headers, buf2); From 3a978f5a69728119b633d246f91cb1eaaf16b4f4 Mon Sep 17 00:00:00 2001 From: Danilo Cesar Lemes de Paula Date: Wed, 7 Dec 2011 15:13:11 -0200 Subject: [PATCH 7/8] Adding opus RTP payloader/depayloader element Adding OPUS RTP module based on the current draft: http://tools.ietf.org/id/draft-spittka-payload-rtp-opus-00.txt https://bugzilla.gnome.org/show_bug.cgi?id=664817 --- ext/opus/Makefile.am | 5 +++-- ext/opus/gstopus.c | 11 +++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ext/opus/Makefile.am b/ext/opus/Makefile.am index cb0a9b338a..cdf3c30ac1 100644 --- a/ext/opus/Makefile.am +++ b/ext/opus/Makefile.am @@ -1,6 +1,6 @@ plugin_LTLIBRARIES = libgstopus.la -libgstopus_la_SOURCES = gstopus.c gstopusdec.c gstopusenc.c gstopusparse.c gstopusheader.c gstopuscommon.c +libgstopus_la_SOURCES = gstopus.c gstopusdec.c gstopusenc.c gstopusparse.c gstopusheader.c gstopuscommon.c gstrtpopuspay.c gstrtpopusdepay.c libgstopus_la_CFLAGS = \ -DGST_USE_UNSTABLE_API \ $(GST_PLUGINS_BASE_CFLAGS) \ @@ -9,10 +9,11 @@ libgstopus_la_CFLAGS = \ libgstopus_la_LIBADD = \ -lgstaudio-$(GST_MAJORMINOR) \ $(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_MAJORMINOR) \ + -lgstrtp-@GST_MAJORMINOR@ \ $(GST_BASE_LIBS) \ $(GST_LIBS) \ $(OPUS_LIBS) libgstopus_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(LIBM) libgstopus_la_LIBTOOLFLAGS = --tag=disable-static -noinst_HEADERS = gstopusenc.h gstopusdec.h gstopusparse.h gstopusheader.h gstopuscommon.h +noinst_HEADERS = gstopusenc.h gstopusdec.h gstopusparse.h gstopusheader.h gstopuscommon.h gstrtpopuspay.h gstrtpopusdepay.h diff --git a/ext/opus/gstopus.c b/ext/opus/gstopus.c index c5f68a131c..8db6e197f9 100644 --- a/ext/opus/gstopus.c +++ b/ext/opus/gstopus.c @@ -25,6 +25,9 @@ #include "gstopusenc.h" #include "gstopusparse.h" +#include "gstrtpopuspay.h" +#include "gstrtpopusdepay.h" + #include static gboolean @@ -43,6 +46,14 @@ plugin_init (GstPlugin * plugin) GST_TYPE_OPUS_PARSE)) return FALSE; + if (!gst_element_register (plugin, "rtpopusdepay", GST_RANK_NONE, + GST_TYPE_RTP_OPUS_DEPAY)) + return FALSE; + + if (!gst_element_register (plugin, "rtpopuspay", GST_RANK_NONE, + GST_TYPE_RTP_OPUS_PAY)) + return FALSE; + gst_tag_register_musicbrainz_tags (); return TRUE; From 3f47a43f2a0f65db98838eff67ff68affd068a02 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Fri, 9 Dec 2011 17:25:41 +0000 Subject: [PATCH 8/8] opusenc: add upstream negotiation for multistream ability This will help elements that cannot deal with multistream, such as the RTP payloader. The caps now do not include a "streams" field anymore, but a "multistream" boolean, since we have no real use for knowing the exact amount of streams. https://bugzilla.gnome.org/show_bug.cgi?id=665078 --- ext/opus/gstopusenc.c | 66 +++++++++++++++++++++++++++++++++++++++- ext/opus/gstopusheader.c | 7 +++-- 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 502f4a9d94..0ccbbebc5f 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -126,7 +126,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-opus, streams = (int) [1, 255 ]") + GST_STATIC_CAPS ("audio/x-opus") ); #define DEFAULT_AUDIO TRUE @@ -161,6 +161,7 @@ static void gst_opus_enc_finalize (GObject * object); static gboolean gst_opus_enc_sink_event (GstAudioEncoder * benc, GstEvent * event); +static GstCaps *gst_opus_enc_sink_getcaps (GstAudioEncoder * benc); static gboolean gst_opus_enc_setup (GstOpusEnc * enc); static void gst_opus_enc_get_property (GObject * object, guint prop_id, @@ -229,6 +230,7 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) base_class->set_format = GST_DEBUG_FUNCPTR (gst_opus_enc_set_format); base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_opus_enc_handle_frame); base_class->event = GST_DEBUG_FUNCPTR (gst_opus_enc_sink_event); + base_class->getcaps = GST_DEBUG_FUNCPTR (gst_opus_enc_sink_getcaps); g_object_class_install_property (gobject_class, PROP_AUDIO, g_param_spec_boolean ("audio", "Audio or voice", @@ -721,6 +723,68 @@ gst_opus_enc_sink_event (GstAudioEncoder * benc, GstEvent * event) return FALSE; } +static GstCaps * +gst_opus_enc_sink_getcaps (GstAudioEncoder * benc) +{ + GstOpusEnc *enc; + GstCaps *caps; + 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_get_caps (GST_AUDIO_ENCODER_SRC_PAD (benc)); + if (!peercaps) { + GST_DEBUG_OBJECT (benc, "No peercaps, returning template sink caps"); + return + gst_caps_copy (gst_pad_get_pad_template_caps + (GST_AUDIO_ENCODER_SINK_PAD (benc))); + } + + intersect = gst_caps_intersect (peercaps, + gst_pad_get_pad_template_caps (GST_AUDIO_ENCODER_SRC_PAD (benc))); + 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_caps_copy (gst_pad_get_pad_template_caps (GST_AUDIO_ENCODER_SINK_PAD + (benc))); + 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); + } + + GST_DEBUG_OBJECT (enc, "Returning caps: %" GST_PTR_FORMAT, caps); + return caps; +} + static GstFlowReturn gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) { diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 185c85ef18..d3d631fe74 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -151,6 +151,7 @@ gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, GstBuffer * buf1, GstBuffer * buf2) { int n_streams, family; + gboolean multistream; g_return_if_fail (caps); g_return_if_fail (headers && !*headers); @@ -167,9 +168,9 @@ gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, } /* mark and put on caps */ - *caps = - gst_caps_new_simple ("audio/x-opus", "streams", G_TYPE_INT, n_streams, - NULL); + multistream = n_streams > 1; + *caps = gst_caps_new_simple ("audio/x-opus", + "multistream", G_TYPE_BOOLEAN, multistream, NULL); *caps = _gst_caps_set_buffer_array (*caps, "streamheader", buf1, buf2, NULL); *headers = g_slist_prepend (*headers, buf2);