diff options
author | Alessio Bazzica <alessiob@webrtc.org> | 2020-02-14 14:34:56 +0100 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-02-14 14:08:21 +0000 |
commit | 08b11cafae02834dedb3230321c5f2c775febca2 (patch) | |
tree | f186776e3fa2aa772d80ca6413bf34993a7bce08 /modules/audio_coding | |
parent | 2517a47b015ddbab39cf2f1712ab897a6984e1e5 (diff) | |
download | webrtc-08b11cafae02834dedb3230321c5f2c775febca2.tar.gz |
iSAC config: target bitrate exposed for fixed impl
It is now possible to set the target bitrate for iSAC for the fixed
point implementation. Unit tests added.
Bug: webrtc:11360
Change-Id: I60225d4ca1363cdacf18931e7cf412c5aec8d8fe
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/168529
Commit-Queue: Alessio Bazzica <alessiob@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30526}
Diffstat (limited to 'modules/audio_coding')
-rw-r--r-- | modules/audio_coding/codecs/isac/isac_webrtc_api_test.cc | 271 |
1 files changed, 216 insertions, 55 deletions
diff --git a/modules/audio_coding/codecs/isac/isac_webrtc_api_test.cc b/modules/audio_coding/codecs/isac/isac_webrtc_api_test.cc index ac83861c87..3a2d6f498e 100644 --- a/modules/audio_coding/codecs/isac/isac_webrtc_api_test.cc +++ b/modules/audio_coding/codecs/isac/isac_webrtc_api_test.cc @@ -12,22 +12,32 @@ #include <limits> #include <vector> +#include "absl/strings/string_view.h" #include "api/array_view.h" #include "api/audio_codecs/isac/audio_decoder_isac_fix.h" #include "api/audio_codecs/isac/audio_decoder_isac_float.h" #include "api/audio_codecs/isac/audio_encoder_isac_fix.h" #include "api/audio_codecs/isac/audio_encoder_isac_float.h" #include "rtc_base/random.h" +#include "rtc_base/strings/string_builder.h" #include "test/gtest.h" namespace webrtc { namespace { constexpr int kPayloadType = 42; -constexpr int kBitrateBps = 20000; enum class IsacImpl { kFixed, kFloat }; +absl::string_view IsacImplToString(IsacImpl impl) { + switch (impl) { + case IsacImpl::kFixed: + return "fixed"; + case IsacImpl::kFloat: + return "float"; + } +} + std::vector<int16_t> GetRandomSamplesVector(size_t size) { constexpr int32_t kMin = std::numeric_limits<int16_t>::min(); constexpr int32_t kMax = std::numeric_limits<int16_t>::max(); @@ -39,67 +49,210 @@ std::vector<int16_t> GetRandomSamplesVector(size_t size) { return v; } -class IsacApiTest - : public testing::TestWithParam<std::tuple<int, int, IsacImpl, IsacImpl>> { +std::unique_ptr<AudioEncoder> CreateEncoder(IsacImpl impl, + int sample_rate_hz, + int frame_size_ms, + int bitrate_bps) { + RTC_CHECK(sample_rate_hz == 16000 || sample_rate_hz == 32000); + RTC_CHECK(frame_size_ms == 30 || frame_size_ms == 60); + RTC_CHECK_GT(bitrate_bps, 0); + switch (impl) { + case IsacImpl::kFixed: { + AudioEncoderIsacFix::Config config; + config.bit_rate = bitrate_bps; + config.frame_size_ms = frame_size_ms; + RTC_CHECK_EQ(16000, sample_rate_hz); + return AudioEncoderIsacFix::MakeAudioEncoder(config, kPayloadType); + } + case IsacImpl::kFloat: { + AudioEncoderIsacFloat::Config config; + config.bit_rate = bitrate_bps; + config.frame_size_ms = frame_size_ms; + config.sample_rate_hz = sample_rate_hz; + return AudioEncoderIsacFloat::MakeAudioEncoder(config, kPayloadType); + } + } +} + +std::unique_ptr<AudioDecoder> CreateDecoder(IsacImpl impl, int sample_rate_hz) { + RTC_CHECK(sample_rate_hz == 16000 || sample_rate_hz == 32000); + switch (impl) { + case IsacImpl::kFixed: { + webrtc::AudioDecoderIsacFix::Config config; + RTC_CHECK_EQ(16000, sample_rate_hz); + return webrtc::AudioDecoderIsacFix::MakeAudioDecoder(config); + } + case IsacImpl::kFloat: { + webrtc::AudioDecoderIsacFloat::Config config; + config.sample_rate_hz = sample_rate_hz; + return webrtc::AudioDecoderIsacFloat::MakeAudioDecoder(config); + } + } +} + +struct EncoderTestParams { + IsacImpl impl; + int sample_rate_hz; + int frame_size_ms; +}; + +class EncoderTest : public testing::TestWithParam<EncoderTestParams> { protected: - IsacApiTest() : input_frame_(GetRandomSamplesVector(GetInputFrameLength())) {} - rtc::ArrayView<const int16_t> GetInputFrame() { return input_frame_; } - int GetSampleRateHz() const { return std::get<0>(GetParam()); } - int GetEncoderFrameLenght() const { - return GetEncoderFrameLenghtMs() * GetSampleRateHz() / 1000; + EncoderTest() = default; + IsacImpl GetIsacImpl() const { return GetParam().impl; } + int GetSampleRateHz() const { return GetParam().sample_rate_hz; } + int GetFrameSizeMs() const { return GetParam().frame_size_ms; } +}; + +TEST_P(EncoderTest, TestConfig) { + for (int bitrate_bps : {10000, 21000, 32000}) { + SCOPED_TRACE(bitrate_bps); + auto encoder = CreateEncoder(GetIsacImpl(), GetSampleRateHz(), + GetFrameSizeMs(), bitrate_bps); + EXPECT_EQ(GetSampleRateHz(), encoder->SampleRateHz()); + EXPECT_EQ(size_t{1}, encoder->NumChannels()); + EXPECT_EQ(bitrate_bps, encoder->GetTargetBitrate()); } - std::unique_ptr<AudioEncoder> CreateEncoder() const { - switch (GetEncoderIsacImpl()) { - case IsacImpl::kFixed: { - AudioEncoderIsacFix::Config config; - config.frame_size_ms = GetEncoderFrameLenghtMs(); - RTC_CHECK_EQ(16000, GetSampleRateHz()); - return AudioEncoderIsacFix::MakeAudioEncoder(config, kPayloadType); - } - case IsacImpl::kFloat: { - AudioEncoderIsacFloat::Config config; - config.bit_rate = kBitrateBps; - config.frame_size_ms = GetEncoderFrameLenghtMs(); - config.sample_rate_hz = GetSampleRateHz(); - return AudioEncoderIsacFloat::MakeAudioEncoder(config, kPayloadType); - } +} + +// Encodes an input audio sequence with a low and a high target bitrate and +// checks that the number of produces bytes in the first case is less than that +// of the second case. +TEST_P(EncoderTest, TestDifferentBitrates) { + constexpr int kLowBps = 20000; + constexpr int kHighBps = 25000; + auto encoder_low = CreateEncoder(GetIsacImpl(), GetSampleRateHz(), + GetFrameSizeMs(), kLowBps); + auto encoder_high = CreateEncoder(GetIsacImpl(), GetSampleRateHz(), + GetFrameSizeMs(), kHighBps); + int num_bytes_low = 0; + int num_bytes_high = 0; + const auto in = GetRandomSamplesVector( + /*size=*/rtc::CheckedDivExact(GetSampleRateHz(), 100)); + constexpr int kNumFrames = 12; + for (int i = 0; i < kNumFrames; ++i) { + rtc::Buffer low, high; + encoder_low->Encode(/*rtp_timestamp=*/0, in, &low); + encoder_high->Encode(/*rtp_timestamp=*/0, in, &high); + num_bytes_low += low.size(); + num_bytes_high += high.size(); + } + EXPECT_LT(num_bytes_low, num_bytes_high); +} + +// Checks that the target and the measured bitrates are within tolerance. +// TODO(webrtc:11360): Add CBR flag to the config and re-enable test with CBR. +TEST_P(EncoderTest, DISABLED_TestBitrateNearTarget) { + const auto in = GetRandomSamplesVector( + /*size=*/rtc::CheckedDivExact(GetSampleRateHz(), 100)); // 10 ms. + for (int bitrate_bps : {10000, 15000, 20000, 26000, 32000}) { + SCOPED_TRACE(bitrate_bps); + auto e = CreateEncoder(GetIsacImpl(), GetSampleRateHz(), GetFrameSizeMs(), + bitrate_bps); + int num_bytes = 0; + constexpr int kNumFrames = 60; + for (int i = 0; i < kNumFrames; ++i) { + rtc::Buffer encoded; + e->Encode(/*rtp_timestamp=*/0, in, &encoded); + num_bytes += encoded.size(); } + // Inverse of the duration of |kNumFrames| 10 ms frames (unit: seconds^-1). + constexpr float kAudioDurationInv = 100.f / kNumFrames; + const int measured_bitrate_bps = 8 * num_bytes * kAudioDurationInv; + EXPECT_NEAR(bitrate_bps, measured_bitrate_bps, 1000); // Max 1 kbps. } - std::unique_ptr<AudioDecoder> CreateDecoder() const { - switch (GetDecoderIsacImpl()) { - case IsacImpl::kFixed: { - webrtc::AudioDecoderIsacFix::Config config; - RTC_CHECK_EQ(16000, GetSampleRateHz()); - return webrtc::AudioDecoderIsacFix::MakeAudioDecoder(config); - } - case IsacImpl::kFloat: { - webrtc::AudioDecoderIsacFloat::Config config; - config.sample_rate_hz = GetSampleRateHz(); - return webrtc::AudioDecoderIsacFloat::MakeAudioDecoder(config); +} + +// Creates tests for different encoder configurations and implementations. +INSTANTIATE_TEST_SUITE_P( + IsacApiTest, + EncoderTest, + ::testing::ValuesIn([] { + std::vector<EncoderTestParams> cases; + for (IsacImpl impl : {IsacImpl::kFloat, IsacImpl::kFixed}) { + for (int frame_size_ms : {30, 60}) { + cases.push_back({impl, 16000, frame_size_ms}); + } } - } + cases.push_back({IsacImpl::kFloat, 32000, 30}); + return cases; + }()), + [](const ::testing::TestParamInfo<EncoderTestParams>& info) { + rtc::StringBuilder b; + const auto& p = info.param; + b << IsacImplToString(p.impl) << "_" << p.sample_rate_hz << "_" + << p.frame_size_ms; + return b.Release(); + }); + +struct DecoderTestParams { + IsacImpl impl; + int sample_rate_hz; +}; + +class DecoderTest : public testing::TestWithParam<DecoderTestParams> { + protected: + DecoderTest() = default; + IsacImpl GetIsacImpl() const { return GetParam().impl; } + int GetSampleRateHz() const { return GetParam().sample_rate_hz; } +}; + +TEST_P(DecoderTest, TestConfig) { + auto decoder = CreateDecoder(GetIsacImpl(), GetSampleRateHz()); + EXPECT_EQ(GetSampleRateHz(), decoder->SampleRateHz()); + EXPECT_EQ(size_t{1}, decoder->Channels()); +} + +// Creates tests for different decoder configurations and implementations. +INSTANTIATE_TEST_SUITE_P( + IsacApiTest, + DecoderTest, + ::testing::ValuesIn({DecoderTestParams{IsacImpl::kFixed, 16000}, + DecoderTestParams{IsacImpl::kFloat, 16000}, + DecoderTestParams{IsacImpl::kFloat, 32000}}), + [](const ::testing::TestParamInfo<DecoderTestParams>& info) { + const auto& p = info.param; + return (rtc::StringBuilder() + << IsacImplToString(p.impl) << "_" << p.sample_rate_hz) + .Release(); + }); + +struct EncoderDecoderPairTestParams { + int sample_rate_hz; + int frame_size_ms; + IsacImpl encoder_impl; + IsacImpl decoder_impl; +}; + +class EncoderDecoderPairTest + : public testing::TestWithParam<EncoderDecoderPairTestParams> { + protected: + EncoderDecoderPairTest() + : input_frame_(GetRandomSamplesVector(GetInputFrameSize())) {} + rtc::ArrayView<const int16_t> GetInputFrame() { return input_frame_; } + int GetSampleRateHz() const { return GetParam().sample_rate_hz; } + int GetEncoderFrameSizeMs() const { return GetParam().frame_size_ms; } + IsacImpl GetEncoderIsacImpl() const { return GetParam().encoder_impl; } + IsacImpl GetDecoderIsacImpl() const { return GetParam().decoder_impl; } + + int GetEncoderFrameSize() const { + return GetEncoderFrameSizeMs() * GetSampleRateHz() / 1000; } private: const std::vector<int16_t> input_frame_; - int GetInputFrameLength() const { - return rtc::CheckedDivExact(std::get<0>(GetParam()), 100); // 10 ms. - } - int GetEncoderFrameLenghtMs() const { - int frame_size_ms = std::get<1>(GetParam()); - RTC_CHECK(frame_size_ms == 30 || frame_size_ms == 60); - return frame_size_ms; + int GetInputFrameSize() const { + return rtc::CheckedDivExact(GetParam().sample_rate_hz, 100); // 10 ms. } - IsacImpl GetEncoderIsacImpl() const { return std::get<2>(GetParam()); } - IsacImpl GetDecoderIsacImpl() const { return std::get<3>(GetParam()); } }; // Checks that the number of encoded and decoded samples match. -TEST_P(IsacApiTest, EncodeDecode) { - auto encoder = CreateEncoder(); - auto decoder = CreateDecoder(); - const int encoder_frame_length = GetEncoderFrameLenght(); - std::vector<int16_t> out(encoder_frame_length); +TEST_P(EncoderDecoderPairTest, EncodeDecode) { + auto encoder = CreateEncoder(GetEncoderIsacImpl(), GetSampleRateHz(), + GetEncoderFrameSizeMs(), /*bitrate_bps=*/20000); + auto decoder = CreateDecoder(GetDecoderIsacImpl(), GetSampleRateHz()); + const int encoder_frame_size = GetEncoderFrameSize(); + std::vector<int16_t> out(encoder_frame_size); size_t num_encoded_samples = 0; size_t num_decoded_samples = 0; constexpr int kNumFrames = 12; @@ -123,23 +276,31 @@ TEST_P(IsacApiTest, EncodeDecode) { EXPECT_EQ(num_encoded_samples, num_decoded_samples); } -// Creates tests for different encoder frame lengths and different +// Creates tests for different encoder frame sizes and different // encoder/decoder implementations. INSTANTIATE_TEST_SUITE_P( - AllTest, IsacApiTest, + EncoderDecoderPairTest, ::testing::ValuesIn([] { - std::vector<std::tuple<int, int, IsacImpl, IsacImpl>> cases; - for (int frame_length_ms : {30, 60}) { + std::vector<EncoderDecoderPairTestParams> cases; + for (int frame_size_ms : {30, 60}) { for (IsacImpl enc : {IsacImpl::kFloat, IsacImpl::kFixed}) { for (IsacImpl dec : {IsacImpl::kFloat, IsacImpl::kFixed}) { - cases.push_back({16000, frame_length_ms, enc, dec}); + cases.push_back({16000, frame_size_ms, enc, dec}); } } } cases.push_back({32000, 30, IsacImpl::kFloat, IsacImpl::kFloat}); return cases; - }())); + }()), + [](const ::testing::TestParamInfo<EncoderDecoderPairTestParams>& info) { + rtc::StringBuilder b; + const auto& p = info.param; + b << p.sample_rate_hz << "_" << p.frame_size_ms << "_" + << IsacImplToString(p.encoder_impl) << "_" + << IsacImplToString(p.decoder_impl); + return b.Release(); + }); } // namespace } // namespace webrtc |