Consolidate media content setting in BaseChannel

Move the implementations of SetLocalContent_w and SetRemoteContent_w
from the VoiceChannel and VideoChannel subclasses into the BaseChannel
class. This consolidation reduces code duplication and simplifies the
maintenance of SDP push-down logic across different media types.

To facilitate this change, the following modifications were made:
- Introduced std::variant to store media-specific sender and receiver
  parameters within BaseChannel.
- Updated the BaseChannel constructor to require a MediaType, allowing
  the unified methods to branch logic for audio or video as needed.
- Enhanced RTCError::InvalidParameter to support optional descriptive
  messages, providing clearer feedback when parameter settings fail.
- Cleaned up the ChannelInterface by removing AsVideoChannel and
  AsVoiceChannel helper methods, favoring the use of specialized media
  channel accessors.

Bug: webrtc:42224170
Change-Id: Ida66f61c12e992fed872a744bd412a3dc0c24f45
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/466020
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#47539}
diff --git a/pc/channel.cc b/pc/channel.cc
index e998e37..6edacf4 100644
--- a/pc/channel.cc
+++ b/pc/channel.cc
@@ -178,6 +178,7 @@
     std::unique_ptr<MediaSendChannelInterface> send_media_channel_impl,
     std::unique_ptr<MediaReceiveChannelInterface> receive_media_channel_impl,
     absl::string_view mid,
+    MediaType media_type,
     bool srtp_required,
     CryptoOptions crypto_options,
     UniqueRandomIdGenerator* ssrc_generator,
@@ -197,6 +198,13 @@
               ? RtpExtension::kPreferEncryptedExtension
               : RtpExtension::kDiscardEncryptedExtension),
       mid_(std::string(mid)),
+      last_recv_params_(media_type == MediaType::VIDEO
+                            ? ReceiverParamsVariant(VideoReceiverParameters())
+                            : ReceiverParamsVariant(AudioReceiverParameters())),
+      last_send_params_(media_type == MediaType::VIDEO
+                            ? SenderParamsVariant(VideoSenderParameters())
+                            : SenderParamsVariant(AudioSenderParameter())),
+      media_type_(media_type),
       ssrc_generator_(ssrc_generator) {
   RTC_DCHECK_RUN_ON(worker_thread_);
   RTC_DCHECK(media_send_channel_);
@@ -209,6 +217,8 @@
   TRACE_EVENT0("webrtc", "BaseChannel::~BaseChannel");
   RTC_DCHECK_RUN_ON(worker_thread_);
 
+  DisableMedia_w();
+
   // Eats any outstanding messages or packets.
   alive_->SetNotAlive();
   // The channel must be disconnected from the transport before destruction.
@@ -339,7 +349,6 @@
   return SetRemoteContent_w(content, type);
 }
 
-
 bool BaseChannel::IsReadyToSendMedia_w() const {
   // Send outgoing data if we are enabled, have local and remote content,
   // and we have had some form of connectivity.
@@ -403,8 +412,6 @@
   media_send_channel()->OnNetworkRouteChanged(transport_name(), new_route);
 }
 
-
-
 void BaseChannel::OnTransportReadyToSend(bool ready) {
   RTC_DCHECK_RUN_ON(network_thread());
   RTC_DCHECK(network_initialized());
@@ -634,6 +641,228 @@
                    << " for " << ToString();
 }
 
