Split WebRtcVoiceChannel into Send and Receive classes

No-Try: true
Bug: webrtc:13931
Change-Id: I947879aeef244e721546f765b64b9a8f1544409a
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/307740
Reviewed-by: Tomas Gunnarsson <tommi@webrtc.org>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40220}
diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc
index 4c7abed..c09e137 100644
--- a/media/engine/webrtc_voice_engine.cc
+++ b/media/engine/webrtc_voice_engine.cc
@@ -444,15 +444,13 @@
   std::unique_ptr<VoiceMediaSendChannelInterface> send_channel;
   std::unique_ptr<VoiceMediaReceiveChannelInterface> receive_channel;
   if (role == MediaChannel::Role::kSend || role == MediaChannel::Role::kBoth) {
-    send_channel = std::make_unique<WebRtcVoiceMediaChannel>(
-        MediaChannel::Role::kSend, this, config, options, crypto_options, call,
-        codec_pair_id);
+    send_channel = std::make_unique<WebRtcVoiceSendChannel>(
+        this, config, options, crypto_options, call, codec_pair_id);
   }
   if (role == MediaChannel::Role::kReceive ||
       role == MediaChannel::Role::kBoth) {
-    receive_channel = std::make_unique<WebRtcVoiceMediaChannel>(
-        MediaChannel::Role::kReceive, this, config, options, crypto_options,
-        call, codec_pair_id);
+    receive_channel = std::make_unique<WebRtcVoiceReceiveChannel>(
+        this, config, options, crypto_options, call, codec_pair_id);
   }
   return new VoiceMediaShimChannel(std::move(send_channel),
                                    std::move(receive_channel));
@@ -766,8 +764,7 @@
   return out;
 }
 
