From 0ad3c1af0a66ec6fb54b0cef0ed3c42fa407157f Mon Sep 17 00:00:00 2001 From: "tina.legrand@webrtc.org" Date: Wed, 7 Nov 2012 08:07:29 +0000 Subject: [PATCH] Adding Opus stereo support to WebRTC This CL adds support for sending and receiving stereo using the Opus codec. BUG=issue1013 Review URL: https://webrtc-codereview.appspot.com/930008 git-svn-id: http://webrtc.googlecode.com/svn/trunk@3050 4adac7df-926f-26a2-2b94-8c16560cd09d --- .../codecs/opus/interface/opus_interface.h | 5 +- .../audio_coding/codecs/opus/opus_interface.c | 115 +++++++++++++++--- .../main/source/acm_codec_database.cc | 25 +++- .../main/source/acm_codec_database.h | 6 + .../audio_coding/main/source/acm_opus.cc | 70 +++++++++-- .../audio_coding/main/source/acm_opus.h | 5 + .../main/source/audio_coding_module.gypi | 3 + .../audio_coding/main/test/TestStereo.cc | 63 +++++++++- .../audio_coding/main/test/TestStereo.h | 2 + webrtc/modules/audio_coding/neteq/codec_db.c | 2 + .../neteq/interface/webrtc_neteq.h | 1 + .../interface/webrtc_neteq_help_macros.h | 11 ++ .../audio_coding/neteq/packet_buffer.c | 3 +- webrtc/modules/audio_coding/neteq/recin.c | 1 + 14 files changed, 276 insertions(+), 36 deletions(-) diff --git a/webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h b/webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h index dcfd87f3f6..697121e506 100644 --- a/webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h +++ b/webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h @@ -74,6 +74,7 @@ int16_t WebRtcOpus_DecoderFree(OpusDecInst* inst); * -1 - Error */ int16_t WebRtcOpus_DecoderInit(OpusDecInst* inst); +int16_t WebRtcOpus_DecoderInitSlave(OpusDecInst* inst); /**************************************************************************** * WebRtcOpus_Decode(...) @@ -98,7 +99,9 @@ int16_t WebRtcOpus_DecoderInit(OpusDecInst* inst); int16_t WebRtcOpus_Decode(OpusDecInst* inst, int16_t* encoded, int16_t encoded_bytes, int16_t* decoded, int16_t* audio_type); - +int16_t WebRtcOpus_DecodeSlave(OpusDecInst* inst, int16_t* encoded, + int16_t encoded_bytes, int16_t* decoded, + int16_t* audio_type); /**************************************************************************** * WebRtcOpus_DecodePlc(...) * diff --git a/webrtc/modules/audio_coding/codecs/opus/opus_interface.c b/webrtc/modules/audio_coding/codecs/opus/opus_interface.c index f61ecc5a97..867262d0b6 100644 --- a/webrtc/modules/audio_coding/codecs/opus/opus_interface.c +++ b/webrtc/modules/audio_coding/codecs/opus/opus_interface.c @@ -29,8 +29,8 @@ enum { */ kWebRtcOpusMaxDecodeFrameSizeMs = 120, - /* Sample count is 48 kHz * samples per frame. */ - kWebRtcOpusMaxFrameSize = 48 * kWebRtcOpusMaxDecodeFrameSizeMs, + /* Sample count is 48 kHz * samples per frame * stereo. */ + kWebRtcOpusMaxFrameSize = 48 * kWebRtcOpusMaxDecodeFrameSizeMs * 2, }; struct WebRtcOpusEncInst { @@ -82,18 +82,25 @@ int16_t WebRtcOpus_SetBitRate(OpusEncInst* inst, int32_t rate) { } struct WebRtcOpusDecInst { - int16_t state_48_32[8]; - OpusDecoder* decoder; + int16_t state_48_32_left[8]; + int16_t state_48_32_right[8]; + OpusDecoder* decoder_left; + OpusDecoder* decoder_right; + int channels; }; int16_t WebRtcOpus_DecoderCreate(OpusDecInst** inst, int channels) { OpusDecInst* state; state = (OpusDecInst*) calloc(1, sizeof(OpusDecInst)); if (state) { - int error; + int error_l; + int error_r; // Always create a 48000 Hz Opus decoder. - state->decoder = opus_decoder_create(48000, channels, &error); - if (error == OPUS_OK && state->decoder != NULL ) { + state->decoder_left = opus_decoder_create(48000, channels, &error_l); + state->decoder_right = opus_decoder_create(48000, channels, &error_r); + if (error_l == OPUS_OK && error_r == OPUS_OK && + state->decoder_left != NULL && state->decoder_right != NULL) { + state->channels = channels; *inst = state; return 0; } @@ -104,27 +111,37 @@ int16_t WebRtcOpus_DecoderCreate(OpusDecInst** inst, int channels) { } int16_t WebRtcOpus_DecoderFree(OpusDecInst* inst) { - opus_decoder_destroy(inst->decoder); + opus_decoder_destroy(inst->decoder_left); + opus_decoder_destroy(inst->decoder_right); free(inst); return 0; } int16_t WebRtcOpus_DecoderInit(OpusDecInst* inst) { - int error = opus_decoder_ctl(inst->decoder, OPUS_RESET_STATE); + int error = opus_decoder_ctl(inst->decoder_left, OPUS_RESET_STATE); if (error == OPUS_OK) { - memset(inst->state_48_32, 0, sizeof(inst->state_48_32)); + memset(inst->state_48_32_left, 0, sizeof(inst->state_48_32_left)); return 0; } return -1; } -static int DecodeNative(OpusDecInst* inst, int16_t* encoded, +int16_t WebRtcOpus_DecoderInitSlave(OpusDecInst* inst) { + int error = opus_decoder_ctl(inst->decoder_right, OPUS_RESET_STATE); + if (error == OPUS_OK) { + memset(inst->state_48_32_right, 0, sizeof(inst->state_48_32_right)); + return 0; + } + return -1; +} + +static int DecodeNative(OpusDecoder* inst, int16_t* encoded, int16_t encoded_bytes, int16_t* decoded, int16_t* audio_type) { unsigned char* coded = (unsigned char*) encoded; opus_int16* audio = (opus_int16*) decoded; - int res = opus_decode(inst->decoder, coded, encoded_bytes, audio, + int res = opus_decode(inst, coded, encoded_bytes, audio, kWebRtcOpusMaxFrameSize, 0); /* TODO(tlegrand): set to DTX for zero-length packets? */ *audio_type = 0; @@ -148,16 +165,82 @@ int16_t WebRtcOpus_Decode(OpusDecInst* inst, int16_t* encoded, int16_t output_samples; int i; + /* If mono case, just do a regular call to the decoder. + * If stereo, call to WebRtcOpus_Decode() gives left channel as output, and + * calls to WebRtcOpus_Decode_slave() give right channel as output. + * This is to make stereo work with the current setup of NetEQ, which + * requires two calls to the decoder to produce stereo. */ + /* Decode to a temporary buffer. */ - decoded_samples = DecodeNative(inst, encoded, encoded_bytes, buffer16, - audio_type); + decoded_samples = DecodeNative(inst->decoder_left, encoded, encoded_bytes, + buffer16, audio_type); if (decoded_samples < 0) { return -1; } + if (inst->channels == 2) { + /* The parameter |decoded_samples| holds the number of samples pairs, in + * case of stereo. Number of samples in |buffer16| equals |decoded_samples| + * times 2. */ + for (i = 0; i < decoded_samples; i++) { + /* Take every second sample, starting at the first sample. This gives + * the left channel. */ + buffer16[i] = buffer16[i * 2]; + } + } + /* Resample from 48 kHz to 32 kHz. */ + for (i = 0; i < 7; i++) { + buffer32[i] = inst->state_48_32_left[i]; + inst->state_48_32_left[i] = buffer16[decoded_samples - 7 + i]; + } + for (i = 0; i < decoded_samples; i++) { + buffer32[7 + i] = buffer16[i]; + } + /* Resampling 3 samples to 2. Function divides the input in |blocks| number + * of 3-sample groups, and output is |blocks| number of 2-sample groups. */ + blocks = decoded_samples / 3; + WebRtcSpl_Resample48khzTo32khz(buffer32, buffer32, blocks); + output_samples = (int16_t) (blocks * 2); + WebRtcSpl_VectorBitShiftW32ToW16(decoded, output_samples, buffer32, 15); + + return output_samples; +} + +int16_t WebRtcOpus_DecodeSlave(OpusDecInst* inst, int16_t* encoded, + int16_t encoded_bytes, int16_t* decoded, + int16_t* audio_type) { + /* Enough for 120 ms (the largest Opus packet size) of mono audio at 48 kHz + * and resampler overlap. This will need to be enlarged for stereo decoding. + */ + int16_t buffer16[kWebRtcOpusMaxFrameSize]; + int32_t buffer32[kWebRtcOpusMaxFrameSize + 7]; + int decoded_samples; + int blocks; + int16_t output_samples; + int i; + + /* Decode to a temporary buffer. */ + decoded_samples = DecodeNative(inst->decoder_right, encoded, encoded_bytes, + buffer16, audio_type); + if (decoded_samples < 0) { + return -1; + } + if (inst->channels == 2) { + /* The parameter |decoded_samples| holds the number of samples pairs, in + * case of stereo. Number of samples in |buffer16| equals |decoded_samples| + * times 2. */ + for (i = 0; i < decoded_samples; i++) { + /* Take every second sample, starting at the second sample. This gives + * the right channel. */ + buffer16[i] = buffer16[i * 2 + 1]; + } + } else { + /* Decode slave should never be called for mono packets. */ + return -1; + } /* Resample from 48 kHz to 32 kHz. */ for (i = 0; i < 7; i++) { - buffer32[i] = inst->state_48_32[i]; - inst->state_48_32[i] = buffer16[decoded_samples -7 + i]; + buffer32[i] = inst->state_48_32_right[i]; + inst->state_48_32_right[i] = buffer16[decoded_samples - 7 + i]; } for (i = 0; i < decoded_samples; i++) { buffer32[7 + i] = buffer16[i]; diff --git a/webrtc/modules/audio_coding/main/source/acm_codec_database.cc b/webrtc/modules/audio_coding/main/source/acm_codec_database.cc index 14e613720b..338dce1395 100644 --- a/webrtc/modules/audio_coding/main/source/acm_codec_database.cc +++ b/webrtc/modules/audio_coding/main/source/acm_codec_database.cc @@ -107,10 +107,10 @@ namespace webrtc { // codecs. Note! There are a limited number of payload types. If more codecs // are defined they will receive reserved fixed payload types (values 69-95). const int kDynamicPayloadtypes[ACMCodecDB::kMaxNumCodecs] = { - 105, 107, 108, 109, 111, 112, 113, 114, 115, 116, 117, 121, - 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, - 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, - 68, 67 + 105, 107, 108, 109, 111, 112, 113, 114, 115, 116, 117, 92, + 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, + 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, + 67, 66 }; // Creates database with all supported codecs at compile time. @@ -189,8 +189,11 @@ const CodecInst ACMCodecDB::database_[] = { {3, "GSM", 8000, 160, 1, 13200}, #endif #ifdef WEBRTC_CODEC_OPUS - // Opus supports 48, 24, 16, 12, 8 kHz. + // Opus internally supports 48, 24, 16, 12, 8 kHz. + // Mono {120, "opus", 48000, 960, 1, 32000}, + // Stereo + {121, "opus", 48000, 960, 2, 32000}, #endif #ifdef WEBRTC_CODEC_SPEEX {kDynamicPayloadtypes[count_database++], "speex", 8000, 160, 1, 11000}, @@ -282,6 +285,9 @@ const ACMCodecDB::CodecSettings ACMCodecDB::codec_settings_[] = { #ifdef WEBRTC_CODEC_OPUS // Opus supports frames shorter than 10ms, // but it doesn't help us to use them. + // Mono + {1, {960}, 0, 2}, + // Stereo {1, {960}, 0, 2}, #endif #ifdef WEBRTC_CODEC_SPEEX @@ -369,7 +375,10 @@ const WebRtcNetEQDecoder ACMCodecDB::neteq_decoders_[] = { kDecoderGSMFR, #endif #ifdef WEBRTC_CODEC_OPUS + // Mono kDecoderOpus, + // Stereo + kDecoderOpus_2ch, #endif #ifdef WEBRTC_CODEC_SPEEX kDecoderSPEEX_8, @@ -758,7 +767,11 @@ ACMGenericCodec* ACMCodecDB::CreateCodecInstance(const CodecInst* codec_inst) { #endif } else if (!STR_CASE_CMP(codec_inst->plname, "opus")) { #ifdef WEBRTC_CODEC_OPUS - return new ACMOpus(kOpus); + if (codec_inst->channels == 1) { + return new ACMOpus(kOpus); + } else { + return new ACMOpus(kOpus_2ch); + } #endif } else if (!STR_CASE_CMP(codec_inst->plname, "speex")) { #ifdef WEBRTC_CODEC_SPEEX diff --git a/webrtc/modules/audio_coding/main/source/acm_codec_database.h b/webrtc/modules/audio_coding/main/source/acm_codec_database.h index 8baf24e94c..60578db0bd 100644 --- a/webrtc/modules/audio_coding/main/source/acm_codec_database.h +++ b/webrtc/modules/audio_coding/main/source/acm_codec_database.h @@ -92,7 +92,10 @@ class ACMCodecDB { , kGSMFR #endif #ifdef WEBRTC_CODEC_OPUS + // Mono , kOpus + // Stereo + , kOpus_2ch #endif #ifdef WEBRTC_CODEC_SPEEX , kSPEEX8 @@ -175,7 +178,10 @@ class ACMCodecDB { enum {kSPEEX16 = -1}; #endif #ifndef WEBRTC_CODEC_OPUS + // Mono enum {kOpus = -1}; + // Stereo + enum {kOpus_2ch = -1}; #endif #ifndef WEBRTC_CODEC_AVT enum {kAVT = -1}; diff --git a/webrtc/modules/audio_coding/main/source/acm_opus.cc b/webrtc/modules/audio_coding/main/source/acm_opus.cc index e7dcfe8c10..ca2892161b 100644 --- a/webrtc/modules/audio_coding/main/source/acm_opus.cc +++ b/webrtc/modules/audio_coding/main/source/acm_opus.cc @@ -29,7 +29,8 @@ ACMOpus::ACMOpus(int16_t /* codecID */) : _encoderInstPtr(NULL), _decoderInstPtr(NULL), _sampleFreq(0), - _bitrate(0) { + _bitrate(0), + _channels(1) { return; } @@ -91,18 +92,26 @@ int16_t ACMOpus::SetBitRateSafe(const int32_t /*rate*/) { return -1; } +bool ACMOpus::IsTrueStereoCodec() { + return true; +} + +void ACMOpus::SplitStereoPacket(uint8_t* /*payload*/, + int32_t* /*payload_length*/) {} + #else //===================== Actual Implementation ======================= ACMOpus::ACMOpus(int16_t codecID) : _encoderInstPtr(NULL), _decoderInstPtr(NULL), _sampleFreq(32000), // Default sampling frequency. - _bitrate(20000) { // Default bit-rate. + _bitrate(20000), // Default bit-rate. + _channels(1) { // Default mono _codecID = codecID; // Opus has internal DTX, but we dont use it for now. _hasInternalDTX = false; - if (_codecID != ACMCodecDB::kOpus) { + if ((_codecID != ACMCodecDB::kOpus) && (_codecID != ACMCodecDB::kOpus_2ch)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Wrong codec id for Opus."); _sampleFreq = -1; @@ -140,7 +149,7 @@ int16_t ACMOpus::InternalEncode(uint8_t* bitStream, int16_t* bitStreamLenByte) { // Increment the read index. This tells the caller how far // we have gone forward in reading the audio buffer. - _inAudioIxRead += _frameLenSmpl; + _inAudioIxRead += _frameLenSmpl * _channels; return *bitStreamLenByte; } @@ -159,6 +168,9 @@ int16_t ACMOpus::InternalInitEncoder(WebRtcACMCodecParams* codecParams) { } ret = WebRtcOpus_EncoderCreate(&_encoderInstPtr, codecParams->codecInstant.channels); + // Store number of channels. + _channels = codecParams->codecInstant.channels; + if (ret < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Encoder creation failed for Opus"); @@ -170,6 +182,10 @@ int16_t ACMOpus::InternalInitEncoder(WebRtcACMCodecParams* codecParams) { "Setting initial bitrate failed for Opus"); return ret; } + + // Store bitrate. + _bitrate = codecParams->codecInstant.rate; + return 0; } @@ -182,7 +198,14 @@ int16_t ACMOpus::InternalInitDecoder(WebRtcACMCodecParams* codecParams) { codecParams->codecInstant.channels) < 0) { return -1; } - return WebRtcOpus_DecoderInit(_decoderInstPtr); + + if (WebRtcOpus_DecoderInit(_decoderInstPtr) < 0) { + return -1; + } + if (WebRtcOpus_DecoderInitSlave(_decoderInstPtr) < 0) { + return -1; + } + return 0; } int32_t ACMOpus::CodecDef(WebRtcNetEQ_CodecDef& codecDef, @@ -198,12 +221,26 @@ int32_t ACMOpus::CodecDef(WebRtcNetEQ_CodecDef& codecDef, // TODO(tlegrand): Decoder is registered in NetEQ as a 32 kHz decoder, which // is true until we have a full 48 kHz system, and remove the downsampling // in the Opus decoder wrapper. - SET_CODEC_PAR((codecDef), kDecoderOpus, codecInst.pltype, _decoderInstPtr, - 32000); - SET_OPUS_FUNCTIONS((codecDef)); + if (codecInst.channels == 1) { + SET_CODEC_PAR(codecDef, kDecoderOpus, codecInst.pltype, _decoderInstPtr, + 32000); + } else { + SET_CODEC_PAR(codecDef, kDecoderOpus_2ch, codecInst.pltype, + _decoderInstPtr, 32000); + } + + // If this is the master of NetEQ, regular decoder will be added, otherwise + // the slave decoder will be used. + if (_isMaster) { + SET_OPUS_FUNCTIONS(codecDef); + } else { + SET_OPUSSLAVE_FUNCTIONS(codecDef); + } + return 0; } + ACMGenericCodec* ACMOpus::CreateInstance(void) { return NULL; } @@ -258,6 +295,23 @@ int16_t ACMOpus::SetBitRateSafe(const int32_t rate) { return -1; } +bool ACMOpus::IsTrueStereoCodec() { + return true; +} + +// Copy the stereo packet so that NetEq will insert into both master and slave. +void ACMOpus::SplitStereoPacket(uint8_t* payload, int32_t* payload_length) { + // Check for valid inputs. + assert(payload != NULL); + assert(*payload_length > 0); + + // Duplicate the payload. + memcpy(&payload[*payload_length], &payload[0], + sizeof(uint8_t) * (*payload_length)); + // Double the size of the packet. + *payload_length *= 2; +} + #endif // WEBRTC_CODEC_OPUS } // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/source/acm_opus.h b/webrtc/modules/audio_coding/main/source/acm_opus.h index 72794c4778..34cd3764f7 100644 --- a/webrtc/modules/audio_coding/main/source/acm_opus.h +++ b/webrtc/modules/audio_coding/main/source/acm_opus.h @@ -50,10 +50,15 @@ class ACMOpus : public ACMGenericCodec { int16_t SetBitRateSafe(const int32_t rate); + bool IsTrueStereoCodec(); + + void SplitStereoPacket(uint8_t* payload, int32_t* payload_length); + WebRtcOpusEncInst* _encoderInstPtr; WebRtcOpusDecInst* _decoderInstPtr; uint16_t _sampleFreq; uint16_t _bitrate; + int _channels; }; } // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/source/audio_coding_module.gypi b/webrtc/modules/audio_coding/main/source/audio_coding_module.gypi index 36c3cb77ee..2ee2baf574 100644 --- a/webrtc/modules/audio_coding/main/source/audio_coding_module.gypi +++ b/webrtc/modules/audio_coding/main/source/audio_coding_module.gypi @@ -120,6 +120,9 @@ '<(DEPTH)/testing/gtest.gyp:gtest', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', ], + 'defines': [ + '<@(audio_coding_defines)', + ], 'sources': [ '../test/ACMTest.cc', '../test/APITest.cc', diff --git a/webrtc/modules/audio_coding/main/test/TestStereo.cc b/webrtc/modules/audio_coding/main/test/TestStereo.cc index 9910ead997..97d6c2ebdc 100644 --- a/webrtc/modules/audio_coding/main/test/TestStereo.cc +++ b/webrtc/modules/audio_coding/main/test/TestStereo.cc @@ -121,6 +121,7 @@ TestStereo::TestStereo(int test_mode) pcma_pltype_(-1), pcmu_pltype_(-1), celt_pltype_(-1), + opus_pltype_(-1), cn_8khz_pltype_(-1), cn_16khz_pltype_(-1), cn_32khz_pltype_(-1) { @@ -432,6 +433,29 @@ void TestStereo::Perform() { celt_pltype_); Run(channel_a2b_, audio_channels, codec_channels); out_file_.Close(); +#endif +#ifdef WEBRTC_CODEC_OPUS + if(test_mode_ != 0) { + printf("===========================================================\n"); + printf("Test number: %d\n",test_cntr_ + 1); + printf("Test type: Stereo-to-stereo\n"); + } + channel_a2b_->set_codec_mode(kStereo); + audio_channels = 2; + codec_channels = 2; + test_cntr_++; + OpenOutFile(test_cntr_); + char codec_opus[] = "opus"; + RegisterSendCodec('A', codec_opus, 48000, 40000, 960, codec_channels, + opus_pltype_); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_opus, 48000, 64000, 960, codec_channels, + opus_pltype_); + Run(channel_a2b_, audio_channels, codec_channels); + RegisterSendCodec('A', codec_opus, 48000, 510000, 960, codec_channels, + opus_pltype_); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); #endif // // Test Mono-To-Stereo for all codecs. @@ -520,6 +544,20 @@ void TestStereo::Perform() { Run(channel_a2b_, audio_channels, codec_channels); out_file_.Close(); #endif +#ifdef WEBRTC_CODEC_OPUS + if(test_mode_ != 0) { + printf("===============================================================\n"); + printf("Test number: %d\n",test_cntr_ + 1); + printf("Test type: Mono-to-stereo\n"); + } + test_cntr_++; + channel_a2b_->set_codec_mode(kStereo); + OpenOutFile(test_cntr_); + RegisterSendCodec('A', codec_opus, 48000, 64000, 960, codec_channels, + opus_pltype_); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); +#endif // // Test Stereo-To-Mono for all codecs. @@ -615,6 +653,19 @@ void TestStereo::Perform() { Run(channel_a2b_, audio_channels, codec_channels); out_file_.Close(); #endif +#ifdef WEBRTC_CODEC_OPUS + if(test_mode_ != 0) { + printf("===============================================================\n"); + printf("Test number: %d\n",test_cntr_ + 1); + printf("Test type: Stereo-to-mono\n"); + } + test_cntr_++; + OpenOutFile(test_cntr_); + RegisterSendCodec('A', codec_opus, 48000, 32000, 960, codec_channels, + opus_pltype_); + Run(channel_a2b_, audio_channels, codec_channels); + out_file_.Close(); +#endif // Print out which codecs were tested, and which were not, in the run. if (test_mode_ != 0) { @@ -696,6 +747,8 @@ void TestStereo::RegisterSendCodec(char side, char* codec_name, my_codec_param.rate = rate; my_codec_param.pacsize = pack_size; CHECK_ERROR(my_acm->RegisterSendCodec(my_codec_param)); + + send_codec_name_ = codec_name; } void TestStereo::Run(TestPackStereo* channel, int in_channels, int out_channels, @@ -741,11 +794,13 @@ void TestStereo::Run(TestPackStereo* channel, int in_channels, int out_channels, // Verify that the received packet size matches the settings rec_size = channel->payload_size(); if ((0 < rec_size) & (rec_size < 65535)) { - if ((rec_size != pack_size_bytes_ * out_channels) - && (pack_size_bytes_ < 65535)) { - error_count++; + // Opus is variable rate, skip this test. + if (strcmp(send_codec_name_, "opus")) { + if ((rec_size != pack_size_bytes_ * out_channels) + && (pack_size_bytes_ < 65535)) { + error_count++; + } } - // Verify that the timestamp is updated with expected length time_stamp_diff = channel->timestamp_diff(); if ((counter_ > 10) && (time_stamp_diff != pack_size_samp_)) { diff --git a/webrtc/modules/audio_coding/main/test/TestStereo.h b/webrtc/modules/audio_coding/main/test/TestStereo.h index 3023139b5b..e9905158b8 100644 --- a/webrtc/modules/audio_coding/main/test/TestStereo.h +++ b/webrtc/modules/audio_coding/main/test/TestStereo.h @@ -97,6 +97,7 @@ class TestStereo : public ACMTest { WebRtc_UWord16 pack_size_samp_; WebRtc_UWord16 pack_size_bytes_; int counter_; + char* send_codec_name_; // Payload types for stereo codecs and CNG int g722_pltype_; @@ -106,6 +107,7 @@ class TestStereo : public ACMTest { int pcma_pltype_; int pcmu_pltype_; int celt_pltype_; + int opus_pltype_; int cn_8khz_pltype_; int cn_16khz_pltype_; int cn_32khz_pltype_; diff --git a/webrtc/modules/audio_coding/neteq/codec_db.c b/webrtc/modules/audio_coding/neteq/codec_db.c index ebc92162b6..b69b886fd3 100644 --- a/webrtc/modules/audio_coding/neteq/codec_db.c +++ b/webrtc/modules/audio_coding/neteq/codec_db.c @@ -116,6 +116,7 @@ int WebRtcNetEQ_DbAdd(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codec, #endif #ifdef NETEQ_OPUS_CODEC case kDecoderOpus : + case kDecoderOpus_2ch : #endif #ifdef NETEQ_G722_CODEC case kDecoderG722 : @@ -463,6 +464,7 @@ int WebRtcNetEQ_DbGetSplitInfo(SplitInfo_t *inst, enum WebRtcNetEQDecoder codecI #endif #ifdef NETEQ_OPUS_CODEC case kDecoderOpus: + case kDecoderOpus_2ch : #endif #ifdef NETEQ_ARBITRARY_CODEC case kDecoderArbitrary: diff --git a/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h b/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h index 9fc82973f8..83326cc578 100644 --- a/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h +++ b/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq.h @@ -63,6 +63,7 @@ enum WebRtcNetEQDecoder kDecoderG722_1C_32, kDecoderG722_1C_48, kDecoderOpus, + kDecoderOpus_2ch, kDecoderSPEEX_8, kDecoderSPEEX_16, kDecoderCELT_32, diff --git a/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h b/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h index d885faab70..519eb71846 100644 --- a/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h +++ b/webrtc/modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h @@ -327,6 +327,17 @@ inst.funcUpdBWEst=NULL; \ inst.funcGetErrorCode=NULL; +#define SET_OPUSSLAVE_FUNCTIONS(inst) \ + inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcOpus_DecodeSlave; \ + inst.funcDecodeRCU=NULL; \ + inst.funcDecodePLC=NULL; \ + inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcOpus_DecoderInitSlave; \ + inst.funcAddLatePkt=NULL; \ + inst.funcGetMDinfo=NULL; \ + inst.funcGetPitch=NULL; \ + inst.funcUpdBWEst=NULL; \ + inst.funcGetErrorCode=NULL; + #define SET_SPEEX_FUNCTIONS(inst) \ inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcSpeex_Decode; \ inst.funcDecodeRCU=NULL; \ diff --git a/webrtc/modules/audio_coding/neteq/packet_buffer.c b/webrtc/modules/audio_coding/neteq/packet_buffer.c index 7fbea58a42..74a4903d98 100644 --- a/webrtc/modules/audio_coding/neteq/packet_buffer.c +++ b/webrtc/modules/audio_coding/neteq/packet_buffer.c @@ -578,7 +578,8 @@ int WebRtcNetEQ_GetDefaultCodecSettings(const enum WebRtcNetEQDecoder *codecID, codecBytes = 1560; /* 240ms @ 52kbps (30ms frames) */ codecBuffers = 8; } - else if (codecID[i] == kDecoderOpus) + else if ((codecID[i] == kDecoderOpus) || + (codecID[i] == kDecoderOpus_2ch)) { codecBytes = 15300; /* 240ms @ 510kbps (60ms frames) */ codecBuffers = 30; /* Replicating the value for PCMu/a */ diff --git a/webrtc/modules/audio_coding/neteq/recin.c b/webrtc/modules/audio_coding/neteq/recin.c index 399250d357..aa8a3b55c0 100644 --- a/webrtc/modules/audio_coding/neteq/recin.c +++ b/webrtc/modules/audio_coding/neteq/recin.c @@ -378,6 +378,7 @@ int WebRtcNetEQ_GetTimestampScaling(MCUInst_t *MCU_inst, int rtpPayloadType) break; } case kDecoderOpus: + case kDecoderOpus_2ch: { /* We resample Opus internally to 32 kHz, but timestamps * are counted at 48 kHz. So there are two output samples