Split audio and video channels into Send and Receive APIs.

The implementation here has a number of changes that force the callers
that called the "channel" functions into specific interfaces rather than
just letting C++ take care of it; this should go away once there stops
being a common implementation class for those interfaces.

Bug: webrtc:13931
Change-Id: Ic4e279528a341bc0a0e88d2e1e76c90bc43a1035
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/287640
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38888}
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 7999be8..b884b0d 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -60,11 +60,13 @@
     "../api:rtp_sender_interface",
     "../api:scoped_refptr",
     "../api:sequence_checker",
+    "../api:transport_api",
     "../api/audio:audio_frame_processor",
     "../api/audio_codecs:audio_codecs_api",
     "../api/crypto:frame_decryptor_interface",
     "../api/crypto:frame_encryptor_interface",
     "../api/crypto:options",
+    "../api/task_queue",
     "../api/task_queue:pending_task_safety_flag",
     "../api/transport:datagram_transport_interface",
     "../api/transport:stun_types",
@@ -94,6 +96,7 @@
     "../rtc_base:socket",
     "../rtc_base:stringutils",
     "../rtc_base:timeutils",
+    "../rtc_base/network:sent_packet",
     "../rtc_base/synchronization:mutex",
     "../rtc_base/system:file_wrapper",
     "../rtc_base/system:no_unique_address",
@@ -105,6 +108,7 @@
   absl_deps = [
     "//third_party/abseil-cpp/absl/algorithm:container",
     "//third_party/abseil-cpp/absl/container:inlined_vector",
+    "//third_party/abseil-cpp/absl/functional:any_invocable",
     "//third_party/abseil-cpp/absl/strings",
     "//third_party/abseil-cpp/absl/types:optional",
   ]
@@ -115,8 +119,9 @@
     "base/codec.cc",
     "base/codec.h",
     "base/delayable.h",
-    "base/media_channel.cc",
     "base/media_channel.h",
+    "base/media_channel_impl.cc",
+    "base/media_channel_impl.h",
     "base/media_constants.cc",
     "base/media_constants.h",
     "base/media_engine.cc",
diff --git a/media/base/fake_media_engine.h b/media/base/fake_media_engine.h
index 3e5bc6a..a03a8a6 100644
--- a/media/base/fake_media_engine.h
+++ b/media/base/fake_media_engine.h
@@ -238,6 +238,24 @@
     rtcp_packets_.push_back(std::string(packet->cdata<char>(), packet->size()));
   }
 
