diff --git a/api/audio_codecs/opus/audio_encoder_opus_config.cc b/api/audio_codecs/opus/audio_encoder_opus_config.cc index 301c25695d..2847ceac74 100644 --- a/api/audio_codecs/opus/audio_encoder_opus_config.cc +++ b/api/audio_codecs/opus/audio_encoder_opus_config.cc @@ -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; diff --git a/modules/BUILD.gn b/modules/BUILD.gn index 40ea128200..501ba2a449 100644 --- a/modules/BUILD.gn +++ b/modules/BUILD.gn @@ -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", diff --git a/modules/audio_coding/acm2/acm_receive_test.h b/modules/audio_coding/acm2/acm_receive_test.h index fce3d6b8b9..9d004c65e6 100644 --- a/modules/audio_coding/acm2/acm_receive_test.h +++ b/modules/audio_coding/acm2/acm_receive_test.h @@ -33,7 +33,8 @@ class AcmReceiveTestOldApi { enum NumOutputChannels : size_t { kArbitraryChannels = 0, kMonoOutput = 1, - kStereoOutput = 2 + kStereoOutput = 2, + kQuadOutput = 4 }; AcmReceiveTestOldApi(PacketSource* packet_source, diff --git a/modules/audio_coding/acm2/acm_send_test.cc b/modules/audio_coding/acm2/acm_send_test.cc index 98d673f3a1..b6110b692f 100644 --- a/modules/audio_coding/acm2/acm_send_test.cc +++ b/modules/audio_coding/acm2/acm_send_test.cc @@ -106,13 +106,9 @@ std::unique_ptr 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(input_block_size_samples_); diff --git a/modules/audio_coding/acm2/audio_coding_module.cc b/modules/audio_coding/acm2/audio_coding_module.cc index 67ef556ac8..1547b37198 100644 --- a/modules/audio_coding/acm2/audio_coding_module.cc +++ b/modules/audio_coding/acm2/audio_coding_module.cc @@ -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; } diff --git a/modules/audio_coding/acm2/audio_coding_module_unittest.cc b/modules/audio_coding/acm2/audio_coding_module_unittest.cc index c9a03a1db8..a609f980e6 100644 --- a/modules/audio_coding/acm2/audio_coding_module_unittest.cc +++ b/modules/audio_coding/acm2/audio_coding_module_unittest.cc @@ -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 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 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 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(config, kOpusPayloadType), + kOpusPayloadType)); + + AudioDecoderOpusImpl opus_decoder(kNumChannels); + + rtc::scoped_refptr decoder_factory = + new rtc::RefCountedObject(&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, rtc::Buffer*)>( &AudioEncoderPcmU::Encode))); + ASSERT_TRUE(SetUpSender(kTestFileMono32kHz, 32000)); ASSERT_NO_FATAL_FAILURE( SetUpTestExternalEncoder(std::move(mock_encoder), config.payload_type)); Run("81a9d4c0bb72e9becc43aef124c981e9", "8f9b8750bd80fe26b6cbf6659b89f0f9", diff --git a/modules/audio_coding/codecs/opus/audio_decoder_opus.cc b/modules/audio_coding/codecs/opus/audio_decoder_opus.cc index 1accfe42e9..b6eada9d7c 100644 --- a/modules/audio_coding/codecs/opus/audio_decoder_opus.cc +++ b/modules/audio_coding/codecs/opus/audio_decoder_opus.cc @@ -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_); diff --git a/modules/audio_coding/neteq/tools/input_audio_file.cc b/modules/audio_coding/neteq/tools/input_audio_file.cc index 6d11064724..d5e28629b4 100644 --- a/modules/audio_coding/neteq/tools/input_audio_file.cc +++ b/modules/audio_coding/neteq/tools/input_audio_file.cc @@ -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_); } diff --git a/modules/audio_coding/neteq/tools/output_wav_file.h b/modules/audio_coding/neteq/tools/output_wav_file.h index 3ffcfc6d62..6982a76a39 100644 --- a/modules/audio_coding/neteq/tools/output_wav_file.h +++ b/modules/audio_coding/neteq/tools/output_wav_file.h @@ -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); diff --git a/resources/audio_coding/testfile_fake_stereo_32kHz.pcm.sha1 b/resources/audio_coding/testfile_fake_stereo_32kHz.pcm.sha1 new file mode 100644 index 0000000000..004f8bb7f9 --- /dev/null +++ b/resources/audio_coding/testfile_fake_stereo_32kHz.pcm.sha1 @@ -0,0 +1 @@ +4f382602b5605dbbbf78451810ce644788681262 \ No newline at end of file