Using AudioOption to enable audio network adaptor.

BUG=webrtc:6303

Review-Url: https://codereview.webrtc.org/2397573006
Cr-Commit-Position: refs/heads/master@{#14845}
diff --git a/webrtc/api/call/audio_send_stream.cc b/webrtc/api/call/audio_send_stream.cc
index 06cbc54..52c30f0 100644
--- a/webrtc/api/call/audio_send_stream.cc
+++ b/webrtc/api/call/audio_send_stream.cc
@@ -34,6 +34,8 @@
 AudioSendStream::Config::Config(Transport* send_transport)
     : send_transport(send_transport) {}
 
+AudioSendStream::Config::~Config() = default;
+
 std::string AudioSendStream::Config::ToString() const {
   std::stringstream ss;
   ss << "{rtp: " << rtp.ToString();
@@ -82,6 +84,8 @@
   ss << ", opus_max_playback_rate: " << opus_max_playback_rate;
   ss << ", cng_payload_type: " << cng_payload_type;
   ss << ", cng_plfreq: " << cng_plfreq;
+  ss << ", min_ptime: " << min_ptime_ms;
+  ss << ", max_ptime: " << max_ptime_ms;
   ss << ", codec_inst: " << ::ToString(codec_inst);
   ss << '}';
   return ss.str();
@@ -89,30 +93,16 @@
 
 bool AudioSendStream::Config::SendCodecSpec::operator==(
     const AudioSendStream::Config::SendCodecSpec& rhs) const {
-  if (nack_enabled != rhs.nack_enabled) {
-    return false;
+  if (nack_enabled == rhs.nack_enabled &&
+      transport_cc_enabled == rhs.transport_cc_enabled &&
+      enable_codec_fec == rhs.enable_codec_fec &&
+      enable_opus_dtx == rhs.enable_opus_dtx &&
+      opus_max_playback_rate == rhs.opus_max_playback_rate &&
+      cng_payload_type == rhs.cng_payload_type &&
+      cng_plfreq == rhs.cng_plfreq && max_ptime_ms == rhs.max_ptime_ms &&
+      min_ptime_ms == rhs.min_ptime_ms && codec_inst == rhs.codec_inst) {
+    return true;
   }
-  if (transport_cc_enabled != rhs.transport_cc_enabled) {
-    return false;
-  }
-  if (enable_codec_fec != rhs.enable_codec_fec) {
-    return false;
-  }
-  if (enable_opus_dtx != rhs.enable_opus_dtx) {
-    return false;
-  }
-  if (opus_max_playback_rate != rhs.opus_max_playback_rate) {
-    return false;
-  }
-  if (cng_payload_type != rhs.cng_payload_type) {
-    return false;
-  }
-  if (cng_plfreq != rhs.cng_plfreq) {
-    return false;
-  }
-  if (codec_inst != rhs.codec_inst) {
-    return false;
-  }
-  return true;
+  return false;
 }
 }  // namespace webrtc
diff --git a/webrtc/api/call/audio_send_stream.h b/webrtc/api/call/audio_send_stream.h
index 7ff791e..78ab8ec 100644
--- a/webrtc/api/call/audio_send_stream.h
+++ b/webrtc/api/call/audio_send_stream.h
@@ -55,6 +55,7 @@
   struct Config {
     Config() = delete;
     explicit Config(Transport* send_transport);
+    ~Config();
     std::string ToString() const;
 
     // Send-stream specific RTP settings.
@@ -92,6 +93,10 @@
     int min_bitrate_kbps = -1;
     int max_bitrate_kbps = -1;
 
+    // Defines whether to turn on audio network adaptor, and defines its config
+    // string.
+    rtc::Optional<std::string> audio_network_adaptor_config;
+
     struct SendCodecSpec {
       SendCodecSpec();
       std::string ToString() const;
@@ -108,6 +113,8 @@
       int opus_max_playback_rate = 0;
       int cng_payload_type = -1;
       int cng_plfreq = -1;
+      int max_ptime_ms = -1;
+      int min_ptime_ms = -1;
       webrtc::CodecInst codec_inst;
     } send_codec_spec;
   };
diff --git a/webrtc/audio/audio_send_stream.cc b/webrtc/audio/audio_send_stream.cc
index 5c08c9b..51f9073 100644
--- a/webrtc/audio/audio_send_stream.cc
+++ b/webrtc/audio/audio_send_stream.cc
@@ -328,6 +328,20 @@
         return false;
       }
     }