+RTCError BaseChannel::SetLocalContent_w(const MediaContentDescription* content,
+                                        SdpType type) {
+  TRACE_EVENT0("webrtc", "BaseChannel::SetLocalContent_w");
+  RTC_DLOG(LS_INFO) << "Setting local description for " << ToString();
+
+  RtpHeaderExtensions header_extensions =
+      GetDeduplicatedRtpHeaderExtensions(content->rtp_header_extensions());
+
+  std::optional<flat_set<uint8_t>> payload_types;
+  if (RtpTransceiverDirectionHasRecv(content->direction())) {
+    payload_types.emplace();
+    for (const Codec& codec : content->codecs()) {
+      payload_types->insert(codec.id);
+    }
+  }
+
+  RTCError error = MaybeUpdateDemuxerAndRtpExtensions_w(
+      /*update_demuxer=*/false, std::move(payload_types), header_extensions,
+      /*ssrcs=*/std::nullopt);
+  if (!error.ok()) {
+    return error;
+  }
+
+  RTC_LOG_THREAD_BLOCK_COUNT();
+
+  media_send_channel()->SetExtmapAllowMixed(content->extmap_allow_mixed());
+
+  if (media_type_ == MediaType::VIDEO) {
+    VideoReceiverParameters recv_params =
+        std::get<VideoReceiverParameters>(last_recv_params_);
+    MediaChannelParametersFromMediaDescription(
+        content, header_extensions,
+        RtpTransceiverDirectionHasRecv(content->direction()), &recv_params);
+    recv_params.mid = mid();
+
+    VideoSenderParameters send_params;
+    if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) {
+      send_params = std::get<VideoSenderParameters>(last_send_params_);
+      send_params.extensions = header_extensions;
+      send_params.extmap_allow_mixed = content->extmap_allow_mixed();
+
+      error = MaybeIgnorePacketization(recv_params, send_params);
+      if (!error.ok())
+        return error;
+    }
+
+    if (!video_media_receive_channel()->SetReceiverParameters(recv_params)) {
+      StringBuilder sb;
+      sb << "Failed to set local video description recv parameters for "
+            "m-section with mid='"
+         << mid() << "'.";
+      error = RTCError::InvalidParameter(sb.str());
+      return error;
+    }
+    last_recv_params_ = recv_params;
+
+    if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) {
+      if (!video_media_send_channel()->SetSenderParameters(send_params)) {
+        StringBuilder sb;
+        sb << "Failed to set send parameters for m-section with mid='" << mid()
+           << "'.";
+        error = RTCError::InvalidParameter(sb.str());
+        return error;
+      }
+      last_send_params_ = send_params;
+    }
+  } else {
+    AudioReceiverParameters recv_params =
+        std::get<AudioReceiverParameters>(last_recv_params_);
+    MediaChannelParametersFromMediaDescription(
+        content, header_extensions,
+        RtpTransceiverDirectionHasRecv(content->direction()), &recv_params);
+    recv_params.mid = mid();
+
+    AudioSenderParameter send_params;
+    if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) {
+      send_params = std::get<AudioSenderParameter>(last_send_params_);
+      send_params.extensions = header_extensions;
+      send_params.extmap_allow_mixed = content->extmap_allow_mixed();
+    }
+
+    if (!voice_media_receive_channel()->SetReceiverParameters(recv_params)) {
+      StringBuilder sb;
+      sb << "Failed to set local audio description recv parameters for "
+            "m-section with mid='"
+         << mid() << "'.";
+      error = RTCError::InvalidParameter(sb.str());
+      return error;
+    }
+    last_recv_params_ = recv_params;
+
+    if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) {
+      if (!voice_media_send_channel()->SetSenderParameters(send_params)) {
+        StringBuilder sb;
+        sb << "Failed to set send parameters for m-section with mid='" << mid()
+           << "'.";
+        error = RTCError::InvalidParameter(sb.str());
+        return error;
+      }
+      last_send_params_ = send_params;
+    }
+  }
+
+  error = UpdateLocalStreams_w(content->streams(), type);
+  if (!error.ok()) {
+    return error;
+  }
+
+  set_local_content_direction(content->direction());
+  UpdateMediaSendRecvState_w();
+
+  RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0);
+
+  return RTCError::OK();
+}
+
+RTCError BaseChannel::SetRemoteContent_w(const MediaContentDescription* content,
+                                         SdpType type) {
+  TRACE_EVENT0("webrtc", "BaseChannel::SetRemoteContent_w");
+  RTC_LOG(LS_INFO) << "Setting remote description for " << ToString();
+
+  RtpHeaderExtensions header_extensions =
+      GetDeduplicatedRtpHeaderExtensions(content->rtp_header_extensions());
+
+  RTCError error = MaybeUpdateDemuxerAndRtpExtensions_w(
+      /*update_demuxer=*/false, /*payload_types=*/std::nullopt,
+      header_extensions, /*ssrcs=*/std::nullopt);
+  if (!error.ok()) {
+    return error;
+  }
+
+  RTC_LOG_THREAD_BLOCK_COUNT();
+
+  if (media_type_ == MediaType::VIDEO) {
+    VideoSenderParameters send_params =
+        std::get<VideoSenderParameters>(last_send_params_);
+    RtpSendParametersFromMediaDescription(content, extensions_filter(),
+                                          &send_params);
+    send_params.mid = mid();
+    send_params.conference_mode = content->conference_mode();
+
+    VideoReceiverParameters recv_params;
+    if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) {
+      recv_params = std::get<VideoReceiverParameters>(last_recv_params_);
+      recv_params.extensions = send_params.extensions;
+      recv_params.rtcp.reduced_size = send_params.rtcp.reduced_size;
+
+      error = MaybeIgnorePacketization(send_params, recv_params);
+      if (!error.ok())
+        return error;
+    }
+
+    if (!video_media_send_channel()->SetSenderParameters(send_params)) {
+      StringBuilder sb;
+      sb << "Failed to set remote video description send parameters for "
+            "m-section with mid='"
+         << mid() << "'.";
+      return RTCError::InvalidParameter(sb.str());
+    }
+    last_send_params_ = send_params;
+
+    if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) {
+      if (!video_media_receive_channel()->SetReceiverParameters(recv_params)) {
+        StringBuilder sb;
+        sb << "Failed to set recv parameters for m-section with mid='" << mid()
+           << "'.";
+        return RTCError::InvalidParameter(sb.str());
+      }
+      last_recv_params_ = recv_params;
+    }
+  } else {
+    AudioSenderParameter send_params =
+        std::get<AudioSenderParameter>(last_send_params_);
+    RtpSendParametersFromMediaDescription(content, extensions_filter(),
+                                          &send_params);
+    send_params.mid = mid();
+
+    AudioReceiverParameters recv_params;
+    if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) {
+      recv_params = std::get<AudioReceiverParameters>(last_recv_params_);
+      recv_params.extensions = send_params.extensions;
+    }
+
+    if (!voice_media_send_channel()->SetSenderParameters(send_params)) {
+      StringBuilder sb;
+      sb << "Failed to set remote audio description send parameters for "
+            "m-section with mid='"
+         << mid() << "'.";
+      return RTCError::InvalidParameter(sb.str());
+    }
+    last_send_params_ = send_params;
+
+    if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) {
+      if (!voice_media_receive_channel()->SetReceiverParameters(recv_params)) {
+        StringBuilder sb;
+        sb << "Failed to set recv parameters for m-section with mid='" << mid()
+           << "'.";
+        return RTCError::InvalidParameter(sb.str());
+      }
+      last_recv_params_ = recv_params;
+    }
+  }
+
+  if (media_type_ == MediaType::AUDIO) {
+    voice_media_receive_channel()->SetRtcpMode(content->rtcp_reduced_size()
+                                                   ? RtcpMode::kReducedSize
+                                                   : RtcpMode::kCompound);
+    voice_media_receive_channel()->SetReceiveNackEnabled(
+        voice_media_send_channel()->SenderNackEnabled());
+    voice_media_receive_channel()->SetReceiveNonSenderRttEnabled(
+        voice_media_send_channel()->SenderNonSenderRttEnabled());
+  }
+
+  RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0);
+
+  error = UpdateRemoteStreams_w(content, type);
+
+  RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1);
+
+  return error;
+}
+
 void BaseChannel::UpdateWritableState_n() {
   TRACE_EVENT0("webrtc", "BaseChannel::UpdateWritableState_n");
   if (rtp_transport_->IsWritable(/*rtcp=*/true) &&
@@ -861,148 +1090,12 @@
                   std::move(media_send_channel),
                   std::move(media_receive_channel),
                   mid,
+                  MediaType::AUDIO,
                   srtp_required,
                   crypto_options,
                   ssrc_generator,
                   std::move(callbacks)) {}
 
-VoiceChannel::~VoiceChannel() {
-  TRACE_EVENT0("webrtc", "VoiceChannel::~VoiceChannel");
-  // this can't be done in the base class, since it calls a virtual
-  DisableMedia_w();
-}
-
-
-
-RTCError VoiceChannel::SetLocalContent_w(const MediaContentDescription* content,
-                                         SdpType type) {
-  TRACE_EVENT0("webrtc", "VoiceChannel::SetLocalContent_w");
-  RTC_DLOG(LS_INFO) << "Setting local voice description for " << ToString();
-
-  RtpHeaderExtensions header_extensions =
-      GetDeduplicatedRtpHeaderExtensions(content->rtp_header_extensions());
-
-  std::optional<flat_set<uint8_t>> payload_types;
-  if (RtpTransceiverDirectionHasRecv(content->direction())) {
-    payload_types.emplace();
-    for (const Codec& codec : content->codecs()) {
-      payload_types->insert(codec.id);
-    }
-  }
-
-  RTCError error = MaybeUpdateDemuxerAndRtpExtensions_w(
-      /*update_demuxer=*/false, std::move(payload_types), header_extensions,
-      /*ssrcs=*/std::nullopt);
-  if (!error.ok()) {
-    return error;
-  }
-
-  RTC_LOG_THREAD_BLOCK_COUNT();
-
-  // TODO: issues.webrtc.org/383078466 - remove if pushdown on answer is enough.
-  media_send_channel()->SetExtmapAllowMixed(content->extmap_allow_mixed());
-
-  AudioReceiverParameters recv_params = last_recv_params_;
-  MediaChannelParametersFromMediaDescription(
-      content, header_extensions,
-      RtpTransceiverDirectionHasRecv(content->direction()), &recv_params);
-  recv_params.mid = mid();
-
-  if (!media_receive_channel()->SetReceiverParameters(recv_params)) {
-    return RTCError::InvalidParameter()
-           << "Failed to set local audio description recv parameters for "
-              "m-section with mid='"
-           << mid() << "'.";
-  }
-
-  last_recv_params_ = recv_params;
-
-  if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) {
-    AudioSenderParameter send_params = last_send_params_;
-    RTC_DCHECK(!send_params.mid.empty());
-    send_params.extensions = header_extensions;
-    send_params.extmap_allow_mixed = content->extmap_allow_mixed();
-    if (!media_send_channel()->SetSenderParameters(send_params)) {
-      return RTCError::InvalidParameter()
-             << "Failed to set send parameters for m-section with mid='"
-             << mid() << "'.";
-    }
-    last_send_params_ = send_params;
-  }
-
-  error = UpdateLocalStreams_w(content->streams(), type);
-  if (!error.ok()) {
-    return error;
-  }
-
-  set_local_content_direction(content->direction());
-  UpdateMediaSendRecvState_w();
-
-  RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0);
-
-  return RTCError::OK();
-}
-
-RTCError VoiceChannel::SetRemoteContent_w(
-    const MediaContentDescription* content,
-    SdpType type) {
-  TRACE_EVENT0("webrtc", "VoiceChannel::SetRemoteContent_w");
-  RTC_LOG(LS_INFO) << "Setting remote voice description for " << ToString();
-
-  AudioSenderParameter send_params = last_send_params_;
-  RtpSendParametersFromMediaDescription(content, extensions_filter(),
-                                        &send_params);
-  send_params.mid = mid();
-
-  RTCError error = MaybeUpdateDemuxerAndRtpExtensions_w(
-      /*update_demuxer=*/false, /*payload_types=*/std::nullopt,
-      send_params.extensions, /*ssrcs=*/std::nullopt);
-  if (!error.ok()) {
-    return error;
-  }
-
-  RTC_LOG_THREAD_BLOCK_COUNT();
-
-  if (!media_send_channel()->SetSenderParameters(send_params)) {
-    return RTCError::InvalidParameter()
-           << "Failed to set remote audio description send parameters for "
-              "m-section with mid='"
-           << mid() << "'.";
-  }
-
-  if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) {
-    AudioReceiverParameters recv_params = last_recv_params_;
-    recv_params.extensions = send_params.extensions;
-    if (!media_receive_channel()->SetReceiverParameters(recv_params)) {
-      return RTCError::InvalidParameter()
-             << "Failed to set recv parameters for m-section with mid='"
-             << mid() << "'.";
-    }
-    last_recv_params_ = recv_params;
-  }
-  // The receive channel can send RTCP packets in the reverse direction. It
-  // should use the reduced size mode if a peer has requested it through the
-  // remote content.
-  media_receive_channel()->SetRtcpMode(content->rtcp_reduced_size()
-                                           ? RtcpMode::kReducedSize
-                                           : RtcpMode::kCompound);
-  // Update Receive channel based on Send channel's codec information.
-  // TODO(bugs.webrtc.org/14911): This is silly. Stop doing it.
-  media_receive_channel()->SetReceiveNackEnabled(
-      media_send_channel()->SenderNackEnabled());
-  media_receive_channel()->SetReceiveNonSenderRttEnabled(
-      media_send_channel()->SenderNonSenderRttEnabled());
-  last_send_params_ = send_params;
-
-  RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0);
-
-  error = UpdateRemoteStreams_w(content, type);
-
-  RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1);
-
-  return error;
-}
-
 VideoChannel::VideoChannel(
     TaskQueueBase* worker_thread,
     Thread* network_thread,
@@ -1020,158 +1113,10 @@
                   std::move(media_send_channel),
                   std::move(media_receive_channel),
                   mid,
+                  MediaType::VIDEO,
                   srtp_required,
                   crypto_options,
                   ssrc_generator,
                   std::move(callbacks)) {}
 
