Add support for external decoders in ACM

Test added too.

COAUTHOR=henrik.lundin@webrtc.org
BUG=4474
TBR=minyue@webrtc.org

Review URL: https://codereview.webrtc.org/1312493004

Cr-Commit-Position: refs/heads/master@{#9765}
diff --git a/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.cc b/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.cc
index 96a1fc5..dd570e6 100644
--- a/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.cc
+++ b/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.cc
@@ -143,6 +143,15 @@
   }
 }
 
+int AcmReceiveTestOldApi::RegisterExternalReceiveCodec(
+    int rtp_payload_type,
+    AudioDecoder* external_decoder,
+    int sample_rate_hz,
+    int num_channels) {
+  return acm_->RegisterExternalReceiveCodec(rtp_payload_type, external_decoder,
+                                            sample_rate_hz, num_channels);
+}
+
 void AcmReceiveTestOldApi::Run() {
   for (rtc::scoped_ptr<Packet> packet(packet_source_->NextPacket()); packet;
        packet.reset(packet_source_->NextPacket())) {
diff --git a/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.h b/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.h
index 5e5ff9a..8e80ca7 100644
--- a/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.h
+++ b/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.h
@@ -17,6 +17,7 @@
 
 namespace webrtc {
 class AudioCodingModule;
+class AudioDecoder;
 struct CodecInst;
 
 namespace test {
@@ -44,6 +45,11 @@
   // files.
   void RegisterNetEqTestCodecs();
 
+  int RegisterExternalReceiveCodec(int rtp_payload_type,
+                                   AudioDecoder* external_decoder,
+                                   int sample_rate_hz,
+                                   int num_channels);
+
   // Runs the test and returns true if successful.
   void Run();
 
diff --git a/webrtc/modules/audio_coding/main/acm2/acm_receiver.cc b/webrtc/modules/audio_coding/main/acm2/acm_receiver.cc
index ae5a04f..4c11197 100644
--- a/webrtc/modules/audio_coding/main/acm2/acm_receiver.cc
+++ b/webrtc/modules/audio_coding/main/acm2/acm_receiver.cc
@@ -476,8 +476,10 @@
                               int channels,
                               int sample_rate_hz,
                               AudioDecoder* audio_decoder) {
-  assert(acm_codec_id >= 0);
-  NetEqDecoder neteq_decoder = ACMCodecDB::neteq_decoders_[acm_codec_id];
+  assert(acm_codec_id >= -1);  // -1 means external decoder
+  NetEqDecoder neteq_decoder = (acm_codec_id == -1)
+                                   ? kDecoderArbitrary
+                                   : ACMCodecDB::neteq_decoders_[acm_codec_id];
 
   // Make sure the right decoder is registered for Opus.
   if (neteq_decoder == kDecoderOpus && channels == 2) {
@@ -491,14 +493,15 @@
   auto it = decoders_.find(payload_type);
   if (it != decoders_.end()) {
     const Decoder& decoder = it->second;
-    if (decoder.acm_codec_id == acm_codec_id && decoder.channels == channels &&
+    if (acm_codec_id != -1 && decoder.acm_codec_id == acm_codec_id &&
+        decoder.channels == channels &&
         decoder.sample_rate_hz == sample_rate_hz) {
       // Re-registering the same codec. Do nothing and return.
       return 0;
     }
 
-    // Changing codec or number of channels. First unregister the old codec,
-    // then register the new one.
+    // Changing codec. First unregister the old codec, then register the new
+    // one.
     if (neteq_->RemovePayloadType(payload_type) != NetEq::kOK) {
       LOG(LERROR) << "Cannot remove payload " << static_cast<int>(payload_type);
       return -1;
diff --git a/webrtc/modules/audio_coding/main/acm2/acm_receiver.h b/webrtc/modules/audio_coding/main/acm2/acm_receiver.h
index 46207fd..fe27249 100644
--- a/webrtc/modules/audio_coding/main/acm2/acm_receiver.h
+++ b/webrtc/modules/audio_coding/main/acm2/acm_receiver.h
@@ -93,21 +93,24 @@
   // Adds a new codec to the NetEq codec database.
   //
   // Input:
-  //   - acm_codec_id        : ACM codec ID.
+  //   - acm_codec_id        : ACM codec ID; -1 means external decoder.
   //   - payload_type        : payload type.
   //   - sample_rate_hz      : sample rate.
-  //   - audio_decoder       : pointer to a decoder object. If it is NULL
-  //                           then NetEq will internally create the decoder
-  //                           object. Otherwise, NetEq will store this pointer
-  //                           as the decoder corresponding with the given
-  //                           payload type. NetEq won't acquire the ownership
-  //                           of this pointer. It is up to the client of this
-  //                           class (ACM) to delete it. By providing
-  //                           |audio_decoder| ACM will have control over the
-  //                           decoder instance of the codec. This is essential
-  //                           for a codec like iSAC which encoder/decoder
-  //                           encoder has to know about decoder (bandwidth
-  //                           estimator that is updated at decoding time).
+  //   - audio_decoder       : pointer to a decoder object. If it's null, then
+  //                           NetEq will internally create a decoder object
+  //                           based on the value of |acm_codec_id| (which
+  //                           mustn't be -1). Otherwise, NetEq will use the
+  //                           given decoder for the given payload type. NetEq
+  //                           won't take ownership of the decoder; it's up to
+  //                           the caller to delete it when it's no longer
+  //                           needed.
+  //
+  //                           Providing an existing decoder object here is
+  //                           necessary for external decoders, but may also be
+  //                           used for built-in decoders if NetEq doesn't have
+  //                           all the info it needs to construct them properly
+  //                           (e.g. iSAC, where the decoder needs to be paired
+  //                           with an encoder).
   //
   // Return value             : 0 if OK.
   //                           <0 if NetEq returned an error.
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 9c31832..32d60a7 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
@@ -667,6 +667,29 @@
                             codec_manager_.GetAudioDecoder(codec));
 }
 
+int AudioCodingModuleImpl::RegisterExternalReceiveCodec(
+    int rtp_payload_type,
+    AudioDecoder* external_decoder,
+    int sample_rate_hz,
+    int num_channels) {
+  CriticalSectionScoped lock(acm_crit_sect_);
+  DCHECK(receiver_initialized_);
+  if (num_channels > 2 || num_channels < 0) {
+    LOG_F(LS_ERROR) << "Unsupported number of channels: " << num_channels;
+    return -1;
+  }
+
+  // Check if the payload-type is valid.
+  if (!ACMCodecDB::ValidPayloadType(rtp_payload_type)) {
+    LOG_F(LS_ERROR) << "Invalid payload-type " << rtp_payload_type
+                    << " for external decoder.";
+    return -1;
+  }
+
+  return receiver_.AddCodec(-1 /* external */, rtp_payload_type, num_channels,
+                            sample_rate_hz, external_decoder);
+}
+
 // Get current received codec.
 int AudioCodingModuleImpl::ReceiveCodec(CodecInst* current_codec) const {
   CriticalSectionScoped lock(acm_crit_sect_);
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 19ca01b..beb49bc 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
@@ -137,6 +137,11 @@
   // for codecs, CNG, DTMF, RED.
   int RegisterReceiveCodec(const CodecInst& receive_codec) override;
 
+  int RegisterExternalReceiveCodec(int rtp_payload_type,
+                                   AudioDecoder* external_decoder,
+                                   int sample_rate_hz,
+                                   int num_channels) override;
+
   // Get current received codec.
   int ReceiveCodec(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 71c436b..0af6af8 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
@@ -23,6 +23,8 @@
 #include "webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.h"
 #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h"
 #include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h"
+#include "webrtc/modules/audio_coding/neteq/audio_decoder_impl.h"
+#include "webrtc/modules/audio_coding/neteq/mock/mock_audio_decoder.h"
 #include "webrtc/modules/audio_coding/neteq/tools/audio_checksum.h"
 #include "webrtc/modules/audio_coding/neteq/tools/audio_loop.h"
 #include "webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.h"
@@ -873,7 +875,16 @@
   }
 
  protected:
-  void Run(int output_freq_hz, const std::string& checksum_ref) {
+  struct ExternalDecoder {
+    int rtp_payload_type;
+    AudioDecoder* external_decoder;
+    int sample_rate_hz;
+    int num_channels;
+  };
+
+  void Run(int output_freq_hz,
+           const std::string& checksum_ref,
+           const std::vector<ExternalDecoder>& external_decoders) {
     const std::string input_file_name =
         webrtc::test::ResourcePath("audio_coding/neteq_universal_new", "rtp");
     rtc::scoped_ptr<test::RtpFileSource> packet_source(
@@ -901,6 +912,11 @@
         output_freq_hz,
         test::AcmReceiveTestOldApi::kArbitraryChannels);
     ASSERT_NO_FATAL_FAILURE(test.RegisterNetEqTestCodecs());
+    for (const auto& ed : external_decoders) {
+      ASSERT_EQ(0, test.RegisterExternalReceiveCodec(
+                       ed.rtp_payload_type, ed.external_decoder,
+                       ed.sample_rate_hz, ed.num_channels));
+    }
     test.Run();
 
     std::string checksum_string = checksum.Finish();
@@ -915,10 +931,10 @@
 #define MAYBE_8kHzOutput 8kHzOutput
 #endif
 TEST_F(AcmReceiverBitExactnessOldApi, MAYBE_8kHzOutput) {
-  Run(8000,
-      PlatformChecksum("dcee98c623b147ebe1b40dd30efa896e",
-                       "adc92e173f908f93b96ba5844209815a",
-                       "908002dc01fc4eb1d2be24eb1d3f354b"));
+  Run(8000, PlatformChecksum("dcee98c623b147ebe1b40dd30efa896e",
+                             "adc92e173f908f93b96ba5844209815a",
+                             "908002dc01fc4eb1d2be24eb1d3f354b"),
+      std::vector<ExternalDecoder>());
 }
 
 // Fails Android ARM64. https://code.google.com/p/webrtc/issues/detail?id=4199
@@ -928,10 +944,10 @@
 #define MAYBE_16kHzOutput 16kHzOutput
 #endif
 TEST_F(AcmReceiverBitExactnessOldApi, MAYBE_16kHzOutput) {
-  Run(16000,
-      PlatformChecksum("f790e7a8cce4e2c8b7bb5e0e4c5dac0d",
-                       "8cffa6abcb3e18e33b9d857666dff66a",
-                       "a909560b5ca49fa472b17b7b277195e9"));
+  Run(16000, PlatformChecksum("f790e7a8cce4e2c8b7bb5e0e4c5dac0d",
+                              "8cffa6abcb3e18e33b9d857666dff66a",
+                              "a909560b5ca49fa472b17b7b277195e9"),
+      std::vector<ExternalDecoder>());
 }
 
 // Fails Android ARM64. https://code.google.com/p/webrtc/issues/detail?id=4199
@@ -941,10 +957,10 @@
 #define MAYBE_32kHzOutput 32kHzOutput
 #endif
 TEST_F(AcmReceiverBitExactnessOldApi, MAYBE_32kHzOutput) {
-  Run(32000,
-      PlatformChecksum("306e0d990ee6e92de3fbecc0123ece37",
-                       "3e126fe894720c3f85edadcc91964ba5",
-                       "441aab4b347fb3db4e9244337aca8d8e"));
+  Run(32000, PlatformChecksum("306e0d990ee6e92de3fbecc0123ece37",
+                              "3e126fe894720c3f85edadcc91964ba5",
+                              "441aab4b347fb3db4e9244337aca8d8e"),
+      std::vector<ExternalDecoder>());
 }
 
 // Fails Android ARM64. https://code.google.com/p/webrtc/issues/detail?id=4199
@@ -954,10 +970,52 @@
 #define MAYBE_48kHzOutput 48kHzOutput
 #endif
 TEST_F(AcmReceiverBitExactnessOldApi, MAYBE_48kHzOutput) {
-  Run(48000,
-      PlatformChecksum("aa7c232f63a67b2a72703593bdd172e0",
-                       "0155665e93067c4e89256b944dd11999",
-                       "4ee2730fa1daae755e8a8fd3abd779ec"));
+  Run(48000, PlatformChecksum("aa7c232f63a67b2a72703593bdd172e0",
+                              "0155665e93067c4e89256b944dd11999",
+                              "4ee2730fa1daae755e8a8fd3abd779ec"),
+      std::vector<ExternalDecoder>());
+}
+
+// Fails Android ARM64. https://code.google.com/p/webrtc/issues/detail?id=4199
+#if defined(WEBRTC_ANDROID) && defined(__aarch64__)
+#define MAYBE_48kHzOutputExternalDecoder DISABLED_48kHzOutputExternalDecoder
+#else
+#define MAYBE_48kHzOutputExternalDecoder 48kHzOutputExternalDecoder
+#endif
+TEST_F(AcmReceiverBitExactnessOldApi, MAYBE_48kHzOutputExternalDecoder) {
+  AudioDecoderPcmU decoder;
+  MockAudioDecoder mock_decoder;
+  // Set expectations on the mock decoder and also delegate the calls to the
+  // real decoder.
+  EXPECT_CALL(mock_decoder, Init())
+      .Times(AtLeast(1))
+      .WillRepeatedly(Invoke(&decoder, &AudioDecoderPcmU::Init));
+  EXPECT_CALL(mock_decoder, IncomingPacket(_, _, _, _, _))
+      .Times(AtLeast(1))
+      .WillRepeatedly(Invoke(&decoder, &AudioDecoderPcmU::IncomingPacket));
+  EXPECT_CALL(mock_decoder, Channels())
+      .Times(AtLeast(1))
+      .WillRepeatedly(Invoke(&decoder, &AudioDecoderPcmU::Channels));
+  EXPECT_CALL(mock_decoder, Decode(_, _, _, _, _, _))
+      .Times(AtLeast(1))
+      .WillRepeatedly(Invoke(&decoder, &AudioDecoderPcmU::Decode));
+  EXPECT_CALL(mock_decoder, HasDecodePlc())
+      .Times(AtLeast(1))
+      .WillRepeatedly(Invoke(&decoder, &AudioDecoderPcmU::HasDecodePlc));
+  ExternalDecoder ed;
+  ed.rtp_payload_type = 0;
+  ed.external_decoder = &mock_decoder;
+  ed.sample_rate_hz = 8000;
+  ed.num_channels = 1;
+  std::vector<ExternalDecoder> external_decoders;
+  external_decoders.push_back(ed);
+
+  Run(48000, PlatformChecksum("aa7c232f63a67b2a72703593bdd172e0",
+                              "0155665e93067c4e89256b944dd11999",
+                              "4ee2730fa1daae755e8a8fd3abd779ec"),
+      external_decoders);
+
+  EXPECT_CALL(mock_decoder, Die());
 }
 
 // This test verifies bit exactness for the send-side of ACM. The test setup is
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 b7d9a91..7357528 100644
--- a/webrtc/modules/audio_coding/main/interface/audio_coding_module.h
+++ b/webrtc/modules/audio_coding/main/interface/audio_coding_module.h
@@ -29,6 +29,7 @@
 class AudioFrame;
 class RTPFragmentationHeader;
 class AudioEncoderMutable;
+class AudioDecoder;
 
 #define WEBRTC_10MS_PCM_AUDIO 960  // 16 bits super wideband 48 kHz
 
@@ -576,8 +577,12 @@
   //   -1 if failed to register the codec
   //    0 if the codec registered successfully.
   //
-  virtual int32_t RegisterReceiveCodec(
-      const CodecInst& receive_codec) = 0;
+  virtual int RegisterReceiveCodec(const CodecInst& receive_codec) = 0;
+
+  virtual int RegisterExternalReceiveCodec(int rtp_payload_type,
+                                           AudioDecoder* external_decoder,
+                                           int sample_rate_hz,
+                                           int num_channels) = 0;
 
   ///////////////////////////////////////////////////////////////////////////
   // int32_t UnregisterReceiveCodec()