+
+    if (config_.audio_network_adaptor_config) {
+      // Audio network adaptor is only allowed for Opus currently.
+      // |SetReceiverFrameLengthRange| needs to be called before
+      // |EnableAudioNetworkAdaptor|.
+      channel_proxy_->SetReceiverFrameLengthRange(send_codec_spec.min_ptime_ms,
+                                                  send_codec_spec.max_ptime_ms);
+      channel_proxy_->EnableAudioNetworkAdaptor(
+          *config_.audio_network_adaptor_config);
+      LOG(LS_INFO) << "Audio network adaptor enabled on SSRC "
+                   << config_.rtp.ssrc;
+    } else {
+      channel_proxy_->DisableAudioNetworkAdaptor();
+    }
   }
 
   // Set the CN payloadtype and the VAD status.
diff --git a/webrtc/audio/audio_send_stream_unittest.cc b/webrtc/audio/audio_send_stream_unittest.cc
index a2832de..3cfe13f 100644
--- a/webrtc/audio/audio_send_stream_unittest.cc
+++ b/webrtc/audio/audio_send_stream_unittest.cc
@@ -69,7 +69,6 @@
         bitrate_allocator_(&limit_observer_),
         worker_queue_("ConfigHelper_worker_queue") {
     using testing::Invoke;
-    using testing::StrEq;
 
     EXPECT_CALL(voice_engine_,
         RegisterVoiceEngineObserver(_)).WillOnce(Return(0));
@@ -79,40 +78,15 @@
     config.voice_engine = &voice_engine_;
     audio_state_ = AudioState::Create(config);
 
+    SetupDefaultChannelProxy();
+
     EXPECT_CALL(voice_engine_, ChannelProxyFactory(kChannelId))
         .WillOnce(Invoke([this](int channel_id) {
-          EXPECT_FALSE(channel_proxy_);
-          channel_proxy_ = new testing::StrictMock<MockVoEChannelProxy>();
-          EXPECT_CALL(*channel_proxy_, SetRTCPStatus(true)).Times(1);
-          EXPECT_CALL(*channel_proxy_, SetLocalSSRC(kSsrc)).Times(1);
-          EXPECT_CALL(*channel_proxy_, SetRTCP_CNAME(StrEq(kCName))).Times(1);
-          EXPECT_CALL(*channel_proxy_, SetNACKStatus(true, 10)).Times(1);
-          EXPECT_CALL(*channel_proxy_,
-              SetSendAbsoluteSenderTimeStatus(true, kAbsSendTimeId)).Times(1);
-          EXPECT_CALL(*channel_proxy_,
-              SetSendAudioLevelIndicationStatus(true, kAudioLevelId)).Times(1);
-          EXPECT_CALL(*channel_proxy_, EnableSendTransportSequenceNumber(
-                                           kTransportSequenceNumberId))
-              .Times(1);
-          EXPECT_CALL(*channel_proxy_,
-                      RegisterSenderCongestionControlObjects(
-                          congestion_controller_.pacer(),
-                          congestion_controller_.GetTransportFeedbackObserver(),
-                          congestion_controller_.packet_router()))
-              .Times(1);
-          EXPECT_CALL(*channel_proxy_, ResetCongestionControlObjects())
-              .Times(1);
-          EXPECT_CALL(*channel_proxy_, RegisterExternalTransport(nullptr))
-              .Times(1);
-          EXPECT_CALL(*channel_proxy_, DeRegisterExternalTransport())
-              .Times(1);
-          EXPECT_CALL(*channel_proxy_, SetRtcEventLog(testing::NotNull()))
-              .Times(1);
-          EXPECT_CALL(*channel_proxy_, SetRtcEventLog(testing::IsNull()))
-              .Times(1);  // Destructor resets the event log
           return channel_proxy_;
         }));
+
     SetupMockForSetupSendCodec();
+
     stream_config_.voe_channel_id = kChannelId;
     stream_config_.rtp.ssrc = kSsrc;
     stream_config_.rtp.nack.rtp_history_ms = 200;
@@ -139,6 +113,36 @@
   RtcEventLog* event_log() { return &event_log_; }
   MockVoiceEngine* voice_engine() { return &voice_engine_; }
 
+  void SetupDefaultChannelProxy() {
+    using testing::StrEq;
+    channel_proxy_ = new testing::StrictMock<MockVoEChannelProxy>();
+    EXPECT_CALL(*channel_proxy_, SetRTCPStatus(true)).Times(1);
+    EXPECT_CALL(*channel_proxy_, SetLocalSSRC(kSsrc)).Times(1);
+    EXPECT_CALL(*channel_proxy_, SetRTCP_CNAME(StrEq(kCName))).Times(1);
+    EXPECT_CALL(*channel_proxy_, SetNACKStatus(true, 10)).Times(1);
+    EXPECT_CALL(*channel_proxy_,
+                SetSendAbsoluteSenderTimeStatus(true, kAbsSendTimeId))
+        .Times(1);
+    EXPECT_CALL(*channel_proxy_,
+                SetSendAudioLevelIndicationStatus(true, kAudioLevelId))
+        .Times(1);
+    EXPECT_CALL(*channel_proxy_,
+                EnableSendTransportSequenceNumber(kTransportSequenceNumberId))
+        .Times(1);
+    EXPECT_CALL(*channel_proxy_,
+                RegisterSenderCongestionControlObjects(
+                    congestion_controller_.pacer(),
+                    congestion_controller_.GetTransportFeedbackObserver(),
+                    congestion_controller_.packet_router()))
+        .Times(1);
+    EXPECT_CALL(*channel_proxy_, ResetCongestionControlObjects()).Times(1);
+    EXPECT_CALL(*channel_proxy_, RegisterExternalTransport(nullptr)).Times(1);
+    EXPECT_CALL(*channel_proxy_, DeRegisterExternalTransport()).Times(1);
+    EXPECT_CALL(*channel_proxy_, SetRtcEventLog(testing::NotNull())).Times(1);
+    EXPECT_CALL(*channel_proxy_, SetRtcEventLog(testing::IsNull()))
+        .Times(1);  // Destructor resets the event log
+  }
+
   void SetupMockForSetupSendCodec() {
     EXPECT_CALL(voice_engine_, SetVADStatus(kChannelId, false, _, _))
         .WillOnce(Return(0));
@@ -228,6 +232,8 @@
   config.send_codec_spec.opus_max_playback_rate = 32000;
   config.send_codec_spec.cng_payload_type = 42;
   config.send_codec_spec.cng_plfreq = 56;
+  config.send_codec_spec.min_ptime_ms = 20;
+  config.send_codec_spec.max_ptime_ms = 60;
   config.send_codec_spec.codec_inst = kIsacCodec;
   EXPECT_EQ(
       "{rtp: {ssrc: 1234, extensions: [{uri: "
@@ -236,9 +242,9 @@
       "voe_channel_id: 1, min_bitrate_kbps: 12, max_bitrate_kbps: 34, "
       "send_codec_spec: {nack_enabled: true, transport_cc_enabled: false, "
       "enable_codec_fec: true, enable_opus_dtx: false, opus_max_playback_rate: "
-      "32000, cng_payload_type: 42, cng_plfreq: 56, codec_inst: {pltype: "
-      "103, plname: \"isac\", plfreq: 16000, pacsize: 320, channels: 1, rate: "
-      "32000}}}",
+      "32000, cng_payload_type: 42, cng_plfreq: 56, min_ptime: 20, max_ptime: "
+      "60, codec_inst: {pltype: 103, plname: \"isac\", plfreq: 16000, pacsize: "
+      "320, channels: 1, rate: 32000}}}",
       config.ToString());
 }
 
@@ -331,6 +337,10 @@
   stream_config.send_codec_spec.opus_max_playback_rate = 12345;
   stream_config.send_codec_spec.cng_plfreq = 16000;
   stream_config.send_codec_spec.cng_payload_type = 105;
+  stream_config.send_codec_spec.min_ptime_ms = 10;
+  stream_config.send_codec_spec.max_ptime_ms = 60;
+  stream_config.audio_network_adaptor_config =
+      rtc::Optional<std::string>("abced");
   EXPECT_CALL(*helper.voice_engine(), SetFECStatus(kChannelId, true))
       .WillOnce(Return(0));
   EXPECT_CALL(
@@ -347,6 +357,13 @@
                   kChannelId, stream_config.send_codec_spec.cng_payload_type,
                   webrtc::kFreq16000Hz))
       .WillOnce(Return(0));
