diff --git a/webrtc/media/BUILD.gn b/webrtc/media/BUILD.gn index 34e815501c..b4511fbc59 100644 --- a/webrtc/media/BUILD.gn +++ b/webrtc/media/BUILD.gn @@ -92,6 +92,8 @@ source_set("rtc_media") { "base/videosourcebase.h", "devices/videorendererfactory.h", "engine/nullwebrtcvideoengine.h", + "engine/payload_type_mapper.cc", + "engine/payload_type_mapper.h", "engine/simulcast.cc", "engine/simulcast.h", "engine/webrtccommon.h", @@ -301,6 +303,7 @@ if (rtc_include_tests) { "base/videoengine_unittest.h", "base/videoframe_unittest.h", "engine/nullwebrtcvideoengine_unittest.cc", + "engine/payload_type_mapper_unittest.cc", "engine/simulcast_unittest.cc", "engine/webrtcmediaengine_unittest.cc", "engine/webrtcvideocapturer_unittest.cc", diff --git a/webrtc/media/engine/nullwebrtcvideoengine_unittest.cc b/webrtc/media/engine/nullwebrtcvideoengine_unittest.cc index 38d6e77488..52cd0f2a62 100644 --- a/webrtc/media/engine/nullwebrtcvideoengine_unittest.cc +++ b/webrtc/media/engine/nullwebrtcvideoengine_unittest.cc @@ -12,6 +12,7 @@ #include "webrtc/media/engine/nullwebrtcvideoengine.h" #include "webrtc/media/engine/webrtcvoiceengine.h" +#include "webrtc/modules/audio_coding/codecs/mock/mock_audio_decoder_factory.h" namespace cricket { @@ -34,7 +35,9 @@ class WebRtcMediaEngineNullVideo // Simple test to check if NullWebRtcVideoEngine implements the methods // required by CompositeMediaEngine. TEST(NullWebRtcVideoEngineTest, CheckInterface) { - WebRtcMediaEngineNullVideo engine(nullptr, nullptr, nullptr, nullptr); + WebRtcMediaEngineNullVideo engine( + nullptr, webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr, + nullptr); EXPECT_TRUE(engine.Init()); } diff --git a/webrtc/media/engine/payload_type_mapper.cc b/webrtc/media/engine/payload_type_mapper.cc new file mode 100644 index 0000000000..87b54be33b --- /dev/null +++ b/webrtc/media/engine/payload_type_mapper.cc @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2016 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 "webrtc/media/engine/payload_type_mapper.h" + +#include "webrtc/common_types.h" +#include "webrtc/media/base/mediaconstants.h" + +namespace cricket { + +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 + // payload types unassigned by the RFC are used for dynamic payload type + // mapping, before any static payload ids. At this point, we only support + // mapping within the exclusive range. + : next_unused_payload_type_(96), + max_payload_type_(127), + mappings_({ + // Static payload type assignments according to RFC 3551. + {{"PCMU", 8000, 1}, 0}, + {{"GSM", 8000, 1}, 3}, + {{"G723", 8000, 1}, 4}, + {{"DVI4", 8000, 1}, 5}, + {{"DVI4", 16000, 1}, 6}, + {{"LPC", 8000, 1}, 7}, + {{"PCMA", 8000, 1}, 8}, + {{"G722", 8000, 1}, 9}, + {{"L16", 44100, 2}, 10}, + {{"L16", 44100, 1}, 11}, + {{"QCELP", 8000, 1}, 12}, + {{"CN", 8000, 1}, 13}, + // RFC 4566 is a bit ambiguous on the contents of the "encoding + // parameters" field, which, for audio, encodes the number of + // channels. It is "optional and may be omitted if the number of + // channels is one". Does that necessarily imply that an omitted + // encoding parameter means one channel? Since RFC 3551 doesn't + // specify a value for this parameter for MPA, I've included both 0 + // and 1 here, to increase the chances it will be correctly used if + // someone implements an MPEG audio encoder/decoder. + {{"MPA", 90000, 0}, 14}, + {{"MPA", 90000, 1}, 14}, + {{"G728", 8000, 1}, 15}, + {{"DVI4", 11025, 1}, 16}, + {{"DVI4", 22050, 1}, 17}, + {{"G729", 8000, 1}, 18}, + + // Payload type assignments currently used by WebRTC. + // Includes video, to reduce collisions (and thus reassignments) + // RTX codecs mapping to specific video payload types + {{kRtxCodecName, 90000, 0, + {{kCodecParamAssociatedPayloadType, + std::to_string(kDefaultVp8PlType)}}}, + kDefaultRtxVp8PlType}, + {{kRtxCodecName, 90000, 0, + {{kCodecParamAssociatedPayloadType, + std::to_string(kDefaultVp9PlType)}}}, + kDefaultRtxVp9PlType}, + {{kRtxCodecName, 90000, 0, + {{kCodecParamAssociatedPayloadType, + std::to_string(kDefaultRedPlType)}}}, + kDefaultRtxRedPlType}, + {{kRtxCodecName, 90000, 0, + {{kCodecParamAssociatedPayloadType, + std::to_string(kDefaultH264PlType)}}}, + kDefaultRtxH264PlType}, + // Other codecs + {{kVp8CodecName, 90000, 0}, kDefaultVp8PlType}, + {{kVp9CodecName, 90000, 0}, kDefaultVp9PlType}, + {{kIlbcCodecName, 8000, 1}, 102}, + {{kIsacCodecName, 16000, 1}, 103}, + {{kIsacCodecName, 32000, 1}, 104}, + {{kCnCodecName, 16000, 1}, 105}, + {{kCnCodecName, 32000, 1}, 106}, + {{kH264CodecName, 90000, 0}, kDefaultH264PlType}, + {{kOpusCodecName, 48000, 2, + {{"minptime", "10"}, {"useinbandfec", "1"}}}, 111}, + {{kRedCodecName, 90000, 0}, kDefaultRedPlType}, + {{kUlpfecCodecName, 90000, 0}, kDefaultUlpfecType}, + {{kDtmfCodecName, 8000, 1}, 126}}) { + // TODO(ossu): Try to keep this as change-proof as possible until we're able + // to remove the payload type constants from everywhere in the code. + for (const auto& mapping : mappings_) { + used_payload_types_.insert(mapping.second); + } +} + +PayloadTypeMapper::~PayloadTypeMapper() = default; + +rtc::Optional PayloadTypeMapper::GetMappingFor( + const webrtc::SdpAudioFormat& format) { + auto iter = mappings_.find(format); + if (iter != mappings_.end()) + return rtc::Optional(iter->second); + + for (; next_unused_payload_type_ <= max_payload_type_; + ++next_unused_payload_type_) { + int payload_type = next_unused_payload_type_; + if (used_payload_types_.find(payload_type) == used_payload_types_.end()) { + used_payload_types_.insert(payload_type); + mappings_[format] = payload_type; + ++next_unused_payload_type_; + return rtc::Optional(payload_type); + } + } + + return rtc::Optional(); +} + +rtc::Optional PayloadTypeMapper::FindMappingFor( + const webrtc::SdpAudioFormat& format) const { + auto iter = mappings_.find(format); + if (iter != mappings_.end()) + return rtc::Optional(iter->second); + + return rtc::Optional(); +} + +rtc::Optional PayloadTypeMapper::ToAudioCodec( + const webrtc::SdpAudioFormat& format) { + // TODO(ossu): We can safely set bitrate to zero here, since that field is + // not presented in the SDP. It is used to ferry around some target bitrate + // values for certain codecs (ISAC and Opus) and in ways it really + // shouldn't. It should be removed once we no longer use CodecInsts in the + // ACM or NetEq. + auto opt_payload_type = GetMappingFor(format); + if (opt_payload_type) { + AudioCodec codec(*opt_payload_type, format.name, format.clockrate_hz, 0, + format.num_channels); + codec.params = format.parameters; + return rtc::Optional(std::move(codec)); + } + + return rtc::Optional(); +} + +bool PayloadTypeMapper::SdpAudioFormatOrdering::operator()( + const webrtc::SdpAudioFormat& a, + const webrtc::SdpAudioFormat& b) const { + if (a.clockrate_hz == b.clockrate_hz) { + if (a.num_channels == b.num_channels) { + int name_cmp = STR_CASE_CMP(a.name.c_str(), b.name.c_str()); + if (name_cmp == 0) + return a.parameters < b.parameters; + return name_cmp < 0; + } + return a.num_channels < b.num_channels; + } + return a.clockrate_hz < b.clockrate_hz; +} + +} // namespace cricket diff --git a/webrtc/media/engine/payload_type_mapper.h b/webrtc/media/engine/payload_type_mapper.h new file mode 100644 index 0000000000..a79fb4756c --- /dev/null +++ b/webrtc/media/engine/payload_type_mapper.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016 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 WEBRTC_MEDIA_ENGINE_PAYLOAD_TYPE_MAPPER_H_ +#define WEBRTC_MEDIA_ENGINE_PAYLOAD_TYPE_MAPPER_H_ + +#include +#include + +#include "webrtc/base/optional.h" +#include "webrtc/media/base/codec.h" +#include "webrtc/modules/audio_coding/codecs/audio_format.h" + +namespace cricket { + +class PayloadTypeMapper { + public: + PayloadTypeMapper(); + ~PayloadTypeMapper(); + + // Finds the current payload type for |format| or assigns a new one, if no + // current mapping exists. Will return an empty value if it was unable to + // create a mapping, i.e. if all dynamic payload type ids have been used up. + rtc::Optional GetMappingFor(const webrtc::SdpAudioFormat& format); + + // Finds the current payload type for |format|, if any. Returns an empty value + // if no payload type mapping exists for the format. + rtc::Optional FindMappingFor(const webrtc::SdpAudioFormat& format) const; + + // Like GetMappingFor, but fills in an AudioCodec structure with the necessary + // information instead. + rtc::Optional ToAudioCodec(const webrtc::SdpAudioFormat& format); + + private: + struct SdpAudioFormatOrdering { + bool operator()(const webrtc::SdpAudioFormat& a, + const webrtc::SdpAudioFormat& b) const; + }; + + int next_unused_payload_type_; + int max_payload_type_; + std::map mappings_; + std::set used_payload_types_; +}; + +} // namespace cricket +#endif // WEBRTC_MEDIA_ENGINE_PAYLOAD_TYPE_MAPPER_H_ diff --git a/webrtc/media/engine/payload_type_mapper_unittest.cc b/webrtc/media/engine/payload_type_mapper_unittest.cc new file mode 100644 index 0000000000..7042086638 --- /dev/null +++ b/webrtc/media/engine/payload_type_mapper_unittest.cc @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2016 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 +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/media/engine/payload_type_mapper.h" + +namespace cricket { + +class PayloadTypeMapperTest : public testing::Test { + public: + // TODO(ossu): These are to work around missing comparison operators in + // rtc::Optional. They should be removed once Optional has been updated. + int FindMapping(const webrtc::SdpAudioFormat& format) { + auto opt_mapping = mapper_.FindMappingFor(format); + if (opt_mapping) + return *opt_mapping; + return -1; + } + + int GetMapping(const webrtc::SdpAudioFormat& format) { + auto opt_mapping = mapper_.GetMappingFor(format); + if (opt_mapping) + return *opt_mapping; + return -1; + } + + protected: + PayloadTypeMapper mapper_; +}; + +TEST_F(PayloadTypeMapperTest, StaticPayloadTypes) { + EXPECT_EQ(0, FindMapping({"pcmu", 8000, 1})); + EXPECT_EQ(3, FindMapping({"gsm", 8000, 1})); + EXPECT_EQ(4, FindMapping({"g723", 8000, 1})); + EXPECT_EQ(5, FindMapping({"dvi4", 8000, 1})); + EXPECT_EQ(6, FindMapping({"dvi4", 16000, 1})); + EXPECT_EQ(7, FindMapping({"lpc", 8000, 1})); + EXPECT_EQ(8, FindMapping({"pcma", 8000, 1})); + EXPECT_EQ(9, FindMapping({"g722", 8000, 1})); + EXPECT_EQ(10, FindMapping({"l16", 44100, 2})); + EXPECT_EQ(11, FindMapping({"l16", 44100, 1})); + EXPECT_EQ(12, FindMapping({"qcelp", 8000, 1})); + EXPECT_EQ(13, FindMapping({"cn", 8000, 1})); + EXPECT_EQ(14, FindMapping({"mpa", 90000, 0})); + EXPECT_EQ(14, FindMapping({"mpa", 90000, 1})); + EXPECT_EQ(15, FindMapping({"g728", 8000, 1})); + EXPECT_EQ(16, FindMapping({"dvi4", 11025, 1})); + EXPECT_EQ(17, FindMapping({"dvi4", 22050, 1})); + EXPECT_EQ(18, FindMapping({"g729", 8000, 1})); +} + +TEST_F(PayloadTypeMapperTest, WebRTCPayloadTypes) { + // Tests that the payload mapper knows about the formats we've been using in + // WebRTC, with their hard coded values. + auto video_mapping = [this] (const char *name) { + return FindMapping({name, kVideoCodecClockrate, 0}); + }; + EXPECT_EQ(kDefaultVp8PlType, video_mapping(kVp8CodecName)); + EXPECT_EQ(kDefaultVp9PlType, video_mapping(kVp9CodecName)); + EXPECT_EQ(kDefaultH264PlType, video_mapping(kH264CodecName)); + EXPECT_EQ(kDefaultRedPlType, video_mapping(kRedCodecName)); + EXPECT_EQ(kDefaultUlpfecType, video_mapping(kUlpfecCodecName)); + + auto rtx_mapping = [this] (int payload_type) { + return FindMapping({kRtxCodecName, kVideoCodecClockrate, 0, + {{ kCodecParamAssociatedPayloadType, std::to_string(payload_type)}}}); + }; + EXPECT_EQ(kDefaultRtxVp8PlType, rtx_mapping(kDefaultVp8PlType)); + EXPECT_EQ(kDefaultRtxVp9PlType, rtx_mapping(kDefaultVp9PlType)); + EXPECT_EQ(kDefaultRtxH264PlType, rtx_mapping(kDefaultH264PlType)); + EXPECT_EQ(kDefaultRtxRedPlType, rtx_mapping(kDefaultRedPlType)); + + EXPECT_EQ(102, FindMapping({kIlbcCodecName, 8000, 1})); + EXPECT_EQ(103, FindMapping({kIsacCodecName, 16000, 1})); + EXPECT_EQ(104, FindMapping({kIsacCodecName, 32000, 1})); + EXPECT_EQ(105, FindMapping({kCnCodecName, 16000, 1})); + EXPECT_EQ(106, FindMapping({kCnCodecName, 32000, 1})); + EXPECT_EQ(111, FindMapping({kOpusCodecName, 48000, 2, + {{"minptime", "10"}, {"useinbandfec", "1"}}})); + EXPECT_EQ(126, FindMapping({kDtmfCodecName, 8000, 1})); +} + +TEST_F(PayloadTypeMapperTest, ValidDynamicPayloadTypes) { + // RFC 3551 says: + // "This profile reserves payload type numbers in the range 96-127 + // exclusively for dynamic assignment. Applications SHOULD first use + // values in this range for dynamic payload types. Those applications + // which need to define more than 32 dynamic payload types MAY bind + // codes below 96, in which case it is RECOMMENDED that unassigned + // payload type numbers be used first. However, the statically assigned + // payload types are default bindings and MAY be dynamically bound to + // new encodings if needed." + + // Tests that the payload mapper uses values in the dynamic payload type range + // (96 - 127) before any others and that the values returned are all valid. + bool has_been_below_96 = false; + std::set used_payload_types; + for (int i = 0; i != 256; ++i) { + std::string format_name = "unknown_format_" + std::to_string(i); + webrtc::SdpAudioFormat format(format_name.c_str(), i*100, (i % 2) + 1); + auto opt_payload_type = mapper_.GetMappingFor(format); + bool mapper_is_full = false; + + // There's a limited number of slots for payload types. We're fine with not + // being able to map them all. + if (opt_payload_type) { + int payload_type = *opt_payload_type; + EXPECT_FALSE(mapper_is_full) << "Mapping should not fail sporadically"; + EXPECT_EQ(used_payload_types.find(payload_type), used_payload_types.end()) + << "Payload types must not be reused"; + used_payload_types.insert(payload_type); + EXPECT_GE(payload_type, 0) << "Negative payload types are invalid"; + EXPECT_LE(payload_type, 127) << "Payload types above 127 are invalid"; + EXPECT_FALSE(payload_type >= 96 && has_been_below_96); + if (payload_type < 96) + has_been_below_96 = true; + + EXPECT_EQ(payload_type, FindMapping(format)) + << "Mapping must be permanent after successful call to " + "GetMappingFor"; + EXPECT_EQ(payload_type, GetMapping(format)) + << "Subsequent calls to GetMappingFor must return the same value"; + } else { + mapper_is_full = true; + } + } + + // Also, we must've been able to map at least one dynamic payload type. + EXPECT_FALSE(used_payload_types.empty()) + << "Mapper must support at least one user-defined payload type"; +} + +TEST_F(PayloadTypeMapperTest, ToAudioCodec) { + webrtc::SdpAudioFormat format("unknown_format", 4711, 17); + auto opt_payload_type = mapper_.GetMappingFor(format); + EXPECT_TRUE(opt_payload_type); + auto opt_audio_codec = mapper_.ToAudioCodec(format); + EXPECT_TRUE(opt_audio_codec); + + if (opt_payload_type && opt_audio_codec) { + int payload_type = *opt_payload_type; + const AudioCodec& codec = *opt_audio_codec; + + EXPECT_EQ(codec.id, payload_type); + EXPECT_EQ(codec.name, format.name); + EXPECT_EQ(codec.clockrate, format.clockrate_hz); + EXPECT_EQ(codec.channels, format.num_channels); + EXPECT_EQ(codec.params, format.parameters); + } +} + +} // namespace cricket diff --git a/webrtc/media/engine/webrtcmediaengine.h b/webrtc/media/engine/webrtcmediaengine.h index 24deeb4d0e..dae63477d6 100644 --- a/webrtc/media/engine/webrtcmediaengine.h +++ b/webrtc/media/engine/webrtcmediaengine.h @@ -18,6 +18,7 @@ #include "webrtc/media/base/mediaengine.h" namespace webrtc { +class AudioDecoderFactory; class AudioDeviceModule; } namespace cricket { diff --git a/webrtc/media/engine/webrtcvoiceengine.cc b/webrtc/media/engine/webrtcvoiceengine.cc index 469098a322..59a2870cf8 100644 --- a/webrtc/media/engine/webrtcvoiceengine.cc +++ b/webrtc/media/engine/webrtcvoiceengine.cc @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -32,6 +33,7 @@ #include "webrtc/media/base/audiosource.h" #include "webrtc/media/base/mediaconstants.h" #include "webrtc/media/base/streamparams.h" +#include "webrtc/media/engine/payload_type_mapper.h" #include "webrtc/media/engine/webrtcmediaengine.h" #include "webrtc/media/engine/webrtcvoe.h" #include "webrtc/modules/audio_coding/acm2/rent_a_codec.h" @@ -248,7 +250,7 @@ class WebRtcVoiceCodecs final { public: // TODO(solenberg): Do this filtering once off-line, add a simple AudioCodec // list and add a test which verifies VoE supports the listed codecs. - static std::vector SupportedCodecs() { + static std::vector SupportedSendCodecs() { std::vector result; // Iterate first over our preferred codecs list, so that the results are // added in order of preference. @@ -511,13 +513,20 @@ WebRtcVoiceEngine::WebRtcVoiceEngine( RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); LOG(LS_INFO) << "WebRtcVoiceEngine::WebRtcVoiceEngine"; RTC_DCHECK(voe_wrapper); + RTC_DCHECK(decoder_factory); signal_thread_checker_.DetachFromThread(); // Load our audio codec list. - LOG(LS_INFO) << "Supported codecs in order of preference:"; - codecs_ = WebRtcVoiceCodecs::SupportedCodecs(); - for (const AudioCodec& codec : codecs_) { + LOG(LS_INFO) << "Supported send codecs in order of preference:"; + send_codecs_ = WebRtcVoiceCodecs::SupportedSendCodecs(); + for (const AudioCodec& codec : send_codecs_) { + LOG(LS_INFO) << ToString(codec); + } + + LOG(LS_INFO) << "Supported recv codecs in order of preference:"; + recv_codecs_ = CollectRecvCodecs(); + for (const AudioCodec& codec : recv_codecs_) { LOG(LS_INFO) << ToString(codec); } @@ -936,12 +945,12 @@ int WebRtcVoiceEngine::GetInputLevel() { const std::vector& WebRtcVoiceEngine::send_codecs() const { RTC_DCHECK(signal_thread_checker_.CalledOnValidThread()); - return codecs_; + return send_codecs_; } const std::vector& WebRtcVoiceEngine::recv_codecs() const { RTC_DCHECK(signal_thread_checker_.CalledOnValidThread()); - return codecs_; + return recv_codecs_; } RtpCapabilities WebRtcVoiceEngine::GetCapabilities() const { @@ -1081,6 +1090,61 @@ webrtc::AudioDeviceModule* WebRtcVoiceEngine::adm() { return adm_; } +AudioCodecs WebRtcVoiceEngine::CollectRecvCodecs() const { + PayloadTypeMapper mapper; + AudioCodecs out; + const std::vector& formats = + decoder_factory_->GetSupportedFormats(); + + // Only generate CN payload types for these clockrates + std::map> generate_cn = {{ 8000, false }, + { 16000, false }, + { 32000, false }}; + + auto map_format = [&mapper, &out] (const webrtc::SdpAudioFormat& format) { + rtc::Optional opt_codec = mapper.ToAudioCodec(format); + if (!opt_codec) { + LOG(LS_ERROR) << "Unable to assign payload type to format: " << format; + return false; + } + + auto& codec = *opt_codec; + if (IsCodec(codec, kOpusCodecName)) { + // TODO(ossu): Set this specifically for Opus for now, until we have a + // better way of dealing with rtcp-fb parameters. + codec.AddFeedbackParam( + FeedbackParam(kRtcpFbParamTransportCc, kParamValueEmpty)); + } + out.push_back(codec); + return true; + }; + + for (const auto& format : formats) { + if (map_format(format)) { + // TODO(ossu): We should get more than just a format from the factory, so + // we can determine if a format should be used with CN or not. For now, + // generate a CN entry for each supported clock rate also used by a format + // supported by the factory. + auto cn = generate_cn.find(format.clockrate_hz); + if (cn != generate_cn.end() /* && format.allow_comfort_noise */) { + cn->second = true; + } + } + } + + // Add CN codecs after "proper" audio codecs + for (const auto& cn : generate_cn) { + if (cn.second) { + map_format({kCnCodecName, cn.first, 1}); + } + } + + // Add telephone-event codec last + map_format({kDtmfCodecName, 8000, 1}); + + return out; +} + class WebRtcVoiceMediaChannel::WebRtcAudioSendStream : public AudioSource::Sink { public: diff --git a/webrtc/media/engine/webrtcvoiceengine.h b/webrtc/media/engine/webrtcvoiceengine.h index 64e0f5b185..7955c2090a 100644 --- a/webrtc/media/engine/webrtcvoiceengine.h +++ b/webrtc/media/engine/webrtcvoiceengine.h @@ -123,6 +123,8 @@ class WebRtcVoiceEngine final : public webrtc::TraceCallback { int CreateVoEChannel(); webrtc::AudioDeviceModule* adm(); + AudioCodecs CollectRecvCodecs() const; + rtc::ThreadChecker signal_thread_checker_; rtc::ThreadChecker worker_thread_checker_; @@ -132,7 +134,8 @@ class WebRtcVoiceEngine final : public webrtc::TraceCallback { // The primary instance of WebRtc VoiceEngine. std::unique_ptr voe_wrapper_; rtc::scoped_refptr audio_state_; - std::vector codecs_; + std::vector send_codecs_; + std::vector recv_codecs_; std::vector channels_; webrtc::Config voe_config_; bool is_dumping_aec_ = false; diff --git a/webrtc/media/engine/webrtcvoiceengine_unittest.cc b/webrtc/media/engine/webrtcvoiceengine_unittest.cc index 2db70d1521..ec7a168f65 100644 --- a/webrtc/media/engine/webrtcvoiceengine_unittest.cc +++ b/webrtc/media/engine/webrtcvoiceengine_unittest.cc @@ -75,7 +75,9 @@ TEST(WebRtcVoiceEngineTestStubLibrary, StartupShutdown) { cricket::FakeWebRtcVoiceEngine voe; EXPECT_FALSE(voe.IsInited()); { - cricket::WebRtcVoiceEngine engine(&adm, nullptr, new FakeVoEWrapper(&voe)); + cricket::WebRtcVoiceEngine engine( + &adm, webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), + new FakeVoEWrapper(&voe)); EXPECT_TRUE(voe.IsInited()); } EXPECT_FALSE(voe.IsInited()); @@ -96,12 +98,13 @@ class WebRtcVoiceEngineTestFake : public testing::Test { explicit WebRtcVoiceEngineTestFake(const char* field_trials) : call_(webrtc::Call::Config()), 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)); EXPECT_CALL(adm_, BuiltInAGCIsAvailable()).WillOnce(Return(false)); EXPECT_CALL(adm_, BuiltInNSIsAvailable()).WillOnce(Return(false)); - engine_.reset(new cricket::WebRtcVoiceEngine(&adm_, nullptr, + engine_.reset(new cricket::WebRtcVoiceEngine(&adm_, factory, new FakeVoEWrapper(&voe_))); send_parameters_.codecs.push_back(kPcmuCodec); recv_parameters_.codecs.push_back(kPcmuCodec); @@ -3361,20 +3364,10 @@ TEST_F(WebRtcVoiceEngineTestFake, OnReadyToSendSignalsNetworkState) { // Tests that the library initializes and shuts down properly. TEST(WebRtcVoiceEngineTest, StartupShutdown) { - using testing::_; - using testing::AnyNumber; - // If the VoiceEngine wants to gather available codecs early, that's fine but // we never want it to create a decoder at this stage. - rtc::scoped_refptr factory = - new rtc::RefCountedObject; - ON_CALL(*factory.get(), GetSupportedFormats()) - .WillByDefault(Return(std::vector())); - EXPECT_CALL(*factory.get(), GetSupportedFormats()) - .Times(AnyNumber()); - EXPECT_CALL(*factory.get(), MakeAudioDecoderMock(_, _)).Times(0); - - cricket::WebRtcVoiceEngine engine(nullptr, factory); + cricket::WebRtcVoiceEngine engine( + nullptr, webrtc::MockAudioDecoderFactory::CreateUnusedFactory()); std::unique_ptr call( webrtc::Call::Create(webrtc::Call::Config())); cricket::VoiceMediaChannel* channel = engine.CreateChannel( @@ -3389,7 +3382,8 @@ TEST(WebRtcVoiceEngineTest, StartupShutdownWithExternalADM) { EXPECT_CALL(adm, AddRef()).Times(3).WillRepeatedly(Return(0)); EXPECT_CALL(adm, Release()).Times(3).WillRepeatedly(Return(0)); { - cricket::WebRtcVoiceEngine engine(&adm, nullptr); + cricket::WebRtcVoiceEngine engine( + &adm, webrtc::MockAudioDecoderFactory::CreateUnusedFactory()); std::unique_ptr call( webrtc::Call::Create(webrtc::Call::Config())); cricket::VoiceMediaChannel* channel = engine.CreateChannel( @@ -3400,8 +3394,6 @@ TEST(WebRtcVoiceEngineTest, StartupShutdownWithExternalADM) { } // Tests that the library is configured with the codecs we want. -// TODO(ossu): This test should move into the builtin audio codecs module -// eventually. TEST(WebRtcVoiceEngineTest, HasCorrectCodecs) { // TODO(ossu): These tests should move into a future "builtin audio codecs" // module. @@ -3457,10 +3449,13 @@ TEST(WebRtcVoiceEngineTest, HasCorrectCodecs) { cricket::AudioCodec(0, "", 0, 5000, 1), nullptr)); // Verify the payload id of common audio codecs, including CN, ISAC, and G722. - cricket::WebRtcVoiceEngine engine(nullptr, - webrtc::CreateBuiltinAudioDecoderFactory()); + // TODO(ossu): Why are the payload types of codecs with non-static payload + // type assignments checked here? It shouldn't really matter. + cricket::WebRtcVoiceEngine engine( + nullptr, webrtc::MockAudioDecoderFactory::CreateUnusedFactory()); for (std::vector::const_iterator it = - engine.send_codecs().begin(); it != engine.send_codecs().end(); ++it) { + engine.send_codecs().begin(); + it != engine.send_codecs().end(); ++it) { if (it->name == "CN" && it->clockrate == 16000) { EXPECT_EQ(105, it->id); } else if (it->name == "CN" && it->clockrate == 32000) { @@ -3485,7 +3480,8 @@ TEST(WebRtcVoiceEngineTest, HasCorrectCodecs) { // Tests that VoE supports at least 32 channels TEST(WebRtcVoiceEngineTest, Has32Channels) { - cricket::WebRtcVoiceEngine engine(nullptr, nullptr); + cricket::WebRtcVoiceEngine engine( + nullptr, webrtc::MockAudioDecoderFactory::CreateUnusedFactory()); std::unique_ptr call( webrtc::Call::Create(webrtc::Call::Config())); diff --git a/webrtc/media/media.gyp b/webrtc/media/media.gyp index 1e19a84936..0523b710de 100644 --- a/webrtc/media/media.gyp +++ b/webrtc/media/media.gyp @@ -69,6 +69,8 @@ 'base/videosourcebase.h', 'devices/videorendererfactory.h', 'engine/nullwebrtcvideoengine.h', + 'engine/payload_type_mapper.cc', + 'engine/payload_type_mapper.h', 'engine/simulcast.cc', 'engine/simulcast.h', 'engine/webrtccommon.h', @@ -267,6 +269,7 @@ 'base/videoengine_unittest.h', 'base/videoframe_unittest.h', 'engine/nullwebrtcvideoengine_unittest.cc', + 'engine/payload_type_mapper_unittest.cc', 'engine/simulcast_unittest.cc', 'engine/webrtcmediaengine_unittest.cc', 'engine/webrtcvideocapturer_unittest.cc', 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 48b2f5d437..a7f7404892 100644 --- a/webrtc/modules/audio_coding/codecs/builtin_audio_decoder_factory.cc +++ b/webrtc/modules/audio_coding/codecs/builtin_audio_decoder_factory.cc @@ -130,7 +130,31 @@ NamedDecoderConstructor decoder_constructors[] = { class BuiltinAudioDecoderFactory : public AudioDecoderFactory { public: std::vector GetSupportedFormats() override { - FATAL() << "Not implemented yet!"; + static std::vector formats = { +#ifdef WEBRTC_CODEC_OPUS + { "opus", 48000, 2, { + {"minptime", "10" }, + {"useinbandfec", "1" } + } + }, +#endif +#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) + { "isac", 16000, 1 }, +#endif +#if (defined(WEBRTC_CODEC_ISAC)) + { "isac", 32000, 1 }, +#endif +#ifdef WEBRTC_CODEC_G722 + { "G722", 8000, 1 }, +#endif +#ifdef WEBRTC_CODEC_ILBC + { "iLBC", 8000, 1 }, +#endif + { "PCMU", 8000, 1 }, + { "PCMA", 8000, 1 } + }; + + return formats; } std::unique_ptr MakeAudioDecoder( 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 6e5737c89b..f91a26bc10 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 @@ -14,6 +14,7 @@ #include #include "testing/gmock/include/gmock/gmock.h" +#include "webrtc/base/scoped_ref_ptr.h" #include "webrtc/modules/audio_coding/codecs/audio_decoder_factory.h" namespace webrtc { @@ -30,6 +31,45 @@ class MockAudioDecoderFactory : public AudioDecoderFactory { MOCK_METHOD2(MakeAudioDecoderMock, void(const SdpAudioFormat& format, std::unique_ptr* return_value)); + + // Creates a MockAudioDecoderFactory with no formats and that may not be + // invoked to create a codec - useful for initializing a voice engine, for + // example. + static rtc::scoped_refptr + CreateUnusedFactory() { + using testing::_; + using testing::AnyNumber; + using testing::Return; + + rtc::scoped_refptr factory = + new rtc::RefCountedObject; + ON_CALL(*factory.get(), GetSupportedFormats()) + .WillByDefault(Return(std::vector())); + EXPECT_CALL(*factory.get(), GetSupportedFormats()).Times(AnyNumber()); + EXPECT_CALL(*factory.get(), MakeAudioDecoderMock(_, _)).Times(0); + return factory; + } + + // Creates a MockAudioDecoderFactory with no formats that may be invoked to + // create a codec any number of times. It will, though, return nullptr on each + // call, since it supports no codecs. + static rtc::scoped_refptr + CreateEmptyFactory() { + using testing::_; + using testing::AnyNumber; + using testing::Return; + using testing::SetArgPointee; + + rtc::scoped_refptr factory = + new rtc::RefCountedObject; + ON_CALL(*factory.get(), GetSupportedFormats()) + .WillByDefault(Return(std::vector())); + EXPECT_CALL(*factory.get(), GetSupportedFormats()).Times(AnyNumber()); + ON_CALL(*factory.get(), MakeAudioDecoderMock(_, _)) + .WillByDefault(SetArgPointee<1>(nullptr)); + EXPECT_CALL(*factory.get(), MakeAudioDecoderMock(_, _)).Times(AnyNumber()); + return factory; + } }; } // namespace webrtc