Supporting Opus DTX in Voice Engine.

Opus DTX is an Opus specific feature. It does not require WebRTC VAD/DTX, therefore is not set by VoECodec::SetVADStatus(), but rather a dedicated API.

BUG=1014
R=henrika@webrtc.org, pbos@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/43709004

Cr-Commit-Position: refs/heads/master@{#8716}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8716 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/media/webrtc/fakewebrtcvoiceengine.h b/talk/media/webrtc/fakewebrtcvoiceengine.h
index bb529e2..960bad3 100644
--- a/talk/media/webrtc/fakewebrtcvoiceengine.h
+++ b/talk/media/webrtc/fakewebrtcvoiceengine.h
@@ -192,6 +192,7 @@
           vad(false),
           codec_fec(false),
           max_encoding_bandwidth(0),
+          opus_dtx(false),
           red(false),
           nack(false),
           media_processor_registered(false),
@@ -222,6 +223,7 @@
     bool vad;
     bool codec_fec;
     int max_encoding_bandwidth;
+    bool opus_dtx;
     bool red;
     bool nack;
     bool media_processor_registered;
@@ -664,6 +666,16 @@
     return 0;
   }
 
+  WEBRTC_FUNC(SetOpusDtx, (int channel, bool enable_dtx)) {
+    WEBRTC_CHECK_CHANNEL(channel);
+    if (_stricmp(channels_[channel]->send_codec.plname, "opus") != 0) {
+      // Return -1 if current send codec is not Opus.
+      return -1;
+    }
+    channels_[channel]->opus_dtx = enable_dtx;
+    return 0;
+  }
+
   // webrtc::VoEDtmf
   WEBRTC_FUNC(SendTelephoneEvent, (int channel, int event_code,
       bool out_of_band = true, int length_ms = 160, int attenuation_db = 10)) {
diff --git a/webrtc/voice_engine/channel.cc b/webrtc/voice_engine/channel.cc
index 2df3a71..719cc35 100644
--- a/webrtc/voice_engine/channel.cc
+++ b/webrtc/voice_engine/channel.cc
@@ -1565,6 +1565,19 @@
   return 0;
 }
 
+int Channel::SetOpusDtx(bool enable_dtx) {
+  WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
+               "Channel::SetOpusDtx(%d)", enable_dtx);
+  int ret = enable_dtx ? audio_coding_->EnableOpusDtx(true)
+                       : audio_coding_->DisableOpusDtx();
+  if (ret != 0) {
+    _engineStatisticsPtr->SetLastError(
+        VE_AUDIO_CODING_MODULE_ERROR, kTraceError, "SetOpusDtx() failed");
+    return -1;
+  }
+  return 0;
+}
+
 int32_t Channel::RegisterExternalTransport(Transport& transport)
 {
     WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
diff --git a/webrtc/voice_engine/channel.h b/webrtc/voice_engine/channel.h
index cd1469f..e197af8 100644
--- a/webrtc/voice_engine/channel.h
+++ b/webrtc/voice_engine/channel.h
@@ -208,6 +208,7 @@
     int32_t GetRecPayloadType(CodecInst& codec);
     int32_t SetSendCNPayloadType(int type, PayloadFrequencies frequency);
     int SetOpusMaxPlaybackRate(int frequency_hz);
+    int SetOpusDtx(bool enable_dtx);
 
     // VoENetwork
     int32_t RegisterExternalTransport(Transport& transport);
diff --git a/webrtc/voice_engine/include/voe_codec.h b/webrtc/voice_engine/include/voe_codec.h
index a61b49b..4b9f939 100644
--- a/webrtc/voice_engine/include/voe_codec.h
+++ b/webrtc/voice_engine/include/voe_codec.h
@@ -117,6 +117,10 @@
       return -1;
     }
 
+    // If send codec is Opus on a specified |channel|, set its DTX. Returns 0 if
+    // success, and -1 if failed.
+    virtual int SetOpusDtx(int channel, bool enable_dtx) = 0;
+
     // Don't use. To be removed.
     virtual int SetAMREncFormat(int channel, AmrMode mode) { return -1; }
     virtual int SetAMRDecFormat(int channel, AmrMode mode) { return -1; }
diff --git a/webrtc/voice_engine/test/auto_test/standard/codec_test.cc b/webrtc/voice_engine/test/auto_test/standard/codec_test.cc
index 3460f0a..bfc2a30 100644
--- a/webrtc/voice_engine/test/auto_test/standard/codec_test.cc
+++ b/webrtc/voice_engine/test/auto_test/standard/codec_test.cc
@@ -158,6 +158,30 @@
   }
 }
 
