Decoder for multistream Opus.

See https://webrtc-review.googlesource.com/c/src/+/121764 for the
overall vision.

This CL adds a multistream Opus decoder. It's a new code-path to not
interfere with the standard Opus decoder. We introduce new SDP syntax,
which uses terminology of RFC 7845. We also set up the decoder side to
parse it. The encoder part will come in a later CL.

E.g. this is the new SDP syntax for 6.1 surround sound:
"multiopus/48000/6 channel_mapping=0,4,1,2,3,5 num_streams=4 coupled_streams=2"

Bug: webrtc:8649
Change-Id: Ifbc584cbb6d07aed373f223512a20d6d72cec5ec
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/129768
Commit-Queue: Alex Loiko <aleloi@webrtc.org>
Reviewed-by: Oskar Sundbom <ossu@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27493}
diff --git a/api/audio_codecs/BUILD.gn b/api/audio_codecs/BUILD.gn
index d076826..2bbeee5 100644
--- a/api/audio_codecs/BUILD.gn
+++ b/api/audio_codecs/BUILD.gn
@@ -66,7 +66,10 @@
     defines += [ "WEBRTC_USE_BUILTIN_ILBC=0" ]
   }
   if (rtc_include_opus) {
-    deps += [ "opus:audio_decoder_opus" ]
+    deps += [
+      "opus:audio_decoder_multiopus",
+      "opus:audio_decoder_opus",
+    ]
     defines += [ "WEBRTC_USE_BUILTIN_OPUS=1" ]
   } else {
     defines += [ "WEBRTC_USE_BUILTIN_OPUS=0" ]
diff --git a/api/audio_codecs/builtin_audio_decoder_factory.cc b/api/audio_codecs/builtin_audio_decoder_factory.cc
index e3ca1b0..963cfe5 100644
--- a/api/audio_codecs/builtin_audio_decoder_factory.cc
+++ b/api/audio_codecs/builtin_audio_decoder_factory.cc
@@ -22,6 +22,7 @@
 #endif
 #include "api/audio_codecs/isac/audio_decoder_isac.h"
 #if WEBRTC_USE_BUILTIN_OPUS
+#include "api/audio_codecs/opus/audio_decoder_multi_channel_opus.h"
 #include "api/audio_codecs/opus/audio_decoder_opus.h"  // nogncheck
 #endif
 
@@ -53,7 +54,7 @@
   return CreateAudioDecoderFactory<
 
 #if WEBRTC_USE_BUILTIN_OPUS
-      AudioDecoderOpus,
+      AudioDecoderOpus, NotAdvertised<AudioDecoderMultiChannelOpus>,
 #endif
 
       AudioDecoderIsac, AudioDecoderG722,
diff --git a/api/audio_codecs/opus/BUILD.gn b/api/audio_codecs/opus/BUILD.gn
index 5552c21..498e45a 100644
--- a/api/audio_codecs/opus/BUILD.gn
+++ b/api/audio_codecs/opus/BUILD.gn
@@ -15,6 +15,7 @@
 rtc_static_library("audio_encoder_opus_config") {
   visibility = [ "*" ]
   sources = [
+    "audio_decoder_multi_channel_opus_config.h",
     "audio_encoder_opus_config.cc",
     "audio_encoder_opus_config.h",
   ]
@@ -68,3 +69,22 @@
     "//third_party/abseil-cpp/absl/types:optional",
   ]
 }