+  EXPECT_CALL(
+      *helper.channel_proxy(),
+      SetReceiverFrameLengthRange(stream_config.send_codec_spec.min_ptime_ms,
+                                  stream_config.send_codec_spec.max_ptime_ms));
+  EXPECT_CALL(
+      *helper.channel_proxy(),
+      EnableAudioNetworkAdaptor(*stream_config.audio_network_adaptor_config));
   internal::AudioSendStream send_stream(
       stream_config, helper.audio_state(), helper.worker_queue(),
       helper.congestion_controller(), helper.bitrate_allocator(),
diff --git a/webrtc/media/base/mediachannel.h b/webrtc/media/base/mediachannel.h
index afad14c..dc29b9fa 100644
--- a/webrtc/media/base/mediachannel.h
+++ b/webrtc/media/base/mediachannel.h
@@ -167,6 +167,8 @@
     SetFrom(&recording_sample_rate, change.recording_sample_rate);
     SetFrom(&playout_sample_rate, change.playout_sample_rate);
     SetFrom(&combined_audio_video_bwe, change.combined_audio_video_bwe);
+    SetFrom(&audio_network_adaptor, change.audio_network_adaptor);
+    SetFrom(&audio_network_adaptor_config, change.audio_network_adaptor_config);
     SetFrom(&level_control_initial_peak_level_dbfs,
             change.level_control_initial_peak_level_dbfs);
   }