-VideoChannel::~VideoChannel() {
-  TRACE_EVENT0("webrtc", "VideoChannel::~VideoChannel");
-  // this can't be done in the base class, since it calls a virtual
-  DisableMedia_w();
-}
-
-
-
-RTCError VideoChannel::SetLocalContent_w(const MediaContentDescription* content,
-                                         SdpType type) {
-  TRACE_EVENT0("webrtc", "VideoChannel::SetLocalContent_w");
-
-  RtpHeaderExtensions header_extensions =
-      GetDeduplicatedRtpHeaderExtensions(content->rtp_header_extensions());
-
-  std::optional<flat_set<uint8_t>> payload_types;
-  if (RtpTransceiverDirectionHasRecv(content->direction())) {
-    payload_types.emplace();
-    for (const Codec& codec : content->codecs()) {
-      payload_types->insert(codec.id);
-    }
-  }
-
-  RTCError error = MaybeUpdateDemuxerAndRtpExtensions_w(
-      /*update_demuxer=*/false, std::move(payload_types), header_extensions,
-      /*ssrcs=*/std::nullopt);
-  if (!error.ok()) {
-    return error;
-  }
-
-  RTC_LOG_THREAD_BLOCK_COUNT();
-
-  // TODO: issues.webrtc.org/383078466 - remove if pushdown on answer is enough.
-  media_send_channel()->SetExtmapAllowMixed(content->extmap_allow_mixed());
-
-  VideoReceiverParameters recv_params = last_recv_params_;
-  MediaChannelParametersFromMediaDescription(
-      content, header_extensions,
-      RtpTransceiverDirectionHasRecv(content->direction()), &recv_params);
-
-  VideoSenderParameters send_params = last_send_params_;
-  send_params.extensions = header_extensions;
-  send_params.extmap_allow_mixed = content->extmap_allow_mixed();
-
-  // Ensure that there is a matching packetization for each send codec. If the
-  // other peer offered to exclusively send non-standard packetization but we
-  // only accept to receive standard packetization we effectively amend their
-  // offer by ignoring the packetiztion and fall back to standard packetization
-  // instead.
-  if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) {
-    error = MaybeIgnorePacketization(recv_params, send_params);
-    if (!error.ok()) {
-      return error;
-    }
-  }
-
-  if (!media_receive_channel()->SetReceiverParameters(recv_params)) {
-    return RTCError::InvalidParameter()
-           << "Failed to set local video description recv parameters for "
-              "m-section with mid='"
-           << mid() << "'.";
-  }
-
-  last_recv_params_ = recv_params;
-
-  if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) {
-    if (!media_send_channel()->SetSenderParameters(send_params)) {
-      return RTCError::InvalidParameter()
-             << "Failed to set send parameters for m-section with mid='"
-             << mid() << "'.";
-    }
-    last_send_params_ = send_params;
-  }
-
-  error = UpdateLocalStreams_w(content->streams(), type);
-  if (!error.ok()) {
-    return error;
-  }
-
-  set_local_content_direction(content->direction());
-  UpdateMediaSendRecvState_w();
-
-  RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0);
-
-  return RTCError::OK();
-}
-
-RTCError VideoChannel::SetRemoteContent_w(
-    const MediaContentDescription* content,
-    SdpType type) {
-  TRACE_EVENT0("webrtc", "VideoChannel::SetRemoteContent_w");
-  RTC_LOG(LS_INFO) << "Setting remote video description for " << ToString();
-
-  VideoSenderParameters send_params = last_send_params_;
-  RtpSendParametersFromMediaDescription(content, extensions_filter(),
-                                        &send_params);
-  send_params.mid = mid();
-  send_params.conference_mode = content->conference_mode();
-
-  RTCError error = MaybeUpdateDemuxerAndRtpExtensions_w(
-      /*update_demuxer=*/false, /*payload_types=*/std::nullopt,
-      send_params.extensions, /*ssrcs=*/std::nullopt);
-  if (!error.ok()) {
-    return error;
-  }
-
-  RTC_LOG_THREAD_BLOCK_COUNT();
-
-  VideoReceiverParameters recv_params = last_recv_params_;
-
-  // Ensure that there is a matching packetization for each receive codec. If we
-  // offered to exclusively receive a non-standard packetization but the other
-  // peer only accepts to send standard packetization we effectively amend our
-  // offer by ignoring the packetiztion and fall back to standard packetization
-  // instead.
-  if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) {
-    error = MaybeIgnorePacketization(send_params, recv_params);
-    if (!error.ok()) {
-      return error;
-    }
-  }
-
-  if (!media_send_channel()->SetSenderParameters(send_params)) {
-    return RTCError::InvalidParameter()
-           << "Failed to set remote video description send parameters for "
-              "m-section with mid='"
-           << mid() << "'.";
-  }
-  if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) {
-    recv_params.extensions = send_params.extensions;
-    recv_params.rtcp.reduced_size = send_params.rtcp.reduced_size;
-    if (!media_receive_channel()->SetReceiverParameters(recv_params)) {
-      return RTCError::InvalidParameter()
-             << "Failed to set recv parameters for m-section with mid='"
-             << mid() << "'.";
-    }
-    last_recv_params_ = recv_params;
-  }
-
-  RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0);
-
-  last_send_params_ = send_params;
-  error = UpdateRemoteStreams_w(content, type);
-
-  RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1);
-
-  return error;
-}
-
 }  // namespace webrtc
