Remove usage of VoEBase::AssociateSendChannel() from WVoMC.

- Functionality now implemented in AudioReceiveStream and Call.
- Added some missing function to MockChannelProxy.

BUG=webrtc:4690

Review-Url: https://codereview.webrtc.org/2461523002
Cr-Commit-Position: refs/heads/master@{#15072}
diff --git a/webrtc/audio/audio_receive_stream.cc b/webrtc/audio/audio_receive_stream.cc
index cc30939..e2f8f14 100644
--- a/webrtc/audio/audio_receive_stream.cc
+++ b/webrtc/audio/audio_receive_stream.cc
@@ -14,6 +14,7 @@
 #include <utility>
 
 #include "webrtc/api/call/audio_sink.h"
+#include "webrtc/audio/audio_send_stream.h"
 #include "webrtc/audio/audio_state.h"
 #include "webrtc/audio/conversion.h"
 #include "webrtc/base/checks.h"
@@ -143,6 +144,7 @@
   RTC_DCHECK(thread_checker_.CalledOnValidThread());
   LOG(LS_INFO) << "~AudioReceiveStream: " << config_.ToString();
   Stop();
+  channel_proxy_->DisassociateSendChannel();
   channel_proxy_->DeRegisterExternalTransport();
   channel_proxy_->ResetCongestionControlObjects();
   channel_proxy_->SetRtcEventLog(nullptr);
@@ -230,6 +232,18 @@
   return config_;
 }
 
+void AudioReceiveStream::AssociateSendStream(AudioSendStream* send_stream) {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  if (send_stream) {
+    VoiceEngineImpl* voe_impl = static_cast<VoiceEngineImpl*>(voice_engine());
+    std::unique_ptr<voe::ChannelProxy> send_channel_proxy =
+        voe_impl->GetChannelProxy(send_stream->config().voe_channel_id);
+    channel_proxy_->AssociateSendChannel(*send_channel_proxy.get());
+  } else {
+    channel_proxy_->DisassociateSendChannel();
+  }
+}
+
 void AudioReceiveStream::SignalNetworkState(NetworkState state) {
   RTC_DCHECK(thread_checker_.CalledOnValidThread());
 }
diff --git a/webrtc/audio/audio_receive_stream.h b/webrtc/audio/audio_receive_stream.h
index 906ddb6..aeaadc6 100644
--- a/webrtc/audio/audio_receive_stream.h
+++ b/webrtc/audio/audio_receive_stream.h
@@ -30,6 +30,7 @@
 }  // namespace voe
 
 namespace internal {
+class AudioSendStream;
 
 class AudioReceiveStream final : public webrtc::AudioReceiveStream,
                                  public AudioMixer::Source {
@@ -47,6 +48,7 @@
   void SetSink(std::unique_ptr<AudioSinkInterface> sink) override;
   void SetGain(float gain) override;
 
+  void AssociateSendStream(AudioSendStream* send_stream);
   void SignalNetworkState(NetworkState state);
   bool DeliverRtcp(const uint8_t* packet, size_t length);
   bool DeliverRtp(const uint8_t* packet,
diff --git a/webrtc/audio/audio_receive_stream_unittest.cc b/webrtc/audio/audio_receive_stream_unittest.cc
index bde68ed..0331d9b 100644
--- a/webrtc/audio/audio_receive_stream_unittest.cc
+++ b/webrtc/audio/audio_receive_stream_unittest.cc
@@ -113,6 +113,7 @@
           EXPECT_CALL(*channel_proxy_, SetRtcEventLog(testing::IsNull()))
               .Times(1)
               .After(expect_set);
+          EXPECT_CALL(*channel_proxy_, DisassociateSendChannel()).Times(1);
           return channel_proxy_;
         }));
     EXPECT_CALL(voice_engine_, StopPlayout(kChannelId)).WillOnce(Return(0));
diff --git a/webrtc/call/call.cc b/webrtc/call/call.cc
index ebc1aeb..3ed7947 100644
--- a/webrtc/call/call.cc
+++ b/webrtc/call/call.cc
@@ -407,6 +407,14 @@
                audio_send_ssrcs_.end());
     audio_send_ssrcs_[config.rtp.ssrc] = send_stream;
   }
