diff --git a/webrtc/audio/audio_receive_stream.cc b/webrtc/audio/audio_receive_stream.cc index f46337afd1..aaf77a8a97 100644 --- a/webrtc/audio/audio_receive_stream.cc +++ b/webrtc/audio/audio_receive_stream.cc @@ -101,6 +101,10 @@ AudioReceiveStream::AudioReceiveStream( 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 1299ded429..3841672595 100644 --- a/webrtc/call/audio_receive_stream.h +++ b/webrtc/call/audio_receive_stream.h @@ -102,11 +102,8 @@ class AudioReceiveStream { // 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 decoder_map; + // Decoder specifications for every payload type that we can receive. + std::map decoder_map; rtc::scoped_refptr decoder_factory; }; diff --git a/webrtc/media/engine/payload_type_mapper.cc b/webrtc/media/engine/payload_type_mapper.cc index 0d449c4e43..496a39ae48 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 a79fb4756c..248aace10b 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 e6fa12e45e..c23084eed1 100644 --- a/webrtc/media/engine/webrtcvoiceengine.cc +++ b/webrtc/media/engine/webrtcvoiceengine.cc @@ -1542,14 +1542,15 @@ class WebRtcVoiceMediaChannel::WebRtcAudioReceiveStream { 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 @@ class WebRtcVoiceMediaChannel::WebRtcAudioReceiveStream { 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& 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& 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 @@ class WebRtcVoiceMediaChannel::WebRtcAudioReceiveStream { } private: - void RecreateAudioReceiveStream( - uint32_t local_ssrc, - bool use_transport_cc, - bool use_nack, - const std::vector& 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 @@ bool WebRtcVoiceMediaChannel::SetRecvCodecs( return true; } + // Create a payload type -> SdpAudioFormat map with all the decoders. Fail + // unless the factory claims to support all decoders. + std::map 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; - } - } - if (result) { - recv_codecs_ = codecs; + for (auto& kv : recv_streams_) { + kv.second->RecreateAudioReceiveStream(decoder_map); } + 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 f15bde68b7..60389342a8 100644 --- a/webrtc/media/engine/webrtcvoiceengine_unittest.cc +++ b/webrtc/media/engine/webrtcvoiceengine_unittest.cc @@ -107,7 +107,6 @@ class WebRtcVoiceEngineTestFake : public testing::Test { 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 @@ class WebRtcVoiceEngineTestFake : public testing::Test { 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 @@ TEST_F(WebRtcVoiceEngineTestFake, SetRecvCodecsAfterAddingStreams) { 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 be5e2c56dd..574b15ddc8 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 @@ TEST_F(AcmReceiverBitExactnessOldApi, 48kHzOutputExternalDecoder) { std::vector GetSupportedDecoders() override { return fact_->GetSupportedDecoders(); } + bool IsSupportedDecoder(const SdpAudioFormat& format) override { + return format.name == "MockPCMu" ? true + : fact_->IsSupportedDecoder(format); + } std::unique_ptr 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 6787690544..f0ed3011e4 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 @@ rtc::Optional RentACodec::NetEqDecoderToSdpAudioFormat( case NetEqDecoder::kDecoderG722_2ch: return rtc::Optional(SdpAudioFormat("g722", 8000, 2)); case NetEqDecoder::kDecoderOpus: - return rtc::Optional( - SdpAudioFormat("opus", 48000, 2, - std::map{{"stereo", "0"}})); + return rtc::Optional(SdpAudioFormat("opus", 48000, 2)); case NetEqDecoder::kDecoderOpus_2ch: return rtc::Optional( 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 93fca412b2..ca578f3443 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 @@ class AudioDecoderFactory : public rtc::RefCountInterface { public: virtual std::vector GetSupportedDecoders() = 0; + virtual bool IsSupportedDecoder(const SdpAudioFormat& format) = 0; + virtual std::unique_ptr 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 6b63f987f0..0379613703 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 @@ TEST(AudioDecoderFactoryTest, CreateOpus) { 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(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 ebd7cb030b..88e42c5d3f 100644 --- a/webrtc/modules/audio_coding/codecs/audio_format.cc +++ b/webrtc/modules/audio_coding/codecs/audio_format.cc @@ -22,14 +22,28 @@ SdpAudioFormat::SdpAudioFormat(const char* name, 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 1199cc27cd..b3f803cd96 100644 --- a/webrtc/modules/audio_coding/codecs/audio_format.h +++ b/webrtc/modules/audio_coding/codecs/audio_format.h @@ -26,10 +26,15 @@ struct SdpAudioFormat { 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 ef9aa4479b..5d42409ce0 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 + +#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(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(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(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 ff71282f7e..9267a52b72 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 05b65c3b86..54ec776672 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 @@ namespace { struct NamedDecoderConstructor { const char* name; - std::unique_ptr (*constructor)(const SdpAudioFormat&); -}; -std::unique_ptr Unique(AudioDecoder* d) { - return std::unique_ptr(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* 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* 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* 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* 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* 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* 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* 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* 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 num_channels = [&] { + [](const SdpAudioFormat& format, std::unique_ptr* out) { + const rtc::Optional num_channels = [&] { auto stereo = format.parameters.find("stereo"); if (stereo != format.parameters.end()) { if (stereo->second == "0") { return rtc::Optional(1); } else if (stereo->second == "1") { return rtc::Optional(2); + } else { + return rtc::Optional(); // Bad stereo parameter. } } - return rtc::Optional(); + return rtc::Optional(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 @@ class BuiltinAudioDecoderFactory : public AudioDecoderFactory { }, #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 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 dec = dc.constructor(format); - if (dec) { + std::unique_ptr 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 f2ef170710..b988b85dc7 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 @@ namespace webrtc { class MockAudioDecoderFactory : public AudioDecoderFactory { public: MOCK_METHOD0(GetSupportedDecoders, std::vector()); + MOCK_METHOD1(IsSupportedDecoder, bool(const SdpAudioFormat&)); std::unique_ptr MakeAudioDecoder( const SdpAudioFormat& format) { std::unique_ptr return_value; @@ -46,6 +48,8 @@ class MockAudioDecoderFactory : public AudioDecoderFactory { ON_CALL(*factory.get(), GetSupportedDecoders()) .WillByDefault(Return(std::vector())); 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 @@ class MockAudioDecoderFactory : public AudioDecoderFactory { ON_CALL(*factory.get(), GetSupportedDecoders()) .WillByDefault(Return(std::vector())); 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 6dc370858c..b1e603c896 100644 --- a/webrtc/tools/BUILD.gn +++ b/webrtc/tools/BUILD.gn @@ -205,6 +205,9 @@ if (rtc_enable_protobuf) { "../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 68f2e2d95c..5231691b55 100644 --- a/webrtc/voice_engine/channel.cc +++ b/webrtc/voice_engine/channel.cc @@ -1372,6 +1372,11 @@ int32_t Channel::GetVADStatus(bool& enabledVAD, } 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 @@ int32_t Channel::SetRecPayloadType(const CodecInst& codec) { 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 @@ int32_t Channel::SetRecPayloadType(const CodecInst& codec) { 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 d2b24a2ee0..62adc5bdff 100644 --- a/webrtc/voice_engine/channel.h +++ b/webrtc/voice_engine/channel.h @@ -196,6 +196,7 @@ class Channel 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 2dd74facb4..59b920f8e1 100644 --- a/webrtc/voice_engine/channel_proxy.cc +++ b/webrtc/voice_engine/channel_proxy.cc @@ -153,6 +153,13 @@ void ChannelProxy::SetBitrate(int bitrate_bps, int64_t probing_interval_ms) { 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 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 8a78069609..4bd76a4bed 100644 --- a/webrtc/voice_engine/channel_proxy.h +++ b/webrtc/voice_engine/channel_proxy.h @@ -74,6 +74,8 @@ class ChannelProxy { 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 sink); virtual void SetInputMute(bool muted); virtual void RegisterExternalTransport(Transport* transport);