@@ -197,6 +199,8 @@
            recording_sample_rate == o.recording_sample_rate &&
            playout_sample_rate == o.playout_sample_rate &&
            combined_audio_video_bwe == o.combined_audio_video_bwe &&
+           audio_network_adaptor == o.audio_network_adaptor &&
+           audio_network_adaptor_config == o.audio_network_adaptor_config &&
            level_control_initial_peak_level_dbfs ==
                o.level_control_initial_peak_level_dbfs;
   }
@@ -232,6 +236,11 @@
     ost << ToStringIfSet("recording_sample_rate", recording_sample_rate);
     ost << ToStringIfSet("playout_sample_rate", playout_sample_rate);
     ost << ToStringIfSet("combined_audio_video_bwe", combined_audio_video_bwe);
+    ost << ToStringIfSet("audio_network_adaptor", audio_network_adaptor);
+    // The adaptor config is a serialized proto buffer and therefore not human
+    // readable. So we comment out the following line.
+    // ost << ToStringIfSet("audio_network_adaptor_config",
+    //     audio_network_adaptor_config);
     ost << "}";
     return ost.str();
   }
@@ -274,6 +283,10 @@
   // "googCombinedAudioVideoBwe", but not used anywhere. So delete it,
   // and check if any other AudioOptions members are unused.
   rtc::Optional<bool> combined_audio_video_bwe;
+  // Enable audio network adaptor.
+  rtc::Optional<bool> audio_network_adaptor;
+  // Config string for audio network adaptor.
+  rtc::Optional<std::string> audio_network_adaptor_config;
 
  private:
   template <typename T>
diff --git a/webrtc/media/engine/webrtcvoiceengine.cc b/webrtc/media/engine/webrtcvoiceengine.cc
index ebcd161..21e7c81 100644
--- a/webrtc/media/engine/webrtcvoiceengine.cc
+++ b/webrtc/media/engine/webrtcvoiceengine.cc
@@ -186,6 +186,29 @@
   return codec.GetParam(feature, &value) && value == 1;
 }
 
