Use MediaTransportInterface, for audio streams.

Bug: webrtc:9719
Change-Id: I6d3db66b781173b207de51d84193fbd34a7f3239
Reviewed-on: https://webrtc-review.googlesource.com/c/104642
Commit-Queue: Niels Moller <nisse@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Anton Sukhanov <sukhanov@webrtc.org>
Reviewed-by: Peter Slatala <psla@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25385}
diff --git a/api/media_transport_interface.h b/api/media_transport_interface.h
index 2a623a3..fc0922c 100644
--- a/api/media_transport_interface.h
+++ b/api/media_transport_interface.h
@@ -42,7 +42,10 @@
     kSpeech,
 
     // DTX frame (equivalent to webrtc::kAudioFrameCN).
-    kDiscountinuousTransmission,
+    // DTX frame (equivalent to webrtc::kAudioFrameCN).
+    kDiscontinuousTransmission,
+    // TODO(nisse): Mis-spelled version, update users, then delete.
+    kDiscountinuousTransmission = kDiscontinuousTransmission,
   };
 
   MediaTransportEncodedAudioFrame(
diff --git a/audio/BUILD.gn b/audio/BUILD.gn
index 35e10e6..d569d27 100644
--- a/audio/BUILD.gn
+++ b/audio/BUILD.gn
@@ -128,24 +128,34 @@
       "mock_voe_channel_proxy.h",
       "remix_resample_unittest.cc",
       "test/audio_stats_test.cc",
+      "test/media_transport_test.cc",
       "time_interval_unittest.cc",
       "transport_feedback_packet_loss_tracker_unittest.cc",
     ]
     deps = [
       ":audio",
       ":audio_end_to_end_test",
+      "../api:loopback_media_transport",
       "../api:mock_audio_mixer",
       "../api:mock_frame_decryptor",
       "../api:mock_frame_encryptor",
       "../api/audio:audio_frame_api",
+      "../api/audio_codecs:audio_codecs_api",
+      "../api/audio_codecs/opus:audio_decoder_opus",
+      "../api/audio_codecs/opus:audio_encoder_opus",
       "../api/units:time_delta",
+      "../call:mock_bitrate_allocator",
       "../call:mock_call_interfaces",
       "../call:mock_rtp_interfaces",
       "../call:rtp_interfaces",
       "../call:rtp_receiver",
       "../common_audio",
       "../logging:mocks",
+      "../logging:rtc_event_log_api",
       "../modules/audio_device:mock_audio_device",
+
+      # For TestAudioDeviceModule
+      "../modules/audio_device:audio_device_impl",
       "../modules/audio_mixer:audio_mixer_impl",
       "../modules/audio_processing:audio_processing_statistics",
       "../modules/audio_processing:mocks",
@@ -153,6 +163,7 @@
       "../modules/pacing:pacing",
       "../modules/rtp_rtcp:mock_rtp_rtcp",
       "../modules/rtp_rtcp:rtp_rtcp_format",
+      "../modules/utility",
       "../rtc_base:checks",
       "../rtc_base:rtc_base_approved",
       "../rtc_base:rtc_base_tests_utils",
diff --git a/audio/audio_receive_stream.cc b/audio/audio_receive_stream.cc
index 082b6a0..4f2e29c 100644
--- a/audio/audio_receive_stream.cc
+++ b/audio/audio_receive_stream.cc
@@ -58,6 +58,7 @@
   ss << "{rtp: " << rtp.ToString();
   ss << ", rtcp_send_transport: "
      << (rtcp_send_transport ? "(Transport)" : "null");
+  ss << ", media_transport: " << (media_transport ? "(Transport)" : "null");
   if (!sync_group.empty()) {
     ss << ", sync_group: " << sync_group;
   }
@@ -78,8 +79,8 @@
   return absl::make_unique<voe::ChannelReceiveProxy>(
       absl::make_unique<voe::ChannelReceive>(
           module_process_thread, internal_audio_state->audio_device_module(),
-          config.rtcp_send_transport, event_log, config.rtp.remote_ssrc,
-          config.jitter_buffer_max_packets,
+          config.media_transport, config.rtcp_send_transport, event_log,
+          config.rtp.remote_ssrc, config.jitter_buffer_max_packets,
           config.jitter_buffer_fast_accelerate, config.decoder_factory,
           config.codec_pair_id, config.frame_decryptor, config.crypto_options));
 }
@@ -111,8 +112,6 @@
     std::unique_ptr<voe::ChannelReceiveProxy> channel_proxy)
     : audio_state_(audio_state), channel_proxy_(std::move(channel_proxy)) {
   RTC_LOG(LS_INFO) << "AudioReceiveStream: " << config.rtp.remote_ssrc;
-  RTC_DCHECK(receiver_controller);
-  RTC_DCHECK(packet_router);
   RTC_DCHECK(config.decoder_factory);
   RTC_DCHECK(config.rtcp_send_transport);
   RTC_DCHECK(audio_state_);
@@ -120,13 +119,16 @@
 
   module_process_thread_checker_.DetachFromThread();
 
-  // Configure bandwidth estimation.
-  channel_proxy_->RegisterReceiverCongestionControlObjects(packet_router);
+  if (!config.media_transport) {
+    RTC_DCHECK(receiver_controller);
+    RTC_DCHECK(packet_router);
+    // Configure bandwidth estimation.
+    channel_proxy_->RegisterReceiverCongestionControlObjects(packet_router);
 
-  // Register with transport.
-  rtp_stream_receiver_ = receiver_controller->CreateReceiver(
-      config.rtp.remote_ssrc, channel_proxy_.get());
-
+    // Register with transport.
+    rtp_stream_receiver_ = receiver_controller->CreateReceiver(
+        config.rtp.remote_ssrc, channel_proxy_.get());
+  }
   ConfigureStream(this, config, true);
 }
 
@@ -135,7 +137,9 @@
   RTC_LOG(LS_INFO) << "~AudioReceiveStream: " << config_.rtp.remote_ssrc;
   Stop();
   channel_proxy_->DisassociateSendChannel();
-  channel_proxy_->ResetReceiverCongestionControlObjects();
+  if (!config_.media_transport) {
+    channel_proxy_->ResetReceiverCongestionControlObjects();
+  }
 }
 
 void AudioReceiveStream::Reconfigure(
diff --git a/audio/audio_receive_stream_unittest.cc b/audio/audio_receive_stream_unittest.cc
index 8d9c166..a5c7e20 100644
--- a/audio/audio_receive_stream_unittest.cc
+++ b/audio/audio_receive_stream_unittest.cc
@@ -217,7 +217,7 @@
       "{rtp: {remote_ssrc: 1234, local_ssrc: 5678, transport_cc: off, nack: "
       "{rtp_history_ms: 0}, extensions: [{uri: "
       "urn:ietf:params:rtp-hdrext:ssrc-audio-level, id: 3}]}, "
-      "rtcp_send_transport: null}",
+      "rtcp_send_transport: null, media_transport: null}",
       config.ToString());
 }
 
diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc
index 3c9daff..cc7ec5d 100644
--- a/audio/audio_send_stream.cc
+++ b/audio/audio_send_stream.cc
@@ -58,14 +58,15 @@
 std::unique_ptr<voe::ChannelSendProxy> CreateChannelAndProxy(
     rtc::TaskQueue* worker_queue,
     ProcessThread* module_process_thread,
+    MediaTransportInterface* media_transport,
     RtcpRttStats* rtcp_rtt_stats,
     RtcEventLog* event_log,
     FrameEncryptorInterface* frame_encryptor,
     const webrtc::CryptoOptions& crypto_options) {
   return absl::make_unique<voe::ChannelSendProxy>(
-      absl::make_unique<voe::ChannelSend>(worker_queue, module_process_thread,
-                                          rtcp_rtt_stats, event_log,
-                                          frame_encryptor, crypto_options));
+      absl::make_unique<voe::ChannelSend>(
+          worker_queue, module_process_thread, media_transport, rtcp_rtt_stats,
+          event_log, frame_encryptor, crypto_options));
 }
 }  // namespace
 
@@ -97,7 +98,7 @@
     const rtc::scoped_refptr<webrtc::AudioState>& audio_state,
     rtc::TaskQueue* worker_queue,
     ProcessThread* module_process_thread,
-    RtpTransportControllerSendInterface* transport,
+    RtpTransportControllerSendInterface* rtp_transport,
     BitrateAllocatorInterface* bitrate_allocator,
     RtcEventLog* event_log,
     RtcpRttStats* rtcp_rtt_stats,
@@ -106,7 +107,7 @@
     : AudioSendStream(config,
                       audio_state,
                       worker_queue,
-                      transport,
+                      rtp_transport,
                       bitrate_allocator,
                       event_log,
                       rtcp_rtt_stats,
@@ -114,6 +115,7 @@
                       overall_call_lifetime,
                       CreateChannelAndProxy(worker_queue,
                                             module_process_thread,
+                                            config.media_transport,
                                             rtcp_rtt_stats,
                                             event_log,
                                             config.frame_encryptor,
@@ -123,7 +125,7 @@
     const webrtc::AudioSendStream::Config& config,
     const rtc::scoped_refptr<webrtc::AudioState>& audio_state,
     rtc::TaskQueue* worker_queue,
-    RtpTransportControllerSendInterface* transport,
+    RtpTransportControllerSendInterface* rtp_transport,
     BitrateAllocatorInterface* bitrate_allocator,
     RtcEventLog* event_log,
     RtcpRttStats* rtcp_rtt_stats,
@@ -131,12 +133,13 @@
     TimeInterval* overall_call_lifetime,
     std::unique_ptr<voe::ChannelSendProxy> channel_proxy)
     : worker_queue_(worker_queue),
-      config_(Config(nullptr)),
+      config_(Config(/*send_transport=*/nullptr,
+                     /*media_transport=*/nullptr)),
       audio_state_(audio_state),
       channel_proxy_(std::move(channel_proxy)),
       event_log_(event_log),
       bitrate_allocator_(bitrate_allocator),
-      transport_(transport),
+      rtp_transport_(rtp_transport),
       packet_loss_tracker_(kPacketLossTrackerMaxWindowSizeMs,
                            kPacketLossRateMinNumAckedPackets,
                            kRecoverablePacketLossRateMinNumAckedPairs),
@@ -148,7 +151,11 @@
   RTC_DCHECK(audio_state_);
   RTC_DCHECK(channel_proxy_);
   RTC_DCHECK(bitrate_allocator_);
-  RTC_DCHECK(transport);
+  // TODO(nisse): Eventually, we should have only media_transport. But for the
+  // time being, we can have either. When media transport is injected, there
+  // should be no rtp_transport, and below check should be strengthened to XOR
+  // (either rtp_transport or media_transport but not both).
+  RTC_DCHECK(rtp_transport || config.media_transport);
   RTC_DCHECK(overall_call_lifetime_);
 
   channel_proxy_->SetRTCPStatus(true);
@@ -158,17 +165,22 @@
   ConfigureStream(this, config, true);
 
   pacer_thread_checker_.DetachFromThread();
-  // Signal congestion controller this object is ready for OnPacket* callbacks.
-  transport_->RegisterPacketFeedbackObserver(this);
+  if (rtp_transport_) {
+    // Signal congestion controller this object is ready for OnPacket*
+    // callbacks.
+    rtp_transport_->RegisterPacketFeedbackObserver(this);
+  }
 }
 
 AudioSendStream::~AudioSendStream() {
   RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
   RTC_LOG(LS_INFO) << "~AudioSendStream: " << config_.rtp.ssrc;
   RTC_DCHECK(!sending_);
-  transport_->DeRegisterPacketFeedbackObserver(this);
-  channel_proxy_->RegisterTransport(nullptr);
-  channel_proxy_->ResetSenderCongestionControlObjects();
+  if (rtp_transport_) {
+    rtp_transport_->DeRegisterPacketFeedbackObserver(this);
+    channel_proxy_->RegisterTransport(nullptr);
+    channel_proxy_->ResetSenderCongestionControlObjects();
+  }
   // Lifetime can only be updated after deregistering
   // |timed_send_transport_adapter_| in the underlying channel object to avoid
   // data races in |active_lifetime_|.
@@ -272,14 +284,16 @@
       // Probing in application limited region is only used in combination with
       // send side congestion control, wich depends on feedback packets which
       // requires transport sequence numbers to be enabled.
-      stream->transport_->EnablePeriodicAlrProbing(true);
-      bandwidth_observer = stream->transport_->GetBandwidthObserver();
+      if (stream->rtp_transport_) {
+        stream->rtp_transport_->EnablePeriodicAlrProbing(true);
+        bandwidth_observer = stream->rtp_transport_->GetBandwidthObserver();
+      }
     }
-
-    channel_proxy->RegisterSenderCongestionControlObjects(stream->transport_,
-                                                          bandwidth_observer);
+    if (stream->rtp_transport_) {
+      channel_proxy->RegisterSenderCongestionControlObjects(
+          stream->rtp_transport_, bandwidth_observer);
+    }
   }
