Add support for external encoders in ACM
Also introduce tests using external (mock) encoders, both for
CodecOwner and for AudioCodingModule.
Support for external decoders is still missing.
COAUTHOR=henrik.lundin@webrtc.org
BUG=4474
R=jmarusic@webrtc.org, minyue@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/49939004
Cr-Commit-Position: refs/heads/master@{#9206}
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 41e0feb..d0c031e 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 @@
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<size_t>(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 52cb415..8cdc298 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 @@
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 @@
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 fbc27a9..b6470dc 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 @@
// 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 1993485..6ef80bc 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 @@
// 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 cb52cb0..c218c2b 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 @@
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 @@
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<test::AcmSendTestOldApi> send_test_;
rtc::scoped_ptr<test::InputAudioFile> audio_source_;
uint32_t frame_size_rtp_timestamps_;
@@ -1355,6 +1378,39 @@
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 dbbf8f4..cad6ee9 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() = 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 @@
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 @@
}
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 @@
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 2c54512..bb9545d 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 @@
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 53337cf..5f0671d 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 @@
}
} // namespace
-CodecOwner::CodecOwner() : isac_is_encoder_(false) {
+CodecOwner::CodecOwner()
+ : isac_is_encoder_(false), external_speech_encoder_(nullptr) {
}
CodecOwner::~CodecOwner() = default;
@@ -92,7 +93,7 @@
#endif
}
-AudioEncoder* CreateSpeechEncoder(
+void CreateSpeechEncoder(
const CodecInst& speech_inst,
rtc::scoped_ptr<AudioEncoderMutable>* speech_encoder,
rtc::scoped_ptr<AudioEncoderDecoderMutableIsac>* isac_codec,
@@ -105,7 +106,7 @@
}
*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 @@
FATAL();
}
*isac_is_encoder = false;
- return speech_encoder->get();
}
AudioEncoder* CreateRedEncoder(int red_payload_type,
@@ -140,13 +140,13 @@
return red_encoder->get();
}
-AudioEncoder* CreateCngEncoder(int cng_payload_type,
- ACMVADMode vad_mode,
- AudioEncoder* encoder,
- rtc::scoped_ptr<AudioEncoder>* cng_encoder) {
+void CreateCngEncoder(int cng_payload_type,
+ ACMVADMode vad_mode,
+ AudioEncoder* encoder,
+ rtc::scoped_ptr<AudioEncoder>* 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 @@
FATAL();
}
cng_encoder->reset(new AudioEncoderCng(config));
- return cng_encoder->get();
}
} // namespace
@@ -177,12 +176,31 @@
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 @@
}
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 bfce377..2468c3c 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 @@
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 @@
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<AudioEncoderMutable> speech_encoder_;
rtc::scoped_ptr<AudioEncoderDecoderMutableIsac> 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 20a5220..a1366a9 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, 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 9cd2cb4..b7d9a91 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 WebRtcRTPHeader;
class AudioFrame;
class RTPFragmentationHeader;
+class AudioEncoderMutable;
#define WEBRTC_10MS_PCM_AUDIO 960 // 16 bits super wideband 48 kHz
@@ -231,6 +232,11 @@
//
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.