diff --git a/webrtc/modules/audio_coding/codecs/mock/mock_audio_encoder.h b/webrtc/modules/audio_coding/codecs/mock/mock_audio_encoder.h index 25fd7a837..786c83cd6 100644 --- a/webrtc/modules/audio_coding/codecs/mock/mock_audio_encoder.h +++ b/webrtc/modules/audio_coding/codecs/mock/mock_audio_encoder.h @@ -36,6 +36,31 @@ class MockAudioEncoder : public AudioEncoder { uint8_t* encoded)); }; +class MockAudioEncoderMutable : public AudioEncoderMutable { + public: + MOCK_CONST_METHOD0(SampleRateHz, int()); + MOCK_CONST_METHOD0(NumChannels, int()); + MOCK_CONST_METHOD0(MaxEncodedBytes, size_t()); + MOCK_CONST_METHOD0(Num10MsFramesInNextPacket, int()); + MOCK_CONST_METHOD0(Max10MsFramesInAPacket, int()); + MOCK_METHOD1(SetTargetBitrate, void(int)); + MOCK_METHOD1(SetProjectedPacketLossRate, void(double)); + // Note, we explicitly chose not to create a mock for the Encode method. + MOCK_METHOD4(EncodeInternal, + EncodedInfo(uint32_t timestamp, + const int16_t* audio, + size_t max_encoded_bytes, + uint8_t* encoded)); + + MOCK_METHOD0(Reset, void()); + MOCK_METHOD1(SetFec, bool(bool enable)); + MOCK_METHOD1(SetDtx, bool(bool enable)); + MOCK_METHOD1(SetApplication, bool(Application application)); + MOCK_METHOD1(SetMaxPayloadSize, void(int max_payload_size_bytes)); + MOCK_METHOD1(SetMaxRate, void(int max_rate_bps)); + MOCK_METHOD1(SetMaxPlaybackRate, bool(int frequency_hz)); +}; + } // namespace webrtc #endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_MOCK_MOCK_AUDIO_ENCODER_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.cc b/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.cc index 41e0feb5f..d0c031e89 100644 --- a/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.cc +++ b/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.cc @@ -16,6 +16,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/base/checks.h" +#include "webrtc/modules/audio_coding/codecs/audio_encoder.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" #include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" #include "webrtc/modules/audio_coding/neteq/tools/packet.h" @@ -50,18 +51,27 @@ bool AcmSendTestOldApi::RegisterCodec(const char* payload_name, int channels, int payload_type, int frame_size_samples) { - CHECK_EQ(0, - AudioCodingModule::Codec( - payload_name, &codec_, sampling_freq_hz, channels)); - codec_.pltype = payload_type; - codec_.pacsize = frame_size_samples; - codec_registered_ = (acm_->RegisterSendCodec(codec_) == 0); + CodecInst codec; + CHECK_EQ(0, AudioCodingModule::Codec(payload_name, &codec, sampling_freq_hz, + channels)); + codec.pltype = payload_type; + codec.pacsize = frame_size_samples; + codec_registered_ = (acm_->RegisterSendCodec(codec) == 0); input_frame_.num_channels_ = channels; assert(input_block_size_samples_ * input_frame_.num_channels_ <= AudioFrame::kMaxDataSizeSamples); return codec_registered_; } +bool AcmSendTestOldApi::RegisterExternalCodec( + AudioEncoderMutable* external_speech_encoder) { + acm_->RegisterExternalSendCodec(external_speech_encoder); + input_frame_.num_channels_ = external_speech_encoder->NumChannels(); + assert(input_block_size_samples_ * input_frame_.num_channels_ <= + AudioFrame::kMaxDataSizeSamples); + return codec_registered_ = true; +} + Packet* AcmSendTestOldApi::NextPacket() { assert(codec_registered_); if (filter_.test(static_cast(payload_type_))) { diff --git a/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.h b/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.h index 52cb415eb..8cdc29898 100644 --- a/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.h +++ b/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.h @@ -20,6 +20,7 @@ #include "webrtc/system_wrappers/interface/clock.h" namespace webrtc { +class AudioEncoderMutable; namespace test { class InputAudioFile; @@ -40,6 +41,9 @@ class AcmSendTestOldApi : public AudioPacketizationCallback, int payload_type, int frame_size_samples); + // Registers an external send codec. Returns true on success, false otherwise. + bool RegisterExternalCodec(AudioEncoderMutable* external_speech_encoder); + // Returns the next encoded packet. Returns NULL if the test duration was // exceeded. Ownership of the packet is handed over to the caller. // Inherited from PacketSource. @@ -69,7 +73,6 @@ class AcmSendTestOldApi : public AudioPacketizationCallback, int source_rate_hz_; const int input_block_size_samples_; AudioFrame input_frame_; - CodecInst codec_; bool codec_registered_; int test_duration_ms_; // The following member variables are set whenever SendData() is called. diff --git a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc index fbc27a976..b6470dcfd 100644 --- a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc +++ b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc @@ -234,13 +234,19 @@ int AudioCodingModuleImpl::ResetEncoder() { // Can be called multiple times for Codec, CNG, RED. int AudioCodingModuleImpl::RegisterSendCodec(const CodecInst& send_codec) { CriticalSectionScoped lock(acm_crit_sect_); - return codec_manager_.RegisterSendCodec(send_codec); + return codec_manager_.RegisterEncoder(send_codec); +} + +void AudioCodingModuleImpl::RegisterExternalSendCodec( + AudioEncoderMutable* external_speech_encoder) { + CriticalSectionScoped lock(acm_crit_sect_); + codec_manager_.RegisterEncoder(external_speech_encoder); } // Get current send codec. int AudioCodingModuleImpl::SendCodec(CodecInst* current_codec) const { CriticalSectionScoped lock(acm_crit_sect_); - return codec_manager_.SendCodec(current_codec); + return codec_manager_.GetCodecInst(current_codec); } // Get current send frequency. diff --git a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h index 19934858a..6ef80bc66 100644 --- a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h +++ b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h @@ -48,6 +48,9 @@ class AudioCodingModuleImpl : public AudioCodingModule { // Can be called multiple times for Codec, CNG, RED. int RegisterSendCodec(const CodecInst& send_codec) override; + void RegisterExternalSendCodec( + AudioEncoderMutable* external_speech_encoder) override; + // Get current send codec. int SendCodec(CodecInst* current_codec) const override; diff --git a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc index cb52cb037..c218c2bf3 100644 --- a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc +++ b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc @@ -15,6 +15,9 @@ #include "webrtc/base/md5digest.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/base/thread_annotations.h" +#include "webrtc/modules/audio_coding/codecs/audio_encoder.h" +#include "webrtc/modules/audio_coding/codecs/g711/include/audio_encoder_pcm.h" +#include "webrtc/modules/audio_coding/codecs/mock/mock_audio_encoder.h" #include "webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.h" #include "webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" @@ -35,6 +38,10 @@ #include "webrtc/test/testsupport/fileutils.h" #include "webrtc/test/testsupport/gtest_disable.h" +using ::testing::AtLeast; +using ::testing::Invoke; +using ::testing::_; + namespace webrtc { namespace { @@ -849,6 +856,15 @@ class AcmSenderBitExactnessOldApi : public ::testing::Test, frame_size_samples); } + bool RegisterExternalSendCodec(AudioEncoderMutable* external_speech_encoder, + int payload_type) { + payload_type_ = payload_type; + frame_size_rtp_timestamps_ = + external_speech_encoder->Num10MsFramesInNextPacket() * + external_speech_encoder->RtpTimestampRateHz() / 100; + return send_test_->RegisterExternalCodec(external_speech_encoder); + } + // Runs the test. SetUpSender() and RegisterSendCodec() must have been called // before calling this method. void Run(const std::string& audio_checksum_ref, @@ -942,6 +958,13 @@ class AcmSenderBitExactnessOldApi : public ::testing::Test, codec_frame_size_rtp_timestamps)); } + void SetUpTestExternalEncoder(AudioEncoderMutable* external_speech_encoder, + int payload_type) { + ASSERT_TRUE(SetUpSender()); + ASSERT_TRUE( + RegisterExternalSendCodec(external_speech_encoder, payload_type)); + } + rtc::scoped_ptr send_test_; rtc::scoped_ptr audio_source_; uint32_t frame_size_rtp_timestamps_; @@ -1355,6 +1378,39 @@ TEST_F(AcmChangeBitRateOldApi, Pcm16_8khz_10ms_32kbps) { Run(32000, 64000, 64000); } +TEST_F(AcmSenderBitExactnessOldApi, External_Pcmu_20ms) { + CodecInst codec_inst; + codec_inst.channels = 1; + codec_inst.pacsize = 160; + codec_inst.pltype = 0; + AudioEncoderMutablePcmU encoder(codec_inst); + MockAudioEncoderMutable mock_encoder; + // Set expectations on the mock encoder and also delegate the calls to the + // real encoder. + EXPECT_CALL(mock_encoder, Num10MsFramesInNextPacket()) + .Times(AtLeast(1)) + .WillRepeatedly(Invoke( + &encoder, &AudioEncoderMutablePcmU::Num10MsFramesInNextPacket)); + EXPECT_CALL(mock_encoder, Max10MsFramesInAPacket()) + .Times(AtLeast(1)) + .WillRepeatedly( + Invoke(&encoder, &AudioEncoderMutablePcmU::Max10MsFramesInAPacket)); + EXPECT_CALL(mock_encoder, SampleRateHz()) + .Times(AtLeast(1)) + .WillRepeatedly(Invoke(&encoder, &AudioEncoderMutablePcmU::SampleRateHz)); + EXPECT_CALL(mock_encoder, NumChannels()) + .Times(AtLeast(1)) + .WillRepeatedly(Invoke(&encoder, &AudioEncoderMutablePcmU::NumChannels)); + EXPECT_CALL(mock_encoder, EncodeInternal(_, _, _, _)) + .Times(AtLeast(1)) + .WillRepeatedly( + Invoke(&encoder, &AudioEncoderMutablePcmU::EncodeInternal)); + ASSERT_NO_FATAL_FAILURE( + SetUpTestExternalEncoder(&mock_encoder, codec_inst.pltype)); + Run("81a9d4c0bb72e9becc43aef124c981e9", "8f9b8750bd80fe26b6cbf6659b89f0f9", + 50, test::AcmReceiveTestOldApi::kMonoOutput); +} + // This test fixture is implemented to run ACM and change the desired output // frequency during the call. The input packets are simply PCM16b-wb encoded // payloads with a constant value of |kSampleValue|. The test fixture itself diff --git a/webrtc/modules/audio_coding/main/acm2/codec_manager.cc b/webrtc/modules/audio_coding/main/acm2/codec_manager.cc index dbbf8f4f6..cad6ee908 100644 --- a/webrtc/modules/audio_coding/main/acm2/codec_manager.cc +++ b/webrtc/modules/audio_coding/main/acm2/codec_manager.cc @@ -187,7 +187,7 @@ CodecManager::CodecManager() CodecManager::~CodecManager() = default; -int CodecManager::RegisterSendCodec(const CodecInst& send_codec) { +int CodecManager::RegisterEncoder(const CodecInst& send_codec) { DCHECK(thread_checker_.CalledOnValidThread()); int codec_id = IsValidSendCodec(send_codec, true); @@ -321,7 +321,32 @@ int CodecManager::RegisterSendCodec(const CodecInst& send_codec) { return 0; } -int CodecManager::SendCodec(CodecInst* current_codec) const { +void CodecManager::RegisterEncoder( + AudioEncoderMutable* external_speech_encoder) { + // Make up a CodecInst. + send_codec_inst_.channels = external_speech_encoder->NumChannels(); + send_codec_inst_.plfreq = external_speech_encoder->SampleRateHz(); + send_codec_inst_.pacsize = + rtc::CheckedDivExact(external_speech_encoder->Max10MsFramesInAPacket() * + send_codec_inst_.plfreq, + 100); + send_codec_inst_.pltype = -1; // Not valid. + send_codec_inst_.rate = -1; // Not valid. + static const char kName[] = "external"; + memcpy(send_codec_inst_.plname, kName, sizeof(kName)); + + if (stereo_send_) + dtx_enabled_ = false; + codec_fec_enabled_ = codec_fec_enabled_ && + codec_owner_.SpeechEncoder()->SetFec(codec_fec_enabled_); + int cng_pt = dtx_enabled_ + ? CngPayloadType(external_speech_encoder->SampleRateHz()) + : -1; + int red_pt = red_enabled_ ? RedPayloadType(send_codec_inst_.plfreq) : -1; + codec_owner_.SetEncoders(external_speech_encoder, cng_pt, vad_mode_, red_pt); +} + +int CodecManager::GetCodecInst(CodecInst* current_codec) const { int dummy_id = 0; WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, dummy_id, "SendCodec()"); @@ -348,12 +373,11 @@ bool CodecManager::SetCopyRed(bool enable) { } if (red_enabled_ != enable) { red_enabled_ = enable; - if (codec_owner_.Encoder()) - codec_owner_.SetEncoders( - send_codec_inst_, - dtx_enabled_ ? CngPayloadType(send_codec_inst_.plfreq) : -1, - vad_mode_, - red_enabled_ ? RedPayloadType(send_codec_inst_.plfreq) : -1); + if (codec_owner_.Encoder()) { + int cng_pt = dtx_enabled_ ? CngPayloadType(send_codec_inst_.plfreq) : -1; + int red_pt = red_enabled_ ? RedPayloadType(send_codec_inst_.plfreq) : -1; + codec_owner_.ChangeCngAndRed(cng_pt, vad_mode_, red_pt); + } } return true; } @@ -382,12 +406,11 @@ int CodecManager::SetVAD(bool enable, ACMVADMode mode) { if (dtx_enabled_ != enable || vad_mode_ != mode) { dtx_enabled_ = enable; vad_mode_ = mode; - if (codec_owner_.Encoder()) - codec_owner_.SetEncoders( - send_codec_inst_, - dtx_enabled_ ? CngPayloadType(send_codec_inst_.plfreq) : -1, - vad_mode_, - red_enabled_ ? RedPayloadType(send_codec_inst_.plfreq) : -1); + if (codec_owner_.Encoder()) { + int cng_pt = dtx_enabled_ ? CngPayloadType(send_codec_inst_.plfreq) : -1; + int red_pt = red_enabled_ ? RedPayloadType(send_codec_inst_.plfreq) : -1; + codec_owner_.ChangeCngAndRed(cng_pt, vad_mode_, red_pt); + } } return 0; } diff --git a/webrtc/modules/audio_coding/main/acm2/codec_manager.h b/webrtc/modules/audio_coding/main/acm2/codec_manager.h index 2c54512c3..bb9545d88 100644 --- a/webrtc/modules/audio_coding/main/acm2/codec_manager.h +++ b/webrtc/modules/audio_coding/main/acm2/codec_manager.h @@ -31,9 +31,11 @@ class CodecManager final { CodecManager(); ~CodecManager(); - int RegisterSendCodec(const CodecInst& send_codec); + int RegisterEncoder(const CodecInst& send_codec); - int SendCodec(CodecInst* current_codec) const; + void RegisterEncoder(AudioEncoderMutable* external_speech_encoder); + + int GetCodecInst(CodecInst* current_codec) const; bool SetCopyRed(bool enable); diff --git a/webrtc/modules/audio_coding/main/acm2/codec_owner.cc b/webrtc/modules/audio_coding/main/acm2/codec_owner.cc index 53337cfec..5f0671d8b 100644 --- a/webrtc/modules/audio_coding/main/acm2/codec_owner.cc +++ b/webrtc/modules/audio_coding/main/acm2/codec_owner.cc @@ -75,7 +75,8 @@ bool IsG722(const CodecInst& codec) { } } // namespace -CodecOwner::CodecOwner() : isac_is_encoder_(false) { +CodecOwner::CodecOwner() + : isac_is_encoder_(false), external_speech_encoder_(nullptr) { } CodecOwner::~CodecOwner() = default; @@ -92,7 +93,7 @@ AudioEncoderDecoderMutableIsac* CreateIsacCodec(const CodecInst& speech_inst) { #endif } -AudioEncoder* CreateSpeechEncoder( +void CreateSpeechEncoder( const CodecInst& speech_inst, rtc::scoped_ptr* speech_encoder, rtc::scoped_ptr* isac_codec, @@ -105,7 +106,7 @@ AudioEncoder* CreateSpeechEncoder( } *isac_is_encoder = true; speech_encoder->reset(); - return isac_codec->get(); + return; } if (IsOpus(speech_inst)) { speech_encoder->reset(new AudioEncoderMutableOpus(speech_inst)); @@ -123,7 +124,6 @@ AudioEncoder* CreateSpeechEncoder( FATAL(); } *isac_is_encoder = false; - return speech_encoder->get(); } AudioEncoder* CreateRedEncoder(int red_payload_type, @@ -140,13 +140,13 @@ AudioEncoder* CreateRedEncoder(int red_payload_type, return red_encoder->get(); } -AudioEncoder* CreateCngEncoder(int cng_payload_type, - ACMVADMode vad_mode, - AudioEncoder* encoder, - rtc::scoped_ptr* cng_encoder) { +void CreateCngEncoder(int cng_payload_type, + ACMVADMode vad_mode, + AudioEncoder* encoder, + rtc::scoped_ptr* cng_encoder) { if (cng_payload_type == -1) { cng_encoder->reset(); - return encoder; + return; } AudioEncoderCng::Config config; config.num_channels = encoder->NumChannels(); @@ -169,7 +169,6 @@ AudioEncoder* CreateCngEncoder(int cng_payload_type, FATAL(); } cng_encoder->reset(new AudioEncoderCng(config)); - return cng_encoder->get(); } } // namespace @@ -177,12 +176,31 @@ void CodecOwner::SetEncoders(const CodecInst& speech_inst, int cng_payload_type, ACMVADMode vad_mode, int red_payload_type) { - AudioEncoder* encoder = CreateSpeechEncoder(speech_inst, &speech_encoder_, - &isac_codec_, &isac_is_encoder_); - encoder = CreateRedEncoder(red_payload_type, encoder, &red_encoder_); - encoder = - CreateCngEncoder(cng_payload_type, vad_mode, encoder, &cng_encoder_); - DCHECK(!speech_encoder_ || !isac_is_encoder_); + CreateSpeechEncoder(speech_inst, &speech_encoder_, &isac_codec_, + &isac_is_encoder_); + external_speech_encoder_ = nullptr; + ChangeCngAndRed(cng_payload_type, vad_mode, red_payload_type); +} + +void CodecOwner::SetEncoders(AudioEncoderMutable* external_speech_encoder, + int cng_payload_type, + ACMVADMode vad_mode, + int red_payload_type) { + external_speech_encoder_ = external_speech_encoder; + speech_encoder_.reset(); + isac_is_encoder_ = false; + ChangeCngAndRed(cng_payload_type, vad_mode, red_payload_type); +} + +void CodecOwner::ChangeCngAndRed(int cng_payload_type, + ACMVADMode vad_mode, + int red_payload_type) { + AudioEncoder* encoder = + CreateRedEncoder(red_payload_type, SpeechEncoder(), &red_encoder_); + CreateCngEncoder(cng_payload_type, vad_mode, encoder, &cng_encoder_); + int num_true = + !!speech_encoder_ + !!external_speech_encoder_ + isac_is_encoder_; + DCHECK_EQ(num_true, 1); DCHECK(!isac_is_encoder_ || isac_codec_); } @@ -219,8 +237,12 @@ AudioEncoderMutable* CodecOwner::SpeechEncoder() { } const AudioEncoderMutable* CodecOwner::SpeechEncoder() const { - DCHECK(!speech_encoder_ || !isac_is_encoder_); - DCHECK(!isac_is_encoder_ || isac_codec_); + int num_true = + !!speech_encoder_ + !!external_speech_encoder_ + isac_is_encoder_; + DCHECK_GE(num_true, 0); + DCHECK_LE(num_true, 1); + if (external_speech_encoder_) + return external_speech_encoder_; if (speech_encoder_) return speech_encoder_.get(); return isac_is_encoder_ ? isac_codec_.get() : nullptr; diff --git a/webrtc/modules/audio_coding/main/acm2/codec_owner.h b/webrtc/modules/audio_coding/main/acm2/codec_owner.h index bfce37709..2468c3ce0 100644 --- a/webrtc/modules/audio_coding/main/acm2/codec_owner.h +++ b/webrtc/modules/audio_coding/main/acm2/codec_owner.h @@ -34,6 +34,17 @@ class CodecOwner { ACMVADMode vad_mode, int red_payload_type); + void SetEncoders(AudioEncoderMutable* external_speech_encoder, + int cng_payload_type, + ACMVADMode vad_mode, + int red_payload_type); + + void ChangeCngAndRed(int cng_payload_type, + ACMVADMode vad_mode, + int red_payload_type); + + // Returns a pointer to an iSAC decoder owned by the CodecOwner. The decoder + // will live as long as the CodecOwner exists. AudioDecoder* GetIsacDecoder(); AudioEncoder* Encoder(); @@ -42,13 +53,20 @@ class CodecOwner { const AudioEncoderMutable* SpeechEncoder() const; private: - // If iSAC is registered as an encoder, |isac_is_encoder_| is true, - // |isac_codec_| is valid and |speech_encoder_| is null. If another encoder - // is registered, |isac_is_encoder_| is false, |speech_encoder_| is valid - // and |isac_codec_| is valid iff iSAC has been registered as a decoder. + // There are three main cases for the state of the encoder members below: + // 1. An external encoder is used. |external_speech_encoder_| points to it. + // |speech_encoder_| is null, and |isac_is_encoder_| is false. + // 2. The internal iSAC codec is used as encoder. |isac_codec_| points to it + // and |isac_is_encoder_| is true. |external_speech_encoder_| and + // |speech_encoder_| are null. + // 3. Another internal encoder is used. |speech_encoder_| points to it. + // |external_speech_encoder_| is null, and |isac_is_encoder_| is false. + // In addition to case 2, |isac_codec_| is valid when GetIsacDecoder has been + // called. rtc::scoped_ptr speech_encoder_; rtc::scoped_ptr isac_codec_; bool isac_is_encoder_; + AudioEncoderMutable* external_speech_encoder_; // |cng_encoder_| and |red_encoder_| are valid iff CNG or RED, respectively, // are active. diff --git a/webrtc/modules/audio_coding/main/acm2/codec_owner_unittest.cc b/webrtc/modules/audio_coding/main/acm2/codec_owner_unittest.cc index 20a522092..a1366a9b8 100644 --- a/webrtc/modules/audio_coding/main/acm2/codec_owner_unittest.cc +++ b/webrtc/modules/audio_coding/main/acm2/codec_owner_unittest.cc @@ -9,12 +9,17 @@ */ #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/arraysize.h" #include "webrtc/base/safe_conversions.h" +#include "webrtc/modules/audio_coding/codecs/mock/mock_audio_encoder.h" #include "webrtc/modules/audio_coding/main/acm2/codec_owner.h" namespace webrtc { namespace acm2 { +using ::testing::Return; +using ::testing::InSequence; + namespace { const int kDataLengthSamples = 80; const int kPacketSizeSamples = 2 * kDataLengthSamples; @@ -93,5 +98,53 @@ TEST_F(CodecOwnerTest, VerifyCngFrames) { } } +TEST_F(CodecOwnerTest, ExternalEncoder) { + MockAudioEncoderMutable external_encoder; + codec_owner_.SetEncoders(&external_encoder, -1, VADNormal, -1); + const int kSampleRateHz = 8000; + const int kPacketSizeSamples = kSampleRateHz / 100; + int16_t audio[kPacketSizeSamples] = {0}; + uint8_t encoded[kPacketSizeSamples]; + AudioEncoder::EncodedInfo info; + EXPECT_CALL(external_encoder, SampleRateHz()) + .WillRepeatedly(Return(kSampleRateHz)); + + { + InSequence s; + info.encoded_timestamp = 0; + EXPECT_CALL(external_encoder, + EncodeInternal(0, audio, arraysize(encoded), encoded)) + .WillOnce(Return(info)); + EXPECT_CALL(external_encoder, Reset()); + EXPECT_CALL(external_encoder, Reset()); + info.encoded_timestamp = 2; + EXPECT_CALL(external_encoder, + EncodeInternal(2, audio, arraysize(encoded), encoded)) + .WillOnce(Return(info)); + EXPECT_CALL(external_encoder, Reset()); + } + + info = codec_owner_.Encoder()->Encode(0, audio, arraysize(audio), + arraysize(encoded), encoded); + EXPECT_EQ(0u, info.encoded_timestamp); + external_encoder.Reset(); // Dummy call to mark the sequence of expectations. + + // Change to internal encoder. + CodecInst codec_inst = kDefaultCodecInst; + codec_inst.pacsize = kPacketSizeSamples; + codec_owner_.SetEncoders(codec_inst, -1, VADNormal, -1); + // Don't expect any more calls to the external encoder. + info = codec_owner_.Encoder()->Encode(1, audio, arraysize(audio), + arraysize(encoded), encoded); + external_encoder.Reset(); // Dummy call to mark the sequence of expectations. + + // Change back to external encoder again. + codec_owner_.SetEncoders(&external_encoder, -1, VADNormal, -1); + info = codec_owner_.Encoder()->Encode(2, audio, arraysize(audio), + arraysize(encoded), encoded); + EXPECT_EQ(2u, info.encoded_timestamp); + external_encoder.Reset(); // Dummy call to mark the sequence of expectations. +} + } // namespace acm2 } // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/interface/audio_coding_module.h b/webrtc/modules/audio_coding/main/interface/audio_coding_module.h index 9cd2cb4be..b7d9a91b2 100644 --- a/webrtc/modules/audio_coding/main/interface/audio_coding_module.h +++ b/webrtc/modules/audio_coding/main/interface/audio_coding_module.h @@ -28,6 +28,7 @@ struct CodecInst; struct WebRtcRTPHeader; class AudioFrame; class RTPFragmentationHeader; +class AudioEncoderMutable; #define WEBRTC_10MS_PCM_AUDIO 960 // 16 bits super wideband 48 kHz @@ -231,6 +232,11 @@ class AudioCodingModule { // virtual int32_t RegisterSendCodec(const CodecInst& send_codec) = 0; + // Registers |external_speech_encoder| as encoder. The new encoder will + // replace any previously registered speech encoder (internal or external). + virtual void RegisterExternalSendCodec( + AudioEncoderMutable* external_speech_encoder) = 0; + /////////////////////////////////////////////////////////////////////////// // int32_t SendCodec() // Get parameters for the codec currently registered as send codec.