+  // Stuff that deals with encryptors, transformers and the like
+  void SetFrameEncryptor(uint32_t ssrc,
+                         rtc::scoped_refptr<webrtc::FrameEncryptorInterface>
+                             frame_encryptor) override {}
+  void SetEncoderToPacketizerFrameTransformer(
+      uint32_t ssrc,
+      rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
+      override {}
+
+  void SetFrameDecryptor(uint32_t ssrc,
+                         rtc::scoped_refptr<webrtc::FrameDecryptorInterface>
+                             frame_decryptor) override {}
+
+  void SetDepacketizerToDecoderFrameTransformer(
+      uint32_t ssrc,
+      rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
+      override {}
+
  protected:
   bool MuteStream(uint32_t ssrc, bool mute) {
     if (!HasSendStream(ssrc) && ssrc != 0) {
diff --git a/media/base/fake_network_interface.h b/media/base/fake_network_interface.h
index 099b7ca..53c5563 100644
--- a/media/base/fake_network_interface.h
+++ b/media/base/fake_network_interface.h
@@ -31,7 +31,7 @@
 namespace cricket {
 
 // Fake NetworkInterface that sends/receives RTP/RTCP packets.
-class FakeNetworkInterface : public MediaChannel::NetworkInterface {
+class FakeNetworkInterface : public MediaChannelNetworkInterface {
  public:
   FakeNetworkInterface()
       : thread_(rtc::Thread::Current()),
diff --git a/media/base/media_channel.h b/media/base/media_channel.h
index e181154..77a5ba6 100644
--- a/media/base/media_channel.h
+++ b/media/base/media_channel.h
@@ -75,6 +75,10 @@
 class VideoCapturer;
 struct RtpHeader;
 struct VideoFormat;
+class VideoMediaSendChannelInterface;
+class VideoMediaReceiveChannelInterface;
+class VoiceMediaSendChannelInterface;
+class VoiceMediaReceiveChannelInterface;
 
 const int kScreencastDefaultFps = 5;
 
@@ -163,6 +167,23 @@
   }
 };
 
+class MediaChannelNetworkInterface {
+ public:
+  enum SocketType { ST_RTP, ST_RTCP };
+  virtual bool SendPacket(rtc::CopyOnWriteBuffer* packet,
+                          const rtc::PacketOptions& options) = 0;
+  virtual bool SendRtcp(rtc::CopyOnWriteBuffer* packet,
+                        const rtc::PacketOptions& options) = 0;
+  virtual int SetOption(SocketType type,
+                        rtc::Socket::Option opt,
+                        int option) = 0;
+  virtual ~MediaChannelNetworkInterface() {}
+};
+
+// Functions shared across all MediaChannel interfaces.
+// Because there are implementation types that implement multiple
+// interfaces, this is not a base class (no diamond inheritance).
+template <class T>
 class MediaBaseChannelInterface {
  public:
   virtual ~MediaBaseChannelInterface() = default;
@@ -184,13 +205,29 @@
   virtual void OnNetworkRouteChanged(
       absl::string_view transport_name,
       const rtc::NetworkRoute& network_route) = 0;
+
+  // Corresponds to the SDP attribute extmap-allow-mixed, see RFC8285.
+  // Set to true if it's allowed to mix one- and two-byte RTP header extensions
+  // in the same stream. The setter and getter must only be called from
+  // worker_thread.
+  virtual void SetExtmapAllowMixed(bool extmap_allow_mixed) = 0;
+  virtual bool ExtmapAllowMixed() const = 0;
 };
 
-class MediaSendChannelInterface : virtual public MediaBaseChannelInterface {
+class MediaSendChannelInterface
+    : public MediaBaseChannelInterface<MediaSendChannelInterface> {
  public:
   virtual ~MediaSendChannelInterface() = default;
 
-  virtual cricket::MediaType media_type() const = 0;
+  virtual VideoMediaSendChannelInterface* AsVideoSendChannel() {
+    RTC_CHECK_NOTREACHED();
+    return nullptr;
+  }
+  virtual VoiceMediaSendChannelInterface* AsVoiceSendChannel() {
+    RTC_CHECK_NOTREACHED();
+    return nullptr;
+  }
+
   // Creates a new outgoing media stream with SSRCs and CNAME as described
   // by sp.
   virtual bool AddSendStream(const StreamParams& sp) = 0;
@@ -225,10 +262,20 @@
   virtual webrtc::RtpParameters GetRtpSendParameters(uint32_t ssrc) const = 0;
 };
 
-class MediaReceiveChannelInterface : virtual public MediaBaseChannelInterface {
+class MediaReceiveChannelInterface
+    : public MediaBaseChannelInterface<MediaReceiveChannelInterface>,
+      public Delayable {
  public:
   virtual ~MediaReceiveChannelInterface() = default;
 
+  virtual VideoMediaReceiveChannelInterface* AsVideoReceiveChannel() {
+    RTC_CHECK_NOTREACHED();
+    return nullptr;
+  }
+  virtual VoiceMediaReceiveChannelInterface* AsVoiceReceiveChannel() {
+    RTC_CHECK_NOTREACHED();
+    return nullptr;
+  }
   // Creates a new incoming media stream with SSRCs, CNAME as described
   // by sp. In the case of a sp without SSRCs, the unsignaled sp is cached
   // to be used later for unsignaled streams received.
@@ -267,113 +314,6 @@
           frame_transformer) = 0;
 };
 
-class MediaChannel : public MediaSendChannelInterface,
-                     public MediaReceiveChannelInterface {
- public:
-  class NetworkInterface {
-   public:
-    enum SocketType { ST_RTP, ST_RTCP };
-    virtual bool SendPacket(rtc::CopyOnWriteBuffer* packet,
-                            const rtc::PacketOptions& options) = 0;
-    virtual bool SendRtcp(rtc::CopyOnWriteBuffer* packet,
-                          const rtc::PacketOptions& options) = 0;
-    virtual int SetOption(SocketType type,
-                          rtc::Socket::Option opt,
-                          int option) = 0;
-    virtual ~NetworkInterface() {}
-  };
-
-  explicit MediaChannel(webrtc::TaskQueueBase* network_thread,
-                        bool enable_dscp = false);
-  virtual ~MediaChannel();
-
-  // Sets the abstract interface class for sending RTP/RTCP data.
-  virtual void SetInterface(NetworkInterface* iface);
-  // Returns the absolute sendtime extension id value from media channel.
-  virtual int GetRtpSendTimeExtnId() const;
-  // Enable network condition based codec switching.
-  virtual void SetVideoCodecSwitchingEnabled(bool enabled);
-
-  // Base method to send packet using NetworkInterface.
-  bool SendPacket(rtc::CopyOnWriteBuffer* packet,
-                  const rtc::PacketOptions& options);
-
-  bool SendRtcp(rtc::CopyOnWriteBuffer* packet,
-                const rtc::PacketOptions& options);
-
-  int SetOption(NetworkInterface::SocketType type,
-                rtc::Socket::Option opt,
-                int option);
-
-  // Corresponds to the SDP attribute extmap-allow-mixed, see RFC8285.
-  // Set to true if it's allowed to mix one- and two-byte RTP header extensions
-  // in the same stream. The setter and getter must only be called from
-  // worker_thread.
-  void SetExtmapAllowMixed(bool extmap_allow_mixed);
-  bool ExtmapAllowMixed() const;
-
-  // Returns `true` if a non-null NetworkInterface pointer is held.
-  // Must be called on the network thread.
-  bool HasNetworkInterface() const;
-
-  void SetFrameEncryptor(uint32_t ssrc,
-                         rtc::scoped_refptr<webrtc::FrameEncryptorInterface>
-                             frame_encryptor) override;
-  void SetFrameDecryptor(uint32_t ssrc,
-                         rtc::scoped_refptr<webrtc::FrameDecryptorInterface>
-                             frame_decryptor) override;
-
-  void SetEncoderToPacketizerFrameTransformer(
-      uint32_t ssrc,
-      rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
-      override;
-  void SetDepacketizerToDecoderFrameTransformer(
-      uint32_t ssrc,
-      rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
-      override;
-
- protected:
-  int SetOptionLocked(NetworkInterface::SocketType type,
-                      rtc::Socket::Option opt,
-                      int option) RTC_RUN_ON(network_thread_);
-
-  bool DscpEnabled() const;
-
-  // This is the DSCP value used for both RTP and RTCP channels if DSCP is
-  // enabled. It can be changed at any time via `SetPreferredDscp`.
-  rtc::DiffServCodePoint PreferredDscp() const;
-  void SetPreferredDscp(rtc::DiffServCodePoint new_dscp);
-
-  rtc::scoped_refptr<webrtc::PendingTaskSafetyFlag> network_safety();
-
-  // Utility implementation for derived classes (video/voice) that applies
-  // the packet options and passes the data onwards to `SendPacket`.
-  void SendRtp(const uint8_t* data,
-               size_t len,
-               const webrtc::PacketOptions& options);
-
-  void SendRtcp(const uint8_t* data, size_t len);
-
- private:
-  // Apply the preferred DSCP setting to the underlying network interface RTP
-  // and RTCP channels. If DSCP is disabled, then apply the default DSCP value.
-  void UpdateDscp() RTC_RUN_ON(network_thread_);
-
-  bool DoSendPacket(rtc::CopyOnWriteBuffer* packet,
-                    bool rtcp,
-                    const rtc::PacketOptions& options);
-
-  const bool enable_dscp_;
-  const rtc::scoped_refptr<webrtc::PendingTaskSafetyFlag> network_safety_
-      RTC_PT_GUARDED_BY(network_thread_);
-  webrtc::TaskQueueBase* const network_thread_;
-  NetworkInterface* network_interface_ RTC_GUARDED_BY(network_thread_) =
-      nullptr;
-  rtc::DiffServCodePoint preferred_dscp_ RTC_GUARDED_BY(network_thread_) =
-      rtc::DSCP_DEFAULT;
-  bool extmap_allow_mixed_ = false;
-};
-
 // The stats information is structured as follows:
 // Media are represented by either MediaSenderInfo or MediaReceiverInfo.
 // Media contains a vector of SSRC infos that are exclusively used by this
@@ -458,6 +398,7 @@
 struct MediaReceiverInfo {
   MediaReceiverInfo();
   ~MediaReceiverInfo();
+
   void add_ssrc(const SsrcReceiverInfo& stat) { local_stats.push_back(stat); }
   // Temporary utility function for call sites that only provide SSRC.
   // As more info is added into SsrcSenderInfo, this function should go away.
@@ -850,24 +791,9 @@
 
 struct AudioRecvParameters : RtpParameters<AudioCodec> {};
 
-class VoiceMediaChannel : public MediaChannel, public Delayable {
+class VoiceMediaSendChannelInterface : public MediaSendChannelInterface {
  public:
-  VoiceMediaChannel(webrtc::TaskQueueBase* network_thread,
-                    bool enable_dscp = false)
-      : MediaChannel(network_thread, enable_dscp) {}
-  ~VoiceMediaChannel() override {}
-
-  cricket::MediaType media_type() const override;
   virtual bool SetSendParameters(const AudioSendParameters& params) = 0;
-  virtual bool SetRecvParameters(const AudioRecvParameters& params) = 0;
-  // Get the receive parameters for the incoming stream identified by `ssrc`.
-  virtual webrtc::RtpParameters GetRtpReceiveParameters(
-      uint32_t ssrc) const = 0;
-  // Retrieve the receive parameters for the default receive
-  // stream, which is used when SSRCs are not signaled.
-  virtual webrtc::RtpParameters GetDefaultRtpReceiveParameters() const = 0;
-  // Starts or stops playout of received audio.
-  virtual void SetPlayout(bool playout) = 0;
   // Starts or stops sending (and potentially capture) of local audio.
   virtual void SetSend(bool send) = 0;
   // Configure stream for sending.
@@ -875,10 +801,6 @@
                             bool enable,
                             const AudioOptions* options,
                             AudioSource* source) = 0;
-  // Set speaker output volume of the specified ssrc.
-  virtual bool SetOutputVolume(uint32_t ssrc, double volume) = 0;
-  // Set speaker output volume for future unsignaled streams.
-  virtual bool SetDefaultOutputVolume(double volume) = 0;
   // Returns if the telephone-event has been negotiated.
   virtual bool CanInsertDtmf() = 0;
   // Send a DTMF `event`. The DTMF out-of-band signal will be used.
@@ -886,17 +808,29 @@
   // The valid value for the `event` are 0 to 15 which corresponding to
   // DTMF event 0-9, *, #, A-D.
   virtual bool InsertDtmf(uint32_t ssrc, int event, int duration) = 0;
-  // Gets quality stats for the channel.
-  virtual bool GetStats(VoiceMediaInfo* info,
-                        bool get_and_clear_legacy_stats) = 0;
+};
 
+class VoiceMediaReceiveChannelInterface : public MediaReceiveChannelInterface {
+ public:
+  virtual bool SetRecvParameters(const AudioRecvParameters& params) = 0;
+  // Get the receive parameters for the incoming stream identified by `ssrc`.
+  virtual webrtc::RtpParameters GetRtpReceiveParameters(
+      uint32_t ssrc) const = 0;
+  virtual std::vector<webrtc::RtpSource> GetSources(uint32_t ssrc) const = 0;
+  // Retrieve the receive parameters for the default receive
+  // stream, which is used when SSRCs are not signaled.
+  virtual webrtc::RtpParameters GetDefaultRtpReceiveParameters() const = 0;
+  // Starts or stops playout of received audio.
+  virtual void SetPlayout(bool playout) = 0;
+  // Set speaker output volume of the specified ssrc.
+  virtual bool SetOutputVolume(uint32_t ssrc, double volume) = 0;
+  // Set speaker output volume for future unsignaled streams.
+  virtual bool SetDefaultOutputVolume(double volume) = 0;
   virtual void SetRawAudioSink(
       uint32_t ssrc,
       std::unique_ptr<webrtc::AudioSinkInterface> sink) = 0;
   virtual void SetDefaultRawAudioSink(
       std::unique_ptr<webrtc::AudioSinkInterface> sink) = 0;
-
-  virtual std::vector<webrtc::RtpSource> GetSources(uint32_t ssrc) const = 0;
 };
 
 // TODO(deadbeef): Rename to VideoSenderParameters, since they're intended to
@@ -920,22 +854,9 @@
 // encapsulate all the parameters needed for a video RtpReceiver.
 struct VideoRecvParameters : RtpParameters<VideoCodec> {};
 
-class VideoMediaChannel : public MediaChannel, public Delayable {
+class VideoMediaSendChannelInterface : public MediaSendChannelInterface {
  public:
-  explicit VideoMediaChannel(webrtc::TaskQueueBase* network_thread,
-                             bool enable_dscp = false)
-      : MediaChannel(network_thread, enable_dscp) {}
-  ~VideoMediaChannel() override {}
-
-  cricket::MediaType media_type() const override;
   virtual bool SetSendParameters(const VideoSendParameters& params) = 0;
-  virtual bool SetRecvParameters(const VideoRecvParameters& params) = 0;
-  // Get the receive parameters for the incoming stream identified by `ssrc`.
-  virtual webrtc::RtpParameters GetRtpReceiveParameters(
-      uint32_t ssrc) const = 0;
-  // Retrieve the receive parameters for the default receive
-  // stream, which is used when SSRCs are not signaled.
-  virtual webrtc::RtpParameters GetDefaultRtpReceiveParameters() const = 0;
   // Gets the currently set codecs/payload types to be used for outgoing media.
   virtual bool GetSendCodec(VideoCodec* send_codec) = 0;
   // Starts or stops transmission (and potentially capture) of local video.
@@ -946,37 +867,39 @@
       uint32_t ssrc,
       const VideoOptions* options,
       rtc::VideoSourceInterface<webrtc::VideoFrame>* source) = 0;
+  // Cause generation of a keyframe for `ssrc` on a sending channel.
+  virtual void GenerateSendKeyFrame(uint32_t ssrc,
+                                    const std::vector<std::string>& rids) = 0;
+  // Enable network condition based codec switching.
+  virtual void SetVideoCodecSwitchingEnabled(bool enabled) = 0;
+};
+
+class VideoMediaReceiveChannelInterface : public MediaReceiveChannelInterface {
+ public:
+  virtual bool SetRecvParameters(const VideoRecvParameters& params) = 0;
+  // Get the receive parameters for the incoming stream identified by `ssrc`.
+  virtual webrtc::RtpParameters GetRtpReceiveParameters(
+      uint32_t ssrc) const = 0;
+  // Retrieve the receive parameters for the default receive
+  // stream, which is used when SSRCs are not signaled.
+  virtual webrtc::RtpParameters GetDefaultRtpReceiveParameters() const = 0;
   // Sets the sink object to be used for the specified stream.
   virtual bool SetSink(uint32_t ssrc,
                        rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) = 0;
   // The sink is used for the 'default' stream.
   virtual void SetDefaultSink(
       rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) = 0;
-  // This fills the "bitrate parts" (rtx, video bitrate) of the
-  // BandwidthEstimationInfo, since that part that isn't possible to get
-  // through webrtc::Call::GetStats, as they are statistics of the send
-  // streams.
-  // TODO(holmer): We should change this so that either BWE graphs doesn't
-  // need access to bitrates of the streams, or change the (RTC)StatsCollector
-  // so that it's getting the send stream stats separately by calling
-  // GetStats(), and merges with BandwidthEstimationInfo by itself.
-  virtual void FillBitrateInfo(BandwidthEstimationInfo* bwe_info) = 0;
-  // Gets quality stats for the channel.
-  virtual bool GetStats(VideoMediaInfo* info) = 0;
+  // Request generation of a keyframe for `ssrc` on a receiving channel via
+  // RTCP feedback.
+  virtual void RequestRecvKeyFrame(uint32_t ssrc) = 0;
+
+  virtual std::vector<webrtc::RtpSource> GetSources(uint32_t ssrc) const = 0;
   // Set recordable encoded frame callback for `ssrc`
   virtual void SetRecordableEncodedFrameCallback(
       uint32_t ssrc,
       std::function<void(const webrtc::RecordableEncodedFrame&)> callback) = 0;
   // Clear recordable encoded frame callback for `ssrc`
   virtual void ClearRecordableEncodedFrameCallback(uint32_t ssrc) = 0;
-  // Request generation of a keyframe for `ssrc` on a receiving channel via
-  // RTCP feedback.
-  virtual void RequestRecvKeyFrame(uint32_t ssrc) = 0;
-  // Cause generation of a keyframe for `ssrc` on a sending channel.
-  virtual void GenerateSendKeyFrame(uint32_t ssrc,
-                                    const std::vector<std::string>& rids) = 0;
-
-  virtual std::vector<webrtc::RtpSource> GetSources(uint32_t ssrc) const = 0;
 };
 
 // Info about data received in DataMediaChannel.  For use in
diff --git a/media/base/media_channel.cc b/media/base/media_channel_impl.cc
similarity index 88%
rename from media/base/media_channel.cc
rename to media/base/media_channel_impl.cc
index 1c6802d..626de34 100644
--- a/media/base/media_channel.cc
+++ b/media/base/media_channel_impl.cc
@@ -8,9 +8,26 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include "media/base/media_channel.h"
+#include "media/base/media_channel_impl.h"
 
+#include <map>
+#include <string>
+#include <utility>
+
+#include "absl/functional/any_invocable.h"
+#include "api/audio_options.h"
+#include "api/media_stream_interface.h"
+#include "api/rtc_error.h"
+#include "api/rtp_sender_interface.h"
+#include "api/units/time_delta.h"
+#include "api/video/video_timing.h"
+#include "common_video/include/quality_limitation_reason.h"
+#include "media/base/codec.h"
+#include "media/base/media_channel.h"
 #include "media/base/rtp_utils.h"
+#include "media/base/stream_params.h"
+#include "modules/rtp_rtcp/include/report_block_data.h"
+#include "rtc_base/checks.h"
 
 namespace webrtc {
 
@@ -47,7 +64,7 @@
   RTC_DCHECK(!network_interface_);
 }
 
-void MediaChannel::SetInterface(NetworkInterface* iface) {
+void MediaChannel::SetInterface(MediaChannelNetworkInterface* iface) {
   RTC_DCHECK_RUN_ON(network_thread_);
   iface ? network_safety_->SetAlive() : network_safety_->SetNotAlive();
   network_interface_ = iface;
@@ -70,8 +87,6 @@
   // Placeholder should be pure virtual once internal supports it.
 }
 
-void MediaChannel::SetVideoCodecSwitchingEnabled(bool enabled) {}
-
 bool MediaChannel::SendPacket(rtc::CopyOnWriteBuffer* packet,
                               const rtc::PacketOptions& options) {
   return DoSendPacket(packet, false, options);
@@ -82,7 +97,7 @@
   return DoSendPacket(packet, true, options);
 }
 
-int MediaChannel::SetOption(NetworkInterface::SocketType type,
+int MediaChannel::SetOption(MediaChannelNetworkInterface::SocketType type,
                             rtc::Socket::Option opt,
                             int option) {
   RTC_DCHECK_RUN_ON(network_thread_);
@@ -114,7 +129,7 @@
     uint32_t ssrc,
     rtc::scoped_refptr<FrameTransformerInterface> frame_transformer) {}
 
-int MediaChannel::SetOptionLocked(NetworkInterface::SocketType type,
+int MediaChannel::SetOptionLocked(MediaChannelNetworkInterface::SocketType type,
                                   rtc::Socket::Option opt,
                                   int option) {
   if (!network_interface_)
@@ -158,10 +173,11 @@
 void MediaChannel::UpdateDscp() {
   rtc::DiffServCodePoint value =
       enable_dscp_ ? preferred_dscp_ : rtc::DSCP_DEFAULT;
-  int ret =
-      SetOptionLocked(NetworkInterface::ST_RTP, rtc::Socket::OPT_DSCP, value);
+  int ret = SetOptionLocked(MediaChannelNetworkInterface::ST_RTP,
+                            rtc::Socket::OPT_DSCP, value);
   if (ret == 0)
-    SetOptionLocked(NetworkInterface::ST_RTCP, rtc::Socket::OPT_DSCP, value);
+    SetOptionLocked(MediaChannelNetworkInterface::ST_RTCP,
+                    rtc::Socket::OPT_DSCP, value);
 }
 
 bool MediaChannel::DoSendPacket(rtc::CopyOnWriteBuffer* packet,
@@ -273,4 +289,6 @@
   return cricket::MediaType::MEDIA_TYPE_VIDEO;
 }
 
+void VideoMediaChannel::SetVideoCodecSwitchingEnabled(bool enabled) {}
+
 }  // namespace cricket
diff --git a/media/base/media_channel_impl.h b/media/base/media_channel_impl.h
new file mode 100644
index 0000000..41bead7
--- /dev/null
+++ b/media/base/media_channel_impl.h
@@ -0,0 +1,242 @@
+/*
+ *  Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MEDIA_BASE_MEDIA_CHANNEL_IMPL_H_
+#define MEDIA_BASE_MEDIA_CHANNEL_IMPL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "absl/strings/string_view.h"
+#include "api/call/transport.h"
+#include "api/crypto/frame_decryptor_interface.h"
+#include "api/crypto/frame_encryptor_interface.h"
+#include "api/frame_transformer_interface.h"
+#include "api/media_types.h"
+#include "api/scoped_refptr.h"
+#include "api/sequence_checker.h"
+#include "api/task_queue/pending_task_safety_flag.h"
+#include "api/task_queue/task_queue_base.h"
+#include "media/base/media_channel.h"
+#include "rtc_base/async_packet_socket.h"
+#include "rtc_base/copy_on_write_buffer.h"
+#include "rtc_base/dscp.h"
+#include "rtc_base/network/sent_packet.h"
+#include "rtc_base/network_route.h"
+#include "rtc_base/socket.h"
+#include "rtc_base/thread_annotations.h"
+// This file contains the base classes for classes that implement
+// the MediaChannel interfaces.
+// These implementation classes used to be the exposed interface names,
+// but this is in the process of being changed.
+// TODO(bugs.webrtc.org/13931): Consider removing these classes.
+
+namespace cricket {
+
+class VoiceMediaChannel;
+class VideoMediaChannel;
+
+class MediaChannel : public MediaSendChannelInterface,
+                     public MediaReceiveChannelInterface {
+ public:
+  explicit MediaChannel(webrtc::TaskQueueBase* network_thread,
+                        bool enable_dscp = false);
+  virtual ~MediaChannel();
+
+  // Downcasting to the implemented interfaces.
+  MediaSendChannelInterface* AsSendChannel() { return this; }
+
+  MediaReceiveChannelInterface* AsReceiveChannel() { return this; }
+
+  // Downcasting to the subclasses.
+  virtual VideoMediaChannel* AsVideoChannel() {
+    RTC_CHECK_NOTREACHED();
+    return nullptr;
+  }
+
+  virtual VoiceMediaChannel* AsVoiceChannel() {
+    RTC_CHECK_NOTREACHED();
+    return nullptr;
+  }
+
+  // Must declare the methods inherited from the base interface template,
+  // even when abstract, to tell the compiler that all instances of the name
+  // referred to by subclasses of this share the same implementation.
+  cricket::MediaType media_type() const override = 0;
+  void OnPacketReceived(rtc::CopyOnWriteBuffer packet,
+                        int64_t packet_time_us) override = 0;
+  void OnPacketSent(const rtc::SentPacket& sent_packet) override = 0;
+  void OnReadyToSend(bool ready) override = 0;
+  void OnNetworkRouteChanged(absl::string_view transport_name,
+                             const rtc::NetworkRoute& network_route) override =
+      0;
+
+  // Sets the abstract interface class for sending RTP/RTCP data.
+  virtual void SetInterface(MediaChannelNetworkInterface* iface);
+  // Returns the absolute sendtime extension id value from media channel.
+  virtual int GetRtpSendTimeExtnId() const;
+  // Base method to send packet using MediaChannelNetworkInterface.
+  bool SendPacket(rtc::CopyOnWriteBuffer* packet,
+                  const rtc::PacketOptions& options);
+
+  bool SendRtcp(rtc::CopyOnWriteBuffer* packet,
+                const rtc::PacketOptions& options);
+
+  int SetOption(MediaChannelNetworkInterface::SocketType type,
+                rtc::Socket::Option opt,
+                int option);
+
+  // Corresponds to the SDP attribute extmap-allow-mixed, see RFC8285.
+  // Set to true if it's allowed to mix one- and two-byte RTP header extensions
+  // in the same stream. The setter and getter must only be called from
+  // worker_thread.
+  void SetExtmapAllowMixed(bool extmap_allow_mixed) override;
+  bool ExtmapAllowMixed() const override;
+
+  // Returns `true` if a non-null MediaChannelNetworkInterface pointer is held.
+  // Must be called on the network thread.
+  bool HasNetworkInterface() const;
+
+  void SetFrameEncryptor(uint32_t ssrc,
+                         rtc::scoped_refptr<webrtc::FrameEncryptorInterface>
+                             frame_encryptor) override;
+  void SetFrameDecryptor(uint32_t ssrc,
+                         rtc::scoped_refptr<webrtc::FrameDecryptorInterface>
+                             frame_decryptor) override;
+
+  void SetEncoderToPacketizerFrameTransformer(
+      uint32_t ssrc,
+      rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
+      override;
+  void SetDepacketizerToDecoderFrameTransformer(
+      uint32_t ssrc,
+      rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
+      override;
+
+ protected:
+  int SetOptionLocked(MediaChannelNetworkInterface::SocketType type,
+                      rtc::Socket::Option opt,
+                      int option) RTC_RUN_ON(network_thread_);
+
+  bool DscpEnabled() const;
+
+  // This is the DSCP value used for both RTP and RTCP channels if DSCP is
+  // enabled. It can be changed at any time via `SetPreferredDscp`.
+  rtc::DiffServCodePoint PreferredDscp() const;
+  void SetPreferredDscp(rtc::DiffServCodePoint new_dscp);
+
+  rtc::scoped_refptr<webrtc::PendingTaskSafetyFlag> network_safety();
+
+  // Utility implementation for derived classes (video/voice) that applies
+  // the packet options and passes the data onwards to `SendPacket`.
+  void SendRtp(const uint8_t* data,
+               size_t len,
+               const webrtc::PacketOptions& options);
+
+  void SendRtcp(const uint8_t* data, size_t len);
+
+ private:
+  // Apply the preferred DSCP setting to the underlying network interface RTP
+  // and RTCP channels. If DSCP is disabled, then apply the default DSCP value.
+  void UpdateDscp() RTC_RUN_ON(network_thread_);
+
+  bool DoSendPacket(rtc::CopyOnWriteBuffer* packet,
+                    bool rtcp,
+                    const rtc::PacketOptions& options);
+
+  const bool enable_dscp_;
+  const rtc::scoped_refptr<webrtc::PendingTaskSafetyFlag> network_safety_
+      RTC_PT_GUARDED_BY(network_thread_);
+  webrtc::TaskQueueBase* const network_thread_;
+  MediaChannelNetworkInterface* network_interface_
+      RTC_GUARDED_BY(network_thread_) = nullptr;
+  rtc::DiffServCodePoint preferred_dscp_ RTC_GUARDED_BY(network_thread_) =
+      rtc::DSCP_DEFAULT;
+  bool extmap_allow_mixed_ = false;
+};
+
+// Base class for implementation classes
+
+class VideoMediaChannel : public MediaChannel,
+                          public VideoMediaSendChannelInterface,
+                          public VideoMediaReceiveChannelInterface {
+ public:
+  explicit VideoMediaChannel(webrtc::TaskQueueBase* network_thread,
+                             bool enable_dscp = false)
+      : MediaChannel(network_thread, enable_dscp) {}
+  ~VideoMediaChannel() override {}
+
+  // Downcasting to the implemented interfaces.
+  VideoMediaSendChannelInterface* AsVideoSendChannel() override { return this; }
+
+  VideoMediaReceiveChannelInterface* AsVideoReceiveChannel() override {
+    return this;
+  }
+  cricket::MediaType media_type() const override;
+
+  // Downcasting to the subclasses.
+  VideoMediaChannel* AsVideoChannel() override { return this; }
+
+  void SetExtmapAllowMixed(bool mixed) override {
+    MediaChannel::SetExtmapAllowMixed(mixed);
+  }
+  bool ExtmapAllowMixed() const override {
+    return MediaChannel::ExtmapAllowMixed();
+  }
+  // This fills the "bitrate parts" (rtx, video bitrate) of the
+  // BandwidthEstimationInfo, since that part that isn't possible to get
+  // through webrtc::Call::GetStats, as they are statistics of the send
+  // streams.
+  // TODO(holmer): We should change this so that either BWE graphs doesn't
+  // need access to bitrates of the streams, or change the (RTC)StatsCollector
+  // so that it's getting the send stream stats separately by calling
+  // GetStats(), and merges with BandwidthEstimationInfo by itself.
+  virtual void FillBitrateInfo(BandwidthEstimationInfo* bwe_info) = 0;
+  // Gets quality stats for the channel.
+  virtual bool GetStats(VideoMediaInfo* info) = 0;
+  // Enable network condition based codec switching.
+  void SetVideoCodecSwitchingEnabled(bool enabled) override;
+};
+
+// Base class for implementation classes
+class VoiceMediaChannel : public MediaChannel,
+                          public VoiceMediaSendChannelInterface,
+                          public VoiceMediaReceiveChannelInterface {
+ public:
+  MediaType media_type() const override;
+  VoiceMediaChannel(webrtc::TaskQueueBase* network_thread,
+                    bool enable_dscp = false)
+      : MediaChannel(network_thread, enable_dscp) {}
+  ~VoiceMediaChannel() override {}
+
+  // Downcasting to the implemented interfaces.
+  VoiceMediaSendChannelInterface* AsVoiceSendChannel() override { return this; }
+
+  VoiceMediaReceiveChannelInterface* AsVoiceReceiveChannel() override {
+    return this;
+  }
+
+  VoiceMediaChannel* AsVoiceChannel() override { return this; }
+
+  void SetExtmapAllowMixed(bool mixed) override {
+    MediaChannel::SetExtmapAllowMixed(mixed);
+  }
+  bool ExtmapAllowMixed() const override {
+    return MediaChannel::ExtmapAllowMixed();
+  }
+
+  // Gets quality stats for the channel.
+  virtual bool GetStats(VoiceMediaInfo* info,
+                        bool get_and_clear_legacy_stats) = 0;
+};
+
+}  // namespace cricket
+
+#endif  // MEDIA_BASE_MEDIA_CHANNEL_IMPL_H_
diff --git a/media/base/media_engine.h b/media/base/media_engine.h
index e533691..96b54ba 100644
--- a/media/base/media_engine.h
+++ b/media/base/media_engine.h
@@ -24,6 +24,7 @@
 #include "call/audio_state.h"
 #include "media/base/codec.h"
 #include "media/base/media_channel.h"
+#include "media/base/media_channel_impl.h"
 #include "media/base/media_config.h"
 #include "media/base/video_common.h"
 #include "rtc_base/system/file_wrapper.h"
diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc
index a78162e..34beacd 100644
--- a/media/engine/webrtc_video_engine.cc
+++ b/media/engine/webrtc_video_engine.cc
@@ -1887,12 +1887,12 @@
       }));
 }
 
-void WebRtcVideoChannel::SetInterface(NetworkInterface* iface) {
+void WebRtcVideoChannel::SetInterface(MediaChannelNetworkInterface* iface) {
   RTC_DCHECK_RUN_ON(&network_thread_checker_);
   MediaChannel::SetInterface(iface);
   // Set the RTP recv/send buffer to a bigger size.
-  MediaChannel::SetOption(NetworkInterface::ST_RTP, rtc::Socket::OPT_RCVBUF,
-                          kVideoRtpRecvBufferSize);
+  MediaChannel::SetOption(MediaChannelNetworkInterface::ST_RTP,
+                          rtc::Socket::OPT_RCVBUF, kVideoRtpRecvBufferSize);
 
   // Speculative change to increase the outbound socket buffer size.
   // In b/15152257, we are seeing a significant number of packets discarded
@@ -1909,8 +1909,8 @@
     send_buffer_size = kVideoRtpSendBufferSize;
   }
 
-  MediaChannel::SetOption(NetworkInterface::ST_RTP, rtc::Socket::OPT_SNDBUF,
-                          send_buffer_size);
+  MediaChannel::SetOption(MediaChannelNetworkInterface::ST_RTP,
+                          rtc::Socket::OPT_SNDBUF, send_buffer_size);
 }
 
 void WebRtcVideoChannel::SetFrameDecryptor(
diff --git a/media/engine/webrtc_video_engine.h b/media/engine/webrtc_video_engine.h
index 841e04d..0373233 100644
--- a/media/engine/webrtc_video_engine.h
+++ b/media/engine/webrtc_video_engine.h
@@ -180,7 +180,7 @@
   void OnReadyToSend(bool ready) override;
   void OnNetworkRouteChanged(absl::string_view transport_name,
                              const rtc::NetworkRoute& network_route) override;
-  void SetInterface(NetworkInterface* iface) override;
+  void SetInterface(MediaChannelNetworkInterface* iface) override;
 
   // E2E Encrypted Video Frame API
   // Set a frame decryptor to a particular ssrc that will intercept all
diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc
index c151e72..91cd59a 100644
--- a/media/engine/webrtc_video_engine_unittest.cc
+++ b/media/engine/webrtc_video_engine_unittest.cc
@@ -437,7 +437,8 @@
 
   std::unique_ptr<VideoMediaChannel> channel(
       SetSendParamsWithAllSupportedCodecs());
-  EXPECT_TRUE(channel->AddSendStream(StreamParams::CreateLegacy(kSsrc)));
+  EXPECT_TRUE(channel->AsSendChannel()->AddSendStream(
+      StreamParams::CreateLegacy(kSsrc)));
 
   // Add CVO extension.
   const int id = 1;
@@ -481,7 +482,8 @@
   parameters.extensions.push_back(
       RtpExtension(RtpExtension::kVideoRotationUri, id));
   EXPECT_TRUE(channel->SetSendParameters(parameters));
-  EXPECT_TRUE(channel->AddSendStream(StreamParams::CreateLegacy(kSsrc)));
+  EXPECT_TRUE(channel->AsSendChannel()->AddSendStream(
+      StreamParams::CreateLegacy(kSsrc)));
 
   // Set source.
   EXPECT_CALL(
@@ -498,7 +500,8 @@
 
   std::unique_ptr<VideoMediaChannel> channel(
       SetSendParamsWithAllSupportedCodecs());
-  EXPECT_TRUE(channel->AddSendStream(StreamParams::CreateLegacy(kSsrc)));
+  EXPECT_TRUE(channel->AsSendChannel()->AddSendStream(
+      StreamParams::CreateLegacy(kSsrc)));
 
   // Set capturer.
   EXPECT_CALL(
@@ -541,7 +544,8 @@
       call_.get(), GetMediaConfig(), VideoOptions(), webrtc::CryptoOptions(),
       video_bitrate_allocator_factory_.get()));
 
-  EXPECT_TRUE(channel->AddSendStream(StreamParams::CreateLegacy(123)));
+  EXPECT_TRUE(
+      channel->AsSendChannel()->AddSendStream(StreamParams::CreateLegacy(123)));
 
   EXPECT_FALSE(channel->SetSend(true))
       << "Channel should not start without codecs.";
@@ -555,7 +559,8 @@
   std::unique_ptr<VideoMediaChannel> channel(engine_.CreateMediaChannel(
       call_.get(), GetMediaConfig(), VideoOptions(), webrtc::CryptoOptions(),
       video_bitrate_allocator_factory_.get()));
-  EXPECT_TRUE(channel->AddSendStream(StreamParams::CreateLegacy(123)));
+  EXPECT_TRUE(
+      channel->AsSendChannel()->AddSendStream(StreamParams::CreateLegacy(123)));
   VideoMediaInfo info;
   channel->GetStats(&info);
 }
@@ -565,10 +570,10 @@
 
   std::unique_ptr<VideoMediaChannel> channel(
       SetSendParamsWithAllSupportedCodecs());
-  channel->OnReadyToSend(true);
+  channel->AsSendChannel()->OnReadyToSend(true);
 
-  EXPECT_TRUE(
-      channel->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+  EXPECT_TRUE(channel->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(kSsrc)));
   EXPECT_EQ(0, encoder_factory_->GetNumCreatedEncoders());
   EXPECT_TRUE(channel->SetSend(true));
   webrtc::test::FrameForwarder frame_forwarder;
@@ -593,7 +598,7 @@
   EXPECT_EQ(num_created_encoders, encoder_factory_->GetNumCreatedEncoders());
 
   // Remove stream previously added to free the external encoder instance.
-  EXPECT_TRUE(channel->RemoveSendStream(kSsrc));
+  EXPECT_TRUE(channel->AsSendChannel()->RemoveSendStream(kSsrc));
   EXPECT_EQ(0u, encoder_factory_->encoders().size());
 }
 
@@ -645,8 +650,8 @@
   std::unique_ptr<VideoMediaChannel> channel(
       SetSendParamsWithAllSupportedCodecs());
 
-  EXPECT_TRUE(
-      channel->AddRecvStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+  EXPECT_TRUE(channel->AsReceiveChannel()->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(kSsrc)));
 }
 #endif  // defined(RTC_ENABLE_VP9)
 
@@ -657,8 +662,8 @@
   std::unique_ptr<VideoMediaChannel> channel(
       SetSendParamsWithAllSupportedCodecs());
 
-  EXPECT_TRUE(
-      channel->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+  EXPECT_TRUE(channel->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(kSsrc)));
 
   webrtc::test::FrameForwarder frame_forwarder;
   cricket::FakeFrameSource frame_source(1280, 720,
@@ -701,7 +706,7 @@
   }
 
   // Remove stream previously added to free the external encoder instance.
-  EXPECT_TRUE(channel->RemoveSendStream(kSsrc));
+  EXPECT_TRUE(channel->AsSendChannel()->RemoveSendStream(kSsrc));
 }
 
 void WebRtcVideoEngineTest::AssignDefaultAptRtxTypes() {
@@ -819,7 +824,8 @@
 
   std::vector<uint32_t> ssrcs = MAKE_VECTOR(kSsrcs3);
 
-  EXPECT_TRUE(channel->AddSendStream(CreateSimStreamParams("cname", ssrcs)));
+  EXPECT_TRUE(channel->AsSendChannel()->AddSendStream(
+      CreateSimStreamParams("cname", ssrcs)));
   EXPECT_TRUE(channel->SetSend(true));
 
   webrtc::test::FrameForwarder frame_forwarder;
@@ -864,8 +870,8 @@
   parameters.codecs.push_back(GetEngineCodec("H264"));
   EXPECT_TRUE(channel->SetSendParameters(parameters));
 
-  EXPECT_TRUE(
-      channel->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+  EXPECT_TRUE(channel->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(kSsrc)));
   EXPECT_TRUE(channel->SetVideoSend(kSsrc, nullptr, &frame_forwarder));
   // Sending one frame will have allocate the encoder.
   frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
@@ -898,7 +904,8 @@
 
   std::vector<uint32_t> ssrcs = MAKE_VECTOR(kSsrcs3);
 
-  EXPECT_TRUE(channel->AddSendStream(CreateSimStreamParams("cname", ssrcs)));
+  EXPECT_TRUE(channel->AsSendChannel()->AddSendStream(
+      CreateSimStreamParams("cname", ssrcs)));
   EXPECT_TRUE(channel->SetSend(true));
 
   // Send a fake frame, or else the media engine will configure the simulcast
@@ -933,8 +940,8 @@
   parameters.codecs.push_back(GetEngineCodec("H264"));
   EXPECT_TRUE(channel->SetSendParameters(parameters));
 
-  EXPECT_TRUE(
-      channel->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+  EXPECT_TRUE(channel->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(kSsrc)));
 
   // Send a frame of 720p. This should trigger a "real" encoder initialization.
   webrtc::test::FrameForwarder frame_forwarder;
@@ -967,8 +974,8 @@
   EXPECT_TRUE(channel->SetSendParameters(parameters));
 
   const std::vector<uint32_t> ssrcs = MAKE_VECTOR(kSsrcs3);
-  EXPECT_TRUE(
-      channel->AddSendStream(cricket::CreateSimStreamParams("cname", ssrcs)));
+  EXPECT_TRUE(channel->AsSendChannel()->AddSendStream(
+      cricket::CreateSimStreamParams("cname", ssrcs)));
 
   // Send a frame of 720p. This should trigger a "real" encoder initialization.
   webrtc::test::FrameForwarder frame_forwarder;
@@ -1086,8 +1093,8 @@
   std::unique_ptr<VideoMediaChannel> channel(
       SetRecvParamsWithSupportedCodecs(parameters.codecs));
 
-  EXPECT_TRUE(
-      channel->AddRecvStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+  EXPECT_TRUE(channel->AsReceiveChannel()->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(kSsrc)));
   // Decoders are not created until they are used.
   time_controller_.AdvanceTime(webrtc::TimeDelta::Zero());
   EXPECT_EQ(0u, decoder_factory_->decoders().size());
@@ -1097,7 +1104,7 @@
   EXPECT_EQ(0, decoder_factory_->GetNumCreatedDecoders());
 
   // Remove stream previously added to free the external decoder instance.
-  EXPECT_TRUE(channel->RemoveRecvStream(kSsrc));
+  EXPECT_TRUE(channel->AsReceiveChannel()->RemoveRecvStream(kSsrc));
   EXPECT_EQ(0u, decoder_factory_->decoders().size());
 }
 
@@ -1114,8 +1121,8 @@
   std::unique_ptr<VideoMediaChannel> channel(
       SetRecvParamsWithSupportedCodecs(codecs));
 
-  EXPECT_TRUE(
-      channel->AddRecvStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+  EXPECT_TRUE(channel->AsReceiveChannel()->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(kSsrc)));
   // Decoders are not created until they are used.
   time_controller_.AdvanceTime(webrtc::TimeDelta::Zero());
   ASSERT_EQ(0u, decoder_factory_->decoders().size());
@@ -1131,8 +1138,8 @@
   std::unique_ptr<VideoMediaChannel> channel(
       SetRecvParamsWithSupportedCodecs(parameters.codecs));
 
-  EXPECT_TRUE(
-      channel->AddRecvStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+  EXPECT_TRUE(channel->AsReceiveChannel()->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(kSsrc)));
 
   // Call GetSources with |kSsrc + 1| which doesn't exist.
   std::vector<webrtc::RtpSource> sources = channel->GetSources(kSsrc + 1);
@@ -1258,9 +1265,9 @@
   cricket::VideoSendParameters send_parameters;
   send_parameters.codecs.push_back(engine_codecs.at(0));
   EXPECT_TRUE(send_channel->SetSendParameters(send_parameters));
-  send_channel->OnReadyToSend(true);
-  EXPECT_TRUE(
-      send_channel->AddSendStream(StreamParams::CreateLegacy(send_ssrc)));
+  send_channel->AsSendChannel()->OnReadyToSend(true);
+  EXPECT_TRUE(send_channel->AsSendChannel()->AddSendStream(
+      StreamParams::CreateLegacy(send_ssrc)));
   EXPECT_TRUE(send_channel->SetSend(true));
 
   // Set capturer.
@@ -1280,15 +1287,15 @@
   cricket::VideoRecvParameters recv_parameters;
   recv_parameters.codecs.push_back(engine_codecs.at(0));
   EXPECT_TRUE(recv_channel->SetRecvParameters(recv_parameters));
-  EXPECT_TRUE(recv_channel->AddRecvStream(
+  EXPECT_TRUE(recv_channel->AsReceiveChannel()->AddRecvStream(
       cricket::StreamParams::CreateLegacy(recv_ssrc)));
 
   // Remove streams previously added to free the encoder and decoder instance.
   EXPECT_CALL(*encoder_factory, Die());
   EXPECT_CALL(*decoder_factory, Die());
   EXPECT_CALL(*rate_allocator_factory, Die());
-  EXPECT_TRUE(send_channel->RemoveSendStream(send_ssrc));
-  EXPECT_TRUE(recv_channel->RemoveRecvStream(recv_ssrc));
+  EXPECT_TRUE(send_channel->AsSendChannel()->RemoveSendStream(send_ssrc));
+  EXPECT_TRUE(recv_channel->AsReceiveChannel()->RemoveRecvStream(recv_ssrc));
 }
 
 TEST_F(WebRtcVideoEngineTest, DISABLED_RecreatesEncoderOnContentTypeChange) {
@@ -1296,12 +1303,12 @@
   std::unique_ptr<FakeCall> fake_call(new FakeCall());
   std::unique_ptr<VideoMediaChannel> channel(
       SetSendParamsWithAllSupportedCodecs());
-  ASSERT_TRUE(
-      channel->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+  ASSERT_TRUE(channel->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(kSsrc)));
   cricket::VideoCodec codec = GetEngineCodec("VP8");
   cricket::VideoSendParameters parameters;
   parameters.codecs.push_back(codec);
-  channel->OnReadyToSend(true);
+  channel->AsSendChannel()->OnReadyToSend(true);
   channel->SetSend(true);
   ASSERT_TRUE(channel->SetSendParameters(parameters));
 
@@ -1347,7 +1354,7 @@
             encoder_factory_->encoders().back()->GetCodecSettings().mode);
 
   // Remove stream previously added to free the external encoder instance.
-  EXPECT_TRUE(channel->RemoveSendStream(kSsrc));
+  EXPECT_TRUE(channel->AsSendChannel()->RemoveSendStream(kSsrc));
   EXPECT_EQ(0u, encoder_factory_->encoders().size());
 }
 
@@ -1467,7 +1474,7 @@
   EXPECT_TRUE(channel_->SetSink(kSsrc, &renderer_));
   DeliverKeyFrame(kSsrc);
   EXPECT_EQ_WAIT(1, renderer_.num_rendered_frames(), kTimeout);
-  channel_->RemoveRecvStream(kSsrc);
+  channel_->AsReceiveChannel()->RemoveRecvStream(kSsrc);
 }
 
 TEST_F(WebRtcVideoChannelEncodedFrameCallbackTest,
@@ -1480,7 +1487,7 @@
   channel_->SetRecordableEncodedFrameCallback(kSsrc, callback.AsStdFunction());
   DeliverKeyFrame(kSsrc);
   EXPECT_EQ_WAIT(1, renderer_.num_rendered_frames(), kTimeout);
-  channel_->RemoveRecvStream(kSsrc);
+  channel_->AsReceiveChannel()->RemoveRecvStream(kSsrc);
 }
 
 TEST_F(WebRtcVideoChannelEncodedFrameCallbackTest,
@@ -1493,7 +1500,7 @@
   channel_->SetRecordableEncodedFrameCallback(kSsrc, callback.AsStdFunction());
   DeliverKeyFrame(kSsrc);
   EXPECT_EQ_WAIT(1, renderer_.num_rendered_frames(), kTimeout);
-  channel_->RemoveRecvStream(kSsrc);
+  channel_->AsReceiveChannel()->RemoveRecvStream(kSsrc);
 }
 
 TEST_F(WebRtcVideoChannelEncodedFrameCallbackTest,
@@ -1508,7 +1515,7 @@
   channel_->SetRecordableEncodedFrameCallback(kSsrc, callback.AsStdFunction());
   DeliverKeyFrame(kSsrc);  // Expected to not cause function to fire.
   DeliverKeyFrameAndWait(kSsrc + 1);
-  channel_->RemoveRecvStream(kSsrc + 1);
+  channel_->AsReceiveChannel()->RemoveRecvStream(kSsrc + 1);
 }
 
 TEST_F(WebRtcVideoChannelEncodedFrameCallbackTest,
@@ -1523,7 +1530,7 @@
   channel_->SetRecordableEncodedFrameCallback(kSsrc, callback.AsStdFunction());
   DeliverKeyFrame(kSsrc);  // Expected to not cause function to fire.
   DeliverKeyFrameAndWait(kSsrc + 1);
-  channel_->RemoveRecvStream(kSsrc + 1);
+  channel_->AsReceiveChannel()->RemoveRecvStream(kSsrc + 1);
 }
 
 class WebRtcVideoChannelBaseTest : public ::testing::Test {
@@ -1554,14 +1561,15 @@
         static_cast<cricket::WebRtcVideoChannel*>(engine_.CreateMediaChannel(
             call_.get(), media_config, cricket::VideoOptions(),
             webrtc::CryptoOptions(), video_bitrate_allocator_factory_.get())));
-    channel_->OnReadyToSend(true);
+    channel_->AsSendChannel()->OnReadyToSend(true);
     EXPECT_TRUE(channel_.get() != NULL);
     network_interface_.SetDestination(channel_.get());
     channel_->SetInterface(&network_interface_);
     cricket::VideoRecvParameters parameters;
     parameters.codecs = engine_.send_codecs();
     channel_->SetRecvParameters(parameters);
-    EXPECT_TRUE(channel_->AddSendStream(DefaultSendStreamParams()));
+    EXPECT_TRUE(
+        channel_->AsSendChannel()->AddSendStream(DefaultSendStreamParams()));
     frame_forwarder_ = std::make_unique<webrtc::test::FrameForwarder>();
     frame_source_ = std::make_unique<cricket::FakeFrameSource>(
         640, 480, rtc::kNumMicrosecsPerSec / kFramerate);
@@ -1573,7 +1581,7 @@
   void SetUpSecondStream() {
     SetUpSecondStreamWithNoRecv();
     // Setup recv for second stream.
-    EXPECT_TRUE(channel_->AddRecvStream(
+    EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
         cricket::StreamParams::CreateLegacy(kSsrc + 2)));
     // Make the second renderer available for use by a new stream.
     EXPECT_TRUE(channel_->SetSink(kSsrc + 2, &renderer2_));
@@ -1583,12 +1591,12 @@
   // This is required if you want to test unsignalled recv of video rtp packets.
   void SetUpSecondStreamWithNoRecv() {
     // SetUp() already added kSsrc make sure duplicate SSRCs cant be added.
-    EXPECT_TRUE(
-        channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+    EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
+        cricket::StreamParams::CreateLegacy(kSsrc)));
     EXPECT_TRUE(channel_->SetSink(kSsrc, &renderer_));
-    EXPECT_FALSE(
-        channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
-    EXPECT_TRUE(channel_->AddSendStream(
+    EXPECT_FALSE(channel_->AsSendChannel()->AddSendStream(
+        cricket::StreamParams::CreateLegacy(kSsrc)));
+    EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
         cricket::StreamParams::CreateLegacy(kSsrc + 2)));
     // We dont add recv for the second stream.
 
@@ -1841,8 +1849,10 @@
   parameters.conference_mode = true;
   EXPECT_TRUE(channel_->SetSendParameters(parameters));
   EXPECT_TRUE(SetSend(true));
-  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(1)));
-  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(2)));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(1)));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(2)));
   EXPECT_TRUE(channel_->SetSink(1, &renderer1));
   EXPECT_TRUE(channel_->SetSink(2, &renderer2));
   EXPECT_EQ(0, renderer1.num_rendered_frames());
@@ -1891,8 +1901,8 @@
   parameters.codecs.push_back(DefaultCodec());
   parameters.conference_mode = true;
   EXPECT_TRUE(channel_->SetSendParameters(parameters));
-  EXPECT_TRUE(
-      channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(kSsrc)));
   EXPECT_TRUE(channel_->SetSink(kSsrc, &renderer_));
   EXPECT_TRUE(SetSend(true));
   SendFrame();
@@ -1906,11 +1916,11 @@
   const int kTestHeight = 120;
   cricket::FakeFrameSource frame_source(kTestWidth, kTestHeight,
                                         rtc::kNumMicrosecsPerSec / 5);
-  EXPECT_TRUE(
-      channel_->AddSendStream(cricket::StreamParams::CreateLegacy(5678)));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(5678)));
   EXPECT_TRUE(channel_->SetVideoSend(5678, nullptr, &frame_forwarder));
-  EXPECT_TRUE(
-      channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(5678)));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(5678)));
   EXPECT_TRUE(channel_->SetSink(5678, &renderer2));
   frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
   EXPECT_FRAME_ON_RENDERER_WAIT(renderer2, 1, kTestWidth, kTestHeight,
@@ -1978,10 +1988,10 @@
 // Test that we can set the SSRC even after codecs are set.
 TEST_F(WebRtcVideoChannelBaseTest, SetSendSsrcAfterSetCodecs) {
   // Remove stream added in Setup.
-  EXPECT_TRUE(channel_->RemoveSendStream(kSsrc));
+  EXPECT_TRUE(channel_->AsSendChannel()->RemoveSendStream(kSsrc));
   EXPECT_TRUE(SetDefaultCodec());
-  EXPECT_TRUE(
-      channel_->AddSendStream(cricket::StreamParams::CreateLegacy(999)));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(999)));
   EXPECT_TRUE(channel_->SetVideoSend(999u, nullptr, frame_forwarder_.get()));
   EXPECT_TRUE(SetSend(true));
   EXPECT_TRUE(WaitAndSendFrame(0));
@@ -2027,11 +2037,11 @@
   EXPECT_EQ(kSsrc, header.Ssrc());
 
   // Remove the send stream that was added during Setup.
-  EXPECT_TRUE(channel_->RemoveSendStream(kSsrc));
+  EXPECT_TRUE(channel_->AsSendChannel()->RemoveSendStream(kSsrc));
   int rtp_packets = NumRtpPackets();
 
-  EXPECT_TRUE(
-      channel_->AddSendStream(cricket::StreamParams::CreateLegacy(789u)));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(789u)));
   EXPECT_TRUE(channel_->SetVideoSend(789u, nullptr, frame_forwarder_.get()));
   EXPECT_EQ(rtp_packets, NumRtpPackets());
   // Wait 30ms to guarantee the engine does not drop the frame.
@@ -2052,8 +2062,10 @@
   parameters.conference_mode = true;
   EXPECT_TRUE(channel_->SetSendParameters(parameters));
   EXPECT_TRUE(SetSend(true));
-  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(1)));
-  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(2)));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(1)));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(2)));
   EXPECT_TRUE(channel_->SetSink(1, &renderer1));
   EXPECT_TRUE(channel_->SetSink(2, &renderer2));
   EXPECT_EQ(0, renderer1.num_rendered_frames());
@@ -2073,8 +2085,8 @@
   EXPECT_EQ(kVideoHeight, renderer1.height());
   EXPECT_EQ(kVideoWidth, renderer2.width());
   EXPECT_EQ(kVideoHeight, renderer2.height());
-  EXPECT_TRUE(channel_->RemoveRecvStream(2));
-  EXPECT_TRUE(channel_->RemoveRecvStream(1));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->RemoveRecvStream(2));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->RemoveRecvStream(1));
 }
 
 // Tests that we can add and remove capturers and frames are sent out properly
@@ -2172,8 +2184,8 @@
   // WebRTC implementation will drop frames if pushed to quickly. Wait the
   // interval time to avoid that.
   // Set up the stream associated with the engine.
-  EXPECT_TRUE(
-      channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(kSsrc)));
   EXPECT_TRUE(channel_->SetSink(kSsrc, &renderer_));
   cricket::VideoFormat capture_format(
       kVideoWidth, kVideoHeight,
@@ -2181,9 +2193,11 @@
   // Set up additional stream 1.
   cricket::FakeVideoRenderer renderer1;
   EXPECT_FALSE(channel_->SetSink(1, &renderer1));
-  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(1)));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(1)));
   EXPECT_TRUE(channel_->SetSink(1, &renderer1));
-  EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(1)));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(1)));
 
   webrtc::test::FrameForwarder frame_forwarder1;
   cricket::FakeFrameSource frame_source(kVideoWidth, kVideoHeight,
@@ -2192,9 +2206,11 @@
   // Set up additional stream 2.
   cricket::FakeVideoRenderer renderer2;
   EXPECT_FALSE(channel_->SetSink(2, &renderer2));
-  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(2)));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(2)));
   EXPECT_TRUE(channel_->SetSink(2, &renderer2));
-  EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(2)));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(2)));
   webrtc::test::FrameForwarder frame_forwarder2;
 
   // State for all the streams.
@@ -2230,29 +2246,31 @@
 // Tests empty StreamParams is rejected.
 TEST_F(WebRtcVideoChannelBaseTest, RejectEmptyStreamParams) {
   // Remove the send stream that was added during Setup.
-  EXPECT_TRUE(channel_->RemoveSendStream(kSsrc));
+  EXPECT_TRUE(channel_->AsSendChannel()->RemoveSendStream(kSsrc));
 
   cricket::StreamParams empty;
-  EXPECT_FALSE(channel_->AddSendStream(empty));
-  EXPECT_TRUE(
-      channel_->AddSendStream(cricket::StreamParams::CreateLegacy(789u)));
+  EXPECT_FALSE(channel_->AsSendChannel()->AddSendStream(empty));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(789u)));
 }
 
 // Test that multiple send streams can be created and deleted properly.
 TEST_F(WebRtcVideoChannelBaseTest, MultipleSendStreams) {
   // Remove stream added in Setup. I.e. remove stream corresponding to default
   // channel.
-  EXPECT_TRUE(channel_->RemoveSendStream(kSsrc));
+  EXPECT_TRUE(channel_->AsSendChannel()->RemoveSendStream(kSsrc));
   const unsigned int kSsrcsSize = sizeof(kSsrcs4) / sizeof(kSsrcs4[0]);
   for (unsigned int i = 0; i < kSsrcsSize; ++i) {
-    EXPECT_TRUE(channel_->AddSendStream(
+    EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
         cricket::StreamParams::CreateLegacy(kSsrcs4[i])));
   }
   // Delete one of the non default channel streams, let the destructor delete
   // the remaining ones.
-  EXPECT_TRUE(channel_->RemoveSendStream(kSsrcs4[kSsrcsSize - 1]));
+  EXPECT_TRUE(
+      channel_->AsSendChannel()->RemoveSendStream(kSsrcs4[kSsrcsSize - 1]));
   // Stream should already be deleted.
-  EXPECT_FALSE(channel_->RemoveSendStream(kSsrcs4[kSsrcsSize - 1]));
+  EXPECT_FALSE(
+      channel_->AsSendChannel()->RemoveSendStream(kSsrcs4[kSsrcsSize - 1]));
 }
 
 TEST_F(WebRtcVideoChannelBaseTest, SendAndReceiveVp8Vga) {
@@ -2366,7 +2384,8 @@
   EXPECT_TRUE(channel_->SetSendParameters(parameters));
   channel_->SetVideoCodecSwitchingEnabled(true);
 
-  auto send_codecs = channel_->GetRtpSendParameters(kSsrc).codecs;
+  auto send_codecs =
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrc).codecs;
   ASSERT_EQ(send_codecs.size(), 2u);
   EXPECT_THAT("VP9", send_codecs[0].name);
 
@@ -2375,7 +2394,7 @@
   channel_->RequestEncoderFallback();
   rtc::Thread::Current()->ProcessMessages(30);
 
-  send_codecs = channel_->GetRtpSendParameters(kSsrc).codecs;
+  send_codecs = channel_->AsSendChannel()->GetRtpSendParameters(kSsrc).codecs;
   ASSERT_EQ(send_codecs.size(), 2u);
   EXPECT_THAT("VP8", send_codecs[0].name);
 }
@@ -2400,7 +2419,7 @@
     channel_.reset(engine_.CreateMediaChannel(
         fake_call_.get(), GetMediaConfig(), VideoOptions(),
         webrtc::CryptoOptions(), video_bitrate_allocator_factory_.get()));
-    channel_->OnReadyToSend(true);
+    channel_->AsSendChannel()->OnReadyToSend(true);
     last_ssrc_ = 123;
     send_parameters_.codecs = engine_.send_codecs();
     recv_parameters_.codecs = engine_.recv_codecs();
@@ -2434,7 +2453,7 @@
   // the unsignalled receive stream cooldown is no longer in effect.
   void ReceivePacketAndAdvanceTime(rtc::CopyOnWriteBuffer packet,
                                    int64_t packet_time_us) {
-    channel_->OnPacketReceived(packet, packet_time_us);
+    channel_->AsReceiveChannel()->OnPacketReceived(packet, packet_time_us);
     rtc::Thread::Current()->ProcessMessages(0);
     time_controller_.AdvanceTime(
         webrtc::TimeDelta::Millis(kUnsignalledReceiveStreamCooldownMs));
@@ -2447,7 +2466,7 @@
 
   FakeVideoSendStream* AddSendStream(const StreamParams& sp) {
     size_t num_streams = fake_call_->GetVideoSendStreams().size();
-    EXPECT_TRUE(channel_->AddSendStream(sp));
+    EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(sp));
     std::vector<FakeVideoSendStream*> streams =
         fake_call_->GetVideoSendStreams();
     EXPECT_EQ(num_streams + 1, streams.size());
@@ -2464,7 +2483,7 @@
 
   FakeVideoReceiveStream* AddRecvStream(const StreamParams& sp) {
     size_t num_streams = fake_call_->GetVideoReceiveStreams().size();
-    EXPECT_TRUE(channel_->AddRecvStream(sp));
+    EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(sp));
     std::vector<FakeVideoReceiveStream*> streams =
         fake_call_->GetVideoReceiveStreams();
     EXPECT_EQ(num_streams + 1, streams.size());
@@ -2509,8 +2528,8 @@
   void TestExtmapAllowMixedCaller(bool extmap_allow_mixed) {
     // For a caller, the answer will be applied in set remote description
     // where SetSendParameters() is called.
-    EXPECT_TRUE(
-        channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+    EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+        cricket::StreamParams::CreateLegacy(kSsrc)));
     send_parameters_.extmap_allow_mixed = extmap_allow_mixed;
     EXPECT_TRUE(channel_->SetSendParameters(send_parameters_));
     const webrtc::VideoSendStream::Config& config =
@@ -2521,9 +2540,9 @@
   void TestExtmapAllowMixedCallee(bool extmap_allow_mixed) {
     // For a callee, the answer will be applied in set local description
     // where SetExtmapAllowMixed() and AddSendStream() are called.
-    channel_->SetExtmapAllowMixed(extmap_allow_mixed);
-    EXPECT_TRUE(
-        channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+    channel_->AsSendChannel()->SetExtmapAllowMixed(extmap_allow_mixed);
+    EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+        cricket::StreamParams::CreateLegacy(kSsrc)));
     const webrtc::VideoSendStream::Config& config =
         fake_call_->GetVideoSendStreams()[0]->GetConfig();
     EXPECT_EQ(extmap_allow_mixed, config.rtp.extmap_allow_mixed);
@@ -2702,12 +2721,14 @@
     limited_send_params.max_bandwidth_bps = global_max;
     EXPECT_TRUE(channel_->SetSendParameters(limited_send_params));
     webrtc::RtpParameters parameters =
-        channel_->GetRtpSendParameters(last_ssrc_);
+        channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     EXPECT_EQ(1UL, parameters.encodings.size());
     parameters.encodings[0].max_bitrate_bps = stream_max;
-    EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+    EXPECT_TRUE(channel_->AsSendChannel()
+                    ->SetRtpSendParameters(last_ssrc_, parameters)
+                    .ok());
     // Read back the parameteres and verify they have the correct value
-    parameters = channel_->GetRtpSendParameters(last_ssrc_);
+    parameters = channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     EXPECT_EQ(1UL, parameters.encodings.size());
     EXPECT_EQ(stream_max, parameters.encodings[0].max_bitrate_bps);
     // Verify that the new value propagated down to the encoder
@@ -2743,7 +2764,7 @@
 
   cricket::StreamParams sp = cricket::StreamParams::CreateLegacy(kVideoSsrc);
   sp.set_stream_ids({kSyncLabel});
-  EXPECT_TRUE(channel_->AddRecvStream(sp));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(sp));
 
   EXPECT_EQ(1u, fake_call_->GetVideoReceiveStreams().size());
   EXPECT_EQ(kSyncLabel,
@@ -3037,7 +3058,8 @@
 }
 
 TEST_F(WebRtcVideoChannelTest, AddRecvStreamOnlyUsesOneReceiveStream) {
-  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(1)));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(1)));
   EXPECT_EQ(1u, fake_call_->GetVideoReceiveStreams().size());
 }
 
@@ -3318,7 +3340,7 @@
   channel_.reset(engine_.CreateMediaChannel(
       fake_call_.get(), media_config, VideoOptions(), webrtc::CryptoOptions(),
       video_bitrate_allocator_factory_.get()));
-  channel_->OnReadyToSend(true);
+  channel_->AsSendChannel()->OnReadyToSend(true);
 
   channel_->SetSendParameters(send_parameters_);
 
@@ -3329,7 +3351,7 @@
   channel_.reset(engine_.CreateMediaChannel(
       fake_call_.get(), media_config, VideoOptions(), webrtc::CryptoOptions(),
       video_bitrate_allocator_factory_.get()));
-  channel_->OnReadyToSend(true);
+  channel_->AsSendChannel()->OnReadyToSend(true);
 
   channel_->SetSendParameters(send_parameters_);
 
@@ -3498,13 +3520,15 @@
   EXPECT_TRUE(vp9_settings.automaticResizeOn);
 
   webrtc::RtpParameters rtp_parameters =
-      channel_->GetRtpSendParameters(last_ssrc_);
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_THAT(
       rtp_parameters.encodings,
       ElementsAre(Field(&webrtc::RtpEncodingParameters::scalability_mode,
                         absl::nullopt)));
   rtp_parameters.encodings[0].scalability_mode = "L2T1";
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, rtp_parameters)
+                  .ok());
 
   ASSERT_TRUE(stream->GetVp9Settings(&vp9_settings)) << "No VP9 config set.";
   EXPECT_TRUE(vp9_settings.denoisingOn);
@@ -3512,12 +3536,14 @@
   EXPECT_FALSE(vp9_settings.automaticResizeOn)
       << "Automatic resize off for multiple spatial layers.";
 
-  rtp_parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  rtp_parameters = channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_THAT(rtp_parameters.encodings,
               ElementsAre(Field(
                   &webrtc::RtpEncodingParameters::scalability_mode, "L2T1")));
   rtp_parameters.encodings[0].scalability_mode = "L1T1";
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, rtp_parameters)
+                  .ok());
 
   ASSERT_TRUE(stream->GetVp9Settings(&vp9_settings)) << "No VP9 config set.";
   EXPECT_TRUE(vp9_settings.denoisingOn);
@@ -3619,14 +3645,17 @@
   FakeVideoSendStream* stream =
       AddSendStream(CreateSimStreamParams("cname", ssrcs));
 
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(ssrcs[0]);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(ssrcs[0]);
   ASSERT_EQ(kNumSpatialLayers, parameters.encodings.size());
   ASSERT_TRUE(parameters.encodings[0].active);
   ASSERT_TRUE(parameters.encodings[1].active);
   ASSERT_TRUE(parameters.encodings[2].active);
   // Invert value to verify copying.
   parameters.encodings[1].active = false;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(ssrcs[0], parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(ssrcs[0], parameters)
+                  .ok());
 
   webrtc::VideoEncoderConfig encoder_config = stream->GetEncoderConfig().Copy();
 
@@ -3859,7 +3888,7 @@
   channel_.reset(engine_.CreateMediaChannel(
       fake_call_.get(), media_config, VideoOptions(), webrtc::CryptoOptions(),
       video_bitrate_allocator_factory_.get()));
-  channel_->OnReadyToSend(true);
+  channel_->AsSendChannel()->OnReadyToSend(true);
   ASSERT_TRUE(channel_->SetSendParameters(parameters));
 
   AddSendStream();
@@ -3909,7 +3938,7 @@
   channel_.reset(engine_.CreateMediaChannel(
       fake_call_.get(), media_config, VideoOptions(), webrtc::CryptoOptions(),
       video_bitrate_allocator_factory_.get()));
-  channel_->OnReadyToSend(true);
+  channel_->AsSendChannel()->OnReadyToSend(true);
 
   EXPECT_TRUE(channel_->SetSendParameters(parameters));
 
@@ -3942,7 +3971,7 @@
   channel_.reset(engine_.CreateMediaChannel(
       fake_call_.get(), media_config, VideoOptions(), webrtc::CryptoOptions(),
       video_bitrate_allocator_factory_.get()));
-  channel_->OnReadyToSend(true);
+  channel_->AsSendChannel()->OnReadyToSend(true);
 
   EXPECT_TRUE(channel_->SetSendParameters(parameters));
 
@@ -4673,10 +4702,13 @@
   EXPECT_EQ(300000, video_send_stream->GetVideoStreams()[0].max_bitrate_bps);
 
   // The RtpParameter max bitrate overrides the codec's.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   ASSERT_EQ(1u, parameters.encodings.size());
   parameters.encodings[0].max_bitrate_bps = 500000;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
   ASSERT_EQ(1u, video_send_stream->GetVideoStreams().size());
   EXPECT_EQ(parameters.encodings[0].max_bitrate_bps,
             video_send_stream->GetVideoStreams()[0].max_bitrate_bps);
@@ -4693,16 +4725,21 @@
             stream->GetVideoStreams()[0].max_bitrate_bps);
 
   // Get and set the rtp encoding parameters.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(1u, parameters.encodings.size());
 
   parameters.encodings[0].max_bitrate_bps = 99999 - 1;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
   EXPECT_EQ(parameters.encodings[0].max_bitrate_bps,
             stream->GetVideoStreams()[0].max_bitrate_bps);
 
   parameters.encodings[0].max_bitrate_bps = 99999 + 1;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
   EXPECT_EQ(send_parameters_.max_bandwidth_bps,
             stream->GetVideoStreams()[0].max_bitrate_bps);
 }
@@ -5288,16 +5325,21 @@
   EXPECT_EQ(rtc::DSCP_DEFAULT, network_interface->dscp());
 
   // Create a send stream to configure
-  EXPECT_TRUE(channel->AddSendStream(StreamParams::CreateLegacy(kSsrc)));
-  parameters = channel->GetRtpSendParameters(kSsrc);
+  EXPECT_TRUE(channel->AsSendChannel()->AddSendStream(
+      StreamParams::CreateLegacy(kSsrc)));
+  parameters = channel->AsSendChannel()->GetRtpSendParameters(kSsrc);
   ASSERT_FALSE(parameters.encodings.empty());
 
   // Various priorities map to various dscp values.
   parameters.encodings[0].network_priority = webrtc::Priority::kHigh;
-  ASSERT_TRUE(channel->SetRtpSendParameters(kSsrc, parameters, nullptr).ok());
+  ASSERT_TRUE(channel->AsSendChannel()
+                  ->SetRtpSendParameters(kSsrc, parameters, nullptr)
+                  .ok());
   EXPECT_EQ(rtc::DSCP_AF41, network_interface->dscp());
   parameters.encodings[0].network_priority = webrtc::Priority::kVeryLow;
-  ASSERT_TRUE(channel->SetRtpSendParameters(kSsrc, parameters, nullptr).ok());
+  ASSERT_TRUE(channel->AsSendChannel()
+                  ->SetRtpSendParameters(kSsrc, parameters, nullptr)
+                  .ok());
   EXPECT_EQ(rtc::DSCP_CS1, network_interface->dscp());
 
   // Packets should also self-identify their dscp in PacketOptions.
@@ -5326,7 +5368,7 @@
   FakeVideoSendStream* stream1 = AddSendStream();
   EXPECT_EQ(webrtc::RtcpMode::kCompound, stream1->GetConfig().rtp.rtcp_mode);
   webrtc::RtpParameters rtp_parameters =
-      channel_->GetRtpSendParameters(last_ssrc_);
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_FALSE(rtp_parameters.rtcp.reduced_size);
 
   // Now enable reduced size mode.
@@ -5334,7 +5376,7 @@
   EXPECT_TRUE(channel_->SetSendParameters(send_parameters_));
   stream1 = fake_call_->GetVideoSendStreams()[0];
   EXPECT_EQ(webrtc::RtcpMode::kReducedSize, stream1->GetConfig().rtp.rtcp_mode);
-  rtp_parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  rtp_parameters = channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_TRUE(rtp_parameters.rtcp.reduced_size);
 
   // Create a new stream and ensure it picks up the reduced size mode.
@@ -5368,13 +5410,13 @@
   EXPECT_EQ(webrtc::kNetworkUp,
             fake_call_->GetNetworkState(webrtc::MediaType::AUDIO));
 
-  channel_->OnReadyToSend(false);
+  channel_->AsSendChannel()->OnReadyToSend(false);
   EXPECT_EQ(webrtc::kNetworkDown,
             fake_call_->GetNetworkState(webrtc::MediaType::VIDEO));
   EXPECT_EQ(webrtc::kNetworkUp,
             fake_call_->GetNetworkState(webrtc::MediaType::AUDIO));
 
-  channel_->OnReadyToSend(true);
+  channel_->AsSendChannel()->OnReadyToSend(true);
   EXPECT_EQ(webrtc::kNetworkUp,
             fake_call_->GetNetworkState(webrtc::MediaType::VIDEO));
   EXPECT_EQ(webrtc::kNetworkUp,
@@ -5834,11 +5876,12 @@
   FakeVideoSendStream* stream =
       AddSendStream(cricket::CreateSimStreamParams("cname", {kSsrc1, kSsrc2}));
 
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(kSsrc1);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrc1);
   ASSERT_EQ(2u, parameters.encodings.size());
   parameters.encodings[0].active = false;
   parameters.encodings[1].active = true;
-  channel_->SetRtpSendParameters(kSsrc1, parameters);
+  channel_->AsSendChannel()->SetRtpSendParameters(kSsrc1, parameters);
 
   // Fill in dummy stats.
   auto stats = GetInitialisedStats();
@@ -5876,12 +5919,13 @@
   ASSERT_TRUE(stream->GetVp9Settings(&vp9_settings));
   EXPECT_EQ(vp9_settings.numberOfSpatialLayers, 3u);
 
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(kSsrc1);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrc1);
   ASSERT_EQ(3u, parameters.encodings.size());
   parameters.encodings[0].active = false;
   parameters.encodings[1].active = true;
   parameters.encodings[2].active = false;
-  channel_->SetRtpSendParameters(kSsrc1, parameters);
+  channel_->AsSendChannel()->SetRtpSendParameters(kSsrc1, parameters);
 
   // Fill in dummy stats.
   auto stats = GetInitialisedStats();
@@ -5896,12 +5940,12 @@
   ASSERT_TRUE(video_media_info.senders[0].active.has_value());
   EXPECT_TRUE(video_media_info.senders[0].active.value());
 
-  parameters = channel_->GetRtpSendParameters(kSsrc1);
+  parameters = channel_->AsSendChannel()->GetRtpSendParameters(kSsrc1);
   ASSERT_EQ(3u, parameters.encodings.size());
   parameters.encodings[0].active = false;
   parameters.encodings[1].active = false;
   parameters.encodings[2].active = false;
-  channel_->SetRtpSendParameters(kSsrc1, parameters);
+  channel_->AsSendChannel()->SetRtpSendParameters(kSsrc1, parameters);
   ASSERT_TRUE(channel_->GetStats(&video_media_info));
   ASSERT_EQ(video_media_info.senders.size(), 1u);
   // No layer is active.
@@ -6392,7 +6436,7 @@
   EXPECT_EQ(0u, recv_stream->GetConfig().rtp.rtx_ssrc)
       << "Default receive stream should not have configured RTX";
 
-  EXPECT_TRUE(channel_->AddRecvStream(
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
       cricket::CreateSimWithRtxStreamParams("cname", ssrcs, rtx_ssrcs)));
   ASSERT_EQ(1u, fake_call_->GetVideoReceiveStreams().size())
       << "AddRecvStream should have reconfigured, not added a new receiver.";
@@ -6417,8 +6461,8 @@
       cricket::CreateSimWithRtxStreamParams("cname", ssrcs, rtx_ssrcs);
   sp.ssrcs = ssrcs;  // Without RTXs, this is the important part.
 
-  EXPECT_FALSE(channel_->AddSendStream(sp));
-  EXPECT_FALSE(channel_->AddRecvStream(sp));
+  EXPECT_FALSE(channel_->AsSendChannel()->AddSendStream(sp));
+  EXPECT_FALSE(channel_->AsReceiveChannel()->AddRecvStream(sp));
 }
 
 TEST_F(WebRtcVideoChannelTest, RejectsAddingStreamsWithOverlappingRtxSsrcs) {
@@ -6430,20 +6474,20 @@
   StreamParams sp =
       cricket::CreateSimWithRtxStreamParams("cname", ssrcs, rtx_ssrcs);
 
-  EXPECT_TRUE(channel_->AddSendStream(sp));
-  EXPECT_TRUE(channel_->AddRecvStream(sp));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(sp));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(sp));
 
   // The RTX SSRC is already used in previous streams, using it should fail.
   sp = cricket::StreamParams::CreateLegacy(rtx_ssrcs[0]);
-  EXPECT_FALSE(channel_->AddSendStream(sp));
-  EXPECT_FALSE(channel_->AddRecvStream(sp));
+  EXPECT_FALSE(channel_->AsSendChannel()->AddSendStream(sp));
+  EXPECT_FALSE(channel_->AsReceiveChannel()->AddRecvStream(sp));
 
   // After removing the original stream this should be fine to add (makes sure
   // that RTX ssrcs are not forever taken).
-  EXPECT_TRUE(channel_->RemoveSendStream(ssrcs[0]));
-  EXPECT_TRUE(channel_->RemoveRecvStream(ssrcs[0]));
-  EXPECT_TRUE(channel_->AddSendStream(sp));
-  EXPECT_TRUE(channel_->AddRecvStream(sp));
+  EXPECT_TRUE(channel_->AsSendChannel()->RemoveSendStream(ssrcs[0]));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->RemoveRecvStream(ssrcs[0]));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(sp));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(sp));
 }
 
 TEST_F(WebRtcVideoChannelTest,
@@ -6455,21 +6499,23 @@
   StreamParams sp =
       cricket::CreateSimStreamParams("cname", MAKE_VECTOR(kFirstStreamSsrcs));
 
-  EXPECT_TRUE(channel_->AddSendStream(sp));
-  EXPECT_TRUE(channel_->AddRecvStream(sp));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(sp));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(sp));
 
   // One of the SSRCs is already used in previous streams, using it should fail.
   sp = cricket::CreateSimStreamParams("cname",
                                       MAKE_VECTOR(kOverlappingStreamSsrcs));
-  EXPECT_FALSE(channel_->AddSendStream(sp));
-  EXPECT_FALSE(channel_->AddRecvStream(sp));
+  EXPECT_FALSE(channel_->AsSendChannel()->AddSendStream(sp));
+  EXPECT_FALSE(channel_->AsReceiveChannel()->AddRecvStream(sp));
 
   // After removing the original stream this should be fine to add (makes sure
   // that RTX ssrcs are not forever taken).
-  EXPECT_TRUE(channel_->RemoveSendStream(kFirstStreamSsrcs[0]));
-  EXPECT_TRUE(channel_->RemoveRecvStream(kFirstStreamSsrcs[0]));
-  EXPECT_TRUE(channel_->AddSendStream(sp));
-  EXPECT_TRUE(channel_->AddRecvStream(sp));
+  EXPECT_TRUE(
+      channel_->AsSendChannel()->RemoveSendStream(kFirstStreamSsrcs[0]));
+  EXPECT_TRUE(
+      channel_->AsReceiveChannel()->RemoveRecvStream(kFirstStreamSsrcs[0]));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(sp));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(sp));
 }
 
 TEST_F(WebRtcVideoChannelTest, ReportsSsrcGroupsInStats) {
@@ -6481,14 +6527,14 @@
   StreamParams sender_sp = cricket::CreateSimWithRtxStreamParams(
       "cname", MAKE_VECTOR(kSenderSsrcs), MAKE_VECTOR(kSenderRtxSsrcs));
 
-  EXPECT_TRUE(channel_->AddSendStream(sender_sp));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(sender_sp));
 
   static const uint32_t kReceiverSsrcs[] = {3};
   static const uint32_t kReceiverRtxSsrcs[] = {2};
 
   StreamParams receiver_sp = cricket::CreateSimWithRtxStreamParams(
       "cname", MAKE_VECTOR(kReceiverSsrcs), MAKE_VECTOR(kReceiverRtxSsrcs));
-  EXPECT_TRUE(channel_->AddRecvStream(receiver_sp));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(receiver_sp));
 
   cricket::VideoMediaInfo info;
   ASSERT_TRUE(channel_->GetStats(&info));
@@ -6531,9 +6577,9 @@
   const char kSyncLabel[] = "sync_label";
   cricket::StreamParams unsignaled_stream;
   unsignaled_stream.set_stream_ids({kSyncLabel});
-  ASSERT_TRUE(channel_->AddRecvStream(unsignaled_stream));
-  channel_->OnDemuxerCriteriaUpdatePending();
-  channel_->OnDemuxerCriteriaUpdateComplete();
+  ASSERT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(unsignaled_stream));
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdatePending();
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdateComplete();
   rtc::Thread::Current()->ProcessMessages(0);
   // The stream shouldn't have been created at this point because it doesn't
   // have any SSRCs.
@@ -6551,8 +6597,8 @@
 
   // Reset the unsignaled stream to clear the cache. This deletes the receive
   // stream.
-  channel_->ResetUnsignaledRecvStream();
-  channel_->OnDemuxerCriteriaUpdatePending();
+  channel_->AsReceiveChannel()->ResetUnsignaledRecvStream();
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdatePending();
   EXPECT_EQ(0u, fake_call_->GetVideoReceiveStreams().size());
 
   // Until the demuxer criteria has been updated, we ignore in-flight ssrcs of
@@ -6563,7 +6609,7 @@
   // After the demuxer criteria has been updated, we should proceed to create
   // unsignalled receive streams. This time when a default video receive stream
   // is created it won't have a sync_group.
-  channel_->OnDemuxerCriteriaUpdateComplete();
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdateComplete();
   ReceivePacketAndAdvanceTime(packet.Buffer(), /* packet_time_us */ -1);
   EXPECT_EQ(1u, fake_call_->GetVideoReceiveStreams().size());
   EXPECT_TRUE(
@@ -6587,9 +6633,9 @@
             kIncomingUnsignalledSsrc);
 
   // Stream with another SSRC gets signaled.
-  channel_->ResetUnsignaledRecvStream();
+  channel_->AsReceiveChannel()->ResetUnsignaledRecvStream();
   constexpr uint32_t kIncomingSignalledSsrc = kIncomingUnsignalledSsrc + 1;
-  ASSERT_TRUE(channel_->AddRecvStream(
+  ASSERT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
       cricket::StreamParams::CreateLegacy(kIncomingSignalledSsrc)));
 
   // New receiver is for the signaled stream.
@@ -6604,9 +6650,10 @@
   const uint32_t kSsrc2 = 2;
 
   // Starting point: receiving kSsrc1.
-  EXPECT_TRUE(channel_->AddRecvStream(StreamParams::CreateLegacy(kSsrc1)));
-  channel_->OnDemuxerCriteriaUpdatePending();
-  channel_->OnDemuxerCriteriaUpdateComplete();
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
+      StreamParams::CreateLegacy(kSsrc1)));
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdatePending();
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdateComplete();
   rtc::Thread::Current()->ProcessMessages(0);
   EXPECT_EQ(fake_call_->GetVideoReceiveStreams().size(), 1u);
 
@@ -6617,7 +6664,7 @@
 
   // Emulate a second m= section being created by updating the demuxer criteria
   // without adding any streams.
-  channel_->OnDemuxerCriteriaUpdatePending();
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdatePending();
 
   // Emulate there being in-flight packets for kSsrc1 and kSsrc2 arriving before
   // the demuxer is updated.
@@ -6643,7 +6690,7 @@
   // Signal that the demuxer update is complete. Because there are no more
   // pending demuxer updates, receiving unknown ssrcs (kSsrc2) should again
   // result in unsignalled receive streams being created.
-  channel_->OnDemuxerCriteriaUpdateComplete();
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdateComplete();
   rtc::Thread::Current()->ProcessMessages(0);
 
   // Receive packets for kSsrc1 and kSsrc2 again.
@@ -6673,10 +6720,12 @@
   const uint32_t kSsrc2 = 2;
 
   // Starting point: receiving kSsrc1 and kSsrc2.
-  EXPECT_TRUE(channel_->AddRecvStream(StreamParams::CreateLegacy(kSsrc1)));
-  EXPECT_TRUE(channel_->AddRecvStream(StreamParams::CreateLegacy(kSsrc2)));
-  channel_->OnDemuxerCriteriaUpdatePending();
-  channel_->OnDemuxerCriteriaUpdateComplete();
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
+      StreamParams::CreateLegacy(kSsrc1)));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
+      StreamParams::CreateLegacy(kSsrc2)));
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdatePending();
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdateComplete();
   rtc::Thread::Current()->ProcessMessages(0);
   EXPECT_EQ(fake_call_->GetVideoReceiveStreams().size(), 2u);
   EXPECT_EQ(fake_call_->GetDeliveredPacketsForSsrc(kSsrc1), 0u);
@@ -6684,8 +6733,8 @@
 
   // Remove kSsrc1, signal that a demuxer criteria update is pending, but not
   // completed yet.
-  EXPECT_TRUE(channel_->RemoveRecvStream(kSsrc1));
-  channel_->OnDemuxerCriteriaUpdatePending();
+  EXPECT_TRUE(channel_->AsReceiveChannel()->RemoveRecvStream(kSsrc1));
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdatePending();
 
   // We only have a receiver for kSsrc2 now.
   EXPECT_EQ(fake_call_->GetVideoReceiveStreams().size(), 1u);
@@ -6713,7 +6762,7 @@
 
   // Signal that the demuxer update is complete. This means we should stop
   // ignorning kSsrc1.
-  channel_->OnDemuxerCriteriaUpdateComplete();
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdateComplete();
   rtc::Thread::Current()->ProcessMessages(0);
 
   // Receive packets for kSsrc1 and kSsrc2 again.
@@ -6741,20 +6790,22 @@
   const uint32_t kSsrc = 1;
 
   // Starting point: receiving kSsrc.
-  EXPECT_TRUE(channel_->AddRecvStream(StreamParams::CreateLegacy(kSsrc)));
-  channel_->OnDemuxerCriteriaUpdatePending();
-  channel_->OnDemuxerCriteriaUpdateComplete();
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
+      StreamParams::CreateLegacy(kSsrc)));
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdatePending();
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdateComplete();
   rtc::Thread::Current()->ProcessMessages(0);
   ASSERT_EQ(fake_call_->GetVideoReceiveStreams().size(), 1u);
 
   // Remove kSsrc...
-  EXPECT_TRUE(channel_->RemoveRecvStream(kSsrc));
-  channel_->OnDemuxerCriteriaUpdatePending();
+  EXPECT_TRUE(channel_->AsReceiveChannel()->RemoveRecvStream(kSsrc));
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdatePending();
   EXPECT_EQ(fake_call_->GetVideoReceiveStreams().size(), 0u);
   // And then add it back again, before the demuxer knows about the new
   // criteria!
-  EXPECT_TRUE(channel_->AddRecvStream(StreamParams::CreateLegacy(kSsrc)));
-  channel_->OnDemuxerCriteriaUpdatePending();
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
+      StreamParams::CreateLegacy(kSsrc)));
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdatePending();
   EXPECT_EQ(fake_call_->GetVideoReceiveStreams().size(), 1u);
 
   // In-flight packets should arrive because the stream was recreated, even
@@ -6767,7 +6818,7 @@
   EXPECT_EQ(fake_call_->GetDeliveredPacketsForSsrc(kSsrc), 1u);
 
   // Signal that the demuxer knows about the first update: the removal.
-  channel_->OnDemuxerCriteriaUpdateComplete();
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdateComplete();
   rtc::Thread::Current()->ProcessMessages(0);
 
   // This still should not prevent in-flight packets from arriving because we
@@ -6780,8 +6831,8 @@
   EXPECT_EQ(fake_call_->GetDeliveredPacketsForSsrc(kSsrc), 2u);
 
   // Remove the kSsrc again while previous demuxer updates are still pending.
-  EXPECT_TRUE(channel_->RemoveRecvStream(kSsrc));
-  channel_->OnDemuxerCriteriaUpdatePending();
+  EXPECT_TRUE(channel_->AsReceiveChannel()->RemoveRecvStream(kSsrc));
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdatePending();
   EXPECT_EQ(fake_call_->GetVideoReceiveStreams().size(), 0u);
 
   // Now the packet should be dropped and not create an unsignalled receive
@@ -6795,7 +6846,7 @@
   EXPECT_EQ(fake_call_->GetDeliveredPacketsForSsrc(kSsrc), 2u);
 
   // Signal that the demuxer knows about the second update: adding it back.
-  channel_->OnDemuxerCriteriaUpdateComplete();
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdateComplete();
   rtc::Thread::Current()->ProcessMessages(0);
 
   // The packets should continue to be dropped because removal happened after
@@ -6809,7 +6860,7 @@
   EXPECT_EQ(fake_call_->GetDeliveredPacketsForSsrc(kSsrc), 2u);
 
   // Signal that the demuxer knows about the last update: the second removal.
-  channel_->OnDemuxerCriteriaUpdateComplete();
+  channel_->AsReceiveChannel()->OnDemuxerCriteriaUpdateComplete();
   rtc::Thread::Current()->ProcessMessages(0);
 
   // If packets still arrive after the demuxer knows about the latest removal we
@@ -6832,7 +6883,8 @@
     // Receive a packet for kSsrc1.
     RtpPacket packet;
     packet.SetSsrc(kSsrc1);
-    channel_->OnPacketReceived(packet.Buffer(), /* packet_time_us */ -1);
+    channel_->AsReceiveChannel()->OnPacketReceived(packet.Buffer(),
+                                                   /* packet_time_us */ -1);
   }
   rtc::Thread::Current()->ProcessMessages(0);
   time_controller_.AdvanceTime(
@@ -6847,7 +6899,8 @@
     // Receive a packet for kSsrc2.
     RtpPacket packet;
     packet.SetSsrc(kSsrc2);
-    channel_->OnPacketReceived(packet.Buffer(), /* packet_time_us */ -1);
+    channel_->AsReceiveChannel()->OnPacketReceived(packet.Buffer(),
+                                                   /* packet_time_us */ -1);
   }
   rtc::Thread::Current()->ProcessMessages(0);
 