+rtc::Optional<std::string> GetAudioNetworkAdaptorConfig(
+    const AudioOptions& options) {
+  if (options.audio_network_adaptor && *options.audio_network_adaptor &&
+      options.audio_network_adaptor_config) {
+    // Turn on audio network adaptor only when |options_.audio_network_adaptor|
+    // equals true and |options_.audio_network_adaptor_config| has a value.
+    return options.audio_network_adaptor_config;
+  }
+  return rtc::Optional<std::string>();
+}
+
+// Returns integer parameter params[feature] if it is defined. Returns
+// |default_value| otherwise.
+int GetCodecFeatureInt(const AudioCodec& codec,
+                       const char* feature,
+                       int default_value) {
+  int value = 0;
+  if (codec.GetParam(feature, &value)) {
+    return value;
+  }
+  return default_value;
+}
+
 // 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,
@@ -221,29 +244,33 @@
   return bitrate;
 }
 
-// Returns kOpusDefaultPlaybackRate if params[kCodecParamMaxPlaybackRate] is not
-// defined. Returns the value of params[kCodecParamMaxPlaybackRate] otherwise.
-int GetOpusMaxPlaybackRate(const AudioCodec& codec) {
-  int value;
-  if (codec.GetParam(kCodecParamMaxPlaybackRate, &value)) {
-    return value;
-  }
-  return kOpusDefaultMaxPlaybackRate;
-}
-
-void GetOpusConfig(const AudioCodec& codec, webrtc::CodecInst* voe_codec,
-                          bool* enable_codec_fec, int* max_playback_rate,
-                          bool* enable_codec_dtx) {
+void GetOpusConfig(const AudioCodec& codec,
+                   webrtc::CodecInst* voe_codec,
+                   bool* enable_codec_fec,
+                   int* max_playback_rate,
+                   bool* enable_codec_dtx,
+                   int* min_ptime_ms,
+                   int* max_ptime_ms) {
   *enable_codec_fec = IsCodecFeatureEnabled(codec, kCodecParamUseInbandFec);
   *enable_codec_dtx = IsCodecFeatureEnabled(codec, kCodecParamUseDtx);
-  *max_playback_rate = GetOpusMaxPlaybackRate(codec);
+  *max_playback_rate = GetCodecFeatureInt(codec, kCodecParamMaxPlaybackRate,
+                                          kOpusDefaultMaxPlaybackRate);
+  *max_ptime_ms =
+      GetCodecFeatureInt(codec, kCodecParamMaxPTime, kOpusDefaultMaxPTime);
+  *min_ptime_ms =
+      GetCodecFeatureInt(codec, kCodecParamMinPTime, kOpusDefaultMinPTime);
+  if (*max_ptime_ms < *min_ptime_ms) {
+    // If min ptime or max ptime defined by codec parameter is wrong, we use
+    // the default values.
+    *max_ptime_ms = kOpusDefaultMaxPTime;
+    *min_ptime_ms = kOpusDefaultMinPTime;
+  }
 
   // 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);
 }
@@ -897,7 +924,6 @@
       LOG_RTCERR1(SetPlayoutSampleRate, *options.playout_sample_rate);
     }
   }
-
   return true;
 }
 
@@ -1151,6 +1177,7 @@
       const webrtc::AudioSendStream::Config::SendCodecSpec& send_codec_spec,
       const std::vector<webrtc::RtpExtension>& extensions,
       int max_send_bitrate_bps,
+      const rtc::Optional<std::string>& audio_network_adaptor_config,
       webrtc::Call* call,
       webrtc::Transport* send_transport)
       : voe_audio_transport_(voe_audio_transport),
@@ -1166,6 +1193,7 @@
     config_.rtp.c_name = c_name;
     config_.voe_channel_id = ch;
     config_.rtp.extensions = extensions;
+    config_.audio_network_adaptor_config = audio_network_adaptor_config;
     RecreateAudioSendStream(send_codec_spec);
   }
 
@@ -1182,7 +1210,6 @@
     config_.rtp.nack.rtp_history_ms =
         send_codec_spec_.nack_enabled ? kNackRtpHistoryMs : 0;
     config_.send_codec_spec = send_codec_spec_;
-
     auto send_rate = ComputeSendBitrate(
         max_send_bitrate_bps_, rtp_parameters_.encodings[0].max_bitrate_bps,
         send_codec_spec.codec_inst);