diff --git a/pc/channel.h b/pc/channel.h
index 00cd943..27d34d0 100644
--- a/pc/channel.h
+++ b/pc/channel.h
@@ -17,6 +17,7 @@
 #include <optional>
 #include <string>
 #include <utility>
+#include <variant>
 #include <vector>
 
 #include "absl/functional/any_invocable.h"
@@ -69,8 +70,6 @@
 class VoiceChannel;
 
 class BaseChannel : public ChannelInterface,
-                    // TODO(tommi): Consider implementing these interfaces
-                    // via composition.
                     public MediaChannelNetworkInterface,
                     public RtpPacketSinkInterface {
  public:
@@ -89,6 +88,7 @@
       std::unique_ptr<MediaSendChannelInterface> media_send_channel,
       std::unique_ptr<MediaReceiveChannelInterface> media_receive_channel,
       absl::string_view mid,
+      MediaType media_type,
       bool srtp_required,
       CryptoOptions crypto_options,
       UniqueRandomIdGenerator* ssrc_generator,
@@ -98,6 +98,7 @@
   TaskQueueBase* worker_thread() const { return worker_thread_; }
   Thread* network_thread() const { return network_thread_; }
   const std::string& mid() const override { return mid_; }
+  MediaType media_type() const override { return media_type_; }
   // TODO(deadbeef): This is redundant; remove this.
   absl::string_view transport_name() const override {
     RTC_DCHECK_RUN_ON(network_thread());
@@ -147,21 +148,28 @@
   // RtpPacketSinkInterface overrides.
   void OnRtpPacket(const RtpPacketReceived& packet) override;
 
+  MediaSendChannelInterface* media_send_channel() override {
+    return media_send_channel_.get();
+  }
+  MediaReceiveChannelInterface* media_receive_channel() override {
+    return media_receive_channel_.get();
+  }
+
   VideoMediaSendChannelInterface* video_media_send_channel() override {
-    RTC_CHECK(false) << "Attempt to fetch video channel from non-video";
-    return nullptr;
+    RTC_CHECK_EQ(media_type_, MediaType::VIDEO);
+    return media_send_channel_->AsVideoSendChannel();
   }
   VoiceMediaSendChannelInterface* voice_media_send_channel() override {
-    RTC_CHECK(false) << "Attempt to fetch voice channel from non-voice";
-    return nullptr;
+    RTC_CHECK_EQ(media_type_, MediaType::AUDIO);
+    return media_send_channel_->AsVoiceSendChannel();
   }
   VideoMediaReceiveChannelInterface* video_media_receive_channel() override {
-    RTC_CHECK(false) << "Attempt to fetch video channel from non-video";
-    return nullptr;
+    RTC_CHECK_EQ(media_type_, MediaType::VIDEO);
+    return media_receive_channel_->AsVideoReceiveChannel();
   }
   VoiceMediaReceiveChannelInterface* voice_media_receive_channel() override {
-    RTC_CHECK(false) << "Attempt to fetch voice channel from non-voice";
-    return nullptr;
+    RTC_CHECK_EQ(media_type_, MediaType::AUDIO);
+    return media_receive_channel_->AsVoiceReceiveChannel();
   }
 
  protected:
@@ -239,12 +247,10 @@
                                 SdpType type) RTC_RUN_ON(worker_thread());
   RTCError UpdateRemoteStreams_w(const MediaContentDescription* content,
                                  SdpType type) RTC_RUN_ON(worker_thread());
-  virtual RTCError SetLocalContent_w(const MediaContentDescription* content,
-                                     SdpType type)
-      RTC_RUN_ON(worker_thread()) = 0;
-  virtual RTCError SetRemoteContent_w(const MediaContentDescription* content,
-                                      SdpType type)
-      RTC_RUN_ON(worker_thread()) = 0;
+  RTCError SetLocalContent_w(const MediaContentDescription* content,
+                             SdpType type) RTC_RUN_ON(worker_thread());
+  RTCError SetRemoteContent_w(const MediaContentDescription* content,
+                              SdpType type) RTC_RUN_ON(worker_thread());
 
   // Returns a list of RTP header extensions where any extension URI is unique.
   // Encrypted extensions will be either preferred or discarded, depending on
@@ -330,6 +336,15 @@
 
   const std::string mid_;
   flat_set<uint32_t> ssrcs_ RTC_GUARDED_BY(network_thread());
+
+  using ReceiverParamsVariant =
+      std::variant<AudioReceiverParameters, VideoReceiverParameters>;
+  using SenderParamsVariant =
+      std::variant<AudioSenderParameter, VideoSenderParameters>;
+
+  ReceiverParamsVariant last_recv_params_;
+  SenderParamsVariant last_send_params_;
+  const MediaType media_type_;
   // This generator is used to generate SSRCs for local streams.
   // This is needed in cases where SSRCs are not negotiated or set explicitly
   // like in Simulcast.
@@ -353,55 +368,7 @@
       UniqueRandomIdGenerator* ssrc_generator,
       ChannelCallbacks callbacks = {});
 