@@ -6864,7 +6917,8 @@
     // Receive a packet for kSsrc2.
     RtpPacket packet;
     packet.SetSsrc(kSsrc2);
-    channel_->OnPacketReceived(packet.Buffer(), /* packet_time_us */ -1);
+    channel_->AsReceiveChannel()->OnPacketReceived(packet.Buffer(),
+                                                   /* packet_time_us */ -1);
   }
   rtc::Thread::Current()->ProcessMessages(0);
 
@@ -6879,17 +6933,22 @@
 // Test BaseMinimumPlayoutDelayMs on receive streams.
 TEST_F(WebRtcVideoChannelTest, BaseMinimumPlayoutDelayMs) {
   // Test that set won't work for non-existing receive streams.
-  EXPECT_FALSE(channel_->SetBaseMinimumPlayoutDelayMs(kSsrc + 2, 200));
+  EXPECT_FALSE(channel_->AsReceiveChannel()->SetBaseMinimumPlayoutDelayMs(
+      kSsrc + 2, 200));
   // Test that get won't work for non-existing receive streams.
-  EXPECT_FALSE(channel_->GetBaseMinimumPlayoutDelayMs(kSsrc + 2));
+  EXPECT_FALSE(
+      channel_->AsReceiveChannel()->GetBaseMinimumPlayoutDelayMs(kSsrc + 2));
 
   EXPECT_TRUE(AddRecvStream());
   // Test that set works for the existing receive stream.
-  EXPECT_TRUE(channel_->SetBaseMinimumPlayoutDelayMs(last_ssrc_, 200));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->SetBaseMinimumPlayoutDelayMs(
+      last_ssrc_, 200));
   auto* recv_stream = fake_call_->GetVideoReceiveStream(last_ssrc_);
   EXPECT_TRUE(recv_stream);
   EXPECT_EQ(recv_stream->base_mininum_playout_delay_ms(), 200);
