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/codecs/mock/mock_audio_encoder.h b/webrtc/modules/audio_coding/codecs/mock/mock_audio_encoder.h
index 25fd7a8..786c83c 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 @@
                            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 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.