diff --git a/talk/app/webrtc/webrtcsdp.cc b/talk/app/webrtc/webrtcsdp.cc index be1fe47ca..72a72bf43 100644 --- a/talk/app/webrtc/webrtcsdp.cc +++ b/talk/app/webrtc/webrtcsdp.cc @@ -69,6 +69,7 @@ using cricket::kCodecParamSPropStereo; using cricket::kCodecParamStartBitrate; using cricket::kCodecParamStereo; using cricket::kCodecParamUseInbandFec; +using cricket::kCodecParamUseDtx; using cricket::kCodecParamSctpProtocol; using cricket::kCodecParamSctpStreams; using cricket::kCodecParamMaxAverageBitrate; @@ -1560,9 +1561,9 @@ void WriteFmtpParameters(const cricket::CodecParameterMap& parameters, bool IsFmtpParam(const std::string& name) { const char* kFmtpParams[] = { kCodecParamMinPTime, kCodecParamSPropStereo, - kCodecParamStereo, kCodecParamUseInbandFec, kCodecParamStartBitrate, - kCodecParamMaxBitrate, kCodecParamMinBitrate, kCodecParamMaxQuantization, - kCodecParamSctpProtocol, kCodecParamSctpStreams, + kCodecParamStereo, kCodecParamUseInbandFec, kCodecParamUseDtx, + kCodecParamStartBitrate, kCodecParamMaxBitrate, kCodecParamMinBitrate, + kCodecParamMaxQuantization, kCodecParamSctpProtocol, kCodecParamSctpStreams, kCodecParamMaxAverageBitrate, kCodecParamMaxPlaybackRate, kCodecParamAssociatedPayloadType }; diff --git a/talk/media/base/constants.cc b/talk/media/base/constants.cc index 4d41addeb..6712a3f72 100644 --- a/talk/media/base/constants.cc +++ b/talk/media/base/constants.cc @@ -62,6 +62,7 @@ const char kCodecParamMinPTime[] = "minptime"; const char kCodecParamSPropStereo[] = "sprop-stereo"; const char kCodecParamStereo[] = "stereo"; const char kCodecParamUseInbandFec[] = "useinbandfec"; +const char kCodecParamUseDtx[] = "usedtx"; const char kCodecParamMaxAverageBitrate[] = "maxaveragebitrate"; const char kCodecParamMaxPlaybackRate[] = "maxplaybackrate"; @@ -77,6 +78,7 @@ const int kOpusDefaultMinPTime = 3; const int kOpusDefaultSPropStereo = 0; const int kOpusDefaultStereo = 0; const int kOpusDefaultUseInbandFec = 0; +const int kOpusDefaultUseDtx = 0; const int kOpusDefaultMaxPlaybackRate = 48000; const int kPreferredMaxPTime = 60; diff --git a/talk/media/base/constants.h b/talk/media/base/constants.h index 93198fa2a..a67c6679f 100644 --- a/talk/media/base/constants.h +++ b/talk/media/base/constants.h @@ -68,6 +68,7 @@ extern const char kCodecParamMinPTime[]; extern const char kCodecParamSPropStereo[]; extern const char kCodecParamStereo[]; extern const char kCodecParamUseInbandFec[]; +extern const char kCodecParamUseDtx[]; extern const char kCodecParamMaxAverageBitrate[]; extern const char kCodecParamMaxPlaybackRate[]; extern const char kCodecParamSctpProtocol[]; @@ -87,6 +88,7 @@ extern const int kOpusDefaultMinPTime; extern const int kOpusDefaultSPropStereo; extern const int kOpusDefaultStereo; extern const int kOpusDefaultUseInbandFec; +extern const int kOpusDefaultUseDtx; extern const int kOpusDefaultMaxPlaybackRate; // Prefered values in this code base. Note that they may differ from the default diff --git a/talk/media/webrtc/fakewebrtcvoiceengine.h b/talk/media/webrtc/fakewebrtcvoiceengine.h index 960bad310..c394d0cf7 100644 --- a/talk/media/webrtc/fakewebrtcvoiceengine.h +++ b/talk/media/webrtc/fakewebrtcvoiceengine.h @@ -314,6 +314,9 @@ class FakeWebRtcVoiceEngine bool GetVAD(int channel) { return channels_[channel]->vad; } + bool GetOpusDtx(int channel) { + return channels_[channel]->opus_dtx; + } bool GetRED(int channel) { return channels_[channel]->red; } diff --git a/talk/media/webrtc/webrtcvoiceengine.cc b/talk/media/webrtc/webrtcvoiceengine.cc index f20b5e154..16e0ee811 100644 --- a/talk/media/webrtc/webrtcvoiceengine.cc +++ b/talk/media/webrtc/webrtcvoiceengine.cc @@ -79,7 +79,6 @@ static const CodecPref kCodecPrefs[] = { { 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, 48000, 1, 107, false, { } }, { kCnCodecName, 32000, 1, 106, false, { } }, { kCnCodecName, 16000, 1, 105, false, { } }, { kCnCodecName, 8000, 1, 13, false, { } }, @@ -161,6 +160,7 @@ static std::string ToString(const AudioCodec& codec) { << " (" << codec.id << ")"; return ss.str(); } + static std::string ToString(const webrtc::CodecInst& codec) { std::stringstream ss; ss << codec.plname << "/" << codec.plfreq << "/" << codec.channels @@ -191,9 +191,17 @@ static int SeverityToFilter(int severity) { return filter; } +static bool IsCodec(const AudioCodec& codec, const char* ref_name) { + return (_stricmp(codec.name.c_str(), ref_name) == 0); +} + +static bool IsCodec(const webrtc::CodecInst& codec, const char* ref_name) { + return (_stricmp(codec.plname, ref_name) == 0); +} + static bool IsCodecMultiRate(const webrtc::CodecInst& codec) { for (size_t i = 0; i < ARRAY_SIZE(kCodecPrefs); ++i) { - if (_stricmp(kCodecPrefs[i].name, codec.plname) == 0 && + if (IsCodec(codec, kCodecPrefs[i].name) && kCodecPrefs[i].clockrate == codec.plfreq) { return kCodecPrefs[i].is_multi_rate; } @@ -201,18 +209,6 @@ static bool IsCodecMultiRate(const webrtc::CodecInst& codec) { return false; } -static bool IsTelephoneEventCodec(const std::string& name) { - return _stricmp(name.c_str(), "telephone-event") == 0; -} - -static bool IsCNCodec(const std::string& name) { - return _stricmp(name.c_str(), "CN") == 0; -} - -static bool IsRedCodec(const std::string& name) { - return _stricmp(name.c_str(), "red") == 0; -} - static bool FindCodec(const std::vector& codecs, const AudioCodec& codec, AudioCodec* found_codec) { @@ -248,9 +244,9 @@ static int SelectPacketSize(const CodecPref& codec_pref, int ptime_ms) { // 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 ((_stricmp(codec_pref.name, codec->plname) == 0 && + if ((IsCodec(*codec, codec_pref.name) && codec_pref.clockrate == codec->plfreq) || - _stricmp(codec_pref.name, kG722CodecName) == 0) { + IsCodec(*codec, kG722CodecName)) { int packet_size_ms = SelectPacketSize(codec_pref, ptime_ms); if (packet_size_ms) { // Convert unit from milli-seconds to samples. @@ -262,6 +258,87 @@ static bool SetPTimeAsPacketSize(webrtc::CodecInst* codec, int ptime_ms) { return false; } +// Return true if codec.params[feature] == "1", false otherwise. +static bool IsCodecFeatureEnabled(const AudioCodec& codec, + const char* feature) { + int value; + return codec.GetParam(feature, &value) && value == 1; +} + +// Use params[kCodecParamMaxAverageBitrate] if it is defined, use codec.bitrate +// otherwise. If the value (either from params or codec.bitrate) <=0, use the +// default configuration. If the value is beyond feasible bit rate of Opus, +// clamp it. Returns the Opus bit rate for operation. +static int GetOpusBitrate(const AudioCodec& codec, int max_playback_rate) { + int bitrate = 0; + bool use_param = true; + if (!codec.GetParam(kCodecParamMaxAverageBitrate, &bitrate)) { + bitrate = codec.bitrate; + use_param = false; + } + if (bitrate <= 0) { + if (max_playback_rate <= 8000) { + bitrate = kOpusBitrateNb; + } else if (max_playback_rate <= 16000) { + bitrate = kOpusBitrateWb; + } else { + bitrate = kOpusBitrateFb; + } + + if (IsCodecFeatureEnabled(codec, kCodecParamStereo)) { + bitrate *= 2; + } + } else if (bitrate < kOpusMinBitrate || bitrate > kOpusMaxBitrate) { + bitrate = (bitrate < kOpusMinBitrate) ? kOpusMinBitrate : kOpusMaxBitrate; + std::string rate_source = + use_param ? "Codec parameter \"maxaveragebitrate\"" : + "Supplied Opus bitrate"; + LOG(LS_WARNING) << rate_source + << " is invalid and is replaced by: " + << bitrate; + } + return bitrate; +} + +// Returns kOpusDefaultPlaybackRate if params[kCodecParamMaxPlaybackRate] is not +// defined. Returns the value of params[kCodecParamMaxPlaybackRate] otherwise. +static int GetOpusMaxPlaybackRate(const AudioCodec& codec) { + int value; + if (codec.GetParam(kCodecParamMaxPlaybackRate, &value)) { + return value; + } + return kOpusDefaultMaxPlaybackRate; +} + +static void GetOpusConfig(const AudioCodec& codec, webrtc::CodecInst* voe_codec, + bool* enable_codec_fec, int* max_playback_rate, + bool* enable_codec_dtx) { + *enable_codec_fec = IsCodecFeatureEnabled(codec, kCodecParamUseInbandFec); + *enable_codec_dtx = IsCodecFeatureEnabled(codec, kCodecParamUseDtx); + *max_playback_rate = GetOpusMaxPlaybackRate(codec); + + // If OPUS, change what we send according to the "stereo" codec + // parameter, and not the "channels" parameter. We set + // voe_codec.channels to 2 if "stereo=1" and 1 otherwise. If + // the bitrate is not specified, i.e. is <= zero, we set it to the + // appropriate default value for mono or stereo Opus. + + voe_codec->channels = IsCodecFeatureEnabled(codec, kCodecParamStereo) ? 2 : 1; + 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. +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. + ASSERT(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. @@ -283,100 +360,8 @@ static AudioOptions GetDefaultEngineOptions() { return options; } -static bool IsOpus(const AudioCodec& codec) { - return (_stricmp(codec.name.c_str(), kOpusCodecName) == 0); -} - -static bool IsIsac(const AudioCodec& codec) { - return (_stricmp(codec.name.c_str(), kIsacCodecName) == 0); -} - -// True if params["stereo"] == "1" -static bool IsOpusStereoEnabled(const AudioCodec& codec) { - int value; - return codec.GetParam(kCodecParamStereo, &value) && value == 1; -} - -// Use params[kCodecParamMaxAverageBitrate] if it is defined, use codec.bitrate -// otherwise. If the value (either from params or codec.bitrate) <=0, use the -// default configuration. If the value is beyond feasible bit rate of Opus, -// clamp it. Returns the Opus bit rate for operation. -static int GetOpusBitrate(const AudioCodec& codec, int max_playback_rate) { - int bitrate = 0; - bool use_param = true; - if (!codec.GetParam(kCodecParamMaxAverageBitrate, &bitrate)) { - bitrate = codec.bitrate; - use_param = false; - } - if (bitrate <= 0) { - if (max_playback_rate <= 8000) { - bitrate = kOpusBitrateNb; - } - else if (max_playback_rate <= 16000) { - bitrate = kOpusBitrateWb; - } - else { - bitrate = kOpusBitrateFb; - } - - if (IsOpusStereoEnabled(codec)) { - bitrate *= 2; - } - } - else if (bitrate < kOpusMinBitrate || bitrate > kOpusMaxBitrate) { - bitrate = (bitrate < kOpusMinBitrate) ? kOpusMinBitrate : kOpusMaxBitrate; - std::string rate_source = - use_param ? "Codec parameter \"maxaveragebitrate\"" : - "Supplied Opus bitrate"; - LOG(LS_WARNING) << rate_source - << " is invalid and is replaced by: " - << bitrate; - } - return bitrate; -} - -// Return true if params[kCodecParamUseInbandFec] == "1", false -// otherwise. -static bool IsOpusFecEnabled(const AudioCodec& codec) { - int value; - return codec.GetParam(kCodecParamUseInbandFec, &value) && value == 1; -} - -// Returns kOpusDefaultPlaybackRate if params[kCodecParamMaxPlaybackRate] is not -// defined. Returns the value of params[kCodecParamMaxPlaybackRate] otherwise. -static int GetOpusMaxPlaybackRate(const AudioCodec& codec) { - int value; - if (codec.GetParam(kCodecParamMaxPlaybackRate, &value)) { - return value; - } - return kOpusDefaultMaxPlaybackRate; -} - -static void GetOpusConfig(const AudioCodec& codec, webrtc::CodecInst* voe_codec, - bool* enable_codec_fec, int* max_playback_rate) { - *enable_codec_fec = IsOpusFecEnabled(codec); - *max_playback_rate = GetOpusMaxPlaybackRate(codec); - - // If OPUS, change what we send according to the "stereo" codec - // parameter, and not the "channels" parameter. We set - // voe_codec.channels to 2 if "stereo=1" and 1 otherwise. If - // the bitrate is not specified, i.e. is <= zero, we set it to the - // appropriate default value for mono or stereo Opus. - - voe_codec->channels = IsOpusStereoEnabled(codec) ? 2 : 1; - 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. -static void MaybeFixupG722(webrtc::CodecInst* voe_codec, int new_plfreq) { - if (_stricmp(voe_codec->plname, kG722CodecName) == 0) { - // If the ASSERT triggers, the codec definition in WebRTC VoiceEngine - // has changed, and this special case is no longer needed. - ASSERT(voe_codec->plfreq != new_plfreq); - voe_codec->plfreq = new_plfreq; - } +static std::string GetEnableString(bool enable) { + return enable ? "enable" : "disable"; } class WebRtcSoundclipMedia : public SoundclipMedia { @@ -535,13 +520,13 @@ void WebRtcVoiceEngine::ConstructCodecs() { webrtc::CodecInst voe_codec; if (GetVoeCodec(i, &voe_codec)) { // Skip uncompressed formats. - if (_stricmp(voe_codec.plname, kL16CodecName) == 0) { + if (IsCodec(voe_codec, kL16CodecName)) { continue; } const CodecPref* pref = NULL; for (size_t j = 0; j < ARRAY_SIZE(kCodecPrefs); ++j) { - if (_stricmp(kCodecPrefs[j].name, voe_codec.plname) == 0 && + if (IsCodec(voe_codec, kCodecPrefs[j].name) && kCodecPrefs[j].clockrate == voe_codec.plfreq && kCodecPrefs[j].channels == voe_codec.channels) { pref = &kCodecPrefs[j]; @@ -556,11 +541,11 @@ void WebRtcVoiceEngine::ConstructCodecs() { voe_codec.rate, voe_codec.channels, ARRAY_SIZE(kCodecPrefs) - (pref - kCodecPrefs)); LOG(LS_INFO) << ToString(codec); - if (IsIsac(codec)) { + if (IsCodec(codec, kIsacCodecName)) { // Indicate auto-bitrate in signaling. codec.bitrate = 0; } - if (IsOpus(codec)) { + if (IsCodec(codec, kOpusCodecName)) { // Only add fmtp parameters that differ from the spec. if (kPreferredMinPTime != kOpusDefaultMinPTime) { codec.params[kCodecParamMinPTime] = @@ -1323,7 +1308,7 @@ bool WebRtcVoiceEngine::FindWebRtcCodec(const AudioCodec& in, MaybeFixupG722(&voe_codec, 16000); // Apply codec-specific settings. - if (IsIsac(codec)) { + 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; @@ -2120,7 +2105,7 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( bool nack_enabled = nack_enabled_; bool enable_codec_fec = false; - + bool enable_opus_dtx = false; int opus_max_playback_rate = 0; // Set send codec (the first non-telephone-event/CN codec) @@ -2134,7 +2119,7 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( continue; } - if (IsTelephoneEventCodec(it->name) || IsCNCodec(it->name)) { + if (IsCodec(*it, kDtmfCodecName) || IsCodec(*it, kCnCodecName)) { // Skip telephone-event/CN codec, which will be handled later. continue; } @@ -2143,7 +2128,7 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( // Be sure to use the payload type requested by the remote side. // "red", for RED audio, is a special case where the actual codec to be // used is specified in params. - if (IsRedCodec(it->name)) { + if (IsCodec(*it, kRedCodecName)) { // Parse out the RED parameters. If we fail, just ignore RED; // we don't support all possible params/usage scenarios. if (!GetRedSendCodec(*it, codecs, &send_codec)) { @@ -2160,11 +2145,11 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( } else { send_codec = voe_codec; nack_enabled = IsNackEnabled(*it); - // For Opus as the send codec, we are to enable inband FEC if requested - // and set maximum playback rate. - if (IsOpus(*it)) { + // For Opus as the send codec, we are to determine inband FEC, maximum + // playback rate, and opus internal dtx. + if (IsCodec(*it, kOpusCodecName)) { GetOpusConfig(*it, &send_codec, &enable_codec_fec, - &opus_max_playback_rate); + &opus_max_playback_rate, &enable_opus_dtx); } // Set packet size if the AudioCodec param kCodecParamPTime is set. @@ -2207,17 +2192,32 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( } } - // maxplaybackrate should be set after SetSendCodec. - // If opus_max_playback_rate <= 0, the default maximum playback rate of 48 kHz - // will be used. - if (opus_max_playback_rate > 0) { - LOG(LS_INFO) << "Attempt to set maximum playback rate to " - << opus_max_playback_rate - << " Hz on channel " + if (IsCodec(send_codec, kOpusCodecName)) { + // DTX and maxplaybackrate should be set after SetSendCodec. Because current + // send codec has to be Opus. + + // Set Opus internal DTX. + LOG(LS_INFO) << "Attempt to " + << GetEnableString(enable_opus_dtx) + << " Opus DTX on channel " << channel; - if (engine()->voe()->codec()->SetOpusMaxPlaybackRate( - channel, opus_max_playback_rate) == -1) { - LOG(LS_WARNING) << "Could not set maximum playback rate."; + if (engine()->voe()->codec()->SetOpusDtx(channel, enable_opus_dtx)) { + LOG_RTCERR2(SetOpusDtx, channel, enable_opus_dtx); + return false; + } + + // If opus_max_playback_rate <= 0, the default maximum playback rate + // (48 kHz) will be used. + if (opus_max_playback_rate > 0) { + LOG(LS_INFO) << "Attempt to set maximum playback rate to " + << opus_max_playback_rate + << " Hz on channel " + << channel; + if (engine()->voe()->codec()->SetOpusMaxPlaybackRate( + channel, opus_max_playback_rate) == -1) { + LOG_RTCERR2(SetOpusMaxPlaybackRate, channel, opus_max_playback_rate); + return false; + } } } @@ -2241,13 +2241,13 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( // Find the DTMF telephone event "codec" and tell VoiceEngine channels // about it. - if (IsTelephoneEventCodec(it->name)) { + if (IsCodec(*it, kDtmfCodecName)) { if (engine()->voe()->dtmf()->SetSendTelephoneEventPayloadType( channel, it->id) == -1) { LOG_RTCERR2(SetSendTelephoneEventPayloadType, channel, it->id); return false; } - } else if (IsCNCodec(it->name)) { + } else if (IsCodec(*it, kCnCodecName)) { // Turn voice activity detection/comfort noise on if supported. // Set the wideband CN payload type appropriately. // (narrowband always uses the static payload type 13). @@ -2285,7 +2285,9 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( } // Only turn on VAD if we have a CN payload type that matches the // clockrate for the codec we are going to use. - if (it->clockrate == send_codec.plfreq) { + if (it->clockrate == send_codec.plfreq && send_codec.channels != 2) { + // TODO(minyue): If CN frequency == 48000 Hz is allowed, consider the + // interaction between VAD and Opus FEC. LOG(LS_INFO) << "Enabling VAD"; if (engine()->voe()->codec()->SetVADStatus(channel, true) == -1) { LOG_RTCERR2(SetVADStatus, channel, true); @@ -2303,8 +2305,7 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( for (std::vector::const_iterator it = codecs.begin(); it != codecs.end(); ++it) { // Find the DTMF telephone event "codec". - if (_stricmp(it->name.c_str(), "telephone-event") == 0 || - _stricmp(it->name.c_str(), "audio/telephone-event") == 0) { + if (IsCodec(*it, kDtmfCodecName)) { dtmf_allowed_ = true; } } diff --git a/talk/media/webrtc/webrtcvoiceengine_unittest.cc b/talk/media/webrtc/webrtcvoiceengine_unittest.cc index 59388bc51..789502377 100644 --- a/talk/media/webrtc/webrtcvoiceengine_unittest.cc +++ b/talk/media/webrtc/webrtcvoiceengine_unittest.cc @@ -1402,6 +1402,53 @@ TEST_F(WebRtcVoiceEngineTestFake, SetOpusMaxPlaybackRateOnTwoStreams) { voe_.GetMaxEncodingBandwidth(channel_num)); } +// Test that with usedtx=0, Opus DTX is off. +TEST_F(WebRtcVoiceEngineTestFake, DisableOpusDtxOnOpus) { + EXPECT_TRUE(SetupEngine()); + int channel_num = voe_.GetLastChannel(); + std::vector codecs; + codecs.push_back(kOpusCodec); + codecs[0].params["usedtx"] = "0"; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_FALSE(voe_.GetOpusDtx(channel_num)); +} + +// Test that with usedtx=1, Opus DTX is on. +TEST_F(WebRtcVoiceEngineTestFake, EnableOpusDtxOnOpus) { + EXPECT_TRUE(SetupEngine()); + int channel_num = voe_.GetLastChannel(); + std::vector codecs; + codecs.push_back(kOpusCodec); + codecs[0].params["usedtx"] = "1"; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_TRUE(voe_.GetOpusDtx(channel_num)); + EXPECT_FALSE(voe_.GetVAD(channel_num)); // Opus DTX should not affect VAD. +} + +// Test that usedtx=1 works with stereo Opus. +TEST_F(WebRtcVoiceEngineTestFake, EnableOpusDtxOnOpusStereo) { + EXPECT_TRUE(SetupEngine()); + int channel_num = voe_.GetLastChannel(); + std::vector codecs; + codecs.push_back(kOpusCodec); + codecs[0].params["usedtx"] = "1"; + codecs[0].params["stereo"] = "1"; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_TRUE(voe_.GetOpusDtx(channel_num)); + EXPECT_FALSE(voe_.GetVAD(channel_num)); // Opus DTX should not affect VAD. +} + +// Test that usedtx=1 does not work with non Opus. +TEST_F(WebRtcVoiceEngineTestFake, CannotEnableOpusDtxOnNonOpus) { + EXPECT_TRUE(SetupEngine()); + int channel_num = voe_.GetLastChannel(); + std::vector codecs; + codecs.push_back(kIsacCodec); + codecs[0].params["usedtx"] = "1"; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_FALSE(voe_.GetOpusDtx(channel_num)); +} + // Test that we can switch back and forth between Opus and ISAC with CN. TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsIsacOpusSwitching) { EXPECT_TRUE(SetupEngine());