-  EXPECT_EQ(channel_->GetBaseMinimumPlayoutDelayMs(last_ssrc_).value_or(0),
+  EXPECT_EQ(channel_->AsReceiveChannel()
+                ->GetBaseMinimumPlayoutDelayMs(last_ssrc_)
+                .value_or(0),
             200);
 }
 
@@ -6899,8 +6958,12 @@
   const FakeVideoReceiveStream* recv_stream;
 
   // Set default stream with SSRC 0
-  EXPECT_TRUE(channel_->SetBaseMinimumPlayoutDelayMs(0, 200));
-  EXPECT_EQ(200, channel_->GetBaseMinimumPlayoutDelayMs(0).value_or(0));
+  EXPECT_TRUE(
+      channel_->AsReceiveChannel()->SetBaseMinimumPlayoutDelayMs(0, 200));
+  EXPECT_EQ(
+      200,
+      channel_->AsReceiveChannel()->GetBaseMinimumPlayoutDelayMs(0).value_or(
+          0));
 
   // Spawn an unsignaled stream by sending a packet, it should inherit
   // default delay 200.
@@ -6910,14 +6973,20 @@
 
   recv_stream = fake_call_->GetVideoReceiveStream(kIncomingUnsignalledSsrc);
   EXPECT_EQ(recv_stream->base_mininum_playout_delay_ms(), 200);
-  delay_ms = channel_->GetBaseMinimumPlayoutDelayMs(kIncomingUnsignalledSsrc);
+  delay_ms = channel_->AsReceiveChannel()->GetBaseMinimumPlayoutDelayMs(
+      kIncomingUnsignalledSsrc);
   EXPECT_EQ(200, delay_ms.value_or(0));
 
   // Check that now if we change delay for SSRC 0 it will change delay for the
   // default receiving stream as well.
-  EXPECT_TRUE(channel_->SetBaseMinimumPlayoutDelayMs(0, 300));
-  EXPECT_EQ(300, channel_->GetBaseMinimumPlayoutDelayMs(0).value_or(0));
-  delay_ms = channel_->GetBaseMinimumPlayoutDelayMs(kIncomingUnsignalledSsrc);
+  EXPECT_TRUE(
+      channel_->AsReceiveChannel()->SetBaseMinimumPlayoutDelayMs(0, 300));
+  EXPECT_EQ(
+      300,
+      channel_->AsReceiveChannel()->GetBaseMinimumPlayoutDelayMs(0).value_or(
+          0));
+  delay_ms = channel_->AsReceiveChannel()->GetBaseMinimumPlayoutDelayMs(
+      kIncomingUnsignalledSsrc);
   EXPECT_EQ(300, delay_ms.value_or(0));
   recv_stream = fake_call_->GetVideoReceiveStream(kIncomingUnsignalledSsrc);
   EXPECT_EQ(recv_stream->base_mininum_playout_delay_ms(), 300);
@@ -7139,8 +7208,8 @@
   EXPECT_EQ(kSsrcs3[0], recv_stream0->GetConfig().rtp.remote_ssrc);
 
   // Signal the SSRC.
-  EXPECT_TRUE(
-      channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(kSsrcs3[0])));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(kSsrcs3[0])));
   ASSERT_EQ(1u, fake_call_->GetVideoReceiveStreams().size());
   recv_stream0 = fake_call_->GetVideoReceiveStreams()[0];
   EXPECT_EQ(kSsrcs3[0], recv_stream0->GetConfig().rtp.remote_ssrc);