@@ -1201,6 +1228,16 @@
     RecreateAudioSendStream();
   }
 
+  void RecreateAudioSendStream(
+      const rtc::Optional<std::string>& audio_network_adaptor_config) {
+    RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
+    if (config_.audio_network_adaptor_config == audio_network_adaptor_config) {
+      return;
+    }
+    config_.audio_network_adaptor_config = audio_network_adaptor_config;
+    RecreateAudioSendStream();
+  }
+
   bool SetMaxSendBitrate(int bps) {
     RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
     auto send_rate =
@@ -1722,6 +1759,13 @@
         "Failed to apply engine options during channel SetOptions.";
     return false;
   }
+
+  rtc::Optional<std::string> audio_network_adatptor_config =
+      GetAudioNetworkAdaptorConfig(options_);
+  for (auto& it : send_streams_) {
+    it.second->RecreateAudioSendStream(audio_network_adatptor_config);
+  }
+
   LOG(LS_INFO) << "Set voice channel options.  Current options: "
                << options_.ToString();
   return true;
@@ -1835,7 +1879,9 @@
       GetOpusConfig(*codec, &send_codec_spec.codec_inst,
                     &send_codec_spec.enable_codec_fec,
                     &send_codec_spec.opus_max_playback_rate,
-                    &send_codec_spec.enable_opus_dtx);
+                    &send_codec_spec.enable_opus_dtx,
+                    &send_codec_spec.min_ptime_ms,
+                    &send_codec_spec.max_ptime_ms);
     }
 
     // Set packet size if the AudioCodec param kCodecParamPTime is set.
@@ -2011,9 +2057,12 @@
   webrtc::AudioTransport* audio_transport =
       engine()->voe()->base()->audio_transport();
 
+  rtc::Optional<std::string> audio_network_adaptor_config =
+      GetAudioNetworkAdaptorConfig(options_);
   WebRtcAudioSendStream* stream = new WebRtcAudioSendStream(
       channel, audio_transport, ssrc, sp.cname, send_codec_spec_,
-      send_rtp_extensions_, max_send_bitrate_bps_, call_, this);
+      send_rtp_extensions_, max_send_bitrate_bps_, audio_network_adaptor_config,
+      call_, this);
   send_streams_.insert(std::make_pair(ssrc, stream));
 
   // At this point the stream's local SSRC has been updated. If it is the first
diff --git a/webrtc/media/engine/webrtcvoiceengine_unittest.cc b/webrtc/media/engine/webrtcvoiceengine_unittest.cc
index aa041dd..6aedab2 100644
--- a/webrtc/media/engine/webrtcvoiceengine_unittest.cc
+++ b/webrtc/media/engine/webrtcvoiceengine_unittest.cc
@@ -209,10 +209,15 @@
     EXPECT_TRUE(channel_->SetSendParameters(params));
   }
 
