mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-12 21:30:45 +01:00
2nd reland of https://webrtc-review.googlesource.com/c/src/+/114883
The difference to the original is new bitexactness strings. The reason for reland is breaking downstream projects. Original CL description: Tests for multi-stream Opus. This CL (mainly) adds bit-exactness tests for multi-stream Opus. The tests are in audio_coding_unittest.cc. Some refactoring of AcmSendTestOldApi, AcmSenderBitExactnessOldApi is done to make it possible. A few checks for "channels \in {1, 2}" are replaced with "channels \in {1, 2, 4, 6, 8}" in the WebRTC Opus codec wrapper. A few other changes are made to be able to write and read multi-channel WAV files. The SDP changes are NOT included; as of this CL there is no way to set up a multi-stream opus en/de-coder from SDP strings. TBR=ossu@webrtc.org Bug: webrtc:8649 Change-Id: I6261b18c69fd666d43ab34ed8f1bc9d5cc82b21f Reviewed-on: https://webrtc-review.googlesource.com/c/123882 Reviewed-by: Alex Loiko <aleloi@webrtc.org> Commit-Queue: Alex Loiko <aleloi@webrtc.org> Cr-Commit-Position: refs/heads/master@{#26809}
This commit is contained in:
parent
caa499b207
commit
65438812ba
10 changed files with 90 additions and 26 deletions
|
@ -55,7 +55,8 @@ AudioEncoderOpusConfig& AudioEncoderOpusConfig::operator=(
|
|||
bool AudioEncoderOpusConfig::IsOk() const {
|
||||
if (frame_size_ms <= 0 || frame_size_ms % 10 != 0)
|
||||
return false;
|
||||
if (num_channels != 1 && num_channels != 2)
|
||||
if (num_channels != 1 && num_channels != 2 && num_channels != 4 &&
|
||||
num_channels != 6 && num_channels != 8)
|
||||
return false;
|
||||
if (!bitrate_bps)
|
||||
return false;
|
||||
|
|
|
@ -126,6 +126,7 @@ if (rtc_include_tests) {
|
|||
"../resources/audio_coding/speech_mono_32_48kHz.pcm",
|
||||
"../resources/audio_coding/speech_4_channels_48k_one_second.wav",
|
||||
"../resources/audio_coding/testfile32kHz.pcm",
|
||||
"../resources/audio_coding/testfile_fake_stereo_32kHz.pcm",
|
||||
"../resources/audio_coding/teststereo32kHz.pcm",
|
||||
"../resources/audio_device/audio_short16.pcm",
|
||||
"../resources/audio_device/audio_short44.pcm",
|
||||
|
|
|
@ -33,7 +33,8 @@ class AcmReceiveTestOldApi {
|
|||
enum NumOutputChannels : size_t {
|
||||
kArbitraryChannels = 0,
|
||||
kMonoOutput = 1,
|
||||
kStereoOutput = 2
|
||||
kStereoOutput = 2,
|
||||
kQuadOutput = 4
|
||||
};
|
||||
|
||||
AcmReceiveTestOldApi(PacketSource* packet_source,
|
||||
|
|
|
@ -106,13 +106,9 @@ std::unique_ptr<Packet> AcmSendTestOldApi::NextPacket() {
|
|||
// Insert audio and process until one packet is produced.
|
||||
while (clock_.TimeInMilliseconds() < test_duration_ms_) {
|
||||
clock_.AdvanceTimeMilliseconds(kBlockSizeMs);
|
||||
RTC_CHECK(audio_source_->Read(input_block_size_samples_,
|
||||
input_frame_.mutable_data()));
|
||||
if (input_frame_.num_channels_ > 1) {
|
||||
InputAudioFile::DuplicateInterleaved(
|
||||
input_frame_.data(), input_block_size_samples_,
|
||||
input_frame_.num_channels_, input_frame_.mutable_data());
|
||||
}
|
||||
RTC_CHECK(audio_source_->Read(
|
||||
input_block_size_samples_ * input_frame_.num_channels_,
|
||||
input_frame_.mutable_data()));
|
||||
data_to_send_ = false;
|
||||
RTC_CHECK_GE(acm_->Add10MsData(input_frame_), 0);
|
||||
input_frame_.timestamp_ += static_cast<uint32_t>(input_block_size_samples_);
|
||||
|
|
|
@ -477,7 +477,9 @@ int AudioCodingModuleImpl::Add10MsDataInternal(const AudioFrame& audio_frame,
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (audio_frame.num_channels_ != 1 && audio_frame.num_channels_ != 2) {
|
||||
if (audio_frame.num_channels_ != 1 && audio_frame.num_channels_ != 2 &&
|
||||
audio_frame.num_channels_ != 4 && audio_frame.num_channels_ != 6 &&
|
||||
audio_frame.num_channels_ != 8) {
|
||||
RTC_LOG(LS_ERROR) << "Cannot Add 10 ms audio, invalid number of channels.";
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "api/audio_codecs/audio_encoder.h"
|
||||
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
|
||||
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
|
||||
#include "api/audio_codecs/opus/audio_decoder_opus.h"
|
||||
#include "api/audio_codecs/opus/audio_encoder_opus.h"
|
||||
#include "modules/audio_coding/acm2/acm_receive_test.h"
|
||||
#include "modules/audio_coding/acm2/acm_send_test.h"
|
||||
|
@ -24,6 +25,8 @@
|
|||
#include "modules/audio_coding/codecs/g711/audio_decoder_pcm.h"
|
||||
#include "modules/audio_coding/codecs/g711/audio_encoder_pcm.h"
|
||||
#include "modules/audio_coding/codecs/isac/main/include/audio_encoder_isac.h"
|
||||
#include "modules/audio_coding/codecs/opus/audio_decoder_opus.h"
|
||||
#include "modules/audio_coding/codecs/opus/audio_encoder_opus.h"
|
||||
#include "modules/audio_coding/include/audio_coding_module.h"
|
||||
#include "modules/audio_coding/include/audio_coding_module_typedefs.h"
|
||||
#include "modules/audio_coding/neteq/tools/audio_checksum.h"
|
||||
|
@ -44,6 +47,7 @@
|
|||
#include "rtc_base/thread_annotations.h"
|
||||
#include "system_wrappers/include/clock.h"
|
||||
#include "system_wrappers/include/sleep.h"
|
||||
#include "test/audio_decoder_proxy_factory.h"
|
||||
#include "test/gtest.h"
|
||||
#include "test/mock_audio_decoder.h"
|
||||
#include "test/mock_audio_encoder.h"
|
||||
|
@ -935,7 +939,7 @@ class AcmReceiverBitExactnessOldApi : public ::testing::Test {
|
|||
->test_case_name() +
|
||||
"_" + ::testing::UnitTest::GetInstance()->current_test_info()->name() +
|
||||
"_output.wav";
|
||||
test::OutputWavFile output_file(output_file_name, output_freq_hz);
|
||||
test::OutputWavFile output_file(output_file_name, output_freq_hz, 1);
|
||||
test::AudioSinkFork output(&checksum, &output_file);
|
||||
|
||||
test::AcmReceiveTestOldApi test(
|
||||
|
@ -1116,15 +1120,12 @@ class AcmSenderBitExactnessOldApi : public ::testing::Test,
|
|||
|
||||
// Sets up the test::AcmSendTest object. Returns true on success, otherwise
|
||||
// false.
|
||||
bool SetUpSender() {
|
||||
const std::string input_file_name =
|
||||
webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm");
|
||||
bool SetUpSender(std::string input_file_name, int source_rate) {
|
||||
// Note that |audio_source_| will loop forever. The test duration is set
|
||||
// explicitly by |kTestDurationMs|.
|
||||
audio_source_.reset(new test::InputAudioFile(input_file_name));
|
||||
static const int kSourceRateHz = 32000;
|
||||
send_test_.reset(new test::AcmSendTestOldApi(
|
||||
audio_source_.get(), kSourceRateHz, kTestDurationMs));
|
||||
send_test_.reset(new test::AcmSendTestOldApi(audio_source_.get(),
|
||||
source_rate, kTestDurationMs));
|
||||
return send_test_.get() != NULL;
|
||||
}
|
||||
|
||||
|
@ -1157,7 +1158,11 @@ class AcmSenderBitExactnessOldApi : public ::testing::Test,
|
|||
void Run(const std::string& audio_checksum_ref,
|
||||
const std::string& payload_checksum_ref,
|
||||
int expected_packets,
|
||||
test::AcmReceiveTestOldApi::NumOutputChannels expected_channels) {
|
||||
test::AcmReceiveTestOldApi::NumOutputChannels expected_channels,
|
||||
rtc::scoped_refptr<AudioDecoderFactory> decoder_factory = nullptr) {
|
||||
if (!decoder_factory) {
|
||||
decoder_factory = CreateBuiltinAudioDecoderFactory();
|
||||
}
|
||||
// Set up the receiver used to decode the packets and verify the decoded
|
||||
// output.
|
||||
test::AudioChecksum audio_checksum;
|
||||
|
@ -1169,12 +1174,12 @@ class AcmSenderBitExactnessOldApi : public ::testing::Test,
|
|||
"_" + ::testing::UnitTest::GetInstance()->current_test_info()->name() +
|
||||
"_output.wav";
|
||||
const int kOutputFreqHz = 8000;
|
||||
test::OutputWavFile output_file(output_file_name, kOutputFreqHz);
|
||||
test::OutputWavFile output_file(output_file_name, kOutputFreqHz,
|
||||
expected_channels);
|
||||
// Have the output audio sent both to file and to the checksum calculator.
|
||||
test::AudioSinkFork output(&audio_checksum, &output_file);
|
||||
test::AcmReceiveTestOldApi receive_test(this, &output, kOutputFreqHz,
|
||||
expected_channels,
|
||||
CreateBuiltinAudioDecoderFactory());
|
||||
expected_channels, decoder_factory);
|
||||
ASSERT_NO_FATAL_FAILURE(receive_test.RegisterDefaultCodecs());
|
||||
|
||||
// This is where the actual test is executed.
|
||||
|
@ -1250,7 +1255,8 @@ class AcmSenderBitExactnessOldApi : public ::testing::Test,
|
|||
int payload_type,
|
||||
int codec_frame_size_samples,
|
||||
int codec_frame_size_rtp_timestamps) {
|
||||
ASSERT_TRUE(SetUpSender());
|
||||
ASSERT_TRUE(SetUpSender(
|
||||
channels == 1 ? kTestFileMono32kHz : kTestFileFakeStereo32kHz, 32000));
|
||||
ASSERT_TRUE(RegisterSendCodec(codec_name, codec_sample_rate_hz, channels,
|
||||
payload_type, codec_frame_size_samples,
|
||||
codec_frame_size_rtp_timestamps));
|
||||
|
@ -1259,7 +1265,7 @@ class AcmSenderBitExactnessOldApi : public ::testing::Test,
|
|||
void SetUpTestExternalEncoder(
|
||||
std::unique_ptr<AudioEncoder> external_speech_encoder,
|
||||
int payload_type) {
|
||||
ASSERT_TRUE(SetUpSender());
|
||||
ASSERT_TRUE(send_test_);
|
||||
RegisterExternalSendCodec(std::move(external_speech_encoder), payload_type);
|
||||
}
|
||||
|
||||
|
@ -1271,6 +1277,14 @@ class AcmSenderBitExactnessOldApi : public ::testing::Test,
|
|||
uint16_t last_sequence_number_;
|
||||
uint32_t last_timestamp_;
|
||||
std::unique_ptr<rtc::MessageDigest> payload_checksum_;
|
||||
const std::string kTestFileMono32kHz =
|
||||
webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm");
|
||||
const std::string kTestFileFakeStereo32kHz =
|
||||
webrtc::test::ResourcePath("audio_coding/testfile_fake_stereo_32kHz",
|
||||
"pcm");
|
||||
const std::string kTestFileQuad48kHz = webrtc::test::ResourcePath(
|
||||
"audio_coding/speech_4_channels_48k_one_second",
|
||||
"wav");
|
||||
};
|
||||
|
||||
class AcmSenderBitExactnessNewApi : public AcmSenderBitExactnessOldApi {};
|
||||
|
@ -1481,17 +1495,59 @@ TEST_F(AcmSenderBitExactnessOldApi, Opus_stereo_20ms) {
|
|||
TEST_F(AcmSenderBitExactnessNewApi, MAYBE_OpusFromFormat_stereo_20ms) {
|
||||
const auto config = AudioEncoderOpus::SdpToConfig(
|
||||
SdpAudioFormat("opus", 48000, 2, {{"stereo", "1"}}));
|
||||
ASSERT_TRUE(SetUpSender(kTestFileFakeStereo32kHz, 32000));
|
||||
ASSERT_NO_FATAL_FAILURE(SetUpTestExternalEncoder(
|
||||
AudioEncoderOpus::MakeAudioEncoder(*config, 120), 120));
|
||||
Run(audio_checksum, payload_checksum, 50,
|
||||
test::AcmReceiveTestOldApi::kStereoOutput);
|
||||
}
|
||||
|
||||
TEST_F(AcmSenderBitExactnessNewApi, OpusManyChannels) {
|
||||
constexpr int kNumChannels = 4;
|
||||
constexpr int kOpusPayloadType = 120;
|
||||
constexpr int kBitrateBps = 128000;
|
||||
|
||||
// Read a 4 channel file at 48kHz.
|
||||
ASSERT_TRUE(SetUpSender(kTestFileQuad48kHz, 48000));
|
||||
|
||||
// TODO(webrtc:8649): change to higher level
|
||||
// AudioEncoderOpus::MakeAudioEncoder once a multistream encoder can be set up
|
||||
// from SDP.
|
||||
AudioEncoderOpusConfig config = *AudioEncoderOpus::SdpToConfig(
|
||||
SdpAudioFormat("opus", 48000, 2, {{"stereo", "1"}}));
|
||||
config.num_channels = kNumChannels;
|
||||
config.bitrate_bps = kBitrateBps;
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(SetUpTestExternalEncoder(
|
||||
absl::make_unique<AudioEncoderOpusImpl>(config, kOpusPayloadType),
|
||||
kOpusPayloadType));
|
||||
|
||||
AudioDecoderOpusImpl opus_decoder(kNumChannels);
|
||||
|
||||
rtc::scoped_refptr<AudioDecoderFactory> decoder_factory =
|
||||
new rtc::RefCountedObject<test::AudioDecoderProxyFactory>(&opus_decoder);
|
||||
|
||||
// Set up an EXTERNAL DECODER to parse 4 channels.
|
||||
Run(AcmReceiverBitExactnessOldApi::PlatformChecksum( // audio checksum
|
||||
"b70470884d9a8613eff019b0d1c8876e|d0a73d377e0ca1be6b06e989e0ad2c35",
|
||||
"d0a73d377e0ca1be6b06e989e0ad2c35",
|
||||
"b45d2ce5fc4723e9eb41350af9c68f56", "android arm64 audio checksum",
|
||||
"1c9a3c9dacdd4b8fc9ff608227e531f2"),
|
||||
// payload_checksum,
|
||||
AcmReceiverBitExactnessOldApi::PlatformChecksum( // payload checksum
|
||||
"c2e7d40f8269ef754bd86d6be9623fa7|76de0f4992e3937ca60d35bbb0d308d6",
|
||||
"76de0f4992e3937ca60d35bbb0d308d6",
|
||||
"2a310aca965c16c2dfd61a9f9fc0c877", "android arm64 payload checksum",
|
||||
"2294f4b61fb8f174f5196776a0a49be7"),
|
||||
50, test::AcmReceiveTestOldApi::kQuadOutput, decoder_factory);
|
||||
}
|
||||
|
||||
TEST_F(AcmSenderBitExactnessNewApi, OpusFromFormat_stereo_20ms_voip) {
|
||||
auto config = AudioEncoderOpus::SdpToConfig(
|
||||
SdpAudioFormat("opus", 48000, 2, {{"stereo", "1"}}));
|
||||
// If not set, default will be kAudio in case of stereo.
|
||||
config->application = AudioEncoderOpusConfig::ApplicationMode::kVoip;
|
||||
ASSERT_TRUE(SetUpSender(kTestFileFakeStereo32kHz, 32000));
|
||||
ASSERT_NO_FATAL_FAILURE(SetUpTestExternalEncoder(
|
||||
AudioEncoderOpus::MakeAudioEncoder(*config, 120), 120));
|
||||
// Checksum depends on libopus being compiled with or without SSE.
|
||||
|
@ -1775,6 +1831,7 @@ TEST_F(AcmSenderBitExactnessOldApi, External_Pcmu_20ms) {
|
|||
&encoder, static_cast<AudioEncoder::EncodedInfo (AudioEncoder::*)(
|
||||
uint32_t, rtc::ArrayView<const int16_t>, rtc::Buffer*)>(
|
||||
&AudioEncoderPcmU::Encode)));
|
||||
ASSERT_TRUE(SetUpSender(kTestFileMono32kHz, 32000));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
SetUpTestExternalEncoder(std::move(mock_encoder), config.payload_type));
|
||||
Run("81a9d4c0bb72e9becc43aef124c981e9", "8f9b8750bd80fe26b6cbf6659b89f0f9",
|
||||
|
|
|
@ -71,7 +71,8 @@ class OpusFrame : public AudioDecoder::EncodedAudioFrame {
|
|||
|
||||
AudioDecoderOpusImpl::AudioDecoderOpusImpl(size_t num_channels)
|
||||
: channels_(num_channels) {
|
||||
RTC_DCHECK(num_channels == 1 || num_channels == 2);
|
||||
RTC_DCHECK(num_channels == 1 || num_channels == 2 || num_channels == 4 ||
|
||||
num_channels == 6 || num_channels == 8);
|
||||
const int error = WebRtcOpus_DecoderCreate(&dec_state_, channels_);
|
||||
RTC_DCHECK(error == 0);
|
||||
WebRtcOpus_DecoderInit(dec_state_);
|
||||
|
|
|
@ -18,9 +18,11 @@ namespace test {
|
|||
InputAudioFile::InputAudioFile(const std::string file_name, bool loop_at_end)
|
||||
: loop_at_end_(loop_at_end) {
|
||||
fp_ = fopen(file_name.c_str(), "rb");
|
||||
RTC_DCHECK(fp_) << file_name << " could not be opened.";
|
||||
}
|
||||
|
||||
InputAudioFile::~InputAudioFile() {
|
||||
RTC_DCHECK(fp_);
|
||||
fclose(fp_);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,8 +24,10 @@ class OutputWavFile : public AudioSink {
|
|||
public:
|
||||
// Creates an OutputWavFile, opening a file named |file_name| for writing.
|
||||
// The output file is a PCM encoded wav file.
|
||||
OutputWavFile(const std::string& file_name, int sample_rate_hz)
|
||||
: wav_writer_(file_name, sample_rate_hz, 1) {}
|
||||
OutputWavFile(const std::string& file_name,
|
||||
int sample_rate_hz,
|
||||
int num_channels = 1)
|
||||
: wav_writer_(file_name, sample_rate_hz, num_channels) {}
|
||||
|
||||
bool WriteArray(const int16_t* audio, size_t num_samples) override {
|
||||
wav_writer_.WriteSamples(audio, num_samples);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
4f382602b5605dbbbf78451810ce644788681262
|
Loading…
Reference in a new issue