-  ~VoiceChannel() override;
-
-  VideoChannel* AsVideoChannel() override {
-    RTC_CHECK_NOTREACHED();
-    return nullptr;
-  }
-  VoiceChannel* AsVoiceChannel() override { return this; }
-
-  VoiceMediaSendChannelInterface* send_channel() {
-    return media_send_channel_->AsVoiceSendChannel();
-  }
-
-  VoiceMediaReceiveChannelInterface* receive_channel() {
-    return media_receive_channel_->AsVoiceReceiveChannel();
-  }
-
-  VoiceMediaSendChannelInterface* media_send_channel() override {
-    return send_channel();
-  }
-
-  VoiceMediaSendChannelInterface* voice_media_send_channel() override {
-    return send_channel();
-  }
-
-  VoiceMediaReceiveChannelInterface* media_receive_channel() override {
-    return receive_channel();
-  }
-
-  VoiceMediaReceiveChannelInterface* voice_media_receive_channel() override {
-    return receive_channel();
-  }
-
-  MediaType media_type() const override { return MediaType::AUDIO; }
-
- private:
-  // overrides from BaseChannel
-
-  RTCError SetLocalContent_w(const MediaContentDescription* content,
-                             SdpType type) RTC_RUN_ON(worker_thread()) override;
-  RTCError SetRemoteContent_w(const MediaContentDescription* content,
-                              SdpType type)
-      RTC_RUN_ON(worker_thread()) override;
-
-  // Last AudioSenderParameter sent down to the media_channel() via
-  // SetSenderParameters.
-  AudioSenderParameter last_send_params_ RTC_GUARDED_BY(worker_thread());
-  // Last AudioReceiverParameters sent down to the media_channel() via
-  // SetReceiverParameters.
-  AudioReceiverParameters last_recv_params_ RTC_GUARDED_BY(worker_thread());
+  ~VoiceChannel() override = default;
 };
 
 // VideoChannel is a specialization for video.
