Create no-op DTLS if media transport is used.
We'd like to disable RTP code path when media transport is used. In particular, we don't want occasional RTP/RTCP packets sent from the RTP code path when media transport is used.
Long term we will remove this new NoOp DTLS transport, when we stop creating rtp transport.
Bug: webrtc:9719
Change-Id: I27f121edef394465ddc8fe8003e6f4428b10c022
Reviewed-on: https://webrtc-review.googlesource.com/c/117700
Reviewed-by: Anton Sukhanov <sukhanov@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Commit-Queue: Peter Slatala <psla@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26286}
diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn
index f5c00cc..dcc7caf 100644
--- a/p2p/BUILD.gn
+++ b/p2p/BUILD.gn
@@ -43,6 +43,8 @@
"base/icetransportinternal.h",
"base/mdns_message.cc",
"base/mdns_message.h",
+ "base/no_op_dtls_transport.cc",
+ "base/no_op_dtls_transport.h",
"base/p2p_constants.cc",
"base/p2p_constants.h",
"base/p2p_transport_channel.cc",
diff --git a/p2p/base/no_op_dtls_transport.cc b/p2p/base/no_op_dtls_transport.cc
new file mode 100644
index 0000000..3662d88
--- /dev/null
+++ b/p2p/base/no_op_dtls_transport.cc
@@ -0,0 +1,143 @@
+/*
+ * Copyright 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 <algorithm>
+#include <memory>
+#include <utility>
+
+#include "p2p/base/no_op_dtls_transport.h"
+
+#include "absl/memory/memory.h"
+#include "logging/rtc_event_log/events/rtc_event_dtls_transport_state.h"
+#include "logging/rtc_event_log/events/rtc_event_dtls_writable_state.h"
+#include "logging/rtc_event_log/rtc_event_log.h"
+#include "p2p/base/packet_transport_internal.h"
+#include "rtc_base/buffer.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/dscp.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/message_queue.h"
+#include "rtc_base/rtc_certificate.h"
+#include "rtc_base/ssl_stream_adapter.h"
+#include "rtc_base/stream.h"
+#include "rtc_base/thread.h"
+
+namespace cricket {
+
+NoOpDtlsTransport::NoOpDtlsTransport(
+ std::unique_ptr<IceTransportInternal> ice_transport,
+ const webrtc::CryptoOptions& crypto_options)
+ : crypto_options_(webrtc::CryptoOptions::NoGcm()),
+ ice_transport_(std::move(ice_transport)) {
+ RTC_DCHECK(ice_transport_);
+ ice_transport_->SignalWritableState.connect(
+ this, &NoOpDtlsTransport::OnWritableState);
+ ice_transport_->SignalReadyToSend.connect(this,
+ &NoOpDtlsTransport::OnReadyToSend);
+}
+
+NoOpDtlsTransport::~NoOpDtlsTransport() {}
+const webrtc::CryptoOptions& NoOpDtlsTransport::crypto_options() const {
+ return crypto_options_;
+}
+DtlsTransportState NoOpDtlsTransport::dtls_state() const {
+ return DTLS_TRANSPORT_CONNECTED;
+}
+int NoOpDtlsTransport::component() const {
+ return kNoOpDtlsTransportComponent;
+}
+bool NoOpDtlsTransport::IsDtlsActive() const {
+ return true;
+}
+bool NoOpDtlsTransport::GetDtlsRole(rtc::SSLRole* role) const {
+ return false;
+}
+bool NoOpDtlsTransport::SetDtlsRole(rtc::SSLRole role) {
+ return false;
+}
+bool NoOpDtlsTransport::GetSrtpCryptoSuite(int* cipher) {
+ return false;
+}
+bool NoOpDtlsTransport::GetSslCipherSuite(int* cipher) {
+ return false;
+}
+rtc::scoped_refptr<rtc::RTCCertificate> NoOpDtlsTransport::GetLocalCertificate()
+ const {
+ return rtc::scoped_refptr<rtc::RTCCertificate>();
+}
+bool NoOpDtlsTransport::SetLocalCertificate(
+ const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
+ return false;
+}
+std::unique_ptr<rtc::SSLCertChain> NoOpDtlsTransport::GetRemoteSSLCertChain()
+ const {
+ return std::unique_ptr<rtc::SSLCertChain>();
+}
+bool NoOpDtlsTransport::ExportKeyingMaterial(const std::string& label,
+ const uint8_t* context,
+ size_t context_len,
+ bool use_context,
+ uint8_t* result,
+ size_t result_len) {
+ return false;
+}
+bool NoOpDtlsTransport::SetRemoteFingerprint(const std::string& digest_alg,
+ const uint8_t* digest,
+ size_t digest_len) {
+ return true;
+}
+bool NoOpDtlsTransport::SetSslMaxProtocolVersion(
+ rtc::SSLProtocolVersion version) {
+ return true;
+}
+IceTransportInternal* NoOpDtlsTransport::ice_transport() {
+ return ice_transport_.get();
+}
+
+void NoOpDtlsTransport::OnReadyToSend(rtc::PacketTransportInternal* transport) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ if (is_writable_) {
+ SignalReadyToSend(this);
+ }
+}
+
+void NoOpDtlsTransport::OnWritableState(
+ rtc::PacketTransportInternal* transport) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ is_writable_ = ice_transport_->writable();
+ if (is_writable_) {
+ SignalWritableState(this);
+ }
+}
+const std::string& NoOpDtlsTransport::transport_name() const {
+ return ice_transport_->transport_name();
+}
+bool NoOpDtlsTransport::writable() const {
+ return ice_transport_->writable();
+}
+bool NoOpDtlsTransport::receiving() const {
+ return ice_transport_->receiving();
+}
+int NoOpDtlsTransport::SendPacket(const char* data,
+ size_t len,
+ const rtc::PacketOptions& options,
+ int flags) {
+ return 0;
+}
+
+int NoOpDtlsTransport::SetOption(rtc::Socket::Option opt, int value) {
+ return ice_transport_->SetOption(opt, value);
+}
+
+int NoOpDtlsTransport::GetError() {
+ return ice_transport_->GetError();
+}
+
+} // namespace cricket
diff --git a/p2p/base/no_op_dtls_transport.h b/p2p/base/no_op_dtls_transport.h
new file mode 100644
index 0000000..4211160
--- /dev/null
+++ b/p2p/base/no_op_dtls_transport.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2019 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 P2P_BASE_NO_OP_DTLS_TRANSPORT_H_
+#define P2P_BASE_NO_OP_DTLS_TRANSPORT_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "api/crypto/crypto_options.h"
+#include "p2p/base/dtls_transport_internal.h"
+#include "p2p/base/ice_transport_internal.h"
+#include "p2p/base/packet_transport_internal.h"
+#include "rtc_base/buffer.h"
+#include "rtc_base/buffer_queue.h"
+#include "rtc_base/constructor_magic.h"
+#include "rtc_base/ssl_stream_adapter.h"
+#include "rtc_base/stream.h"
+#include "rtc_base/strings/string_builder.h"
+#include "rtc_base/thread_checker.h"
+
+namespace cricket {
+
+constexpr int kNoOpDtlsTransportComponent = -1;
+
+// This implementation wraps a cricket::DtlsTransport, and takes
+// ownership of it.
+// The implementation does not perform any operations, except of being
+// "connected". The purpose of this implementation is to disable RTP transport
+// while MediaTransport is used.
+//
+// This implementation is only temporary. Long-term we will refactor and disable
+// RTP transport entirely when MediaTransport is used. Always connected (after
+// ICE), no-op, dtls transport. This is used when DTLS is disabled.
+//
+// MaybeCreateJsepTransport controller expects DTLS connection to send a
+// 'connected' signal _after_ it is created (if it is created in a connected
+// state, that would not be noticed by jsep transport controller). Therefore,
+// the no-op dtls transport will wait for ICE event "writable", and then
+// immediately report that it's connected (emulating 0-rtt connection).
+//
+// We could simply not set a dtls to active (not set a certificate on the DTLS),
+// and it would use an underyling connection instead.
+// However, when MediaTransport is used, we want to entirely disable
+// dtls/srtp/rtp, in order to avoid multiplexing issues, such as "Failed to
+// unprotect RTCP packet".
+class NoOpDtlsTransport : public DtlsTransportInternal {
+ public:
+ NoOpDtlsTransport(std::unique_ptr<IceTransportInternal> ice_transport,
+ const webrtc::CryptoOptions& crypto_options);
+
+ ~NoOpDtlsTransport() override;
+ const webrtc::CryptoOptions& crypto_options() const override;
+ DtlsTransportState dtls_state() const override;
+ int component() const override;
+ bool IsDtlsActive() const override;
+ bool GetDtlsRole(rtc::SSLRole* role) const override;
+ bool SetDtlsRole(rtc::SSLRole role) override;
+ bool GetSrtpCryptoSuite(int* cipher) override;
+ bool GetSslCipherSuite(int* cipher) override;
+ rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const override;
+ bool SetLocalCertificate(
+ const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override;
+ std::unique_ptr<rtc::SSLCertChain> GetRemoteSSLCertChain() const override;
+ bool ExportKeyingMaterial(const std::string& label,
+ const uint8_t* context,
+ size_t context_len,
+ bool use_context,
+ uint8_t* result,
+ size_t result_len) override;
+ bool SetRemoteFingerprint(const std::string& digest_alg,
+ const uint8_t* digest,
+ size_t digest_len) override;
+ bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) override;
+ IceTransportInternal* ice_transport() override;
+
+ const std::string& transport_name() const override;
+ bool writable() const override;
+ bool receiving() const override;
+
+ private:
+ void OnReadyToSend(rtc::PacketTransportInternal* transport);
+ void OnWritableState(rtc::PacketTransportInternal* transport);
+
+ int SendPacket(const char* data,
+ size_t len,
+ const rtc::PacketOptions& options,
+ int flags) override;
+ int SetOption(rtc::Socket::Option opt, int value) override;
+ int GetError() override;
+
+ rtc::ThreadChecker thread_checker_;
+
+ webrtc::CryptoOptions crypto_options_;
+ std::unique_ptr<IceTransportInternal> ice_transport_;
+ bool is_writable_ = false;
+};
+
+} // namespace cricket
+
+#endif // P2P_BASE_NO_OP_DTLS_TRANSPORT_H_
diff --git a/pc/jsep_transport_controller.cc b/pc/jsep_transport_controller.cc
index dc05073..926d71d 100644
--- a/pc/jsep_transport_controller.cc
+++ b/pc/jsep_transport_controller.cc
@@ -15,6 +15,8 @@
#include <utility>
#include "absl/memory/memory.h"
+#include "p2p/base/ice_transport_internal.h"
+#include "p2p/base/no_op_dtls_transport.h"
#include "p2p/base/port.h"
#include "pc/srtp_filter.h"
#include "rtc_base/bind.h"
@@ -409,23 +411,35 @@
config_.media_transport_factory = media_transport_factory;
}
-std::unique_ptr<cricket::DtlsTransportInternal>
-JsepTransportController::CreateDtlsTransport(const std::string& transport_name,
- bool rtcp) {
- RTC_DCHECK(network_thread_->IsCurrent());
+std::unique_ptr<cricket::IceTransportInternal>
+JsepTransportController::CreateIceTransport(const std::string transport_name,
+ bool rtcp) {
int component = rtcp ? cricket::ICE_CANDIDATE_COMPONENT_RTCP
: cricket::ICE_CANDIDATE_COMPONENT_RTP;
- std::unique_ptr<cricket::DtlsTransportInternal> dtls;
if (config_.external_transport_factory) {
- auto ice = config_.external_transport_factory->CreateIceTransport(
+ return config_.external_transport_factory->CreateIceTransport(
transport_name, component);
+ } else {
+ return absl::make_unique<cricket::P2PTransportChannel>(
+ transport_name, component, port_allocator_, async_resolver_factory_,
+ config_.event_log);
+ }
+}
+
+std::unique_ptr<cricket::DtlsTransportInternal>
+JsepTransportController::CreateDtlsTransport(
+ std::unique_ptr<cricket::IceTransportInternal> ice) {
+ RTC_DCHECK(network_thread_->IsCurrent());
+
+ std::unique_ptr<cricket::DtlsTransportInternal> dtls;
+ if (config_.media_transport_factory) {
+ dtls = absl::make_unique<cricket::NoOpDtlsTransport>(
+ std::move(ice), config_.crypto_options);
+ } else if (config_.external_transport_factory) {
dtls = config_.external_transport_factory->CreateDtlsTransport(
std::move(ice), config_.crypto_options);
} else {
- auto ice = absl::make_unique<cricket::P2PTransportChannel>(
- transport_name, component, port_allocator_, async_resolver_factory_,
- config_.event_log);
dtls = absl::make_unique<cricket::DtlsTransport>(
std::move(ice), config_.crypto_options, config_.event_log);
}
@@ -1032,26 +1046,30 @@
"SDES and DTLS-SRTP cannot be enabled at the same time.");
}
+ std::unique_ptr<cricket::IceTransportInternal> ice =
+ CreateIceTransport(content_info.name, /*rtcp=*/false);
+
+ std::unique_ptr<MediaTransportInterface> media_transport =
+ MaybeCreateMediaTransport(content_info, local, ice.get());
+
std::unique_ptr<cricket::DtlsTransportInternal> rtp_dtls_transport =
- CreateDtlsTransport(content_info.name, /*rtcp =*/false);
+ CreateDtlsTransport(std::move(ice));
std::unique_ptr<cricket::DtlsTransportInternal> rtcp_dtls_transport;
std::unique_ptr<RtpTransport> unencrypted_rtp_transport;
std::unique_ptr<SrtpTransport> sdes_transport;
std::unique_ptr<DtlsSrtpTransport> dtls_srtp_transport;
- std::unique_ptr<MediaTransportInterface> media_transport;
if (config_.rtcp_mux_policy !=
PeerConnectionInterface::kRtcpMuxPolicyRequire &&
content_info.type == cricket::MediaProtocolType::kRtp) {
- rtcp_dtls_transport =
- CreateDtlsTransport(content_info.name, /*rtcp =*/true);
+ RTC_DCHECK(media_transport == nullptr);
+ rtcp_dtls_transport = CreateDtlsTransport(
+ CreateIceTransport(content_info.name, /*rtcp=*/true));
}
- media_transport = MaybeCreateMediaTransport(
- content_info, local, rtp_dtls_transport->ice_transport());
// TODO(sukhanov): Do not create RTP/RTCP transports if media transport is
- // used.
+ // used, and remove the no-op dtls transport when that's done.
if (config_.disable_encryption) {
unencrypted_rtp_transport = CreateUnencryptedRtpTransport(
content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
diff --git a/pc/jsep_transport_controller.h b/pc/jsep_transport_controller.h
index 1f4a632..e57b7d8 100644
--- a/pc/jsep_transport_controller.h
+++ b/pc/jsep_transport_controller.h
@@ -295,7 +295,9 @@
bool local);
std::unique_ptr<cricket::DtlsTransportInternal> CreateDtlsTransport(
- const std::string& transport_name,
+ std::unique_ptr<cricket::IceTransportInternal> ice);
+ std::unique_ptr<cricket::IceTransportInternal> CreateIceTransport(
+ const std::string transport_name,
bool rtcp);
std::unique_ptr<webrtc::RtpTransport> CreateUnencryptedRtpTransport(
diff --git a/pc/jsep_transport_controller_unittest.cc b/pc/jsep_transport_controller_unittest.cc
index 018140d..d6a31c8 100644
--- a/pc/jsep_transport_controller_unittest.cc
+++ b/pc/jsep_transport_controller_unittest.cc
@@ -16,6 +16,7 @@
#include "api/test/fake_media_transport.h"
#include "p2p/base/fake_dtls_transport.h"
#include "p2p/base/fake_ice_transport.h"
+#include "p2p/base/no_op_dtls_transport.h"
#include "p2p/base/transport_factory_interface.h"
#include "p2p/base/transport_info.h"
#include "pc/jsep_transport_controller.h"
@@ -418,10 +419,10 @@
FakeMediaTransportFactory fake_media_transport_factory;
JsepTransportController::Config config;
- config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
+ config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
config.media_transport_factory = &fake_media_transport_factory;
CreateJsepTransportController(config);
- auto description = CreateSessionDescriptionWithoutBundle();
+ auto description = CreateSessionDescriptionWithBundleGroup();
AddCryptoSettings(description.get());
EXPECT_TRUE(transport_controller_
@@ -439,16 +440,20 @@
// Return nullptr for non-existing mids.
EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kVideoMid2));
+
+ EXPECT_EQ(cricket::kNoOpDtlsTransportComponent,
+ transport_controller_->GetDtlsTransport(kAudioMid1)->component())
+ << "Because media transport is used, expected no-op DTLS transport.";
}
TEST_F(JsepTransportControllerTest, GetMediaTransportInCallee) {
FakeMediaTransportFactory fake_media_transport_factory;
JsepTransportController::Config config;
- config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
+ config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
config.media_transport_factory = &fake_media_transport_factory;
CreateJsepTransportController(config);
- auto description = CreateSessionDescriptionWithoutBundle();
+ auto description = CreateSessionDescriptionWithBundleGroup();
AddCryptoSettings(description.get());
EXPECT_TRUE(transport_controller_
->SetRemoteDescription(SdpType::kOffer, description.get())
@@ -465,6 +470,10 @@
// Return nullptr for non-existing mids.
EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kVideoMid2));
+
+ EXPECT_EQ(cricket::kNoOpDtlsTransportComponent,
+ transport_controller_->GetDtlsTransport(kAudioMid1)->component())
+ << "Because media transport is used, expected no-op DTLS transport.";
}
TEST_F(JsepTransportControllerTest, GetMediaTransportIsNotSetIfNoSdes) {
@@ -490,6 +499,10 @@
.ok());
EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kAudioMid1));
+ EXPECT_EQ(cricket::ICE_CANDIDATE_COMPONENT_RTP,
+ transport_controller_->GetDtlsTransport(kAudioMid1)->component())
+ << "Because media transport is NOT used (fallback to RTP), expected "
+ "actual DTLS transport for RTP";
}
TEST_F(JsepTransportControllerTest,
@@ -497,7 +510,7 @@
FakeMediaTransportFactory fake_media_transport_factory;
JsepTransportController::Config config;
- config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
+ config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
config.media_transport_factory = &fake_media_transport_factory;
CreateJsepTransportController(config);
auto description = CreateSessionDescriptionWithoutBundle();
@@ -813,25 +826,24 @@
JsepTransportController::Config config;
config.media_transport_factory = &fake_media_transport_factory;
CreateJsepTransportController(config);
- auto description = CreateSessionDescriptionWithoutBundle();
+
+ // Media Transport is only used with bundle.
+ auto description = CreateSessionDescriptionWithBundleGroup();
AddCryptoSettings(description.get());
EXPECT_TRUE(transport_controller_
->SetLocalDescription(SdpType::kOffer, description.get())
.ok());
- auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
- transport_controller_->GetDtlsTransport(kAudioMid1));
- auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
- transport_controller_->GetDtlsTransport(kVideoMid1));
- fake_audio_dtls->SetWritable(true);
- fake_video_dtls->SetWritable(true);
- // Decreasing connection count from 2 to 1 triggers connection state event.
- fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
- fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
- fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
- fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
- fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
- fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
+ auto fake_audio_ice = static_cast<cricket::FakeIceTransport*>(
+ transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport());
+ auto fake_video_ice = static_cast<cricket::FakeIceTransport*>(
+ transport_controller_->GetDtlsTransport(kVideoMid1)->ice_transport());
+ fake_audio_ice->SetConnectionCount(2);
+ fake_audio_ice->SetConnectionCount(1);
+ fake_video_ice->SetConnectionCount(2);
+ fake_video_ice->SetConnectionCount(1);
+ fake_audio_ice->SetWritable(true);
+ fake_video_ice->SetWritable(true);
// Still not connected, because we are waiting for media transport.
EXPECT_EQ_WAIT(cricket::kIceConnectionConnecting, connection_state_,
@@ -864,19 +876,17 @@
->SetLocalDescription(SdpType::kOffer, description.get())
.ok());
- auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
- transport_controller_->GetDtlsTransport(kAudioMid1));
- auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
- transport_controller_->GetDtlsTransport(kVideoMid1));
- fake_audio_dtls->SetWritable(true);
- fake_video_dtls->SetWritable(true);
+ auto fake_audio_ice = static_cast<cricket::FakeIceTransport*>(
+ transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport());
+ auto fake_video_ice = static_cast<cricket::FakeIceTransport*>(
+ transport_controller_->GetDtlsTransport(kVideoMid1)->ice_transport());
+ fake_audio_ice->SetWritable(true);
+ fake_video_ice->SetWritable(true);
// Decreasing connection count from 2 to 1 triggers connection state event.
- fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
- fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
- fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
- fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
- fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
- fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
+ fake_audio_ice->SetConnectionCount(2);
+ fake_audio_ice->SetConnectionCount(1);
+ fake_video_ice->SetConnectionCount(2);
+ fake_video_ice->SetConnectionCount(1);
FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
transport_controller_->GetMediaTransport(kAudioMid1));