+  {
+    ReadLockScoped read_lock(*receive_crit_);
+    for (const auto& kv : audio_receive_ssrcs_) {
+      if (kv.second->config().rtp.local_ssrc == config.rtp.ssrc) {
+        kv.second->AssociateSendStream(send_stream);
+      }
+    }
+  }
   send_stream->SignalNetworkState(audio_network_state_);
   UpdateAggregateNetworkState();
   return send_stream;
@@ -421,11 +429,19 @@
 
   webrtc::internal::AudioSendStream* audio_send_stream =
       static_cast<webrtc::internal::AudioSendStream*>(send_stream);
+  uint32_t ssrc = audio_send_stream->config().rtp.ssrc;
   {
     WriteLockScoped write_lock(*send_crit_);
-    size_t num_deleted = audio_send_ssrcs_.erase(
-        audio_send_stream->config().rtp.ssrc);
-    RTC_DCHECK(num_deleted == 1);
+    size_t num_deleted = audio_send_ssrcs_.erase(ssrc);
+    RTC_DCHECK_EQ(1, num_deleted);
+  }
+  {
+    ReadLockScoped read_lock(*receive_crit_);
+    for (const auto& kv : audio_receive_ssrcs_) {
+      if (kv.second->config().rtp.local_ssrc == ssrc) {
+        kv.second->AssociateSendStream(nullptr);
+      }
+    }
   }
   UpdateAggregateNetworkState();
   delete audio_send_stream;
@@ -445,6 +461,13 @@
     audio_receive_ssrcs_[config.rtp.remote_ssrc] = receive_stream;
     ConfigureSync(config.sync_group);
   }
+  {
+    ReadLockScoped read_lock(*send_crit_);
+    auto it = audio_send_ssrcs_.find(config.rtp.local_ssrc);
+    if (it != audio_send_ssrcs_.end()) {
+      receive_stream->AssociateSendStream(it->second);
+    }
+  }
   receive_stream->SignalNetworkState(audio_network_state_);
   UpdateAggregateNetworkState();
   return receive_stream;
diff --git a/webrtc/call/call_unittest.cc b/webrtc/call/call_unittest.cc
index 1cdd48a..2b93deb 100644
--- a/webrtc/call/call_unittest.cc
+++ b/webrtc/call/call_unittest.cc
@@ -32,6 +32,7 @@
   }
 
   webrtc::Call* operator->() { return call_.get(); }
+  webrtc::test::MockVoiceEngine* voice_engine() { return &voice_engine_; }
 
  private:
   testing::NiceMock<webrtc::test::MockVoiceEngine> voice_engine_;
@@ -118,6 +119,102 @@
   }
 }
 