@@ -7185,22 +7254,26 @@
 
 TEST_F(WebRtcVideoChannelTest, CannotSetMaxBitrateForNonexistentStream) {
   webrtc::RtpParameters nonexistent_parameters =
-      channel_->GetRtpSendParameters(last_ssrc_);
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(0u, nonexistent_parameters.encodings.size());
 
   nonexistent_parameters.encodings.push_back(webrtc::RtpEncodingParameters());
-  EXPECT_FALSE(
-      channel_->SetRtpSendParameters(last_ssrc_, nonexistent_parameters).ok());
+  EXPECT_FALSE(channel_->AsSendChannel()
+                   ->SetRtpSendParameters(last_ssrc_, nonexistent_parameters)
+                   .ok());
 }
 
 TEST_F(WebRtcVideoChannelTest,
        SetLowMaxBitrateOverwritesVideoStreamMinBitrate) {
   FakeVideoSendStream* stream = AddSendStream();
 
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(1UL, parameters.encodings.size());
   EXPECT_FALSE(parameters.encodings[0].max_bitrate_bps.has_value());
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   // Note that this is testing the behavior of the FakeVideoSendStream, which
   // also calls to CreateEncoderStreams to get the VideoStreams, so essentially
@@ -7212,10 +7285,12 @@
 
   // Set a low max bitrate & check that VideoStream.min_bitrate_bps is limited
   // by this amount.
-  parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  parameters = channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   int low_max_bitrate_bps = webrtc::kDefaultMinVideoBitrateBps - 1000;
   parameters.encodings[0].max_bitrate_bps = low_max_bitrate_bps;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   ASSERT_EQ(1UL, stream->GetVideoStreams().size());
   EXPECT_EQ(low_max_bitrate_bps, stream->GetVideoStreams()[0].min_bitrate_bps);
@@ -7234,10 +7309,13 @@
   int high_min_bitrate_bps = stream->GetVideoStreams()[0].max_bitrate_bps + 1;
 
   // Set a high min bitrate and check that max_bitrate_bps is adjusted up.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(1UL, parameters.encodings.size());
   parameters.encodings[0].min_bitrate_bps = high_min_bitrate_bps;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   ASSERT_EQ(1UL, stream->GetVideoStreams().size());
   EXPECT_EQ(high_min_bitrate_bps, stream->GetVideoStreams()[0].min_bitrate_bps);
@@ -7258,10 +7336,13 @@
 
   // Set min bitrate above global max bitrate and check that min_bitrate_bps is
   // adjusted down.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(1UL, parameters.encodings.size());
   parameters.encodings[0].min_bitrate_bps = 99999 + 1;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
   ASSERT_EQ(1UL, stream->GetVideoStreams().size());
   EXPECT_EQ(send_parameters_.max_bandwidth_bps,
             stream->GetVideoStreams()[0].min_bitrate_bps);
@@ -7272,10 +7353,13 @@
 TEST_F(WebRtcVideoChannelTest, SetMaxFramerateOneStream) {
   FakeVideoSendStream* stream = AddSendStream();
 
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(1UL, parameters.encodings.size());
   EXPECT_FALSE(parameters.encodings[0].max_framerate.has_value());
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   // Note that this is testing the behavior of the FakeVideoSendStream, which
   // also calls to CreateEncoderStreams to get the VideoStreams, so essentially
@@ -7287,9 +7371,11 @@
 
   // Set max framerate and check that VideoStream.max_framerate is set.
   const int kNewMaxFramerate = kDefaultVideoMaxFramerate - 1;
-  parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  parameters = channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   parameters.encodings[0].max_framerate = kNewMaxFramerate;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   ASSERT_EQ(1UL, stream->GetVideoStreams().size());
   EXPECT_EQ(kNewMaxFramerate, stream->GetVideoStreams()[0].max_framerate);
@@ -7298,10 +7384,13 @@
 TEST_F(WebRtcVideoChannelTest, SetNumTemporalLayersForSingleStream) {
   FakeVideoSendStream* stream = AddSendStream();
 
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(1UL, parameters.encodings.size());
   EXPECT_FALSE(parameters.encodings[0].num_temporal_layers.has_value());
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   // Note that this is testing the behavior of the FakeVideoSendStream, which
   // also calls to CreateEncoderStreams to get the VideoStreams, so essentially
@@ -7311,9 +7400,11 @@
   EXPECT_FALSE(stream->GetVideoStreams()[0].num_temporal_layers.has_value());
 
   // Set temporal layers and check that VideoStream.num_temporal_layers is set.
-  parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  parameters = channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   parameters.encodings[0].num_temporal_layers = 2;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   ASSERT_EQ(1UL, stream->GetVideoStreams().size());
   EXPECT_EQ(2UL, stream->GetVideoStreams()[0].num_temporal_layers);
@@ -7322,13 +7413,18 @@
 TEST_F(WebRtcVideoChannelTest,
        CannotSetRtpSendParametersWithIncorrectNumberOfEncodings) {
   AddSendStream();
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   // Two or more encodings should result in failure.
   parameters.encodings.push_back(webrtc::RtpEncodingParameters());
-  EXPECT_FALSE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_FALSE(channel_->AsSendChannel()
+                   ->SetRtpSendParameters(last_ssrc_, parameters)
+                   .ok());
   // Zero encodings should also fail.
   parameters.encodings.clear();
-  EXPECT_FALSE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_FALSE(channel_->AsSendChannel()
+                   ->SetRtpSendParameters(last_ssrc_, parameters)
+                   .ok());
 }
 
 TEST_F(WebRtcVideoChannelTest,
@@ -7337,44 +7433,58 @@
   StreamParams sp = CreateSimStreamParams("cname", ssrcs);
   AddSendStream(sp);
 
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
 
   // Additional encodings should result in failure.
   parameters.encodings.push_back(webrtc::RtpEncodingParameters());
-  EXPECT_FALSE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_FALSE(channel_->AsSendChannel()
+                   ->SetRtpSendParameters(last_ssrc_, parameters)
+                   .ok());
   // Zero encodings should also fail.
   parameters.encodings.clear();
-  EXPECT_FALSE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_FALSE(channel_->AsSendChannel()
+                   ->SetRtpSendParameters(last_ssrc_, parameters)
+                   .ok());
 }
 
 // Changing the SSRC through RtpParameters is not allowed.
 TEST_F(WebRtcVideoChannelTest, CannotSetSsrcInRtpSendParameters) {
   AddSendStream();
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   parameters.encodings[0].ssrc = 0xdeadbeef;
-  EXPECT_FALSE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_FALSE(channel_->AsSendChannel()
+                   ->SetRtpSendParameters(last_ssrc_, parameters)
+                   .ok());
 }
 
 // Tests that when RTCRtpEncodingParameters.bitrate_priority gets set to
 // a value <= 0, setting the parameters returns false.
 TEST_F(WebRtcVideoChannelTest, SetRtpSendParametersInvalidBitratePriority) {
   AddSendStream();
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(1UL, parameters.encodings.size());
   EXPECT_EQ(webrtc::kDefaultBitratePriority,
             parameters.encodings[0].bitrate_priority);
 
   parameters.encodings[0].bitrate_priority = 0;
-  EXPECT_FALSE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_FALSE(channel_->AsSendChannel()
+                   ->SetRtpSendParameters(last_ssrc_, parameters)
+                   .ok());
   parameters.encodings[0].bitrate_priority = -2;
-  EXPECT_FALSE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_FALSE(channel_->AsSendChannel()
+                   ->SetRtpSendParameters(last_ssrc_, parameters)
+                   .ok());
 }
 
 // Tests when the the RTCRtpEncodingParameters.bitrate_priority gets set
 // properly on the VideoChannel and propogates down to the video encoder.
 TEST_F(WebRtcVideoChannelTest, SetRtpSendParametersPriorityOneStream) {
   AddSendStream();
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(1UL, parameters.encodings.size());
   EXPECT_EQ(webrtc::kDefaultBitratePriority,
             parameters.encodings[0].bitrate_priority);
@@ -7382,11 +7492,13 @@
   // Change the value and set it on the VideoChannel.
   double new_bitrate_priority = 2.0;
   parameters.encodings[0].bitrate_priority = new_bitrate_priority;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   // Verify that the encoding parameters bitrate_priority is set for the
   // VideoChannel.
-  parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  parameters = channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(1UL, parameters.encodings.size());
   EXPECT_EQ(new_bitrate_priority, parameters.encodings[0].bitrate_priority);
 
@@ -7430,17 +7542,19 @@
 
   // Get and set the rtp encoding parameters.
   webrtc::RtpParameters parameters =
-      channel_->GetRtpSendParameters(primary_ssrc);
+      channel_->AsSendChannel()->GetRtpSendParameters(primary_ssrc);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
   EXPECT_EQ(webrtc::kDefaultBitratePriority,
             parameters.encodings[0].bitrate_priority);
   // Change the value and set it on the VideoChannel.
   double new_bitrate_priority = 2.0;
   parameters.encodings[0].bitrate_priority = new_bitrate_priority;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(primary_ssrc, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(primary_ssrc, parameters)
+                  .ok());
 
   // Verify that the encoding parameters priority is set on the VideoChannel.
-  parameters = channel_->GetRtpSendParameters(primary_ssrc);
+  parameters = channel_->AsSendChannel()->GetRtpSendParameters(primary_ssrc);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
   EXPECT_EQ(new_bitrate_priority, parameters.encodings[0].bitrate_priority);
 
@@ -7486,12 +7600,14 @@
 
   // Try layers in natural order (smallest to largest).
   {
-    auto rtp_parameters = channel_->GetRtpSendParameters(last_ssrc_);
+    auto rtp_parameters =
+        channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     ASSERT_EQ(3u, rtp_parameters.encodings.size());
     rtp_parameters.encodings[0].scale_resolution_down_by = 4.0;
     rtp_parameters.encodings[1].scale_resolution_down_by = 2.0;
     rtp_parameters.encodings[2].scale_resolution_down_by = 1.0;
-    auto result = channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+    auto result = channel_->AsSendChannel()->SetRtpSendParameters(
+        last_ssrc_, rtp_parameters);
     ASSERT_TRUE(result.ok());
 
     frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
@@ -7508,12 +7624,14 @@
 
   // Try layers in reverse natural order (largest to smallest).
   {
-    auto rtp_parameters = channel_->GetRtpSendParameters(last_ssrc_);
+    auto rtp_parameters =
+        channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     ASSERT_EQ(3u, rtp_parameters.encodings.size());
     rtp_parameters.encodings[0].scale_resolution_down_by = 1.0;
     rtp_parameters.encodings[1].scale_resolution_down_by = 2.0;
     rtp_parameters.encodings[2].scale_resolution_down_by = 4.0;
-    auto result = channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+    auto result = channel_->AsSendChannel()->SetRtpSendParameters(
+        last_ssrc_, rtp_parameters);
     ASSERT_TRUE(result.ok());
 
     frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
@@ -7530,12 +7648,14 @@
 
   // Try layers in mixed order.
   {
-    auto rtp_parameters = channel_->GetRtpSendParameters(last_ssrc_);
+    auto rtp_parameters =
+        channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     ASSERT_EQ(3u, rtp_parameters.encodings.size());
     rtp_parameters.encodings[0].scale_resolution_down_by = 10.0;
     rtp_parameters.encodings[1].scale_resolution_down_by = 2.0;
     rtp_parameters.encodings[2].scale_resolution_down_by = 4.0;
-    auto result = channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+    auto result = channel_->AsSendChannel()->SetRtpSendParameters(
+        last_ssrc_, rtp_parameters);
     ASSERT_TRUE(result.ok());
 
     frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
@@ -7552,12 +7672,14 @@
 
   // Try with a missing scale setting, defaults to 1.0 if any other is set.
   {
-    auto rtp_parameters = channel_->GetRtpSendParameters(last_ssrc_);
+    auto rtp_parameters =
+        channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     ASSERT_EQ(3u, rtp_parameters.encodings.size());
     rtp_parameters.encodings[0].scale_resolution_down_by = 1.0;
     rtp_parameters.encodings[1].scale_resolution_down_by.reset();
     rtp_parameters.encodings[2].scale_resolution_down_by = 4.0;
-    auto result = channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+    auto result = channel_->AsSendChannel()->SetRtpSendParameters(
+        last_ssrc_, rtp_parameters);
     ASSERT_TRUE(result.ok());
 
     frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
@@ -7595,13 +7717,14 @@
   channel_->SetSend(true);
 
   // Set `scale_resolution_down_by`'s.
-  auto rtp_parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  auto rtp_parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   ASSERT_EQ(rtp_parameters.encodings.size(), 3u);
   rtp_parameters.encodings[0].scale_resolution_down_by = 1.0;
   rtp_parameters.encodings[1].scale_resolution_down_by = 2.0;
   rtp_parameters.encodings[2].scale_resolution_down_by = 4.0;
-  const auto result =
-      channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+  const auto result = channel_->AsSendChannel()->SetRtpSendParameters(
+      last_ssrc_, rtp_parameters);
   ASSERT_TRUE(result.ok());
 
   // Use a capture resolution whose width and height are not divisible by 2^3.
@@ -7642,12 +7765,14 @@
 
   // Try layers in natural order (smallest to largest).
   {
-    auto rtp_parameters = channel_->GetRtpSendParameters(last_ssrc_);
+    auto rtp_parameters =
+        channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     ASSERT_EQ(3u, rtp_parameters.encodings.size());
     rtp_parameters.encodings[0].scale_resolution_down_by = 4.0;
     rtp_parameters.encodings[1].scale_resolution_down_by = 2.0;
     rtp_parameters.encodings[2].scale_resolution_down_by = 1.0;
-    auto result = channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+    auto result = channel_->AsSendChannel()->SetRtpSendParameters(
+        last_ssrc_, rtp_parameters);
     ASSERT_TRUE(result.ok());
 
     frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
@@ -7664,12 +7789,14 @@
 
   // Try layers in reverse natural order (largest to smallest).
   {
-    auto rtp_parameters = channel_->GetRtpSendParameters(last_ssrc_);
+    auto rtp_parameters =
+        channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     ASSERT_EQ(3u, rtp_parameters.encodings.size());
     rtp_parameters.encodings[0].scale_resolution_down_by = 1.0;
     rtp_parameters.encodings[1].scale_resolution_down_by = 2.0;
     rtp_parameters.encodings[2].scale_resolution_down_by = 4.0;
-    auto result = channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+    auto result = channel_->AsSendChannel()->SetRtpSendParameters(
+        last_ssrc_, rtp_parameters);
     ASSERT_TRUE(result.ok());
 
     frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
@@ -7686,12 +7813,14 @@
 
   // Try layers in mixed order.
   {
-    auto rtp_parameters = channel_->GetRtpSendParameters(last_ssrc_);
+    auto rtp_parameters =
+        channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     ASSERT_EQ(3u, rtp_parameters.encodings.size());
     rtp_parameters.encodings[0].scale_resolution_down_by = 10.0;
     rtp_parameters.encodings[1].scale_resolution_down_by = 2.0;
     rtp_parameters.encodings[2].scale_resolution_down_by = 4.0;
-    auto result = channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+    auto result = channel_->AsSendChannel()->SetRtpSendParameters(
+        last_ssrc_, rtp_parameters);
     ASSERT_TRUE(result.ok());
 
     frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
@@ -7708,12 +7837,14 @@
 
   // Try with a missing scale setting, defaults to 1.0 if any other is set.
   {
-    auto rtp_parameters = channel_->GetRtpSendParameters(last_ssrc_);
+    auto rtp_parameters =
+        channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     ASSERT_EQ(3u, rtp_parameters.encodings.size());
     rtp_parameters.encodings[0].scale_resolution_down_by = 1.0;
     rtp_parameters.encodings[1].scale_resolution_down_by.reset();
     rtp_parameters.encodings[2].scale_resolution_down_by = 4.0;
-    auto result = channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+    auto result = channel_->AsSendChannel()->SetRtpSendParameters(
+        last_ssrc_, rtp_parameters);
     ASSERT_TRUE(result.ok());
 
     frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
@@ -7751,13 +7882,14 @@
   channel_->SetSend(true);
 
   // Set `scale_resolution_down_by`'s.
-  auto rtp_parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  auto rtp_parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   ASSERT_EQ(rtp_parameters.encodings.size(), 3u);
   rtp_parameters.encodings[0].scale_resolution_down_by = 1.0;
   rtp_parameters.encodings[1].scale_resolution_down_by = 2.0;
   rtp_parameters.encodings[2].scale_resolution_down_by = 4.0;
-  const auto result =
-      channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+  const auto result = channel_->AsSendChannel()->SetRtpSendParameters(
+      last_ssrc_, rtp_parameters);
   ASSERT_TRUE(result.ok());
 
   // Use a capture resolution whose width and height are not divisible by 2^3.
@@ -7786,7 +7918,8 @@
   SetUpSimulcast(true, false);
 
   // Get and set the rtp encoding parameters.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
   for (const auto& encoding : parameters.encodings) {
     EXPECT_FALSE(encoding.max_framerate);
@@ -7796,10 +7929,12 @@
   parameters.encodings[0].max_framerate = 10;
   parameters.encodings[1].max_framerate = 20;
   parameters.encodings[2].max_framerate = 25;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   // Verify that the bitrates are set on the VideoChannel.
-  parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  parameters = channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
   EXPECT_EQ(10, parameters.encodings[0].max_framerate);
   EXPECT_EQ(20, parameters.encodings[1].max_framerate);
@@ -7812,16 +7947,21 @@
   SetUpSimulcast(true, false);
 
   // Get and set the rtp encoding parameters.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
 
   // Num temporal layers should be in the range [1, kMaxTemporalStreams].
   parameters.encodings[0].num_temporal_layers = 0;
   EXPECT_EQ(webrtc::RTCErrorType::INVALID_RANGE,
-            channel_->SetRtpSendParameters(last_ssrc_, parameters).type());
+            channel_->AsSendChannel()
+                ->SetRtpSendParameters(last_ssrc_, parameters)
+                .type());
   parameters.encodings[0].num_temporal_layers = webrtc::kMaxTemporalStreams + 1;
   EXPECT_EQ(webrtc::RTCErrorType::INVALID_RANGE,
-            channel_->SetRtpSendParameters(last_ssrc_, parameters).type());
+            channel_->AsSendChannel()
+                ->SetRtpSendParameters(last_ssrc_, parameters)
+                .type());
 }
 
 TEST_F(WebRtcVideoChannelTest, GetAndSetRtpSendParametersNumTemporalLayers) {
@@ -7829,7 +7969,8 @@
   SetUpSimulcast(true, false);
 
   // Get and set the rtp encoding parameters.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
   for (const auto& encoding : parameters.encodings)
     EXPECT_FALSE(encoding.num_temporal_layers);
@@ -7838,10 +7979,12 @@
   parameters.encodings[0].num_temporal_layers = 3;
   parameters.encodings[1].num_temporal_layers = 3;
   parameters.encodings[2].num_temporal_layers = 3;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   // Verify that the number of temporal layers are set on the VideoChannel.
-  parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  parameters = channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
   EXPECT_EQ(3, parameters.encodings[0].num_temporal_layers);
   EXPECT_EQ(3, parameters.encodings[1].num_temporal_layers);
@@ -7861,12 +8004,15 @@
 
   // Get and set the rtp encoding parameters.
   // Change the value and set it on the VideoChannel.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
   parameters.encodings[0].num_temporal_layers = 3;
   parameters.encodings[1].num_temporal_layers = 2;
   parameters.encodings[2].num_temporal_layers = 1;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   // Verify that the new value is propagated down to the encoder.
   // Check that WebRtcVideoSendStream updates VideoEncoderConfig correctly.
@@ -7886,7 +8032,9 @@
   EXPECT_EQ(1UL, stream->GetVideoStreams()[2].num_temporal_layers);
 
   // No parameter changed, encoder should not be reconfigured.
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
   EXPECT_EQ(2, stream->num_encoder_reconfigurations());
 
   EXPECT_TRUE(channel_->SetVideoSend(last_ssrc_, nullptr, nullptr));
@@ -7906,11 +8054,14 @@
   frame_forwarder.IncomingCapturedFrame(frame_source_.GetFrame());
 
   // Change rtp encoding parameters.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
   parameters.encodings[0].num_temporal_layers = 2;
   parameters.encodings[2].num_temporal_layers = 1;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   // Verify that no value is propagated down to the encoder.
   webrtc::VideoEncoderConfig encoder_config = stream->GetEncoderConfig().Copy();
@@ -7946,11 +8097,14 @@
 
   // Get and set the rtp encoding parameters.
   // Change the value and set it on the VideoChannel.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
   parameters.encodings[0].max_framerate = 15;
   parameters.encodings[2].max_framerate = 20;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   // Verify that the new value propagated down to the encoder.
   // Check that WebRtcVideoSendStream updates VideoEncoderConfig correctly.
@@ -7978,7 +8132,8 @@
   SetUpSimulcast(true, false);
 
   // Get and set the rtp encoding parameters.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
   for (const auto& encoding : parameters.encodings) {
     EXPECT_FALSE(encoding.min_bitrate_bps);
@@ -7992,10 +8147,12 @@
   parameters.encodings[1].max_bitrate_bps = 400000;
   parameters.encodings[2].min_bitrate_bps = 500000;
   parameters.encodings[2].max_bitrate_bps = 600000;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   // Verify that the bitrates are set on the VideoChannel.
-  parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  parameters = channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
   EXPECT_EQ(100000, parameters.encodings[0].min_bitrate_bps);
   EXPECT_EQ(200000, parameters.encodings[0].max_bitrate_bps);
@@ -8010,14 +8167,17 @@
   SetUpSimulcast(true, false);
 
   // Get and set the rtp encoding parameters.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
 
   // Max bitrate lower than min bitrate should fail.
   parameters.encodings[2].min_bitrate_bps = 100000;
   parameters.encodings[2].max_bitrate_bps = 100000 - 1;
   EXPECT_EQ(webrtc::RTCErrorType::INVALID_RANGE,
-            channel_->SetRtpSendParameters(last_ssrc_, parameters).type());
+            channel_->AsSendChannel()
+                ->SetRtpSendParameters(last_ssrc_, parameters)
+                .type());
 }
 
 // Test that min and max bitrate values set via RtpParameters are correctly
@@ -8037,7 +8197,8 @@
 
   // Get and set the rtp encoding parameters.
   // Change the value and set it on the VideoChannel.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
   parameters.encodings[0].min_bitrate_bps = 100000;
   parameters.encodings[0].max_bitrate_bps = 200000;
@@ -8045,7 +8206,9 @@
   parameters.encodings[1].max_bitrate_bps = 400000;
   parameters.encodings[2].min_bitrate_bps = 500000;
   parameters.encodings[2].max_bitrate_bps = 600000;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   // Verify that the new value propagated down to the encoder.
   // Check that WebRtcVideoSendStream updates VideoEncoderConfig correctly.
@@ -8077,7 +8240,9 @@
   EXPECT_EQ(600000, stream->GetVideoStreams()[2].max_bitrate_bps);
 
   // No parameter changed, encoder should not be reconfigured.
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
   EXPECT_EQ(2, stream->num_encoder_reconfigurations());
 
   EXPECT_TRUE(channel_->SetVideoSend(last_ssrc_, nullptr, nullptr));
@@ -8099,7 +8264,8 @@
   frame_forwarder.IncomingCapturedFrame(frame_source_.GetFrame());
 
   // Get and set the rtp encoding parameters.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
 
   // Change the value and set it on the VideoChannel.
@@ -8109,7 +8275,9 @@
   // Layer 1: only configure max bitrate.
   const int kMaxBpsLayer1 = kDefault[1].max_bitrate_bps - 1;
   parameters.encodings[1].max_bitrate_bps = kMaxBpsLayer1;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   // Verify that the new value propagated down to the encoder.
   // Check that WebRtcVideoSendStream updates VideoEncoderConfig correctly.
@@ -8168,7 +8336,8 @@
   frame_forwarder.IncomingCapturedFrame(frame_source_.GetFrame());
 
   // Get and set the rtp encoding parameters.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
 
   // Change the value and set it on the VideoChannel.
@@ -8178,7 +8347,9 @@
   // For layer 1, set the max bitrate below the default min.
   const int kMaxBpsLayer1 = kDefault[1].min_bitrate_bps - 1;
   parameters.encodings[1].max_bitrate_bps = kMaxBpsLayer1;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   // Verify that the new value propagated down to the encoder.
   // FakeVideoSendStream calls CreateEncoderStreams, test that the vector of
@@ -8216,11 +8387,14 @@
   frame_forwarder.IncomingCapturedFrame(frame_source_.GetFrame());
 
   // Set max bitrate for all but the highest layer.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
   parameters.encodings[0].max_bitrate_bps = kDefault[0].max_bitrate_bps;
   parameters.encodings[1].max_bitrate_bps = kDefault[1].max_bitrate_bps;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   // Set max bandwidth equal to total max bitrate.
   send_parameters_.max_bandwidth_bps =
@@ -8265,10 +8439,13 @@
   frame_forwarder.IncomingCapturedFrame(frame_source_.GetFrame());
 
   // Set max bitrate for the highest layer.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
   parameters.encodings[2].max_bitrate_bps = kDefault[2].max_bitrate_bps;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   // Set max bandwidth above the total max bitrate.
   send_parameters_.max_bandwidth_bps =
@@ -8293,11 +8470,14 @@
   EXPECT_TRUE(stream->IsSending());
 
   // Set min and max bitrate.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(1u, parameters.encodings.size());
   parameters.encodings[0].min_bitrate_bps = 80000;
   parameters.encodings[0].max_bitrate_bps = 150000;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
 
   // Check that WebRtcVideoSendStream updates VideoEncoderConfig correctly.
   webrtc::VideoEncoderConfig encoder_config = stream->GetEncoderConfig().Copy();
@@ -8348,16 +8528,21 @@
   EXPECT_TRUE(stream->IsSending());
 
   // Get current parameters and change "active" to false.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   ASSERT_EQ(1u, parameters.encodings.size());
   ASSERT_TRUE(parameters.encodings[0].active);
   parameters.encodings[0].active = false;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
   EXPECT_FALSE(stream->IsSending());
 
   // Now change it back to active and verify we resume sending.
   parameters.encodings[0].active = true;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
   EXPECT_TRUE(stream->IsSending());
 }
 
@@ -8385,7 +8570,7 @@
 
   // Check that all encodings are initially active.
   webrtc::RtpParameters parameters =
-      channel_->GetRtpSendParameters(primary_ssrc);
+      channel_->AsSendChannel()->GetRtpSendParameters(primary_ssrc);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
   EXPECT_TRUE(parameters.encodings[0].active);
   EXPECT_TRUE(parameters.encodings[1].active);
@@ -8396,9 +8581,11 @@
   parameters.encodings[0].active = false;
   parameters.encodings[1].active = true;
   parameters.encodings[2].active = false;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(primary_ssrc, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(primary_ssrc, parameters)
+                  .ok());
   // Verify that the active fields are set on the VideoChannel.
-  parameters = channel_->GetRtpSendParameters(primary_ssrc);
+  parameters = channel_->AsSendChannel()->GetRtpSendParameters(primary_ssrc);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
   EXPECT_FALSE(parameters.encodings[0].active);
   EXPECT_TRUE(parameters.encodings[1].active);
@@ -8417,9 +8604,11 @@
   parameters.encodings[0].active = false;
   parameters.encodings[1].active = false;
   parameters.encodings[2].active = false;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(primary_ssrc, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(primary_ssrc, parameters)
+                  .ok());
   // Verify that the active fields are set on the VideoChannel.
-  parameters = channel_->GetRtpSendParameters(primary_ssrc);
+  parameters = channel_->AsSendChannel()->GetRtpSendParameters(primary_ssrc);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
   EXPECT_FALSE(parameters.encodings[0].active);
   EXPECT_FALSE(parameters.encodings[1].active);
@@ -8459,7 +8648,7 @@
 
   // Check that all encodings are initially active.
   webrtc::RtpParameters parameters =
-      channel_->GetRtpSendParameters(primary_ssrc);
+      channel_->AsSendChannel()->GetRtpSendParameters(primary_ssrc);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
   EXPECT_TRUE(parameters.encodings[0].active);
   EXPECT_TRUE(parameters.encodings[1].active);
@@ -8470,7 +8659,9 @@
   parameters.encodings[0].active = false;
   parameters.encodings[1].active = false;
   parameters.encodings[2].active = true;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(primary_ssrc, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(primary_ssrc, parameters)
+                  .ok());
 
   // Check that the VideoSendStream is updated appropriately. This means its
   // send state was updated and it was reconfigured.
@@ -8503,13 +8694,16 @@
   EXPECT_TRUE(stream->IsSending());
 
   // Get current parameters and change "active" to false.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   ASSERT_EQ(1u, parameters.encodings.size());
   ASSERT_TRUE(parameters.encodings[0].active);
   parameters.encodings[0].active = false;
   EXPECT_EQ(1u, GetFakeSendStreams().size());
   EXPECT_EQ(1, fake_call_->GetNumCreatedSendStreams());
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, parameters)
+                  .ok());
   EXPECT_FALSE(stream->IsSending());
 
   // Reorder the codec list, causing the stream to be reconfigured.
@@ -8537,7 +8731,7 @@
   EXPECT_TRUE(channel_->SetSendParameters(parameters));
 
   webrtc::RtpParameters rtp_parameters =
-      channel_->GetRtpSendParameters(last_ssrc_);
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   ASSERT_EQ(2u, rtp_parameters.codecs.size());
   EXPECT_EQ(GetEngineCodec("VP8").ToCodecParameters(),
             rtp_parameters.codecs[0]);
@@ -8551,7 +8745,8 @@
   params.cname = "rtcpcname";
   AddSendStream(params);
 
-  webrtc::RtpParameters rtp_parameters = channel_->GetRtpSendParameters(kSsrc);
+  webrtc::RtpParameters rtp_parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrc);
   EXPECT_STREQ("rtcpcname", rtp_parameters.rtcp.cname.c_str());
 }
 
@@ -8561,7 +8756,7 @@
   AddSendStream();
 
   webrtc::RtpParameters rtp_parameters =
-      channel_->GetRtpSendParameters(last_ssrc_);
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   ASSERT_EQ(1u, rtp_parameters.encodings.size());
   EXPECT_EQ(last_ssrc_, rtp_parameters.encodings[0].ssrc);
 }
@@ -8570,13 +8765,13 @@
   AddSendStream();
 
   webrtc::RtpParameters rtp_parameters =
-      channel_->GetRtpSendParameters(last_ssrc_);
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   rtp_parameters.header_extensions.emplace_back();
 
   EXPECT_NE(0u, rtp_parameters.header_extensions.size());
 
-  webrtc::RTCError result =
-      channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+  webrtc::RTCError result = channel_->AsSendChannel()->SetRtpSendParameters(
+      last_ssrc_, rtp_parameters);
   EXPECT_EQ(webrtc::RTCErrorType::INVALID_MODIFICATION, result.type());
 }
 
@@ -8587,15 +8782,17 @@
   EXPECT_TRUE(channel_->SetVideoSend(last_ssrc_, nullptr, &frame_forwarder));
 
   webrtc::RtpParameters rtp_parameters =
-      channel_->GetRtpSendParameters(last_ssrc_);
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_FALSE(rtp_parameters.degradation_preference.has_value());
   rtp_parameters.degradation_preference =
       webrtc::DegradationPreference::MAINTAIN_FRAMERATE;
 
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, rtp_parameters)
+                  .ok());
 
   webrtc::RtpParameters updated_rtp_parameters =
-      channel_->GetRtpSendParameters(last_ssrc_);
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(updated_rtp_parameters.degradation_preference,
             webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
 
@@ -8612,13 +8809,16 @@
   EXPECT_TRUE(channel_->SetSendParameters(parameters));
 
   webrtc::RtpParameters initial_params =
-      channel_->GetRtpSendParameters(last_ssrc_);
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
 
   // We should be able to set the params we just got.
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, initial_params).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, initial_params)
+                  .ok());
 
   // ... And this shouldn't change the params returned by GetRtpSendParameters.
-  EXPECT_EQ(initial_params, channel_->GetRtpSendParameters(last_ssrc_));
+  EXPECT_EQ(initial_params,
+            channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_));
 }
 
 // Test that GetRtpReceiveParameters returns the currently configured codecs.
@@ -8757,7 +8957,7 @@
 
   cricket::StreamParams params = cricket::StreamParams::CreateLegacy(1);
   params.AddFidSsrc(1, 2);
-  EXPECT_TRUE(channel_->AddRecvStream(params));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(params));
 }
 
 void WebRtcVideoChannelTest::TestReceiverLocalSsrcConfiguration(
@@ -8789,13 +8989,13 @@
   // Removing first sender should fall back to another (in this case the second)
   // local send stream's SSRC.
   AddSendStream(StreamParams::CreateLegacy(kSecondSenderSsrc));
-  ASSERT_TRUE(channel_->RemoveSendStream(kSenderSsrc));
+  ASSERT_TRUE(channel_->AsSendChannel()->RemoveSendStream(kSenderSsrc));
   receive_streams = fake_call_->GetVideoReceiveStreams();
   ASSERT_EQ(1u, receive_streams.size());
   EXPECT_EQ(kSecondSenderSsrc, receive_streams[0]->GetConfig().rtp.local_ssrc);
 
   // Removing the last sender should fall back to default local SSRC.
-  ASSERT_TRUE(channel_->RemoveSendStream(kSecondSenderSsrc));
+  ASSERT_TRUE(channel_->AsSendChannel()->RemoveSendStream(kSecondSenderSsrc));
   receive_streams = fake_call_->GetVideoReceiveStreams();
   ASSERT_EQ(1u, receive_streams.size());
   EXPECT_EQ(kExpectedDefaultReceiverSsrc,
@@ -8841,14 +9041,16 @@
   FakeVideoSendStream* stream = SetUpSimulcast(true, false);
 
   webrtc::RtpParameters rtp_parameters =
-      channel_->GetRtpSendParameters(last_ssrc_);
+      channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
   ASSERT_EQ(3u, rtp_parameters.encodings.size());
   ASSERT_TRUE(rtp_parameters.encodings[0].active);
   ASSERT_TRUE(rtp_parameters.encodings[1].active);
   ASSERT_TRUE(rtp_parameters.encodings[2].active);
   rtp_parameters.encodings[0].active = false;
   rtp_parameters.encodings[1].active = false;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(last_ssrc_, rtp_parameters)
+                  .ok());
   EXPECT_TRUE(stream->GetEncoderConfig().is_quality_scaling_allowed);
 }
 
@@ -8873,7 +9075,7 @@
     channel_.reset(engine_.CreateMediaChannel(
         &fake_call_, GetMediaConfig(), VideoOptions(), webrtc::CryptoOptions(),
         mock_rate_allocator_factory_.get()));
-    channel_->OnReadyToSend(true);
+    channel_->AsSendChannel()->OnReadyToSend(true);
     last_ssrc_ = 123;
   }
 
@@ -8912,7 +9114,8 @@
     channel_->SetSend(true);
     frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
 
-    auto rtp_parameters = channel_->GetRtpSendParameters(kSsrcs3[0]);
+    auto rtp_parameters =
+        channel_->AsSendChannel()->GetRtpSendParameters(kSsrcs3[0]);
     EXPECT_EQ(num_configured_streams, rtp_parameters.encodings.size());
 
     std::vector<webrtc::VideoStream> video_streams = stream->GetVideoStreams();
@@ -8988,7 +9191,7 @@
 
   FakeVideoSendStream* AddSendStream(const StreamParams& sp) {
     size_t num_streams = fake_call_.GetVideoSendStreams().size();
-    EXPECT_TRUE(channel_->AddSendStream(sp));
+    EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(sp));
     std::vector<FakeVideoSendStream*> streams =
         fake_call_.GetVideoSendStreams();
     EXPECT_EQ(num_streams + 1, streams.size());
@@ -9005,7 +9208,7 @@
 
   FakeVideoReceiveStream* AddRecvStream(const StreamParams& sp) {
     size_t num_streams = fake_call_.GetVideoReceiveStreams().size();
-    EXPECT_TRUE(channel_->AddRecvStream(sp));
+    EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(sp));
     std::vector<FakeVideoReceiveStream*> streams =
         fake_call_.GetVideoReceiveStreams();
     EXPECT_EQ(num_streams + 1, streams.size());
@@ -9105,7 +9308,7 @@
   }
   sp.set_rids(rid_descriptions);
 
-  ASSERT_TRUE(channel_->AddSendStream(sp));
+  ASSERT_TRUE(channel_->AsSendChannel()->AddSendStream(sp));
   const auto& streams = fake_call_->GetVideoSendStreams();
   ASSERT_EQ(1u, streams.size());
   auto stream = streams[0];
