aboutsummaryrefslogtreecommitdiff
path: root/modules/audio_coding
diff options
context:
space:
mode:
authorAlessio Bazzica <alessiob@webrtc.org>2020-02-14 14:34:56 +0100
committerCommit Bot <commit-bot@chromium.org>2020-02-14 14:08:21 +0000
commit08b11cafae02834dedb3230321c5f2c775febca2 (patch)
treef186776e3fa2aa772d80ca6413bf34993a7bce08 /modules/audio_coding
parent2517a47b015ddbab39cf2f1712ab897a6984e1e5 (diff)
downloadwebrtc-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.cc271
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