+TEST(CallTest, CreateDestroy_AssociateAudioSendReceiveStreams_RecvFirst) {
+  rtc::scoped_refptr<webrtc::AudioDecoderFactory> decoder_factory(
+      new rtc::RefCountedObject<webrtc::MockAudioDecoderFactory>);
+  CallHelper call(decoder_factory);
+
+  constexpr int kRecvChannelId = 101;
+
+  // Set up the mock to create a channel proxy which we know of, so that we can
+  // add our expectations to it.
+  test::MockVoEChannelProxy* recv_channel_proxy = nullptr;
+  EXPECT_CALL(*call.voice_engine(), ChannelProxyFactory(testing::_))
+      .WillRepeatedly(testing::Invoke([&](int channel_id) {
+        test::MockVoEChannelProxy* channel_proxy =
+            new testing::NiceMock<test::MockVoEChannelProxy>();
+        EXPECT_CALL(*channel_proxy, GetAudioDecoderFactory())
+            .WillRepeatedly(testing::ReturnRef(decoder_factory));
+        // If being called for the send channel, save a pointer to the channel
+        // proxy for later.
+        if (channel_id == kRecvChannelId) {
+          EXPECT_FALSE(recv_channel_proxy);
+          recv_channel_proxy = channel_proxy;
+        }
+        return channel_proxy;
+      }));
+
+  AudioReceiveStream::Config recv_config;
+  recv_config.rtp.remote_ssrc = 42;
+  recv_config.rtp.local_ssrc = 777;
+  recv_config.voe_channel_id = kRecvChannelId;
+  recv_config.decoder_factory = decoder_factory;
+  AudioReceiveStream* recv_stream = call->CreateAudioReceiveStream(recv_config);
+  EXPECT_NE(recv_stream, nullptr);
+
+  EXPECT_CALL(*recv_channel_proxy, AssociateSendChannel(testing::_)).Times(1);
+  AudioSendStream::Config send_config(nullptr);
+  send_config.rtp.ssrc = 777;
+  send_config.voe_channel_id = 123;
+  AudioSendStream* send_stream = call->CreateAudioSendStream(send_config);
+  EXPECT_NE(send_stream, nullptr);
+
+  EXPECT_CALL(*recv_channel_proxy, DisassociateSendChannel()).Times(1);
+  call->DestroyAudioSendStream(send_stream);
+
+  EXPECT_CALL(*recv_channel_proxy, DisassociateSendChannel()).Times(1);
+  call->DestroyAudioReceiveStream(recv_stream);
+}
+
+TEST(CallTest, CreateDestroy_AssociateAudioSendReceiveStreams_SendFirst) {
+  rtc::scoped_refptr<webrtc::AudioDecoderFactory> decoder_factory(
+      new rtc::RefCountedObject<webrtc::MockAudioDecoderFactory>);
+  CallHelper call(decoder_factory);
+
+  constexpr int kRecvChannelId = 101;
+
+  // Set up the mock to create a channel proxy which we know of, so that we can
+  // add our expectations to it.
+  test::MockVoEChannelProxy* recv_channel_proxy = nullptr;
+  EXPECT_CALL(*call.voice_engine(), ChannelProxyFactory(testing::_))
+      .WillRepeatedly(testing::Invoke([&](int channel_id) {
+        test::MockVoEChannelProxy* channel_proxy =
+            new testing::NiceMock<test::MockVoEChannelProxy>();
+        EXPECT_CALL(*channel_proxy, GetAudioDecoderFactory())
+            .WillRepeatedly(testing::ReturnRef(decoder_factory));
+        // If being called for the send channel, save a pointer to the channel
+        // proxy for later.
+        if (channel_id == kRecvChannelId) {
+          EXPECT_FALSE(recv_channel_proxy);
+          recv_channel_proxy = channel_proxy;
+          // We need to set this expectation here since the channel proxy is
+          // created as a side effect of CreateAudioReceiveStream().
+          EXPECT_CALL(*recv_channel_proxy,
+                      AssociateSendChannel(testing::_)).Times(1);
+        }
+        return channel_proxy;
+      }));
+
+  AudioSendStream::Config send_config(nullptr);
+  send_config.rtp.ssrc = 777;
+  send_config.voe_channel_id = 123;
+  AudioSendStream* send_stream = call->CreateAudioSendStream(send_config);
+  EXPECT_NE(send_stream, nullptr);
+
+  AudioReceiveStream::Config recv_config;
+  recv_config.rtp.remote_ssrc = 42;
+  recv_config.rtp.local_ssrc = 777;
+  recv_config.voe_channel_id = kRecvChannelId;
+  recv_config.decoder_factory = decoder_factory;
+  AudioReceiveStream* recv_stream = call->CreateAudioReceiveStream(recv_config);
+  EXPECT_NE(recv_stream, nullptr);
+
+  EXPECT_CALL(*recv_channel_proxy, DisassociateSendChannel()).Times(1);
+  call->DestroyAudioReceiveStream(recv_stream);
+
+  call->DestroyAudioSendStream(send_stream);
+}
+
 TEST(CallTest, CreateDestroy_FlexfecReceiveStream) {
   CallHelper call;
   FlexfecReceiveStream::Config config;
diff --git a/webrtc/media/engine/fakewebrtcvoiceengine.h b/webrtc/media/engine/fakewebrtcvoiceengine.h
index 2641904..cd6c420 100644
--- a/webrtc/media/engine/fakewebrtcvoiceengine.h
+++ b/webrtc/media/engine/fakewebrtcvoiceengine.h
@@ -57,7 +57,6 @@
       public webrtc::VoEVolumeControl {
  public:
   struct Channel {
-    int associate_send_channel = -1;
     std::vector<webrtc::CodecInst> recv_codecs;
     size_t neteq_capacity = 0;
     bool neteq_fast_accelerate = false;
@@ -79,10 +78,6 @@
     fail_create_channel_ = fail_create_channel;
   }
 
-  int GetAssociateSendChannel(int channel) {
-    return channels_[channel]->associate_send_channel;
-  }
-
   WEBRTC_STUB(Release, ());
 
   // webrtc::VoEBase
@@ -125,11 +120,6 @@
   }
   WEBRTC_FUNC(DeleteChannel, (int channel)) {
     WEBRTC_CHECK_CHANNEL(channel);
-    for (const auto& ch : channels_) {
-      if (ch.second->associate_send_channel == channel) {
-        ch.second->associate_send_channel = -1;
-      }
-    }
     delete channels_[channel];
     channels_.erase(channel);
     return 0;
@@ -142,12 +132,8 @@
   WEBRTC_STUB(StopSend, (int channel));
   WEBRTC_STUB(GetVersion, (char version[1024]));
   WEBRTC_STUB(LastError, ());
-  WEBRTC_FUNC(AssociateSendChannel, (int channel,
-                                     int accociate_send_channel)) {
-    WEBRTC_CHECK_CHANNEL(channel);
-    channels_[channel]->associate_send_channel = accociate_send_channel;
-    return 0;
-  }
+  WEBRTC_STUB(AssociateSendChannel, (int channel,
+                                     int accociate_send_channel));
 
   // webrtc::VoECodec
   WEBRTC_STUB(NumOfCodecs, ());
diff --git a/webrtc/media/engine/webrtcvoiceengine.cc b/webrtc/media/engine/webrtcvoiceengine.cc
index a4768db..35629da 100644
--- a/webrtc/media/engine/webrtcvoiceengine.cc
+++ b/webrtc/media/engine/webrtcvoiceengine.cc
@@ -2085,10 +2085,6 @@
       // TODO(solenberg): Allow applications to set the RTCP SSRC of receive
       // streams instead, so we can avoid recreating the streams here.
       kv.second->RecreateAudioReceiveStream(ssrc);
-      int recv_channel = kv.second->channel();
-      engine()->voe()->base()->AssociateSendChannel(recv_channel, channel);
-      LOG(LS_INFO) << "VoiceEngine channel #" << recv_channel
-                   << " is associated with channel #" << channel << ".";
     }
   }
 