+TEST_F(CodecTest, OpusDtxCanBeSetForOpus) {
+  for (int i = 0; i < voe_codec_->NumOfCodecs(); ++i) {
+    voe_codec_->GetCodec(i, codec_instance_);
+    if (_stricmp("opus", codec_instance_.plname)) {
+      continue;
+    }
+    voe_codec_->SetSendCodec(channel_, codec_instance_);
+    EXPECT_EQ(0, voe_codec_->SetOpusDtx(channel_, false));
+    EXPECT_EQ(0, voe_codec_->SetOpusDtx(channel_, true));
+  }
+}
+
+TEST_F(CodecTest, OpusDtxCannotBeSetForNonOpus) {
+  for (int i = 0; i < voe_codec_->NumOfCodecs(); ++i) {
+    voe_codec_->GetCodec(i, codec_instance_);
+    if (!_stricmp("opus", codec_instance_.plname)) {
+      continue;
+    }
+    voe_codec_->SetSendCodec(channel_, codec_instance_);
+    EXPECT_EQ(-1, voe_codec_->SetOpusDtx(channel_, false));
+    EXPECT_EQ(-1, voe_codec_->SetOpusDtx(channel_, true));
+  }
+}
+
 // TODO(xians, phoglund): Re-enable when issue 372 is resolved.
 TEST_F(CodecTest, DISABLED_ManualVerifySendCodecsForAllPacketSizes) {
   for (int i = 0; i < voe_codec_->NumOfCodecs(); ++i) {
diff --git a/webrtc/voice_engine/test/cmd_test/voe_cmd_test.cc b/webrtc/voice_engine/test/cmd_test/voe_cmd_test.cc
index f0c3471..419ba55 100644
--- a/webrtc/voice_engine/test/cmd_test/voe_cmd_test.cc
+++ b/webrtc/voice_engine/test/cmd_test/voe_cmd_test.cc
@@ -232,6 +232,7 @@
   bool typing_detection = false;
   bool muted = false;
   bool opus_stereo = false;
+  bool opus_dtx = false;
   bool experimental_ns_enabled = false;
   bool debug_recording_started = false;
 
@@ -447,6 +448,7 @@
       printf("%i. Toggle Opus stereo (Opus must be selected again to apply "
              "the setting) \n", option_index++);
       printf("%i. Set Opus maximum playback rate \n", option_index++);
+      printf("%i. Toggle Opus DTX \n", option_index++);
       printf("%i. Set bit rate (only take effect on codecs that allow the "
              "change) \n", option_index++);
       printf("%i. Toggle debug recording \n", option_index++);
@@ -774,6 +776,11 @@
         res = codec->SetOpusMaxPlaybackRate(chan, max_playback_rate);
         VALIDATE;
       } else if (option_selection == option_index++) {
+        opus_dtx = !opus_dtx;
+        res = codec->SetOpusDtx(chan, opus_dtx);
+        VALIDATE;
+        printf("Opus DTX %s.\n", opus_dtx ? "enabled" : "disabled");
+      } else if (option_selection == option_index++) {
         res = codec->GetSendCodec(chan, cinst);
         VALIDATE;
         printf("Current bit rate is %i bps, set to: ", cinst.rate);
diff --git a/webrtc/voice_engine/voe_codec_impl.cc b/webrtc/voice_engine/voe_codec_impl.cc
index aa54a1f..2b0141f 100644
--- a/webrtc/voice_engine/voe_codec_impl.cc
+++ b/webrtc/voice_engine/voe_codec_impl.cc
@@ -436,6 +436,23 @@
   return channelPtr->SetOpusMaxPlaybackRate(frequency_hz);
 }
 
+int VoECodecImpl::SetOpusDtx(int channel, bool enable_dtx) {
+  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
+               "SetOpusDtx(channel=%d, enable_dtx=%d)", channel, enable_dtx);
+  if (!_shared->statistics().Initialized()) {
+    _shared->SetLastError(VE_NOT_INITED, kTraceError);
+    return -1;
+  }
+  voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
+  voe::Channel* channelPtr = ch.channel();
+  if (channelPtr == NULL) {
+    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
+                          "SetOpusDtx failed to locate channel");
+    return -1;
+  }
+  return channelPtr->SetOpusDtx(enable_dtx);
+}
+
 void VoECodecImpl::ACMToExternalCodecRepresentation(CodecInst& toInst,
                                                     const CodecInst& fromInst)
 {
diff --git a/webrtc/voice_engine/voe_codec_impl.h b/webrtc/voice_engine/voe_codec_impl.h
index ab26adc..dad808d 100644
--- a/webrtc/voice_engine/voe_codec_impl.h
+++ b/webrtc/voice_engine/voe_codec_impl.h
@@ -56,6 +56,8 @@
 
     virtual int SetOpusMaxPlaybackRate(int channel, int frequency_hz);
 
+    virtual int SetOpusDtx(int channel, bool enable_dtx);
+
 protected:
     VoECodecImpl(voe::SharedData* shared);
     virtual ~VoECodecImpl();