@@ -418,55 +385,7 @@
       CryptoOptions crypto_options,
       UniqueRandomIdGenerator* ssrc_generator,
       ChannelCallbacks callbacks = {});
-  ~VideoChannel() override;
-
-  VideoChannel* AsVideoChannel() override { return this; }
-  VoiceChannel* AsVoiceChannel() override {
-    RTC_CHECK_NOTREACHED();
-    return nullptr;
-  }
-
-  VideoMediaSendChannelInterface* send_channel() {
-    return media_send_channel_->AsVideoSendChannel();
-  }
-
-  VideoMediaReceiveChannelInterface* receive_channel() {
-    return media_receive_channel_->AsVideoReceiveChannel();
-  }
-
-  VideoMediaSendChannelInterface* media_send_channel() override {
-    return send_channel();
-  }
-
-  VideoMediaSendChannelInterface* video_media_send_channel() override {
-    return send_channel();
-  }
-
-  VideoMediaReceiveChannelInterface* media_receive_channel() override {
-    return receive_channel();
-  }
-
-  VideoMediaReceiveChannelInterface* video_media_receive_channel() override {
-    return receive_channel();
-  }
-
-  MediaType media_type() const override { return MediaType::VIDEO; }
-
- private:
-  // overrides from BaseChannel
-
-  RTCError SetLocalContent_w(const MediaContentDescription* content,
-                             SdpType type) RTC_RUN_ON(worker_thread()) override;
-  RTCError SetRemoteContent_w(const MediaContentDescription* content,
-                              SdpType type)
-      RTC_RUN_ON(worker_thread()) override;
-
-  // Last VideoSenderParameters sent down to the media_channel() via
-  // SetSenderParameters.
-  VideoSenderParameters last_send_params_ RTC_GUARDED_BY(worker_thread());
-  // Last VideoReceiverParameters sent down to the media_channel() via
-  // SetReceiverParameters.
-  VideoReceiverParameters last_recv_params_ RTC_GUARDED_BY(worker_thread());
+  ~VideoChannel() override = default;
 };
 
 }  //  namespace webrtc
