Adding "usedtx" as Opus codec parameter.
This is according to https://tools.ietf.org/html/draft-spittka-payload-rtp-opus-03
Specifically,
usedtx: specifies if the decoder prefers the use of DTX. values are 1 and 0. If no value is specified, usedtx is assumed to be 0.
BUG=1014
R=juberti@webrtc.org, tina.legrand@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/48499004
Cr-Commit-Position: refs/heads/master@{#8872}
diff --git a/talk/app/webrtc/webrtcsdp.cc b/talk/app/webrtc/webrtcsdp.cc
index be1fe47..72a72bf 100644
--- a/talk/app/webrtc/webrtcsdp.cc
+++ b/talk/app/webrtc/webrtcsdp.cc
@@ -69,6 +69,7 @@
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 @@
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 4d41add..6712a3f 100644
--- a/talk/media/base/constants.cc
+++ b/talk/media/base/constants.cc
@@ -62,6 +62,7 @@
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 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 93198fa..a67c667 100644
--- a/talk/media/base/constants.h
+++ b/talk/media/base/constants.h
@@ -68,6 +68,7 @@
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 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 960bad3..c394d0c 100644
--- a/talk/media/webrtc/fakewebrtcvoiceengine.h
+++ b/talk/media/webrtc/fakewebrtcvoiceengine.h
@@ -314,6 +314,9 @@
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 f20b5e1..16e0ee8 100644
--- a/talk/media/webrtc/webrtcvoiceengine.cc
+++ b/talk/media/webrtc/webrtcvoiceengine.cc
@@ -79,7 +79,6 @@
{ 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 @@
<< " (" << 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 @@
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 @@
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<AudioCodec>& codecs,
const AudioCodec& codec,
AudioCodec* found_codec) {
@@ -248,9 +244,9 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
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 @@
// 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 @@
} 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 @@
}
}
- // 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 @@
// 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 @@
}
// 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 @@
for (std::vector<AudioCodec>::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 59388bc..7895023 100644
--- a/talk/media/webrtc/webrtcvoiceengine_unittest.cc
+++ b/talk/media/webrtc/webrtcvoiceengine_unittest.cc
@@ -1402,6 +1402,53 @@
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<cricket::AudioCodec> 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<cricket::AudioCodec> 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<cricket::AudioCodec> 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<cricket::AudioCodec> 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());