-  void SetAudioSend(uint32_t ssrc, bool enable, cricket::AudioSource* source) {
+  void SetAudioSend(uint32_t ssrc, bool enable, cricket::AudioSource* source,
+                    const cricket::AudioOptions* options = nullptr) {
     EXPECT_CALL(apm_, set_output_will_be_muted(!enable));
     ASSERT_TRUE(channel_);
-    EXPECT_TRUE(channel_->SetAudioSend(ssrc, enable, nullptr, source));
+    if (enable && options) {
+      EXPECT_CALL(apm_, ApplyConfig(testing::_));
+      EXPECT_CALL(apm_, SetExtraOptions(testing::_));
+    }
+    EXPECT_TRUE(channel_->SetAudioSend(ssrc, enable, options, source));
   }
 
   void TestInsertDtmf(uint32_t ssrc, bool caller) {
@@ -320,6 +325,10 @@
     return GetSendStreamConfig(ssrc).send_codec_spec.codec_inst.pacsize;
   }
 
+  const rtc::Optional<std::string>& GetAudioNetworkAdaptorConfig(int32_t ssrc) {
+    return GetSendStreamConfig(ssrc).audio_network_adaptor_config;
+  }
+
   void SetAndExpectMaxBitrate(const cricket::AudioCodec& codec,
                               int global_max,
                               int stream_max,
@@ -2367,6 +2376,53 @@
   SetSendParameters(send_parameters_);
 }
 
+TEST_F(WebRtcVoiceEngineTestFake, SetAudioNetworkAdaptorViaOptions) {
+  EXPECT_TRUE(SetupSendStream());
+  send_parameters_.options.audio_network_adaptor = rtc::Optional<bool>(true);
+  send_parameters_.options.audio_network_adaptor_config =
+      rtc::Optional<std::string>("1234");
+  SetSendParameters(send_parameters_);
+  EXPECT_EQ(send_parameters_.options.audio_network_adaptor_config,
+            GetAudioNetworkAdaptorConfig(kSsrc1));
+}
+
+TEST_F(WebRtcVoiceEngineTestFake, AudioSendResetAudioNetworkAdaptor) {
+  EXPECT_TRUE(SetupSendStream());
+  send_parameters_.options.audio_network_adaptor = rtc::Optional<bool>(true);
+  send_parameters_.options.audio_network_adaptor_config =
+      rtc::Optional<std::string>("1234");
+  SetSendParameters(send_parameters_);
+  EXPECT_EQ(send_parameters_.options.audio_network_adaptor_config,
+            GetAudioNetworkAdaptorConfig(kSsrc1));
+  const int initial_num = call_.GetNumCreatedSendStreams();
+  cricket::AudioOptions options;
+  options.audio_network_adaptor = rtc::Optional<bool>(false);
+  SetAudioSend(kSsrc1, true, nullptr, &options);
+  // AudioSendStream expected to be recreated.
+  EXPECT_EQ(initial_num + 1, call_.GetNumCreatedSendStreams());
+  EXPECT_EQ(rtc::Optional<std::string>(), GetAudioNetworkAdaptorConfig(kSsrc1));
+}
+
+TEST_F(WebRtcVoiceEngineTestFake, AudioNetworkAdaptorNotGetOverridden) {
+  EXPECT_TRUE(SetupSendStream());
+  send_parameters_.options.audio_network_adaptor = rtc::Optional<bool>(true);
+  send_parameters_.options.audio_network_adaptor_config =
+      rtc::Optional<std::string>("1234");
+  SetSendParameters(send_parameters_);
+  EXPECT_EQ(send_parameters_.options.audio_network_adaptor_config,
+            GetAudioNetworkAdaptorConfig(kSsrc1));
+  const int initial_num = call_.GetNumCreatedSendStreams();
+  cricket::AudioOptions options;
+  options.audio_network_adaptor = rtc::Optional<bool>();
+  // Unvalued |options.audio_network_adaptor|.should not reset audio network
+  // adaptor.
+  SetAudioSend(kSsrc1, true, nullptr, &options);
+  // AudioSendStream not expected to be recreated.
+  EXPECT_EQ(initial_num, call_.GetNumCreatedSendStreams());
+  EXPECT_EQ(send_parameters_.options.audio_network_adaptor_config,
+            GetAudioNetworkAdaptorConfig(kSsrc1));
+}
+
 // Test that we can set the outgoing SSRC properly.
 // SSRC is set in SetupSendStream() by calling AddSendStream.
 TEST_F(WebRtcVoiceEngineTestFake, SetSendSsrc) {
diff --git a/webrtc/modules/audio_coding/codecs/audio_encoder.h b/webrtc/modules/audio_coding/codecs/audio_encoder.h
index 19dc332..2c8d9ce 100644
--- a/webrtc/modules/audio_coding/codecs/audio_encoder.h
+++ b/webrtc/modules/audio_coding/codecs/audio_encoder.h
@@ -185,7 +185,7 @@
   virtual void OnReceivedRtt(int rtt_ms);
 
   // To allow encoder to adapt its frame length, it must be provided the frame
-  // length range that receives can accept.
+  // length range that receivers can accept.
   virtual void SetReceiverFrameLengthRange(int min_frame_length_ms,
                                            int max_frame_length_ms);
 
diff --git a/webrtc/test/mock_voe_channel_proxy.h b/webrtc/test/mock_voe_channel_proxy.h
index 1847cf4..4cbe509 100644
--- a/webrtc/test/mock_voe_channel_proxy.h
+++ b/webrtc/test/mock_voe_channel_proxy.h
@@ -60,6 +60,11 @@
   MOCK_METHOD1(SetChannelOutputVolumeScaling, void(float scaling));
   MOCK_METHOD1(SetRtcEventLog, void(RtcEventLog* event_log));
   MOCK_METHOD1(SetBitrate, void(int bitrate_bps));
+  MOCK_METHOD1(EnableAudioNetworkAdaptor,
+               void(const std::string& config_string));
+  MOCK_METHOD0(DisableAudioNetworkAdaptor, void());
+  MOCK_METHOD2(SetReceiverFrameLengthRange,
+               void(int min_frame_length_ms, int max_frame_length_ms));
 };
 }  // namespace test
 }  // namespace webrtc