diff --git a/pc/channel_interface.h b/pc/channel_interface.h
index 30c08c7..93114dd 100644
--- a/pc/channel_interface.h
+++ b/pc/channel_interface.h
@@ -28,11 +28,7 @@
 class Call;
 class RtpPacketReceived;
 class VideoBitrateAllocatorFactory;
-class VideoChannel;
-class VoiceChannel;
-}  // namespace webrtc
 
-namespace webrtc {
 
 // Callbacks for packet events in the channel.
 // These are injected at construction time.
@@ -59,9 +55,6 @@
   virtual ~ChannelInterface() = default;
   virtual MediaType media_type() const = 0;
 
-  virtual VideoChannel* AsVideoChannel() = 0;
-  virtual VoiceChannel* AsVoiceChannel() = 0;
-
   virtual MediaSendChannelInterface* media_send_channel() = 0;
   // Typecasts of media_channel(). Will cause an exception if the
   // channel is of the wrong type.
diff --git a/pc/channel_unittest.cc b/pc/channel_unittest.cc
index 62ea4d8..ceb916e 100644
--- a/pc/channel_unittest.cc
+++ b/pc/channel_unittest.cc
@@ -804,7 +804,7 @@
     EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kOffer).ok());
     content.set_extmap_allow_mixed_enum(answer_enum);
     EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kAnswer).ok());
