From eeb2765f6c910c0690780e3b5bd598901accef0a Mon Sep 17 00:00:00 2001 From: Alex Luebs Date: Mon, 20 Nov 2017 11:13:56 -0800 Subject: [PATCH] Implement Opus bandwidth adjustment behind a FieldTrial Bug: webrtc:8522 Change-Id: I3a32ebfecd27ff74b507c2cee9e16aab17153442 Reviewed-on: https://webrtc-review.googlesource.com/22210 Commit-Queue: Alejandro Luebs Reviewed-by: Henrik Lundin Cr-Commit-Position: refs/heads/master@{#20799} --- modules/audio_coding/BUILD.gn | 2 + .../codecs/opus/audio_encoder_opus.cc | 35 ++++ .../codecs/opus/audio_encoder_opus.h | 9 ++ .../opus/audio_encoder_opus_unittest.cc | 59 +++++++ .../codecs/opus/opus_bandwidth_unittest.cc | 151 ++++++++++++++++++ .../audio_coding/codecs/opus/opus_interface.c | 22 ++- .../audio_coding/codecs/opus/opus_interface.h | 35 ++++ .../codecs/opus/opus_speed_test.cc | 19 ++- .../audio_coding/codecs/opus/opus_unittest.cc | 39 +++++ 9 files changed, 368 insertions(+), 3 deletions(-) create mode 100644 modules/audio_coding/codecs/opus/opus_bandwidth_unittest.cc diff --git a/modules/audio_coding/BUILD.gn b/modules/audio_coding/BUILD.gn index 0fed244dc4..871cbe99e9 100644 --- a/modules/audio_coding/BUILD.gn +++ b/modules/audio_coding/BUILD.gn @@ -2067,6 +2067,7 @@ if (rtc_include_tests) { "codecs/isac/unittest.cc", "codecs/legacy_encoded_audio_frame_unittest.cc", "codecs/opus/audio_encoder_opus_unittest.cc", + "codecs/opus/opus_bandwidth_unittest.cc", "codecs/opus/opus_unittest.cc", "codecs/red/audio_encoder_copy_red_unittest.cc", "neteq/audio_multi_vector_unittest.cc", @@ -2140,6 +2141,7 @@ if (rtc_include_tests) { "../../api/audio_codecs:audio_codecs_api", "../../api/audio_codecs:builtin_audio_decoder_factory", "../../api/audio_codecs:builtin_audio_encoder_factory", + "../../api/audio_codecs/opus:audio_decoder_opus", "../../api/audio_codecs/opus:audio_encoder_opus", "../../common_audio", "../../common_audio:mock_common_audio", diff --git a/modules/audio_coding/codecs/opus/audio_encoder_opus.cc b/modules/audio_coding/codecs/opus/audio_encoder_opus.cc index f07cd42068..4b655dfdac 100644 --- a/modules/audio_coding/codecs/opus/audio_encoder_opus.cc +++ b/modules/audio_coding/codecs/opus/audio_encoder_opus.cc @@ -330,6 +330,28 @@ rtc::Optional AudioEncoderOpusImpl::GetNewComplexity( } } +rtc::Optional AudioEncoderOpusImpl::GetNewBandwidth( + const AudioEncoderOpusConfig& config, + OpusEncInst* inst) { + constexpr int kMinWidebandBitrate = 8000; + constexpr int kMaxNarrowbandBitrate = 9000; + constexpr int kAutomaticThreshold = 11000; + RTC_DCHECK(config.IsOk()); + const int bitrate = GetBitrateBps(config); + if (bitrate > kAutomaticThreshold) { + return rtc::Optional(OPUS_AUTO); + } + const int bandwidth = WebRtcOpus_GetBandwidth(inst); + RTC_DCHECK_GE(bandwidth, 0); + if (bitrate > kMaxNarrowbandBitrate && bandwidth < OPUS_BANDWIDTH_WIDEBAND) { + return rtc::Optional(OPUS_BANDWIDTH_WIDEBAND); + } else if (bitrate < kMinWidebandBitrate && + bandwidth > OPUS_BANDWIDTH_NARROWBAND) { + return rtc::Optional(OPUS_BANDWIDTH_NARROWBAND); + } + return rtc::Optional(); +} + class AudioEncoderOpusImpl::PacketLossFractionSmoother { public: explicit PacketLossFractionSmoother() @@ -376,6 +398,9 @@ AudioEncoderOpusImpl::AudioEncoderOpusImpl( : payload_type_(payload_type), send_side_bwe_with_overhead_( webrtc::field_trial::IsEnabled("WebRTC-SendSideBwe-WithOverhead")), + adjust_bandwidth_( + webrtc::field_trial::IsEnabled("WebRTC-AdjustOpusBandwidth")), + bitrate_changed_(true), packet_loss_rate_(0.0), inst_(nullptr), packet_loss_fraction_smoother_(new PacketLossFractionSmoother()), @@ -609,6 +634,14 @@ AudioEncoder::EncodedInfo AudioEncoderOpusImpl::EncodeImpl( // Will use new packet size for next encoding. config_.frame_size_ms = next_frame_length_ms_; + if (adjust_bandwidth_ && bitrate_changed_) { + const auto bandwidth = GetNewBandwidth(config_, inst_); + if (bandwidth) { + RTC_CHECK_EQ(0, WebRtcOpus_SetBandwidth(inst_, *bandwidth)); + } + bitrate_changed_ = false; + } + info.encoded_timestamp = first_timestamp_in_buffer_; info.payload_type = payload_type_; info.send_even_if_empty = true; // Allows Opus to send empty packets. @@ -672,6 +705,7 @@ bool AudioEncoderOpusImpl::RecreateEncoderInstance( // window. complexity_ = GetNewComplexity(config).value_or(config.complexity); RTC_CHECK_EQ(0, WebRtcOpus_SetComplexity(inst_, complexity_)); + bitrate_changed_ = true; if (config.dtx_enabled) { RTC_CHECK_EQ(0, WebRtcOpus_EnableDtx(inst_)); } else { @@ -727,6 +761,7 @@ void AudioEncoderOpusImpl::SetTargetBitrate(int bits_per_second) { complexity_ = *new_complexity; RTC_CHECK_EQ(0, WebRtcOpus_SetComplexity(inst_, complexity_)); } + bitrate_changed_ = true; } void AudioEncoderOpusImpl::ApplyAudioNetworkAdaptor() { diff --git a/modules/audio_coding/codecs/opus/audio_encoder_opus.h b/modules/audio_coding/codecs/opus/audio_encoder_opus.h index 22967c4c15..49c5207740 100644 --- a/modules/audio_coding/codecs/opus/audio_encoder_opus.h +++ b/modules/audio_coding/codecs/opus/audio_encoder_opus.h @@ -43,6 +43,13 @@ class AudioEncoderOpusImpl final : public AudioEncoder { static rtc::Optional GetNewComplexity( const AudioEncoderOpusConfig& config); + // Returns OPUS_AUTO if the the current bitrate is above wideband threshold. + // Returns empty if it is below, but bandwidth coincides with the desired one. + // Otherwise returns the desired bandwidth. + static rtc::Optional GetNewBandwidth( + const AudioEncoderOpusConfig& config, + OpusEncInst* inst); + using AudioNetworkAdaptorCreator = std::function(const std::string&, RtcEventLog*)>; @@ -148,6 +155,8 @@ class AudioEncoderOpusImpl final : public AudioEncoder { AudioEncoderOpusConfig config_; const int payload_type_; const bool send_side_bwe_with_overhead_; + const bool adjust_bandwidth_; + bool bitrate_changed_; float packet_loss_rate_; std::vector input_buffer_; OpusEncInst* inst_; diff --git a/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc b/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc index c3ad488dac..dfef6821ad 100644 --- a/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc +++ b/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc @@ -17,6 +17,7 @@ #include "common_types.h" // NOLINT(build/include) #include "modules/audio_coding/audio_network_adaptor/mock/mock_audio_network_adaptor.h" #include "modules/audio_coding/codecs/opus/audio_encoder_opus.h" +#include "modules/audio_coding/codecs/opus/opus_interface.h" #include "modules/audio_coding/neteq/tools/audio_loop.h" #include "rtc_base/checks.h" #include "rtc_base/fakeclock.h" @@ -473,6 +474,64 @@ TEST(AudioEncoderOpusTest, ConfigComplexityAdaptation) { EXPECT_EQ(6, AudioEncoderOpusImpl::GetNewComplexity(config)); } +// Verifies that the bandwidth adaptation in the config works as intended. +TEST(AudioEncoderOpusTest, ConfigBandwidthAdaptation) { + AudioEncoderOpusConfig config; + // Sample rate of Opus. + constexpr size_t kOpusRateKhz = 48; + std::vector silence( + kOpusRateKhz * config.frame_size_ms * config.num_channels, 0); + constexpr size_t kMaxBytes = 1000; + uint8_t bitstream[kMaxBytes]; + + OpusEncInst* inst; + EXPECT_EQ(0, WebRtcOpus_EncoderCreate( + &inst, config.num_channels, + config.application == + AudioEncoderOpusConfig::ApplicationMode::kVoip + ? 0 + : 1)); + + // Bitrate below minmum wideband. Expect narrowband. + config.bitrate_bps = rtc::Optional(7999); + auto bandwidth = AudioEncoderOpusImpl::GetNewBandwidth(config, inst); + EXPECT_EQ(rtc::Optional(OPUS_BANDWIDTH_NARROWBAND), bandwidth); + WebRtcOpus_SetBandwidth(inst, *bandwidth); + // It is necessary to encode here because Opus has some logic in the encoder + // that goes from the user-set bandwidth to the used and returned one. + WebRtcOpus_Encode(inst, silence.data(), + rtc::CheckedDivExact(silence.size(), config.num_channels), + kMaxBytes, bitstream); + + // Bitrate not yet above maximum narrowband. Expect empty. + config.bitrate_bps = rtc::Optional(9000); + bandwidth = AudioEncoderOpusImpl::GetNewBandwidth(config, inst); + EXPECT_EQ(rtc::Optional(), bandwidth); + + // Bitrate above maximum narrowband. Expect wideband. + config.bitrate_bps = rtc::Optional(9001); + bandwidth = AudioEncoderOpusImpl::GetNewBandwidth(config, inst); + EXPECT_EQ(rtc::Optional(OPUS_BANDWIDTH_WIDEBAND), bandwidth); + WebRtcOpus_SetBandwidth(inst, *bandwidth); + // It is necessary to encode here because Opus has some logic in the encoder + // that goes from the user-set bandwidth to the used and returned one. + WebRtcOpus_Encode(inst, silence.data(), + rtc::CheckedDivExact(silence.size(), config.num_channels), + kMaxBytes, bitstream); + + // Bitrate not yet below minimum wideband. Expect empty. + config.bitrate_bps = rtc::Optional(8000); + bandwidth = AudioEncoderOpusImpl::GetNewBandwidth(config, inst); + EXPECT_EQ(rtc::Optional(), bandwidth); + + // Bitrate above automatic threshold. Expect automatic. + config.bitrate_bps = rtc::Optional(12001); + bandwidth = AudioEncoderOpusImpl::GetNewBandwidth(config, inst); + EXPECT_EQ(rtc::Optional(OPUS_AUTO), bandwidth); + + EXPECT_EQ(0, WebRtcOpus_EncoderFree(inst)); +} + TEST(AudioEncoderOpusTest, EmptyConfigDoesNotAffectEncoderSettings) { auto states = CreateCodec(2); states.encoder->EnableAudioNetworkAdaptor("", nullptr); diff --git a/modules/audio_coding/codecs/opus/opus_bandwidth_unittest.cc b/modules/audio_coding/codecs/opus/opus_bandwidth_unittest.cc new file mode 100644 index 0000000000..4394949f96 --- /dev/null +++ b/modules/audio_coding/codecs/opus/opus_bandwidth_unittest.cc @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "api/audio_codecs/opus/audio_decoder_opus.h" +#include "api/audio_codecs/opus/audio_encoder_opus.h" +#include "common_audio/include/audio_util.h" +#include "common_audio/lapped_transform.h" +#include "common_audio/window_generator.h" +#include "modules/audio_coding/neteq/tools/audio_loop.h" +#include "test/field_trial.h" +#include "test/gtest.h" +#include "test/testsupport/fileutils.h" + +namespace webrtc { +namespace { + +constexpr size_t kNumChannels = 1u; +constexpr int kSampleRateHz = 48000; +constexpr size_t kMaxLoopLengthSamples = kSampleRateHz * 50; // 50 seconds. +constexpr size_t kInputBlockSizeSamples = 10 * kSampleRateHz / 1000; // 10 ms +constexpr size_t kOutputBlockSizeSamples = 20 * kSampleRateHz / 1000; // 20 ms +constexpr size_t kFftSize = 1024; +constexpr size_t kNarrowbandSize = 4000 * kFftSize / kSampleRateHz; +constexpr float kKbdAlpha = 1.5f; + +class PowerRatioEstimator : public LappedTransform::Callback { + public: + PowerRatioEstimator() : low_pow_(0.f), high_pow_(0.f) { + WindowGenerator::KaiserBesselDerived(kKbdAlpha, kFftSize, window_); + transform_.reset(new LappedTransform(kNumChannels, 0u, + kInputBlockSizeSamples, window_, + kFftSize, kFftSize / 2, this)); + } + + void ProcessBlock(float* data) { transform_->ProcessChunk(&data, nullptr); } + + float PowerRatio() { return high_pow_ / low_pow_; } + + protected: + void ProcessAudioBlock(const std::complex* const* input, + size_t num_input_channels, + size_t num_freq_bins, + size_t num_output_channels, + std::complex* const* output) override { + float low_pow = 0.f; + float high_pow = 0.f; + for (size_t i = 0u; i < num_input_channels; ++i) { + for (size_t j = 0u; j < kNarrowbandSize; ++j) { + float low_mag = std::abs(input[i][j]); + low_pow += low_mag * low_mag; + float high_mag = std::abs(input[i][j + kNarrowbandSize]); + high_pow += high_mag * high_mag; + } + } + low_pow_ += low_pow / (num_input_channels * kFftSize); + high_pow_ += high_pow / (num_input_channels * kFftSize); + } + + private: + std::unique_ptr transform_; + float window_[kFftSize]; + float low_pow_; + float high_pow_; +}; + +float EncodedPowerRatio(AudioEncoder* encoder, + AudioDecoder* decoder, + test::AudioLoop* audio_loop) { + // Encode and decode. + uint32_t rtp_timestamp = 0u; + constexpr size_t kBufferSize = 500; + rtc::Buffer encoded(kBufferSize); + std::vector decoded(kOutputBlockSizeSamples); + std::vector decoded_float(kOutputBlockSizeSamples); + AudioDecoder::SpeechType speech_type = AudioDecoder::kSpeech; + PowerRatioEstimator power_ratio_estimator; + for (size_t i = 0; i < 1000; ++i) { + encoded.Clear(); + AudioEncoder::EncodedInfo encoder_info = + encoder->Encode(rtp_timestamp, audio_loop->GetNextBlock(), &encoded); + rtp_timestamp += kInputBlockSizeSamples; + if (encoded.size() > 0) { + int decoder_info = decoder->Decode( + encoded.data(), encoded.size(), kSampleRateHz, + decoded.size() * sizeof(decoded[0]), decoded.data(), &speech_type); + if (decoder_info > 0) { + S16ToFloat(decoded.data(), decoded.size(), decoded_float.data()); + power_ratio_estimator.ProcessBlock(decoded_float.data()); + } + } + } + return power_ratio_estimator.PowerRatio(); +} + +} // namespace + +TEST(BandwidthAdaptationTest, BandwidthAdaptationTest) { + test::ScopedFieldTrials override_field_trials( + "WebRTC-AdjustOpusBandwidth/Enabled/"); + + constexpr float kMaxNarrowbandRatio = 0.003f; + constexpr float kMinWidebandRatio = 0.03f; + + // Create encoder. + AudioEncoderOpusConfig enc_config; + enc_config.bitrate_bps = rtc::Optional(7999); + enc_config.num_channels = kNumChannels; + constexpr int payload_type = 17; + auto encoder = AudioEncoderOpus::MakeAudioEncoder(enc_config, payload_type); + + // Create decoder. + AudioDecoderOpus::Config dec_config; + dec_config.num_channels = kNumChannels; + auto decoder = AudioDecoderOpus::MakeAudioDecoder(dec_config); + + // Open speech file. + const std::string kInputFileName = + webrtc::test::ResourcePath("audio_coding/speech_mono_32_48kHz", "pcm"); + test::AudioLoop audio_loop; + EXPECT_EQ(kSampleRateHz, encoder->SampleRateHz()); + ASSERT_TRUE(audio_loop.Init(kInputFileName, kMaxLoopLengthSamples, + kInputBlockSizeSamples)); + + EXPECT_LT(EncodedPowerRatio(encoder.get(), decoder.get(), &audio_loop), + kMaxNarrowbandRatio); + + encoder->OnReceivedTargetAudioBitrate(9000); + EXPECT_LT(EncodedPowerRatio(encoder.get(), decoder.get(), &audio_loop), + kMaxNarrowbandRatio); + + encoder->OnReceivedTargetAudioBitrate(9001); + EXPECT_GT(EncodedPowerRatio(encoder.get(), decoder.get(), &audio_loop), + kMinWidebandRatio); + + encoder->OnReceivedTargetAudioBitrate(8000); + EXPECT_GT(EncodedPowerRatio(encoder.get(), decoder.get(), &audio_loop), + kMinWidebandRatio); + + encoder->OnReceivedTargetAudioBitrate(12001); + EXPECT_GT(EncodedPowerRatio(encoder.get(), decoder.get(), &audio_loop), + kMinWidebandRatio); +} + +} // namespace webrtc diff --git a/modules/audio_coding/codecs/opus/opus_interface.c b/modules/audio_coding/codecs/opus/opus_interface.c index 5166f4cae5..d219098b01 100644 --- a/modules/audio_coding/codecs/opus/opus_interface.c +++ b/modules/audio_coding/codecs/opus/opus_interface.c @@ -11,7 +11,6 @@ #include "modules/audio_coding/codecs/opus/opus_interface.h" #include "rtc_base/checks.h" -#include "modules/audio_coding/codecs/opus/opus_inst.h" #include #include @@ -229,6 +228,27 @@ int16_t WebRtcOpus_SetComplexity(OpusEncInst* inst, int32_t complexity) { } } +int32_t WebRtcOpus_GetBandwidth(OpusEncInst* inst) { + if (!inst) { + return -1; + } + int32_t bandwidth; + if (opus_encoder_ctl(inst->encoder, OPUS_GET_BANDWIDTH(&bandwidth)) == 0) { + return bandwidth; + } else { + return -1; + } + +} + +int16_t WebRtcOpus_SetBandwidth(OpusEncInst* inst, int32_t bandwidth) { + if (inst) { + return opus_encoder_ctl(inst->encoder, OPUS_SET_BANDWIDTH(bandwidth)); + } else { + return -1; + } +} + int16_t WebRtcOpus_SetForceChannels(OpusEncInst* inst, size_t num_channels) { if (!inst) return -1; diff --git a/modules/audio_coding/codecs/opus/opus_interface.h b/modules/audio_coding/codecs/opus/opus_interface.h index 13e2ee3b7b..4b8e892344 100644 --- a/modules/audio_coding/codecs/opus/opus_interface.h +++ b/modules/audio_coding/codecs/opus/opus_interface.h @@ -13,6 +13,7 @@ #include +#include "modules/audio_coding/codecs/opus/opus_inst.h" #include "typedefs.h" // NOLINT(build/include) #ifdef __cplusplus @@ -221,6 +222,40 @@ int16_t WebRtcOpus_DisableCbr(OpusEncInst* inst); */ int16_t WebRtcOpus_SetComplexity(OpusEncInst* inst, int32_t complexity); +/* + * WebRtcOpus_GetBandwidth(...) + * + * This function returns the current bandwidth. + * + * Input: + * - inst : Encoder context + * + * Return value : Bandwidth - Success + * -1 - Error + */ +int32_t WebRtcOpus_GetBandwidth(OpusEncInst* inst); + +/* + * WebRtcOpus_SetBandwidth(...) + * + * By default Opus decides which bandwidth to encode the signal in depending on + * the the bitrate. This function overrules the previous setting and forces the + * encoder to encode in narrowband/wideband/fullband/etc. + * + * Input: + * - inst : Encoder context + * - bandwidth : New target bandwidth. Valid values are: + * OPUS_BANDWIDTH_NARROWBAND + * OPUS_BANDWIDTH_MEDIUMBAND + * OPUS_BANDWIDTH_WIDEBAND + * OPUS_BANDWIDTH_SUPERWIDEBAND + * OPUS_BANDWIDTH_FULLBAND + * + * Return value : 0 - Success + * -1 - Error + */ +int16_t WebRtcOpus_SetBandwidth(OpusEncInst* inst, int32_t bandwidth); + /* * WebRtcOpus_SetForceChannels(...) * diff --git a/modules/audio_coding/codecs/opus/opus_speed_test.cc b/modules/audio_coding/codecs/opus/opus_speed_test.cc index 0590a1e02f..ca46aa1b16 100644 --- a/modules/audio_coding/codecs/opus/opus_speed_test.cc +++ b/modules/audio_coding/codecs/opus/opus_speed_test.cc @@ -81,10 +81,11 @@ float OpusSpeedTest::DecodeABlock(const uint8_t* bit_stream, return 1000.0 * clocks / CLOCKS_PER_SEC; } +/* Test audio length in second. */ +constexpr size_t kDurationSec = 400; + #define ADD_TEST(complexity) \ TEST_P(OpusSpeedTest, OpusSetComplexityTest##complexity) { \ - /* Test audio length in second. */ \ - size_t kDurationSec = 400; \ /* Set complexity. */ \ printf("Setting complexity to %d ...\n", complexity); \ EXPECT_EQ(0, WebRtcOpus_SetComplexity(opus_encoder_, complexity)); \ @@ -103,6 +104,20 @@ ADD_TEST(2); ADD_TEST(1); ADD_TEST(0); +#define ADD_BANDWIDTH_TEST(bandwidth) \ + TEST_P(OpusSpeedTest, OpusSetBandwidthTest##bandwidth) { \ + /* Set bandwidth. */ \ + printf("Setting bandwidth to %d ...\n", bandwidth); \ + EXPECT_EQ(0, WebRtcOpus_SetBandwidth(opus_encoder_, bandwidth)); \ + EncodeDecode(kDurationSec); \ + } + +ADD_BANDWIDTH_TEST(OPUS_BANDWIDTH_NARROWBAND); +ADD_BANDWIDTH_TEST(OPUS_BANDWIDTH_MEDIUMBAND); +ADD_BANDWIDTH_TEST(OPUS_BANDWIDTH_WIDEBAND); +ADD_BANDWIDTH_TEST(OPUS_BANDWIDTH_SUPERWIDEBAND); +ADD_BANDWIDTH_TEST(OPUS_BANDWIDTH_FULLBAND); + // List all test cases: (channel, bit rat, filename, extension). const coding_param param_set[] = { std::make_tuple(1, diff --git a/modules/audio_coding/codecs/opus/opus_unittest.cc b/modules/audio_coding/codecs/opus/opus_unittest.cc index c9e6ad150b..be0530b74b 100644 --- a/modules/audio_coding/codecs/opus/opus_unittest.cc +++ b/modules/audio_coding/codecs/opus/opus_unittest.cc @@ -458,6 +458,45 @@ TEST_P(OpusTest, OpusSetComplexity) { EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); } +TEST_P(OpusTest, OpusSetBandwidth) { + PrepareSpeechData(channels_, 20, 20); + + int16_t audio_type; + std::unique_ptr output_data_decode( + new int16_t[kOpus20msFrameSamples * channels_]()); + + // Test without creating encoder memory. + EXPECT_EQ(-1, + WebRtcOpus_SetBandwidth(opus_encoder_, OPUS_BANDWIDTH_NARROWBAND)); + EXPECT_EQ(-1, WebRtcOpus_GetBandwidth(opus_encoder_)); + + // Create encoder memory, try with different bandwidths. + EXPECT_EQ(0, + WebRtcOpus_EncoderCreate(&opus_encoder_, channels_, application_)); + EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_decoder_, channels_)); + + EXPECT_EQ(-1, WebRtcOpus_SetBandwidth(opus_encoder_, + OPUS_BANDWIDTH_NARROWBAND - 1)); + EXPECT_EQ(0, + WebRtcOpus_SetBandwidth(opus_encoder_, OPUS_BANDWIDTH_NARROWBAND)); + EncodeDecode(opus_encoder_, speech_data_.GetNextBlock(), opus_decoder_, + output_data_decode.get(), &audio_type); + EXPECT_EQ(OPUS_BANDWIDTH_NARROWBAND, WebRtcOpus_GetBandwidth(opus_encoder_)); + EXPECT_EQ(0, WebRtcOpus_SetBandwidth(opus_encoder_, OPUS_BANDWIDTH_FULLBAND)); + EncodeDecode(opus_encoder_, speech_data_.GetNextBlock(), opus_decoder_, + output_data_decode.get(), &audio_type); + EXPECT_EQ(OPUS_BANDWIDTH_FULLBAND, WebRtcOpus_GetBandwidth(opus_encoder_)); + EXPECT_EQ( + -1, WebRtcOpus_SetBandwidth(opus_encoder_, OPUS_BANDWIDTH_FULLBAND + 1)); + EncodeDecode(opus_encoder_, speech_data_.GetNextBlock(), opus_decoder_, + output_data_decode.get(), &audio_type); + EXPECT_EQ(OPUS_BANDWIDTH_FULLBAND, WebRtcOpus_GetBandwidth(opus_encoder_)); + + // Free memory. + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); + EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_decoder_)); +} + TEST_P(OpusTest, OpusForceChannels) { // Test without creating encoder memory. EXPECT_EQ(-1, WebRtcOpus_SetForceChannels(opus_encoder_, 1));