diff --git a/webrtc/voice_engine/channel_proxy.cc b/webrtc/voice_engine/channel_proxy.cc
index 982cf1d..67c7fe0 100644
--- a/webrtc/voice_engine/channel_proxy.cc
+++ b/webrtc/voice_engine/channel_proxy.cc
@@ -214,6 +214,24 @@
   channel()->SetRtcEventLog(event_log);
 }
 
+void ChannelProxy::EnableAudioNetworkAdaptor(const std::string& config_string) {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  bool ret = channel()->EnableAudioNetworkAdaptor(config_string);
+  RTC_DCHECK(ret);
+;}
+
+void ChannelProxy::DisableAudioNetworkAdaptor() {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  channel()->DisableAudioNetworkAdaptor();
+}
+
+void ChannelProxy::SetReceiverFrameLengthRange(int min_frame_length_ms,
+                                               int max_frame_length_ms) {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  channel()->SetReceiverFrameLengthRange(min_frame_length_ms,
+                                         max_frame_length_ms);
+}
+
 AudioMixer::Source::AudioFrameInfo ChannelProxy::GetAudioFrameWithInfo(
     int sample_rate_hz,
     AudioFrame* audio_frame) {
diff --git a/webrtc/voice_engine/channel_proxy.h b/webrtc/voice_engine/channel_proxy.h
index a9ec860..8bb452b 100644
--- a/webrtc/voice_engine/channel_proxy.h
+++ b/webrtc/voice_engine/channel_proxy.h
@@ -65,34 +65,31 @@
   virtual void RegisterReceiverCongestionControlObjects(
       PacketRouter* packet_router);
   virtual void ResetCongestionControlObjects();
-
   virtual CallStatistics GetRTCPStatistics() const;
   virtual std::vector<ReportBlock> GetRemoteRTCPReportBlocks() const;
   virtual NetworkStatistics GetNetworkStatistics() const;
   virtual AudioDecodingCallStats GetDecodingCallStatistics() const;
   virtual int32_t GetSpeechOutputLevelFullRange() const;
   virtual uint32_t GetDelayEstimate() const;
-
   virtual bool SetSendTelephoneEventPayloadType(int payload_type);
   virtual bool SendTelephoneEventOutband(int event, int duration_ms);
   virtual void SetBitrate(int bitrate_bps);
   virtual void SetSink(std::unique_ptr<AudioSinkInterface> sink);
   virtual void SetInputMute(bool muted);
-
   virtual void RegisterExternalTransport(Transport* transport);
   virtual void DeRegisterExternalTransport();
   virtual bool ReceivedRTPPacket(const uint8_t* packet,
                                  size_t length,
                                  const PacketTime& packet_time);
   virtual bool ReceivedRTCPPacket(const uint8_t* packet, size_t length);
-
   virtual const rtc::scoped_refptr<AudioDecoderFactory>&
       GetAudioDecoderFactory() const;
-
   virtual void SetChannelOutputVolumeScaling(float scaling);
-
   virtual void SetRtcEventLog(RtcEventLog* event_log);
-
+  virtual void EnableAudioNetworkAdaptor(const std::string& config_string);
+  virtual void DisableAudioNetworkAdaptor();
+  virtual void SetReceiverFrameLengthRange(int min_frame_length_ms,
+                                           int max_frame_length_ms);
   virtual AudioMixer::Source::AudioFrameInfo GetAudioFrameWithInfo(
       int sample_rate_hz,
       AudioFrame* audio_frame);