diff --git a/talk/media/webrtc/fakewebrtcvoiceengine.h b/talk/media/webrtc/fakewebrtcvoiceengine.h index f0bd6a6e85..6ca334169c 100644 --- a/talk/media/webrtc/fakewebrtcvoiceengine.h +++ b/talk/media/webrtc/fakewebrtcvoiceengine.h @@ -41,6 +41,7 @@ #include "webrtc/base/gunit.h" #include "webrtc/base/stringutils.h" #include "webrtc/config.h" +#include "webrtc/modules/audio_coding/acm2/rent_a_codec.h" #include "webrtc/modules/audio_processing/include/audio_processing.h" namespace cricket { @@ -63,18 +64,6 @@ static const int kOpusBandwidthFb = 20000; #define WEBRTC_CHECK_CHANNEL(channel) \ if (channels_.find(channel) == channels_.end()) return -1; -#define WEBRTC_ASSERT_CHANNEL(channel) \ - RTC_DCHECK(channels_.find(channel) != channels_.end()); - -// Verify the header extension ID, if enabled, is within the bounds specified in -// [RFC5285]: 1-14 inclusive. -#define WEBRTC_CHECK_HEADER_EXTENSION_ID(enable, id) \ - do { \ - if (enable && (id < 1 || id > 14)) { \ - return -1; \ - } \ - } while (0); - class FakeAudioProcessing : public webrtc::AudioProcessing { public: FakeAudioProcessing() : experimental_ns_enabled_(false) {} @@ -189,6 +178,7 @@ class FakeWebRtcVoiceEngine nack_max_packets(0), send_ssrc(0), associate_send_channel(-1), + recv_codecs(), neteq_capacity(-1), neteq_fast_accelerate(false) { memset(&send_codec, 0, sizeof(send_codec)); @@ -219,13 +209,10 @@ class FakeWebRtcVoiceEngine bool neteq_fast_accelerate; }; - FakeWebRtcVoiceEngine(const cricket::AudioCodec* const* codecs, - int num_codecs) + FakeWebRtcVoiceEngine() : inited_(false), last_channel_(-1), fail_create_channel_(false), - codecs_(codecs), - num_codecs_(num_codecs), num_set_send_codecs_(0), ec_enabled_(false), ec_metrics_enabled_(false), @@ -247,12 +234,7 @@ class FakeWebRtcVoiceEngine memset(&agc_config_, 0, sizeof(agc_config_)); } ~FakeWebRtcVoiceEngine() { - // Ought to have all been deleted by the WebRtcVoiceMediaChannel - // destructors, but just in case ... - for (std::map::const_iterator i = channels_.begin(); - i != channels_.end(); ++i) { - delete i->second; - } + RTC_CHECK(channels_.empty()); } bool ec_metrics_enabled() const { return ec_metrics_enabled_; } @@ -291,7 +273,7 @@ class FakeWebRtcVoiceEngine return channels_[channel]->nack_max_packets; } const webrtc::PacketTime& GetLastRtpPacketTime(int channel) { - WEBRTC_ASSERT_CHANNEL(channel); + RTC_DCHECK(channels_.find(channel) != channels_.end()); return channels_[channel]->last_rtp_packet_time; } int GetSendCNPayloadType(int channel, bool wideband) { @@ -335,11 +317,8 @@ class FakeWebRtcVoiceEngine return -1; } Channel* ch = new Channel(); - for (int i = 0; i < NumOfCodecs(); ++i) { - webrtc::CodecInst codec; - GetCodec(i, codec); - ch->recv_codecs.push_back(codec); - } + auto db = webrtc::acm2::RentACodec::Database(); + ch->recv_codecs.assign(db.begin(), db.end()); if (config.Get().enabled) { ch->neteq_capacity = config.Get().capacity; } @@ -439,22 +418,8 @@ class FakeWebRtcVoiceEngine webrtc::RtcEventLog* GetEventLog() { return nullptr; } // webrtc::VoECodec - WEBRTC_FUNC(NumOfCodecs, ()) { - return num_codecs_; - } - WEBRTC_FUNC(GetCodec, (int index, webrtc::CodecInst& codec)) { - if (index < 0 || index >= NumOfCodecs()) { - return -1; - } - const cricket::AudioCodec& c(*codecs_[index]); - codec.pltype = c.id; - rtc::strcpyn(codec.plname, sizeof(codec.plname), c.name.c_str()); - codec.plfreq = c.clockrate; - codec.pacsize = 0; - codec.channels = c.channels; - codec.rate = c.bitrate; - return 0; - } + WEBRTC_STUB(NumOfCodecs, ()); + WEBRTC_STUB(GetCodec, (int index, webrtc::CodecInst& codec)); WEBRTC_FUNC(SetSendCodec, (int channel, const webrtc::CodecInst& codec)) { WEBRTC_CHECK_CHANNEL(channel); // To match the behavior of the real implementation. @@ -492,16 +457,17 @@ class FakeWebRtcVoiceEngine } } // Otherwise try to find this codec and update its payload type. + int result = -1; // not found for (std::vector::iterator it = ch->recv_codecs.begin(); it != ch->recv_codecs.end(); ++it) { if (strcmp(it->plname, codec.plname) == 0 && - it->plfreq == codec.plfreq) { + it->plfreq == codec.plfreq && + it->channels == codec.channels) { it->pltype = codec.pltype; - it->channels = codec.channels; - return 0; + result = 0; } } - return -1; // not found + return result; } WEBRTC_FUNC(SetSendCNPayloadType, (int channel, int type, webrtc::PayloadFrequencies frequency)) { @@ -932,8 +898,6 @@ class FakeWebRtcVoiceEngine int last_channel_; std::map channels_; bool fail_create_channel_; - const cricket::AudioCodec* const* codecs_; - int num_codecs_; int num_set_send_codecs_; // how many times we call SetSendCodec(). bool ec_enabled_; bool ec_metrics_enabled_; @@ -957,8 +921,6 @@ class FakeWebRtcVoiceEngine FakeAudioProcessing audio_processing_; }; -#undef WEBRTC_CHECK_HEADER_EXTENSION_ID - } // namespace cricket #endif // TALK_SESSION_PHONE_FAKEWEBRTCVOICEENGINE_H_ diff --git a/talk/media/webrtc/webrtcvoiceengine.cc b/talk/media/webrtc/webrtcvoiceengine.cc index 721990a036..4bd0400dfa 100644 --- a/talk/media/webrtc/webrtcvoiceengine.cc +++ b/talk/media/webrtc/webrtcvoiceengine.cc @@ -53,6 +53,7 @@ #include "webrtc/base/stringutils.h" #include "webrtc/call/rtc_event_log.h" #include "webrtc/common.h" +#include "webrtc/modules/audio_coding/acm2/rent_a_codec.h" #include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/system_wrappers/include/field_trial.h" #include "webrtc/system_wrappers/include/trace.h" @@ -66,32 +67,6 @@ const int kDefaultTraceFilter = webrtc::kTraceNone | webrtc::kTraceTerseInfo | const int kElevatedTraceFilter = kDefaultTraceFilter | webrtc::kTraceStateInfo | webrtc::kTraceInfo; -const int kMaxNumPacketSize = 6; -struct CodecPref { - const char* name; - int clockrate; - int channels; - int payload_type; - bool is_multi_rate; - int packet_sizes_ms[kMaxNumPacketSize]; -}; -// Note: keep the supported packet sizes in ascending order. -const CodecPref kCodecPrefs[] = { - { kOpusCodecName, 48000, 2, 111, true, { 10, 20, 40, 60 } }, - { kIsacCodecName, 16000, 1, 103, true, { 30, 60 } }, - { kIsacCodecName, 32000, 1, 104, true, { 30 } }, - // G722 should be advertised as 8000 Hz because of the RFC "bug". - { kG722CodecName, 8000, 1, 9, false, { 10, 20, 30, 40, 50, 60 } }, - { kIlbcCodecName, 8000, 1, 102, false, { 20, 30, 40, 60 } }, - { kPcmuCodecName, 8000, 1, 0, false, { 10, 20, 30, 40, 50, 60 } }, - { kPcmaCodecName, 8000, 1, 8, false, { 10, 20, 30, 40, 50, 60 } }, - { kCnCodecName, 32000, 1, 106, false, { } }, - { kCnCodecName, 16000, 1, 105, false, { } }, - { kCnCodecName, 8000, 1, 13, false, { } }, - { kRedCodecName, 8000, 1, 127, false, { } }, - { kDtmfCodecName, 8000, 1, 126, false, { } }, -}; - // For Linux/Mac, using the default device is done by specifying index 0 for // VoE 4.0 and not -1 (which was the case for VoE 3.5). // @@ -185,13 +160,6 @@ std::string ToString(const webrtc::CodecInst& codec) { return ss.str(); } -void LogMultiline(rtc::LoggingSeverity sev, char* text) { - const char* delim = "\r\n"; - for (char* tok = strtok(text, delim); tok; tok = strtok(NULL, delim)) { - LOG_V(sev) << tok; - } -} - bool IsCodec(const AudioCodec& codec, const char* ref_name) { return (_stricmp(codec.name.c_str(), ref_name) == 0); } @@ -200,19 +168,9 @@ bool IsCodec(const webrtc::CodecInst& codec, const char* ref_name) { return (_stricmp(codec.plname, ref_name) == 0); } -bool IsCodecMultiRate(const webrtc::CodecInst& codec) { - for (size_t i = 0; i < arraysize(kCodecPrefs); ++i) { - if (IsCodec(codec, kCodecPrefs[i].name) && - kCodecPrefs[i].clockrate == codec.plfreq) { - return kCodecPrefs[i].is_multi_rate; - } - } - return false; -} - bool FindCodec(const std::vector& codecs, - const AudioCodec& codec, - AudioCodec* found_codec) { + const AudioCodec& codec, + AudioCodec* found_codec) { for (const AudioCodec& c : codecs) { if (c.Matches(codec)) { if (found_codec != NULL) { @@ -242,38 +200,8 @@ bool IsNackEnabled(const AudioCodec& codec) { kParamValueEmpty)); } -int SelectPacketSize(const CodecPref& codec_pref, int ptime_ms) { - int selected_packet_size_ms = codec_pref.packet_sizes_ms[0]; - for (int packet_size_ms : codec_pref.packet_sizes_ms) { - if (packet_size_ms && packet_size_ms <= ptime_ms) { - selected_packet_size_ms = packet_size_ms; - } - } - return selected_packet_size_ms; -} - -// If the AudioCodec param kCodecParamPTime is set, then we will set it to codec -// pacsize if it's valid, or we will pick the next smallest value we support. -// TODO(Brave): Query supported packet sizes from ACM when the API is ready. -bool SetPTimeAsPacketSize(webrtc::CodecInst* codec, int ptime_ms) { - for (const CodecPref& codec_pref : kCodecPrefs) { - if ((IsCodec(*codec, codec_pref.name) && - codec_pref.clockrate == codec->plfreq) || - IsCodec(*codec, kG722CodecName)) { - int packet_size_ms = SelectPacketSize(codec_pref, ptime_ms); - if (packet_size_ms) { - // Convert unit from milli-seconds to samples. - codec->pacsize = (codec->plfreq / 1000) * packet_size_ms; - return true; - } - } - } - return false; -} - // Return true if codec.params[feature] == "1", false otherwise. -bool IsCodecFeatureEnabled(const AudioCodec& codec, - const char* feature) { +bool IsCodecFeatureEnabled(const AudioCodec& codec, const char* feature) { int value; return codec.GetParam(feature, &value) && value == 1; } @@ -340,18 +268,6 @@ void GetOpusConfig(const AudioCodec& codec, webrtc::CodecInst* voe_codec, voe_codec->rate = GetOpusBitrate(codec, *max_playback_rate); } -// Changes RTP timestamp rate of G722. This is due to the "bug" in the RFC -// which says that G722 should be advertised as 8 kHz although it is a 16 kHz -// codec. -void MaybeFixupG722(webrtc::CodecInst* voe_codec, int new_plfreq) { - if (IsCodec(*voe_codec, kG722CodecName)) { - // If the ASSERT triggers, the codec definition in WebRTC VoiceEngine - // has changed, and this special case is no longer needed. - RTC_DCHECK(voe_codec->plfreq != new_plfreq); - voe_codec->plfreq = new_plfreq; - } -} - // Gets the default set of options applied to the engine. Historically, these // were supplied as a combination of flags from the channel manager (ec, agc, // ns, and highpass) and the rest hardcoded in InitInternal. @@ -393,54 +309,17 @@ std::vector FindAudioRtpHeaderExtensions( } return result; } -} // namespace { -WebRtcVoiceEngine::WebRtcVoiceEngine() - : voe_wrapper_(new VoEWrapper()), - audio_state_(webrtc::AudioState::Create(MakeAudioStateConfig(voe()))) { - Construct(); -} - -WebRtcVoiceEngine::WebRtcVoiceEngine(VoEWrapper* voe_wrapper) - : voe_wrapper_(voe_wrapper) { - Construct(); -} - -void WebRtcVoiceEngine::Construct() { - RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); - LOG(LS_VERBOSE) << "WebRtcVoiceEngine::WebRtcVoiceEngine"; - - signal_thread_checker_.DetachFromThread(); - std::memset(&default_agc_config_, 0, sizeof(default_agc_config_)); - - webrtc::Trace::set_level_filter(kDefaultTraceFilter); - webrtc::Trace::SetTraceCallback(this); - - // Load our audio codec list. - ConstructCodecs(); - - // Load our RTP Header extensions. - rtp_header_extensions_.push_back( - RtpHeaderExtension(kRtpAudioLevelHeaderExtension, - kRtpAudioLevelHeaderExtensionDefaultId)); - rtp_header_extensions_.push_back( - RtpHeaderExtension(kRtpAbsoluteSenderTimeHeaderExtension, - kRtpAbsoluteSenderTimeHeaderExtensionDefaultId)); - if (webrtc::field_trial::FindFullName("WebRTC-SendSideBwe") == "Enabled") { - rtp_header_extensions_.push_back(RtpHeaderExtension( - kRtpTransportSequenceNumberHeaderExtension, - kRtpTransportSequenceNumberHeaderExtensionDefaultId)); - } - options_ = GetDefaultEngineOptions(); -} - -void WebRtcVoiceEngine::ConstructCodecs() { - RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); - LOG(LS_INFO) << "WebRtc VoiceEngine codecs:"; - int ncodecs = voe_wrapper_->codec()->NumOfCodecs(); - for (int i = 0; i < ncodecs; ++i) { - webrtc::CodecInst voe_codec; - if (GetVoeCodec(i, &voe_codec)) { +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() { + LOG(LS_INFO) << "WebRtc VoiceEngine codecs:"; + std::vector result; + for (webrtc::CodecInst voe_codec : webrtc::acm2::RentACodec::Database()) { + // Change the sample rate of G722 to 8000 to match SDP. + MaybeFixupG722(&voe_codec, 8000); // Skip uncompressed formats. if (IsCodec(voe_codec, kL16CodecName)) { continue; @@ -483,24 +362,181 @@ void WebRtcVoiceEngine::ConstructCodecs() { // TODO(hellner): Add ptime, sprop-stereo, and stereo // when they can be set to values other than the default. } - codecs_.push_back(codec); + result.push_back(codec); } else { LOG(LS_WARNING) << "Unexpected codec: " << ToString(voe_codec); } } + // Make sure they are in local preference order. + std::sort(result.begin(), result.end(), &AudioCodec::Preferable); + return result; } - // Make sure they are in local preference order. - std::sort(codecs_.begin(), codecs_.end(), &AudioCodec::Preferable); -} -bool WebRtcVoiceEngine::GetVoeCodec(int index, webrtc::CodecInst* codec) { - RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); - if (voe_wrapper_->codec()->GetCodec(index, *codec) == -1) { + static bool ToCodecInst(const AudioCodec& in, + webrtc::CodecInst* out) { + for (webrtc::CodecInst voe_codec : webrtc::acm2::RentACodec::Database()) { + // Change the sample rate of G722 to 8000 to match SDP. + MaybeFixupG722(&voe_codec, 8000); + AudioCodec codec(voe_codec.pltype, voe_codec.plname, voe_codec.plfreq, + voe_codec.rate, voe_codec.channels, 0); + bool multi_rate = IsCodecMultiRate(voe_codec); + // Allow arbitrary rates for ISAC to be specified. + if (multi_rate) { + // Set codec.bitrate to 0 so the check for codec.Matches() passes. + codec.bitrate = 0; + } + if (codec.Matches(in)) { + if (out) { + // Fixup the payload type. + voe_codec.pltype = in.id; + + // Set bitrate if specified. + if (multi_rate && in.bitrate != 0) { + voe_codec.rate = in.bitrate; + } + + // Reset G722 sample rate to 16000 to match WebRTC. + MaybeFixupG722(&voe_codec, 16000); + + // Apply codec-specific settings. + if (IsCodec(codec, kIsacCodecName)) { + // If ISAC and an explicit bitrate is not specified, + // enable auto bitrate adjustment. + voe_codec.rate = (in.bitrate > 0) ? in.bitrate : -1; + } + *out = voe_codec; + } + return true; + } + } return false; } - // Change the sample rate of G722 to 8000 to match SDP. - MaybeFixupG722(codec, 8000); - return true; + + static bool IsCodecMultiRate(const webrtc::CodecInst& codec) { + for (size_t i = 0; i < arraysize(kCodecPrefs); ++i) { + if (IsCodec(codec, kCodecPrefs[i].name) && + kCodecPrefs[i].clockrate == codec.plfreq) { + return kCodecPrefs[i].is_multi_rate; + } + } + return false; + } + + // If the AudioCodec param kCodecParamPTime is set, then we will set it to + // codec pacsize if it's valid, or we will pick the next smallest value we + // support. + // TODO(Brave): Query supported packet sizes from ACM when the API is ready. + static bool SetPTimeAsPacketSize(webrtc::CodecInst* codec, int ptime_ms) { + for (const CodecPref& codec_pref : kCodecPrefs) { + if ((IsCodec(*codec, codec_pref.name) && + codec_pref.clockrate == codec->plfreq) || + IsCodec(*codec, kG722CodecName)) { + int packet_size_ms = SelectPacketSize(codec_pref, ptime_ms); + if (packet_size_ms) { + // Convert unit from milli-seconds to samples. + codec->pacsize = (codec->plfreq / 1000) * packet_size_ms; + return true; + } + } + } + return false; + } + + private: + static const int kMaxNumPacketSize = 6; + struct CodecPref { + const char* name; + int clockrate; + int channels; + int payload_type; + bool is_multi_rate; + int packet_sizes_ms[kMaxNumPacketSize]; + }; + // Note: keep the supported packet sizes in ascending order. + static const CodecPref kCodecPrefs[12]; + + static int SelectPacketSize(const CodecPref& codec_pref, int ptime_ms) { + int selected_packet_size_ms = codec_pref.packet_sizes_ms[0]; + for (int packet_size_ms : codec_pref.packet_sizes_ms) { + if (packet_size_ms && packet_size_ms <= ptime_ms) { + selected_packet_size_ms = packet_size_ms; + } + } + return selected_packet_size_ms; + } + + // Changes RTP timestamp rate of G722. This is due to the "bug" in the RFC + // which says that G722 should be advertised as 8 kHz although it is a 16 kHz + // codec. + static void MaybeFixupG722(webrtc::CodecInst* voe_codec, int new_plfreq) { + if (IsCodec(*voe_codec, kG722CodecName)) { + // If the ASSERT triggers, the codec definition in WebRTC VoiceEngine + // has changed, and this special case is no longer needed. + RTC_DCHECK(voe_codec->plfreq != new_plfreq); + voe_codec->plfreq = new_plfreq; + } + } +}; + +const WebRtcVoiceCodecs::CodecPref WebRtcVoiceCodecs::kCodecPrefs[12] = { + { kOpusCodecName, 48000, 2, 111, true, { 10, 20, 40, 60 } }, + { kIsacCodecName, 16000, 1, 103, true, { 30, 60 } }, + { kIsacCodecName, 32000, 1, 104, true, { 30 } }, + // G722 should be advertised as 8000 Hz because of the RFC "bug". + { kG722CodecName, 8000, 1, 9, false, { 10, 20, 30, 40, 50, 60 } }, + { kIlbcCodecName, 8000, 1, 102, false, { 20, 30, 40, 60 } }, + { kPcmuCodecName, 8000, 1, 0, false, { 10, 20, 30, 40, 50, 60 } }, + { kPcmaCodecName, 8000, 1, 8, false, { 10, 20, 30, 40, 50, 60 } }, + { kCnCodecName, 32000, 1, 106, false, { } }, + { kCnCodecName, 16000, 1, 105, false, { } }, + { kCnCodecName, 8000, 1, 13, false, { } }, + { kRedCodecName, 8000, 1, 127, false, { } }, + { kDtmfCodecName, 8000, 1, 126, false, { } }, +}; +} // namespace { + +bool WebRtcVoiceEngine::ToCodecInst(const AudioCodec& in, + webrtc::CodecInst* out) { + return WebRtcVoiceCodecs::ToCodecInst(in, out); +} + +WebRtcVoiceEngine::WebRtcVoiceEngine() + : voe_wrapper_(new VoEWrapper()), + audio_state_(webrtc::AudioState::Create(MakeAudioStateConfig(voe()))) { + Construct(); +} + +WebRtcVoiceEngine::WebRtcVoiceEngine(VoEWrapper* voe_wrapper) + : voe_wrapper_(voe_wrapper) { + Construct(); +} + +void WebRtcVoiceEngine::Construct() { + RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); + LOG(LS_VERBOSE) << "WebRtcVoiceEngine::WebRtcVoiceEngine"; + + signal_thread_checker_.DetachFromThread(); + std::memset(&default_agc_config_, 0, sizeof(default_agc_config_)); + + webrtc::Trace::set_level_filter(kDefaultTraceFilter); + webrtc::Trace::SetTraceCallback(this); + + // Load our audio codec list. + codecs_ = WebRtcVoiceCodecs::SupportedCodecs(); + + // Load our RTP Header extensions. + rtp_header_extensions_.push_back( + RtpHeaderExtension(kRtpAudioLevelHeaderExtension, + kRtpAudioLevelHeaderExtensionDefaultId)); + rtp_header_extensions_.push_back( + RtpHeaderExtension(kRtpAbsoluteSenderTimeHeaderExtension, + kRtpAbsoluteSenderTimeHeaderExtensionDefaultId)); + if (webrtc::field_trial::FindFullName("WebRTC-SendSideBwe") == "Enabled") { + rtp_header_extensions_.push_back(RtpHeaderExtension( + kRtpTransportSequenceNumberHeaderExtension, + kRtpTransportSequenceNumberHeaderExtensionDefaultId)); + } + options_ = GetDefaultEngineOptions(); } WebRtcVoiceEngine::~WebRtcVoiceEngine() { @@ -539,10 +575,15 @@ bool WebRtcVoiceEngine::InitInternal() { webrtc::Trace::set_level_filter(kDefaultTraceFilter); // Log the VoiceEngine version info - char buffer[1024] = ""; - voe_wrapper_->base()->GetVersion(buffer); - LOG(LS_INFO) << "WebRtc VoiceEngine Version:"; - LogMultiline(rtc::LS_INFO, buffer); + { + char buffer[1024] = ""; + voe_wrapper_->base()->GetVersion(buffer); + LOG(LS_INFO) << "WebRtc VoiceEngine Version:"; + const char* delim = "\r\n"; + for (char* tok = strtok(buffer, delim); tok; tok = strtok(NULL, delim)) { + LOG(LS_INFO) << tok; + } + } // Save the default AGC configuration settings. This must happen before // calling SetOptions or the default will be overwritten. @@ -1056,55 +1097,6 @@ const std::vector& WebRtcVoiceEngine::codecs() { return codecs_; } -bool WebRtcVoiceEngine::FindCodec(const AudioCodec& in) { - RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); - return FindWebRtcCodec(in, NULL); -} - -// Get the VoiceEngine codec that matches |in|, with the supplied settings. -bool WebRtcVoiceEngine::FindWebRtcCodec(const AudioCodec& in, - webrtc::CodecInst* out) { - RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); - int ncodecs = voe_wrapper_->codec()->NumOfCodecs(); - for (int i = 0; i < ncodecs; ++i) { - webrtc::CodecInst voe_codec; - if (GetVoeCodec(i, &voe_codec)) { - AudioCodec codec(voe_codec.pltype, voe_codec.plname, voe_codec.plfreq, - voe_codec.rate, voe_codec.channels, 0); - bool multi_rate = IsCodecMultiRate(voe_codec); - // Allow arbitrary rates for ISAC to be specified. - if (multi_rate) { - // Set codec.bitrate to 0 so the check for codec.Matches() passes. - codec.bitrate = 0; - } - if (codec.Matches(in)) { - if (out) { - // Fixup the payload type. - voe_codec.pltype = in.id; - - // Set bitrate if specified. - if (multi_rate && in.bitrate != 0) { - voe_codec.rate = in.bitrate; - } - - // Reset G722 sample rate to 16000 to match WebRTC. - MaybeFixupG722(&voe_codec, 16000); - - // Apply codec-specific settings. - if (IsCodec(codec, kIsacCodecName)) { - // If ISAC and an explicit bitrate is not specified, - // enable auto bitrate adjustment. - voe_codec.rate = (in.bitrate > 0) ? in.bitrate : -1; - } - *out = voe_codec; - } - return true; - } - } - } - return false; -} - const std::vector& WebRtcVoiceEngine::rtp_header_extensions() const { RTC_DCHECK(signal_thread_checker_.CalledOnValidThread()); @@ -1592,7 +1584,26 @@ bool WebRtcVoiceMediaChannel::SetRecvCodecs( PausePlayout(); } - bool result = SetRecvCodecsInternal(new_codecs); + bool result = true; + for (const AudioCodec& codec : new_codecs) { + webrtc::CodecInst voe_codec; + 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; } @@ -1627,7 +1638,7 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( // Ignore codecs we don't know about. The negotiation step should prevent // this, but double-check to be sure. webrtc::CodecInst voe_codec; - if (!engine()->FindWebRtcCodec(codec, &voe_codec)) { + if (!WebRtcVoiceEngine::ToCodecInst(codec, &voe_codec)) { LOG(LS_WARNING) << "Unknown codec " << ToString(codec); continue; } @@ -1668,7 +1679,7 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( // Set packet size if the AudioCodec param kCodecParamPTime is set. int ptime_ms = 0; if (codec.GetParam(kCodecParamPTime, &ptime_ms)) { - if (!SetPTimeAsPacketSize(&send_codec, ptime_ms)) { + if (!WebRtcVoiceCodecs::SetPTimeAsPacketSize(&send_codec, ptime_ms)) { LOG(LS_WARNING) << "Failed to set packet size for codec " << send_codec.plname; return false; @@ -1746,7 +1757,7 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( // Ignore codecs we don't know about. The negotiation step should prevent // this, but double-check to be sure. webrtc::CodecInst voe_codec; - if (!engine()->FindWebRtcCodec(codec, &voe_codec)) { + if (!WebRtcVoiceEngine::ToCodecInst(codec, &voe_codec)) { LOG(LS_WARNING) << "Unknown codec " << ToString(codec); continue; } @@ -2116,24 +2127,20 @@ bool WebRtcVoiceMediaChannel::AddRecvStream(const StreamParams& sp) { } // Turn off all supported codecs. - const int ncodecs = engine()->voe()->codec()->NumOfCodecs(); - for (int i = 0; i < ncodecs; ++i) { - webrtc::CodecInst voe_codec; - if (engine()->voe()->codec()->GetCodec(i, voe_codec) != -1) { - voe_codec.pltype = -1; - if (engine()->voe()->codec()->SetRecPayloadType( - channel, voe_codec) == -1) { - LOG_RTCERR2(SetRecPayloadType, channel, ToString(voe_codec)); - DeleteVoEChannel(channel); - return false; - } + // TODO(solenberg): Remove once "no codecs" is the default state of a stream. + for (webrtc::CodecInst voe_codec : webrtc::acm2::RentACodec::Database()) { + voe_codec.pltype = -1; + if (engine()->voe()->codec()->SetRecPayloadType(channel, voe_codec) == -1) { + LOG_RTCERR2(SetRecPayloadType, channel, ToString(voe_codec)); + DeleteVoEChannel(channel); + return false; } } // Only enable those configured for this channel. for (const auto& codec : recv_codecs_) { webrtc::CodecInst voe_codec; - if (engine()->FindWebRtcCodec(codec, &voe_codec)) { + if (WebRtcVoiceEngine::ToCodecInst(codec, &voe_codec)) { voe_codec.pltype = codec.id; if (engine()->voe()->codec()->SetRecPayloadType( channel, voe_codec) == -1) { @@ -2486,7 +2493,7 @@ bool WebRtcVoiceMediaChannel::SetSendBitrateInternal(int bps) { return true; webrtc::CodecInst codec = *send_codec_; - bool is_multi_rate = IsCodecMultiRate(codec); + bool is_multi_rate = WebRtcVoiceCodecs::IsCodecMultiRate(codec); if (is_multi_rate) { // If codec is multi-rate then just set the bitrate. @@ -2635,7 +2642,7 @@ bool WebRtcVoiceMediaChannel::GetRedSendCodec(const AudioCodec& red_codec, if (codec.id == red_pt) { // If we find the right codec, that will be the codec we pass to // SetSendCodec, with the desired payload type. - if (engine()->FindWebRtcCodec(codec, send_codec)) { + if (WebRtcVoiceEngine::ToCodecInst(codec, send_codec)) { return true; } else { break; @@ -2659,30 +2666,6 @@ bool WebRtcVoiceMediaChannel::SetPlayout(int channel, bool playout) { } return true; } - -bool WebRtcVoiceMediaChannel::SetRecvCodecsInternal( - const std::vector& new_codecs) { - RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); - for (const AudioCodec& codec : new_codecs) { - webrtc::CodecInst voe_codec; - if (engine()->FindWebRtcCodec(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)); - return false; - } - } - } else { - LOG(LS_WARNING) << "Unknown codec " << ToString(codec); - return false; - } - } - return true; -} } // namespace cricket #endif // HAVE_WEBRTC_VOICE diff --git a/talk/media/webrtc/webrtcvoiceengine.h b/talk/media/webrtc/webrtcvoiceengine.h index 9c7f7bb525..57c9439efc 100644 --- a/talk/media/webrtc/webrtcvoiceengine.h +++ b/talk/media/webrtc/webrtcvoiceengine.h @@ -57,8 +57,10 @@ class WebRtcVoiceMediaChannel; // It uses the WebRtc VoiceEngine library for audio handling. class WebRtcVoiceEngine final : public webrtc::TraceCallback { friend class WebRtcVoiceMediaChannel; - public: + // Exposed for the WVoE/MC unit test. + static bool ToCodecInst(const AudioCodec& in, webrtc::CodecInst* out); + WebRtcVoiceEngine(); // Dependency injection for testing. explicit WebRtcVoiceEngine(VoEWrapper* voe_wrapper); @@ -78,9 +80,6 @@ class WebRtcVoiceEngine final : public webrtc::TraceCallback { int GetInputLevel(); const std::vector& codecs(); - bool FindCodec(const AudioCodec& codec); - bool FindWebRtcCodec(const AudioCodec& codec, webrtc::CodecInst* gcodec); - const std::vector& rtp_header_extensions() const; // For tracking WebRtc channels. Needed because we have to pause them @@ -114,8 +113,6 @@ class WebRtcVoiceEngine final : public webrtc::TraceCallback { private: void Construct(); - void ConstructCodecs(); - bool GetVoeCodec(int index, webrtc::CodecInst* codec); bool InitInternal(); // Every option that is "set" will be applied. Every option not "set" will be // ignored. This allows us to selectively turn on and off different options @@ -258,7 +255,6 @@ class WebRtcVoiceMediaChannel final : public VoiceMediaChannel, } bool SetSendCodecs(int channel, const std::vector& codecs); bool SetSendBitrateInternal(int bps); - bool SetRecvCodecsInternal(const std::vector& new_codecs); rtc::ThreadChecker worker_thread_checker_; diff --git a/talk/media/webrtc/webrtcvoiceengine_unittest.cc b/talk/media/webrtc/webrtcvoiceengine_unittest.cc index b52f6fc4fe..888de7d8a4 100644 --- a/talk/media/webrtc/webrtcvoiceengine_unittest.cc +++ b/talk/media/webrtc/webrtcvoiceengine_unittest.cc @@ -54,10 +54,6 @@ const cricket::AudioCodec kCn8000Codec(13, "CN", 8000, 0, 1, 0); const cricket::AudioCodec kCn16000Codec(105, "CN", 16000, 0, 1, 0); const cricket::AudioCodec kTelephoneEventCodec(106, "telephone-event", 8000, 0, 1, 0); -const cricket::AudioCodec* const kAudioCodecs[] = { - &kPcmuCodec, &kIsacCodec, &kOpusCodec, &kG722CodecVoE, &kRedCodec, - &kCn8000Codec, &kCn16000Codec, &kTelephoneEventCodec, -}; const uint32_t kSsrc1 = 0x99; const uint32_t kSsrc2 = 0x98; const uint32_t kSsrcs4[] = { 1, 2, 3, 4 }; @@ -81,7 +77,6 @@ class WebRtcVoiceEngineTestFake : public testing::Test { public: WebRtcVoiceEngineTestFake() : call_(webrtc::Call::Config()), - voe_(kAudioCodecs, arraysize(kAudioCodecs)), engine_(new FakeVoEWrapper(&voe_)), channel_(nullptr) { send_parameters_.codecs.push_back(kPcmuCodec); @@ -451,32 +446,33 @@ TEST_F(WebRtcVoiceEngineTestFake, FindCodec) { cricket::AudioCodec codec; webrtc::CodecInst codec_inst; // Find PCMU with explicit clockrate and bitrate. - EXPECT_TRUE(engine_.FindWebRtcCodec(kPcmuCodec, &codec_inst)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(kPcmuCodec, &codec_inst)); // Find ISAC with explicit clockrate and 0 bitrate. - EXPECT_TRUE(engine_.FindWebRtcCodec(kIsacCodec, &codec_inst)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(kIsacCodec, &codec_inst)); // Find telephone-event with explicit clockrate and 0 bitrate. - EXPECT_TRUE(engine_.FindWebRtcCodec(kTelephoneEventCodec, &codec_inst)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(kTelephoneEventCodec, + &codec_inst)); // Find ISAC with a different payload id. codec = kIsacCodec; codec.id = 127; - EXPECT_TRUE(engine_.FindWebRtcCodec(codec, &codec_inst)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(codec, &codec_inst)); EXPECT_EQ(codec.id, codec_inst.pltype); // Find PCMU with a 0 clockrate. codec = kPcmuCodec; codec.clockrate = 0; - EXPECT_TRUE(engine_.FindWebRtcCodec(codec, &codec_inst)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(codec, &codec_inst)); EXPECT_EQ(codec.id, codec_inst.pltype); EXPECT_EQ(8000, codec_inst.plfreq); // Find PCMU with a 0 bitrate. codec = kPcmuCodec; codec.bitrate = 0; - EXPECT_TRUE(engine_.FindWebRtcCodec(codec, &codec_inst)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(codec, &codec_inst)); EXPECT_EQ(codec.id, codec_inst.pltype); EXPECT_EQ(64000, codec_inst.rate); // Find ISAC with an explicit bitrate. codec = kIsacCodec; codec.bitrate = 32000; - EXPECT_TRUE(engine_.FindWebRtcCodec(codec, &codec_inst)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(codec, &codec_inst)); EXPECT_EQ(codec.id, codec_inst.pltype); EXPECT_EQ(32000, codec_inst.rate); } @@ -539,7 +535,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetRecvCodecsWithOpusNoStereo) { cricket::StreamParams::CreateLegacy(kSsrc1))); int channel_num = voe_.GetLastChannel(); webrtc::CodecInst opus; - engine_.FindWebRtcCodec(kOpusCodec, &opus); + cricket::WebRtcVoiceEngine::ToCodecInst(kOpusCodec, &opus); // Even without stereo parameters, recv codecs still specify channels = 2. EXPECT_EQ(2, opus.channels); EXPECT_EQ(111, opus.pltype); @@ -562,7 +558,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetRecvCodecsWithOpus0Stereo) { cricket::StreamParams::CreateLegacy(kSsrc1))); int channel_num2 = voe_.GetLastChannel(); webrtc::CodecInst opus; - engine_.FindWebRtcCodec(kOpusCodec, &opus); + cricket::WebRtcVoiceEngine::ToCodecInst(kOpusCodec, &opus); // Even when stereo is off, recv codecs still specify channels = 2. EXPECT_EQ(2, opus.channels); EXPECT_EQ(111, opus.pltype); @@ -585,7 +581,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetRecvCodecsWithOpus1Stereo) { cricket::StreamParams::CreateLegacy(kSsrc1))); int channel_num2 = voe_.GetLastChannel(); webrtc::CodecInst opus; - engine_.FindWebRtcCodec(kOpusCodec, &opus); + cricket::WebRtcVoiceEngine::ToCodecInst(kOpusCodec, &opus); EXPECT_EQ(2, opus.channels); EXPECT_EQ(111, opus.pltype); EXPECT_STREQ("opus", opus.plname); @@ -670,7 +666,7 @@ TEST_F(WebRtcVoiceEngineTestFake, AddRecvCodecsWhilePlaying) { int channel_num = voe_.GetLastChannel(); EXPECT_TRUE(voe_.GetPlayout(channel_num)); webrtc::CodecInst gcodec; - EXPECT_TRUE(engine_.FindWebRtcCodec(kOpusCodec, &gcodec)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(kOpusCodec, &gcodec)); EXPECT_EQ(kOpusCodec.id, gcodec.pltype); } @@ -3246,54 +3242,60 @@ TEST(WebRtcVoiceEngineTest, StartupShutdown) { // Tests that the library is configured with the codecs we want. TEST(WebRtcVoiceEngineTest, HasCorrectCodecs) { - cricket::WebRtcVoiceEngine engine; // Check codecs by name. - EXPECT_TRUE(engine.FindCodec( - cricket::AudioCodec(96, "OPUS", 48000, 0, 2, 0))); - EXPECT_TRUE(engine.FindCodec( - cricket::AudioCodec(96, "ISAC", 16000, 0, 1, 0))); - EXPECT_TRUE(engine.FindCodec( - cricket::AudioCodec(96, "ISAC", 32000, 0, 1, 0))); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(96, "OPUS", 48000, 0, 2, 0), nullptr)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(96, "ISAC", 16000, 0, 1, 0), nullptr)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(96, "ISAC", 32000, 0, 1, 0), nullptr)); // Check that name matching is case-insensitive. - EXPECT_TRUE(engine.FindCodec( - cricket::AudioCodec(96, "ILBC", 8000, 0, 1, 0))); - EXPECT_TRUE(engine.FindCodec( - cricket::AudioCodec(96, "iLBC", 8000, 0, 1, 0))); - EXPECT_TRUE(engine.FindCodec( - cricket::AudioCodec(96, "PCMU", 8000, 0, 1, 0))); - EXPECT_TRUE(engine.FindCodec( - cricket::AudioCodec(96, "PCMA", 8000, 0, 1, 0))); - EXPECT_TRUE(engine.FindCodec( - cricket::AudioCodec(96, "G722", 8000, 0, 1, 0))); - EXPECT_TRUE(engine.FindCodec( - cricket::AudioCodec(96, "red", 8000, 0, 1, 0))); - EXPECT_TRUE(engine.FindCodec( - cricket::AudioCodec(96, "CN", 32000, 0, 1, 0))); - EXPECT_TRUE(engine.FindCodec( - cricket::AudioCodec(96, "CN", 16000, 0, 1, 0))); - EXPECT_TRUE(engine.FindCodec( - cricket::AudioCodec(96, "CN", 8000, 0, 1, 0))); - EXPECT_TRUE(engine.FindCodec( - cricket::AudioCodec(96, "telephone-event", 8000, 0, 1, 0))); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(96, "ILBC", 8000, 0, 1, 0), nullptr)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(96, "iLBC", 8000, 0, 1, 0), nullptr)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(96, "PCMU", 8000, 0, 1, 0), nullptr)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(96, "PCMA", 8000, 0, 1, 0), nullptr)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(96, "G722", 8000, 0, 1, 0), nullptr)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(96, "red", 8000, 0, 1, 0), nullptr)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(96, "CN", 32000, 0, 1, 0), nullptr)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(96, "CN", 16000, 0, 1, 0), nullptr)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(96, "CN", 8000, 0, 1, 0), nullptr)); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(96, "telephone-event", 8000, 0, 1, 0), nullptr)); // Check codecs with an id by id. - EXPECT_TRUE(engine.FindCodec( - cricket::AudioCodec(0, "", 8000, 0, 1, 0))); // PCMU - EXPECT_TRUE(engine.FindCodec( - cricket::AudioCodec(8, "", 8000, 0, 1, 0))); // PCMA - EXPECT_TRUE(engine.FindCodec( - cricket::AudioCodec(9, "", 8000, 0, 1, 0))); // G722 - EXPECT_TRUE(engine.FindCodec( - cricket::AudioCodec(13, "", 8000, 0, 1, 0))); // CN + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(0, "", 8000, 0, 1, 0), nullptr)); // PCMU + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(8, "", 8000, 0, 1, 0), nullptr)); // PCMA + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(9, "", 8000, 0, 1, 0), nullptr)); // G722 + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(13, "", 8000, 0, 1, 0), nullptr)); // CN // Check sample/bitrate matching. - EXPECT_TRUE(engine.FindCodec( - cricket::AudioCodec(0, "PCMU", 8000, 64000, 1, 0))); + EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(0, "PCMU", 8000, 64000, 1, 0), nullptr)); // Check that bad codecs fail. - EXPECT_FALSE(engine.FindCodec(cricket::AudioCodec(99, "ABCD", 0, 0, 1, 0))); - EXPECT_FALSE(engine.FindCodec(cricket::AudioCodec(88, "", 0, 0, 1, 0))); - EXPECT_FALSE(engine.FindCodec(cricket::AudioCodec(0, "", 0, 0, 2, 0))); - EXPECT_FALSE(engine.FindCodec(cricket::AudioCodec(0, "", 5000, 0, 1, 0))); - EXPECT_FALSE(engine.FindCodec(cricket::AudioCodec(0, "", 0, 5000, 1, 0))); + EXPECT_FALSE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(99, "ABCD", 0, 0, 1, 0), nullptr)); + EXPECT_FALSE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(88, "", 0, 0, 1, 0), nullptr)); + EXPECT_FALSE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(0, "", 0, 0, 2, 0), nullptr)); + EXPECT_FALSE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(0, "", 5000, 0, 1, 0), nullptr)); + EXPECT_FALSE(cricket::WebRtcVoiceEngine::ToCodecInst( + cricket::AudioCodec(0, "", 0, 5000, 1, 0), nullptr)); + // Verify the payload id of common audio codecs, including CN, ISAC, and G722. + cricket::WebRtcVoiceEngine engine; for (std::vector::const_iterator it = engine.codecs().begin(); it != engine.codecs().end(); ++it) { if (it->name == "CN" && it->clockrate == 16000) { @@ -3320,7 +3322,6 @@ TEST(WebRtcVoiceEngineTest, HasCorrectCodecs) { EXPECT_EQ("1", it->params.find("useinbandfec")->second); } } - engine.Terminate(); }