@@ -2110,6 +2106,10 @@
 
   it->second->SetSend(false);
 
+  // TODO(solenberg): If we're removing the receiver_reports_ssrc_ stream, find
+  // the first active send stream and use that instead, reassociating receive
+  // streams.
+
   // Clean up and delete the send stream+channel.
   int channel = it->second->channel();
   LOG(LS_INFO) << "Removing audio send stream " << ssrc
@@ -2182,15 +2182,6 @@
     }
   }
 
-  const int send_channel = GetSendChannelId(receiver_reports_ssrc_);
-  if (send_channel != -1) {
-    // Associate receive channel with first send channel (so the receive channel
-    // can obtain RTT from the send channel)
-    engine()->voe()->base()->AssociateSendChannel(channel, send_channel);
-    LOG(LS_INFO) << "VoiceEngine channel #" << channel
-                 << " is associated with channel #" << send_channel << ".";
-  }
-
   recv_streams_.insert(std::make_pair(
       ssrc, new WebRtcAudioReceiveStream(channel, ssrc, receiver_reports_ssrc_,
                                          recv_transport_cc_enabled_,
diff --git a/webrtc/media/engine/webrtcvoiceengine_unittest.cc b/webrtc/media/engine/webrtcvoiceengine_unittest.cc
index 93300e4..6f0ff63 100644
--- a/webrtc/media/engine/webrtcvoiceengine_unittest.cc
+++ b/webrtc/media/engine/webrtcvoiceengine_unittest.cc
@@ -50,6 +50,7 @@
 const uint32_t kSsrc1 = 0x99;
 const uint32_t kSsrc2 = 2;
 const uint32_t kSsrc3 = 3;
+const uint32_t kSsrc4 = 0x42;
 const uint32_t kSsrcs4[] = { 1, 2, 3, 4 };
 
 constexpr int kRtpHistoryMs = 5000;
@@ -3239,42 +3240,29 @@
 
 // All receive channels should be associated with the first send channel,
 // since they do not send RTCP SR.
-TEST_F(WebRtcVoiceEngineTestFake, AssociateFirstSendChannel) {
+TEST_F(WebRtcVoiceEngineTestFake, AssociateFirstSendChannel_SendCreatedFirst) {
   EXPECT_TRUE(SetupSendStream());
-  SetSendParameters(send_parameters_);
-  int default_channel = voe_.GetLastChannel();
-  EXPECT_TRUE(AddRecvStream(1));
-  int recv_ch = voe_.GetLastChannel();
-  EXPECT_NE(recv_ch, default_channel);
-  EXPECT_EQ(voe_.GetAssociateSendChannel(recv_ch), default_channel);
-  EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(2)));
-  EXPECT_EQ(voe_.GetAssociateSendChannel(recv_ch), default_channel);
-  EXPECT_TRUE(AddRecvStream(3));
-  recv_ch = voe_.GetLastChannel();
-  EXPECT_NE(recv_ch, default_channel);
-  EXPECT_EQ(voe_.GetAssociateSendChannel(recv_ch), default_channel);
+  EXPECT_TRUE(AddRecvStream(kSsrc2));
+  EXPECT_EQ(kSsrc1, GetRecvStreamConfig(kSsrc2).rtp.local_ssrc);
+  EXPECT_TRUE(channel_->AddSendStream(
+      cricket::StreamParams::CreateLegacy(kSsrc3)));
+  EXPECT_EQ(kSsrc1, GetRecvStreamConfig(kSsrc2).rtp.local_ssrc);
+  EXPECT_TRUE(AddRecvStream(kSsrc4));
+  EXPECT_EQ(kSsrc1, GetRecvStreamConfig(kSsrc4).rtp.local_ssrc);
 }
 
-TEST_F(WebRtcVoiceEngineTestFake, AssociateChannelResetUponDeleteChannnel) {
-  EXPECT_TRUE(SetupSendStream());
-  SetSendParameters(send_parameters_);
-
-  EXPECT_TRUE(AddRecvStream(1));
-  int recv_ch = voe_.GetLastChannel();
-
-  EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(2)));
-  int send_ch = voe_.GetLastChannel();
-
-  // Manually associate |recv_ch| to |send_ch|. This test is to verify a
-  // deleting logic, i.e., deleting |send_ch| will reset the associate send
-  // channel of |recv_ch|.This is not a common case, since, normally, only the
-  // default channel can be associated. However, the default is not deletable.
-  // So we force the |recv_ch| to associate with a non-default channel.
-  EXPECT_EQ(0, voe_.AssociateSendChannel(recv_ch, send_ch));
-  EXPECT_EQ(voe_.GetAssociateSendChannel(recv_ch), send_ch);
-
-  EXPECT_TRUE(channel_->RemoveSendStream(2));
-  EXPECT_EQ(voe_.GetAssociateSendChannel(recv_ch), -1);
+TEST_F(WebRtcVoiceEngineTestFake, AssociateFirstSendChannel_RecvCreatedFirst) {
+  EXPECT_TRUE(SetupRecvStream());
+  EXPECT_EQ(0xFA17FA17u, GetRecvStreamConfig(kSsrc1).rtp.local_ssrc);
+  EXPECT_TRUE(channel_->AddSendStream(
+      cricket::StreamParams::CreateLegacy(kSsrc2)));
+  EXPECT_EQ(kSsrc2, GetRecvStreamConfig(kSsrc1).rtp.local_ssrc);
+  EXPECT_TRUE(AddRecvStream(kSsrc3));
+  EXPECT_EQ(kSsrc2, GetRecvStreamConfig(kSsrc3).rtp.local_ssrc);
+  EXPECT_TRUE(channel_->AddSendStream(
+      cricket::StreamParams::CreateLegacy(kSsrc4)));
+  EXPECT_EQ(kSsrc2, GetRecvStreamConfig(kSsrc1).rtp.local_ssrc);
+  EXPECT_EQ(kSsrc2, GetRecvStreamConfig(kSsrc3).rtp.local_ssrc);
 }
 
 TEST_F(WebRtcVoiceEngineTestFake, SetRawAudioSink) {
diff --git a/webrtc/test/mock_voe_channel_proxy.h b/webrtc/test/mock_voe_channel_proxy.h
index 1f4b14f..aea30e8 100644
--- a/webrtc/test/mock_voe_channel_proxy.h
+++ b/webrtc/test/mock_voe_channel_proxy.h
@@ -44,9 +44,10 @@
   MOCK_CONST_METHOD0(GetDelayEstimate, uint32_t());
   MOCK_METHOD1(SetSendTelephoneEventPayloadType, bool(int payload_type));
   MOCK_METHOD2(SendTelephoneEventOutband, bool(int event, int duration_ms));
-  MOCK_METHOD1(SetInputMute, void(bool muted));
+  MOCK_METHOD1(SetBitrate, void(int bitrate_bps));
   // TODO(solenberg): Talk the compiler into accepting this mock method:
   // MOCK_METHOD1(SetSink, void(std::unique_ptr<AudioSinkInterface> sink));
+  MOCK_METHOD1(SetInputMute, void(bool muted));
   MOCK_METHOD1(RegisterExternalTransport, void(Transport* transport));
   MOCK_METHOD0(DeRegisterExternalTransport, void());
   MOCK_METHOD3(ReceivedRTPPacket, bool(const uint8_t* packet,
@@ -57,13 +58,19 @@
                      const rtc::scoped_refptr<AudioDecoderFactory>&());
   MOCK_METHOD1(SetChannelOutputVolumeScaling, void(float scaling));
   MOCK_METHOD1(SetRtcEventLog, void(RtcEventLog* event_log));
-  MOCK_METHOD1(SetTransportOverhead, void(int transport_overhead_per_packet));
-  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));
+  MOCK_METHOD2(GetAudioFrameWithInfo,
+      AudioMixer::Source::AudioFrameInfo(int sample_rate_hz,
+                                         AudioFrame* audio_frame));
+  MOCK_CONST_METHOD0(NeededFrequency, int());
+  MOCK_METHOD1(SetTransportOverhead, void(int transport_overhead_per_packet));
+  MOCK_METHOD1(AssociateSendChannel,
+               void(const ChannelProxy& send_channel_proxy));
+  MOCK_METHOD0(DisassociateSendChannel, void());
 };
 }  // namespace test
 }  // namespace webrtc