@@ -9155,11 +9358,11 @@
 
   {  // TEST requested_resolution < frame size
     webrtc::RtpParameters rtp_parameters =
-        channel_->GetRtpSendParameters(last_ssrc_);
+        channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     EXPECT_EQ(1UL, rtp_parameters.encodings.size());
     rtp_parameters.encodings[0].requested_resolution = {.width = 640,
                                                         .height = 360};
-    channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+    channel_->AsSendChannel()->SetRtpSendParameters(last_ssrc_, rtp_parameters);
 
     frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
 
@@ -9170,11 +9373,12 @@
   }
 
   {  // TEST requested_resolution == frame size
-    auto rtp_parameters = channel_->GetRtpSendParameters(last_ssrc_);
+    auto rtp_parameters =
+        channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     EXPECT_EQ(1UL, rtp_parameters.encodings.size());
     rtp_parameters.encodings[0].requested_resolution = {.width = 1280,
                                                         .height = 720};
-    channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+    channel_->AsSendChannel()->SetRtpSendParameters(last_ssrc_, rtp_parameters);
 
     frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
     auto streams = stream->GetVideoStreams();
@@ -9184,11 +9388,12 @@
   }
 
   {  // TEST requested_resolution > frame size
-    auto rtp_parameters = channel_->GetRtpSendParameters(last_ssrc_);
+    auto rtp_parameters =
+        channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     EXPECT_EQ(1UL, rtp_parameters.encodings.size());
     rtp_parameters.encodings[0].requested_resolution = {.width = 2 * 1280,
                                                         .height = 2 * 720};
-    channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+    channel_->AsSendChannel()->SetRtpSendParameters(last_ssrc_, rtp_parameters);
 
     frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
     auto streams = stream->GetVideoStreams();
@@ -9212,11 +9417,12 @@
   EXPECT_TRUE(channel_->SetVideoSend(last_ssrc_, nullptr, &frame_forwarder));
 
   {
-    auto rtp_parameters = channel_->GetRtpSendParameters(last_ssrc_);
+    auto rtp_parameters =
+        channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     EXPECT_EQ(1UL, rtp_parameters.encodings.size());
     rtp_parameters.encodings[0].requested_resolution = {.width = 720,
                                                         .height = 720};
-    channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+    channel_->AsSendChannel()->SetRtpSendParameters(last_ssrc_, rtp_parameters);
 
     frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
 
@@ -9227,11 +9433,12 @@
   }
 
   {
-    auto rtp_parameters = channel_->GetRtpSendParameters(last_ssrc_);
+    auto rtp_parameters =
+        channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     EXPECT_EQ(1UL, rtp_parameters.encodings.size());
     rtp_parameters.encodings[0].requested_resolution = {.width = 1280,
                                                         .height = 1280};
-    channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+    channel_->AsSendChannel()->SetRtpSendParameters(last_ssrc_, rtp_parameters);
 
     frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
 
@@ -9242,11 +9449,12 @@
   }
 
   {
-    auto rtp_parameters = channel_->GetRtpSendParameters(last_ssrc_);
+    auto rtp_parameters =
+        channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     EXPECT_EQ(1UL, rtp_parameters.encodings.size());
     rtp_parameters.encodings[0].requested_resolution = {.width = 650,
                                                         .height = 650};
-    channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+    channel_->AsSendChannel()->SetRtpSendParameters(last_ssrc_, rtp_parameters);
 
     auto streams = stream->GetVideoStreams();
     ASSERT_EQ(streams.size(), 1u);
@@ -9270,7 +9478,7 @@
 
   {
     webrtc::RtpParameters rtp_parameters =
-        channel_->GetRtpSendParameters(last_ssrc_);
+        channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     EXPECT_EQ(3UL, rtp_parameters.encodings.size());
     rtp_parameters.encodings[0].requested_resolution = {.width = 320,
                                                         .height = 180};
@@ -9278,7 +9486,7 @@
                                                         .height = 360};
     rtp_parameters.encodings[2].requested_resolution = {.width = 1280,
                                                         .height = 720};
-    channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+    channel_->AsSendChannel()->SetRtpSendParameters(last_ssrc_, rtp_parameters);
 
     frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
 
@@ -9292,7 +9500,7 @@
 
   {
     webrtc::RtpParameters rtp_parameters =
-        channel_->GetRtpSendParameters(last_ssrc_);
+        channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     EXPECT_EQ(3UL, rtp_parameters.encodings.size());
     rtp_parameters.encodings[0].requested_resolution = {.width = 320,
                                                         .height = 180};
@@ -9300,7 +9508,7 @@
 
     rtp_parameters.encodings[2].requested_resolution = {.width = 1280,
                                                         .height = 720};
-    channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+    channel_->AsSendChannel()->SetRtpSendParameters(last_ssrc_, rtp_parameters);
 
     frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
 
@@ -9313,7 +9521,7 @@
 
   {
     webrtc::RtpParameters rtp_parameters =
-        channel_->GetRtpSendParameters(last_ssrc_);
+        channel_->AsSendChannel()->GetRtpSendParameters(last_ssrc_);
     EXPECT_EQ(3UL, rtp_parameters.encodings.size());
     rtp_parameters.encodings[0].requested_resolution = {.width = 320,
                                                         .height = 180};
@@ -9322,7 +9530,7 @@
                                                         .height = 360};
     rtp_parameters.encodings[2].requested_resolution = {.width = 960,
                                                         .height = 540};
-    channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+    channel_->AsSendChannel()->SetRtpSendParameters(last_ssrc_, rtp_parameters);
 
     frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame());
 
diff --git a/media/engine/webrtc_voice_engine_unittest.cc b/media/engine/webrtc_voice_engine_unittest.cc
index cac13b9..2c638be 100644
--- a/media/engine/webrtc_voice_engine_unittest.cc
+++ b/media/engine/webrtc_voice_engine_unittest.cc
@@ -247,7 +247,7 @@
     if (!SetupChannel()) {
       return false;
     }
-    if (!channel_->AddSendStream(sp)) {
+    if (!channel_->AsSendChannel()->AddSendStream(sp)) {
       return false;
     }
     if (!use_null_apm_) {
@@ -258,21 +258,23 @@
 
   bool AddRecvStream(uint32_t ssrc) {
     EXPECT_TRUE(channel_);
-    return channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(ssrc));
+    return channel_->AsReceiveChannel()->AddRecvStream(
+        cricket::StreamParams::CreateLegacy(ssrc));
   }
 
   void SetupForMultiSendStream() {
     EXPECT_TRUE(SetupSendStream());
     // Remove stream added in Setup.
     EXPECT_TRUE(call_.GetAudioSendStream(kSsrcX));
-    EXPECT_TRUE(channel_->RemoveSendStream(kSsrcX));
+    EXPECT_TRUE(channel_->AsSendChannel()->RemoveSendStream(kSsrcX));
     // Verify the channel does not exist.
     EXPECT_FALSE(call_.GetAudioSendStream(kSsrcX));
   }
 
   void DeliverPacket(const void* data, int len) {
     rtc::CopyOnWriteBuffer packet(reinterpret_cast<const uint8_t*>(data), len);
-    channel_->OnPacketReceived(packet, /* packet_time_us */ -1);
+    channel_->AsReceiveChannel()->OnPacketReceived(packet,
+                                                   /* packet_time_us */ -1);
     rtc::Thread::Current()->ProcessMessages(0);
   }
 
@@ -338,8 +340,8 @@
     if (caller) {
       // If this is a caller, local description will be applied and add the
       // send stream.
-      EXPECT_TRUE(
-          channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
+      EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+          cricket::StreamParams::CreateLegacy(kSsrcX)));
     }
 
     // Test we can only InsertDtmf when the other side supports telephone-event.
@@ -354,8 +356,8 @@
     if (!caller) {
       // If this is callee, there's no active send channel yet.
       EXPECT_FALSE(channel_->InsertDtmf(ssrc, 2, 123));
-      EXPECT_TRUE(
-          channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
+      EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+          cricket::StreamParams::CreateLegacy(kSsrcX)));
     }
 
     // Check we fail if the ssrc is invalid.
@@ -377,8 +379,8 @@
     // For a caller, the answer will be applied in set remote description
     // where SetSendParameters() is called.
     EXPECT_TRUE(SetupChannel());
-    EXPECT_TRUE(
-        channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
+    EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+        cricket::StreamParams::CreateLegacy(kSsrcX)));
     send_parameters_.extmap_allow_mixed = extmap_allow_mixed;
     SetSendParameters(send_parameters_);
     const webrtc::AudioSendStream::Config& config = GetSendStreamConfig(kSsrcX);
@@ -390,8 +392,8 @@
     // where SetExtmapAllowMixed() and AddSendStream() are called.
     EXPECT_TRUE(SetupChannel());
     channel_->SetExtmapAllowMixed(extmap_allow_mixed);
-    EXPECT_TRUE(
-        channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
+    EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+        cricket::StreamParams::CreateLegacy(kSsrcX)));
 
     const webrtc::AudioSendStream::Config& config = GetSendStreamConfig(kSsrcX);
     EXPECT_EQ(extmap_allow_mixed, config.rtp.extmap_allow_mixed);
@@ -419,11 +421,14 @@
 
   // Sets the per-stream maximum bitrate limit for the specified SSRC.
   bool SetMaxBitrateForStream(int32_t ssrc, int bitrate) {
-    webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(ssrc);
+    webrtc::RtpParameters parameters =
+        channel_->AsSendChannel()->GetRtpSendParameters(ssrc);
     EXPECT_EQ(1UL, parameters.encodings.size());
 
     parameters.encodings[0].max_bitrate_bps = bitrate;
-    return channel_->SetRtpSendParameters(ssrc, parameters).ok();
+    return channel_->AsSendChannel()
+        ->SetRtpSendParameters(ssrc, parameters)
+        .ok();
   }
 
   void SetGlobalMaxBitrate(const cricket::AudioCodec& codec, int bitrate) {
@@ -469,7 +474,7 @@
     // Verify that reading back the parameters gives results
     // consistent with the Set() result.
     webrtc::RtpParameters resulting_parameters =
-        channel_->GetRtpSendParameters(kSsrcX);
+        channel_->AsSendChannel()->GetRtpSendParameters(kSsrcX);
     EXPECT_EQ(1UL, resulting_parameters.encodings.size());
     EXPECT_EQ(expected_result ? stream_max : -1,
               resulting_parameters.encodings[0].max_bitrate_bps);
@@ -530,8 +535,8 @@
     EXPECT_EQ(id, GetSendStreamConfig(kSsrcX).rtp.extensions[0].id);
 
     // Ensure extension is set properly on new stream.
-    EXPECT_TRUE(
-        channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcY)));
+    EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+        cricket::StreamParams::CreateLegacy(kSsrcY)));
     EXPECT_NE(call_.GetAudioSendStream(kSsrcX),
               call_.GetAudioSendStream(kSsrcY));
     EXPECT_EQ(1u, GetSendStreamConfig(kSsrcY).rtp.extensions.size());
@@ -816,8 +821,8 @@
 // Test that we can add a send stream and that it has the correct defaults.
 TEST_P(WebRtcVoiceEngineTestFake, CreateSendStream) {
   EXPECT_TRUE(SetupChannel());
-  EXPECT_TRUE(
-      channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(kSsrcX)));
   const webrtc::AudioSendStream::Config& config = GetSendStreamConfig(kSsrcX);
   EXPECT_EQ(kSsrcX, config.rtp.ssrc);
   EXPECT_EQ("", config.rtp.c_name);
@@ -1074,8 +1079,8 @@
   parameters.max_bandwidth_bps = kDesiredBitrate;
   SetSendParameters(parameters);
 
-  EXPECT_TRUE(
-      channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(kSsrcX)));
 
   EXPECT_EQ(kDesiredBitrate, GetCodecBitrate(kSsrcX));
 }
@@ -1126,12 +1131,13 @@
 TEST_P(WebRtcVoiceEngineTestFake, CannotSetMaxBitrateForNonexistentStream) {
   EXPECT_TRUE(SetupChannel());
   webrtc::RtpParameters nonexistent_parameters =
-      channel_->GetRtpSendParameters(kSsrcX);
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrcX);
   EXPECT_EQ(0u, nonexistent_parameters.encodings.size());
 
   nonexistent_parameters.encodings.push_back(webrtc::RtpEncodingParameters());
-  EXPECT_FALSE(
-      channel_->SetRtpSendParameters(kSsrcX, nonexistent_parameters).ok());
+  EXPECT_FALSE(channel_->AsSendChannel()
+                   ->SetRtpSendParameters(kSsrcX, nonexistent_parameters)
+                   .ok());
 }
 
 TEST_P(WebRtcVoiceEngineTestFake,
@@ -1142,21 +1148,26 @@
   // for each encoding individually.
 
   EXPECT_TRUE(SetupSendStream());
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(kSsrcX);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrcX);
   // Two or more encodings should result in failure.
   parameters.encodings.push_back(webrtc::RtpEncodingParameters());
-  EXPECT_FALSE(channel_->SetRtpSendParameters(kSsrcX, parameters).ok());
+  EXPECT_FALSE(
+      channel_->AsSendChannel()->SetRtpSendParameters(kSsrcX, parameters).ok());
   // Zero encodings should also fail.
   parameters.encodings.clear();
-  EXPECT_FALSE(channel_->SetRtpSendParameters(kSsrcX, parameters).ok());
+  EXPECT_FALSE(
+      channel_->AsSendChannel()->SetRtpSendParameters(kSsrcX, parameters).ok());
 }
 
 // Changing the SSRC through RtpParameters is not allowed.
 TEST_P(WebRtcVoiceEngineTestFake, CannotSetSsrcInRtpSendParameters) {
   EXPECT_TRUE(SetupSendStream());
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(kSsrcX);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrcX);
   parameters.encodings[0].ssrc = 0xdeadbeef;
-  EXPECT_FALSE(channel_->SetRtpSendParameters(kSsrcX, parameters).ok());
+  EXPECT_FALSE(
+      channel_->AsSendChannel()->SetRtpSendParameters(kSsrcX, parameters).ok());
 }
 
 // Test that a stream will not be sending if its encoding is made
@@ -1166,34 +1177,40 @@
   SetSend(true);
   EXPECT_TRUE(GetSendStream(kSsrcX).IsSending());
   // Get current parameters and change "active" to false.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(kSsrcX);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrcX);
   ASSERT_EQ(1u, parameters.encodings.size());
   ASSERT_TRUE(parameters.encodings[0].active);
   parameters.encodings[0].active = false;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(kSsrcX, parameters).ok());
+  EXPECT_TRUE(
+      channel_->AsSendChannel()->SetRtpSendParameters(kSsrcX, parameters).ok());
   EXPECT_FALSE(GetSendStream(kSsrcX).IsSending());
 
   // Now change it back to active and verify we resume sending.
   // This should occur even when other parameters are updated.
   parameters.encodings[0].active = true;
   parameters.encodings[0].max_bitrate_bps = absl::optional<int>(6000);
-  EXPECT_TRUE(channel_->SetRtpSendParameters(kSsrcX, parameters).ok());
+  EXPECT_TRUE(
+      channel_->AsSendChannel()->SetRtpSendParameters(kSsrcX, parameters).ok());
   EXPECT_TRUE(GetSendStream(kSsrcX).IsSending());
 }
 
 TEST_P(WebRtcVoiceEngineTestFake, SetRtpParametersAdaptivePtime) {
   EXPECT_TRUE(SetupSendStream());
   // Get current parameters and change "adaptive_ptime" to true.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(kSsrcX);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrcX);
   ASSERT_EQ(1u, parameters.encodings.size());
   ASSERT_FALSE(parameters.encodings[0].adaptive_ptime);
   parameters.encodings[0].adaptive_ptime = true;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(kSsrcX, parameters).ok());
+  EXPECT_TRUE(
+      channel_->AsSendChannel()->SetRtpSendParameters(kSsrcX, parameters).ok());
   EXPECT_TRUE(GetAudioNetworkAdaptorConfig(kSsrcX));
   EXPECT_EQ(16000, GetSendStreamConfig(kSsrcX).min_bitrate_bps);
 
   parameters.encodings[0].adaptive_ptime = false;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(kSsrcX, parameters).ok());
+  EXPECT_TRUE(
+      channel_->AsSendChannel()->SetRtpSendParameters(kSsrcX, parameters).ok());
   EXPECT_FALSE(GetAudioNetworkAdaptorConfig(kSsrcX));
   EXPECT_EQ(32000, GetSendStreamConfig(kSsrcX).min_bitrate_bps);
 }
@@ -1207,9 +1224,11 @@
   EXPECT_EQ(send_parameters_.options.audio_network_adaptor_config,
             GetAudioNetworkAdaptorConfig(kSsrcX));
 
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(kSsrcX);
+  webrtc::RtpParameters parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrcX);
   parameters.encodings[0].adaptive_ptime = false;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(kSsrcX, parameters).ok());
+  EXPECT_TRUE(
+      channel_->AsSendChannel()->SetRtpSendParameters(kSsrcX, parameters).ok());
   EXPECT_EQ(send_parameters_.options.audio_network_adaptor_config,
             GetAudioNetworkAdaptorConfig(kSsrcX));
 }
@@ -1227,8 +1246,8 @@
   SetupForMultiSendStream();
   // Create send streams.
   for (uint32_t ssrc : kSsrcs4) {
-    EXPECT_TRUE(
-        channel_->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc)));
+    EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+        cricket::StreamParams::CreateLegacy(ssrc)));
   }
   // Configure one stream to be limited by the stream config, another to be
   // limited by the global max, and the third one with no per-stream limit
@@ -1258,7 +1277,8 @@
   parameters.codecs.push_back(kPcmuCodec);
   SetSendParameters(parameters);
 
-  webrtc::RtpParameters rtp_parameters = channel_->GetRtpSendParameters(kSsrcX);
+  webrtc::RtpParameters rtp_parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrcX);
   ASSERT_EQ(2u, rtp_parameters.codecs.size());
   EXPECT_EQ(kOpusCodec.ToCodecParameters(), rtp_parameters.codecs[0]);
   EXPECT_EQ(kPcmuCodec.ToCodecParameters(), rtp_parameters.codecs[1]);
@@ -1270,7 +1290,8 @@
   params.cname = "rtcpcname";
   EXPECT_TRUE(SetupSendStream(params));
 
-  webrtc::RtpParameters rtp_parameters = channel_->GetRtpSendParameters(kSsrcX);
+  webrtc::RtpParameters rtp_parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrcX);
   EXPECT_STREQ("rtcpcname", rtp_parameters.rtcp.cname.c_str());
 }
 
@@ -1278,20 +1299,22 @@
        DetectRtpSendParameterHeaderExtensionsChange) {
   EXPECT_TRUE(SetupSendStream());
 
-  webrtc::RtpParameters rtp_parameters = channel_->GetRtpSendParameters(kSsrcX);
+  webrtc::RtpParameters rtp_parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrcX);
   rtp_parameters.header_extensions.emplace_back();
 
   EXPECT_NE(0u, rtp_parameters.header_extensions.size());
 
   webrtc::RTCError result =
-      channel_->SetRtpSendParameters(kSsrcX, rtp_parameters);
+      channel_->AsSendChannel()->SetRtpSendParameters(kSsrcX, rtp_parameters);
   EXPECT_EQ(webrtc::RTCErrorType::INVALID_MODIFICATION, result.type());
 }
 
 // Test that GetRtpSendParameters returns an SSRC.
 TEST_P(WebRtcVoiceEngineTestFake, GetRtpSendParametersSsrc) {
   EXPECT_TRUE(SetupSendStream());
-  webrtc::RtpParameters rtp_parameters = channel_->GetRtpSendParameters(kSsrcX);
+  webrtc::RtpParameters rtp_parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrcX);
   ASSERT_EQ(1u, rtp_parameters.encodings.size());
   EXPECT_EQ(kSsrcX, rtp_parameters.encodings[0].ssrc);
 }
@@ -1304,14 +1327,19 @@
   parameters.codecs.push_back(kPcmuCodec);
   SetSendParameters(parameters);
 
-  webrtc::RtpParameters initial_params = channel_->GetRtpSendParameters(kSsrcX);
+  webrtc::RtpParameters initial_params =
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrcX);
 
   // We should be able to set the params we just got.
-  EXPECT_TRUE(channel_->SetRtpSendParameters(kSsrcX, initial_params).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(kSsrcX, initial_params)
+                  .ok());
 
   // ... And this shouldn't change the params returned by GetRtpSendParameters.
-  webrtc::RtpParameters new_params = channel_->GetRtpSendParameters(kSsrcX);
-  EXPECT_EQ(initial_params, channel_->GetRtpSendParameters(kSsrcX));
+  webrtc::RtpParameters new_params =
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrcX);
+  EXPECT_EQ(initial_params,
+            channel_->AsSendChannel()->GetRtpSendParameters(kSsrcX));
 }
 
 // Test that max_bitrate_bps in send stream config gets updated correctly when
@@ -1322,13 +1350,16 @@
   send_parameters.codecs.push_back(kOpusCodec);
   SetSendParameters(send_parameters);
 
-  webrtc::RtpParameters rtp_parameters = channel_->GetRtpSendParameters(kSsrcX);
+  webrtc::RtpParameters rtp_parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrcX);
   // Expect empty on parameters.encodings[0].max_bitrate_bps;
   EXPECT_FALSE(rtp_parameters.encodings[0].max_bitrate_bps);
 
   constexpr int kMaxBitrateBps = 6000;
   rtp_parameters.encodings[0].max_bitrate_bps = kMaxBitrateBps;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(kSsrcX, rtp_parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(kSsrcX, rtp_parameters)
+                  .ok());
 
   const int max_bitrate = GetSendStreamConfig(kSsrcX).max_bitrate_bps;
   EXPECT_EQ(max_bitrate, kMaxBitrateBps);
@@ -1338,35 +1369,44 @@
 // a value <= 0, setting the parameters returns false.
 TEST_P(WebRtcVoiceEngineTestFake, SetRtpSendParameterInvalidBitratePriority) {
   EXPECT_TRUE(SetupSendStream());
-  webrtc::RtpParameters rtp_parameters = channel_->GetRtpSendParameters(kSsrcX);
+  webrtc::RtpParameters rtp_parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrcX);
   EXPECT_EQ(1UL, rtp_parameters.encodings.size());
   EXPECT_EQ(webrtc::kDefaultBitratePriority,
             rtp_parameters.encodings[0].bitrate_priority);
 
   rtp_parameters.encodings[0].bitrate_priority = 0;
-  EXPECT_FALSE(channel_->SetRtpSendParameters(kSsrcX, rtp_parameters).ok());
+  EXPECT_FALSE(channel_->AsSendChannel()
+                   ->SetRtpSendParameters(kSsrcX, rtp_parameters)
+                   .ok());
   rtp_parameters.encodings[0].bitrate_priority = -1.0;
-  EXPECT_FALSE(channel_->SetRtpSendParameters(kSsrcX, rtp_parameters).ok());
+  EXPECT_FALSE(channel_->AsSendChannel()
+                   ->SetRtpSendParameters(kSsrcX, rtp_parameters)
+                   .ok());
 }
 
 // Test that the bitrate_priority in the send stream config gets updated when
 // SetRtpSendParameters is set for the VoiceMediaChannel.
 TEST_P(WebRtcVoiceEngineTestFake, SetRtpSendParameterUpdatesBitratePriority) {
   EXPECT_TRUE(SetupSendStream());
-  webrtc::RtpParameters rtp_parameters = channel_->GetRtpSendParameters(kSsrcX);
+  webrtc::RtpParameters rtp_parameters =
+      channel_->AsSendChannel()->GetRtpSendParameters(kSsrcX);
 
   EXPECT_EQ(1UL, rtp_parameters.encodings.size());
   EXPECT_EQ(webrtc::kDefaultBitratePriority,
             rtp_parameters.encodings[0].bitrate_priority);
   double new_bitrate_priority = 2.0;
   rtp_parameters.encodings[0].bitrate_priority = new_bitrate_priority;
-  EXPECT_TRUE(channel_->SetRtpSendParameters(kSsrcX, rtp_parameters).ok());
+  EXPECT_TRUE(channel_->AsSendChannel()
+                  ->SetRtpSendParameters(kSsrcX, rtp_parameters)
+                  .ok());
 
   // The priority should get set for both the audio channel's rtp parameters
   // and the audio send stream's audio config.
-  EXPECT_EQ(
-      new_bitrate_priority,
-      channel_->GetRtpSendParameters(kSsrcX).encodings[0].bitrate_priority);
+  EXPECT_EQ(new_bitrate_priority, channel_->AsSendChannel()
+                                      ->GetRtpSendParameters(kSsrcX)
+                                      .encodings[0]
+                                      .bitrate_priority);
   EXPECT_EQ(new_bitrate_priority, GetSendStreamConfig(kSsrcX).bitrate_priority);
 }
 
@@ -1792,8 +1832,8 @@
   // NACK should be enabled even with no send stream.
   EXPECT_EQ(kRtpHistoryMs, GetRecvStreamConfig(kSsrcX).rtp.nack.rtp_history_ms);
 
-  EXPECT_TRUE(
-      channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(kSsrcX)));
 }
 
 // Test that we can enable NACK on receive streams.
@@ -2038,8 +2078,8 @@
   parameters.codecs[2].id = 97;  // narrowband CN
   parameters.codecs[3].id = 98;  // DTMF
   SetSendParameters(parameters);
-  EXPECT_TRUE(
-      channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(kSsrcX)));
 
   const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
   EXPECT_EQ(96, send_codec_spec.payload_type);
@@ -2212,8 +2252,8 @@
   SetSend(true);
 
   for (uint32_t ssrc : kSsrcs4) {
-    EXPECT_TRUE(
-        channel_->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc)));
+    EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+        cricket::StreamParams::CreateLegacy(ssrc)));
     SetAudioSend(ssrc, true, &fake_source_);
     // Verify that we are in a sending state for all the created streams.
     EXPECT_TRUE(GetSendStream(ssrc).IsSending());
@@ -2222,9 +2262,9 @@
 
   // Delete the send streams.
   for (uint32_t ssrc : kSsrcs4) {
-    EXPECT_TRUE(channel_->RemoveSendStream(ssrc));
+    EXPECT_TRUE(channel_->AsSendChannel()->RemoveSendStream(ssrc));
     EXPECT_FALSE(call_.GetAudioSendStream(ssrc));
-    EXPECT_FALSE(channel_->RemoveSendStream(ssrc));
+    EXPECT_FALSE(channel_->AsSendChannel()->RemoveSendStream(ssrc));
   }
   EXPECT_EQ(0u, call_.GetAudioSendStreams().size());
 }
@@ -2235,8 +2275,8 @@
 
   // Create send streams.
   for (uint32_t ssrc : kSsrcs4) {
-    EXPECT_TRUE(
-        channel_->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc)));
+    EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+        cricket::StreamParams::CreateLegacy(ssrc)));
   }
 
   cricket::AudioSendParameters parameters;
@@ -2275,8 +2315,8 @@
 
   // Create the send channels and they should be a "not sending" date.
   for (uint32_t ssrc : kSsrcs4) {
-    EXPECT_TRUE(
-        channel_->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc)));
+    EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+        cricket::StreamParams::CreateLegacy(ssrc)));
     SetAudioSend(ssrc, true, &fake_source_);
     EXPECT_FALSE(GetSendStream(ssrc).IsSending());
   }
@@ -2302,8 +2342,8 @@
 
   // Create send streams.
   for (uint32_t ssrc : kSsrcs4) {
-    EXPECT_TRUE(
-        channel_->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc)));
+    EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+        cricket::StreamParams::CreateLegacy(ssrc)));
   }
 
   // Create a receive stream to check that none of the send streams end up in
@@ -2337,7 +2377,7 @@
   // Remove the kSsrcY stream. No receiver stats.
   {
     cricket::VoiceMediaInfo info;
-    EXPECT_TRUE(channel_->RemoveRecvStream(kSsrcY));
+    EXPECT_TRUE(channel_->AsReceiveChannel()->RemoveRecvStream(kSsrcY));
     EXPECT_CALL(*adm_, GetPlayoutUnderrunCount()).WillOnce(Return(0));
     EXPECT_EQ(true,
               channel_->GetStats(&info, /*get_and_clear_legacy_stats=*/true));
@@ -2398,8 +2438,8 @@
   EXPECT_TRUE(GetRecvStream(kSsrcZ).started());
 
   // Now remove the recv streams.
-  EXPECT_TRUE(channel_->RemoveRecvStream(kSsrcZ));
-  EXPECT_TRUE(channel_->RemoveRecvStream(kSsrcY));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->RemoveRecvStream(kSsrcZ));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->RemoveRecvStream(kSsrcY));
 }
 
 TEST_P(WebRtcVoiceEngineTestFake, SetAudioNetworkAdaptorViaOptions) {
@@ -2489,7 +2529,7 @@
   // Remove the kSsrcY stream. No receiver stats.
   {
     cricket::VoiceMediaInfo info;
-    EXPECT_TRUE(channel_->RemoveRecvStream(kSsrcY));
+    EXPECT_TRUE(channel_->AsReceiveChannel()->RemoveRecvStream(kSsrcY));
     EXPECT_CALL(*adm_, GetPlayoutUnderrunCount()).WillOnce(Return(0));
     EXPECT_EQ(true,
               channel_->GetStats(&info, /*get_and_clear_legacy_stats=*/true));
@@ -2527,8 +2567,8 @@
 TEST_P(WebRtcVoiceEngineTestFake, SetSendSsrcAfterCreatingReceiveChannel) {
   EXPECT_TRUE(SetupChannel());
   EXPECT_TRUE(AddRecvStream(kSsrcY));
-  EXPECT_TRUE(
-      channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(kSsrcX)));
   EXPECT_TRUE(call_.GetAudioSendStream(kSsrcX));
   EXPECT_EQ(kSsrcX, GetRecvStreamConfig(kSsrcY).rtp.local_ssrc);
 }
@@ -2590,9 +2630,9 @@
   EXPECT_EQ(s3.received_packets(), 1);
   EXPECT_TRUE(s3.VerifyLastPacket(packets[3], sizeof(packets[3])));
 
-  EXPECT_TRUE(channel_->RemoveRecvStream(ssrc3));
-  EXPECT_TRUE(channel_->RemoveRecvStream(ssrc2));
-  EXPECT_TRUE(channel_->RemoveRecvStream(ssrc1));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->RemoveRecvStream(ssrc3));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->RemoveRecvStream(ssrc2));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->RemoveRecvStream(ssrc1));
 }
 
 // Test that receiving on an unsignaled stream works (a stream is created).
