diff --git a/AUTHORS b/AUTHORS index c5a7c03eed..3ed6b6c1eb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -62,4 +62,5 @@ Telenor Digital AS <*@telenor.com> Temasys Communications <*@temasys.io> The Chromium Authors <*@chromium.org> The WebRTC Authors <*@webrtc.org> +Wire Swiss GmbH <*@wire.com> Vonage Holdings Corp. <*@vonage.com> diff --git a/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.h b/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.h index 15ded47a91..8f5bba6e8b 100644 --- a/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.h +++ b/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.h @@ -56,6 +56,7 @@ class AudioEncoderOpus final : public AudioEncoder { ApplicationMode application = kVoip; rtc::Optional bitrate_bps; // Unset means to use default value. bool fec_enabled = false; + bool cbr_enabled = false; int max_playback_rate_hz = 48000; int complexity = kDefaultComplexity; // This value may change in the struct's constructor. diff --git a/webrtc/modules/audio_coding/codecs/opus/opus_interface.c b/webrtc/modules/audio_coding/codecs/opus/opus_interface.c index 5bb3472b5a..59c6c9b2a0 100644 --- a/webrtc/modules/audio_coding/codecs/opus/opus_interface.c +++ b/webrtc/modules/audio_coding/codecs/opus/opus_interface.c @@ -205,6 +205,22 @@ int16_t WebRtcOpus_DisableDtx(OpusEncInst* inst) { } } +int16_t WebRtcOpus_EnableCbr(OpusEncInst* inst) { + if (inst) { + return opus_encoder_ctl(inst->encoder, OPUS_SET_VBR(0)); + } else { + return -1; + } +} + +int16_t WebRtcOpus_DisableCbr(OpusEncInst* inst) { + if (inst) { + return opus_encoder_ctl(inst->encoder, OPUS_SET_VBR(1)); + } else { + return -1; + } +} + int16_t WebRtcOpus_SetComplexity(OpusEncInst* inst, int32_t complexity) { if (inst) { return opus_encoder_ctl(inst->encoder, OPUS_SET_COMPLEXITY(complexity)); diff --git a/webrtc/modules/audio_coding/codecs/opus/opus_interface.h b/webrtc/modules/audio_coding/codecs/opus/opus_interface.h index 3db5152e47..29851518a8 100644 --- a/webrtc/modules/audio_coding/codecs/opus/opus_interface.h +++ b/webrtc/modules/audio_coding/codecs/opus/opus_interface.h @@ -180,6 +180,32 @@ int16_t WebRtcOpus_EnableDtx(OpusEncInst* inst); */ int16_t WebRtcOpus_DisableDtx(OpusEncInst* inst); +/**************************************************************************** + * WebRtcOpus_EnableCbr() + * + * This function enables CBR for encoding. + * + * Input: + * - inst : Encoder context + * + * Return value : 0 - Success + * -1 - Error + */ +int16_t WebRtcOpus_EnableCbr(OpusEncInst* inst); + +/**************************************************************************** + * WebRtcOpus_DisableCbr() + * + * This function disables CBR for encoding. + * + * Input: + * - inst : Encoder context + * + * Return value : 0 - Success + * -1 - Error + */ +int16_t WebRtcOpus_DisableCbr(OpusEncInst* inst); + /* * WebRtcOpus_SetComplexity(...) * diff --git a/webrtc/modules/audio_coding/codecs/opus/opus_unittest.cc b/webrtc/modules/audio_coding/codecs/opus/opus_unittest.cc index 6b51bf9b67..c2614abd6e 100644 --- a/webrtc/modules/audio_coding/codecs/opus/opus_unittest.cc +++ b/webrtc/modules/audio_coding/codecs/opus/opus_unittest.cc @@ -40,6 +40,8 @@ class OpusTest : public TestWithParam<::testing::tuple> { void TestDtxEffect(bool dtx, int block_length_ms); + void TestCbrEffect(bool dtx, int block_length_ms); + // Prepare |speech_data_| for encoding, read from a hard-coded file. // After preparation, |speech_data_.GetNextBlock()| returns a pointer to a // block of |block_length_ms| milliseconds. The data is looped every @@ -300,6 +302,52 @@ void OpusTest::TestDtxEffect(bool dtx, int block_length_ms) { EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_decoder_)); } +// Test if CBR does what we expect. +void OpusTest::TestCbrEffect(bool cbr, int block_length_ms) { + PrepareSpeechData(channels_, block_length_ms, 2000); + const size_t samples = kOpusRateKhz * block_length_ms; + + int32_t max_pkt_size_diff = 0; + int32_t prev_pkt_size = 0; + + // Create encoder memory. + EXPECT_EQ(0, + WebRtcOpus_EncoderCreate(&opus_encoder_, channels_, application_)); + EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_decoder_, channels_)); + + // Set bitrate. + EXPECT_EQ( + 0, WebRtcOpus_SetBitRate(opus_encoder_, channels_ == 1 ? 32000 : 64000)); + + // Setting CBR. + EXPECT_EQ(0, cbr ? WebRtcOpus_EnableCbr(opus_encoder_) + : WebRtcOpus_DisableCbr(opus_encoder_)); + + int16_t audio_type; + std::vector audio_out(samples * channels_); + for (int i = 0; i < 100; ++i) { + EXPECT_EQ(samples, static_cast(EncodeDecode( + opus_encoder_, speech_data_.GetNextBlock(), + opus_decoder_, audio_out.data(), &audio_type))); + + if (prev_pkt_size > 0) { + int32_t diff = std::abs((int32_t)encoded_bytes_ - prev_pkt_size); + max_pkt_size_diff = std::max(max_pkt_size_diff, diff); + } + prev_pkt_size = encoded_bytes_; + } + + if (cbr) { + EXPECT_EQ(max_pkt_size_diff, 0); + } else { + EXPECT_GT(max_pkt_size_diff, 0); + } + + // Free memory. + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); + EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_decoder_)); +} + // Test failing Create. TEST(OpusTest, OpusCreateFail) { WebRtcOpusEncInst* opus_encoder; @@ -525,6 +573,18 @@ TEST_P(OpusTest, OpusDtxOn) { TestDtxEffect(true, 40); } +TEST_P(OpusTest, OpusCbrOff) { + TestCbrEffect(false, 10); + TestCbrEffect(false, 20); + TestCbrEffect(false, 40); +} + +TEST_P(OpusTest, OpusCbrOn) { + TestCbrEffect(true, 10); + TestCbrEffect(true, 20); + TestCbrEffect(true, 40); +} + TEST_P(OpusTest, OpusSetPacketLossRate) { // Test without creating encoder memory. EXPECT_EQ(-1, WebRtcOpus_SetPacketLossRate(opus_encoder_, 50));