iSAC API wrapper unit test fix

Use speech content instead of white noise and enable target vs measured
bitrate tests.

Bug: webrtc:11360
Change-Id: If8c8e73f943eda14efeb22ba406c7a1bed7d32b4
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/168660
Commit-Queue: Alessio Bazzica <alessiob@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30630}
This commit is contained in:
Alessio Bazzica 2020-02-27 13:46:51 +01:00 committed by Commit Bot
parent 4a6f81829b
commit a7382f7879
6 changed files with 53 additions and 39 deletions

View File

@ -57,6 +57,7 @@ rtc_source_set("module_fec_api") {
if (rtc_include_tests) { if (rtc_include_tests) {
modules_tests_resources = [ modules_tests_resources = [
"../resources/audio_coding/testfile16kHz.pcm",
"../resources/audio_coding/testfile32kHz.pcm", "../resources/audio_coding/testfile32kHz.pcm",
"../resources/audio_coding/teststereo32kHz.pcm", "../resources/audio_coding/teststereo32kHz.pcm",
"../resources/foreman_cif.yuv", "../resources/foreman_cif.yuv",
@ -111,6 +112,7 @@ if (rtc_include_tests) {
"../resources/audio_coding/speech_4_channels_48k_one_second.wav", "../resources/audio_coding/speech_4_channels_48k_one_second.wav",
"../resources/audio_coding/speech_mono_16kHz.pcm", "../resources/audio_coding/speech_mono_16kHz.pcm",
"../resources/audio_coding/speech_mono_32_48kHz.pcm", "../resources/audio_coding/speech_mono_32_48kHz.pcm",
"../resources/audio_coding/testfile16kHz.pcm",
"../resources/audio_coding/testfile32kHz.pcm", "../resources/audio_coding/testfile32kHz.pcm",
"../resources/audio_coding/testfile_fake_stereo_32kHz.pcm", "../resources/audio_coding/testfile_fake_stereo_32kHz.pcm",
"../resources/audio_coding/teststereo32kHz.pcm", "../resources/audio_coding/teststereo32kHz.pcm",

View File

@ -28,7 +28,6 @@ class AudioEncoderIsacT final : public AudioEncoder {
// - 32000 Hz, 30 ms, 10000-56000 bps (if T has super-wideband support) // - 32000 Hz, 30 ms, 10000-56000 bps (if T has super-wideband support)
struct Config { struct Config {
bool IsOk() const; bool IsOk() const;
int payload_type = 103; int payload_type = 103;
int sample_rate_hz = 16000; int sample_rate_hz = 16000;
int frame_size_ms = 30; int frame_size_ms = 30;

View File

@ -126,7 +126,7 @@ void AudioEncoderIsacT<T>::RecreateEncoderInstance(const Config& config) {
if (isac_state_) if (isac_state_)
RTC_CHECK_EQ(0, T::Free(isac_state_)); RTC_CHECK_EQ(0, T::Free(isac_state_));
RTC_CHECK_EQ(0, T::Create(&isac_state_)); RTC_CHECK_EQ(0, T::Create(&isac_state_));
RTC_CHECK_EQ(0, T::EncoderInit(isac_state_, 1)); RTC_CHECK_EQ(0, T::EncoderInit(isac_state_, /*coding_mode=*/1));
RTC_CHECK_EQ(0, T::SetEncSampRate(isac_state_, config.sample_rate_hz)); RTC_CHECK_EQ(0, T::SetEncSampRate(isac_state_, config.sample_rate_hz));
const int bit_rate = config.bit_rate == 0 ? kDefaultBitRate : config.bit_rate; const int bit_rate = config.bit_rate == 0 ? kDefaultBitRate : config.bit_rate;
RTC_CHECK_EQ(0, T::Control(isac_state_, bit_rate, config.frame_size_ms)); RTC_CHECK_EQ(0, T::Control(isac_state_, bit_rate, config.frame_size_ms));

View File

@ -9,7 +9,7 @@
*/ */
#include <array> #include <array>
#include <limits> #include <memory>
#include <vector> #include <vector>
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
@ -18,9 +18,11 @@
#include "api/audio_codecs/isac/audio_decoder_isac_float.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_fix.h"
#include "api/audio_codecs/isac/audio_encoder_isac_float.h" #include "api/audio_codecs/isac/audio_encoder_isac_float.h"
#include "rtc_base/random.h" #include "modules/audio_coding/test/PCMFile.h"
#include "rtc_base/checks.h"
#include "rtc_base/strings/string_builder.h" #include "rtc_base/strings/string_builder.h"
#include "test/gtest.h" #include "test/gtest.h"
#include "test/testsupport/file_utils.h"
namespace webrtc { namespace webrtc {
namespace { namespace {
@ -38,15 +40,31 @@ absl::string_view IsacImplToString(IsacImpl impl) {
} }
} }
std::vector<int16_t> GetRandomSamplesVector(size_t size) { std::unique_ptr<PCMFile> GetPcmTestFileReader(int sample_rate_hz) {
constexpr int32_t kMin = std::numeric_limits<int16_t>::min(); std::string filename;
constexpr int32_t kMax = std::numeric_limits<int16_t>::max(); switch (sample_rate_hz) {
std::vector<int16_t> v(size); case 16000:
Random gen(/*seed=*/42); filename = test::ResourcePath("audio_coding/testfile16kHz", "pcm");
for (auto& x : v) { break;
x = static_cast<int16_t>(gen.Rand(kMin, kMax)); case 32000:
filename = test::ResourcePath("audio_coding/testfile32kHz", "pcm");
break;
default:
RTC_NOTREACHED() << "No test file available for " << sample_rate_hz
<< " Hz.";
} }
return v; auto pcm_file = std::make_unique<PCMFile>();
pcm_file->ReadStereo(false);
pcm_file->Open(filename, sample_rate_hz, "rb", /*auto_rewind=*/true);
pcm_file->FastForward(/*num_10ms_blocks=*/100); // Skip initial silence.
RTC_CHECK(!pcm_file->EndOfFile());
return pcm_file;
}
// Returns a view to the interleaved samples of an AudioFrame object.
rtc::ArrayView<const int16_t> AudioFrameToView(const AudioFrame& audio_frame) {
return {audio_frame.data(),
audio_frame.samples_per_channel() * audio_frame.num_channels()};
} }
std::unique_ptr<AudioEncoder> CreateEncoder(IsacImpl impl, std::unique_ptr<AudioEncoder> CreateEncoder(IsacImpl impl,
@ -119,6 +137,7 @@ TEST_P(EncoderTest, TestConfig) {
// checks that the number of produces bytes in the first case is less than that // checks that the number of produces bytes in the first case is less than that
// of the second case. // of the second case.
TEST_P(EncoderTest, TestDifferentBitrates) { TEST_P(EncoderTest, TestDifferentBitrates) {
auto pcm_file = GetPcmTestFileReader(GetSampleRateHz());
constexpr int kLowBps = 20000; constexpr int kLowBps = 20000;
constexpr int kHighBps = 25000; constexpr int kHighBps = 25000;
auto encoder_low = CreateEncoder(GetIsacImpl(), GetSampleRateHz(), auto encoder_low = CreateEncoder(GetIsacImpl(), GetSampleRateHz(),
@ -127,39 +146,39 @@ TEST_P(EncoderTest, TestDifferentBitrates) {
GetFrameSizeMs(), kHighBps); GetFrameSizeMs(), kHighBps);
int num_bytes_low = 0; int num_bytes_low = 0;
int num_bytes_high = 0; int num_bytes_high = 0;
const auto in = GetRandomSamplesVector(
/*size=*/rtc::CheckedDivExact(GetSampleRateHz(), 100));
constexpr int kNumFrames = 12; constexpr int kNumFrames = 12;
for (int i = 0; i < kNumFrames; ++i) { for (int i = 0; i < kNumFrames; ++i) {
AudioFrame in;
pcm_file->Read10MsData(in);
rtc::Buffer low, high; rtc::Buffer low, high;
encoder_low->Encode(/*rtp_timestamp=*/0, in, &low); encoder_low->Encode(/*rtp_timestamp=*/0, AudioFrameToView(in), &low);
encoder_high->Encode(/*rtp_timestamp=*/0, in, &high); encoder_high->Encode(/*rtp_timestamp=*/0, AudioFrameToView(in), &high);
num_bytes_low += low.size(); num_bytes_low += low.size();
num_bytes_high += high.size(); num_bytes_high += high.size();
} }
EXPECT_LT(num_bytes_low, num_bytes_high); EXPECT_LT(num_bytes_low, num_bytes_high);
} }
// Checks that the target and the measured bitrates are within tolerance. // Checks that, given a target bitrate, the encoder does not overshoot too much.
// TODO(webrtc:11360): Add CBR flag to the config and re-enable test with CBR. TEST_P(EncoderTest, DoNotOvershootTargetBitrate) {
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}) { for (int bitrate_bps : {10000, 15000, 20000, 26000, 32000}) {
SCOPED_TRACE(bitrate_bps); SCOPED_TRACE(bitrate_bps);
auto pcm_file = GetPcmTestFileReader(GetSampleRateHz());
auto e = CreateEncoder(GetIsacImpl(), GetSampleRateHz(), GetFrameSizeMs(), auto e = CreateEncoder(GetIsacImpl(), GetSampleRateHz(), GetFrameSizeMs(),
bitrate_bps); bitrate_bps);
int num_bytes = 0; int num_bytes = 0;
constexpr int kNumFrames = 60; constexpr int kNumFrames = 200; // 2 seconds.
for (int i = 0; i < kNumFrames; ++i) { for (int i = 0; i < kNumFrames; ++i) {
AudioFrame in;
pcm_file->Read10MsData(in);
rtc::Buffer encoded; rtc::Buffer encoded;
e->Encode(/*rtp_timestamp=*/0, in, &encoded); e->Encode(/*rtp_timestamp=*/0, AudioFrameToView(in), &encoded);
num_bytes += encoded.size(); num_bytes += encoded.size();
} }
// Inverse of the duration of |kNumFrames| 10 ms frames (unit: seconds^-1). // Inverse of the duration of |kNumFrames| 10 ms frames (unit: seconds^-1).
constexpr float kAudioDurationInv = 100.f / kNumFrames; constexpr float kAudioDurationInv = 100.f / kNumFrames;
const int measured_bitrate_bps = 8 * num_bytes * kAudioDurationInv; const int measured_bitrate_bps = 8 * num_bytes * kAudioDurationInv;
EXPECT_NEAR(bitrate_bps, measured_bitrate_bps, 1000); // Max 1 kbps. EXPECT_LT(measured_bitrate_bps, bitrate_bps + 2000); // Max 2 kbps extra.
} }
} }
@ -227,27 +246,19 @@ struct EncoderDecoderPairTestParams {
class EncoderDecoderPairTest class EncoderDecoderPairTest
: public testing::TestWithParam<EncoderDecoderPairTestParams> { : public testing::TestWithParam<EncoderDecoderPairTestParams> {
protected: protected:
EncoderDecoderPairTest() EncoderDecoderPairTest() = default;
: input_frame_(GetRandomSamplesVector(GetInputFrameSize())) {}
rtc::ArrayView<const int16_t> GetInputFrame() { return input_frame_; }
int GetSampleRateHz() const { return GetParam().sample_rate_hz; } int GetSampleRateHz() const { return GetParam().sample_rate_hz; }
int GetEncoderFrameSizeMs() const { return GetParam().frame_size_ms; } int GetEncoderFrameSizeMs() const { return GetParam().frame_size_ms; }
IsacImpl GetEncoderIsacImpl() const { return GetParam().encoder_impl; } IsacImpl GetEncoderIsacImpl() const { return GetParam().encoder_impl; }
IsacImpl GetDecoderIsacImpl() const { return GetParam().decoder_impl; } IsacImpl GetDecoderIsacImpl() const { return GetParam().decoder_impl; }
int GetEncoderFrameSize() const { int GetEncoderFrameSize() const {
return GetEncoderFrameSizeMs() * GetSampleRateHz() / 1000; return GetEncoderFrameSizeMs() * GetSampleRateHz() / 1000;
} }
private:
const std::vector<int16_t> input_frame_;
int GetInputFrameSize() const {
return rtc::CheckedDivExact(GetParam().sample_rate_hz, 100); // 10 ms.
}
}; };
// Checks that the number of encoded and decoded samples match. // Checks that the number of encoded and decoded samples match.
TEST_P(EncoderDecoderPairTest, EncodeDecode) { TEST_P(EncoderDecoderPairTest, EncodeDecode) {
auto pcm_file = GetPcmTestFileReader(GetSampleRateHz());
auto encoder = CreateEncoder(GetEncoderIsacImpl(), GetSampleRateHz(), auto encoder = CreateEncoder(GetEncoderIsacImpl(), GetSampleRateHz(),
GetEncoderFrameSizeMs(), /*bitrate_bps=*/20000); GetEncoderFrameSizeMs(), /*bitrate_bps=*/20000);
auto decoder = CreateDecoder(GetDecoderIsacImpl(), GetSampleRateHz()); auto decoder = CreateDecoder(GetDecoderIsacImpl(), GetSampleRateHz());
@ -257,10 +268,11 @@ TEST_P(EncoderDecoderPairTest, EncodeDecode) {
size_t num_decoded_samples = 0; size_t num_decoded_samples = 0;
constexpr int kNumFrames = 12; constexpr int kNumFrames = 12;
for (int i = 0; i < kNumFrames; ++i) { for (int i = 0; i < kNumFrames; ++i) {
AudioFrame in;
pcm_file->Read10MsData(in);
rtc::Buffer encoded; rtc::Buffer encoded;
auto in = GetInputFrame(); encoder->Encode(/*rtp_timestamp=*/0, AudioFrameToView(in), &encoded);
encoder->Encode(/*rtp_timestamp=*/0, in, &encoded); num_encoded_samples += in.samples_per_channel();
num_encoded_samples += in.size();
if (encoded.empty()) { if (encoded.empty()) {
continue; continue;
} }

View File

@ -304,7 +304,7 @@ typedef struct {
int16_t maxRateInBytes; int16_t maxRateInBytes;
/*--- /*---
If set to 1 iSAC will not addapt the frame-size, if used in If set to 1 iSAC will not adapt the frame-size, if used in
channel-adaptive mode. The initial value will be used for all rates. channel-adaptive mode. The initial value will be used for all rates.
---*/ ---*/
int16_t enforceFrameSize; int16_t enforceFrameSize;
@ -312,7 +312,7 @@ typedef struct {
/*----- /*-----
This records the BWE index the encoder injected into the bit-stream. This records the BWE index the encoder injected into the bit-stream.
It will be used in RCU. The same BWE index of main payload will be in It will be used in RCU. The same BWE index of main payload will be in
the redundant payload. We can not retrive it from BWE because it is the redundant payload. We can not retrieve it from BWE because it is
a recursive procedure (WebRtcIsac_GetDownlinkBwJitIndexImpl) and has to be a recursive procedure (WebRtcIsac_GetDownlinkBwJitIndexImpl) and has to be
called only once per each encode. called only once per each encode.
-----*/ -----*/

View File

@ -0,0 +1 @@
0d2702e5c350c2a4ad3a641c4d96271e8aa12e6c