@@ -2615,7 +2655,7 @@
   EXPECT_TRUE(SetupChannel());
   cricket::StreamParams unsignaled_stream;
   unsignaled_stream.set_stream_ids({kSyncLabel});
-  ASSERT_TRUE(channel_->AddRecvStream(unsignaled_stream));
+  ASSERT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(unsignaled_stream));
   // The stream shouldn't have been created at this point because it doesn't
   // have any SSRCs.
   EXPECT_EQ(0u, call_.GetAudioReceiveStreams().size());
@@ -2629,8 +2669,8 @@
 
   // Remset the unsignaled stream to clear the cached parameters. If a new
   // default unsignaled receive stream is created it will not have a sync group.
-  channel_->ResetUnsignaledRecvStream();
-  channel_->RemoveRecvStream(kSsrc1);
+  channel_->AsReceiveChannel()->ResetUnsignaledRecvStream();
+  channel_->AsReceiveChannel()->RemoveRecvStream(kSsrc1);
 
   DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
 
@@ -2659,7 +2699,7 @@
   ASSERT_EQ(receivers1.size(), 2u);
 
   // Should remove all default streams.
-  channel_->ResetUnsignaledRecvStream();
+  channel_->AsReceiveChannel()->ResetUnsignaledRecvStream();
   const auto& receivers2 = call_.GetAudioReceiveStreams();
   EXPECT_EQ(0u, receivers2.size());
 }
@@ -2788,7 +2828,7 @@
   stream_params.ssrcs.push_back(1);
   stream_params.set_stream_ids({new_stream_id});
 
-  EXPECT_TRUE(channel_->AddRecvStream(stream_params));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(stream_params));
   EXPECT_EQ(1u, streams.size());
   // The audio receive stream should not have been recreated.
   EXPECT_EQ(audio_receive_stream_id, streams.front()->id());
@@ -3186,7 +3226,7 @@
   EXPECT_FALSE(channel_->SetOutputVolume(kSsrcY, 0.5));
   cricket::StreamParams stream;
   stream.ssrcs.push_back(kSsrcY);
-  EXPECT_TRUE(channel_->AddRecvStream(stream));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(stream));
   EXPECT_DOUBLE_EQ(1, GetRecvStream(kSsrcY).gain());
   EXPECT_TRUE(channel_->SetOutputVolume(kSsrcY, 3));
   EXPECT_DOUBLE_EQ(3, GetRecvStream(kSsrcY).gain());
@@ -3228,14 +3268,18 @@
 
 TEST_P(WebRtcVoiceEngineTestFake, BaseMinimumPlayoutDelayMs) {
   EXPECT_TRUE(SetupChannel());
-  EXPECT_FALSE(channel_->SetBaseMinimumPlayoutDelayMs(kSsrcY, 200));
-  EXPECT_FALSE(channel_->GetBaseMinimumPlayoutDelayMs(kSsrcY).has_value());
+  EXPECT_FALSE(
+      channel_->AsReceiveChannel()->SetBaseMinimumPlayoutDelayMs(kSsrcY, 200));
+  EXPECT_FALSE(channel_->AsReceiveChannel()
+                   ->GetBaseMinimumPlayoutDelayMs(kSsrcY)
+                   .has_value());
 
   cricket::StreamParams stream;
   stream.ssrcs.push_back(kSsrcY);
-  EXPECT_TRUE(channel_->AddRecvStream(stream));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(stream));
   EXPECT_EQ(0, GetRecvStream(kSsrcY).base_mininum_playout_delay_ms());
-  EXPECT_TRUE(channel_->SetBaseMinimumPlayoutDelayMs(kSsrcY, 300));
+  EXPECT_TRUE(
+      channel_->AsReceiveChannel()->SetBaseMinimumPlayoutDelayMs(kSsrcY, 300));
   EXPECT_EQ(300, GetRecvStream(kSsrcY).base_mininum_playout_delay_ms());
 }
 
@@ -3246,43 +3290,70 @@
 
   // Spawn an unsignaled stream by sending a packet - delay should be 0.
   DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
-  EXPECT_EQ(0, channel_->GetBaseMinimumPlayoutDelayMs(kSsrc1).value_or(-1));
+  EXPECT_EQ(0, channel_->AsReceiveChannel()
+                   ->GetBaseMinimumPlayoutDelayMs(kSsrc1)
+                   .value_or(-1));
   // Check that it doesn't provide default values for unknown ssrc.
-  EXPECT_FALSE(channel_->GetBaseMinimumPlayoutDelayMs(kSsrcY).has_value());
+  EXPECT_FALSE(channel_->AsReceiveChannel()
+                   ->GetBaseMinimumPlayoutDelayMs(kSsrcY)
+                   .has_value());
 
   // Check that default value for unsignaled streams is 0.
-  EXPECT_EQ(0, channel_->GetBaseMinimumPlayoutDelayMs(kSsrc0).value_or(-1));
+  EXPECT_EQ(0, channel_->AsReceiveChannel()
+                   ->GetBaseMinimumPlayoutDelayMs(kSsrc0)
+                   .value_or(-1));
 
   // Should remember the delay 100 which will be set on new unsignaled streams,
   // and also set the delay to 100 on existing unsignaled streams.
-  EXPECT_TRUE(channel_->SetBaseMinimumPlayoutDelayMs(kSsrc0, 100));
-  EXPECT_EQ(100, channel_->GetBaseMinimumPlayoutDelayMs(kSsrc0).value_or(-1));
+  EXPECT_TRUE(
+      channel_->AsReceiveChannel()->SetBaseMinimumPlayoutDelayMs(kSsrc0, 100));
+  EXPECT_EQ(100, channel_->AsReceiveChannel()
+                     ->GetBaseMinimumPlayoutDelayMs(kSsrc0)
+                     .value_or(-1));
   // Check that it doesn't provide default values for unknown ssrc.
-  EXPECT_FALSE(channel_->GetBaseMinimumPlayoutDelayMs(kSsrcY).has_value());
+  EXPECT_FALSE(channel_->AsReceiveChannel()
+                   ->GetBaseMinimumPlayoutDelayMs(kSsrcY)
+                   .has_value());
 
   // Spawn an unsignaled stream by sending a packet - delay should be 100.
   unsigned char pcmuFrame2[sizeof(kPcmuFrame)];
   memcpy(pcmuFrame2, kPcmuFrame, sizeof(kPcmuFrame));
   rtc::SetBE32(&pcmuFrame2[8], kSsrcX);
   DeliverPacket(pcmuFrame2, sizeof(pcmuFrame2));
-  EXPECT_EQ(100, channel_->GetBaseMinimumPlayoutDelayMs(kSsrcX).value_or(-1));
+  EXPECT_EQ(100, channel_->AsReceiveChannel()
+                     ->GetBaseMinimumPlayoutDelayMs(kSsrcX)
+                     .value_or(-1));
 
   // Setting delay with SSRC=0 should affect all unsignaled streams.
-  EXPECT_TRUE(channel_->SetBaseMinimumPlayoutDelayMs(kSsrc0, 300));
+  EXPECT_TRUE(
+      channel_->AsReceiveChannel()->SetBaseMinimumPlayoutDelayMs(kSsrc0, 300));
   if (kMaxUnsignaledRecvStreams > 1) {
-    EXPECT_EQ(300, channel_->GetBaseMinimumPlayoutDelayMs(kSsrc1).value_or(-1));
+    EXPECT_EQ(300, channel_->AsReceiveChannel()
+                       ->GetBaseMinimumPlayoutDelayMs(kSsrc1)
+                       .value_or(-1));
   }
-  EXPECT_EQ(300, channel_->GetBaseMinimumPlayoutDelayMs(kSsrcX).value_or(-1));
+  EXPECT_EQ(300, channel_->AsReceiveChannel()
+                     ->GetBaseMinimumPlayoutDelayMs(kSsrcX)
+                     .value_or(-1));
 
   // Setting delay on an individual stream affects only that.
-  EXPECT_TRUE(channel_->SetBaseMinimumPlayoutDelayMs(kSsrcX, 400));
+  EXPECT_TRUE(
+      channel_->AsReceiveChannel()->SetBaseMinimumPlayoutDelayMs(kSsrcX, 400));
   if (kMaxUnsignaledRecvStreams > 1) {
-    EXPECT_EQ(300, channel_->GetBaseMinimumPlayoutDelayMs(kSsrc1).value_or(-1));
+    EXPECT_EQ(300, channel_->AsReceiveChannel()
+                       ->GetBaseMinimumPlayoutDelayMs(kSsrc1)
+                       .value_or(-1));
   }
-  EXPECT_EQ(400, channel_->GetBaseMinimumPlayoutDelayMs(kSsrcX).value_or(-1));
-  EXPECT_EQ(300, channel_->GetBaseMinimumPlayoutDelayMs(kSsrc0).value_or(-1));
+  EXPECT_EQ(400, channel_->AsReceiveChannel()
+                     ->GetBaseMinimumPlayoutDelayMs(kSsrcX)
+                     .value_or(-1));
+  EXPECT_EQ(300, channel_->AsReceiveChannel()
+                     ->GetBaseMinimumPlayoutDelayMs(kSsrc0)
+                     .value_or(-1));
   // Check that it doesn't provide default values for unknown ssrc.
-  EXPECT_FALSE(channel_->GetBaseMinimumPlayoutDelayMs(kSsrcY).has_value());
+  EXPECT_FALSE(channel_->AsReceiveChannel()
+                   ->GetBaseMinimumPlayoutDelayMs(kSsrcY)
+                   .has_value());
 }
 
 TEST_P(WebRtcVoiceEngineTestFake, SetsSyncGroupFromStreamId) {
@@ -3294,9 +3365,9 @@
   sp.set_stream_ids({kStreamId});
   // Creating two channels to make sure that sync label is set properly for both
   // the default voice channel and following ones.
-  EXPECT_TRUE(channel_->AddRecvStream(sp));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(sp));
   sp.ssrcs[0] += 1;
-  EXPECT_TRUE(channel_->AddRecvStream(sp));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(sp));
 
   ASSERT_EQ(2u, call_.GetAudioReceiveStreams().size());
   EXPECT_EQ(kStreamId,
@@ -3319,8 +3390,8 @@
   EXPECT_TRUE(SetupSendStream());
   SetSendParameters(send_parameters_);
   for (uint32_t ssrc : ssrcs) {
-    EXPECT_TRUE(
-        channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(ssrc)));
+    EXPECT_TRUE(channel_->AsReceiveChannel()->AddRecvStream(
+        cricket::StreamParams::CreateLegacy(ssrc)));
   }
 
   EXPECT_EQ(2u, call_.GetAudioReceiveStreams().size());
@@ -3381,7 +3452,8 @@
   const cricket::FakeAudioReceiveStream* s =
       call_.GetAudioReceiveStream(kAudioSsrc);
   EXPECT_EQ(0, s->received_packets());
-  channel_->OnPacketReceived(kPcmuPacket, /* packet_time_us */ -1);
+  channel_->AsReceiveChannel()->OnPacketReceived(kPcmuPacket,
+                                                 /* packet_time_us */ -1);
   rtc::Thread::Current()->ProcessMessages(0);
 
   EXPECT_EQ(1, s->received_packets());
@@ -3393,8 +3465,8 @@
   EXPECT_TRUE(SetupSendStream());
   EXPECT_TRUE(AddRecvStream(kSsrcY));
   EXPECT_EQ(kSsrcX, GetRecvStreamConfig(kSsrcY).rtp.local_ssrc);
-  EXPECT_TRUE(
-      channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcZ)));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(kSsrcZ)));
   EXPECT_EQ(kSsrcX, GetRecvStreamConfig(kSsrcY).rtp.local_ssrc);
   EXPECT_TRUE(AddRecvStream(kSsrcW));
   EXPECT_EQ(kSsrcX, GetRecvStreamConfig(kSsrcW).rtp.local_ssrc);
@@ -3403,13 +3475,13 @@
 TEST_P(WebRtcVoiceEngineTestFake, AssociateFirstSendChannel_RecvCreatedFirst) {
   EXPECT_TRUE(SetupRecvStream());
   EXPECT_EQ(0xFA17FA17u, GetRecvStreamConfig(kSsrcX).rtp.local_ssrc);
-  EXPECT_TRUE(
-      channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcY)));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(kSsrcY)));
   EXPECT_EQ(kSsrcY, GetRecvStreamConfig(kSsrcX).rtp.local_ssrc);
   EXPECT_TRUE(AddRecvStream(kSsrcZ));
   EXPECT_EQ(kSsrcY, GetRecvStreamConfig(kSsrcZ).rtp.local_ssrc);
-  EXPECT_TRUE(
-      channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcW)));
+  EXPECT_TRUE(channel_->AsSendChannel()->AddSendStream(
+      cricket::StreamParams::CreateLegacy(kSsrcW)));
   EXPECT_EQ(kSsrcY, GetRecvStreamConfig(kSsrcX).rtp.local_ssrc);
   EXPECT_EQ(kSsrcY, GetRecvStreamConfig(kSsrcZ).rtp.local_ssrc);
 }
@@ -3457,7 +3529,7 @@
   EXPECT_NE(nullptr, GetRecvStream(kSsrc1).sink());
 
   // If we remove and add a default stream, it should get the same sink.
-  EXPECT_TRUE(channel_->RemoveRecvStream(kSsrc1));
+  EXPECT_TRUE(channel_->AsReceiveChannel()->RemoveRecvStream(kSsrc1));
   DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
   EXPECT_NE(nullptr, GetRecvStream(kSsrc1).sink());
 
@@ -3507,13 +3579,13 @@
   EXPECT_EQ(webrtc::kNetworkUp,
             call_.GetNetworkState(webrtc::MediaType::VIDEO));
 
-  channel_->OnReadyToSend(false);
+  channel_->AsSendChannel()->OnReadyToSend(false);
   EXPECT_EQ(webrtc::kNetworkDown,
             call_.GetNetworkState(webrtc::MediaType::AUDIO));
   EXPECT_EQ(webrtc::kNetworkUp,
             call_.GetNetworkState(webrtc::MediaType::VIDEO));
 
-  channel_->OnReadyToSend(true);
+  channel_->AsSendChannel()->OnReadyToSend(true);
   EXPECT_EQ(webrtc::kNetworkUp,
             call_.GetNetworkState(webrtc::MediaType::AUDIO));
   EXPECT_EQ(webrtc::kNetworkUp,
diff --git a/pc/audio_rtp_receiver.cc b/pc/audio_rtp_receiver.cc
index 09acfa8..7af460b 100644
--- a/pc/audio_rtp_receiver.cc
+++ b/pc/audio_rtp_receiver.cc
@@ -28,7 +28,7 @@
     std::string receiver_id,
     std::vector<std::string> stream_ids,
     bool is_unified_plan,
-    cricket::VoiceMediaChannel* voice_channel /*= nullptr*/)
+    cricket::VoiceMediaReceiveChannelInterface* voice_channel /*= nullptr*/)
     : AudioRtpReceiver(worker_thread,
                        receiver_id,
                        CreateStreamsFromIds(std::move(stream_ids)),
@@ -40,7 +40,7 @@
     const std::string& receiver_id,
     const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams,
     bool is_unified_plan,
-    cricket::VoiceMediaChannel* voice_channel /*= nullptr*/)
+    cricket::VoiceMediaReceiveChannelInterface* voice_channel /*= nullptr*/)
     : worker_thread_(worker_thread),
       id_(receiver_id),
       source_(rtc::make_ref_counted<RemoteAudioSource>(
@@ -324,7 +324,8 @@
 
   media_channel ? worker_thread_safety_->SetAlive()
                 : worker_thread_safety_->SetNotAlive();
-  media_channel_ = static_cast<cricket::VoiceMediaChannel*>(media_channel);
+  media_channel_ =
+      static_cast<cricket::VoiceMediaReceiveChannelInterface*>(media_channel);
 }
 
 void AudioRtpReceiver::NotifyFirstPacketReceived() {
diff --git a/pc/audio_rtp_receiver.h b/pc/audio_rtp_receiver.h
index cfebfe5..2e0f77c 100644
--- a/pc/audio_rtp_receiver.h
+++ b/pc/audio_rtp_receiver.h
@@ -50,18 +50,19 @@
   // However, when using that, the assumption is that right after construction,
   // a call to either `SetupUnsignaledMediaChannel` or `SetupMediaChannel`
   // will be made, which will internally start the source on the worker thread.
-  AudioRtpReceiver(rtc::Thread* worker_thread,
-                   std::string receiver_id,
-                   std::vector<std::string> stream_ids,
-                   bool is_unified_plan,
-                   cricket::VoiceMediaChannel* voice_channel = nullptr);
+  AudioRtpReceiver(
+      rtc::Thread* worker_thread,
+      std::string receiver_id,
+      std::vector<std::string> stream_ids,
+      bool is_unified_plan,
+      cricket::VoiceMediaReceiveChannelInterface* voice_channel = nullptr);
   // TODO(https://crbug.com/webrtc/9480): Remove this when streams() is removed.
   AudioRtpReceiver(
       rtc::Thread* worker_thread,
       const std::string& receiver_id,
       const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams,
       bool is_unified_plan,
-      cricket::VoiceMediaChannel* media_channel = nullptr);
+      cricket::VoiceMediaReceiveChannelInterface* media_channel = nullptr);
   virtual ~AudioRtpReceiver();
 
   // ObserverInterface implementation
@@ -135,8 +136,8 @@
   const std::string id_;
   const rtc::scoped_refptr<RemoteAudioSource> source_;
   const rtc::scoped_refptr<AudioTrackProxyWithInternal<AudioTrack>> track_;
-  cricket::VoiceMediaChannel* media_channel_ RTC_GUARDED_BY(worker_thread_) =
-      nullptr;
+  cricket::VoiceMediaReceiveChannelInterface* media_channel_
+      RTC_GUARDED_BY(worker_thread_) = nullptr;
   absl::optional<uint32_t> ssrc_ RTC_GUARDED_BY(worker_thread_);
   std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams_
       RTC_GUARDED_BY(&signaling_thread_checker_);
diff --git a/pc/audio_rtp_receiver_unittest.cc b/pc/audio_rtp_receiver_unittest.cc
index bab6b74..eb77212 100644
--- a/pc/audio_rtp_receiver_unittest.cc
+++ b/pc/audio_rtp_receiver_unittest.cc
@@ -66,7 +66,7 @@
 
   receiver_->track();
   receiver_->track()->set_enabled(true);
-  receiver_->SetMediaChannel(&media_channel_);
+  receiver_->SetMediaChannel(media_channel_.AsVoiceReceiveChannel());
   EXPECT_CALL(media_channel_, SetDefaultRawAudioSink(_)).Times(0);
   receiver_->SetupMediaChannel(kSsrc);
 
@@ -86,7 +86,7 @@
   receiver_->OnSetVolume(kVolume);
 
   receiver_->track()->set_enabled(true);
-  receiver_->SetMediaChannel(&media_channel_);
+  receiver_->SetMediaChannel(media_channel_.AsVoiceReceiveChannel());
 
   // The previosly set initial volume should be propagated to the provided
   // media_channel_ as soon as SetupMediaChannel is called.
diff --git a/pc/channel.cc b/pc/channel.cc
index 3d17f24..4c078de 100644
--- a/pc/channel.cc
+++ b/pc/channel.cc
@@ -869,7 +869,7 @@
       webrtc::RtpTransceiverDirectionHasRecv(content->direction()),
       &recv_params);
 
