| /* |
| * 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<float>* const* input, |
| size_t num_input_channels, |
| size_t num_freq_bins, |
| size_t num_output_channels, |
| std::complex<float>* 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<LappedTransform> 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<int16_t> decoded(kOutputBlockSizeSamples); |
| std::vector<float> 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<int>(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 |