Pass SdpAudioFormat through Channel, without converting to CodecInst

BUG=webrtc:5805

Review-Url: https://codereview.webrtc.org/2516993002
Cr-Commit-Position: refs/heads/master@{#16165}
diff --git a/webrtc/audio/audio_receive_stream.cc b/webrtc/audio/audio_receive_stream.cc
index f46337a..aaf77a8 100644
--- a/webrtc/audio/audio_receive_stream.cc
+++ b/webrtc/audio/audio_receive_stream.cc
@@ -101,6 +101,10 @@
 
   channel_proxy_->RegisterExternalTransport(config.rtcp_send_transport);
 
+  for (const auto& kv : config.decoder_map) {
+    channel_proxy_->SetRecPayloadType(kv.first, kv.second);
+  }
+
   for (const auto& extension : config.rtp.extensions) {
     if (extension.uri == RtpExtension::kAudioLevelUri) {
       channel_proxy_->SetReceiveAudioLevelIndicationStatus(true, extension.id);
diff --git a/webrtc/call/audio_receive_stream.h b/webrtc/call/audio_receive_stream.h
index 1299ded..3841672 100644
--- a/webrtc/call/audio_receive_stream.h
+++ b/webrtc/call/audio_receive_stream.h
@@ -102,11 +102,8 @@
     // stream to one audio stream. Tracked by issue webrtc:4762.
     std::string sync_group;
 
-    // Decoders for every payload that we can receive. Call owns the
-    // AudioDecoder instances once the Config is submitted to
-    // Call::CreateReceiveStream().
-    // TODO(solenberg): Use unique_ptr<> once our std lib fully supports C++11.
-    std::map<uint8_t, AudioDecoder*> decoder_map;
+    // Decoder specifications for every payload type that we can receive.
+    std::map<int, SdpAudioFormat> decoder_map;
 
     rtc::scoped_refptr<AudioDecoderFactory> decoder_factory;
   };
diff --git a/webrtc/media/engine/payload_type_mapper.cc b/webrtc/media/engine/payload_type_mapper.cc
index 0d449c4..496a39a 100644
--- a/webrtc/media/engine/payload_type_mapper.cc
+++ b/webrtc/media/engine/payload_type_mapper.cc
@@ -12,9 +12,14 @@
 
 #include "webrtc/common_types.h"
 #include "webrtc/media/base/mediaconstants.h"
+#include "webrtc/modules/audio_coding/codecs/audio_format.h"
 
 namespace cricket {
 
+webrtc::SdpAudioFormat AudioCodecToSdpAudioFormat(const AudioCodec& ac) {
+  return webrtc::SdpAudioFormat(ac.name, ac.clockrate, ac.channels, ac.params);
+}
+
 PayloadTypeMapper::PayloadTypeMapper()
     // RFC 3551 reserves payload type numbers in the range 96-127 exclusively
     // for dynamic assignment. Once those are used up, it is recommended that
diff --git a/webrtc/media/engine/payload_type_mapper.h b/webrtc/media/engine/payload_type_mapper.h
index a79fb47..248aace 100644
--- a/webrtc/media/engine/payload_type_mapper.h
+++ b/webrtc/media/engine/payload_type_mapper.h
@@ -20,6 +20,8 @@
 
 namespace cricket {
 
+webrtc::SdpAudioFormat AudioCodecToSdpAudioFormat(const AudioCodec& ac);
+
 class PayloadTypeMapper {
  public:
   PayloadTypeMapper();
diff --git a/webrtc/media/engine/webrtcvoiceengine.cc b/webrtc/media/engine/webrtcvoiceengine.cc
index e6fa12e..c23084e 100644
--- a/webrtc/media/engine/webrtcvoiceengine.cc
+++ b/webrtc/media/engine/webrtcvoiceengine.cc
@@ -1542,14 +1542,15 @@
     RTC_DCHECK_GE(ch, 0);
     RTC_DCHECK(call);
     config_.rtp.remote_ssrc = remote_ssrc;
+    config_.rtp.local_ssrc = local_ssrc;
+    config_.rtp.transport_cc = use_transport_cc;
+    config_.rtp.nack.rtp_history_ms = use_nack ? kNackRtpHistoryMs : 0;
+    config_.rtp.extensions = extensions;
     config_.rtcp_send_transport = rtcp_send_transport;
     config_.voe_channel_id = ch;
     config_.sync_group = sync_group;
     config_.decoder_factory = decoder_factory;
-    RecreateAudioReceiveStream(local_ssrc,
-                               use_transport_cc,
-                               use_nack,
-                               extensions);
+    RecreateAudioReceiveStream();
   }
 
   ~WebRtcAudioReceiveStream() {
@@ -1559,27 +1560,40 @@
 
   void RecreateAudioReceiveStream(uint32_t local_ssrc) {
     RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
-    RecreateAudioReceiveStream(local_ssrc,
-                               config_.rtp.transport_cc,
-                               config_.rtp.nack.rtp_history_ms != 0,
-                               config_.rtp.extensions);
+    config_.rtp.local_ssrc = local_ssrc;
+    RecreateAudioReceiveStream();
   }
 
   void RecreateAudioReceiveStream(bool use_transport_cc, bool use_nack) {
     RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
-    RecreateAudioReceiveStream(config_.rtp.local_ssrc,
-                               use_transport_cc,
-                               use_nack,
-                               config_.rtp.extensions);
+    config_.rtp.transport_cc = use_transport_cc;
+    config_.rtp.nack.rtp_history_ms = use_nack ? kNackRtpHistoryMs : 0;
+    RecreateAudioReceiveStream();
   }
 
   void RecreateAudioReceiveStream(
       const std::vector<webrtc::RtpExtension>& extensions) {
     RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
-    RecreateAudioReceiveStream(config_.rtp.local_ssrc,
-                               config_.rtp.transport_cc,
-                               config_.rtp.nack.rtp_history_ms != 0,
-                               extensions);
+    config_.rtp.extensions = extensions;
+    RecreateAudioReceiveStream();
+  }
+
+  // Set a new payload type -> decoder map. The new map must be a superset of
+  // the old one.
+  void RecreateAudioReceiveStream(
+      const std::map<int, webrtc::SdpAudioFormat>& decoder_map) {
+    RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
+    RTC_DCHECK([&] {
+      for (const auto& item : config_.decoder_map) {
+        auto it = decoder_map.find(item.first);
+        if (it == decoder_map.end() || *it != item) {
+          return false;  // The old map isn't a subset of the new map.
+        }
+      }
+      return true;
+    }());
+    config_.decoder_map = decoder_map;
+    RecreateAudioReceiveStream();
   }
 
   webrtc::AudioReceiveStream::Stats GetStats() const {
@@ -1617,21 +1631,11 @@
   }
 
  private:
-  void RecreateAudioReceiveStream(
-      uint32_t local_ssrc,
-      bool use_transport_cc,
-      bool use_nack,
-      const std::vector<webrtc::RtpExtension>& extensions) {
+  void RecreateAudioReceiveStream() {
     RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
     if (stream_) {
       call_->DestroyAudioReceiveStream(stream_);
-      stream_ = nullptr;
     }
-    config_.rtp.local_ssrc = local_ssrc;
-    config_.rtp.transport_cc = use_transport_cc;
-    config_.rtp.nack.rtp_history_ms = use_nack ? kNackRtpHistoryMs : 0;
-    config_.rtp.extensions = extensions;
-    RTC_DCHECK(!stream_);
     stream_ = call_->CreateAudioReceiveStream(config_);
     RTC_CHECK(stream_);
     SetPlayout(playout_);
@@ -1901,40 +1905,34 @@
     return true;
   }
 
+  // Create a payload type -> SdpAudioFormat map with all the decoders. Fail
+  // unless the factory claims to support all decoders.
+  std::map<int, webrtc::SdpAudioFormat> decoder_map;
+  for (const AudioCodec& codec : codecs) {
+    auto format = AudioCodecToSdpAudioFormat(codec);
+    if (!IsCodec(codec, "cn") && !IsCodec(codec, "telephone-event") &&
+        !engine()->decoder_factory_->IsSupportedDecoder(format)) {
+      LOG(LS_ERROR) << "Unsupported codec: " << format;
+      return false;
+    }
+    decoder_map.insert({codec.id, std::move(format)});
+  }
+
   if (playout_) {
     // Receive codecs can not be changed while playing. So we temporarily
     // pause playout.
     ChangePlayout(false);
   }
 
-  bool result = true;
-  for (const AudioCodec& codec : new_codecs) {
-    webrtc::CodecInst voe_codec = {0};
-    if (WebRtcVoiceEngine::ToCodecInst(codec, &voe_codec)) {
-      LOG(LS_INFO) << ToString(codec);
-      voe_codec.pltype = codec.id;
-      for (const auto& ch : recv_streams_) {
-        if (engine()->voe()->codec()->SetRecPayloadType(
-                ch.second->channel(), voe_codec) == -1) {
-          LOG_RTCERR2(SetRecPayloadType, ch.second->channel(),
-                      ToString(voe_codec));
-          result = false;
-        }
-      }
-    } else {
-      LOG(LS_WARNING) << "Unknown codec " << ToString(codec);
-      result = false;
-      break;
-    }
+  for (auto& kv : recv_streams_) {
+    kv.second->RecreateAudioReceiveStream(decoder_map);
   }
-  if (result) {
-    recv_codecs_ = codecs;
-  }
+  recv_codecs_ = codecs;
 
   if (desired_playout_ && !playout_) {
     ChangePlayout(desired_playout_);
   }
-  return result;
+  return true;
 }
 
 // Utility function called from SetSendParameters() to extract current send
diff --git a/webrtc/media/engine/webrtcvoiceengine_unittest.cc b/webrtc/media/engine/webrtcvoiceengine_unittest.cc
index f15bde6..6038934 100644
--- a/webrtc/media/engine/webrtcvoiceengine_unittest.cc
+++ b/webrtc/media/engine/webrtcvoiceengine_unittest.cc
@@ -107,7 +107,6 @@
   explicit WebRtcVoiceEngineTestFake(const char* field_trials)
       : call_(webrtc::Call::Config(&event_log_)), voe_(&apm_),
         override_field_trials_(field_trials) {
-    auto factory = webrtc::MockAudioDecoderFactory::CreateUnusedFactory();
     EXPECT_CALL(adm_, AddRef()).WillOnce(Return(0));
     EXPECT_CALL(adm_, Release()).WillOnce(Return(0));
     EXPECT_CALL(adm_, BuiltInAECIsAvailable()).WillOnce(Return(false));
@@ -116,8 +115,12 @@
     EXPECT_CALL(apm_, ApplyConfig(testing::_));
     EXPECT_CALL(apm_, SetExtraOptions(testing::_));
     EXPECT_CALL(apm_, Initialize()).WillOnce(Return(0));
-    engine_.reset(new cricket::WebRtcVoiceEngine(&adm_, factory, nullptr,
-                                                 new FakeVoEWrapper(&voe_)));
+    // TODO(kwiberg): We should use a mock AudioDecoderFactory, but a bunch of
+    // the tests here probe the specific set of codecs provided by the builtin
+    // factory. Those tests should probably be moved elsewhere.
+    engine_.reset(new cricket::WebRtcVoiceEngine(
+        &adm_, webrtc::CreateBuiltinAudioDecoderFactory(), nullptr,
+        new FakeVoEWrapper(&voe_)));
     send_parameters_.codecs.push_back(kPcmuCodec);
     recv_parameters_.codecs.push_back(kPcmuCodec);
   }
@@ -873,14 +876,9 @@
   parameters.codecs[0].id = 106;  // collide with existing CN 32k
   EXPECT_TRUE(channel_->SetRecvParameters(parameters));
 
-  int channel_num2 = voe_.GetLastChannel();
-  webrtc::CodecInst gcodec;
-  rtc::strcpyn(gcodec.plname, arraysize(gcodec.plname), "ISAC");
-  gcodec.plfreq = 16000;
-  gcodec.channels = 1;
-  EXPECT_EQ(0, voe_.GetRecPayloadType(channel_num2, gcodec));
-  EXPECT_EQ(106, gcodec.pltype);
-  EXPECT_STREQ("ISAC", gcodec.plname);
+  const auto& dm = GetRecvStreamConfig(kSsrc1).decoder_map;
+  ASSERT_EQ(1, dm.count(106));
+  EXPECT_EQ(webrtc::SdpAudioFormat("isac", 16000, 1), dm.at(106));
 }
 
 // Test that we can apply the same set of codecs again while playing.
diff --git a/webrtc/modules/audio_coding/acm2/audio_coding_module_unittest.cc b/webrtc/modules/audio_coding/acm2/audio_coding_module_unittest.cc
index be5e2c5..574b15d 100644
--- a/webrtc/modules/audio_coding/acm2/audio_coding_module_unittest.cc
+++ b/webrtc/modules/audio_coding/acm2/audio_coding_module_unittest.cc
@@ -1041,6 +1041,10 @@
     std::vector<AudioCodecSpec> GetSupportedDecoders() override {
       return fact_->GetSupportedDecoders();
     }
+    bool IsSupportedDecoder(const SdpAudioFormat& format) override {
+      return format.name == "MockPCMu" ? true
+                                       : fact_->IsSupportedDecoder(format);
+    }
     std::unique_ptr<AudioDecoder> MakeAudioDecoder(
         const SdpAudioFormat& format) override {
       return format.name == "MockPCMu" ? std::move(mock_decoder_)
diff --git a/webrtc/modules/audio_coding/acm2/rent_a_codec.cc b/webrtc/modules/audio_coding/acm2/rent_a_codec.cc
index 6787690..f0ed301 100644
--- a/webrtc/modules/audio_coding/acm2/rent_a_codec.cc
+++ b/webrtc/modules/audio_coding/acm2/rent_a_codec.cc
@@ -87,9 +87,7 @@
     case NetEqDecoder::kDecoderG722_2ch:
       return rtc::Optional<SdpAudioFormat>(SdpAudioFormat("g722", 8000, 2));
     case NetEqDecoder::kDecoderOpus:
-      return rtc::Optional<SdpAudioFormat>(
-          SdpAudioFormat("opus", 48000, 2,
-                         std::map<std::string, std::string>{{"stereo", "0"}}));
+      return rtc::Optional<SdpAudioFormat>(SdpAudioFormat("opus", 48000, 2));
     case NetEqDecoder::kDecoderOpus_2ch:
       return rtc::Optional<SdpAudioFormat>(
           SdpAudioFormat("opus", 48000, 2,
diff --git a/webrtc/modules/audio_coding/codecs/audio_decoder_factory.h b/webrtc/modules/audio_coding/codecs/audio_decoder_factory.h
index 93fca41..ca578f3 100644
--- a/webrtc/modules/audio_coding/codecs/audio_decoder_factory.h
+++ b/webrtc/modules/audio_coding/codecs/audio_decoder_factory.h
@@ -27,6 +27,8 @@
  public:
   virtual std::vector<AudioCodecSpec> GetSupportedDecoders() = 0;
 
+  virtual bool IsSupportedDecoder(const SdpAudioFormat& format) = 0;
+
   virtual std::unique_ptr<AudioDecoder> MakeAudioDecoder(
       const SdpAudioFormat& format) = 0;
 };
diff --git a/webrtc/modules/audio_coding/codecs/audio_decoder_factory_unittest.cc b/webrtc/modules/audio_coding/codecs/audio_decoder_factory_unittest.cc
index 6b63f98..0379613 100644
--- a/webrtc/modules/audio_coding/codecs/audio_decoder_factory_unittest.cc
+++ b/webrtc/modules/audio_coding/codecs/audio_decoder_factory_unittest.cc
@@ -122,8 +122,8 @@
         if (stereo != "XX") {
           params["stereo"] = stereo;
         }
-        bool good =
-            (hz == 48000 && channels == 2 && (stereo == "0" || stereo == "1"));
+        const bool good = (hz == 48000 && channels == 2 &&
+                           (stereo == "XX" || stereo == "0" || stereo == "1"));
         EXPECT_EQ(good, static_cast<bool>(adf->MakeAudioDecoder(SdpAudioFormat(
                             "opus", hz, channels, std::move(params)))));
       }
diff --git a/webrtc/modules/audio_coding/codecs/audio_format.cc b/webrtc/modules/audio_coding/codecs/audio_format.cc
index ebd7cb0..88e42c5 100644
--- a/webrtc/modules/audio_coding/codecs/audio_format.cc
+++ b/webrtc/modules/audio_coding/codecs/audio_format.cc
@@ -22,14 +22,28 @@
                                int num_channels)
     : name(name), clockrate_hz(clockrate_hz), num_channels(num_channels) {}
 
+SdpAudioFormat::SdpAudioFormat(const std::string& name,
+                               int clockrate_hz,
+                               int num_channels)
+    : name(name), clockrate_hz(clockrate_hz), num_channels(num_channels) {}
+
 SdpAudioFormat::SdpAudioFormat(const char* name,
                                int clockrate_hz,
                                int num_channels,
-                               Parameters&& param)
+                               const Parameters& param)
     : name(name),
       clockrate_hz(clockrate_hz),
       num_channels(num_channels),
-      parameters(std::move(param)) {}
+      parameters(param) {}
+
+SdpAudioFormat::SdpAudioFormat(const std::string& name,
+                               int clockrate_hz,
+                               int num_channels,
+                               const Parameters& param)
+    : name(name),
+      clockrate_hz(clockrate_hz),
+      num_channels(num_channels),
+      parameters(param) {}
 
 SdpAudioFormat::~SdpAudioFormat() = default;
 SdpAudioFormat& SdpAudioFormat::operator=(const SdpAudioFormat&) = default;
diff --git a/webrtc/modules/audio_coding/codecs/audio_format.h b/webrtc/modules/audio_coding/codecs/audio_format.h
index 1199cc2..b3f803c 100644
--- a/webrtc/modules/audio_coding/codecs/audio_format.h
+++ b/webrtc/modules/audio_coding/codecs/audio_format.h
@@ -26,10 +26,15 @@
   SdpAudioFormat(const SdpAudioFormat&);
   SdpAudioFormat(SdpAudioFormat&&);
   SdpAudioFormat(const char* name, int clockrate_hz, int num_channels);
+  SdpAudioFormat(const std::string& name, int clockrate_hz, int num_channels);
   SdpAudioFormat(const char* name,
                  int clockrate_hz,
                  int num_channels,
-                 Parameters&& param);
+                 const Parameters& param);
+  SdpAudioFormat(const std::string& name,
+                 int clockrate_hz,
+                 int num_channels,
+                 const Parameters& param);
   ~SdpAudioFormat();
 
   SdpAudioFormat& operator=(const SdpAudioFormat&);
diff --git a/webrtc/modules/audio_coding/codecs/audio_format_conversion.cc b/webrtc/modules/audio_coding/codecs/audio_format_conversion.cc
index ef9aa44..5d42409 100644
--- a/webrtc/modules/audio_coding/codecs/audio_format_conversion.cc
+++ b/webrtc/modules/audio_coding/codecs/audio_format_conversion.cc
@@ -10,21 +10,79 @@
 
 #include "webrtc/modules/audio_coding/codecs/audio_format_conversion.h"
 
+#include <string.h>
+
+#include "webrtc/base/array_view.h"
 #include "webrtc/base/checks.h"
+#include "webrtc/base/optional.h"
 #include "webrtc/base/safe_conversions.h"
+#include "webrtc/base/sanitizer.h"
 
 namespace webrtc {
 
+namespace {
+
+CodecInst MakeCodecInst(int payload_type,
+                        const char* name,
+                        int sample_rate,
+                        int num_channels) {
+  // Create a CodecInst with some fields set. The remaining fields are zeroed,
+  // but we tell MSan to consider them uninitialized.
+  CodecInst ci = {0};
+  rtc::MsanMarkUninitialized(rtc::MakeArrayView(&ci, 1));
+  ci.pltype = payload_type;
+  strncpy(ci.plname, name, sizeof(ci.plname));
+  ci.plname[sizeof(ci.plname) - 1] = '\0';
+  ci.plfreq = sample_rate;
+  ci.channels = rtc::checked_cast<size_t>(num_channels);
+  return ci;
+}
+
+}  // namespace
+
 SdpAudioFormat CodecInstToSdp(const CodecInst& ci) {
-  if (STR_CASE_CMP(ci.plname, "g722") == 0 && ci.plfreq == 16000) {
+  if (STR_CASE_CMP(ci.plname, "g722") == 0) {
+    RTC_CHECK_EQ(16000, ci.plfreq);
     RTC_CHECK(ci.channels == 1 || ci.channels == 2);
     return {"g722", 8000, static_cast<int>(ci.channels)};
-  } else if (STR_CASE_CMP(ci.plname, "opus") == 0 && ci.plfreq == 48000) {
+  } else if (STR_CASE_CMP(ci.plname, "opus") == 0) {
+    RTC_CHECK_EQ(48000, ci.plfreq);
     RTC_CHECK(ci.channels == 1 || ci.channels == 2);
-    return {"opus", 48000, 2, {{"stereo", ci.channels == 1 ? "0" : "1"}}};
+    return ci.channels == 1
+               ? SdpAudioFormat("opus", 48000, 2)
+               : SdpAudioFormat("opus", 48000, 2, {{"stereo", "1"}});
   } else {
     return {ci.plname, ci.plfreq, rtc::checked_cast<int>(ci.channels)};
   }
 }
 
+CodecInst SdpToCodecInst(int payload_type, const SdpAudioFormat& audio_format) {
+  if (STR_CASE_CMP(audio_format.name.c_str(), "g722") == 0) {
+    RTC_CHECK_EQ(8000, audio_format.clockrate_hz);
+    RTC_CHECK(audio_format.num_channels == 1 || audio_format.num_channels == 2);
+    return MakeCodecInst(payload_type, "g722", 16000,
+                         audio_format.num_channels);
+  } else if (STR_CASE_CMP(audio_format.name.c_str(), "opus") == 0) {
+    RTC_CHECK_EQ(48000, audio_format.clockrate_hz);
+    RTC_CHECK_EQ(2, audio_format.num_channels);
+    const int num_channels = [&] {
+      auto stereo = audio_format.parameters.find("stereo");
+      if (stereo != audio_format.parameters.end()) {
+        if (stereo->second == "0") {
+          return 1;
+        } else if (stereo->second == "1") {
+          return 2;
+        } else {
+          RTC_CHECK(false);  // Bad stereo parameter.
+        }
+      }
+      return 1;  // Default to mono.
+    }();
+    return MakeCodecInst(payload_type, "opus", 48000, num_channels);
+  } else {
+    return MakeCodecInst(payload_type, audio_format.name.c_str(),
+                         audio_format.clockrate_hz, audio_format.num_channels);
+  }
+}
+
 }  // namespace webrtc
diff --git a/webrtc/modules/audio_coding/codecs/audio_format_conversion.h b/webrtc/modules/audio_coding/codecs/audio_format_conversion.h
index ff71282..9267a52 100644
--- a/webrtc/modules/audio_coding/codecs/audio_format_conversion.h
+++ b/webrtc/modules/audio_coding/codecs/audio_format_conversion.h
@@ -17,6 +17,7 @@
 namespace webrtc {
 
 SdpAudioFormat CodecInstToSdp(const CodecInst& codec_inst);
+CodecInst SdpToCodecInst(int payload_type, const SdpAudioFormat& audio_format);
 
 }  // namespace webrtc
 
diff --git a/webrtc/modules/audio_coding/codecs/builtin_audio_decoder_factory.cc b/webrtc/modules/audio_coding/codecs/builtin_audio_decoder_factory.cc
index 05b65c3..54ec776 100644
--- a/webrtc/modules/audio_coding/codecs/builtin_audio_decoder_factory.cc
+++ b/webrtc/modules/audio_coding/codecs/builtin_audio_decoder_factory.cc
@@ -40,89 +40,133 @@
 
 struct NamedDecoderConstructor {
   const char* name;
-  std::unique_ptr<AudioDecoder> (*constructor)(const SdpAudioFormat&);
-};
 
-std::unique_ptr<AudioDecoder> Unique(AudioDecoder* d) {
-  return std::unique_ptr<AudioDecoder>(d);
-}
+  // If |format| is good, return true and (if |out| isn't null) reset |*out| to
+  // a new decoder object. If the |format| is not good, return false.
+  bool (*constructor)(const SdpAudioFormat& format,
+                      std::unique_ptr<AudioDecoder>* out);
+};
 
 // TODO(kwiberg): These factory functions should probably be moved to each
 // decoder.
 NamedDecoderConstructor decoder_constructors[] = {
     {"pcmu",
-     [](const SdpAudioFormat& format) {
-       return format.clockrate_hz == 8000 && format.num_channels >= 1
-                  ? Unique(new AudioDecoderPcmU(format.num_channels))
-                  : nullptr;
+     [](const SdpAudioFormat& format, std::unique_ptr<AudioDecoder>* out) {
+       if (format.clockrate_hz == 8000 && format.num_channels >= 1) {
+         if (out) {
+           out->reset(new AudioDecoderPcmU(format.num_channels));
+         }
+         return true;
+       } else {
+         return false;
+       }
      }},
     {"pcma",
-     [](const SdpAudioFormat& format) {
-       return format.clockrate_hz == 8000 && format.num_channels >= 1
-                  ? Unique(new AudioDecoderPcmA(format.num_channels))
-                  : nullptr;
+     [](const SdpAudioFormat& format, std::unique_ptr<AudioDecoder>* out) {
+       if (format.clockrate_hz == 8000 && format.num_channels >= 1) {
+         if (out) {
+           out->reset(new AudioDecoderPcmA(format.num_channels));
+         }
+         return true;
+       } else {
+         return false;
+       }
      }},
 #ifdef WEBRTC_CODEC_ILBC
     {"ilbc",
-     [](const SdpAudioFormat& format) {
-       return format.clockrate_hz == 8000 && format.num_channels == 1
-                  ? Unique(new AudioDecoderIlbc)
-                  : nullptr;
+     [](const SdpAudioFormat& format, std::unique_ptr<AudioDecoder>* out) {
+       if (format.clockrate_hz == 8000 && format.num_channels == 1) {
+         if (out) {
+           out->reset(new AudioDecoderIlbc);
+         }
+         return true;
+       } else {
+         return false;
+       }
      }},
 #endif
 #if defined(WEBRTC_CODEC_ISACFX)
     {"isac",
-     [](const SdpAudioFormat& format) {
-       return format.clockrate_hz == 16000 && format.num_channels == 1
-                  ? Unique(new AudioDecoderIsacFix(format.clockrate_hz))
-                  : nullptr;
+     [](const SdpAudioFormat& format, std::unique_ptr<AudioDecoder>* out) {
+       if (format.clockrate_hz == 16000 && format.num_channels == 1) {
+         if (out) {
+           out->reset(new AudioDecoderIsacFix(format.clockrate_hz));
+         }
+         return true;
+       } else {
+         return false;
+       }
      }},
 #elif defined(WEBRTC_CODEC_ISAC)
     {"isac",
-     [](const SdpAudioFormat& format) {
-       return (format.clockrate_hz == 16000 || format.clockrate_hz == 32000) &&
-                      format.num_channels == 1
-                  ? Unique(new AudioDecoderIsac(format.clockrate_hz))
-                  : nullptr;
+     [](const SdpAudioFormat& format, std::unique_ptr<AudioDecoder>* out) {
+       if ((format.clockrate_hz == 16000 || format.clockrate_hz == 32000) &&
+           format.num_channels == 1) {
+         if (out) {
+           out->reset(new AudioDecoderIsac(format.clockrate_hz));
+         }
+         return true;
+       } else {
+         return false;
+       }
      }},
 #endif
     {"l16",
-     [](const SdpAudioFormat& format) {
-       return format.num_channels >= 1
-                  ? Unique(new AudioDecoderPcm16B(format.clockrate_hz,
-                                                  format.num_channels))
-                  : nullptr;
+     [](const SdpAudioFormat& format, std::unique_ptr<AudioDecoder>* out) {
+       if (format.num_channels >= 1) {
+         if (out) {
+           out->reset(new AudioDecoderPcm16B(format.clockrate_hz,
+                                             format.num_channels));
+         }
+         return true;
+       } else {
+         return false;
+       }
      }},
 #ifdef WEBRTC_CODEC_G722
     {"g722",
-     [](const SdpAudioFormat& format) {
+     [](const SdpAudioFormat& format, std::unique_ptr<AudioDecoder>* out) {
        if (format.clockrate_hz == 8000) {
-         if (format.num_channels == 1)
-           return Unique(new AudioDecoderG722);
-         if (format.num_channels == 2)
-           return Unique(new AudioDecoderG722Stereo);
+         if (format.num_channels == 1) {
+           if (out) {
+             out->reset(new AudioDecoderG722);
+           }
+           return true;
+         } else if (format.num_channels == 2) {
+           if (out) {
+             out->reset(new AudioDecoderG722Stereo);
+           }
+           return true;
+         }
        }
-       return Unique(nullptr);
+       return false;
      }},
 #endif
 #ifdef WEBRTC_CODEC_OPUS
     {"opus",
-     [](const SdpAudioFormat& format) {
-       rtc::Optional<int> num_channels = [&] {
+     [](const SdpAudioFormat& format, std::unique_ptr<AudioDecoder>* out) {
+       const rtc::Optional<int> num_channels = [&] {
          auto stereo = format.parameters.find("stereo");
          if (stereo != format.parameters.end()) {
            if (stereo->second == "0") {
              return rtc::Optional<int>(1);
            } else if (stereo->second == "1") {
              return rtc::Optional<int>(2);
+           } else {
+             return rtc::Optional<int>();  // Bad stereo parameter.
            }
          }
-         return rtc::Optional<int>();
+         return rtc::Optional<int>(1);  // Default to mono.
        }();
-       return format.clockrate_hz == 48000 && format.num_channels == 2 &&
-                      num_channels
-                  ? Unique(new AudioDecoderOpus(*num_channels))
-                  : nullptr;
+       if (format.clockrate_hz == 48000 && format.num_channels == 2 &&
+           num_channels) {
+         if (out) {
+           out->reset(new AudioDecoderOpus(*num_channels));
+         }
+         return true;
+       } else {
+         return false;
+       }
      }},
 #endif
 };
@@ -140,37 +184,48 @@
       },
 #endif
 #if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX))
-      { { "isac", 16000, 1 }, true },
+      {{"isac", 16000, 1}, true},
 #endif
 #if (defined(WEBRTC_CODEC_ISAC))
-      { { "isac", 32000, 1 }, true },
+      {{"isac", 32000, 1}, true},
 #endif
 #ifdef WEBRTC_CODEC_G722
-      { { "G722", 8000,  1 }, true },
+      {{"G722", 8000, 1}, true},
 #endif
 #ifdef WEBRTC_CODEC_ILBC
-      { { "iLBC", 8000,  1 }, true },
+      {{"iLBC", 8000, 1}, true},
 #endif
-      { { "PCMU", 8000,  1 }, true },
-      { { "PCMA", 8000,  1 }, true }
+      {{"PCMU", 8000, 1}, true},
+      {{"PCMA", 8000, 1}, true}
     };
 
     return specs;
   }
 
+  bool IsSupportedDecoder(const SdpAudioFormat& format) override {
+    for (const auto& dc : decoder_constructors) {
+      if (STR_CASE_CMP(format.name.c_str(), dc.name) == 0) {
+        return dc.constructor(format, nullptr);
+      }
+    }
+    return false;
+  }
+
   std::unique_ptr<AudioDecoder> MakeAudioDecoder(
       const SdpAudioFormat& format) override {
     for (const auto& dc : decoder_constructors) {
       if (STR_CASE_CMP(format.name.c_str(), dc.name) == 0) {
-        std::unique_ptr<AudioDecoder> dec = dc.constructor(format);
-        if (dec) {
+        std::unique_ptr<AudioDecoder> decoder;
+        bool ok = dc.constructor(format, &decoder);
+        RTC_DCHECK_EQ(ok, decoder != nullptr);
+        if (decoder) {
           const int expected_sample_rate_hz =
               STR_CASE_CMP(format.name.c_str(), "g722") == 0
                   ? 2 * format.clockrate_hz
                   : format.clockrate_hz;
-          RTC_CHECK_EQ(expected_sample_rate_hz, dec->SampleRateHz());
+          RTC_CHECK_EQ(expected_sample_rate_hz, decoder->SampleRateHz());
         }
-        return dec;
+        return decoder;
       }
     }
     return nullptr;
diff --git a/webrtc/modules/audio_coding/codecs/mock/mock_audio_decoder_factory.h b/webrtc/modules/audio_coding/codecs/mock/mock_audio_decoder_factory.h
index f2ef170..b988b85 100644
--- a/webrtc/modules/audio_coding/codecs/mock/mock_audio_decoder_factory.h
+++ b/webrtc/modules/audio_coding/codecs/mock/mock_audio_decoder_factory.h
@@ -15,6 +15,7 @@
 
 #include "webrtc/base/scoped_ref_ptr.h"
 #include "webrtc/modules/audio_coding/codecs/audio_decoder_factory.h"
+#include "webrtc/modules/audio_coding/codecs/builtin_audio_decoder_factory.h"
 #include "webrtc/test/gmock.h"
 
 namespace webrtc {
@@ -22,6 +23,7 @@
 class MockAudioDecoderFactory : public AudioDecoderFactory {
  public:
   MOCK_METHOD0(GetSupportedDecoders, std::vector<AudioCodecSpec>());
+  MOCK_METHOD1(IsSupportedDecoder, bool(const SdpAudioFormat&));
   std::unique_ptr<AudioDecoder> MakeAudioDecoder(
       const SdpAudioFormat& format) {
     std::unique_ptr<AudioDecoder> return_value;
@@ -46,6 +48,8 @@
     ON_CALL(*factory.get(), GetSupportedDecoders())
         .WillByDefault(Return(std::vector<webrtc::AudioCodecSpec>()));
     EXPECT_CALL(*factory.get(), GetSupportedDecoders()).Times(AnyNumber());
+    ON_CALL(*factory, IsSupportedDecoder(_)).WillByDefault(Return(false));
+    EXPECT_CALL(*factory, IsSupportedDecoder(_)).Times(AnyNumber());
     EXPECT_CALL(*factory.get(), MakeAudioDecoderMock(_, _)).Times(0);
     return factory;
   }
@@ -65,6 +69,8 @@
     ON_CALL(*factory.get(), GetSupportedDecoders())
         .WillByDefault(Return(std::vector<webrtc::AudioCodecSpec>()));
     EXPECT_CALL(*factory.get(), GetSupportedDecoders()).Times(AnyNumber());
+    ON_CALL(*factory, IsSupportedDecoder(_)).WillByDefault(Return(false));
+    EXPECT_CALL(*factory, IsSupportedDecoder(_)).Times(AnyNumber());
     ON_CALL(*factory.get(), MakeAudioDecoderMock(_, _))
         .WillByDefault(SetArgPointee<1>(nullptr));
     EXPECT_CALL(*factory.get(), MakeAudioDecoderMock(_, _)).Times(AnyNumber());
diff --git a/webrtc/tools/BUILD.gn b/webrtc/tools/BUILD.gn
index 6dc3708..b1e603c 100644
--- a/webrtc/tools/BUILD.gn
+++ b/webrtc/tools/BUILD.gn
@@ -205,6 +205,9 @@
       "../call:call_interfaces",
       "../logging:rtc_event_log_impl",
       "../logging:rtc_event_log_parser",
+
+      # TODO(kwiberg): Remove this dependency.
+      "../modules/audio_coding:audio_format",
       "../modules/congestion_controller",
       "../modules/rtp_rtcp",
       "../system_wrappers:system_wrappers_default",
diff --git a/webrtc/voice_engine/channel.cc b/webrtc/voice_engine/channel.cc
index 68f2e2d..5231691 100644
--- a/webrtc/voice_engine/channel.cc
+++ b/webrtc/voice_engine/channel.cc
@@ -1372,6 +1372,11 @@
 }
 
 int32_t Channel::SetRecPayloadType(const CodecInst& codec) {
+  return SetRecPayloadType(codec.pltype, CodecInstToSdp(codec));
+}
+
+int32_t Channel::SetRecPayloadType(int payload_type,
+                                   const SdpAudioFormat& format) {
   WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
                "Channel::SetRecPayloadType()");
 
@@ -1382,7 +1387,22 @@
     return -1;
   }
 
-  if (codec.pltype == -1) {
+  const CodecInst codec = [&] {
+    CodecInst c = SdpToCodecInst(payload_type, format);
+
+    // Bug 6986: Emulate an old bug that caused us to always choose to decode
+    // Opus in stereo. To be able to remove this, we first need to fix the
+    // other half of bug 6986, which is about losing the Opus "stereo"
+    // parameter.
+    // TODO(kwiberg): Remove this special case, a.k.a. fix bug 6986.
+    if (STR_CASE_CMP(codec.plname, "opus") == 0) {
+      c.channels = 2;
+    }
+
+    return c;
+  }();
+
+  if (payload_type == -1) {
     // De-register the selected codec (RTP/RTCP module and ACM)
 
     int8_t pltype(-1);
@@ -1420,11 +1440,9 @@
       return -1;
     }
   }
-  if (!audio_coding_->RegisterReceiveCodec(codec.pltype,
-                                           CodecInstToSdp(codec))) {
-    audio_coding_->UnregisterReceiveCodec(codec.pltype);
-    if (!audio_coding_->RegisterReceiveCodec(codec.pltype,
-                                             CodecInstToSdp(codec))) {
+  if (!audio_coding_->RegisterReceiveCodec(payload_type, format)) {
+    audio_coding_->UnregisterReceiveCodec(payload_type);
+    if (!audio_coding_->RegisterReceiveCodec(payload_type, format)) {
       _engineStatisticsPtr->SetLastError(
           VE_AUDIO_CODING_MODULE_ERROR, kTraceError,
           "SetRecPayloadType() ACM registration failed - 1");
diff --git a/webrtc/voice_engine/channel.h b/webrtc/voice_engine/channel.h
index d2b24a2..62adc5b 100644
--- a/webrtc/voice_engine/channel.h
+++ b/webrtc/voice_engine/channel.h
@@ -196,6 +196,7 @@
   int32_t SetVADStatus(bool enableVAD, ACMVADMode mode, bool disableDTX);
   int32_t GetVADStatus(bool& enabledVAD, ACMVADMode& mode, bool& disabledDTX);
   int32_t SetRecPayloadType(const CodecInst& codec);
+  int32_t SetRecPayloadType(int payload_type, const SdpAudioFormat& format);
   int32_t GetRecPayloadType(CodecInst& codec);
   int32_t SetSendCNPayloadType(int type, PayloadFrequencies frequency);
   int SetOpusMaxPlaybackRate(int frequency_hz);
diff --git a/webrtc/voice_engine/channel_proxy.cc b/webrtc/voice_engine/channel_proxy.cc
index 2dd74fa..59b920f 100644
--- a/webrtc/voice_engine/channel_proxy.cc
+++ b/webrtc/voice_engine/channel_proxy.cc
@@ -153,6 +153,13 @@
   channel()->SetBitRate(bitrate_bps, probing_interval_ms);
 }
 
+void ChannelProxy::SetRecPayloadType(int payload_type,
+                                     const SdpAudioFormat& format) {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  const int result = channel()->SetRecPayloadType(payload_type, format);
+  RTC_DCHECK_EQ(0, result);
+}
+
 void ChannelProxy::SetSink(std::unique_ptr<AudioSinkInterface> sink) {
   RTC_DCHECK(thread_checker_.CalledOnValidThread());
   channel()->SetSink(std::move(sink));
diff --git a/webrtc/voice_engine/channel_proxy.h b/webrtc/voice_engine/channel_proxy.h
index 8a78069..4bd76a4 100644
--- a/webrtc/voice_engine/channel_proxy.h
+++ b/webrtc/voice_engine/channel_proxy.h
@@ -74,6 +74,8 @@
                                                 int payload_frequency);
   virtual bool SendTelephoneEventOutband(int event, int duration_ms);
   virtual void SetBitrate(int bitrate_bps, int64_t probing_interval_ms);
+  virtual void SetRecPayloadType(int payload_type,
+                                 const SdpAudioFormat& format);
   virtual void SetSink(std::unique_ptr<AudioSinkInterface> sink);
   virtual void SetInputMute(bool muted);
   virtual void RegisterExternalTransport(Transport* transport);