-  if (!media_send_channel()->SetRecvParameters(recv_params)) {
+  if (!media_receive_channel()->SetRecvParameters(recv_params)) {
     error_desc = StringFormat(
         "Failed to set local audio description recv parameters for m-section "
         "with mid='%s'.",
@@ -1008,7 +1008,7 @@
     }
   }
 
-  if (!media_send_channel()->SetRecvParameters(recv_params)) {
+  if (!media_receive_channel()->SetRecvParameters(recv_params)) {
     error_desc = StringFormat(
         "Failed to set local video description recv parameters for m-section "
         "with mid='%s'.",
@@ -1103,7 +1103,7 @@
   last_send_params_ = send_params;
 
   if (needs_recv_params_update) {
-    if (!media_send_channel()->SetRecvParameters(recv_params)) {
+    if (!media_receive_channel()->SetRecvParameters(recv_params)) {
       error_desc = StringFormat(
           "Failed to set recv parameters for m-section with mid='%s'.",
           mid().c_str());
diff --git a/pc/channel.h b/pc/channel.h
index bc30993..08a6679 100644
--- a/pc/channel.h
+++ b/pc/channel.h
@@ -32,6 +32,7 @@
 #include "call/rtp_demuxer.h"
 #include "call/rtp_packet_sink_interface.h"
 #include "media/base/media_channel.h"
+#include "media/base/media_channel_impl.h"
 #include "media/base/stream_params.h"
 #include "modules/rtp_rtcp/source/rtp_packet_received.h"
 #include "pc/channel_interface.h"
@@ -70,7 +71,7 @@
                     public sigslot::has_slots<>,
                     // TODO(tommi): Consider implementing these interfaces
                     // via composition.
-                    public MediaChannel::NetworkInterface,
+                    public MediaChannelNetworkInterface,
                     public webrtc::RtpPacketSinkInterface {
  public:
   // If `srtp_required` is true, the channel will not send or receive any
@@ -155,25 +156,29 @@
   // RtpPacketSinkInterface overrides.
   void OnRtpPacket(const webrtc::RtpPacketReceived& packet) override;
 
+  MediaChannel* media_channel() const override { return media_channel_.get(); }
+
   MediaSendChannelInterface* media_send_channel() const override {
-    return media_channel_.get();
+    return media_channel_->AsSendChannel();
   }
-  VideoMediaChannel* video_media_send_channel() const override {
+  VideoMediaSendChannelInterface* video_media_send_channel() const override {
     RTC_CHECK(false) << "Attempt to fetch video channel from non-video";
     return nullptr;
   }
-  VoiceMediaChannel* voice_media_send_channel() const override {
+  VoiceMediaSendChannelInterface* voice_media_send_channel() const override {
     RTC_CHECK(false) << "Attempt to fetch voice channel from non-voice";
     return nullptr;
   }
   MediaReceiveChannelInterface* media_receive_channel() const override {
-    return media_channel_.get();
+    return media_channel_->AsReceiveChannel();
   }
-  VideoMediaChannel* video_media_receive_channel() const override {
+  VideoMediaReceiveChannelInterface* video_media_receive_channel()
+      const override {
     RTC_CHECK(false) << "Attempt to fetch video channel from non-video";
     return nullptr;
   }
-  VoiceMediaChannel* voice_media_receive_channel() const override {
+  VoiceMediaReceiveChannelInterface* voice_media_receive_channel()
+      const override {
     RTC_CHECK(false) << "Attempt to fetch voice channel from non-voice";
     return nullptr;
   }
@@ -379,22 +384,22 @@
   ~VoiceChannel();
 
   // downcasts a MediaChannel
-  VoiceMediaChannel* media_send_channel() const override {
-    return static_cast<VoiceMediaChannel*>(BaseChannel::media_send_channel());
+  VoiceMediaSendChannelInterface* media_send_channel() const override {
+    return media_channel()->AsVoiceChannel()->AsVoiceSendChannel();
   }
 
-  VoiceMediaChannel* voice_media_send_channel() const override {
-    return static_cast<VoiceMediaChannel*>(media_send_channel());
+  VoiceMediaSendChannelInterface* voice_media_send_channel() const override {
+    return media_send_channel();
   }
 
   // downcasts a MediaChannel
-  VoiceMediaChannel* media_receive_channel() const override {
-    return static_cast<VoiceMediaChannel*>(
-        BaseChannel::media_receive_channel());
+  VoiceMediaReceiveChannelInterface* media_receive_channel() const override {
+    return media_channel()->AsVoiceChannel()->AsVoiceReceiveChannel();
   }
 
-  VoiceMediaChannel* voice_media_receive_channel() const override {
-    return static_cast<VoiceMediaChannel*>(media_receive_channel());
+  VoiceMediaReceiveChannelInterface* voice_media_receive_channel()
+      const override {
+    return media_receive_channel();
   }
 
   cricket::MediaType media_type() const override {
@@ -435,22 +440,22 @@
   ~VideoChannel();
 
   // downcasts a MediaChannel
-  VideoMediaChannel* media_send_channel() const override {
-    return static_cast<VideoMediaChannel*>(BaseChannel::media_send_channel());
+  VideoMediaSendChannelInterface* media_send_channel() const override {
+    return media_channel()->AsVideoChannel()->AsVideoSendChannel();
   }
 
-  VideoMediaChannel* video_media_send_channel() const override {
-    return static_cast<cricket::VideoMediaChannel*>(media_send_channel());
+  VideoMediaSendChannelInterface* video_media_send_channel() const override {
+    return media_send_channel();
   }
 
   // downcasts a MediaChannel
-  VideoMediaChannel* media_receive_channel() const override {
-    return static_cast<VideoMediaChannel*>(
-        BaseChannel::media_receive_channel());
+  VideoMediaReceiveChannelInterface* media_receive_channel() const override {
+    return media_channel()->AsVideoChannel()->AsVideoReceiveChannel();
   }
 
-  VideoMediaChannel* video_media_receive_channel() const override {
-    return static_cast<cricket::VideoMediaChannel*>(media_receive_channel());
+  VideoMediaReceiveChannelInterface* video_media_receive_channel()
+      const override {
+    return media_receive_channel();
   }
 
   cricket::MediaType media_type() const override {
diff --git a/pc/channel_interface.h b/pc/channel_interface.h
index 032bfad..445712b 100644
--- a/pc/channel_interface.h
+++ b/pc/channel_interface.h
@@ -28,6 +28,7 @@
 
 namespace cricket {
 
+class MediaChannel;
 class MediaContentDescription;
 struct MediaConfig;
 
@@ -47,16 +48,20 @@
   virtual ~ChannelInterface() = default;
   virtual cricket::MediaType media_type() const = 0;
 
+  // Temporary fix while MediaChannel is being reconstructed
+  virtual MediaChannel* media_channel() const = 0;
   virtual MediaSendChannelInterface* media_send_channel() const = 0;
   // Typecasts of media_channel(). Will cause an exception if the
   // channel is of the wrong type.
-  virtual VideoMediaChannel* video_media_send_channel() const = 0;
-  virtual VoiceMediaChannel* voice_media_send_channel() const = 0;
+  virtual VideoMediaSendChannelInterface* video_media_send_channel() const = 0;
+  virtual VoiceMediaSendChannelInterface* voice_media_send_channel() const = 0;
   virtual MediaReceiveChannelInterface* media_receive_channel() const = 0;
   // Typecasts of media_channel(). Will cause an exception if the
   // channel is of the wrong type.
-  virtual VideoMediaChannel* video_media_receive_channel() const = 0;
-  virtual VoiceMediaChannel* voice_media_receive_channel() const = 0;
+  virtual VideoMediaReceiveChannelInterface* video_media_receive_channel()
+      const = 0;
+  virtual VoiceMediaReceiveChannelInterface* voice_media_receive_channel()
+      const = 0;
 
   // Returns a string view for the transport name. Fetching the transport name
   // must be done on the network thread only and note that the lifetime of
diff --git a/pc/legacy_stats_collector.cc b/pc/legacy_stats_collector.cc
index 5941b8b..ad9f7ad 100644
--- a/pc/legacy_stats_collector.cc
+++ b/pc/legacy_stats_collector.cc
@@ -34,6 +34,7 @@
 #include "api/video/video_timing.h"
 #include "call/call.h"
 #include "media/base/media_channel.h"
+#include "media/base/media_channel_impl.h"
 #include "modules/audio_processing/include/audio_processing_statistics.h"
 #include "p2p/base/ice_transport_internal.h"
 #include "p2p/base/p2p_constants.h"
@@ -1042,7 +1043,8 @@
     }
     auto* video_channel = transceiver->internal()->channel();
     if (video_channel) {
-      video_media_channels.push_back(video_channel->video_media_send_channel());
+      video_media_channels.push_back(static_cast<cricket::VideoMediaChannel*>(
+          video_channel->video_media_send_channel()));
     }
   }
 
@@ -1150,15 +1152,15 @@
 };
 
 std::unique_ptr<MediaChannelStatsGatherer> CreateMediaChannelStatsGatherer(
-    cricket::MediaSendChannelInterface* channel) {
+    cricket::MediaChannel* channel) {
   RTC_DCHECK(channel);
   if (channel->media_type() == cricket::MEDIA_TYPE_AUDIO) {
     return std::make_unique<VoiceMediaChannelStatsGatherer>(
-        static_cast<cricket::VoiceMediaChannel*>(channel));
+        channel->AsVoiceChannel());
   } else {
     RTC_DCHECK_EQ(channel->media_type(), cricket::MEDIA_TYPE_VIDEO);
     return std::make_unique<VideoMediaChannelStatsGatherer>(
-        static_cast<cricket::VideoMediaChannel*>(channel));
+        channel->AsVideoChannel());
   }
 }
 
@@ -1179,7 +1181,7 @@
         continue;
       }
       std::unique_ptr<MediaChannelStatsGatherer> gatherer =
-          CreateMediaChannelStatsGatherer(channel->media_send_channel());
+          CreateMediaChannelStatsGatherer(channel->media_channel());
       gatherer->mid = channel->mid();
       gatherer->transport_name = transport_names_by_mid.at(gatherer->mid);
 
diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc
index 8f2015e..5de77fe 100644
--- a/pc/peer_connection.cc
+++ b/pc/peer_connection.cc
@@ -1174,14 +1174,14 @@
     auto audio_sender =
         AudioRtpSender::Create(worker_thread(), rtc::CreateRandomUuid(),
                                legacy_stats_.get(), rtp_manager());
-    audio_sender->SetMediaChannel(rtp_manager()->voice_media_channel());
+    audio_sender->SetMediaChannel(rtp_manager()->voice_media_send_channel());
     new_sender = RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
         signaling_thread(), audio_sender);
     rtp_manager()->GetAudioTransceiver()->internal()->AddSender(new_sender);
   } else if (kind == MediaStreamTrackInterface::kVideoKind) {
     auto video_sender = VideoRtpSender::Create(
         worker_thread(), rtc::CreateRandomUuid(), rtp_manager());
-    video_sender->SetMediaChannel(rtp_manager()->video_media_channel());
+    video_sender->SetMediaChannel(rtp_manager()->video_media_send_channel());
     new_sender = RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
         signaling_thread(), video_sender);
     rtp_manager()->GetVideoTransceiver()->internal()->AddSender(new_sender);
@@ -1629,15 +1629,16 @@
   }
 
   if (modified_config.allow_codec_switching.has_value()) {
-    std::vector<cricket::VideoMediaChannel*> channels;
+    std::vector<cricket::VideoMediaSendChannelInterface*> channels;
     for (const auto& transceiver : rtp_manager()->transceivers()->List()) {
       if (transceiver->media_type() != cricket::MEDIA_TYPE_VIDEO)
         continue;
 
       auto* video_channel = transceiver->internal()->channel();
       if (video_channel)
-        channels.push_back(static_cast<cricket::VideoMediaChannel*>(
-            video_channel->media_send_channel()));
+        channels.push_back(
+            static_cast<cricket::VideoMediaSendChannelInterface*>(
+                video_channel->media_send_channel()));
     }
 
     worker_thread()->BlockingCall(
diff --git a/pc/remote_audio_source.cc b/pc/remote_audio_source.cc
index 1058d1c..a516c57 100644
--- a/pc/remote_audio_source.cc
+++ b/pc/remote_audio_source.cc
@@ -70,8 +70,9 @@
   }
 }
 
-void RemoteAudioSource::Start(cricket::VoiceMediaChannel* media_channel,
-                              absl::optional<uint32_t> ssrc) {
+void RemoteAudioSource::Start(
+    cricket::VoiceMediaReceiveChannelInterface* media_channel,
+    absl::optional<uint32_t> ssrc) {
   RTC_DCHECK_RUN_ON(worker_thread_);
 
   // Register for callbacks immediately before AddSink so that we always get
@@ -84,8 +85,9 @@
              std::make_unique<AudioDataProxy>(this));
 }
 
-void RemoteAudioSource::Stop(cricket::VoiceMediaChannel* media_channel,
-                             absl::optional<uint32_t> ssrc) {
+void RemoteAudioSource::Stop(
+    cricket::VoiceMediaReceiveChannelInterface* media_channel,
+    absl::optional<uint32_t> ssrc) {
   RTC_DCHECK_RUN_ON(worker_thread_);
   RTC_DCHECK(media_channel);
   ssrc ? media_channel->SetRawAudioSink(*ssrc, nullptr)
diff --git a/pc/remote_audio_source.h b/pc/remote_audio_source.h
index d294a0f..0fac606 100644
--- a/pc/remote_audio_source.h
+++ b/pc/remote_audio_source.h
@@ -49,9 +49,9 @@
 
   // Register and unregister remote audio source with the underlying media
   // engine.
-  void Start(cricket::VoiceMediaChannel* media_channel,
+  void Start(cricket::VoiceMediaReceiveChannelInterface* media_channel,
              absl::optional<uint32_t> ssrc);
-  void Stop(cricket::VoiceMediaChannel* media_channel,
+  void Stop(cricket::VoiceMediaReceiveChannelInterface* media_channel,
             absl::optional<uint32_t> ssrc);
   void SetState(SourceState new_state);
 
diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc
index 374780e..d500a7b 100644
--- a/pc/rtc_stats_collector.cc
+++ b/pc/rtc_stats_collector.cc
@@ -36,6 +36,7 @@
 #include "api/video_codecs/scalability_mode.h"
 #include "common_video/include/quality_limitation_reason.h"
 #include "media/base/media_channel.h"
+#include "media/base/media_channel_impl.h"
 #include "modules/audio_processing/include/audio_processing_statistics.h"
 #include "modules/rtp_rtcp/include/report_block_data.h"
 #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
@@ -2365,13 +2366,15 @@
 
       if (media_type == cricket::MEDIA_TYPE_AUDIO) {
         cricket::VoiceMediaChannel* voice_channel =
-            channel->voice_media_send_channel();
+            static_cast<cricket::VoiceMediaChannel*>(
+                channel->voice_media_send_channel());
         RTC_DCHECK(voice_stats.find(voice_channel) == voice_stats.end());
         voice_stats.insert(
             std::make_pair(voice_channel, cricket::VoiceMediaInfo()));
       } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
         cricket::VideoMediaChannel* video_channel =
-            channel->video_media_send_channel();
+            static_cast<cricket::VideoMediaChannel*>(
+                channel->video_media_send_channel());
         RTC_DCHECK(video_stats.find(video_channel) == video_stats.end());
         video_stats.insert(
             std::make_pair(video_channel, cricket::VideoMediaInfo()));
@@ -2410,12 +2413,14 @@
         cricket::MediaType media_type = transceiver->media_type();
         if (media_type == cricket::MEDIA_TYPE_AUDIO) {
           cricket::VoiceMediaChannel* voice_channel =
-              channel->voice_media_send_channel();
+              static_cast<cricket::VoiceMediaChannel*>(
+                  channel->voice_media_send_channel());
           RTC_DCHECK(voice_stats.find(voice_channel) != voice_stats.end());
           voice_media_info = std::move(voice_stats[voice_channel]);
         } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
           cricket::VideoMediaChannel* video_channel =
-              channel->video_media_send_channel();
+              static_cast<cricket::VideoMediaChannel*>(
+                  channel->video_media_send_channel());
           RTC_DCHECK(video_stats.find(video_channel) != video_stats.end());
           video_media_info = std::move(video_stats[video_channel]);
         }
diff --git a/pc/rtp_sender.h b/pc/rtp_sender.h
index 0a2348c..29e5f16 100644
--- a/pc/rtp_sender.h
+++ b/pc/rtp_sender.h
@@ -378,8 +378,8 @@
   void RemoveTrackFromStats() override;
 
  private:
-  cricket::VoiceMediaChannel* voice_media_channel() {
-    return static_cast<cricket::VoiceMediaChannel*>(media_channel_);
+  cricket::VoiceMediaSendChannelInterface* voice_media_channel() {
+    return media_channel_->AsVoiceSendChannel();
   }
   rtc::scoped_refptr<AudioTrackInterface> audio_track() const {
     return rtc::scoped_refptr<AudioTrackInterface>(
@@ -436,8 +436,8 @@
   void AttachTrack() override;
 
  private:
-  cricket::VideoMediaChannel* video_media_channel() {
-    return static_cast<cricket::VideoMediaChannel*>(media_channel_);
+  cricket::VideoMediaSendChannelInterface* video_media_channel() {
+    return media_channel_->AsVideoSendChannel();
   }
   rtc::scoped_refptr<VideoTrackInterface> video_track() const {
     return rtc::scoped_refptr<VideoTrackInterface>(
diff --git a/pc/rtp_sender_receiver_unittest.cc b/pc/rtp_sender_receiver_unittest.cc
index 42072de..a189e65 100644
--- a/pc/rtp_sender_receiver_unittest.cc
+++ b/pc/rtp_sender_receiver_unittest.cc
@@ -204,7 +204,7 @@
     ASSERT_TRUE(audio_rtp_sender_->SetTrack(audio_track_.get()));
     EXPECT_CALL(*set_streams_observer, OnSetStreams());
     audio_rtp_sender_->SetStreams({local_stream_->id()});
-    audio_rtp_sender_->SetMediaChannel(voice_media_channel());
+    audio_rtp_sender_->SetMediaChannel(voice_media_channel()->AsSendChannel());
     audio_rtp_sender_->SetSsrc(kAudioSsrc);
     VerifyVoiceChannelInput();
   }
@@ -212,7 +212,8 @@
   void CreateAudioRtpSenderWithNoTrack() {
     audio_rtp_sender_ =
         AudioRtpSender::Create(worker_thread_, /*id=*/"", nullptr, nullptr);
-    audio_rtp_sender_->SetMediaChannel(voice_media_channel());
+    audio_rtp_sender_->SetMediaChannel(
+        voice_media_channel()->AsVoiceSendChannel());
   }
 
   void CreateVideoRtpSender(uint32_t ssrc) {
@@ -264,14 +265,16 @@
     ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_.get()));
     EXPECT_CALL(*set_streams_observer, OnSetStreams());
     video_rtp_sender_->SetStreams({local_stream_->id()});
-    video_rtp_sender_->SetMediaChannel(video_media_channel());
+    video_rtp_sender_->SetMediaChannel(
+        video_media_channel()->AsVideoSendChannel());
     video_rtp_sender_->SetSsrc(ssrc);
     VerifyVideoChannelInput(ssrc);
   }
   void CreateVideoRtpSenderWithNoTrack() {
     video_rtp_sender_ =
         VideoRtpSender::Create(worker_thread_, /*id=*/"", nullptr);
-    video_rtp_sender_->SetMediaChannel(video_media_channel());
+    video_rtp_sender_->SetMediaChannel(
+        video_media_channel()->AsVideoSendChannel());
   }
 
   void DestroyAudioRtpSender() {
@@ -289,7 +292,8 @@
     audio_rtp_receiver_ = rtc::make_ref_counted<AudioRtpReceiver>(
         rtc::Thread::Current(), kAudioTrackId, streams,
         /*is_unified_plan=*/true);
-    audio_rtp_receiver_->SetMediaChannel(voice_media_channel());
+    audio_rtp_receiver_->SetMediaChannel(
+        voice_media_channel()->AsVoiceReceiveChannel());
     audio_rtp_receiver_->SetupMediaChannel(kAudioSsrc);
     audio_track_ = audio_rtp_receiver_->audio_track();
     VerifyVoiceChannelOutput();
@@ -299,7 +303,8 @@
       std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams = {}) {
     video_rtp_receiver_ = rtc::make_ref_counted<VideoRtpReceiver>(
         rtc::Thread::Current(), kVideoTrackId, streams);
-    video_rtp_receiver_->SetMediaChannel(video_media_channel());
+    video_rtp_receiver_->SetMediaChannel(
+        video_media_channel()->AsVideoReceiveChannel());
     video_rtp_receiver_->SetupMediaChannel(kVideoSsrc);
     video_track_ = video_rtp_receiver_->video_track();
     VerifyVideoChannelOutput();
@@ -319,7 +324,8 @@
 
     video_rtp_receiver_ = rtc::make_ref_counted<VideoRtpReceiver>(
         rtc::Thread::Current(), kVideoTrackId, streams);
-    video_rtp_receiver_->SetMediaChannel(video_media_channel());
+    video_rtp_receiver_->SetMediaChannel(
+        video_media_channel()->AsVideoReceiveChannel());
     video_rtp_receiver_->SetupMediaChannel(primary_ssrc);
     video_track_ = video_rtp_receiver_->video_track();
   }
@@ -689,15 +695,17 @@
 
 TEST_F(RtpSenderReceiverTest, AudioRtpReceiverDelay) {
   CreateAudioRtpReceiver();
-  VerifyRtpReceiverDelayBehaviour(voice_media_channel(),
-                                  audio_rtp_receiver_.get(), kAudioSsrc);
+  VerifyRtpReceiverDelayBehaviour(
+      voice_media_channel()->AsVoiceReceiveChannel(), audio_rtp_receiver_.get(),
+      kAudioSsrc);
   DestroyAudioRtpReceiver();
 }
 
 TEST_F(RtpSenderReceiverTest, VideoRtpReceiverDelay) {
   CreateVideoRtpReceiver();
-  VerifyRtpReceiverDelayBehaviour(video_media_channel(),
-                                  video_rtp_receiver_.get(), kVideoSsrc);
+  VerifyRtpReceiverDelayBehaviour(
+      video_media_channel()->AsVideoReceiveChannel(), video_rtp_receiver_.get(),
+      kVideoSsrc);
   DestroyVideoRtpReceiver();
 }
 
@@ -936,7 +944,8 @@
   cricket::StreamParams stream_params =
       cricket::CreateSimStreamParams("cname", ssrcs);
   voice_media_channel()->AddSendStream(stream_params);
-  audio_rtp_sender_->SetMediaChannel(voice_media_channel());
+  audio_rtp_sender_->SetMediaChannel(
+      voice_media_channel()->AsVoiceSendChannel());
   audio_rtp_sender_->SetSsrc(1);
 
   params = audio_rtp_sender_->GetParameters();
@@ -1189,7 +1198,8 @@
   cricket::StreamParams stream_params =
       cricket::CreateSimStreamParams("cname", ssrcs);
   video_media_channel()->AddSendStream(stream_params);
-  video_rtp_sender_->SetMediaChannel(video_media_channel());
+  video_rtp_sender_->SetMediaChannel(
+      video_media_channel()->AsVideoSendChannel());
   video_rtp_sender_->SetSsrc(kVideoSsrcSimulcast);
 
   params = video_rtp_sender_->GetParameters();
@@ -1229,7 +1239,8 @@
   cricket::StreamParams stream_params =
       cricket::CreateSimStreamParams("cname", ssrcs);
   video_media_channel()->AddSendStream(stream_params);
-  video_rtp_sender_->SetMediaChannel(video_media_channel());
+  video_rtp_sender_->SetMediaChannel(
+      video_media_channel()->AsVideoSendChannel());
   video_rtp_sender_->SetSsrc(kVideoSsrcSimulcast);
 
   params = video_rtp_sender_->GetParameters();
@@ -1272,7 +1283,8 @@
   cricket::StreamParams stream_params =
       cricket::StreamParams::CreateLegacy(kVideoSsrc);
   video_media_channel()->AddSendStream(stream_params);
-  video_rtp_sender_->SetMediaChannel(video_media_channel());
+  video_rtp_sender_->SetMediaChannel(
+      video_media_channel()->AsVideoSendChannel());
   EXPECT_DEATH(video_rtp_sender_->SetSsrc(kVideoSsrcSimulcast), "");
 }
 #endif
@@ -1687,7 +1699,8 @@
   ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_.get()));
   EXPECT_CALL(*set_streams_observer, OnSetStreams());
   video_rtp_sender_->SetStreams({local_stream_->id()});
-  video_rtp_sender_->SetMediaChannel(video_media_channel());
+  video_rtp_sender_->SetMediaChannel(
+      video_media_channel()->AsVideoSendChannel());
   video_track_->set_enabled(true);
 
   // Sender is not ready to send (no SSRC) so no option should have been set.
diff --git a/pc/rtp_transmission_manager.cc b/pc/rtp_transmission_manager.cc
index 4fcb991..96b748b 100644
--- a/pc/rtp_transmission_manager.cc
+++ b/pc/rtp_transmission_manager.cc
@@ -72,8 +72,8 @@
   return observer_;
 }
 
-cricket::VoiceMediaChannel* RtpTransmissionManager::voice_media_channel()
-    const {
+cricket::VoiceMediaSendChannelInterface*
+RtpTransmissionManager::voice_media_send_channel() const {
   RTC_DCHECK_RUN_ON(signaling_thread());
   RTC_DCHECK(!IsUnifiedPlan());
   auto* voice_channel = GetAudioTransceiver()->internal()->channel();
@@ -84,8 +84,8 @@
   }
 }
 
-cricket::VideoMediaChannel* RtpTransmissionManager::video_media_channel()
-    const {
+cricket::VideoMediaSendChannelInterface*
+RtpTransmissionManager::video_media_send_channel() const {
   RTC_DCHECK_RUN_ON(signaling_thread());
   RTC_DCHECK(!IsUnifiedPlan());
   auto* video_channel = GetVideoTransceiver()->internal()->channel();
@@ -95,6 +95,29 @@
     return nullptr;
   }
 }
+cricket::VoiceMediaReceiveChannelInterface*
+RtpTransmissionManager::voice_media_receive_channel() const {
+  RTC_DCHECK_RUN_ON(signaling_thread());
+  RTC_DCHECK(!IsUnifiedPlan());
+  auto* voice_channel = GetAudioTransceiver()->internal()->channel();
+  if (voice_channel) {
+    return voice_channel->voice_media_receive_channel();
+  } else {
+    return nullptr;
+  }
+}
+
+cricket::VideoMediaReceiveChannelInterface*
+RtpTransmissionManager::video_media_receive_channel() const {
+  RTC_DCHECK_RUN_ON(signaling_thread());
+  RTC_DCHECK(!IsUnifiedPlan());
+  auto* video_channel = GetVideoTransceiver()->internal()->channel();
+  if (video_channel) {
+    return video_channel->video_media_receive_channel();
+  } else {
+    return nullptr;
+  }
+}
 
 RTCErrorOr<rtc::scoped_refptr<RtpSenderInterface>>
 RtpTransmissionManager::AddTrack(
@@ -132,7 +155,7 @@
                    init_send_encodings ? *init_send_encodings
                                        : std::vector<RtpEncodingParameters>());
   if (track->kind() == MediaStreamTrackInterface::kAudioKind) {
-    new_sender->internal()->SetMediaChannel(voice_media_channel());
+    new_sender->internal()->SetMediaChannel(voice_media_send_channel());
     GetAudioTransceiver()->internal()->AddSender(new_sender);
     const RtpSenderInfo* sender_info =
         FindSenderInfo(local_audio_sender_infos_,
@@ -142,7 +165,7 @@
     }
   } else {
     RTC_DCHECK_EQ(MediaStreamTrackInterface::kVideoKind, track->kind());
-    new_sender->internal()->SetMediaChannel(video_media_channel());
+    new_sender->internal()->SetMediaChannel(video_media_send_channel());
     GetVideoTransceiver()->internal()->AddSender(new_sender);
     const RtpSenderInfo* sender_info =
         FindSenderInfo(local_video_sender_infos_,
@@ -389,7 +412,7 @@
   auto new_sender = CreateSender(cricket::MEDIA_TYPE_AUDIO, track->id(),
                                  rtc::scoped_refptr<AudioTrackInterface>(track),
                                  {stream->id()}, {});
-  new_sender->internal()->SetMediaChannel(voice_media_channel());
+  new_sender->internal()->SetMediaChannel(voice_media_send_channel());
   GetAudioTransceiver()->internal()->AddSender(new_sender);
   // If the sender has already been configured in SDP, we call SetSsrc,
   // which will connect the sender to the underlying transport. This can
@@ -436,7 +459,7 @@
   auto new_sender = CreateSender(cricket::MEDIA_TYPE_VIDEO, track->id(),
                                  rtc::scoped_refptr<VideoTrackInterface>(track),
                                  {stream->id()}, {});
-  new_sender->internal()->SetMediaChannel(video_media_channel());
+  new_sender->internal()->SetMediaChannel(video_media_send_channel());
   GetVideoTransceiver()->internal()->AddSender(new_sender);
   const RtpSenderInfo* sender_info =
       FindSenderInfo(local_video_sender_infos_, stream->id(), track->id());
@@ -468,7 +491,7 @@
   // the constructor taking stream IDs instead.
   auto audio_receiver = rtc::make_ref_counted<AudioRtpReceiver>(
       worker_thread(), remote_sender_info.sender_id, streams, IsUnifiedPlan(),
-      voice_media_channel());
+      voice_media_receive_channel());
   if (remote_sender_info.sender_id == kDefaultAudioSenderId) {
     audio_receiver->SetupUnsignaledMediaChannel();
   } else {
@@ -497,7 +520,7 @@
       remote_sender_info.sender_id == kDefaultVideoSenderId
           ? absl::nullopt
           : absl::optional<uint32_t>(remote_sender_info.first_ssrc),
-      video_media_channel());
+      video_media_receive_channel());
 
   auto receiver = RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
       signaling_thread(), worker_thread(), std::move(video_receiver));
diff --git a/pc/rtp_transmission_manager.h b/pc/rtp_transmission_manager.h
index 06ce4fe..b41848c 100644
--- a/pc/rtp_transmission_manager.h
+++ b/pc/rtp_transmission_manager.h
@@ -204,8 +204,12 @@
 
   // Plan B helpers for getting the voice/video media channels for the single
   // audio/video transceiver, if it exists.
-  cricket::VoiceMediaChannel* voice_media_channel() const;
-  cricket::VideoMediaChannel* video_media_channel() const;
+  cricket::VoiceMediaSendChannelInterface* voice_media_send_channel() const;
+  cricket::VideoMediaSendChannelInterface* video_media_send_channel() const;
+  cricket::VoiceMediaReceiveChannelInterface* voice_media_receive_channel()
+      const;
+  cricket::VideoMediaReceiveChannelInterface* video_media_receive_channel()
+      const;
 
  private:
   rtc::Thread* signaling_thread() const { return context_->signaling_thread(); }
diff --git a/pc/test/mock_channel_interface.h b/pc/test/mock_channel_interface.h
index 6188a71..273e4a1 100644
--- a/pc/test/mock_channel_interface.h
+++ b/pc/test/mock_channel_interface.h
@@ -25,6 +25,7 @@
 class MockChannelInterface : public cricket::ChannelInterface {
  public:
   MOCK_METHOD(cricket::MediaType, media_type, (), (const, override));
+  MOCK_METHOD(MediaChannel*, media_channel, (), (const, override));
   MOCK_METHOD(MediaChannel*, media_send_channel, (), (const, override));
   MOCK_METHOD(VoiceMediaChannel*,
               voice_media_send_channel,
diff --git a/pc/test/mock_voice_media_channel.h b/pc/test/mock_voice_media_channel.h
index cd80ef8..5a9b880 100644
--- a/pc/test/mock_voice_media_channel.h
+++ b/pc/test/mock_voice_media_channel.h
@@ -16,6 +16,7 @@
 
 #include "api/call/audio_sink.h"
 #include "media/base/media_channel.h"
+#include "media/base/media_channel_impl.h"
 #include "rtc_base/gunit.h"
 #include "test/gmock.h"
 #include "test/gtest.h"
@@ -29,7 +30,10 @@
   explicit MockVoiceMediaChannel(webrtc::TaskQueueBase* network_thread)
       : VoiceMediaChannel(network_thread) {}
 
-  MOCK_METHOD(void, SetInterface, (NetworkInterface * iface), (override));
+  MOCK_METHOD(void,
+              SetInterface,
+              (MediaChannelNetworkInterface * iface),
+              (override));
   MOCK_METHOD(void,
               OnPacketReceived,
               (rtc::CopyOnWriteBuffer packet, int64_t packet_time_us),
@@ -64,7 +68,6 @@
       (uint32_t ssrc,
        rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor),
       (override));
-  MOCK_METHOD(void, SetVideoCodecSwitchingEnabled, (bool enabled), (override));
   MOCK_METHOD(webrtc::RtpParameters,
               GetRtpSendParameters,
               (uint32_t ssrc),
diff --git a/pc/video_rtp_receiver.cc b/pc/video_rtp_receiver.cc
index 1a4964b..18dfc82 100644
--- a/pc/video_rtp_receiver.cc
+++ b/pc/video_rtp_receiver.cc
@@ -276,7 +276,11 @@
     SetEncodedSinkEnabled(false);
   }
 
-  media_channel_ = static_cast<cricket::VideoMediaChannel*>(media_channel);
+  if (media_channel) {
+    media_channel_ = media_channel->AsVideoReceiveChannel();
+  } else {
+    media_channel_ = nullptr;
+  }
 
   if (media_channel_) {
     if (saved_generate_keyframe_) {
diff --git a/pc/video_rtp_receiver.h b/pc/video_rtp_receiver.h
index f572853..086246d 100644
--- a/pc/video_rtp_receiver.h
+++ b/pc/video_rtp_receiver.h
@@ -149,8 +149,8 @@
   rtc::Thread* const worker_thread_;
 
   const std::string id_;
-  cricket::VideoMediaChannel* media_channel_ RTC_GUARDED_BY(worker_thread_) =
-      nullptr;
+  cricket::VideoMediaReceiveChannelInterface* media_channel_
+      RTC_GUARDED_BY(worker_thread_) = nullptr;
   absl::optional<uint32_t> ssrc_ RTC_GUARDED_BY(worker_thread_);
   // `source_` is held here to be able to change the state of the source when
   // the VideoRtpReceiver is stopped.