+
+rtc_static_library("audio_decoder_multiopus") {
+  visibility = [ "*" ]
+  poisonous = [ "audio_codecs" ]
+  sources = [
+    "audio_decoder_multi_channel_opus.cc",
+    "audio_decoder_multi_channel_opus.h",
+  ]
+  deps = [
+    ":audio_encoder_opus_config",
+    "..:audio_codecs_api",
+    "../../../modules/audio_coding:webrtc_multiopus",
+    "../../../rtc_base:rtc_base_approved",
+    "../../../rtc_base/system:rtc_export",
+    "//third_party/abseil-cpp/absl/memory",
+    "//third_party/abseil-cpp/absl/strings",
+    "//third_party/abseil-cpp/absl/types:optional",
+  ]
+}
diff --git a/api/audio_codecs/opus/audio_decoder_multi_channel_opus.cc b/api/audio_codecs/opus/audio_decoder_multi_channel_opus.cc
new file mode 100644
index 0000000..6ba2b6d
--- /dev/null
+++ b/api/audio_codecs/opus/audio_decoder_multi_channel_opus.cc
@@ -0,0 +1,70 @@
+/*
+ *  Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "api/audio_codecs/opus/audio_decoder_multi_channel_opus.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "absl/memory/memory.h"
+#include "absl/strings/match.h"
+#include "modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_impl.h"
+
+namespace webrtc {
+
+absl::optional<AudioDecoderMultiChannelOpusConfig>
+AudioDecoderMultiChannelOpus::SdpToConfig(const SdpAudioFormat& format) {
+  return AudioDecoderMultiChannelOpusImpl::SdpToConfig(format);
+}
+
+void AudioDecoderMultiChannelOpus::AppendSupportedDecoders(
+    std::vector<AudioCodecSpec>* specs) {
+  // To get full utilization of the surround support of the Opus lib, we can
+  // mark which channel is the low frequency effects (LFE). But that is not done
+  // ATM.
+  {
+    AudioCodecInfo surround_5_1_opus_info{48000, 6,
+                                          /* default_bitrate_bps= */ 128000};
+    surround_5_1_opus_info.allow_comfort_noise = false;
+    surround_5_1_opus_info.supports_network_adaption = false;
+    SdpAudioFormat opus_format({"multiopus",
+                                48000,
+                                6,
+                                {{"minptime", "10"},
+                                 {"useinbandfec", "1"},
+                                 {"channel_mapping", "0,4,1,2,3,5"},
+                                 {"num_streams", "4"},
+                                 {"coupled_streams", "2"}}});
+    specs->push_back({std::move(opus_format), surround_5_1_opus_info});
+  }
+  {
+    AudioCodecInfo surround_7_1_opus_info{48000, 8,
+                                          /* default_bitrate_bps= */ 200000};
+    surround_7_1_opus_info.allow_comfort_noise = false;
+    surround_7_1_opus_info.supports_network_adaption = false;
+    SdpAudioFormat opus_format({"multiopus",
+                                48000,
+                                8,
+                                {{"minptime", "10"},
+                                 {"useinbandfec", "1"},
+                                 {"channel_mapping", "0,6,1,2,3,4,5,7"},
+                                 {"num_streams", "5"},
+                                 {"coupled_streams", "3"}}});
+    specs->push_back({std::move(opus_format), surround_7_1_opus_info});
+  }
+}
+
+std::unique_ptr<AudioDecoder> AudioDecoderMultiChannelOpus::MakeAudioDecoder(
+    AudioDecoderMultiChannelOpusConfig config,
+    absl::optional<AudioCodecPairId> /*codec_pair_id*/) {
+  return AudioDecoderMultiChannelOpusImpl::MakeAudioDecoder(config);
+}
+}  // namespace webrtc
diff --git a/api/audio_codecs/opus/audio_decoder_multi_channel_opus.h b/api/audio_codecs/opus/audio_decoder_multi_channel_opus.h
new file mode 100644
index 0000000..b5ca0fe
--- /dev/null
+++ b/api/audio_codecs/opus/audio_decoder_multi_channel_opus.h
@@ -0,0 +1,40 @@
+/*
+ *  Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_AUDIO_CODECS_OPUS_AUDIO_DECODER_MULTI_CHANNEL_OPUS_H_
+#define API_AUDIO_CODECS_OPUS_AUDIO_DECODER_MULTI_CHANNEL_OPUS_H_
+
+#include <memory>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/audio_codecs/audio_codec_pair_id.h"
+#include "api/audio_codecs/audio_decoder.h"
+#include "api/audio_codecs/audio_format.h"
+#include "api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// Opus decoder API for use as a template parameter to
+// CreateAudioDecoderFactory<...>().
+struct RTC_EXPORT AudioDecoderMultiChannelOpus {
+  using Config = AudioDecoderMultiChannelOpusConfig;
+  static absl::optional<AudioDecoderMultiChannelOpusConfig> SdpToConfig(
+      const SdpAudioFormat& audio_format);
+  static void AppendSupportedDecoders(std::vector<AudioCodecSpec>* specs);
+  static std::unique_ptr<AudioDecoder> MakeAudioDecoder(
+      AudioDecoderMultiChannelOpusConfig config,
+      absl::optional<AudioCodecPairId> codec_pair_id = absl::nullopt);
+};
+
+}  // namespace webrtc
+
+#endif  // API_AUDIO_CODECS_OPUS_AUDIO_DECODER_MULTI_CHANNEL_OPUS_H_
diff --git a/api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h b/api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h
new file mode 100644
index 0000000..30bc76e
--- /dev/null
+++ b/api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h
@@ -0,0 +1,63 @@
+/*
+ *  Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_AUDIO_CODECS_OPUS_AUDIO_DECODER_MULTI_CHANNEL_OPUS_CONFIG_H_
+#define API_AUDIO_CODECS_OPUS_AUDIO_DECODER_MULTI_CHANNEL_OPUS_CONFIG_H_
+
+#include <vector>
+
+namespace webrtc {
+struct AudioDecoderMultiChannelOpusConfig {
+  // The number of channels that the decoder will output.
+  int num_channels;
+
+  // Number of mono or stereo encoded Opus streams.
+  int num_streams;
+
+  // Number of channel pairs coupled together, see RFC 7845 section
+  // 5.1.1. Has to be less than the number of streams.
+  int coupled_streams;
+
+  // Channel mapping table, defines the mapping from encoded streams to output
+  // channels. See RFC 7845 section 5.1.1.
+  std::vector<unsigned char> channel_mapping;
+
+  bool IsOk() const {
+    if (num_channels < 0 || num_streams < 0 || coupled_streams < 0) {
+      return false;
+    }
+    if (num_streams < coupled_streams) {
+      return false;
+    }
+    if (channel_mapping.size() != static_cast<size_t>(num_channels)) {
+      return false;
+    }
+
+    // Every mono stream codes one channel, every coupled stream codes two. This
+    // is the total coded channel count:
+    const int max_coded_channel = num_streams + coupled_streams;
+    for (const auto& x : channel_mapping) {
+      // Coded channels >= max_coded_channel don't exist. Except for 255, which
+      // tells Opus to put silence in output channel x.
+      if (x >= max_coded_channel && x != 255) {
+        return false;
+      }
+    }
+
+    if (num_channels > 255 || max_coded_channel >= 255) {
+      return false;
+    }
+    return true;
+  }
+};
+
+}  // namespace webrtc
+
+#endif  //  API_AUDIO_CODECS_OPUS_AUDIO_DECODER_MULTI_CHANNEL_OPUS_CONFIG_H_
diff --git a/modules/audio_coding/BUILD.gn b/modules/audio_coding/BUILD.gn
index b19dcc0..0feee55 100644
--- a/modules/audio_coding/BUILD.gn
+++ b/modules/audio_coding/BUILD.gn
@@ -735,6 +735,22 @@
   ]
 }
 
+rtc_static_library("audio_coding_opus_common") {
+  sources = [
+    "codecs/opus/audio_coder_opus_common.cc",
+    "codecs/opus/audio_coder_opus_common.h",
+  ]
+
+  deps = [
+    "../../api:array_view",
+    "../../api/audio_codecs:audio_codecs_api",
+    "../../rtc_base:checks",
+    "../../rtc_base:stringutils",
+    "//third_party/abseil-cpp/absl/strings",
+    "//third_party/abseil-cpp/absl/types:optional",
+  ]
+}
+
 rtc_static_library("webrtc_opus") {
   visibility += webrtc_default_visibility
   poisonous = [ "audio_codecs" ]
@@ -746,6 +762,7 @@
   ]
 
   deps = [
+    ":audio_coding_opus_common",
     ":audio_network_adaptor",
     "../../api:array_view",
     "../../api/audio_codecs:audio_codecs_api",
@@ -761,7 +778,41 @@
     "//third_party/abseil-cpp/absl/strings",
     "//third_party/abseil-cpp/absl/types:optional",
   ]
-  public_deps = [
+  public_deps = [  # no-presubmit-check TODO(webrtc:8603)
+    ":webrtc_opus_c",
+  ]
+
+  defines = audio_codec_defines
+
+  if (rtc_build_opus) {
+    public_deps += [ rtc_opus_dir ]  # no-presubmit-check TODO(webrtc:8603)
+  } else if (build_with_mozilla) {
+    include_dirs = [ "/media/libopus/include" ]
+  }
+}
+
+rtc_static_library("webrtc_multiopus") {
+  visibility += webrtc_default_visibility
+  poisonous = [ "audio_codecs" ]
+  sources = [
+    "codecs/opus/audio_decoder_multi_channel_opus_impl.cc",
+    "codecs/opus/audio_decoder_multi_channel_opus_impl.h",
+  ]
+
+  deps = [
+    ":audio_coding_opus_common",
+    "../../api/audio_codecs:audio_codecs_api",
+    "../../api/audio_codecs/opus:audio_encoder_opus_config",
+    "../../rtc_base:checks",
+    "../../rtc_base:logging",
+    "../../rtc_base:macromagic",
+    "../../rtc_base:rtc_base_approved",
+    "../../rtc_base:safe_minmax",
+    "../../rtc_base:stringutils",
+    "//third_party/abseil-cpp/absl/memory",
+    "//third_party/abseil-cpp/absl/types:optional",
+  ]
+  public_deps = [  # no-presubmit-check TODO(webrtc:8603)
     ":webrtc_opus_c",
   ]
 
@@ -1876,6 +1927,7 @@
       "codecs/isac/main/source/isac_unittest.cc",
       "codecs/isac/unittest.cc",
       "codecs/legacy_encoded_audio_frame_unittest.cc",
+      "codecs/opus/audio_decoder_multi_channel_opus_unittest.cc",
       "codecs/opus/audio_encoder_opus_unittest.cc",
       "codecs/opus/opus_bandwidth_unittest.cc",
       "codecs/opus/opus_unittest.cc",
@@ -1931,6 +1983,7 @@
       ":acm_send_test",
       ":audio_coding",
       ":audio_coding_module_typedefs",
+      ":audio_coding_opus_common",
       ":audio_encoder_cng",
       ":audio_network_adaptor",
       ":g711",
@@ -1953,6 +2006,7 @@
       "../../api/audio_codecs:audio_codecs_api",
       "../../api/audio_codecs:builtin_audio_decoder_factory",
       "../../api/audio_codecs:builtin_audio_encoder_factory",
+      "../../api/audio_codecs/opus:audio_decoder_multiopus",
       "../../api/audio_codecs/opus:audio_decoder_opus",
       "../../api/audio_codecs/opus:audio_encoder_opus",
       "../../common_audio",
diff --git a/modules/audio_coding/acm2/audio_coding_module_unittest.cc b/modules/audio_coding/acm2/audio_coding_module_unittest.cc
index ef5f024..df4d75f 100644
--- a/modules/audio_coding/acm2/audio_coding_module_unittest.cc
+++ b/modules/audio_coding/acm2/audio_coding_module_unittest.cc
@@ -17,6 +17,7 @@
 #include "api/audio_codecs/audio_encoder.h"
 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
 #include "api/audio_codecs/builtin_audio_encoder_factory.h"
+#include "api/audio_codecs/opus/audio_decoder_multi_channel_opus.h"
 #include "api/audio_codecs/opus/audio_decoder_opus.h"
 #include "api/audio_codecs/opus/audio_encoder_opus.h"
 #include "modules/audio_coding/acm2/acm_receive_test.h"
@@ -1522,20 +1523,31 @@
 
   // TODO(webrtc:8649): change to higher level
   // AudioEncoderOpus::MakeAudioEncoder once a multistream encoder can be set up
-  // from SDP.
+  // from SDP. - This is now done for the Decoder.
+
+  // The Encoder and Decoder are set up differently (and the test is disabled)
+  // until the changes from
+  // https://webrtc-review.googlesource.com/c/src/+/121764 land.
   AudioEncoderOpusConfig config = *AudioEncoderOpus::SdpToConfig(
       SdpAudioFormat("opus", 48000, 2, {{"stereo", "1"}}));
   config.num_channels = kNumChannels;
   config.bitrate_bps = kBitrateBps;
 
+  const auto sdp_format = SdpAudioFormat(
+      "multiopus", 48000, kNumChannels,
+      {{"channel_mapping", "0,1,2,3"}, {"coupled_streams", "2"}});
+  const auto decoder_config =
+      AudioDecoderMultiChannelOpus::SdpToConfig(sdp_format);
+  const auto opus_decoder =
+      AudioDecoderMultiChannelOpus::MakeAudioDecoder(*decoder_config);
+
   ASSERT_NO_FATAL_FAILURE(SetUpTestExternalEncoder(
       absl::make_unique<AudioEncoderOpusImpl>(config, kOpusPayloadType),
       kOpusPayloadType));
 
-  AudioDecoderOpusImpl opus_decoder(kNumChannels);
-
   rtc::scoped_refptr<AudioDecoderFactory> decoder_factory =
-      new rtc::RefCountedObject<test::AudioDecoderProxyFactory>(&opus_decoder);
+      new rtc::RefCountedObject<test::AudioDecoderProxyFactory>(
+          opus_decoder.get());
 
   // Set up an EXTERNAL DECODER to parse 4 channels.
   Run(AcmReceiverBitExactnessOldApi::PlatformChecksum(  // audio checksum
diff --git a/modules/audio_coding/codecs/opus/audio_coder_opus_common.cc b/modules/audio_coding/codecs/opus/audio_coder_opus_common.cc
new file mode 100644
index 0000000..fca87e2
--- /dev/null
+++ b/modules/audio_coding/codecs/opus/audio_coder_opus_common.cc
@@ -0,0 +1,50 @@
+/*
+ *  Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/audio_coding/codecs/opus/audio_coder_opus_common.h"
+
+namespace webrtc {
+
+absl::optional<std::string> GetFormatParameter(const SdpAudioFormat& format,
+                                               const std::string& param) {
+  auto it = format.parameters.find(param);
+  if (it == format.parameters.end())
+    return absl::nullopt;
+
+  return it->second;
+}
+
+// Parses a comma-separated string "1,2,0,6" into a std::vector<unsigned char>.
+template <>
+absl::optional<std::vector<unsigned char>> GetFormatParameter(
+    const SdpAudioFormat& format,
+    const std::string& param) {
+  std::vector<unsigned char> result;
+  const std::string comma_separated_list =
+      GetFormatParameter(format, param).value_or("");
+  size_t pos = 0;
+  while (pos < comma_separated_list.size()) {
+    const size_t next_comma = comma_separated_list.find(',', pos);
+    const size_t distance_to_next_comma = next_comma == std::string::npos
+                                              ? std::string::npos
+                                              : (next_comma - pos);
+    auto substring_with_number =
+        comma_separated_list.substr(pos, distance_to_next_comma);
+    auto conv = rtc::StringToNumber<int>(substring_with_number);
+    if (!conv.has_value()) {
+      return absl::nullopt;
+    }
+    result.push_back(*conv);
+    pos += substring_with_number.size() + 1;
+  }
+  return result;
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_coding/codecs/opus/audio_coder_opus_common.h b/modules/audio_coding/codecs/opus/audio_coder_opus_common.h
new file mode 100644
index 0000000..cad914e
--- /dev/null
+++ b/modules/audio_coding/codecs/opus/audio_coder_opus_common.h
@@ -0,0 +1,88 @@
+/*
+ *  Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_AUDIO_CODING_CODECS_OPUS_AUDIO_CODER_OPUS_COMMON_H_
+#define MODULES_AUDIO_CODING_CODECS_OPUS_AUDIO_CODER_OPUS_COMMON_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/audio_codecs/audio_decoder.h"
+#include "api/audio_codecs/audio_format.h"
+#include "rtc_base/string_to_number.h"
+
+namespace webrtc {
+
+absl::optional<std::string> GetFormatParameter(const SdpAudioFormat& format,
+                                               const std::string& param);
+
+template <typename T>
+absl::optional<T> GetFormatParameter(const SdpAudioFormat& format,
+                                     const std::string& param) {
+  return rtc::StringToNumber<T>(GetFormatParameter(format, param).value_or(""));
+}
+
+template <>
+absl::optional<std::vector<unsigned char>> GetFormatParameter(
+    const SdpAudioFormat& format,
+    const std::string& param);
+
+class OpusFrame : public AudioDecoder::EncodedAudioFrame {
+ public:
+  OpusFrame(AudioDecoder* decoder,
+            rtc::Buffer&& payload,
+            bool is_primary_payload)
+      : decoder_(decoder),
+        payload_(std::move(payload)),
+        is_primary_payload_(is_primary_payload) {}
+
+  size_t Duration() const override {
+    int ret;
+    if (is_primary_payload_) {
+      ret = decoder_->PacketDuration(payload_.data(), payload_.size());
+    } else {
+      ret = decoder_->PacketDurationRedundant(payload_.data(), payload_.size());
+    }
+    return (ret < 0) ? 0 : static_cast<size_t>(ret);
+  }
+
+  bool IsDtxPacket() const override { return payload_.size() <= 2; }
+
+  absl::optional<DecodeResult> Decode(
+      rtc::ArrayView<int16_t> decoded) const override {
+    AudioDecoder::SpeechType speech_type = AudioDecoder::kSpeech;
+    int ret;
+    if (is_primary_payload_) {
+      ret = decoder_->Decode(
+          payload_.data(), payload_.size(), decoder_->SampleRateHz(),
+          decoded.size() * sizeof(int16_t), decoded.data(), &speech_type);
+    } else {
+      ret = decoder_->DecodeRedundant(
+          payload_.data(), payload_.size(), decoder_->SampleRateHz(),
+          decoded.size() * sizeof(int16_t), decoded.data(), &speech_type);
+    }
+
+    if (ret < 0)
+      return absl::nullopt;
+
+    return DecodeResult{static_cast<size_t>(ret), speech_type};
+  }
+
+ private:
+  AudioDecoder* const decoder_;
+  const rtc::Buffer payload_;
+  const bool is_primary_payload_;
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_CODING_CODECS_OPUS_AUDIO_CODER_OPUS_COMMON_H_
diff --git a/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_impl.cc b/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_impl.cc
new file mode 100644
index 0000000..df0f448
--- /dev/null
+++ b/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_impl.cc
@@ -0,0 +1,178 @@
+/*
+ *  Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_impl.h"
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/memory/memory.h"
+#include "modules/audio_coding/codecs/opus/audio_coder_opus_common.h"
+#include "rtc_base/string_to_number.h"
+
+namespace webrtc {
+
+std::unique_ptr<AudioDecoderMultiChannelOpusImpl>
+AudioDecoderMultiChannelOpusImpl::MakeAudioDecoder(
+    AudioDecoderMultiChannelOpusConfig config) {
+  if (!config.IsOk()) {
+    return nullptr;
+  }
+  // Fill the pointer with a working decoder through the C interface. This
+  // allocates memory.
+  OpusDecInst* dec_state = nullptr;
+  const int error = WebRtcOpus_MultistreamDecoderCreate(
+      &dec_state, config.num_channels, config.num_streams,
+      config.coupled_streams, config.channel_mapping.data());
+  if (error != 0) {
+    return nullptr;
+  }
+
+  // Pass the ownership to DecoderImpl. Not using 'make_unique' because the
+  // c-tor is private.
+  return std::unique_ptr<AudioDecoderMultiChannelOpusImpl>(
+      new AudioDecoderMultiChannelOpusImpl(dec_state, config));
+}
+
+AudioDecoderMultiChannelOpusImpl::AudioDecoderMultiChannelOpusImpl(
+    OpusDecInst* dec_state,
+    AudioDecoderMultiChannelOpusConfig config)
+    : dec_state_(dec_state), config_(config) {
+  RTC_DCHECK(dec_state);
+  WebRtcOpus_DecoderInit(dec_state_);
+}
+
+AudioDecoderMultiChannelOpusImpl::~AudioDecoderMultiChannelOpusImpl() {
+  WebRtcOpus_DecoderFree(dec_state_);
+}
+
+absl::optional<AudioDecoderMultiChannelOpusConfig>
+AudioDecoderMultiChannelOpusImpl::SdpToConfig(const SdpAudioFormat& format) {
+  AudioDecoderMultiChannelOpusConfig config;
+  config.num_channels = format.num_channels;
+  auto num_streams = GetFormatParameter<int>(format, "num_streams");
+  if (!num_streams.has_value()) {
+    return absl::nullopt;
+  }
+  config.num_streams = *num_streams;
+
+  auto coupled_streams = GetFormatParameter<int>(format, "coupled_streams");
+  if (!coupled_streams.has_value()) {
+    return absl::nullopt;
+  }
+  config.coupled_streams = *coupled_streams;
+
+  auto channel_mapping =
+      GetFormatParameter<std::vector<unsigned char>>(format, "channel_mapping");
+  if (!channel_mapping.has_value()) {
+    return absl::nullopt;
+  }
+  config.channel_mapping = *channel_mapping;
+  return config;
+}
+
+std::vector<AudioDecoder::ParseResult>
+AudioDecoderMultiChannelOpusImpl::ParsePayload(rtc::Buffer&& payload,
+                                               uint32_t timestamp) {
+  std::vector<ParseResult> results;
+
+  if (PacketHasFec(payload.data(), payload.size())) {
+    const int duration =
+        PacketDurationRedundant(payload.data(), payload.size());
+    RTC_DCHECK_GE(duration, 0);
+    rtc::Buffer payload_copy(payload.data(), payload.size());
+    std::unique_ptr<EncodedAudioFrame> fec_frame(
+        new OpusFrame(this, std::move(payload_copy), false));
+    results.emplace_back(timestamp - duration, 1, std::move(fec_frame));
+  }
+  std::unique_ptr<EncodedAudioFrame> frame(
+      new OpusFrame(this, std::move(payload), true));
+  results.emplace_back(timestamp, 0, std::move(frame));
+  return results;
+}
+
+int AudioDecoderMultiChannelOpusImpl::DecodeInternal(const uint8_t* encoded,
+                                                     size_t encoded_len,
+                                                     int sample_rate_hz,
+                                                     int16_t* decoded,
+                                                     SpeechType* speech_type) {
+  RTC_DCHECK_EQ(sample_rate_hz, 48000);
+  int16_t temp_type = 1;  // Default is speech.
+  int ret =
+      WebRtcOpus_Decode(dec_state_, encoded, encoded_len, decoded, &temp_type);
+  if (ret > 0)
+    ret *= static_cast<int>(
+        config_.num_channels);  // Return total number of samples.
+  *speech_type = ConvertSpeechType(temp_type);
+  return ret;
+}
+
+int AudioDecoderMultiChannelOpusImpl::DecodeRedundantInternal(
+    const uint8_t* encoded,
+    size_t encoded_len,
+    int sample_rate_hz,
+    int16_t* decoded,
+    SpeechType* speech_type) {
+  if (!PacketHasFec(encoded, encoded_len)) {
+    // This packet is a RED packet.
+    return DecodeInternal(encoded, encoded_len, sample_rate_hz, decoded,
+                          speech_type);
+  }
+
+  RTC_DCHECK_EQ(sample_rate_hz, 48000);
+  int16_t temp_type = 1;  // Default is speech.
+  int ret = WebRtcOpus_DecodeFec(dec_state_, encoded, encoded_len, decoded,
+                                 &temp_type);
+  if (ret > 0)
+    ret *= static_cast<int>(
+        config_.num_channels);  // Return total number of samples.
+  *speech_type = ConvertSpeechType(temp_type);
+  return ret;
+}
+
+void AudioDecoderMultiChannelOpusImpl::Reset() {
+  WebRtcOpus_DecoderInit(dec_state_);
+}
+
+int AudioDecoderMultiChannelOpusImpl::PacketDuration(const uint8_t* encoded,
+                                                     size_t encoded_len) const {
+  return WebRtcOpus_DurationEst(dec_state_, encoded, encoded_len);
+}
+
+int AudioDecoderMultiChannelOpusImpl::PacketDurationRedundant(
+    const uint8_t* encoded,
+    size_t encoded_len) const {
+  if (!PacketHasFec(encoded, encoded_len)) {
+    // This packet is a RED packet.
+    return PacketDuration(encoded, encoded_len);
+  }
+
+  return WebRtcOpus_FecDurationEst(encoded, encoded_len);
+}
+
+bool AudioDecoderMultiChannelOpusImpl::PacketHasFec(const uint8_t* encoded,
+                                                    size_t encoded_len) const {
+  int fec;
+  fec = WebRtcOpus_PacketHasFec(encoded, encoded_len);
+  return (fec == 1);
+}
+
+int AudioDecoderMultiChannelOpusImpl::SampleRateHz() const {
+  return 48000;
+}
+
+size_t AudioDecoderMultiChannelOpusImpl::Channels() const {
+  return config_.num_channels;
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_impl.h b/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_impl.h
new file mode 100644
index 0000000..5e5e6d4
--- /dev/null
+++ b/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_impl.h
@@ -0,0 +1,70 @@
+/*
+ *  Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_AUDIO_CODING_CODECS_OPUS_AUDIO_DECODER_MULTI_CHANNEL_OPUS_IMPL_H_
+#define MODULES_AUDIO_CODING_CODECS_OPUS_AUDIO_DECODER_MULTI_CHANNEL_OPUS_IMPL_H_
+
+#include <stddef.h>
+#include <memory>
+#include <vector>
+
+#include "api/audio_codecs/audio_decoder.h"
+#include "api/audio_codecs/audio_format.h"
+#include "api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h"
+#include "modules/audio_coding/codecs/opus/opus_interface.h"
+#include "rtc_base/buffer.h"
+#include "rtc_base/constructor_magic.h"
+
+namespace webrtc {
+
+class AudioDecoderMultiChannelOpusImpl final : public AudioDecoder {
+ public:
+  static std::unique_ptr<AudioDecoderMultiChannelOpusImpl> MakeAudioDecoder(
+      AudioDecoderMultiChannelOpusConfig config);
+
+  ~AudioDecoderMultiChannelOpusImpl() override;
+
+  std::vector<ParseResult> ParsePayload(rtc::Buffer&& payload,
+                                        uint32_t timestamp) override;
+  void Reset() override;
+  int PacketDuration(const uint8_t* encoded, size_t encoded_len) const override;
+  int PacketDurationRedundant(const uint8_t* encoded,
+                              size_t encoded_len) const override;
+  bool PacketHasFec(const uint8_t* encoded, size_t encoded_len) const override;
+  int SampleRateHz() const override;
+  size_t Channels() const override;
+
+  static absl::optional<AudioDecoderMultiChannelOpusConfig> SdpToConfig(
+      const SdpAudioFormat& format);
+
+ protected:
+  int DecodeInternal(const uint8_t* encoded,
+                     size_t encoded_len,
+                     int sample_rate_hz,
+                     int16_t* decoded,
+                     SpeechType* speech_type) override;
+  int DecodeRedundantInternal(const uint8_t* encoded,
+                              size_t encoded_len,
+                              int sample_rate_hz,
+                              int16_t* decoded,
+                              SpeechType* speech_type) override;
+
+ private:
+  AudioDecoderMultiChannelOpusImpl(OpusDecInst* dec_state,
+                                   AudioDecoderMultiChannelOpusConfig config);
+
+  OpusDecInst* dec_state_;
+  const AudioDecoderMultiChannelOpusConfig config_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderMultiChannelOpusImpl);
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_CODING_CODECS_OPUS_AUDIO_DECODER_MULTI_CHANNEL_OPUS_IMPL_H_
diff --git a/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_unittest.cc b/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_unittest.cc
new file mode 100644
index 0000000..23b9506
--- /dev/null
+++ b/modules/audio_coding/codecs/opus/audio_decoder_multi_channel_opus_unittest.cc
@@ -0,0 +1,160 @@
+/*
+ *  Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "api/audio_codecs/opus/audio_decoder_multi_channel_opus.h"
+
+#include "modules/audio_coding/codecs/opus/audio_coder_opus_common.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+using ::testing::NiceMock;
+using ::testing::Return;
+
+TEST(AudioDecoderMultiOpusTest, GetFormatParameter) {
+  const SdpAudioFormat sdp_format("multiopus", 48000, 4,
+                                  {{"channel_mapping", "0,1,2,3"},
+                                   {"coupled_streams", "2"},
+                                   {"num_streams", "2"}});
+
+  EXPECT_EQ(GetFormatParameter(sdp_format, "channel_mapping"),
+            absl::optional<std::string>("0,1,2,3"));
+
+  EXPECT_EQ(GetFormatParameter<int>(sdp_format, "coupled_streams"),
+            absl::optional<int>(2));
+
+  EXPECT_EQ(GetFormatParameter(sdp_format, "missing"), absl::nullopt);
+
+  EXPECT_EQ(GetFormatParameter<int>(sdp_format, "channel_mapping"),
+            absl::nullopt);
+}
+
+TEST(AudioDecoderMultiOpusTest, InvalidChannelMappings) {
+  {
+    // Can't use channel 3 if there are only 2 channels.
+    const SdpAudioFormat sdp_format("multiopus", 48000, 2,
+                                    {{"channel_mapping", "3,0"},
+                                     {"coupled_streams", "1"},
+                                     {"num_streams", "2"}});
+    const absl::optional<AudioDecoderMultiChannelOpus::Config> decoder_config =
+        AudioDecoderMultiChannelOpus::SdpToConfig(sdp_format);
+    ASSERT_TRUE(decoder_config.has_value());
+    EXPECT_FALSE(decoder_config->IsOk());
+
+    const std::unique_ptr<AudioDecoder> opus_decoder =
+        AudioDecoderMultiChannelOpus::MakeAudioDecoder(*decoder_config);
+
+    EXPECT_FALSE(opus_decoder);
+  }
+  {
+    // The mapping is too long. There are only 5 channels, but 6 elements in the
+    // mapping.
+    const SdpAudioFormat sdp_format("multiopus", 48000, 5,
+                                    {{"channel_mapping", "0,1,2,3,4,5"},
+                                     {"coupled_streams", "0"},
+                                     {"num_streams", "2"}});
+    const absl::optional<AudioDecoderMultiChannelOpus::Config> decoder_config =
+        AudioDecoderMultiChannelOpus::SdpToConfig(sdp_format);
+    ASSERT_TRUE(decoder_config.has_value());
+    EXPECT_FALSE(decoder_config->IsOk());
+
+    const std::unique_ptr<AudioDecoder> opus_decoder =
+        AudioDecoderMultiChannelOpus::MakeAudioDecoder(*decoder_config);
+
+    EXPECT_FALSE(opus_decoder);
+  }
+  {
+    // The mapping doesn't parse correctly.
+    const SdpAudioFormat sdp_format(
+        "multiopus", 48000, 5,
+        {{"channel_mapping", "0,1,two,3,4"}, {"coupled_streams", "0"}});
+    const absl::optional<AudioDecoderMultiChannelOpus::Config> decoder_config =
+        AudioDecoderMultiChannelOpus::SdpToConfig(sdp_format);
+    EXPECT_FALSE(decoder_config.has_value());
+  }
+}
+
+TEST(AudioDecoderMultiOpusTest, ValidSdpToConfigProducesCorrectConfig) {
+  const SdpAudioFormat sdp_format("multiopus", 48000, 4,
+                                  {{"channel_mapping", "3,1,2,0"},
+                                   {"coupled_streams", "2"},
+                                   {"num_streams", "2"}});
+
+  const absl::optional<AudioDecoderMultiChannelOpus::Config> decoder_config =
+      AudioDecoderMultiChannelOpus::SdpToConfig(sdp_format);
+
+  ASSERT_TRUE(decoder_config.has_value());
+  EXPECT_TRUE(decoder_config->IsOk());
+  EXPECT_EQ(decoder_config->coupled_streams, 2);
+  EXPECT_THAT(decoder_config->channel_mapping,
+              testing::ContainerEq(std::vector<unsigned char>({3, 1, 2, 0})));
+}
+
+TEST(AudioDecoderMultiOpusTest, InvalidSdpToConfigDoesNotProduceConfig) {
+  {
+    const SdpAudioFormat sdp_format("multiopus", 48000, 4,
+                                    {{"channel_mapping", "0,1,2,3"},
+                                     {"coupled_stream", "2"},
+                                     {"num_streams", "2"}});
+
+    const absl::optional<AudioDecoderMultiChannelOpus::Config> decoder_config =
+        AudioDecoderMultiChannelOpus::SdpToConfig(sdp_format);
+
+    EXPECT_FALSE(decoder_config.has_value());
+  }
+
+  {
+    const SdpAudioFormat sdp_format("multiopus", 48000, 4,
+                                    {{"channel_mapping", "0,1,2 3"},
+                                     {"coupled_streams", "2"},
+                                     {"num_streams", "2"}});
+
+    const absl::optional<AudioDecoderMultiChannelOpus::Config> decoder_config =
+        AudioDecoderMultiChannelOpus::SdpToConfig(sdp_format);
+
+    EXPECT_FALSE(decoder_config.has_value());
+  }
+}
+
+TEST(AudioDecoderMultiOpusTest, CodecsCanBeCreated) {
+  const SdpAudioFormat sdp_format("multiopus", 48000, 4,
+                                  {{"channel_mapping", "0,1,2,3"},
+                                   {"coupled_streams", "2"},
+                                   {"num_streams", "2"}});
+
+  const absl::optional<AudioDecoderMultiChannelOpus::Config> decoder_config =
+      AudioDecoderMultiChannelOpus::SdpToConfig(sdp_format);
+
+  ASSERT_TRUE(decoder_config.has_value());
+
+  const std::unique_ptr<AudioDecoder> opus_decoder =
+      AudioDecoderMultiChannelOpus::MakeAudioDecoder(*decoder_config);
+
+  EXPECT_TRUE(opus_decoder);
+}
+
+TEST(AudioDecoderMultiOpusTest, AdvertisedCodecsCanBeCreated) {
+  std::vector<AudioCodecSpec> specs;
+  AudioDecoderMultiChannelOpus::AppendSupportedDecoders(&specs);
+
+  EXPECT_FALSE(specs.empty());
+
+  for (const AudioCodecSpec& spec : specs) {
+    const absl::optional<AudioDecoderMultiChannelOpus::Config> decoder_config =
+        AudioDecoderMultiChannelOpus::SdpToConfig(spec.format);
+    ASSERT_TRUE(decoder_config.has_value());
+
+    const std::unique_ptr<AudioDecoder> opus_decoder =
+        AudioDecoderMultiChannelOpus::MakeAudioDecoder(*decoder_config);
+
+    EXPECT_TRUE(opus_decoder);
+  }
+}
+}  // namespace webrtc
diff --git a/modules/audio_coding/codecs/opus/audio_decoder_opus.cc b/modules/audio_coding/codecs/opus/audio_decoder_opus.cc
index b6eada9..77e1535 100644
--- a/modules/audio_coding/codecs/opus/audio_decoder_opus.cc
+++ b/modules/audio_coding/codecs/opus/audio_decoder_opus.cc
@@ -15,64 +15,14 @@
 
 #include "absl/types/optional.h"
 #include "api/array_view.h"
+#include "modules/audio_coding/codecs/opus/audio_coder_opus_common.h"
 #include "rtc_base/checks.h"
 
 namespace webrtc {
 
-namespace {
-class OpusFrame : public AudioDecoder::EncodedAudioFrame {
- public:
-  OpusFrame(AudioDecoderOpusImpl* decoder,
-            rtc::Buffer&& payload,
-            bool is_primary_payload)
-      : decoder_(decoder),
-        payload_(std::move(payload)),
-        is_primary_payload_(is_primary_payload) {}
-
-  size_t Duration() const override {
-    int ret;
-    if (is_primary_payload_) {
-      ret = decoder_->PacketDuration(payload_.data(), payload_.size());
-    } else {
-      ret = decoder_->PacketDurationRedundant(payload_.data(), payload_.size());
-    }
-    return (ret < 0) ? 0 : static_cast<size_t>(ret);
-  }
-
-  bool IsDtxPacket() const override { return payload_.size() <= 2; }
-
-  absl::optional<DecodeResult> Decode(
-      rtc::ArrayView<int16_t> decoded) const override {
-    AudioDecoder::SpeechType speech_type = AudioDecoder::kSpeech;
-    int ret;
-    if (is_primary_payload_) {
-      ret = decoder_->Decode(
-          payload_.data(), payload_.size(), decoder_->SampleRateHz(),
-          decoded.size() * sizeof(int16_t), decoded.data(), &speech_type);
-    } else {
-      ret = decoder_->DecodeRedundant(
-          payload_.data(), payload_.size(), decoder_->SampleRateHz(),
-          decoded.size() * sizeof(int16_t), decoded.data(), &speech_type);
-    }
-
-    if (ret < 0)
-      return absl::nullopt;
-
-    return DecodeResult{static_cast<size_t>(ret), speech_type};
-  }
-
- private:
-  AudioDecoderOpusImpl* const decoder_;
-  const rtc::Buffer payload_;
-  const bool is_primary_payload_;
-};
-
-}  // namespace
-
 AudioDecoderOpusImpl::AudioDecoderOpusImpl(size_t num_channels)
     : channels_(num_channels) {
-  RTC_DCHECK(num_channels == 1 || num_channels == 2 || num_channels == 4 ||
-             num_channels == 6 || num_channels == 8);
+  RTC_DCHECK(num_channels == 1 || num_channels == 2);
   const int error = WebRtcOpus_DecoderCreate(&dec_state_, channels_);
   RTC_DCHECK(error == 0);
   WebRtcOpus_DecoderInit(dec_state_);
diff --git a/modules/audio_coding/codecs/opus/audio_encoder_opus.cc b/modules/audio_coding/codecs/opus/audio_encoder_opus.cc
index 69aa8b9..97ae343 100644
--- a/modules/audio_coding/codecs/opus/audio_encoder_opus.cc
+++ b/modules/audio_coding/codecs/opus/audio_encoder_opus.cc
@@ -19,6 +19,7 @@
 #include "absl/strings/match.h"
 #include "modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl.h"
 #include "modules/audio_coding/audio_network_adaptor/controller_manager.h"
+#include "modules/audio_coding/codecs/opus/audio_coder_opus_common.h"
 #include "modules/audio_coding/codecs/opus/opus_interface.h"
 #include "rtc_base/arraysize.h"
 #include "rtc_base/checks.h"
@@ -105,21 +106,6 @@
   }
 }
 
-absl::optional<std::string> GetFormatParameter(const SdpAudioFormat& format,
-                                               const std::string& param) {
-  auto it = format.parameters.find(param);
-  if (it == format.parameters.end())
-    return absl::nullopt;
-
-  return it->second;
-}
-
-template <typename T>
-absl::optional<T> GetFormatParameter(const SdpAudioFormat& format,
-                                     const std::string& param) {
-  return rtc::StringToNumber<T>(GetFormatParameter(format, param).value_or(""));
-}
-
 int CalculateDefaultBitrate(int max_playback_rate, size_t num_channels) {
   const int bitrate = [&] {
     if (max_playback_rate <= 8000) {
diff --git a/modules/audio_coding/codecs/opus/opus_interface.c b/modules/audio_coding/codecs/opus/opus_interface.c
index f6e053a..d07a5b4 100644
--- a/modules/audio_coding/codecs/opus/opus_interface.c
+++ b/modules/audio_coding/codecs/opus/opus_interface.c
@@ -79,6 +79,7 @@
     OpusEncInst** inst,
     size_t channels,
     int32_t application,
+    size_t streams,
     size_t coupled_streams,
     const unsigned char *channel_mapping) {
   int opus_app;
@@ -99,7 +100,6 @@
   OpusEncInst* state = (OpusEncInst*)calloc(1, sizeof(OpusEncInst));
   RTC_DCHECK(state);
 
-  int streams = channels - coupled_streams;
   int error;
   state->multistream_encoder =
       opus_multistream_encoder_create(
@@ -407,6 +407,7 @@
 
 int16_t WebRtcOpus_MultistreamDecoderCreate(
     OpusDecInst** inst, size_t channels,
+    size_t streams,
     size_t coupled_streams,
     const unsigned char* channel_mapping) {
   int error;
@@ -419,11 +420,9 @@
       return -1;
     }
 
-    int streams = channels - coupled_streams;
-
     // Create new memory, always at 48000 Hz.
     state->multistream_decoder = opus_multistream_decoder_create(
-        48000, (int)channels,
+        48000, channels,
         streams,
         coupled_streams,
         channel_mapping,
diff --git a/modules/audio_coding/codecs/opus/opus_interface.h b/modules/audio_coding/codecs/opus/opus_interface.h
index 04eaba9..f5bcc45 100644
--- a/modules/audio_coding/codecs/opus/opus_interface.h
+++ b/modules/audio_coding/codecs/opus/opus_interface.h
@@ -53,11 +53,12 @@
  * This function creates an Opus encoder with any supported channel count.
  *
  * Input:
- *      - channels           : number of channels.
+ *      - channels           : number of channels in the input of the encoder.
  *      - application        : 0 - VOIP applications.
  *                                 Favor speech intelligibility.
  *                             1 - Audio applications.
  *                                 Favor faithfulness to the original input.
+ *      - streams            : number of streams, as described in RFC 7845.
  *      - coupled_streams    : number of coupled streams, as described in
  *                             RFC 7845.
  *      - channel_mapping    : the channel mapping; pointer to array of
@@ -74,6 +75,7 @@
     OpusEncInst** inst,
     size_t channels,
     int32_t application,
+    size_t streams,
     size_t coupled_streams,
     const unsigned char* channel_mapping);
 
@@ -332,7 +334,10 @@
  * This function creates an Opus decoder with any supported channel count.
  *
  * Input:
- *      - channels           : number of channels.
+ *      - channels           : number of output channels that the decoder
+ *                             will produce.
+ *      - streams            : number of encoded streams, as described in
+ *                             RFC 7845.
  *      - coupled_streams    : number of coupled streams, as described in
  *                             RFC 7845.
  *      - channel_mapping    : the channel mapping; pointer to array of
@@ -348,6 +353,7 @@
 int16_t WebRtcOpus_MultistreamDecoderCreate(
     OpusDecInst** inst,
     size_t channels,
+    size_t streams,
     size_t coupled_streams,
     const unsigned char* channel_mapping);
 
diff --git a/modules/audio_coding/codecs/opus/opus_unittest.cc b/modules/audio_coding/codecs/opus/opus_unittest.cc
index aa7eee9..d506e60 100644
--- a/modules/audio_coding/codecs/opus/opus_unittest.cc
+++ b/modules/audio_coding/codecs/opus/opus_unittest.cc
@@ -25,12 +25,15 @@
 // Equivalent to SDP params
 // {{"channel_mapping", "0,1,2,3"}, {"coupled_streams", "2"}}.
 constexpr unsigned char kQuadChannelMapping[] = {0, 1, 2, 3};
+constexpr int kQuadTotalStreams = 2;
 constexpr int kQuadCoupledStreams = 2;
 
 constexpr unsigned char kStereoChannelMapping[] = {0, 1};
+constexpr int kStereoTotalStreams = 1;
 constexpr int kStereoCoupledStreams = 1;
 
 constexpr unsigned char kMonoChannelMapping[] = {0};
+constexpr int kMonoTotalStreams = 1;
 constexpr int kMonoCoupledStreams = 0;
 
 void CreateSingleOrMultiStreamEncoder(WebRtcOpusEncInst** opus_encoder,
@@ -41,16 +44,16 @@
     EXPECT_EQ(0, WebRtcOpus_EncoderCreate(opus_encoder, channels, application));
   } else if (force_multistream && channels == 1) {
     EXPECT_EQ(0, WebRtcOpus_MultistreamEncoderCreate(
-                     opus_encoder, channels, application, kMonoCoupledStreams,
-                     kMonoChannelMapping));
+                     opus_encoder, channels, application, kMonoTotalStreams,
+                     kMonoCoupledStreams, kMonoChannelMapping));
   } else if (force_multistream && channels == 2) {
     EXPECT_EQ(0, WebRtcOpus_MultistreamEncoderCreate(
-                     opus_encoder, channels, application, kStereoCoupledStreams,
-                     kStereoChannelMapping));
+                     opus_encoder, channels, application, kStereoTotalStreams,
+                     kStereoCoupledStreams, kStereoChannelMapping));
   } else if (channels == 4) {
     EXPECT_EQ(0, WebRtcOpus_MultistreamEncoderCreate(
-                     opus_encoder, channels, application, kQuadCoupledStreams,
-                     kQuadChannelMapping));
+                     opus_encoder, channels, application, kQuadTotalStreams,
+                     kQuadCoupledStreams, kQuadChannelMapping));
   } else {
     EXPECT_TRUE(false) << channels;
   }
@@ -62,17 +65,17 @@
   if (!force_multistream && (channels == 1 || channels == 2)) {
     EXPECT_EQ(0, WebRtcOpus_DecoderCreate(opus_decoder, channels));
   } else if (channels == 1) {
-    EXPECT_EQ(0, WebRtcOpus_MultistreamDecoderCreate(opus_decoder, channels,
-                                                     kMonoCoupledStreams,
-                                                     kMonoChannelMapping));
+    EXPECT_EQ(0, WebRtcOpus_MultistreamDecoderCreate(
+                     opus_decoder, channels, kMonoTotalStreams,
+                     kMonoCoupledStreams, kMonoChannelMapping));
   } else if (channels == 2) {
-    EXPECT_EQ(0, WebRtcOpus_MultistreamDecoderCreate(opus_decoder, channels,
-                                                     kStereoCoupledStreams,
-                                                     kStereoChannelMapping));
+    EXPECT_EQ(0, WebRtcOpus_MultistreamDecoderCreate(
+                     opus_decoder, channels, kStereoTotalStreams,
+                     kStereoCoupledStreams, kStereoChannelMapping));
   } else if (channels == 4) {
-    EXPECT_EQ(0, WebRtcOpus_MultistreamDecoderCreate(opus_decoder, channels,
-                                                     kQuadCoupledStreams,
-                                                     kQuadChannelMapping));
+    EXPECT_EQ(0, WebRtcOpus_MultistreamDecoderCreate(
+                     opus_decoder, channels, kQuadTotalStreams,
+                     kQuadCoupledStreams, kQuadChannelMapping));
   } else {
     EXPECT_TRUE(false) << channels;
   }
@@ -886,7 +889,7 @@
                          OpusTest,
                          ::testing::ValuesIn({
                              std::make_tuple(1, 0, true),
-                             std::make_tuple(1, 1, true),
+                             std::make_tuple(2, 1, true),
                              std::make_tuple(2, 0, false),
                              std::make_tuple(4, 0, false),
                              std::make_tuple(1, 1, false),
diff --git a/test/fuzzers/BUILD.gn b/test/fuzzers/BUILD.gn
index bd2c931..c887108 100644
--- a/test/fuzzers/BUILD.gn
+++ b/test/fuzzers/BUILD.gn
@@ -339,6 +339,17 @@
   ]
 }
 
+webrtc_fuzzer_test("audio_decoder_multiopus_fuzzer") {
+  sources = [
+    "audio_decoder_multistream_opus_fuzzer.cc",
+  ]
+  deps = [
+    ":audio_decoder_fuzzer",
+    "../../api/audio_codecs/opus:audio_decoder_multiopus",
+    "../../api/audio_codecs/opus:audio_encoder_opus_config",
+  ]
+}
+
 webrtc_fuzzer_test("audio_encoder_opus_fuzzer") {
   sources = [
     "audio_encoder_opus_fuzzer.cc",
diff --git a/test/fuzzers/audio_decoder_multistream_opus_fuzzer.cc b/test/fuzzers/audio_decoder_multistream_opus_fuzzer.cc
new file mode 100644
index 0000000..7705773
--- /dev/null
+++ b/test/fuzzers/audio_decoder_multistream_opus_fuzzer.cc
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "api/audio_codecs/opus/audio_decoder_multi_channel_opus.h"
+#include "api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h"
+#include "test/fuzzers/audio_decoder_fuzzer.h"
+
+namespace webrtc {
+
+AudioDecoderMultiChannelOpusConfig MakeDecoderConfig(
+    int num_channels,
+    int coupled_streams,
+    std::vector<unsigned char> channel_mapping) {
+  AudioDecoderMultiChannelOpusConfig config;
+  config.num_channels = num_channels;
+  config.coupled_streams = coupled_streams;
+  config.channel_mapping = channel_mapping;
+  return config;
+}
+
+void FuzzOneInput(const uint8_t* data, size_t size) {
+  const std::vector<AudioDecoderMultiChannelOpusConfig> surround_configs = {
+      MakeDecoderConfig(1, 0, {0}),  // Mono
+
+      MakeDecoderConfig(2, 0, {0, 0}),  // Copy the first (of
+                                        // 2) decoded streams
+                                        // into both output
+                                        // channel 0 and output
+                                        // channel 1. Ignore
+                                        // the 2nd decoded
+                                        // stream.
+
+      MakeDecoderConfig(4, 2, {0, 1, 2, 3}),             // Quad.
+      MakeDecoderConfig(6, 2, {0, 4, 1, 2, 3, 5}),       // 5.1
+      MakeDecoderConfig(8, 3, {0, 6, 1, 2, 3, 4, 5, 7})  // 7.1
+  };
+
+  const auto config = surround_configs[data[0] % surround_configs.size()];
+  std::unique_ptr<AudioDecoder> dec =
+      AudioDecoderMultiChannelOpus::MakeAudioDecoder(config);
+  RTC_CHECK(dec);
+  const int kSampleRateHz = 48000;
+  const size_t kAllocatedOuputSizeSamples =
+      4 * kSampleRateHz / 10;  // 4x100 ms, 4 times the size of the output array
+                               // for the stereo Opus codec. It should be enough
+                               // for 8 channels.
+  int16_t output[kAllocatedOuputSizeSamples];
+  FuzzAudioDecoder(DecoderFunctionType::kNormalDecode, data, size, dec.get(),
+                   kSampleRateHz, sizeof(output), output);
+}
+}  // namespace webrtc