diff --git a/webrtc/test/mock_voice_engine.h b/webrtc/test/mock_voice_engine.h
index 85d27bd..84c1f5a 100644
--- a/webrtc/test/mock_voice_engine.h
+++ b/webrtc/test/mock_voice_engine.h
@@ -36,7 +36,7 @@
     ++_ref_count;
     // We add this default behavior to make the mock easier to use in tests. It
     // will create a NiceMock of a voe::ChannelProxy.
-    // TODO(ossu): As long as AudioReceiveStream is implmented as a wrapper
+    // TODO(ossu): As long as AudioReceiveStream is implemented as a wrapper
     // around Channel, we need to make sure ChannelProxy returns the same
     // decoder factory as the one passed in when creating an AudioReceiveStream.
     ON_CALL(*this, ChannelProxyFactory(testing::_))
@@ -48,7 +48,7 @@
           return proxy;
         }));
   }
-  ~MockVoiceEngine() /* override */ {
+  virtual ~MockVoiceEngine() /* override */ {
     // Decrease ref count before base class d-tor is called; otherwise it will
     // trigger an assertion.
     --_ref_count;
@@ -57,7 +57,7 @@
   MOCK_METHOD1(ChannelProxyFactory, voe::ChannelProxy*(int channel_id));
 
   // VoiceEngineImpl
-  std::unique_ptr<voe::ChannelProxy> GetChannelProxy(
+  virtual std::unique_ptr<voe::ChannelProxy> GetChannelProxy(
       int channel_id) /* override */ {
     return std::unique_ptr<voe::ChannelProxy>(ChannelProxyFactory(channel_id));
   }
diff --git a/webrtc/voice_engine/channel.cc b/webrtc/voice_engine/channel.cc
index 66f14bd..6a30fbc 100644
--- a/webrtc/voice_engine/channel.cc
+++ b/webrtc/voice_engine/channel.cc
@@ -2813,6 +2813,13 @@
   return 0;
 }
 
+void Channel::set_associate_send_channel(const ChannelOwner& channel) {
+  RTC_DCHECK(!channel.channel() ||
+             channel.channel()->ChannelId() != _channelId);
+  rtc::CritScope lock(&assoc_send_channel_lock_);
+  associate_send_channel_ = channel;
+}
+
 void Channel::DisassociateSendChannel(int channel_id) {
   rtc::CritScope lock(&assoc_send_channel_lock_);
   Channel* channel = associate_send_channel_.channel();
diff --git a/webrtc/voice_engine/channel.h b/webrtc/voice_engine/channel.h
index 4bfcd74..da020e6 100644
--- a/webrtc/voice_engine/channel.h
+++ b/webrtc/voice_engine/channel.h
@@ -405,12 +405,7 @@
 
   // Associate to a send channel.
   // Used for obtaining RTT for a receive-only channel.
-  void set_associate_send_channel(const ChannelOwner& channel) {
-    assert(_channelId != channel.channel()->ChannelId());
-    rtc::CritScope lock(&assoc_send_channel_lock_);
-    associate_send_channel_ = channel;
-  }
-
+  void set_associate_send_channel(const ChannelOwner& channel);
   // Disassociate a send channel if it was associated.
   void DisassociateSendChannel(int channel_id);
 
diff --git a/webrtc/voice_engine/channel_proxy.cc b/webrtc/voice_engine/channel_proxy.cc
index 1f9d7f3..bfd5b17 100644
--- a/webrtc/voice_engine/channel_proxy.cc
+++ b/webrtc/voice_engine/channel_proxy.cc
@@ -236,6 +236,17 @@
   channel()->SetTransportOverhead(transport_overhead_per_packet);
 }
 
+void ChannelProxy::AssociateSendChannel(
+    const ChannelProxy& send_channel_proxy) {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  channel()->set_associate_send_channel(send_channel_proxy.channel_owner_);
+}
+
+void ChannelProxy::DisassociateSendChannel() {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  channel()->set_associate_send_channel(ChannelOwner(nullptr));
+}
+
 Channel* ChannelProxy::channel() const {
   RTC_DCHECK(channel_owner_.channel());
   return channel_owner_.channel();
diff --git a/webrtc/voice_engine/channel_proxy.h b/webrtc/voice_engine/channel_proxy.h
index 44b25fd..182e252 100644
--- a/webrtc/voice_engine/channel_proxy.h
+++ b/webrtc/voice_engine/channel_proxy.h
@@ -91,10 +91,10 @@
   virtual AudioMixer::Source::AudioFrameInfo GetAudioFrameWithInfo(
       int sample_rate_hz,
       AudioFrame* audio_frame);
-
   virtual int NeededFrequency() const;
-
   virtual void SetTransportOverhead(int transport_overhead_per_packet);
+  virtual void AssociateSendChannel(const ChannelProxy& send_channel_proxy);
+  virtual void DisassociateSendChannel();
 
  private:
   Channel* channel() const;