diff --git a/api/audio_codecs/opus/audio_decoder_opus.cc b/api/audio_codecs/opus/audio_decoder_opus.cc index cd7041681f..82bb1b2e2c 100644 --- a/api/audio_codecs/opus/audio_decoder_opus.cc +++ b/api/audio_codecs/opus/audio_decoder_opus.cc @@ -20,6 +20,18 @@ namespace webrtc { +bool AudioDecoderOpus::Config::IsOk() const { + if (sample_rate_hz != 16000 && sample_rate_hz != 48000) { + // Unsupported sample rate. (libopus supports a few other rates as + // well; we can add support for them when needed.) + return false; + } + if (num_channels != 1 && num_channels != 2) { + return false; + } + return true; +} + absl::optional AudioDecoderOpus::SdpToConfig( const SdpAudioFormat& format) { const auto num_channels = [&]() -> absl::optional { @@ -38,7 +50,10 @@ absl::optional AudioDecoderOpus::SdpToConfig( if (absl::EqualsIgnoreCase(format.name, "opus") && format.clockrate_hz == 48000 && format.num_channels == 2 && num_channels) { - return Config{*num_channels}; + Config config; + config.num_channels = *num_channels; + RTC_DCHECK(config.IsOk()); + return config; } else { return absl::nullopt; } @@ -57,7 +72,9 @@ void AudioDecoderOpus::AppendSupportedDecoders( std::unique_ptr AudioDecoderOpus::MakeAudioDecoder( Config config, absl::optional /*codec_pair_id*/) { - return absl::make_unique(config.num_channels); + RTC_DCHECK(config.IsOk()); + return absl::make_unique(config.num_channels, + config.sample_rate_hz); } } // namespace webrtc diff --git a/api/audio_codecs/opus/audio_decoder_opus.h b/api/audio_codecs/opus/audio_decoder_opus.h index 6fbbcb598a..ec0f61d5bb 100644 --- a/api/audio_codecs/opus/audio_decoder_opus.h +++ b/api/audio_codecs/opus/audio_decoder_opus.h @@ -26,7 +26,9 @@ namespace webrtc { // CreateAudioDecoderFactory<...>(). struct RTC_EXPORT AudioDecoderOpus { struct Config { - int num_channels; + bool IsOk() const; // Checks if the values are currently OK. + int sample_rate_hz = 48000; + int num_channels = 1; }; static absl::optional SdpToConfig(const SdpAudioFormat& audio_format); static void AppendSupportedDecoders(std::vector* specs); diff --git a/modules/audio_coding/codecs/opus/audio_decoder_opus.cc b/modules/audio_coding/codecs/opus/audio_decoder_opus.cc index 03695490ce..cff9685548 100644 --- a/modules/audio_coding/codecs/opus/audio_decoder_opus.cc +++ b/modules/audio_coding/codecs/opus/audio_decoder_opus.cc @@ -20,10 +20,13 @@ namespace webrtc { -AudioDecoderOpusImpl::AudioDecoderOpusImpl(size_t num_channels) - : channels_(num_channels) { +AudioDecoderOpusImpl::AudioDecoderOpusImpl(size_t num_channels, + int sample_rate_hz) + : channels_{num_channels}, sample_rate_hz_{sample_rate_hz} { RTC_DCHECK(num_channels == 1 || num_channels == 2); - const int error = WebRtcOpus_DecoderCreate(&dec_state_, channels_, 48000); + RTC_DCHECK(sample_rate_hz == 16000 || sample_rate_hz == 48000); + const int error = + WebRtcOpus_DecoderCreate(&dec_state_, channels_, sample_rate_hz_); RTC_DCHECK(error == 0); WebRtcOpus_DecoderInit(dec_state_); } @@ -57,7 +60,7 @@ int AudioDecoderOpusImpl::DecodeInternal(const uint8_t* encoded, int sample_rate_hz, int16_t* decoded, SpeechType* speech_type) { - RTC_DCHECK_EQ(sample_rate_hz, 48000); + RTC_DCHECK_EQ(sample_rate_hz, sample_rate_hz_); int16_t temp_type = 1; // Default is speech. int ret = WebRtcOpus_Decode(dec_state_, encoded, encoded_len, decoded, &temp_type); @@ -78,7 +81,7 @@ int AudioDecoderOpusImpl::DecodeRedundantInternal(const uint8_t* encoded, speech_type); } - RTC_DCHECK_EQ(sample_rate_hz, 48000); + RTC_DCHECK_EQ(sample_rate_hz, sample_rate_hz_); int16_t temp_type = 1; // Default is speech. int ret = WebRtcOpus_DecodeFec(dec_state_, encoded, encoded_len, decoded, &temp_type); @@ -104,7 +107,7 @@ int AudioDecoderOpusImpl::PacketDurationRedundant(const uint8_t* encoded, return PacketDuration(encoded, encoded_len); } - return WebRtcOpus_FecDurationEst(encoded, encoded_len, 48000); + return WebRtcOpus_FecDurationEst(encoded, encoded_len, sample_rate_hz_); } bool AudioDecoderOpusImpl::PacketHasFec(const uint8_t* encoded, @@ -115,7 +118,7 @@ bool AudioDecoderOpusImpl::PacketHasFec(const uint8_t* encoded, } int AudioDecoderOpusImpl::SampleRateHz() const { - return 48000; + return sample_rate_hz_; } size_t AudioDecoderOpusImpl::Channels() const { diff --git a/modules/audio_coding/codecs/opus/audio_decoder_opus.h b/modules/audio_coding/codecs/opus/audio_decoder_opus.h index d8cc5c1fc2..3a2bb71431 100644 --- a/modules/audio_coding/codecs/opus/audio_decoder_opus.h +++ b/modules/audio_coding/codecs/opus/audio_decoder_opus.h @@ -24,7 +24,8 @@ namespace webrtc { class AudioDecoderOpusImpl final : public AudioDecoder { public: - explicit AudioDecoderOpusImpl(size_t num_channels); + explicit AudioDecoderOpusImpl(size_t num_channels, + int sample_rate_hz = 48000); ~AudioDecoderOpusImpl() override; std::vector ParsePayload(rtc::Buffer&& payload, @@ -52,6 +53,7 @@ class AudioDecoderOpusImpl final : public AudioDecoder { private: OpusDecInst* dec_state_; const size_t channels_; + const int sample_rate_hz_; RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderOpusImpl); }; diff --git a/modules/audio_coding/neteq/audio_decoder_unittest.cc b/modules/audio_coding/neteq/audio_decoder_unittest.cc index 42d96db4df..d8e35ce0f6 100644 --- a/modules/audio_coding/neteq/audio_decoder_unittest.cc +++ b/modules/audio_coding/neteq/audio_decoder_unittest.cc @@ -426,33 +426,34 @@ class AudioDecoderG722StereoTest : public AudioDecoderTest { } }; -class AudioDecoderOpusTest : public AudioDecoderTest { +class AudioDecoderOpusTest + : public AudioDecoderTest, + public testing::WithParamInterface> { protected: AudioDecoderOpusTest() : AudioDecoderTest() { - codec_input_rate_hz_ = 48000; - frame_size_ = 480; + channels_ = opus_num_channels_; + codec_input_rate_hz_ = opus_sample_rate_hz_; + frame_size_ = rtc::CheckedDivExact(opus_sample_rate_hz_, 100); data_length_ = 10 * frame_size_; - decoder_ = new AudioDecoderOpusImpl(1); + decoder_ = + new AudioDecoderOpusImpl(opus_num_channels_, opus_sample_rate_hz_); AudioEncoderOpusConfig config; - config.frame_size_ms = static_cast(frame_size_) / 48; - config.application = AudioEncoderOpusConfig::ApplicationMode::kVoip; + config.frame_size_ms = 10; + config.sample_rate_hz = opus_sample_rate_hz_; + config.num_channels = opus_num_channels_; + config.application = opus_num_channels_ == 1 + ? AudioEncoderOpusConfig::ApplicationMode::kVoip + : AudioEncoderOpusConfig::ApplicationMode::kAudio; audio_encoder_ = AudioEncoderOpus::MakeAudioEncoder(config, payload_type_); } + const int opus_sample_rate_hz_{std::get<0>(GetParam())}; + const int opus_num_channels_{std::get<1>(GetParam())}; }; -class AudioDecoderOpusStereoTest : public AudioDecoderOpusTest { - protected: - AudioDecoderOpusStereoTest() : AudioDecoderOpusTest() { - channels_ = 2; - delete decoder_; - decoder_ = new AudioDecoderOpusImpl(2); - AudioEncoderOpusConfig config; - config.frame_size_ms = static_cast(frame_size_) / 48; - config.num_channels = 2; - config.application = AudioEncoderOpusConfig::ApplicationMode::kAudio; - audio_encoder_ = AudioEncoderOpus::MakeAudioEncoder(config, payload_type_); - } -}; +INSTANTIATE_TEST_SUITE_P(Param, + AudioDecoderOpusTest, + testing::Combine(testing::Values(16000, 48000), + testing::Values(1, 2))); TEST_F(AudioDecoderPcmUTest, EncodeDecode) { int tolerance = 251; @@ -592,41 +593,22 @@ TEST_F(AudioDecoderG722StereoTest, SetTargetBitrate) { TestSetAndGetTargetBitratesWithFixedCodec(audio_encoder_.get(), 128000); } -TEST_F(AudioDecoderOpusTest, EncodeDecode) { - int tolerance = 6176; - double mse = 238630.0; - int delay = 22; // Delay from input to output. - EncodeDecodeTest(0, tolerance, mse, delay); - ReInitTest(); - EXPECT_FALSE(decoder_->HasDecodePlc()); -} - -namespace { -void TestOpusSetTargetBitrates(AudioEncoder* audio_encoder) { - EXPECT_EQ(6000, SetAndGetTargetBitrate(audio_encoder, 5999)); - EXPECT_EQ(6000, SetAndGetTargetBitrate(audio_encoder, 6000)); - EXPECT_EQ(32000, SetAndGetTargetBitrate(audio_encoder, 32000)); - EXPECT_EQ(510000, SetAndGetTargetBitrate(audio_encoder, 510000)); - EXPECT_EQ(510000, SetAndGetTargetBitrate(audio_encoder, 511000)); -} -} // namespace - -TEST_F(AudioDecoderOpusTest, SetTargetBitrate) { - TestOpusSetTargetBitrates(audio_encoder_.get()); -} - -TEST_F(AudioDecoderOpusStereoTest, EncodeDecode) { - int tolerance = 6176; - int channel_diff_tolerance = 0; - double mse = 238630.0; - int delay = 22; // Delay from input to output. +TEST_P(AudioDecoderOpusTest, EncodeDecode) { + constexpr int tolerance = 6176; + const int channel_diff_tolerance = opus_sample_rate_hz_ == 16000 ? 6 : 0; + constexpr double mse = 238630.0; + constexpr int delay = 22; // Delay from input to output. EncodeDecodeTest(0, tolerance, mse, delay, channel_diff_tolerance); ReInitTest(); EXPECT_FALSE(decoder_->HasDecodePlc()); } -TEST_F(AudioDecoderOpusStereoTest, SetTargetBitrate) { - TestOpusSetTargetBitrates(audio_encoder_.get()); +TEST_P(AudioDecoderOpusTest, SetTargetBitrate) { + EXPECT_EQ(6000, SetAndGetTargetBitrate(audio_encoder_.get(), 5999)); + EXPECT_EQ(6000, SetAndGetTargetBitrate(audio_encoder_.get(), 6000)); + EXPECT_EQ(32000, SetAndGetTargetBitrate(audio_encoder_.get(), 32000)); + EXPECT_EQ(510000, SetAndGetTargetBitrate(audio_encoder_.get(), 510000)); + EXPECT_EQ(510000, SetAndGetTargetBitrate(audio_encoder_.get(), 511000)); } } // namespace webrtc