-    EXPECT_EQ(answer, media_send_channel1()->ExtmapAllowMixed());
+    EXPECT_EQ(answer, channel1_->media_send_channel()->ExtmapAllowMixed());
   }
 
   // Test that SetLocalContent and SetRemoteContent properly deals
@@ -1522,8 +1522,9 @@
         channel1_->SetLocalContent(&local_media_content1_, SdpType::kOffer)
             .ok());
     EXPECT_EQ(media_send_channel1_impl()->max_bps(), -1);
-    VerifyMaxBitrate(media_send_channel1()->GetRtpSendParameters(kSsrc1),
-                     std::nullopt);
+    VerifyMaxBitrate(
+        channel1_->media_send_channel()->GetRtpSendParameters(kSsrc1),
+        std::nullopt);
   }
 
   // Test that when a channel gets new RtpTransport with a call to
@@ -1635,20 +1636,6 @@
     ProcessThreadQueue(webrtc::Thread::Current());
   }
 
-  // Accessors that return the standard VideoMedia{Send|Receive}ChannelInterface
-  typename T::MediaSendChannelInterface* media_send_channel1() {
-    return channel1_->media_send_channel();
-  }
-  typename T::MediaSendChannelInterface* media_send_channel2() {
-    return channel2_->media_send_channel();
-  }
-  typename T::MediaReceiveChannelInterface* media_receive_channel1() {
-    return channel1_->media_receive_channel();
-  }
-  typename T::MediaReceiveChannelInterface* media_receive_channel2() {
-    return channel2_->media_receive_channel();
-  }
-
   // Accessors that return the FakeMedia<type>SendChannel object.
   // Note that these depend on getting the object back that was
   // passed to the channel constructor.
diff --git a/pc/test/mock_channel_interface.h b/pc/test/mock_channel_interface.h
index ca569c8..bdcf548 100644
--- a/pc/test/mock_channel_interface.h
+++ b/pc/test/mock_channel_interface.h
@@ -34,8 +34,7 @@
 class MockChannelInterface : public ChannelInterface {
  public:
   MOCK_METHOD(MediaType, media_type, (), (const, override));
-  MOCK_METHOD(VideoChannel*, AsVideoChannel, (), (override));
-  MOCK_METHOD(VoiceChannel*, AsVoiceChannel, (), (override));
+
   MOCK_METHOD(MediaSendChannelInterface*, media_send_channel, (), (override));
   MOCK_METHOD(VoiceMediaSendChannelInterface*,
               voice_media_send_channel,