-
   // MID RTP header extension.
   if ((first_time || new_ids.mid != old_ids.mid ||
        new_config.rtp.mid != old_config.rtp.mid) &&
@@ -312,7 +326,7 @@
        !webrtc::field_trial::IsEnabled("WebRTC-Audio-SendSideBwe") ||
        webrtc::field_trial::IsEnabled("WebRTC-Audio-ABWENoTWCC"))) {
     // Audio BWE is enabled.
-    transport_->packet_sender()->SetAccountForAudioPackets(true);
+    rtp_transport_->packet_sender()->SetAccountForAudioPackets(true);
     rtp_rtcp_module_->SetAsPartOfAllocation(true);
     ConfigureBitrateObserver(config_.min_bitrate_bps, config_.max_bitrate_bps,
                              config_.bitrate_priority,
@@ -725,13 +739,13 @@
       !new_config.has_dscp &&
       (has_transport_sequence_number ||
        !webrtc::field_trial::IsEnabled("WebRTC-Audio-SendSideBwe"))) {
-    stream->transport_->packet_sender()->SetAccountForAudioPackets(true);
+    stream->rtp_transport_->packet_sender()->SetAccountForAudioPackets(true);
     stream->ConfigureBitrateObserver(
         new_config.min_bitrate_bps, new_config.max_bitrate_bps,
         new_config.bitrate_priority, has_transport_sequence_number);
     stream->rtp_rtcp_module_->SetAsPartOfAllocation(true);
   } else {
-    stream->transport_->packet_sender()->SetAccountForAudioPackets(false);
+    stream->rtp_transport_->packet_sender()->SetAccountForAudioPackets(false);
     stream->RemoveBitrateObserver();
     stream->rtp_rtcp_module_->SetAsPartOfAllocation(false);
   }
diff --git a/audio/audio_send_stream.h b/audio/audio_send_stream.h
index 3683c91..c86a9dc 100644
--- a/audio/audio_send_stream.h
+++ b/audio/audio_send_stream.h
@@ -45,7 +45,7 @@
                   const rtc::scoped_refptr<webrtc::AudioState>& audio_state,
                   rtc::TaskQueue* worker_queue,
                   ProcessThread* module_process_thread,
-                  RtpTransportControllerSendInterface* transport,
+                  RtpTransportControllerSendInterface* rtp_transport,
                   BitrateAllocatorInterface* bitrate_allocator,
                   RtcEventLog* event_log,
                   RtcpRttStats* rtcp_rtt_stats,
@@ -55,7 +55,7 @@
   AudioSendStream(const webrtc::AudioSendStream::Config& config,
                   const rtc::scoped_refptr<webrtc::AudioState>& audio_state,
                   rtc::TaskQueue* worker_queue,
-                  RtpTransportControllerSendInterface* transport,
+                  RtpTransportControllerSendInterface* rtp_transport,
                   BitrateAllocatorInterface* bitrate_allocator,
                   RtcEventLog* event_log,
                   RtcpRttStats* rtcp_rtt_stats,
@@ -138,7 +138,7 @@
   bool sending_ = false;
 
   BitrateAllocatorInterface* const bitrate_allocator_;
-  RtpTransportControllerSendInterface* const transport_;
+  RtpTransportControllerSendInterface* const rtp_transport_;
 
   rtc::CriticalSection packet_loss_tracker_cs_;
   TransportFeedbackPacketLossTracker packet_loss_tracker_
diff --git a/audio/audio_send_stream_unittest.cc b/audio/audio_send_stream_unittest.cc
index 31c10ec..e17030f 100644
--- a/audio/audio_send_stream_unittest.cc
+++ b/audio/audio_send_stream_unittest.cc
@@ -129,7 +129,7 @@
 
 struct ConfigHelper {
   ConfigHelper(bool audio_bwe_enabled, bool expect_set_encoder_call)
-      : stream_config_(nullptr),
+      : stream_config_(/*send_transport=*/nullptr, /*media_transport=*/nullptr),
         audio_processing_(new rtc::RefCountedObject<MockAudioProcessing>()),
         bitrate_allocator_(&limit_observer_),
         worker_queue_("ConfigHelper_worker_queue"),
@@ -318,7 +318,8 @@
 }  // namespace
 
 TEST(AudioSendStreamTest, ConfigToString) {
-  AudioSendStream::Config config(nullptr);
+  AudioSendStream::Config config(/*send_transport=*/nullptr,
+                                 /*media_transport=*/nullptr);
   config.rtp.ssrc = kSsrc;
   config.rtp.c_name = kCName;
   config.min_bitrate_bps = 12000;
@@ -335,6 +336,7 @@
       "{rtp: {ssrc: 1234, extensions: [{uri: "
       "urn:ietf:params:rtp-hdrext:ssrc-audio-level, id: 2}], nack: "
       "{rtp_history_ms: 0}, c_name: foo_name}, send_transport: null, "
+      "media_transport: null, "
       "min_bitrate_bps: 12000, max_bitrate_bps: 34000, "
       "send_codec_spec: {nack_enabled: true, transport_cc_enabled: false, "
       "cng_payload_type: 42, payload_type: 103, "
diff --git a/audio/channel_receive.cc b/audio/channel_receive.cc
index fbe1500..1626dd2 100644
--- a/audio/channel_receive.cc
+++ b/audio/channel_receive.cc
@@ -51,12 +51,47 @@
 constexpr int kVoiceEngineMinMinPlayoutDelayMs = 0;
 constexpr int kVoiceEngineMaxMinPlayoutDelayMs = 10000;
 
+webrtc::FrameType WebrtcFrameTypeForMediaTransportFrameType(
+    MediaTransportEncodedAudioFrame::FrameType frame_type) {
+  switch (frame_type) {
+    case MediaTransportEncodedAudioFrame::FrameType::kSpeech:
+      return kAudioFrameSpeech;
+      break;
+
+    case MediaTransportEncodedAudioFrame::FrameType::
+        kDiscountinuousTransmission:
+      return kAudioFrameCN;
+      break;
+  }
+}
+
+WebRtcRTPHeader CreateWebrtcRTPHeaderForMediaTransportFrame(
+    const MediaTransportEncodedAudioFrame& frame,
+    uint64_t channel_id) {
+  webrtc::WebRtcRTPHeader webrtc_header = {};
+  webrtc_header.header.payloadType = frame.payload_type();
+  webrtc_header.header.payload_type_frequency = frame.sampling_rate_hz();
+  webrtc_header.header.timestamp = frame.starting_sample_index();
+  webrtc_header.header.sequenceNumber = frame.sequence_number();
+
+  webrtc_header.frameType =
+      WebrtcFrameTypeForMediaTransportFrameType(frame.frame_type());
+
+  webrtc_header.header.ssrc = static_cast<uint32_t>(channel_id);
+
+  // The rest are initialized by the RTPHeader constructor.
+  return webrtc_header;
+}
+
 }  // namespace
 
 int32_t ChannelReceive::OnReceivedPayloadData(
     const uint8_t* payloadData,
     size_t payloadSize,
     const WebRtcRTPHeader* rtpHeader) {
+  // We should not be receiving any RTP packets if media_transport is set.
+  RTC_CHECK(!media_transport_);
+
   if (!channel_state_.Get().playing) {
     // Avoid inserting into NetEQ when we are not playing. Count the
     // packet as discarded.
@@ -83,6 +118,27 @@
   return 0;
 }
 
+// MediaTransportAudioSinkInterface override.
+void ChannelReceive::OnData(uint64_t channel_id,
+                            MediaTransportEncodedAudioFrame frame) {
+  RTC_CHECK(media_transport_);
+
+  if (!channel_state_.Get().playing) {
+    // Avoid inserting into NetEQ when we are not playing. Count the
+    // packet as discarded.
+    return;
+  }
+
+  // Send encoded audio frame to Decoder / NetEq.
+  if (audio_coding_->IncomingPacket(
+          frame.encoded_data().data(), frame.encoded_data().size(),
+          CreateWebrtcRTPHeaderForMediaTransportFrame(frame, channel_id)) !=
+      0) {
+    RTC_DLOG(LS_ERROR) << "ChannelReceive::OnData: unable to "
+                          "push data to the ACM";
+  }
+}
+
 AudioMixer::Source::AudioFrameInfo ChannelReceive::GetAudioFrameWithInfo(
     int sample_rate_hz,
     AudioFrame* audio_frame) {
@@ -200,6 +256,7 @@
 ChannelReceive::ChannelReceive(
     ProcessThread* module_process_thread,
     AudioDeviceModule* audio_device_module,
+    MediaTransportInterface* media_transport,
     Transport* rtcp_send_transport,
     RtcEventLog* rtc_event_log,
     uint32_t remote_ssrc,
@@ -224,6 +281,7 @@
       _audioDeviceModulePtr(audio_device_module),
       _outputGain(1.0f),
       associated_send_channel_(nullptr),
+      media_transport_(media_transport),
       frame_decryptor_(frame_decryptor),
       crypto_options_(crypto_options) {
   RTC_DCHECK(module_process_thread);
@@ -279,10 +337,19 @@
   // be transmitted since the Transport object will then be invalid.
   // RTCP is enabled by default.
   _rtpRtcpModule->SetRTCPStatus(RtcpMode::kCompound);
+
+  if (media_transport_) {
+    media_transport_->SetReceiveAudioSink(this);
+  }
 }
 
 void ChannelReceive::Terminate() {
   RTC_DCHECK(construction_thread_.CalledOnValidThread());
+
+  if (media_transport_) {
+    media_transport_->SetReceiveAudioSink(nullptr);
+  }
+
   // Must be called on the same thread as Init().
   rtp_receive_statistics_->RegisterRtcpStatisticsCallback(NULL);
 
diff --git a/audio/channel_receive.h b/audio/channel_receive.h
index 4862ab0..0c50962 100644
--- a/audio/channel_receive.h
+++ b/audio/channel_receive.h
@@ -20,6 +20,7 @@
 #include "api/call/audio_sink.h"
 #include "api/call/transport.h"
 #include "api/crypto/cryptooptions.h"
+#include "api/media_transport_interface.h"
 #include "api/rtpreceiverinterface.h"
 #include "audio/audio_level.h"
 #include "call/syncable.h"
@@ -103,11 +104,12 @@
   State state_;
 };
 
-class ChannelReceive : public RtpData {
+class ChannelReceive : public RtpData, public MediaTransportAudioSinkInterface {
  public:
   // Used for receive streams.
   ChannelReceive(ProcessThread* module_process_thread,
                  AudioDeviceModule* audio_device_module,
+                 MediaTransportInterface* media_transport,
                  Transport* rtcp_send_transport,
                  RtcEventLog* rtc_event_log,
                  uint32_t remote_ssrc,
@@ -165,6 +167,10 @@
   int GetRTPStatistics(CallReceiveStatistics& stats);  // NOLINT
   void SetNACKStatus(bool enable, int maxNumberOfPackets);
 
+  // MediaTransportAudioSinkInterface override;
+  void OnData(uint64_t channel_id,
+              MediaTransportEncodedAudioFrame frame) override;
+
   // From RtpData in the RTP/RTCP module
   int32_t OnReceivedPayloadData(const uint8_t* payloadData,
                                 size_t payloadSize,
@@ -259,6 +265,8 @@
 
   rtc::ThreadChecker construction_thread_;
 
+  MediaTransportInterface* const media_transport_;
+
   // E2EE Audio Frame Decryption
   rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor_;
   webrtc::CryptoOptions crypto_options_;
diff --git a/audio/channel_send.cc b/audio/channel_send.cc
index 7d01a4d..4490b19 100644
--- a/audio/channel_send.cc
+++ b/audio/channel_send.cc
@@ -48,6 +48,24 @@
 constexpr int64_t kMaxRetransmissionWindowMs = 1000;
 constexpr int64_t kMinRetransmissionWindowMs = 30;
 
+MediaTransportEncodedAudioFrame::FrameType
+MediaTransportFrameTypeForWebrtcFrameType(webrtc::FrameType frame_type) {
+  switch (frame_type) {
+    case kAudioFrameSpeech:
+      return MediaTransportEncodedAudioFrame::FrameType::kSpeech;
+      break;
+
+    case kAudioFrameCN:
+      return MediaTransportEncodedAudioFrame::FrameType::
+          kDiscontinuousTransmission;
+      break;
+
+    default:
+      RTC_CHECK(false) << "Unexpected frame type=" << frame_type;
+      break;
+  }
+}
+
 }  // namespace
 
 const int kTelephoneEventAttenuationdB = 10;
@@ -255,6 +273,23 @@
                               size_t payloadSize,
                               const RTPFragmentationHeader* fragmentation) {
   RTC_DCHECK_RUN_ON(encoder_queue_);
+  rtc::ArrayView<const uint8_t> payload(payloadData, payloadSize);
+
+  if (media_transport() != nullptr) {
+    return SendMediaTransportAudio(frameType, payloadType, timeStamp, payload,
+                                   fragmentation);
+  } else {
+    return SendRtpAudio(frameType, payloadType, timeStamp, payload,
+                        fragmentation);
+  }
+}
+
+int32_t ChannelSend::SendRtpAudio(FrameType frameType,
+                                  uint8_t payloadType,
+                                  uint32_t timeStamp,
+                                  rtc::ArrayView<const uint8_t> payload,
+                                  const RTPFragmentationHeader* fragmentation) {
+  RTC_DCHECK_RUN_ON(encoder_queue_);
   if (_includeAudioLevelIndication) {
     // Store current audio level in the RTP/RTCP module.
     // The level will be used in combination with voice-activity state
@@ -269,16 +304,15 @@
     // TODO(benwright@webrtc.org) - Allocate enough to always encrypt inline.
     // Allocate a buffer to hold the maximum possible encrypted payload.
     size_t max_ciphertext_size = frame_encryptor_->GetMaxCiphertextByteSize(
-        cricket::MEDIA_TYPE_AUDIO, payloadSize);
+        cricket::MEDIA_TYPE_AUDIO, payload.size());
     encrypted_audio_payload.SetSize(max_ciphertext_size);
 
     // Encrypt the audio payload into the buffer.
     size_t bytes_written = 0;
     int encrypt_status = frame_encryptor_->Encrypt(
         cricket::MEDIA_TYPE_AUDIO, _rtpRtcpModule->SSRC(),
-        /*additional_data=*/nullptr,
-        rtc::ArrayView<const uint8_t>(payloadData, payloadSize),
-        encrypted_audio_payload, &bytes_written);
+        /*additional_data=*/nullptr, payload, encrypted_audio_payload,
+        &bytes_written);
     if (encrypt_status != 0) {
       RTC_DLOG(LS_ERROR) << "Channel::SendData() failed encrypt audio payload: "
                          << encrypt_status;
@@ -287,8 +321,7 @@
     // Resize the buffer to the exact number of bytes actually used.
     encrypted_audio_payload.SetSize(bytes_written);
     // Rewrite the payloadData and size to the new encrypted payload.
-    payloadData = encrypted_audio_payload.data();
-    payloadSize = encrypted_audio_payload.size();
+    payload = encrypted_audio_payload;
   } else if (crypto_options_.sframe.require_frame_encryption) {
     RTC_DLOG(LS_ERROR) << "Channel::SendData() failed sending audio payload: "
                        << "A frame encryptor is required but one is not set.";
@@ -298,12 +331,13 @@
   // Push data from ACM to RTP/RTCP-module to deliver audio frame for
   // packetization.
   // This call will trigger Transport::SendPacket() from the RTP/RTCP module.
-  if (!_rtpRtcpModule->SendOutgoingData(
-          (FrameType&)frameType, payloadType, timeStamp,
-          // Leaving the time when this frame was
-          // received from the capture device as
-          // undefined for voice for now.
-          -1, payloadData, payloadSize, fragmentation, nullptr, nullptr)) {
+  if (!_rtpRtcpModule->SendOutgoingData((FrameType&)frameType, payloadType,
+                                        timeStamp,
+                                        // Leaving the time when this frame was
+                                        // received from the capture device as
+                                        // undefined for voice for now.
+                                        -1, payload.data(), payload.size(),
+                                        fragmentation, nullptr, nullptr)) {
     RTC_DLOG(LS_ERROR)
         << "ChannelSend::SendData() failed to send data to RTP/RTCP module";
     return -1;
@@ -312,9 +346,68 @@
   return 0;
 }
 
+int32_t ChannelSend::SendMediaTransportAudio(
+    FrameType frameType,
+    uint8_t payloadType,
+    uint32_t timeStamp,
+    rtc::ArrayView<const uint8_t> payload,
+    const RTPFragmentationHeader* fragmentation) {
+  RTC_DCHECK_RUN_ON(encoder_queue_);
+  // TODO(nisse): Use null _transportPtr for MediaTransport.
+  // RTC_DCHECK(_transportPtr == nullptr);
+  uint64_t channel_id;
+  int sampling_rate_hz;
+  {
+    rtc::CritScope cs(&media_transport_lock_);
+    if (media_transport_payload_type_ != payloadType) {
+      // Payload type is being changed, media_transport_sampling_frequency_,
+      // no longer current.
+      return -1;
+    }
+    sampling_rate_hz = media_transport_sampling_frequency_;
+    channel_id = media_transport_channel_id_;
+  }
+  const MediaTransportEncodedAudioFrame frame(
+      /*sampling_rate_hz=*/sampling_rate_hz,
+
+      // TODO(nisse): Timestamp and sample index are the same for all supported
+      // audio codecs except G722. Refactor audio coding module to only use
+      // sample index, and leave translation to RTP time, when needed, for
+      // RTP-specific code.
+      /*starting_sample_index=*/timeStamp,
+
+      // Sample count isn't conveniently available from the AudioCodingModule,
+      // and needs some refactoring to wire up in a good way. For now, left as
+      // zero.
+      /*sample_count=*/0,
+
+      /*sequence_number=*/media_transport_sequence_number_,
+      MediaTransportFrameTypeForWebrtcFrameType(frameType), payloadType,
+      std::vector<uint8_t>(payload.begin(), payload.end()));
+
+  // TODO(nisse): Introduce a MediaTransportSender object bound to a specific
+  // channel id.
+  RTCError rtc_error =
+      media_transport()->SendAudioFrame(channel_id, std::move(frame));
+
+  if (!rtc_error.ok()) {
+    RTC_LOG(LS_ERROR) << "Failed to send frame, rtc_error="
+                      << ToString(rtc_error.type()) << ", "
+                      << rtc_error.message();
+    return -1;
+  }
+
+  ++media_transport_sequence_number_;
+
+  return 0;
+}
+
 bool ChannelSend::SendRtp(const uint8_t* data,
                           size_t len,
                           const PacketOptions& options) {
+  // We should not be sending RTP packets if media transport is available.
+  RTC_CHECK(!media_transport());
+
   rtc::CritScope cs(&_callbackCritSect);
 
   if (_transportPtr == NULL) {
@@ -356,6 +449,7 @@
 
 ChannelSend::ChannelSend(rtc::TaskQueue* encoder_queue,
                          ProcessThread* module_process_thread,
+                         MediaTransportInterface* media_transport,
                          RtcpRttStats* rtcp_rtt_stats,
                          RtcEventLog* rtc_event_log,
                          FrameEncryptorInterface* frame_encryptor,
@@ -380,6 +474,7 @@
       use_twcc_plr_for_ana_(
           webrtc::field_trial::FindFullName("UseTwccPlrForAna") == "Enabled"),
       encoder_queue_(encoder_queue),
+      media_transport_(media_transport),
       frame_encryptor_(frame_encryptor),
       crypto_options_(crypto_options) {
   RTC_DCHECK(module_process_thread);
@@ -556,6 +651,13 @@
     }
   }
 
+  if (media_transport_) {
+    rtc::CritScope cs(&media_transport_lock_);
+    media_transport_payload_type_ = payload_type;
+    // TODO(nisse): Currently broken for G722, since timestamps passed through
+    // encoder use RTP clock rather than sample count, and they differ for G722.
+    media_transport_sampling_frequency_ = encoder->RtpTimestampRateHz();
+  }
   audio_coding_->SetEncoder(std::move(encoder));
   return true;
 }
@@ -720,6 +822,10 @@
     RTC_DLOG(LS_ERROR) << "SetLocalSSRC() already sending";
     return -1;
   }
+  if (media_transport_) {
+    rtc::CritScope cs(&media_transport_lock_);
+    media_transport_channel_id_ = ssrc;
+  }
   _rtpRtcpModule->SetSSRC(ssrc);
   return 0;
 }
diff --git a/audio/channel_send.h b/audio/channel_send.h
index 54da0f5..deca5b1 100644
--- a/audio/channel_send.h
+++ b/audio/channel_send.h
@@ -20,6 +20,7 @@
 #include "api/audio_codecs/audio_encoder.h"
 #include "api/call/transport.h"
 #include "api/crypto/cryptooptions.h"
+#include "api/media_transport_interface.h"
 #include "common_types.h"  // NOLINT(build/include)
 #include "modules/audio_coding/include/audio_coding_module.h"
 #include "modules/audio_processing/rms_level.h"
@@ -119,6 +120,7 @@
 
   ChannelSend(rtc::TaskQueue* encoder_queue,
               ProcessThread* module_process_thread,
+              MediaTransportInterface* media_transport,
               RtcpRttStats* rtcp_rtt_stats,
               RtcEventLog* rtc_event_log,
               FrameEncryptorInterface* frame_encryptor,
@@ -251,6 +253,21 @@
 
   int GetRtpTimestampRateHz() const;
 
+  int32_t SendRtpAudio(FrameType frameType,
+                       uint8_t payloadType,
+                       uint32_t timeStamp,
+                       rtc::ArrayView<const uint8_t> payload,
+                       const RTPFragmentationHeader* fragmentation);
+
+  int32_t SendMediaTransportAudio(FrameType frameType,
+                                  uint8_t payloadType,
+                                  uint32_t timeStamp,
+                                  rtc::ArrayView<const uint8_t> payload,
+                                  const RTPFragmentationHeader* fragmentation);
+
+  // Return media transport or nullptr if using RTP.
+  MediaTransportInterface* media_transport() { return media_transport_; }
+
   // Called on the encoder task queue when a new input audio frame is ready
   // for encoding.
   void ProcessAndEncodeAudioOnTaskQueue(AudioFrame* audio_input);
@@ -300,6 +317,20 @@
   bool encoder_queue_is_active_ RTC_GUARDED_BY(encoder_queue_lock_) = false;
   rtc::TaskQueue* encoder_queue_ = nullptr;
 
+  MediaTransportInterface* const media_transport_;
+  int media_transport_sequence_number_ RTC_GUARDED_BY(encoder_queue_) = 0;
+
+  rtc::CriticalSection media_transport_lock_;
+  // Currently set by SetLocalSSRC.
+  uint64_t media_transport_channel_id_ RTC_GUARDED_BY(&media_transport_lock_) =
+      0;
+  // Cache payload type and sampling frequency from most recent call to
+  // SetEncoder. Needed to set MediaTransportEncodedAudioFrame metadata, and
+  // invalidate on encoder change.
+  int media_transport_payload_type_ RTC_GUARDED_BY(&media_transport_lock_);
+  int media_transport_sampling_frequency_
+      RTC_GUARDED_BY(&media_transport_lock_);
+
   // E2EE Audio Frame Encryption
   rtc::scoped_refptr<FrameEncryptorInterface> frame_encryptor_;
   // E2EE Frame Encryption Options
diff --git a/audio/test/media_transport_test.cc b/audio/test/media_transport_test.cc
new file mode 100644
index 0000000..7c25a72
--- /dev/null
+++ b/audio/test/media_transport_test.cc
@@ -0,0 +1,142 @@
+/*
+ *  Copyright (c) 2018 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.
+ */
+
+#include "api/audio_codecs/audio_decoder_factory_template.h"
+#include "api/audio_codecs/audio_encoder_factory_template.h"
+#include "api/audio_codecs/opus/audio_decoder_opus.h"
+#include "api/audio_codecs/opus/audio_encoder_opus.h"
+#include "api/test/loopback_media_transport.h"
+#include "api/test/mock_audio_mixer.h"
+#include "audio/audio_receive_stream.h"
+#include "audio/audio_send_stream.h"
+#include "call/test/mock_bitrate_allocator.h"
+#include "logging/rtc_event_log/rtc_event_log.h"
+#include "modules/audio_device/include/test_audio_device.h"
+#include "modules/audio_mixer/audio_mixer_impl.h"
+#include "modules/audio_processing/include/mock_audio_processing.h"
+#include "modules/utility/include/process_thread.h"
+#include "rtc_base/task_queue.h"
+#include "rtc_base/timeutils.h"
+#include "test/gtest.h"
+#include "test/mock_transport.h"
+
+namespace webrtc {
+namespace test {
+
+namespace {
+constexpr int kPayloadTypeOpus = 17;
+constexpr int kSamplingFrequency = 48000;
+constexpr int kNumChannels = 2;
+constexpr int kWantedSamples = 3000;
+constexpr int kTestTimeoutMs = 2 * rtc::kNumMillisecsPerSec;
+
+class TestRenderer : public TestAudioDeviceModule::Renderer {
+ public:
+  TestRenderer(int sampling_frequency, int num_channels, size_t wanted_samples)
+      : sampling_frequency_(sampling_frequency),
+        num_channels_(num_channels),
+        wanted_samples_(wanted_samples) {}
+  ~TestRenderer() override = default;
+
+  int SamplingFrequency() const override { return sampling_frequency_; }
+  int NumChannels() const override { return num_channels_; }
+
+  bool Render(rtc::ArrayView<const int16_t> data) override {
+    if (data.size() >= wanted_samples_) {
+      return false;
+    }
+    wanted_samples_ -= data.size();
+    return true;
+  }
+
+ private:
+  const int sampling_frequency_;
+  const int num_channels_;
+  size_t wanted_samples_;
+};
+
+}  // namespace
+
+TEST(AudioWithMediaTransport, DeliversAudio) {
+  MediaTransportPair transport_pair;
+  MockTransport rtcp_send_transport;
+  MockTransport send_transport;
+  std::unique_ptr<RtcEventLog> null_event_log = RtcEventLog::CreateNull();
+  MockBitrateAllocator bitrate_allocator;
+
+  rtc::scoped_refptr<TestAudioDeviceModule> audio_device =
+      TestAudioDeviceModule::CreateTestAudioDeviceModule(
+          TestAudioDeviceModule::CreatePulsedNoiseCapturer(
+              /* max_amplitude= */ 10000, kSamplingFrequency, kNumChannels),
+          absl::make_unique<TestRenderer>(kSamplingFrequency, kNumChannels,
+                                          kWantedSamples));
+
+  AudioState::Config audio_config;
+  audio_config.audio_mixer = AudioMixerImpl::Create();
+  // TODO(nisse): Is a mock AudioProcessing enough?
+  audio_config.audio_processing =
+      new rtc::RefCountedObject<MockAudioProcessing>();
+  audio_config.audio_device_module = audio_device;
+  rtc::scoped_refptr<AudioState> audio_state = AudioState::Create(audio_config);
+
+  // TODO(nisse): Use some lossless codec?
+  const SdpAudioFormat audio_format("opus", kSamplingFrequency, kNumChannels);
+
+  // Setup receive stream;
+  webrtc::AudioReceiveStream::Config receive_config;
+  // TODO(nisse): Update AudioReceiveStream to not require rtcp_send_transport
+  // when a MediaTransport is provided.
+  receive_config.rtcp_send_transport = &rtcp_send_transport;
+  receive_config.media_transport = transport_pair.first();
+  receive_config.decoder_map.emplace(kPayloadTypeOpus, audio_format);
+  receive_config.decoder_factory =
+      CreateAudioDecoderFactory<AudioDecoderOpus>();
+
+  std::unique_ptr<ProcessThread> receive_process_thread =
+      ProcessThread::Create("audio recv thread");
+
+  webrtc::internal::AudioReceiveStream receive_stream(
+      /*rtp_stream_receiver_controller=*/nullptr,
+      /*packet_router=*/nullptr, receive_process_thread.get(), receive_config,
+      audio_state, null_event_log.get());
+
+  // TODO(nisse): Update AudioSendStream to not require send_transport when a
+  // MediaTransport is provided.
+  AudioSendStream::Config send_config(&send_transport, transport_pair.second());
+  send_config.send_codec_spec =
+      AudioSendStream::Config::SendCodecSpec(kPayloadTypeOpus, audio_format);
+  send_config.encoder_factory = CreateAudioEncoderFactory<AudioEncoderOpus>();
+  rtc::TaskQueue send_tq("audio send queue");
+  std::unique_ptr<ProcessThread> send_process_thread =
+      ProcessThread::Create("audio send thread");
+  TimeInterval life_time;
+  webrtc::internal::AudioSendStream send_stream(
+      send_config, audio_state, &send_tq, send_process_thread.get(),
+      /*transport=*/nullptr, &bitrate_allocator, null_event_log.get(),
+      /*rtcp_rtt_stats=*/nullptr, absl::optional<RtpState>(), &life_time);
+
+  audio_device->Init();  // Starts thread.
+  audio_device->RegisterAudioCallback(audio_state->audio_transport());
+
+  receive_stream.Start();
+  send_stream.Start();
+  audio_device->StartPlayout();
+  audio_device->StartRecording();
+
+  EXPECT_TRUE(audio_device->WaitForPlayoutEnd(kTestTimeoutMs));
+
+  audio_device->StopRecording();
+  audio_device->StopPlayout();
+  receive_stream.Stop();
+  send_stream.Stop();
+}
+
+}  // namespace test
+}  // namespace webrtc
diff --git a/call/audio_receive_stream.h b/call/audio_receive_stream.h
index 786c3ba..bad0c1a 100644
--- a/call/audio_receive_stream.h
+++ b/call/audio_receive_stream.h
@@ -20,6 +20,7 @@
 #include "api/audio_codecs/audio_decoder_factory.h"
 #include "api/call/transport.h"
 #include "api/crypto/cryptooptions.h"
+#include "api/media_transport_interface.h"
 #include "api/rtpparameters.h"
 #include "api/rtpreceiverinterface.h"
 #include "call/rtp_config.h"
@@ -107,6 +108,8 @@
 
     Transport* rtcp_send_transport = nullptr;
 
+    MediaTransportInterface* media_transport = nullptr;
+
     // NetEq settings.
     size_t jitter_buffer_max_packets = 50;
     bool jitter_buffer_fast_accelerate = false;
diff --git a/call/audio_send_stream.cc b/call/audio_send_stream.cc
index d0ef421..398b722 100644
--- a/call/audio_send_stream.cc
+++ b/call/audio_send_stream.cc
@@ -21,8 +21,12 @@
 AudioSendStream::Stats::Stats() = default;
 AudioSendStream::Stats::~Stats() = default;
 
+AudioSendStream::Config::Config(Transport* send_transport,
+                                MediaTransportInterface* media_transport)
+    : send_transport(send_transport), media_transport(media_transport) {}
+
 AudioSendStream::Config::Config(Transport* send_transport)
-    : send_transport(send_transport) {}
+    : Config(send_transport, nullptr) {}
 
 AudioSendStream::Config::~Config() = default;
 
@@ -31,6 +35,7 @@
   rtc::SimpleStringBuilder ss(buf);
   ss << "{rtp: " << rtp.ToString();
   ss << ", send_transport: " << (send_transport ? "(Transport)" : "null");
+  ss << ", media_transport: " << (media_transport ? "(Transport)" : "null");
   ss << ", min_bitrate_bps: " << min_bitrate_bps;
   ss << ", max_bitrate_bps: " << max_bitrate_bps;
   ss << ", send_codec_spec: "
diff --git a/call/audio_send_stream.h b/call/audio_send_stream.h
index fb431e0..3595b6c 100644
--- a/call/audio_send_stream.h
+++ b/call/audio_send_stream.h
@@ -23,6 +23,7 @@
 #include "api/call/transport.h"
 #include "api/crypto/cryptooptions.h"
 #include "api/crypto/frameencryptorinterface.h"
+#include "api/media_transport_interface.h"
 #include "api/rtpparameters.h"
 #include "call/rtp_config.h"
 #include "modules/audio_processing/include/audio_processing_statistics.h"
@@ -64,6 +65,7 @@
 
   struct Config {
     Config() = delete;
+    Config(Transport* send_transport, MediaTransportInterface* media_transport);
     explicit Config(Transport* send_transport);
     ~Config();
     std::string ToString() const;
@@ -95,6 +97,8 @@
     // the entire life of the AudioSendStream and is owned by the API client.
     Transport* send_transport = nullptr;
 
+    MediaTransportInterface* media_transport = nullptr;
+
     // Bitrate limits used for variable audio bitrate streams. Set both to -1 to
     // disable audio bitrate adaptation.
     // Note: This is still an experimental feature and not ready for real usage.
diff --git a/call/call_perf_tests.cc b/call/call_perf_tests.cc
index 082e22f..acc6af0 100644
--- a/call/call_perf_tests.cc
+++ b/call/call_perf_tests.cc
@@ -231,7 +231,8 @@
     CreateSendConfig(1, 0, 0, video_send_transport.get());
     CreateMatchingReceiveConfigs(receive_transport.get());
 
-    AudioSendStream::Config audio_send_config(audio_send_transport.get());
+    AudioSendStream::Config audio_send_config(audio_send_transport.get(),
+                                              /*media_transport=*/nullptr);
     audio_send_config.rtp.ssrc = kAudioSendSsrc;
     audio_send_config.send_codec_spec = AudioSendStream::Config::SendCodecSpec(
         kAudioSendPayloadType, {"ISAC", 16000, 1});
diff --git a/call/call_unittest.cc b/call/call_unittest.cc
index 8ae0400..83e96ff 100644
--- a/call/call_unittest.cc
+++ b/call/call_unittest.cc
@@ -62,7 +62,8 @@
 
 TEST(CallTest, CreateDestroy_AudioSendStream) {
   CallHelper call;
-  AudioSendStream::Config config(nullptr);
+  AudioSendStream::Config config(/*send_transport=*/nullptr,
+                                 /*media_transport=*/nullptr);
   config.rtp.ssrc = 42;
   AudioSendStream* stream = call->CreateAudioSendStream(config);
   EXPECT_NE(stream, nullptr);
@@ -84,7 +85,8 @@
 
 TEST(CallTest, CreateDestroy_AudioSendStreams) {
   CallHelper call;
-  AudioSendStream::Config config(nullptr);
+  AudioSendStream::Config config(/*send_transport=*/nullptr,
+                                 /*media_transport=*/nullptr);
   std::list<AudioSendStream*> streams;
   for (int i = 0; i < 2; ++i) {
     for (uint32_t ssrc = 0; ssrc < 1234567; ssrc += 34567) {
@@ -142,7 +144,8 @@
   AudioReceiveStream* recv_stream = call->CreateAudioReceiveStream(recv_config);
   EXPECT_NE(recv_stream, nullptr);
 
-  AudioSendStream::Config send_config(nullptr);
+  AudioSendStream::Config send_config(/*send_transport=*/nullptr,
+                                      /*media_transport=*/nullptr);
   send_config.rtp.ssrc = 777;
   AudioSendStream* send_stream = call->CreateAudioSendStream(send_config);
   EXPECT_NE(send_stream, nullptr);
@@ -160,7 +163,8 @@
 
 TEST(CallTest, CreateDestroy_AssociateAudioSendReceiveStreams_SendFirst) {
   CallHelper call;
-  AudioSendStream::Config send_config(nullptr);
+  AudioSendStream::Config send_config(/*send_transport=*/nullptr,
+                                      /*media_transport=*/nullptr);
   send_config.rtp.ssrc = 777;
   AudioSendStream* send_stream = call->CreateAudioSendStream(send_config);
   EXPECT_NE(send_stream, nullptr);
@@ -263,7 +267,8 @@
   CallHelper call;
 
   auto create_stream_and_get_rtp_state = [&](uint32_t ssrc) {
-    AudioSendStream::Config config(nullptr);
+    AudioSendStream::Config config(/*send_transport=*/nullptr,
+                                   /*media_transport=*/nullptr);
     config.rtp.ssrc = ssrc;
     AudioSendStream* stream = call->CreateAudioSendStream(config);
     const RtpState rtp_state =
diff --git a/media/engine/webrtcvoiceengine.cc b/media/engine/webrtcvoiceengine.cc
index 03cc543..889c7ec 100644
--- a/media/engine/webrtcvoiceengine.cc
+++ b/media/engine/webrtcvoiceengine.cc
@@ -22,6 +22,7 @@
 #include "absl/strings/match.h"
 #include "api/audio_codecs/audio_codec_pair_id.h"
 #include "api/call/audio_sink.h"
+#include "api/media_transport_interface.h"
 #include "media/base/audiosource.h"
 #include "media/base/mediaconstants.h"
 #include "media/base/streamparams.h"
@@ -709,12 +710,13 @@
       const absl::optional<std::string>& audio_network_adaptor_config,
       webrtc::Call* call,
       webrtc::Transport* send_transport,
+      webrtc::MediaTransportInterface* media_transport,
       const rtc::scoped_refptr<webrtc::AudioEncoderFactory>& encoder_factory,
       const absl::optional<webrtc::AudioCodecPairId> codec_pair_id,
       rtc::scoped_refptr<webrtc::FrameEncryptorInterface> frame_encryptor,
       const webrtc::CryptoOptions& crypto_options)
       : call_(call),
-        config_(send_transport),
+        config_(send_transport, media_transport),
         send_side_bwe_with_overhead_(
             webrtc::field_trial::IsEnabled("WebRTC-SendSideBwe-WithOverhead")),
         max_send_bitrate_bps_(max_send_bitrate_bps),
@@ -1076,6 +1078,7 @@
       const std::vector<webrtc::RtpExtension>& extensions,
       webrtc::Call* call,
       webrtc::Transport* rtcp_send_transport,
+      webrtc::MediaTransportInterface* media_transport,
       const rtc::scoped_refptr<webrtc::AudioDecoderFactory>& decoder_factory,
       const std::map<int, webrtc::SdpAudioFormat>& decoder_map,
       absl::optional<webrtc::AudioCodecPairId> codec_pair_id,
@@ -1091,6 +1094,7 @@
     config_.rtp.nack.rtp_history_ms = use_nack ? kNackRtpHistoryMs : 0;
     config_.rtp.extensions = extensions;
     config_.rtcp_send_transport = rtcp_send_transport;
+    config_.media_transport = media_transport;
     config_.jitter_buffer_max_packets = jitter_buffer_max_packets;
     config_.jitter_buffer_fast_accelerate = jitter_buffer_fast_accelerate;
     if (!stream_ids.empty()) {
@@ -1792,7 +1796,8 @@
   WebRtcAudioSendStream* stream = new WebRtcAudioSendStream(
       ssrc, mid_, sp.cname, sp.id, send_codec_spec_, send_rtp_extensions_,
       max_send_bitrate_bps_, audio_network_adaptor_config, call_, this,
-      engine()->encoder_factory_, codec_pair_id_, nullptr, crypto_options_);
+      media_transport(), engine()->encoder_factory_, codec_pair_id_, nullptr,
+      crypto_options_);
   send_streams_.insert(std::make_pair(ssrc, stream));
 
   // At this point the stream's local SSRC has been updated. If it is the first
@@ -1873,13 +1878,14 @@
 
   // Create a new channel for receiving audio data.
   recv_streams_.insert(std::make_pair(
-      ssrc, new WebRtcAudioReceiveStream(
-                ssrc, receiver_reports_ssrc_, recv_transport_cc_enabled_,
-                recv_nack_enabled_, sp.stream_ids(), recv_rtp_extensions_,
-                call_, this, engine()->decoder_factory_, decoder_map_,
-                codec_pair_id_, engine()->audio_jitter_buffer_max_packets_,
-                engine()->audio_jitter_buffer_fast_accelerate_,
-                unsignaled_frame_decryptor_, crypto_options_)));
+      ssrc,
+      new WebRtcAudioReceiveStream(
+          ssrc, receiver_reports_ssrc_, recv_transport_cc_enabled_,
+          recv_nack_enabled_, sp.stream_ids(), recv_rtp_extensions_, call_,
+          this, media_transport(), engine()->decoder_factory_, decoder_map_,
+          codec_pair_id_, engine()->audio_jitter_buffer_max_packets_,
+          engine()->audio_jitter_buffer_fast_accelerate_,
+          unsignaled_frame_decryptor_, crypto_options_)));
   recv_streams_[ssrc]->SetPlayout(playout_);
 
   return true;
diff --git a/modules/audio_device/include/test_audio_device.cc b/modules/audio_device/include/test_audio_device.cc
index ea59247..23f8576 100644
--- a/modules/audio_device/include/test_audio_device.cc
+++ b/modules/audio_device/include/test_audio_device.cc
@@ -176,9 +176,10 @@
           uint32_t new_mic_level = 0;
           if (recording_buffer_.size() > 0) {
             audio_callback_->RecordedDataIsAvailable(
-                recording_buffer_.data(), recording_buffer_.size(), 2,
-                capturer_->NumChannels(), capturer_->SamplingFrequency(), 0, 0,
-                0, false, new_mic_level);
+                recording_buffer_.data(),
+                recording_buffer_.size() / capturer_->NumChannels(),
+                2 * capturer_->NumChannels(), capturer_->NumChannels(),
+                capturer_->SamplingFrequency(), 0, 0, 0, false, new_mic_level);
           }
           if (!keep_capturing) {
             capturing_ = false;
@@ -191,9 +192,10 @@
           int64_t ntp_time_ms = -1;
           const int sampling_frequency = renderer_->SamplingFrequency();
           audio_callback_->NeedMorePlayData(
-              SamplesPerFrame(sampling_frequency), 2, renderer_->NumChannels(),
-              sampling_frequency, playout_buffer_.data(), samples_out,
-              &elapsed_time_ms, &ntp_time_ms);
+              SamplesPerFrame(sampling_frequency), 2 * renderer_->NumChannels(),
+              renderer_->NumChannels(), sampling_frequency,
+              playout_buffer_.data(), samples_out, &elapsed_time_ms,
+              &ntp_time_ms);
           const bool keep_rendering =
               renderer_->Render(rtc::ArrayView<const int16_t>(
                   playout_buffer_.data(), samples_out));
diff --git a/test/call_test.cc b/test/call_test.cc
index c96f197..286807e5 100644
--- a/test/call_test.cc
+++ b/test/call_test.cc
@@ -39,7 +39,8 @@
       send_event_log_(RtcEventLog::CreateNull()),
       recv_event_log_(RtcEventLog::CreateNull()),
       sender_call_transport_controller_(nullptr),
-      audio_send_config_(nullptr),
+      audio_send_config_(/*send_transport=*/nullptr,
+                         /*media_transport=*/nullptr),
       audio_send_stream_(nullptr),
       bbr_network_controller_factory_(new BbrNetworkControllerFactory()),
       fake_encoder_factory_([this]() {
@@ -259,7 +260,8 @@
   RTC_DCHECK_LE(num_audio_streams, 1);
   RTC_DCHECK_LE(num_flexfec_streams, 1);
   if (num_audio_streams > 0) {
-    AudioSendStream::Config audio_send_config(send_transport);
+    AudioSendStream::Config audio_send_config(send_transport,
+                                              /*media_transport=*/nullptr);
     audio_send_config.rtp.ssrc = kAudioSendSsrc;
     audio_send_config.send_codec_spec = AudioSendStream::Config::SendCodecSpec(
         kAudioSendPayloadType, {"opus", 48000, 2, {{"stereo", "1"}}});
diff --git a/test/scenario/audio_stream.cc b/test/scenario/audio_stream.cc
index 132998b..ceefb6c 100644
--- a/test/scenario/audio_stream.cc
+++ b/test/scenario/audio_stream.cc
@@ -67,7 +67,8 @@
     rtc::scoped_refptr<AudioEncoderFactory> encoder_factory,
     Transport* send_transport)
     : sender_(sender), config_(config) {
-  AudioSendStream::Config send_config(send_transport);
+  AudioSendStream::Config send_config(send_transport,
+                                      /*media_transport=*/nullptr);
   ssrc_ = sender->GetNextAudioSsrc();
   send_config.rtp.ssrc = ssrc_;
   SdpAudioFormat::Parameters sdp_params;
diff --git a/video/video_quality_test.cc b/video/video_quality_test.cc
index f2891f4..964e918 100644
--- a/video/video_quality_test.cc
+++ b/video/video_quality_test.cc
@@ -1255,7 +1255,8 @@
 }
 
 void VideoQualityTest::SetupAudio(Transport* transport) {
-  AudioSendStream::Config audio_send_config(transport);
+  AudioSendStream::Config audio_send_config(transport,
+                                            /*media_transport=*/nullptr);
   audio_send_config.rtp.ssrc = kAudioSendSsrc;
 
   // Add extension to enable audio send side BWE, and allow audio bit rate