-class WebRtcVoiceMediaChannel::WebRtcAudioSendStream
-    : public AudioSource::Sink {
+class WebRtcVoiceSendChannel::WebRtcAudioSendStream : public AudioSource::Sink {
  public:
   WebRtcAudioSendStream(
       uint32_t ssrc,
@@ -1179,7 +1176,7 @@
   std::atomic<int> num_encoded_channels_{-1};
 };
 
-class WebRtcVoiceMediaChannel::WebRtcAudioReceiveStream {
+class WebRtcVoiceReceiveChannel::WebRtcAudioReceiveStream {
  public:
   WebRtcAudioReceiveStream(webrtc::AudioReceiveStreamInterface::Config config,
                            webrtc::Call* call)
@@ -1288,41 +1285,68 @@
       RTC_GUARDED_BY(worker_thread_checker_);
 };
 
-WebRtcVoiceMediaChannel::WebRtcVoiceMediaChannel(
-    MediaChannel::Role role,
+WebRtcVoiceSendChannel::WebRtcVoiceSendChannel(
     WebRtcVoiceEngine* engine,
     const MediaConfig& config,
     const AudioOptions& options,
     const webrtc::CryptoOptions& crypto_options,
     webrtc::Call* call,
     webrtc::AudioCodecPairId codec_pair_id)
-    : VoiceMediaChannel(role, call->network_thread(), config.enable_dscp),
+    : MediaChannelUtil(call->network_thread(), config.enable_dscp),
       worker_thread_(call->worker_thread()),
       engine_(engine),
       call_(call),
       audio_config_(config.audio),
       codec_pair_id_(codec_pair_id),
       crypto_options_(crypto_options) {
-  RTC_LOG(LS_VERBOSE) << "WebRtcVoiceMediaChannel::WebRtcVoiceMediaChannel";
+  RTC_LOG(LS_VERBOSE) << "WebRtcVoiceSendChannel::WebRtcVoiceSendChannel";
   RTC_DCHECK(call);
   SetOptions(options);
 }
 
-WebRtcVoiceMediaChannel::~WebRtcVoiceMediaChannel() {
+WebRtcVoiceReceiveChannel::WebRtcVoiceReceiveChannel(
+    WebRtcVoiceEngine* engine,
+    const MediaConfig& config,
+    const AudioOptions& options,
+    const webrtc::CryptoOptions& crypto_options,
+    webrtc::Call* call,
+    webrtc::AudioCodecPairId codec_pair_id)
+    : MediaChannelUtil(call->network_thread(), config.enable_dscp),
+      worker_thread_(call->worker_thread()),
+      engine_(engine),
+      call_(call),
+      audio_config_(config.audio),
+      codec_pair_id_(codec_pair_id),
+      crypto_options_(crypto_options) {
+  RTC_LOG(LS_VERBOSE) << "WebRtcVoiceReceiveChannel::WebRtcVoiceReceiveChannel";
+  RTC_DCHECK(call);
+  SetOptions(options);
+}
+
+WebRtcVoiceSendChannel::~WebRtcVoiceSendChannel() {
   RTC_DCHECK_RUN_ON(worker_thread_);
-  RTC_LOG(LS_VERBOSE) << "WebRtcVoiceMediaChannel::~WebRtcVoiceMediaChannel";
+  RTC_DLOG(LS_VERBOSE) << "WebRtcVoiceSendChannel::~WebRtcVoiceSendChannel";
   // TODO(solenberg): Should be able to delete the streams directly, without
   //                  going through RemoveNnStream(), once stream objects handle
   //                  all (de)configuration.
   while (!send_streams_.empty()) {
     RemoveSendStream(send_streams_.begin()->first);
   }
+}
+
+WebRtcVoiceReceiveChannel::~WebRtcVoiceReceiveChannel() {
+  RTC_DCHECK_RUN_ON(worker_thread_);
+  RTC_DLOG(LS_VERBOSE)
+      << "WebRtcVoiceReceiveChannel::~WebRtcVoiceReceiveChannel";
+  // TODO(solenberg): Should be able to delete the streams directly, without
+  //                  going through RemoveNnStream(), once stream objects handle
+  //                  all (de)configuration.
   while (!recv_streams_.empty()) {
     RemoveRecvStream(recv_streams_.begin()->first);
   }
 }
 
-bool WebRtcVoiceMediaChannel::SetSendParameters(
+bool WebRtcVoiceSendChannel::SetSendParameters(
     const AudioSendParameters& params) {
   TRACE_EVENT0("webrtc", "WebRtcVoiceMediaChannel::SetSendParameters");
   RTC_DCHECK_RUN_ON(worker_thread_);
@@ -1368,7 +1392,7 @@
   return SetOptions(params.options);
 }
 
-bool WebRtcVoiceMediaChannel::SetRecvParameters(
+bool WebRtcVoiceReceiveChannel::SetRecvParameters(
     const AudioRecvParameters& params) {
   TRACE_EVENT0("webrtc", "WebRtcVoiceMediaChannel::SetRecvParameters");
   RTC_DCHECK_RUN_ON(worker_thread_);
@@ -1395,7 +1419,7 @@
   return true;
 }
 
-webrtc::RtpParameters WebRtcVoiceMediaChannel::GetRtpSendParameters(
+webrtc::RtpParameters WebRtcVoiceSendChannel::GetRtpSendParameters(
     uint32_t ssrc) const {
   RTC_DCHECK_RUN_ON(worker_thread_);
   auto it = send_streams_.find(ssrc);
@@ -1415,7 +1439,7 @@
   return rtp_params;
 }
 
-webrtc::RTCError WebRtcVoiceMediaChannel::SetRtpSendParameters(
+webrtc::RTCError WebRtcVoiceSendChannel::SetRtpSendParameters(
     uint32_t ssrc,
     const webrtc::RtpParameters& parameters,
     webrtc::SetParametersCallback callback) {
@@ -1474,7 +1498,7 @@
   return it->second->SetRtpParameters(reduced_params, std::move(callback));
 }
 
-webrtc::RtpParameters WebRtcVoiceMediaChannel::GetRtpReceiveParameters(
+webrtc::RtpParameters WebRtcVoiceReceiveChannel::GetRtpReceiveParameters(
     uint32_t ssrc) const {
   RTC_DCHECK_RUN_ON(worker_thread_);
   webrtc::RtpParameters rtp_params;
@@ -1496,8 +1520,8 @@
   return rtp_params;
 }
 
-webrtc::RtpParameters WebRtcVoiceMediaChannel::GetDefaultRtpReceiveParameters()
-    const {
+webrtc::RtpParameters
+WebRtcVoiceReceiveChannel::GetDefaultRtpReceiveParameters() const {
   RTC_DCHECK_RUN_ON(worker_thread_);
   webrtc::RtpParameters rtp_params;
   if (!default_sink_) {
@@ -1514,7 +1538,8 @@
   return rtp_params;
 }
 
-bool WebRtcVoiceMediaChannel::SetOptions(const AudioOptions& options) {
+// TODO(hta): Consider if send and receive SetOptions can be merged.
+bool WebRtcVoiceSendChannel::SetOptions(const AudioOptions& options) {
   RTC_DCHECK_RUN_ON(worker_thread_);
   RTC_LOG(LS_INFO) << "Setting voice channel options: " << options.ToString();
 
@@ -1530,12 +1555,27 @@
     it.second->SetAudioNetworkAdaptorConfig(audio_network_adaptor_config);
   }
 
-  RTC_LOG(LS_INFO) << "Set voice channel options. Current options: "
+  RTC_LOG(LS_INFO) << "Set voice send channel options. Current options: "
                    << options_.ToString();
   return true;
 }
 
-bool WebRtcVoiceMediaChannel::SetRecvCodecs(
+bool WebRtcVoiceReceiveChannel::SetOptions(const AudioOptions& options) {
+  RTC_DCHECK_RUN_ON(worker_thread_);
+  RTC_LOG(LS_INFO) << "Setting voice channel options: " << options.ToString();
+
+  // We retain all of the existing options, and apply the given ones
+  // on top.  This means there is no way to "clear" options such that
+  // they go back to the engine default.
+  options_.SetAll(options);
+  engine()->ApplyOptions(options_);
+
+  RTC_LOG(LS_INFO) << "Set voice receive channel options. Current options: "
+                   << options_.ToString();
+  return true;
+}
+
+bool WebRtcVoiceReceiveChannel::SetRecvCodecs(
     const std::vector<AudioCodec>& codecs) {
   RTC_DCHECK_RUN_ON(worker_thread_);
 
@@ -1647,7 +1687,7 @@
 // Utility function called from SetSendParameters() to extract current send
 // codec settings from the given list of codecs (originally from SDP). Both send
 // and receive streams may be reconfigured based on the new settings.
-bool WebRtcVoiceMediaChannel::SetSendCodecs(
+bool WebRtcVoiceSendChannel::SetSendCodecs(
     const std::vector<AudioCodec>& codecs) {
   RTC_DCHECK_RUN_ON(worker_thread_);
   dtmf_payload_type_ = absl::nullopt;
@@ -1775,20 +1815,15 @@
   call_->GetTransportControllerSend()->SetSdpBitrateParameters(bitrate_config);
 
   send_codecs_ = codecs;
-  // In legacy kBoth mode, the MediaChannel sets the NACK status.
-  // In other modes, this is done externally.
 
-  if (role() == MediaChannel::Role::kBoth) {
-    SetReceiveNackEnabled(send_codec_spec_->nack_enabled);
-    SetReceiveNonSenderRttEnabled(send_codec_spec_->enable_non_sender_rtt);
-  } else if (send_codec_changed_callback_) {
+  if (send_codec_changed_callback_) {
     send_codec_changed_callback_();
   }
 
   return true;
 }
 
-void WebRtcVoiceMediaChannel::SetReceiveNackEnabled(bool enabled) {
+void WebRtcVoiceReceiveChannel::SetReceiveNackEnabled(bool enabled) {
   // Check if the NACK status has changed on the
   // preferred send codec, and in that case reconfigure all receive streams.
   if (recv_nack_enabled_ != enabled) {
@@ -1800,7 +1835,7 @@
   }
 }
 
-void WebRtcVoiceMediaChannel::SetReceiveNonSenderRttEnabled(bool enabled) {
+void WebRtcVoiceReceiveChannel::SetReceiveNonSenderRttEnabled(bool enabled) {
   // Check if the receive-side RTT status has changed on the preferred send
   // codec, in that case reconfigure all receive streams.
   if (enable_non_sender_rtt_ != enabled) {
@@ -1812,7 +1847,7 @@
   }
 }
 
-void WebRtcVoiceMediaChannel::SetPlayout(bool playout) {
+void WebRtcVoiceReceiveChannel::SetPlayout(bool playout) {
   TRACE_EVENT0("webrtc", "WebRtcVoiceMediaChannel::SetPlayout");
   RTC_DCHECK_RUN_ON(worker_thread_);
   if (playout_ == playout) {
@@ -1825,7 +1860,7 @@
   playout_ = playout;
 }
 
-void WebRtcVoiceMediaChannel::SetSend(bool send) {
+void WebRtcVoiceSendChannel::SetSend(bool send) {
   TRACE_EVENT0("webrtc", "WebRtcVoiceMediaChannel::SetSend");
   if (send_ == send) {
     return;
@@ -1855,10 +1890,10 @@
   send_ = send;
 }
 
-bool WebRtcVoiceMediaChannel::SetAudioSend(uint32_t ssrc,
-                                           bool enable,
-                                           const AudioOptions* options,
-                                           AudioSource* source) {
+bool WebRtcVoiceSendChannel::SetAudioSend(uint32_t ssrc,
+                                          bool enable,
+                                          const AudioOptions* options,
+                                          AudioSource* source) {
   RTC_DCHECK_RUN_ON(worker_thread_);
   // TODO(solenberg): The state change should be fully rolled back if any one of
   //                  these calls fail.
@@ -1874,12 +1909,10 @@
   return true;
 }
 
-bool WebRtcVoiceMediaChannel::AddSendStream(const StreamParams& sp) {
+bool WebRtcVoiceSendChannel::AddSendStream(const StreamParams& sp) {
   TRACE_EVENT0("webrtc", "WebRtcVoiceMediaChannel::AddSendStream");
   RTC_DCHECK_RUN_ON(worker_thread_);
   RTC_LOG(LS_INFO) << "AddSendStream: " << sp.ToString();
-  RTC_DCHECK(role() == MediaChannel::Role::kSend ||
-             role() == MediaChannel::Role::kBoth);
 
   uint32_t ssrc = sp.first_ssrc();
   RTC_DCHECK(0 != ssrc);
@@ -1898,21 +1931,7 @@
       call_, this, engine()->encoder_factory_, codec_pair_id_, nullptr,
       crypto_options_);
   send_streams_.insert(std::make_pair(ssrc, stream));
-  if (role() == MediaChannel::Role::kBoth) {
-    // In legacy kBoth mode, the MediaChannel takes the responsibility for
-    // telling the receiver about the local SSRC.
-    // In kSend mode, this happens via a callback.
-
-    // At this point the stream's local SSRC has been updated. If it is the
-    // first send stream, make sure that all the receive streams are updated
-    // with the same SSRC in order to send receiver reports.
-    if (send_streams_.size() == 1) {
-      receiver_reports_ssrc_ = ssrc;
-      for (auto& kv : recv_streams_) {
-        call_->OnLocalSsrcUpdated(kv.second->stream(), ssrc);
-      }
-    }
-  } else if (ssrc_list_changed_callback_) {
+  if (ssrc_list_changed_callback_) {
     std::set<uint32_t> ssrcs_in_use;
     for (auto it : send_streams_) {
       ssrcs_in_use.insert(it.first);
@@ -1924,7 +1943,7 @@
   return true;
 }
 
-bool WebRtcVoiceMediaChannel::RemoveSendStream(uint32_t ssrc) {
+bool WebRtcVoiceSendChannel::RemoveSendStream(uint32_t ssrc) {
   TRACE_EVENT0("webrtc", "WebRtcVoiceMediaChannel::RemoveSendStream");
   RTC_DCHECK_RUN_ON(worker_thread_);
   RTC_LOG(LS_INFO) << "RemoveSendStream: " << ssrc;
@@ -1950,11 +1969,9 @@
   return true;
 }
 
-bool WebRtcVoiceMediaChannel::AddRecvStream(const StreamParams& sp) {
+bool WebRtcVoiceReceiveChannel::AddRecvStream(const StreamParams& sp) {
   TRACE_EVENT0("webrtc", "WebRtcVoiceMediaChannel::AddRecvStream");
   RTC_DCHECK_RUN_ON(worker_thread_);
-  RTC_DCHECK(role() == MediaChannel::Role::kReceive ||
-             role() == MediaChannel::Role::kBoth);
   RTC_LOG(LS_INFO) << "AddRecvStream: " << sp.ToString();
 
   if (!sp.has_ssrcs()) {
@@ -2001,7 +2018,7 @@
   return true;
 }
 
-bool WebRtcVoiceMediaChannel::RemoveRecvStream(uint32_t ssrc) {
+bool WebRtcVoiceReceiveChannel::RemoveRecvStream(uint32_t ssrc) {
   TRACE_EVENT0("webrtc", "WebRtcVoiceMediaChannel::RemoveRecvStream");
   RTC_DCHECK_RUN_ON(worker_thread_);
   RTC_LOG(LS_INFO) << "RemoveRecvStream: " << ssrc;
@@ -2021,7 +2038,7 @@
   return true;
 }
 
-void WebRtcVoiceMediaChannel::ResetUnsignaledRecvStream() {
+void WebRtcVoiceReceiveChannel::ResetUnsignaledRecvStream() {
   RTC_DCHECK_RUN_ON(worker_thread_);
   RTC_LOG(LS_INFO) << "ResetUnsignaledRecvStream.";
   unsignaled_stream_params_ = StreamParams();
@@ -2032,7 +2049,7 @@
   }
 }
 
-absl::optional<uint32_t> WebRtcVoiceMediaChannel::GetUnsignaledSsrc() const {
+absl::optional<uint32_t> WebRtcVoiceReceiveChannel::GetUnsignaledSsrc() const {
   if (unsignaled_recv_ssrcs_.empty()) {
     return absl::nullopt;
   }
@@ -2041,12 +2058,12 @@
   return unsignaled_recv_ssrcs_.back();
 }
 
-void WebRtcVoiceMediaChannel::SetSsrcListChangedCallback(
+void WebRtcVoiceSendChannel::SetSsrcListChangedCallback(
     absl::AnyInvocable<void(const std::set<uint32_t>&)> callback) {
   ssrc_list_changed_callback_ = std::move(callback);
 }
 
-void WebRtcVoiceMediaChannel::ChooseReceiverReportSsrc(
+void WebRtcVoiceReceiveChannel::ChooseReceiverReportSsrc(
     const std::set<uint32_t>& choices) {
   // Don't change SSRC if set is empty. Note that this differs from
   // the behavior of video.
@@ -2067,11 +2084,11 @@
 // TODO(https://crbug.com/webrtc/12676): Implement a fix for the unsignalled
 // SSRC race that can happen when an m= section goes from receiving to not
 // receiving.
-void WebRtcVoiceMediaChannel::OnDemuxerCriteriaUpdatePending() {}
-void WebRtcVoiceMediaChannel::OnDemuxerCriteriaUpdateComplete() {}
+void WebRtcVoiceReceiveChannel::OnDemuxerCriteriaUpdatePending() {}
+void WebRtcVoiceReceiveChannel::OnDemuxerCriteriaUpdateComplete() {}
 
-bool WebRtcVoiceMediaChannel::SetLocalSource(uint32_t ssrc,
-                                             AudioSource* source) {
+bool WebRtcVoiceSendChannel::SetLocalSource(uint32_t ssrc,
+                                            AudioSource* source) {
   auto it = send_streams_.find(ssrc);
   if (it == send_streams_.end()) {
     if (source) {
@@ -2093,7 +2110,7 @@
   return true;
 }
 
-bool WebRtcVoiceMediaChannel::SetOutputVolume(uint32_t ssrc, double volume) {
+bool WebRtcVoiceReceiveChannel::SetOutputVolume(uint32_t ssrc, double volume) {
   RTC_DCHECK_RUN_ON(worker_thread_);
   RTC_LOG(LS_INFO) << rtc::StringFormat("WRVMC::%s({ssrc=%u}, {volume=%.2f})",
                                         __func__, ssrc, volume);
@@ -2111,7 +2128,7 @@
   return true;
 }
 
-bool WebRtcVoiceMediaChannel::SetDefaultOutputVolume(double volume) {
+bool WebRtcVoiceReceiveChannel::SetDefaultOutputVolume(double volume) {
   RTC_DCHECK_RUN_ON(worker_thread_);
   default_recv_volume_ = volume;
   for (uint32_t ssrc : unsignaled_recv_ssrcs_) {
@@ -2127,8 +2144,8 @@
   return true;
 }
 
-bool WebRtcVoiceMediaChannel::SetBaseMinimumPlayoutDelayMs(uint32_t ssrc,
-                                                           int delay_ms) {
+bool WebRtcVoiceReceiveChannel::SetBaseMinimumPlayoutDelayMs(uint32_t ssrc,
+                                                             int delay_ms) {
   RTC_DCHECK_RUN_ON(worker_thread_);
   std::vector<uint32_t> ssrcs(1, ssrc);
   // SSRC of 0 represents the default receive stream.
@@ -2150,7 +2167,7 @@
   return true;
 }
 
-absl::optional<int> WebRtcVoiceMediaChannel::GetBaseMinimumPlayoutDelayMs(
+absl::optional<int> WebRtcVoiceReceiveChannel::GetBaseMinimumPlayoutDelayMs(
     uint32_t ssrc) const {
   // SSRC of 0 represents the default receive stream.
   if (ssrc == 0) {
@@ -2165,11 +2182,11 @@
   return absl::nullopt;
 }
 
-bool WebRtcVoiceMediaChannel::CanInsertDtmf() {
+bool WebRtcVoiceSendChannel::CanInsertDtmf() {
   return dtmf_payload_type_.has_value() && send_;
 }
 
-void WebRtcVoiceMediaChannel::SetFrameDecryptor(
+void WebRtcVoiceReceiveChannel::SetFrameDecryptor(
     uint32_t ssrc,
     rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor) {
   RTC_DCHECK_RUN_ON(worker_thread_);
@@ -2183,7 +2200,7 @@
   }
 }
 
-void WebRtcVoiceMediaChannel::SetFrameEncryptor(
+void WebRtcVoiceSendChannel::SetFrameEncryptor(
     uint32_t ssrc,
     rtc::scoped_refptr<webrtc::FrameEncryptorInterface> frame_encryptor) {
   RTC_DCHECK_RUN_ON(worker_thread_);
@@ -2193,9 +2210,9 @@
   }
 }
 
-bool WebRtcVoiceMediaChannel::InsertDtmf(uint32_t ssrc,
-                                         int event,
-                                         int duration) {
+bool WebRtcVoiceSendChannel::InsertDtmf(uint32_t ssrc,
+                                        int event,
+                                        int duration) {
   RTC_DCHECK_RUN_ON(worker_thread_);
   RTC_LOG(LS_INFO) << "WebRtcVoiceMediaChannel::InsertDtmf";
   if (!CanInsertDtmf()) {
@@ -2217,7 +2234,7 @@
                                         event, duration);
 }
 
-void WebRtcVoiceMediaChannel::OnPacketReceived(
+void WebRtcVoiceReceiveChannel::OnPacketReceived(
     const webrtc::RtpPacketReceived& packet) {
   RTC_DCHECK_RUN_ON(&network_thread_checker_);
 
@@ -2245,12 +2262,12 @@
         call_->Receiver()->DeliverRtpPacket(
             webrtc::MediaType::AUDIO, std::move(packet),
             absl::bind_front(
-                &WebRtcVoiceMediaChannel::MaybeCreateDefaultReceiveStream,
+                &WebRtcVoiceReceiveChannel::MaybeCreateDefaultReceiveStream,
                 this));
       }));
 }
 
-bool WebRtcVoiceMediaChannel::MaybeCreateDefaultReceiveStream(
+bool WebRtcVoiceReceiveChannel::MaybeCreateDefaultReceiveStream(
     const webrtc::RtpPacketReceived& packet) {
   // Create an unsignaled receive stream for this previously not received
   // ssrc. If there already is N unsignaled receive streams, delete the
@@ -2297,7 +2314,7 @@
   return true;
 }
 
-void WebRtcVoiceMediaChannel::OnPacketSent(const rtc::SentPacket& sent_packet) {
+void WebRtcVoiceSendChannel::OnPacketSent(const rtc::SentPacket& sent_packet) {
   RTC_DCHECK_RUN_ON(&network_thread_checker_);
   // TODO(tommi): We shouldn't need to go through call_ to deliver this
   // notification. We should already have direct access to
@@ -2308,7 +2325,7 @@
   call_->OnSentPacket(sent_packet);
 }
 
-void WebRtcVoiceMediaChannel::OnNetworkRouteChanged(
+void WebRtcVoiceSendChannel::OnNetworkRouteChanged(
     absl::string_view transport_name,
     const rtc::NetworkRoute& network_route) {
   RTC_DCHECK_RUN_ON(&network_thread_checker_);
@@ -2323,7 +2340,7 @@
       }));
 }
 
-bool WebRtcVoiceMediaChannel::MuteStream(uint32_t ssrc, bool muted) {
+bool WebRtcVoiceSendChannel::MuteStream(uint32_t ssrc, bool muted) {
   RTC_DCHECK_RUN_ON(worker_thread_);
   const auto it = send_streams_.find(ssrc);
   if (it == send_streams_.end()) {
@@ -2349,7 +2366,7 @@
   return true;
 }
 
-bool WebRtcVoiceMediaChannel::SetMaxSendBitrate(int bps) {
+bool WebRtcVoiceSendChannel::SetMaxSendBitrate(int bps) {
   RTC_LOG(LS_INFO) << "WebRtcVoiceMediaChannel::SetMaxSendBitrate.";
   max_send_bitrate_bps_ = bps;
   bool success = true;
@@ -2361,7 +2378,7 @@
   return success;
 }
 
-void WebRtcVoiceMediaChannel::OnReadyToSend(bool ready) {
+void WebRtcVoiceSendChannel::OnReadyToSend(bool ready) {
   RTC_DCHECK_RUN_ON(&network_thread_checker_);
   RTC_LOG(LS_VERBOSE) << "OnReadyToSend: " << (ready ? "Ready." : "Not ready.");
   call_->SignalChannelNetworkState(
@@ -2369,7 +2386,7 @@
       ready ? webrtc::kNetworkUp : webrtc::kNetworkDown);
 }
 
-bool WebRtcVoiceMediaChannel::GetSendStats(VoiceMediaSendInfo* info) {
+bool WebRtcVoiceSendChannel::GetStats(VoiceMediaSendInfo* info) {
   TRACE_EVENT0("webrtc", "WebRtcVoiceMediaChannel::GetSendStats");
   RTC_DCHECK_RUN_ON(worker_thread_);
   RTC_DCHECK(info);
@@ -2380,8 +2397,7 @@
   // senders.
   RTC_DCHECK(info->senders.size() == 0U || send_streams_.size() == 0);
   for (const auto& stream : send_streams_) {
-    webrtc::AudioSendStream::Stats stats =
-        stream.second->GetStats(recv_streams_.size() > 0);
+    webrtc::AudioSendStream::Stats stats = stream.second->GetStats(false);
     VoiceSenderInfo sinfo;
     sinfo.add_ssrc(stats.local_ssrc);
     sinfo.payload_bytes_sent = stats.payload_bytes_sent;
@@ -2418,8 +2434,8 @@
   return true;
 }
 
-bool WebRtcVoiceMediaChannel::GetReceiveStats(VoiceMediaReceiveInfo* info,
-                                              bool get_and_clear_legacy_stats) {
+bool WebRtcVoiceReceiveChannel::GetStats(VoiceMediaReceiveInfo* info,
+                                         bool get_and_clear_legacy_stats) {
   TRACE_EVENT0("webrtc", "WebRtcVoiceMediaChannel::GetReceiveStats");
   RTC_DCHECK_RUN_ON(worker_thread_);
   RTC_DCHECK(info);
@@ -2529,7 +2545,7 @@
   return true;
 }
 
-void WebRtcVoiceMediaChannel::FillSendCodecStats(
+void WebRtcVoiceSendChannel::FillSendCodecStats(
     VoiceMediaSendInfo* voice_media_info) {
   for (const auto& sender : voice_media_info->senders) {
     auto codec = absl::c_find_if(send_codecs_, [&sender](const AudioCodec& c) {
@@ -2542,7 +2558,7 @@
   }
 }
 
-void WebRtcVoiceMediaChannel::FillReceiveCodecStats(
+void WebRtcVoiceReceiveChannel::FillReceiveCodecStats(
     VoiceMediaReceiveInfo* voice_media_info) {
   for (const auto& receiver : voice_media_info->receivers) {
     auto codec =
@@ -2557,7 +2573,7 @@
   }
 }
 
-void WebRtcVoiceMediaChannel::SetRawAudioSink(
+void WebRtcVoiceReceiveChannel::SetRawAudioSink(
     uint32_t ssrc,
     std::unique_ptr<webrtc::AudioSinkInterface> sink) {
   RTC_DCHECK_RUN_ON(worker_thread_);
@@ -2571,7 +2587,7 @@
   it->second->SetRawAudioSink(std::move(sink));
 }
 
-void WebRtcVoiceMediaChannel::SetDefaultRawAudioSink(
+void WebRtcVoiceReceiveChannel::SetDefaultRawAudioSink(
     std::unique_ptr<webrtc::AudioSinkInterface> sink) {
   RTC_DCHECK_RUN_ON(worker_thread_);
   RTC_LOG(LS_VERBOSE) << "WebRtcVoiceMediaChannel::SetDefaultRawAudioSink:";
@@ -2583,7 +2599,7 @@
   default_sink_ = std::move(sink);
 }
 
-std::vector<webrtc::RtpSource> WebRtcVoiceMediaChannel::GetSources(
+std::vector<webrtc::RtpSource> WebRtcVoiceReceiveChannel::GetSources(
     uint32_t ssrc) const {
   auto it = recv_streams_.find(ssrc);
   if (it == recv_streams_.end()) {
@@ -2594,7 +2610,7 @@
   return it->second->GetSources();
 }
 
-void WebRtcVoiceMediaChannel::SetEncoderToPacketizerFrameTransformer(
+void WebRtcVoiceSendChannel::SetEncoderToPacketizerFrameTransformer(
     uint32_t ssrc,
     rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer) {
   RTC_DCHECK_RUN_ON(worker_thread_);
@@ -2608,7 +2624,7 @@
       std::move(frame_transformer));
 }
 
-void WebRtcVoiceMediaChannel::SetDepacketizerToDecoderFrameTransformer(
+void WebRtcVoiceReceiveChannel::SetDepacketizerToDecoderFrameTransformer(
     uint32_t ssrc,
     rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer) {
   RTC_DCHECK_RUN_ON(worker_thread_);
@@ -2629,19 +2645,31 @@
       std::move(frame_transformer));
 }
 
-bool WebRtcVoiceMediaChannel::SendRtp(const uint8_t* data,
-                                      size_t len,
-                                      const webrtc::PacketOptions& options) {
-  MediaChannel::SendRtp(data, len, options);
+bool WebRtcVoiceSendChannel::SendRtp(const uint8_t* data,
+                                     size_t len,
+                                     const webrtc::PacketOptions& options) {
+  MediaChannelUtil::SendRtp(data, len, options);
   return true;
 }
 
-bool WebRtcVoiceMediaChannel::SendRtcp(const uint8_t* data, size_t len) {
-  MediaChannel::SendRtcp(data, len);
+bool WebRtcVoiceSendChannel::SendRtcp(const uint8_t* data, size_t len) {
+  MediaChannelUtil::SendRtcp(data, len);
   return true;
 }
 
-bool WebRtcVoiceMediaChannel::MaybeDeregisterUnsignaledRecvStream(
+bool WebRtcVoiceReceiveChannel::SendRtp(const uint8_t* data,
+                                        size_t len,
+                                        const webrtc::PacketOptions& options) {
+  MediaChannelUtil::SendRtp(data, len, options);
+  return true;
+}
+
+bool WebRtcVoiceReceiveChannel::SendRtcp(const uint8_t* data, size_t len) {
+  MediaChannelUtil::SendRtcp(data, len);
+  return true;
+}
+
+bool WebRtcVoiceReceiveChannel::MaybeDeregisterUnsignaledRecvStream(
     uint32_t ssrc) {
   RTC_DCHECK_RUN_ON(worker_thread_);
   auto it = absl::c_find(unsignaled_recv_ssrcs_, ssrc);
diff --git a/media/engine/webrtc_voice_engine.h b/media/engine/webrtc_voice_engine.h
index 4e398f4..d7661a6 100644
--- a/media/engine/webrtc_voice_engine.h
+++ b/media/engine/webrtc_voice_engine.h
@@ -76,12 +76,12 @@
 namespace cricket {
 
 class AudioSource;
-class WebRtcVoiceMediaChannel;
 
 // WebRtcVoiceEngine is a class to be used with CompositeMediaEngine.
 // It uses the WebRtc VoiceEngine library for audio handling.
 class WebRtcVoiceEngine final : public VoiceEngineInterface {
-  friend class WebRtcVoiceMediaChannel;
+  friend class WebRtcVoiceSendChannel;
+  friend class WebRtcVoiceReceiveChannel;
 
  public:
   WebRtcVoiceEngine(
@@ -180,38 +180,54 @@
   const bool minimized_remsampling_on_mobile_trial_enabled_;
 };
 
-// WebRtcVoiceMediaChannel is an implementation of VoiceMediaChannel that uses
-// WebRtc Voice Engine.
-class WebRtcVoiceMediaChannel final : public VoiceMediaChannel,
-                                      public webrtc::Transport {
+class WebRtcVoiceSendChannel final : public MediaChannelUtil,
+                                     public VoiceMediaSendChannelInterface,
+                                     public webrtc::Transport {
  public:
-  WebRtcVoiceMediaChannel(MediaChannel::Role role,
-                          WebRtcVoiceEngine* engine,
-                          const MediaConfig& config,
-                          const AudioOptions& options,
-                          const webrtc::CryptoOptions& crypto_options,
-                          webrtc::Call* call,
-                          webrtc::AudioCodecPairId codec_pair_id);
+  WebRtcVoiceSendChannel(WebRtcVoiceEngine* engine,
+                         const MediaConfig& config,
+                         const AudioOptions& options,
+                         const webrtc::CryptoOptions& crypto_options,
+                         webrtc::Call* call,
+                         webrtc::AudioCodecPairId codec_pair_id);
 
-  WebRtcVoiceMediaChannel() = delete;
-  WebRtcVoiceMediaChannel(const WebRtcVoiceMediaChannel&) = delete;
-  WebRtcVoiceMediaChannel& operator=(const WebRtcVoiceMediaChannel&) = delete;
+  WebRtcVoiceSendChannel() = delete;
+  WebRtcVoiceSendChannel(const WebRtcVoiceSendChannel&) = delete;
+  WebRtcVoiceSendChannel& operator=(const WebRtcVoiceSendChannel&) = delete;
 
-  ~WebRtcVoiceMediaChannel() override;
+  ~WebRtcVoiceSendChannel() override;
+
+  MediaType media_type() const override { return MEDIA_TYPE_AUDIO; }
+  VideoMediaSendChannelInterface* AsVideoSendChannel() override {
+    RTC_CHECK_NOTREACHED();
+    return nullptr;
+  }
+  VoiceMediaSendChannelInterface* AsVoiceSendChannel() override { return this; }
+
+  // Functions imported from MediaChannelUtil
+  void SetInterface(MediaChannelNetworkInterface* iface) override {
+    MediaChannelUtil::SetInterface(iface);
+  }
+
+  bool HasNetworkInterface() const override {
+    return MediaChannelUtil::HasNetworkInterface();
+  }
+  void SetExtmapAllowMixed(bool extmap_allow_mixed) override {
+    MediaChannelUtil::SetExtmapAllowMixed(extmap_allow_mixed);
+  }
+  bool ExtmapAllowMixed() const override {
+    return MediaChannelUtil::ExtmapAllowMixed();
+  }
 
   const AudioOptions& options() const { return options_; }
 
   bool SetSendParameters(const AudioSendParameters& params) override;
-  bool SetRecvParameters(const AudioRecvParameters& params) override;
   webrtc::RtpParameters GetRtpSendParameters(uint32_t ssrc) const override;
   webrtc::RTCError SetRtpSendParameters(
       uint32_t ssrc,
       const webrtc::RtpParameters& parameters,
       webrtc::SetParametersCallback callback) override;
-  webrtc::RtpParameters GetRtpReceiveParameters(uint32_t ssrc) const override;
-  webrtc::RtpParameters GetDefaultRtpReceiveParameters() const override;
 
-  void SetPlayout(bool playout) override;
   void SetSend(bool send) override;
   bool SetAudioSend(uint32_t ssrc,
                     bool enable,
@@ -219,25 +235,11 @@
                     AudioSource* source) override;
   bool AddSendStream(const StreamParams& sp) override;
   bool RemoveSendStream(uint32_t ssrc) override;
-  bool AddRecvStream(const StreamParams& sp) override;
-  bool RemoveRecvStream(uint32_t ssrc) override;
-  void ResetUnsignaledRecvStream() override;
-  absl::optional<uint32_t> GetUnsignaledSsrc() const override;
 
-  void ChooseReceiverReportSsrc(const std::set<uint32_t>& choices) override;
   void SetSsrcListChangedCallback(
       absl::AnyInvocable<void(const std::set<uint32_t>&)> callback) override;
 
-  void OnDemuxerCriteriaUpdatePending() override;
-  void OnDemuxerCriteriaUpdateComplete() override;
-
   // E2EE Frame API
-  // Set a frame decryptor to a particular ssrc that will intercept all
-  // incoming audio payloads and attempt to decrypt them before forwarding the
-  // result.
-  void SetFrameDecryptor(uint32_t ssrc,
-                         rtc::scoped_refptr<webrtc::FrameDecryptorInterface>
-                             frame_decryptor) override;
   // Set a frame encryptor to a particular ssrc that will intercept all
   // outgoing audio payloads frames and attempt to encrypt them and forward the
   // result to the packetizer.
@@ -245,36 +247,14 @@
                          rtc::scoped_refptr<webrtc::FrameEncryptorInterface>
                              frame_encryptor) override;
 
-  bool SetOutputVolume(uint32_t ssrc, double volume) override;
-  // Applies the new volume to current and future unsignaled streams.
-  bool SetDefaultOutputVolume(double volume) override;
-
-  bool SetBaseMinimumPlayoutDelayMs(uint32_t ssrc, int delay_ms) override;
-  absl::optional<int> GetBaseMinimumPlayoutDelayMs(
-      uint32_t ssrc) const override;
-
   bool CanInsertDtmf() override;
   bool InsertDtmf(uint32_t ssrc, int event, int duration) override;
 
-  void OnPacketReceived(const webrtc::RtpPacketReceived& packet) override;
   void OnPacketSent(const rtc::SentPacket& sent_packet) override;
   void OnNetworkRouteChanged(absl::string_view transport_name,
                              const rtc::NetworkRoute& network_route) override;
   void OnReadyToSend(bool ready) override;
-  bool GetSendStats(VoiceMediaSendInfo* info) override;
-  bool GetReceiveStats(VoiceMediaReceiveInfo* info,
-                       bool get_and_clear_legacy_stats) override;
-
-  // Set the audio sink for an existing stream.
-  void SetRawAudioSink(
-      uint32_t ssrc,
-      std::unique_ptr<webrtc::AudioSinkInterface> sink) override;
-  // Will set the audio sink on the latest unsignaled stream, future or
-  // current. Only one stream at a time will use the sink.
-  void SetDefaultRawAudioSink(
-      std::unique_ptr<webrtc::AudioSinkInterface> sink) override;
-
-  std::vector<webrtc::RtpSource> GetSources(uint32_t ssrc) const override;
+  bool GetStats(VoiceMediaSendInfo* info) override;
 
   // Sets a frame transformer between encoder and packetizer, to transform
   // encoded frames before sending them out the network.
@@ -282,10 +262,6 @@
       uint32_t ssrc,
       rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
       override;
-  void SetDepacketizerToDecoderFrameTransformer(
-      uint32_t ssrc,
-      rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
-      override;
 
   // implements Transport interface
   bool SendRtp(const uint8_t* data,
@@ -308,16 +284,17 @@
   }
   bool SendCodecHasNack() const override { return SenderNackEnabled(); }
 
-  void SetReceiveNackEnabled(bool enabled) override;
-  void SetReceiveNonSenderRttEnabled(bool enabled) override;
   void SetSendCodecChangedCallback(
       absl::AnyInvocable<void()> callback) override {
     send_codec_changed_callback_ = std::move(callback);
   }
+  MediaChannel* ImplForTesting() override {
+    RTC_CHECK_NOTREACHED();
+    return nullptr;
+  }
 
  private:
   bool SetOptions(const AudioOptions& options);
-  bool SetRecvCodecs(const std::vector<AudioCodec>& codecs);
   bool SetSendCodecs(const std::vector<AudioCodec>& codecs);
   bool SetLocalSource(uint32_t ssrc, AudioSource* source);
   bool MuteStream(uint32_t ssrc, bool mute);
@@ -326,6 +303,161 @@
   bool SetMaxSendBitrate(int bps);
   void SetupRecording();
 
+  webrtc::TaskQueueBase* const worker_thread_;
+  webrtc::ScopedTaskSafety task_safety_;
+  webrtc::SequenceChecker network_thread_checker_{
+      webrtc::SequenceChecker::kDetached};
+
+  WebRtcVoiceEngine* const engine_ = nullptr;
+  std::vector<AudioCodec> send_codecs_;
+
+  int max_send_bitrate_bps_ = 0;
+  AudioOptions options_;
+  absl::optional<int> dtmf_payload_type_;
+  int dtmf_payload_freq_ = -1;
+  bool enable_non_sender_rtt_ = false;
+  bool send_ = false;
+  webrtc::Call* const call_ = nullptr;
+
+  const MediaConfig::Audio audio_config_;
+
+  class WebRtcAudioSendStream;
+
+  std::map<uint32_t, WebRtcAudioSendStream*> send_streams_;
+  std::vector<webrtc::RtpExtension> send_rtp_extensions_;
+  std::string mid_;
+
+  absl::optional<webrtc::AudioSendStream::Config::SendCodecSpec>
+      send_codec_spec_;
+
+  // TODO(kwiberg): Per-SSRC codec pair IDs?
+  const webrtc::AudioCodecPairId codec_pair_id_;
+
+  // Per peer connection crypto options that last for the lifetime of the peer
+  // connection.
+  const webrtc::CryptoOptions crypto_options_;
+  rtc::scoped_refptr<webrtc::FrameTransformerInterface>
+      unsignaled_frame_transformer_;
+
+  void FillSendCodecStats(VoiceMediaSendInfo* voice_media_info);
+
+  // Callback invoked whenever the send codec changes.
+  // TODO(bugs.webrtc.org/13931): Remove again when coupling isn't needed.
+  absl::AnyInvocable<void()> send_codec_changed_callback_;
+  // Callback invoked whenever the list of SSRCs changes.
+  absl::AnyInvocable<void(const std::set<uint32_t>&)>
+      ssrc_list_changed_callback_;
+};
+
+class WebRtcVoiceReceiveChannel final
+    : public MediaChannelUtil,
+      public VoiceMediaReceiveChannelInterface,
+      public webrtc::Transport {
+ public:
+  WebRtcVoiceReceiveChannel(WebRtcVoiceEngine* engine,
+                            const MediaConfig& config,
+                            const AudioOptions& options,
+                            const webrtc::CryptoOptions& crypto_options,
+                            webrtc::Call* call,
+                            webrtc::AudioCodecPairId codec_pair_id);
+
+  WebRtcVoiceReceiveChannel() = delete;
+  WebRtcVoiceReceiveChannel(const WebRtcVoiceReceiveChannel&) = delete;
+  WebRtcVoiceReceiveChannel& operator=(const WebRtcVoiceReceiveChannel&) =
+      delete;
+
+  ~WebRtcVoiceReceiveChannel() override;
+
+  MediaType media_type() const override { return MEDIA_TYPE_AUDIO; }
+
+  VideoMediaReceiveChannelInterface* AsVideoReceiveChannel() override {
+    RTC_CHECK_NOTREACHED();
+    return nullptr;
+  }
+  VoiceMediaReceiveChannelInterface* AsVoiceReceiveChannel() override {
+    return this;
+  }
+
+  const AudioOptions& options() const { return options_; }
+
+  void SetInterface(MediaChannelNetworkInterface* iface) override {
+    MediaChannelUtil::SetInterface(iface);
+  }
+  bool SetRecvParameters(const AudioRecvParameters& params) override;
+  webrtc::RtpParameters GetRtpReceiveParameters(uint32_t ssrc) const override;
+  webrtc::RtpParameters GetDefaultRtpReceiveParameters() const override;
+
+  void SetPlayout(bool playout) override;
+  bool AddRecvStream(const StreamParams& sp) override;
+  bool RemoveRecvStream(uint32_t ssrc) override;
+  void ResetUnsignaledRecvStream() override;
+  absl::optional<uint32_t> GetUnsignaledSsrc() const override;
+
+  void ChooseReceiverReportSsrc(const std::set<uint32_t>& choices) override;
+
+  void OnDemuxerCriteriaUpdatePending() override;
+  void OnDemuxerCriteriaUpdateComplete() override;
+
+  // E2EE Frame API
+  // Set a frame decryptor to a particular ssrc that will intercept all
+  // incoming audio payloads and attempt to decrypt them before forwarding the
+  // result.
+  void SetFrameDecryptor(uint32_t ssrc,
+                         rtc::scoped_refptr<webrtc::FrameDecryptorInterface>
+                             frame_decryptor) override;
+
+  bool SetOutputVolume(uint32_t ssrc, double volume) override;
+  // Applies the new volume to current and future unsignaled streams.
+  bool SetDefaultOutputVolume(double volume) override;
+
+  bool SetBaseMinimumPlayoutDelayMs(uint32_t ssrc, int delay_ms) override;
+  absl::optional<int> GetBaseMinimumPlayoutDelayMs(
+      uint32_t ssrc) const override;
+
+  void OnPacketReceived(const webrtc::RtpPacketReceived& packet) override;
+  bool GetStats(VoiceMediaReceiveInfo* info,
+                bool get_and_clear_legacy_stats) override;
+
+  // Set the audio sink for an existing stream.
+  void SetRawAudioSink(
+      uint32_t ssrc,
+      std::unique_ptr<webrtc::AudioSinkInterface> sink) override;
+  // Will set the audio sink on the latest unsignaled stream, future or
+  // current. Only one stream at a time will use the sink.
+  void SetDefaultRawAudioSink(
+      std::unique_ptr<webrtc::AudioSinkInterface> sink) override;
+
+  std::vector<webrtc::RtpSource> GetSources(uint32_t ssrc) const override;
+
+  void SetDepacketizerToDecoderFrameTransformer(
+      uint32_t ssrc,
+      rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
+      override;
+
+  // implements Transport interface
+  bool SendRtp(const uint8_t* data,
+               size_t len,
+               const webrtc::PacketOptions& options) override;
+
+  bool SendRtcp(const uint8_t* data, size_t len) override;
+
+  void SetReceiveNackEnabled(bool enabled) override;
+  void SetReceiveNonSenderRttEnabled(bool enabled) override;
+
+  MediaChannel* ImplForTesting() override {
+    RTC_CHECK_NOTREACHED();
+    return nullptr;
+  }
+
+ private:
+  bool SetOptions(const AudioOptions& options);
+  bool SetRecvCodecs(const std::vector<AudioCodec>& codecs);
+  bool SetLocalSource(uint32_t ssrc, AudioSource* source);
+  bool MuteStream(uint32_t ssrc, bool mute);
+
+  WebRtcVoiceEngine* engine() { return engine_; }
+  void SetupRecording();
+
   // Expected to be invoked once per packet that belongs to this channel that
   // can not be demuxed. Returns true if a default receive stream has been
   // created.
@@ -340,21 +472,16 @@
       webrtc::SequenceChecker::kDetached};
 
   WebRtcVoiceEngine* const engine_ = nullptr;
-  std::vector<AudioCodec> send_codecs_;
 
   // TODO(kwiberg): decoder_map_ and recv_codecs_ store the exact same
   // information, in slightly different formats. Eliminate recv_codecs_.
   std::map<int, webrtc::SdpAudioFormat> decoder_map_;
   std::vector<AudioCodec> recv_codecs_;
 
-  int max_send_bitrate_bps_ = 0;
   AudioOptions options_;
-  absl::optional<int> dtmf_payload_type_;
-  int dtmf_payload_freq_ = -1;
   bool recv_nack_enabled_ = false;
   bool enable_non_sender_rtt_ = false;
   bool playout_ = false;
-  bool send_ = false;
   webrtc::Call* const call_ = nullptr;
 
   const MediaConfig::Audio audio_config_;
@@ -381,10 +508,6 @@
   // and https://code.google.com/p/chromium/issues/detail?id=547661
   uint32_t receiver_reports_ssrc_ = 0xFA17FA17u;
 
-  class WebRtcAudioSendStream;
-
-  std::map<uint32_t, WebRtcAudioSendStream*> send_streams_;
-  std::vector<webrtc::RtpExtension> send_rtp_extensions_;
   std::string mid_;
 
   class WebRtcAudioReceiveStream;
@@ -408,15 +531,7 @@
   rtc::scoped_refptr<webrtc::FrameTransformerInterface>
       unsignaled_frame_transformer_;
 
-  void FillSendCodecStats(VoiceMediaSendInfo* voice_media_info);
   void FillReceiveCodecStats(VoiceMediaReceiveInfo* voice_media_info);
-
-  // Callback invoked whenever the send codec changes.
-  // TODO(bugs.webrtc.org/13931): Remove again when coupling isn't needed.
-  absl::AnyInvocable<void()> send_codec_changed_callback_;
-  // Callback invoked whenever the list of SSRCs changes.
-  absl::AnyInvocable<void(const std::set<uint32_t>&)>
-      ssrc_list_changed_callback_;
 };
 
 }  // namespace cricket
diff --git a/media/engine/webrtc_voice_engine_unittest.cc b/media/engine/webrtc_voice_engine_unittest.cc
index a5ffd53..86dc95e 100644
--- a/media/engine/webrtc_voice_engine_unittest.cc
+++ b/media/engine/webrtc_voice_engine_unittest.cc
@@ -804,18 +804,18 @@
     return apm_config_.high_pass_filter.enabled;
   }
 
-  cricket::WebRtcVoiceMediaChannel* SendImplFromPointer(
+  cricket::WebRtcVoiceSendChannel* SendImplFromPointer(
       cricket::VoiceMediaChannel* channel) {
-    return static_cast<cricket::WebRtcVoiceMediaChannel*>(
+    return static_cast<cricket::WebRtcVoiceSendChannel*>(
         static_cast<cricket::VoiceMediaShimChannel*>(channel)
             ->SendImplForTesting());
   }
 
-  cricket::WebRtcVoiceMediaChannel* SendImpl() {
+  cricket::WebRtcVoiceSendChannel* SendImpl() {
     return SendImplFromPointer(channel_);
   }
-  cricket::WebRtcVoiceMediaChannel* ReceiveImpl() {
-    return static_cast<cricket::WebRtcVoiceMediaChannel*>(
+  cricket::WebRtcVoiceReceiveChannel* ReceiveImpl() {
+    return static_cast<cricket::WebRtcVoiceReceiveChannel*>(
         static_cast<cricket::VoiceMediaShimChannel*>(channel_)
             ->ReceiveImplForTesting());
   }
@@ -3457,7 +3457,7 @@
   rtc::CopyOnWriteBuffer kRtcpPacket(kRtcp, sizeof(kRtcp));
 
   EXPECT_TRUE(SetupSendStream());
-  cricket::VoiceMediaChannel* media_channel = ReceiveImpl();
+  cricket::VoiceMediaReceiveChannelInterface* media_channel = ReceiveImpl();
   SetSendParameters(send_parameters_);
   EXPECT_TRUE(media_channel->AddRecvStream(
       cricket::StreamParams::CreateLegacy(kAudioSsrc)));
@@ -3629,7 +3629,7 @@
 TEST_P(WebRtcVoiceEngineTestFake, GetSourcesWithNonExistingSsrc) {
   // Setup an recv stream with `kSsrcX`.
   SetupRecvStream();
-  cricket::WebRtcVoiceMediaChannel* media_channel = ReceiveImpl();
+  cricket::WebRtcVoiceReceiveChannel* media_channel = ReceiveImpl();
   // Call GetSources with `kSsrcY` which doesn't exist.
   std::vector<webrtc::RtpSource> sources = media_channel->GetSources(kSsrcY);
   EXPECT_EQ(0u, sources.size());
@@ -3828,9 +3828,9 @@
     call_config.trials = &field_trials;
     call_config.task_queue_factory = task_queue_factory.get();
     auto call = absl::WrapUnique(webrtc::Call::Create(call_config));
-    cricket::WebRtcVoiceMediaChannel channel(
-        cricket::MediaChannel::Role::kReceive, &engine, cricket::MediaConfig(),
-        cricket::AudioOptions(), webrtc::CryptoOptions(), call.get(),
+    cricket::WebRtcVoiceReceiveChannel channel(
+        &engine, cricket::MediaConfig(), cricket::AudioOptions(),
+        webrtc::CryptoOptions(), call.get(),
         webrtc::AudioCodecPairId::Create());
     cricket::AudioRecvParameters parameters;
     parameters.codecs = engine.recv_codecs();
@@ -3864,10 +3864,9 @@
     call_config.audio_state = webrtc::AudioState::Create(config);
   }
   auto call = absl::WrapUnique(webrtc::Call::Create(call_config));
-  cricket::WebRtcVoiceMediaChannel channel(
-      cricket::MediaChannel::Role::kSend, &engine, cricket::MediaConfig(),
-      cricket::AudioOptions(), webrtc::CryptoOptions(), call.get(),
-      webrtc::AudioCodecPairId::Create());
+  cricket::WebRtcVoiceSendChannel channel(
+      &engine, cricket::MediaConfig(), cricket::AudioOptions(),
+      webrtc::CryptoOptions(), call.get(), webrtc::AudioCodecPairId::Create());
   {
     cricket::AudioSendParameters params;
     params.codecs.push_back(cricket::AudioCodec(1, "opus", 48000, 32000, 2));