From 316f3ac13b51a540e72db4d165d34006ce9edcf2 Mon Sep 17 00:00:00 2001 From: Anton Sukhanov Date: Thu, 23 May 2019 15:50:38 -0700 Subject: [PATCH] Datagram Transport Integration - Implement datagram transport adaptor, which wraps datagram transport in DtlsTransportInternal. Datagram adaptor owns both ICE and Datagram Transports. - Implement setup of datagram transport based on RTCConfiguration flag use_datagram_transport. This is very similar to MediaTransport setup with the exception that we create DTLS datagram adaptor. - Propagate maximum datagram size to video encoder via MediaTransportConfig. TODO: Currently this CL can only be tested in downstream projects. Once we add fake datagram transport, we will be able to implement unit tests similar to loopback media transport. Bug: webrtc:9719 Change-Id: I4fa4a5725598dfee5da4f0f374269a7e289d48ed Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/138100 Commit-Queue: Anton Sukhanov Reviewed-by: Bjorn Mellem Reviewed-by: Steve Anton Cr-Commit-Position: refs/heads/master@{#28047} --- api/media_transport_config.cc | 25 +- api/media_transport_config.h | 23 +- media/base/rtp_data_engine_unittest.cc | 3 +- media/engine/webrtc_video_engine.cc | 8 + p2p/BUILD.gn | 2 + p2p/base/datagram_dtls_adaptor.cc | 405 +++++++++++++++++++++++ p2p/base/datagram_dtls_adaptor.h | 154 +++++++++ p2p/base/dtls_transport.cc | 18 - p2p/base/dtls_transport_internal.cc | 18 + p2p/base/dtls_transport_internal.h | 14 + pc/jsep_transport.cc | 7 +- pc/jsep_transport.h | 10 + pc/jsep_transport_controller.cc | 202 +++++++++-- pc/jsep_transport_controller.h | 49 ++- pc/jsep_transport_controller_unittest.cc | 7 +- pc/peer_connection.cc | 54 ++- pc/peer_connection.h | 16 - 17 files changed, 917 insertions(+), 98 deletions(-) create mode 100644 p2p/base/datagram_dtls_adaptor.cc create mode 100644 p2p/base/datagram_dtls_adaptor.h diff --git a/api/media_transport_config.cc b/api/media_transport_config.cc index 7eb4cd4018..99c4140ffb 100644 --- a/api/media_transport_config.cc +++ b/api/media_transport_config.cc @@ -10,11 +10,30 @@ #include "api/media_transport_config.h" +#include "rtc_base/checks.h" +#include "rtc_base/string_utils.h" +#include "rtc_base/strings/string_builder.h" + namespace webrtc { -std::string MediaTransportConfig::DebugString() const { - return (media_transport != nullptr ? "{media_transport: (Transport)}" - : "{media_transport: null}"); +MediaTransportConfig::MediaTransportConfig( + MediaTransportInterface* media_transport) + : media_transport(media_transport) { + RTC_DCHECK(media_transport != nullptr); +} + +MediaTransportConfig::MediaTransportConfig(size_t rtp_max_packet_size) + : rtp_max_packet_size(rtp_max_packet_size) { + RTC_DCHECK_GT(rtp_max_packet_size, 0); +} + +std::string MediaTransportConfig::DebugString() + const { // TODO(sukhanov): Add rtp_max_packet_size (requires fixing + // audio_send/receive_stream_unittest.cc). + rtc::StringBuilder result; + result << "{media_transport: " + << (media_transport != nullptr ? "(Transport)" : "null") << "}"; + return result.Release(); } } // namespace webrtc diff --git a/api/media_transport_config.h b/api/media_transport_config.h index d5de42a672..7c5104b1f9 100644 --- a/api/media_transport_config.h +++ b/api/media_transport_config.h @@ -13,28 +13,33 @@ #include #include +#include "absl/types/optional.h" + namespace webrtc { class MediaTransportInterface; -// MediaTransportConfig contains meida transport (if provided) and passed from -// PeerConnection to call obeject and media layers that require access to media -// transport. In the future we can add other transport (for example, datagram -// transport) and related configuration. +// Media transport config is made available to both transport and audio / video +// layers, but access to individual interfaces should not be open without +// necessity. struct MediaTransportConfig { // Default constructor for no-media transport scenarios. MediaTransportConfig() = default; - // TODO(sukhanov): Consider adding RtpTransport* to MediaTransportConfig, - // because it's almost always passes along with media_transport. - // Does not own media_transport. - explicit MediaTransportConfig(MediaTransportInterface* media_transport) - : media_transport(media_transport) {} + // Constructor for media transport scenarios. + // Note that |media_transport| may not be nullptr. + explicit MediaTransportConfig(MediaTransportInterface* media_transport); + + // Constructor for datagram transport scenarios. + explicit MediaTransportConfig(size_t rtp_max_packet_size); std::string DebugString() const; // If provided, all media is sent through media_transport. MediaTransportInterface* media_transport = nullptr; + + // If provided, limits RTP packet size (excludes ICE, IP or network overhead). + absl::optional rtp_max_packet_size; }; } // namespace webrtc diff --git a/media/base/rtp_data_engine_unittest.cc b/media/base/rtp_data_engine_unittest.cc index df0f90416c..cd7d295f6b 100644 --- a/media/base/rtp_data_engine_unittest.cc +++ b/media/base/rtp_data_engine_unittest.cc @@ -74,8 +74,7 @@ class RtpDataMediaChannelTest : public ::testing::Test { cricket::MediaConfig config; cricket::RtpDataMediaChannel* channel = static_cast(dme->CreateChannel(config)); - channel->SetInterface(iface_.get(), webrtc::MediaTransportConfig( - /*media_transport=*/nullptr)); + channel->SetInterface(iface_.get(), webrtc::MediaTransportConfig()); channel->SignalDataReceived.connect(receiver_.get(), &FakeDataReceiver::OnDataReceived); return channel; diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc index eecae16dea..f95ab952c5 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc @@ -18,6 +18,7 @@ #include "absl/algorithm/container.h" #include "absl/strings/match.h" +#include "api/datagram_transport_interface.h" #include "api/video/video_codec_constants.h" #include "api/video_codecs/sdp_video_format.h" #include "api/video_codecs/video_decoder_factory.h" @@ -1101,6 +1102,13 @@ bool WebRtcVideoChannel::AddSendStream(const StreamParams& sp) { config.rtp.extmap_allow_mixed = ExtmapAllowMixed(); config.rtcp_report_interval_ms = video_config_.rtcp_report_interval_ms; + // If sending through Datagram Transport, limit packet size to maximum + // packet size supported by datagram_transport. + if (media_transport_config().rtp_max_packet_size) { + config.rtp.max_packet_size = + media_transport_config().rtp_max_packet_size.value(); + } + WebRtcVideoSendStream* stream = new WebRtcVideoSendStream( call_, sp, std::move(config), default_send_options_, video_config_.enable_cpu_adaptation, bitrate_config_.max_bitrate_bps, diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn index 5db66be499..e404896e5d 100644 --- a/p2p/BUILD.gn +++ b/p2p/BUILD.gn @@ -25,6 +25,8 @@ rtc_static_library("rtc_p2p") { "base/basic_packet_socket_factory.cc", "base/basic_packet_socket_factory.h", "base/candidate_pair_interface.h", + "base/datagram_dtls_adaptor.cc", + "base/datagram_dtls_adaptor.h", "base/dtls_transport.cc", "base/dtls_transport.h", "base/dtls_transport_internal.cc", diff --git a/p2p/base/datagram_dtls_adaptor.cc b/p2p/base/datagram_dtls_adaptor.cc new file mode 100644 index 0000000000..ecf14b3195 --- /dev/null +++ b/p2p/base/datagram_dtls_adaptor.cc @@ -0,0 +1,405 @@ +/* + * 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 "p2p/base/datagram_dtls_adaptor.h" + +#include +#include +#include + +#include "absl/memory/memory.h" +#include "absl/strings/string_view.h" +#include "api/rtc_error.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/dtls_transport_internal.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/flags.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" + +#ifdef BYPASS_DATAGRAM_DTLS_TEST_ONLY +// Send unencrypted packets directly to ICE, bypassing datagtram +// transport. Use in tests only. +constexpr bool kBypassDatagramDtlsTestOnly = true; +#else +constexpr bool kBypassDatagramDtlsTestOnly = false; +#endif + +namespace cricket { + +DatagramDtlsAdaptor::DatagramDtlsAdaptor( + std::unique_ptr ice_transport, + std::unique_ptr datagram_transport, + const webrtc::CryptoOptions& crypto_options, + webrtc::RtcEventLog* event_log) + : crypto_options_(crypto_options), + ice_transport_(std::move(ice_transport)), + datagram_transport_(std::move(datagram_transport)), + event_log_(event_log) { + RTC_DCHECK(ice_transport_); + RTC_DCHECK(datagram_transport_); + ConnectToIceTransport(); +} + +void DatagramDtlsAdaptor::ConnectToIceTransport() { + if (kBypassDatagramDtlsTestOnly) { + // In bypass mode we have to subscribe to ICE read and sent events. + // Test only case to use ICE directly instead of data transport. + ice_transport_->SignalReadPacket.connect( + this, &DatagramDtlsAdaptor::OnReadPacket); + + ice_transport_->SignalSentPacket.connect( + this, &DatagramDtlsAdaptor::OnSentPacket); + + ice_transport_->SignalWritableState.connect( + this, &DatagramDtlsAdaptor::OnWritableState); + ice_transport_->SignalReadyToSend.connect( + this, &DatagramDtlsAdaptor::OnReadyToSend); + ice_transport_->SignalReceivingState.connect( + this, &DatagramDtlsAdaptor::OnReceivingState); + } else { + // Subscribe to Data Transport read packets. + datagram_transport_->SetDatagramSink(this); + datagram_transport_->SetTransportStateCallback(this); + + // Datagram transport does not propagate network route change. + ice_transport_->SignalNetworkRouteChanged.connect( + this, &DatagramDtlsAdaptor::OnNetworkRouteChanged); + } +} + +DatagramDtlsAdaptor::~DatagramDtlsAdaptor() { + // Unsubscribe from Datagram Transport dinks. + datagram_transport_->SetDatagramSink(nullptr); + datagram_transport_->SetTransportStateCallback(nullptr); + + // Make sure datagram transport is destroyed before ICE. + datagram_transport_.reset(); + ice_transport_.reset(); +} + +const webrtc::CryptoOptions& DatagramDtlsAdaptor::crypto_options() const { + return crypto_options_; +} + +int DatagramDtlsAdaptor::SendPacket(const char* data, + size_t len, + const rtc::PacketOptions& options, + int flags) { + // TODO(sukhanov): Handle options and flags. + if (kBypassDatagramDtlsTestOnly) { + // In bypass mode sent directly to ICE. + return ice_transport_->SendPacket(data, len, options); + } + + // Send datagram with id equal to options.packet_id, so we get it back + // in DatagramDtlsAdaptor::OnDatagramSent() and propagate notification + // up. + webrtc::RTCError error = datagram_transport_->SendDatagram( + rtc::MakeArrayView(reinterpret_cast(data), len), + /*datagram_id=*/options.packet_id); + + return (error.ok() ? len : -1); +} + +void DatagramDtlsAdaptor::OnReadPacket(rtc::PacketTransportInternal* transport, + const char* data, + size_t size, + const int64_t& packet_time_us, + int flags) { + // Only used in bypass mode. + RTC_DCHECK(kBypassDatagramDtlsTestOnly); + + RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK_EQ(transport, ice_transport_.get()); + RTC_DCHECK(flags == 0); + + PropagateReadPacket( + rtc::MakeArrayView(reinterpret_cast(data), size), + packet_time_us); +} + +void DatagramDtlsAdaptor::OnDatagramReceived( + rtc::ArrayView data) { + RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK(!kBypassDatagramDtlsTestOnly); + + // TODO(sukhanov): I am not filling out time, but on my video quality + // test in WebRTC the time was not set either and higher layers of the stack + // overwrite -1 with current current rtc time. Leaveing comment for now to + // make sure it works as expected. + int64_t packet_time_us = -1; + + PropagateReadPacket(data, packet_time_us); +} + +void DatagramDtlsAdaptor::OnDatagramSent(webrtc::DatagramId datagram_id) { + // When we called DatagramTransportInterface::SendDatagram, we passed + // packet_id as datagram_id, so we simply need to set it in sent_packet + // and propagate notification up the stack. + + // Also see how DatagramDtlsAdaptor::OnSentPacket handles OnSentPacket + // notification from ICE in bypass mode. + rtc::SentPacket sent_packet(/*packet_id=*/datagram_id, rtc::TimeMillis()); + + PropagateOnSentNotification(sent_packet); +} + +void DatagramDtlsAdaptor::OnSentPacket(rtc::PacketTransportInternal* transport, + const rtc::SentPacket& sent_packet) { + // Only used in bypass mode. + RTC_DCHECK(kBypassDatagramDtlsTestOnly); + RTC_DCHECK_RUN_ON(&thread_checker_); + + PropagateOnSentNotification(sent_packet); +} + +void DatagramDtlsAdaptor::PropagateOnSentNotification( + const rtc::SentPacket& sent_packet) { + RTC_DCHECK_RUN_ON(&thread_checker_); + SignalSentPacket(this, sent_packet); +} + +void DatagramDtlsAdaptor::PropagateReadPacket( + rtc::ArrayView data, + const int64_t& packet_time_us) { + RTC_DCHECK_RUN_ON(&thread_checker_); + SignalReadPacket(this, reinterpret_cast(data.data()), + data.size(), packet_time_us, /*flags=*/0); +} + +int DatagramDtlsAdaptor::component() const { + return kDatagramDtlsAdaptorComponent; +} +bool DatagramDtlsAdaptor::IsDtlsActive() const { + return false; +} +bool DatagramDtlsAdaptor::GetDtlsRole(rtc::SSLRole* role) const { + return false; +} +bool DatagramDtlsAdaptor::SetDtlsRole(rtc::SSLRole role) { + return false; +} +bool DatagramDtlsAdaptor::GetSrtpCryptoSuite(int* cipher) { + return false; +} +bool DatagramDtlsAdaptor::GetSslCipherSuite(int* cipher) { + return false; +} + +rtc::scoped_refptr +DatagramDtlsAdaptor::GetLocalCertificate() const { + return nullptr; +} + +bool DatagramDtlsAdaptor::SetLocalCertificate( + const rtc::scoped_refptr& certificate) { + return false; +} + +std::unique_ptr DatagramDtlsAdaptor::GetRemoteSSLCertChain() + const { + return nullptr; +} + +bool DatagramDtlsAdaptor::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 DatagramDtlsAdaptor::SetRemoteFingerprint(const std::string& digest_alg, + const uint8_t* digest, + size_t digest_len) { + // TODO(sukhanov): We probably should not called with fingerptints in + // datagram scenario, but we may need to change code up the stack before + // we can return false or DCHECK. + return true; +} + +bool DatagramDtlsAdaptor::SetSslMaxProtocolVersion( + rtc::SSLProtocolVersion version) { + // TODO(sukhanov): We may be able to return false and/or DCHECK that we + // are not called if datagram transport is used, but we need to change + // integration before we can do it. + return true; +} + +IceTransportInternal* DatagramDtlsAdaptor::ice_transport() { + return ice_transport_.get(); +} + +webrtc::DatagramTransportInterface* DatagramDtlsAdaptor::datagram_transport() { + return datagram_transport_.get(); +} + +// Similar implementaton as in p2p/base/dtls_transport.cc. +void DatagramDtlsAdaptor::OnReadyToSend( + rtc::PacketTransportInternal* transport) { + RTC_DCHECK_RUN_ON(&thread_checker_); + if (writable()) { + SignalReadyToSend(this); + } +} + +void DatagramDtlsAdaptor::OnWritableState( + rtc::PacketTransportInternal* transport) { + RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK(transport == ice_transport_.get()); + RTC_LOG(LS_VERBOSE) << ": ice_transport writable state changed to " + << ice_transport_->writable(); + + if (kBypassDatagramDtlsTestOnly) { + // Note: SignalWritableState fired by set_writable. + set_writable(ice_transport_->writable()); + return; + } + + switch (dtls_state()) { + case DTLS_TRANSPORT_NEW: + break; + case DTLS_TRANSPORT_CONNECTED: + // Note: SignalWritableState fired by set_writable. + // Do we also need set_receiving(ice_transport_->receiving()) here now, in + // case we lose that signal before "DTLS" connects? + // DtlsTransport::OnWritableState does not set_receiving in a similar + // case, so leaving it out for the time being, but it would be good to + // understand why. + set_writable(ice_transport_->writable()); + break; + case DTLS_TRANSPORT_CONNECTING: + // Do nothing. + break; + case DTLS_TRANSPORT_FAILED: + case DTLS_TRANSPORT_CLOSED: + // Should not happen. Do nothing. + break; + } +} + +void DatagramDtlsAdaptor::OnStateChanged(webrtc::MediaTransportState state) { + // Convert MediaTransportState to DTLS state. + switch (state) { + case webrtc::MediaTransportState::kPending: + set_dtls_state(DTLS_TRANSPORT_CONNECTING); + break; + + case webrtc::MediaTransportState::kWritable: + // Since we do not set writable state until datagram transport is + // connected, we need to call set_writable first. + set_writable(ice_transport_->writable()); + set_dtls_state(DTLS_TRANSPORT_CONNECTED); + break; + + case webrtc::MediaTransportState::kClosed: + set_dtls_state(DTLS_TRANSPORT_CLOSED); + break; + } +} + +DtlsTransportState DatagramDtlsAdaptor::dtls_state() const { + return dtls_state_; +} + +const std::string& DatagramDtlsAdaptor::transport_name() const { + return ice_transport_->transport_name(); +} + +bool DatagramDtlsAdaptor::writable() const { + // NOTE that even if ice is writable, writable_ maybe false, because we + // propagte writable only after DTLS is connect (this is consistent with + // implementation in dtls_transport.cc). + return writable_; +} + +bool DatagramDtlsAdaptor::receiving() const { + return receiving_; +} + +int DatagramDtlsAdaptor::SetOption(rtc::Socket::Option opt, int value) { + return ice_transport_->SetOption(opt, value); +} + +int DatagramDtlsAdaptor::GetError() { + return ice_transport_->GetError(); +} + +void DatagramDtlsAdaptor::OnNetworkRouteChanged( + absl::optional network_route) { + RTC_DCHECK_RUN_ON(&thread_checker_); + SignalNetworkRouteChanged(network_route); +} + +void DatagramDtlsAdaptor::OnReceivingState( + rtc::PacketTransportInternal* transport) { + RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK(transport == ice_transport_.get()); + RTC_LOG(LS_VERBOSE) << "ice_transport receiving state changed to " + << ice_transport_->receiving(); + + if (kBypassDatagramDtlsTestOnly || dtls_state() == DTLS_TRANSPORT_CONNECTED) { + // Note: SignalReceivingState fired by set_receiving. + set_receiving(ice_transport_->receiving()); + } +} + +void DatagramDtlsAdaptor::set_receiving(bool receiving) { + if (receiving_ == receiving) { + return; + } + receiving_ = receiving; + SignalReceivingState(this); +} + +// Similar implementaton as in p2p/base/dtls_transport.cc. +void DatagramDtlsAdaptor::set_writable(bool writable) { + if (writable_ == writable) { + return; + } + if (event_log_) { + event_log_->Log( + absl::make_unique(writable)); + } + RTC_LOG(LS_VERBOSE) << "set_writable to: " << writable; + writable_ = writable; + if (writable_) { + SignalReadyToSend(this); + } + SignalWritableState(this); +} + +// Similar implementaton as in p2p/base/dtls_transport.cc. +void DatagramDtlsAdaptor::set_dtls_state(DtlsTransportState state) { + if (dtls_state_ == state) { + return; + } + if (event_log_) { + event_log_->Log(absl::make_unique( + ConvertDtlsTransportState(state))); + } + RTC_LOG(LS_VERBOSE) << "set_dtls_state from:" << dtls_state_ << " to " + << state; + dtls_state_ = state; + SignalDtlsState(this, state); +} + +} // namespace cricket diff --git a/p2p/base/datagram_dtls_adaptor.h b/p2p/base/datagram_dtls_adaptor.h new file mode 100644 index 0000000000..2f6fdc1236 --- /dev/null +++ b/p2p/base/datagram_dtls_adaptor.h @@ -0,0 +1,154 @@ +/* + * 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_DATAGRAM_DTLS_ADAPTOR_H_ +#define P2P_BASE_DATAGRAM_DTLS_ADAPTOR_H_ + +#include +#include +#include + +#include "api/crypto/crypto_options.h" +#include "api/datagram_transport_interface.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 kDatagramDtlsAdaptorComponent = -1; + +// DTLS wrapper around DatagramTransportInterface. +// Does not encrypt. +// Owns Datagram and Ice transports. +class DatagramDtlsAdaptor : public DtlsTransportInternal, + public webrtc::DatagramSinkInterface, + public webrtc::MediaTransportStateCallback { + public: + // TODO(sukhanov): Taking crypto options, because DtlsTransportInternal + // has a virtual getter crypto_options(). Consider removing getter and + // removing crypto_options from DatagramDtlsAdaptor. + DatagramDtlsAdaptor( + std::unique_ptr ice_transport, + std::unique_ptr datagram_transport, + const webrtc::CryptoOptions& crypto_options, + webrtc::RtcEventLog* event_log); + + ~DatagramDtlsAdaptor() override; + + // Connects to ICE transport callbacks. + void ConnectToIceTransport(); + + // ===================================================== + // Overrides for webrtc::DatagramTransportSinkInterface + // and MediaTransportStateCallback + // ===================================================== + void OnDatagramReceived(rtc::ArrayView data) override; + + void OnDatagramSent(webrtc::DatagramId datagram_id) override; + + void OnStateChanged(webrtc::MediaTransportState state) override; + + // ===================================================== + // DtlsTransportInternal overrides + // ===================================================== + 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 GetLocalCertificate() const override; + bool SetLocalCertificate( + const rtc::scoped_refptr& certificate) override; + std::unique_ptr 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; + webrtc::DatagramTransportInterface* datagram_transport() override; + + const std::string& transport_name() const override; + bool writable() const override; + bool receiving() const override; + + private: + void set_receiving(bool receiving); + void set_writable(bool writable); + void set_dtls_state(DtlsTransportState state); + + // Forwards incoming packet up the stack. + void PropagateReadPacket(rtc::ArrayView data, + const int64_t& packet_time_us); + + // Signals SentPacket notification. + void PropagateOnSentNotification(const rtc::SentPacket& sent_packet); + + // Listens to read packet notifications from ICE (only used in bypass mode). + void OnReadPacket(rtc::PacketTransportInternal* transport, + const char* data, + size_t size, + const int64_t& packet_time_us, + int flags); + + void OnReadyToSend(rtc::PacketTransportInternal* transport); + void OnWritableState(rtc::PacketTransportInternal* transport); + void OnNetworkRouteChanged(absl::optional network_route); + void OnReceivingState(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; + void OnSentPacket(rtc::PacketTransportInternal* transport, + const rtc::SentPacket& sent_packet); + + rtc::ThreadChecker thread_checker_; + webrtc::CryptoOptions crypto_options_; + std::unique_ptr ice_transport_; + + std::unique_ptr datagram_transport_; + + // Current ICE writable state. Must be modified by calling set_ice_writable(), + // which propagates change notifications. + bool writable_ = false; + + // Current receiving state. Must be modified by calling set_receiving(), which + // propagates change notifications. + bool receiving_ = false; + + // Current DTLS state. Must be modified by calling set_dtls_state(), which + // propagates change notifications. + DtlsTransportState dtls_state_ = DTLS_TRANSPORT_NEW; + + webrtc::RtcEventLog* const event_log_; +}; + +} // namespace cricket + +#endif // P2P_BASE_DATAGRAM_DTLS_ADAPTOR_H_ diff --git a/p2p/base/dtls_transport.cc b/p2p/base/dtls_transport.cc index d3db35bfb9..46f0f99520 100644 --- a/p2p/base/dtls_transport.cc +++ b/p2p/base/dtls_transport.cc @@ -764,24 +764,6 @@ void DtlsTransport::set_writable(bool writable) { SignalWritableState(this); } -static webrtc::DtlsTransportState ConvertDtlsTransportState( - cricket::DtlsTransportState cricket_state) { - switch (cricket_state) { - case DtlsTransportState::DTLS_TRANSPORT_NEW: - return webrtc::DtlsTransportState::kNew; - case DtlsTransportState::DTLS_TRANSPORT_CONNECTING: - return webrtc::DtlsTransportState::kConnecting; - case DtlsTransportState::DTLS_TRANSPORT_CONNECTED: - return webrtc::DtlsTransportState::kConnected; - case DtlsTransportState::DTLS_TRANSPORT_CLOSED: - return webrtc::DtlsTransportState::kClosed; - case DtlsTransportState::DTLS_TRANSPORT_FAILED: - return webrtc::DtlsTransportState::kFailed; - } - RTC_NOTREACHED(); - return webrtc::DtlsTransportState::kNew; -} - void DtlsTransport::set_dtls_state(DtlsTransportState state) { if (dtls_state_ == state) { return; diff --git a/p2p/base/dtls_transport_internal.cc b/p2p/base/dtls_transport_internal.cc index 6997dbc702..dd23b1baa7 100644 --- a/p2p/base/dtls_transport_internal.cc +++ b/p2p/base/dtls_transport_internal.cc @@ -16,4 +16,22 @@ DtlsTransportInternal::DtlsTransportInternal() = default; DtlsTransportInternal::~DtlsTransportInternal() = default; +webrtc::DtlsTransportState ConvertDtlsTransportState( + cricket::DtlsTransportState cricket_state) { + switch (cricket_state) { + case DtlsTransportState::DTLS_TRANSPORT_NEW: + return webrtc::DtlsTransportState::kNew; + case DtlsTransportState::DTLS_TRANSPORT_CONNECTING: + return webrtc::DtlsTransportState::kConnecting; + case DtlsTransportState::DTLS_TRANSPORT_CONNECTED: + return webrtc::DtlsTransportState::kConnected; + case DtlsTransportState::DTLS_TRANSPORT_CLOSED: + return webrtc::DtlsTransportState::kClosed; + case DtlsTransportState::DTLS_TRANSPORT_FAILED: + return webrtc::DtlsTransportState::kFailed; + } + RTC_NOTREACHED(); + return webrtc::DtlsTransportState::kNew; +} + } // namespace cricket diff --git a/p2p/base/dtls_transport_internal.h b/p2p/base/dtls_transport_internal.h index b9c399d111..16e8b8151a 100644 --- a/p2p/base/dtls_transport_internal.h +++ b/p2p/base/dtls_transport_internal.h @@ -13,10 +13,13 @@ #include #include + #include #include #include "api/crypto/crypto_options.h" +#include "api/datagram_transport_interface.h" +#include "api/dtls_transport_interface.h" #include "api/scoped_refptr.h" #include "p2p/base/ice_transport_internal.h" #include "p2p/base/packet_transport_internal.h" @@ -41,6 +44,9 @@ enum DtlsTransportState { DTLS_TRANSPORT_FAILED, }; +webrtc::DtlsTransportState ConvertDtlsTransportState( + cricket::DtlsTransportState cricket_state); + enum PacketFlags { PF_NORMAL = 0x00, // A normal packet. PF_SRTP_BYPASS = 0x01, // An encrypted SRTP packet; bypass any additional @@ -59,6 +65,14 @@ class DtlsTransportInternal : public rtc::PacketTransportInternal { virtual const webrtc::CryptoOptions& crypto_options() const = 0; + // Returns datagram transport or nullptr if not using datagram transport. + // TODO(sukhanov): Make pure virtual. + // TODO(sukhanov): Consider moving ownership of datagram transport and ICE + // to JsepTransport. + virtual webrtc::DatagramTransportInterface* datagram_transport() { + return nullptr; + } + virtual DtlsTransportState dtls_state() const = 0; virtual int component() const = 0; diff --git a/pc/jsep_transport.cc b/pc/jsep_transport.cc index f82cf2a11a..26311d19db 100644 --- a/pc/jsep_transport.cc +++ b/pc/jsep_transport.cc @@ -116,6 +116,7 @@ JsepTransport::JsepTransport( : nullptr), media_transport_(std::move(media_transport)) { RTC_DCHECK(rtp_dtls_transport_); + RTC_DCHECK(!datagram_transport() || !media_transport_); // Verify the "only one out of these three can be set" invariant. if (unencrypted_rtp_transport_) { RTC_DCHECK(!sdes_transport); @@ -135,12 +136,13 @@ JsepTransport::JsepTransport( } JsepTransport::~JsepTransport() { + // Disconnect media transport state callbacks and make sure we delete media + // transports before ICE. if (media_transport_) { media_transport_->SetMediaTransportStateCallback(nullptr); - - // Make sure we delete media transport before ICE. media_transport_.reset(); } + // Clear all DtlsTransports. There may be pointers to these from // other places, so we can't assume they'll be deleted by the destructor. rtp_dtls_transport_->Clear(); @@ -717,5 +719,4 @@ void JsepTransport::OnStateChanged(webrtc::MediaTransportState state) { } SignalMediaTransportStateChanged(); } - } // namespace cricket diff --git a/pc/jsep_transport.h b/pc/jsep_transport.h index 0f314039d5..fce21be6e6 100644 --- a/pc/jsep_transport.h +++ b/pc/jsep_transport.h @@ -217,6 +217,12 @@ class JsepTransport : public sigslot::has_slots<>, return media_transport_.get(); } + // Returns datagram transport, if available. + webrtc::DatagramTransportInterface* datagram_transport() const { + rtc::CritScope scope(&accessor_lock_); + return rtp_dtls_transport_->internal()->datagram_transport(); + } + // Returns the latest media transport state. webrtc::MediaTransportState media_transport_state() const { rtc::CritScope scope(&accessor_lock_); @@ -332,6 +338,10 @@ class JsepTransport : public sigslot::has_slots<>, // If |media_transport_| is provided, this variable represents the state of // media transport. + // + // NOTE: datagram transport state is handled by DatagramDtlsAdaptor, because + // DatagramDtlsAdaptor owns DatagramTransport. This state only represents + // media transport. webrtc::MediaTransportState media_transport_state_ RTC_GUARDED_BY(accessor_lock_) = webrtc::MediaTransportState::kPending; diff --git a/pc/jsep_transport_controller.cc b/pc/jsep_transport_controller.cc index fd9551a328..55f1d1cfdb 100644 --- a/pc/jsep_transport_controller.cc +++ b/pc/jsep_transport_controller.cc @@ -15,6 +15,9 @@ #include "absl/algorithm/container.h" #include "absl/memory/memory.h" +#include "api/datagram_transport_interface.h" +#include "api/media_transport_interface.h" +#include "p2p/base/datagram_dtls_adaptor.h" #include "p2p/base/ice_transport_internal.h" #include "p2p/base/no_op_dtls_transport.h" #include "p2p/base/port.h" @@ -136,12 +139,42 @@ RtpTransportInternal* JsepTransportController::GetRtpTransport( return jsep_transport->rtp_transport(); } -MediaTransportInterface* JsepTransportController::GetMediaTransport( +MediaTransportConfig JsepTransportController::GetMediaTransportConfig( const std::string& mid) const { auto jsep_transport = GetJsepTransportForMid(mid); if (!jsep_transport) { + return MediaTransportConfig(); + } + + MediaTransportInterface* media_transport = nullptr; + if (config_.use_media_transport_for_media) { + media_transport = jsep_transport->media_transport(); + } + + DatagramTransportInterface* datagram_transport = + jsep_transport->datagram_transport(); + + // Media transport and datagram transports can not be used together. + RTC_DCHECK(!media_transport || !datagram_transport); + + if (media_transport) { + return MediaTransportConfig(media_transport); + } else if (datagram_transport) { + return MediaTransportConfig( + /*rtp_max_packet_size=*/datagram_transport->GetLargestDatagramSize()); + } else { + return MediaTransportConfig(); + } +} + +MediaTransportInterface* +JsepTransportController::GetMediaTransportForDataChannel( + const std::string& mid) const { + auto jsep_transport = GetJsepTransportForMid(mid); + if (!jsep_transport || !config_.use_media_transport_for_data_channels) { return nullptr; } + return jsep_transport->media_transport(); } @@ -403,7 +436,8 @@ void JsepTransportController::SetActiveResetSrtpParams( void JsepTransportController::SetMediaTransportSettings( bool use_media_transport_for_media, - bool use_media_transport_for_data_channels) { + bool use_media_transport_for_data_channels, + bool use_datagram_transport) { RTC_DCHECK(use_media_transport_for_media == config_.use_media_transport_for_media || jsep_transports_by_name_.empty()) @@ -419,6 +453,7 @@ void JsepTransportController::SetMediaTransportSettings( config_.use_media_transport_for_media = use_media_transport_for_media; config_.use_media_transport_for_data_channels = use_media_transport_for_data_channels; + config_.use_datagram_transport = use_datagram_transport; } std::unique_ptr @@ -439,16 +474,25 @@ JsepTransportController::CreateIceTransport(const std::string transport_name, std::unique_ptr JsepTransportController::CreateDtlsTransport( - std::unique_ptr ice) { + std::unique_ptr ice, + std::unique_ptr datagram_transport) { RTC_DCHECK(network_thread_->IsCurrent()); std::unique_ptr dtls; - // If media transport is used for both media and data channels, - // then we don't need to create DTLS. - // Otherwise, DTLS is still created. - if (config_.media_transport_factory && - config_.use_media_transport_for_media && - config_.use_media_transport_for_data_channels) { + + if (datagram_transport) { + RTC_DCHECK(config_.use_datagram_transport); + + // Create DTLS wrapper around DatagramTransportInterface. + dtls = absl::make_unique( + std::move(ice), std::move(datagram_transport), config_.crypto_options, + config_.event_log); + } else if (config_.media_transport_factory && + config_.use_media_transport_for_media && + config_.use_media_transport_for_data_channels) { + // If media transport is used for both media and data channels, + // then we don't need to create DTLS. + // Otherwise, DTLS is still created. dtls = absl::make_unique( std::move(ice), config_.crypto_options); } else if (config_.external_transport_factory) { @@ -1024,6 +1068,72 @@ JsepTransportController::MaybeCreateMediaTransport( return media_transport_result.MoveValue(); } +// TODO(sukhanov): Refactor to avoid code duplication for Media and Datagram +// transports setup. +std::unique_ptr +JsepTransportController::MaybeCreateDatagramTransport( + const cricket::ContentInfo& content_info, + const cricket::SessionDescription& description, + bool local) { + if (config_.media_transport_factory == nullptr) { + return nullptr; + } + + if (!config_.use_datagram_transport) { + return nullptr; + } + + // Caller (offerer) datagram transport. + if (local) { + if (offer_datagram_transport_) { + RTC_LOG(LS_INFO) << "Offered datagram transport has now been activated."; + return std::move(offer_datagram_transport_); + } else { + RTC_LOG(LS_INFO) + << "Not returning datagram transport. Either SDES wasn't enabled, or " + "datagram transport didn't return an offer earlier."; + return nullptr; + } + } + + // Remote offer. If no x-mt lines, do not create datagram transport. + if (description.MediaTransportSettings().empty()) { + return nullptr; + } + + // When bundle is enabled, two JsepTransports are created, and then + // the second transport is destroyed (right away). + // For datagram transport, we don't want to create the second + // datagram transport in the first place. + RTC_LOG(LS_INFO) << "Returning new, client datagram transport."; + + RTC_DCHECK(!local) + << "If datagram transport is used, you must call " + "GenerateOrGetLastMediaTransportOffer before SetLocalDescription. You " + "also must use kRtcpMuxPolicyRequire and kBundlePolicyMaxBundle with " + "datagram transport."; + MediaTransportSettings settings; + settings.is_caller = local; + settings.event_log = config_.event_log; + + // Assume there is only one media transport (or if more, use the first one). + if (!local && !description.MediaTransportSettings().empty() && + config_.media_transport_factory->GetTransportName() == + description.MediaTransportSettings()[0].transport_name) { + settings.remote_transport_parameters = + description.MediaTransportSettings()[0].transport_setting; + } + + auto datagram_transport_result = + config_.media_transport_factory->CreateDatagramTransport(network_thread_, + settings); + + // TODO(sukhanov): Proper error handling. + RTC_CHECK(datagram_transport_result.ok()); + + return datagram_transport_result.MoveValue(); +} + RTCError JsepTransportController::MaybeCreateJsepTransport( bool local, const cricket::ContentInfo& content_info, @@ -1052,8 +1162,15 @@ RTCError JsepTransportController::MaybeCreateJsepTransport( media_transport->Connect(ice.get()); } + std::unique_ptr datagram_transport = + MaybeCreateDatagramTransport(content_info, description, local); + if (datagram_transport) { + datagram_transport_created_once_ = true; + datagram_transport->Connect(ice.get()); + } + std::unique_ptr rtp_dtls_transport = - CreateDtlsTransport(std::move(ice)); + CreateDtlsTransport(std::move(ice), std::move(datagram_transport)); std::unique_ptr rtcp_dtls_transport; std::unique_ptr unencrypted_rtp_transport; @@ -1064,19 +1181,36 @@ RTCError JsepTransportController::MaybeCreateJsepTransport( PeerConnectionInterface::kRtcpMuxPolicyRequire && content_info.type == cricket::MediaProtocolType::kRtp) { RTC_DCHECK(media_transport == nullptr); + RTC_DCHECK(datagram_transport == nullptr); rtcp_dtls_transport = CreateDtlsTransport( - CreateIceTransport(content_info.name, /*rtcp=*/true)); + CreateIceTransport(content_info.name, /*rtcp=*/true), + /*datagram_transport=*/nullptr); } - // TODO(sukhanov): Do not create RTP/RTCP transports if media transport is - // used, and remove the no-op dtls transport when that's done. - if (config_.disable_encryption) { + if (datagram_transport) { + // TODO(sukhanov): We use unencrypted RTP transport over DatagramTransport, + // because MediaTransport encrypts. In the future we may want to + // implement our own version of RtpTransport over MediaTransport, because + // it will give us more control over things like: + // - Fusing + // - Rtp header compression + // - Handling Rtcp feedback. + RTC_LOG(LS_INFO) << "Creating UnencryptedRtpTransport, because datagram " + "transport is used."; + RTC_DCHECK(!rtcp_dtls_transport); + unencrypted_rtp_transport = CreateUnencryptedRtpTransport( + content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get()); + } else if (config_.disable_encryption) { + RTC_LOG(LS_INFO) + << "Creating UnencryptedRtpTransport, becayse encryption is disabled."; unencrypted_rtp_transport = CreateUnencryptedRtpTransport( content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get()); } else if (!content_desc->cryptos().empty()) { sdes_transport = CreateSdesTransport( content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get()); + RTC_LOG(LS_INFO) << "Creating SdesTransport."; } else { + RTC_LOG(LS_INFO) << "Creating DtlsSrtpTransport."; dtls_srtp_transport = CreateDtlsSrtpTransport( content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get()); } @@ -1087,6 +1221,7 @@ RTCError JsepTransportController::MaybeCreateJsepTransport( std::move(sdes_transport), std::move(dtls_srtp_transport), std::move(rtp_dtls_transport), std::move(rtcp_dtls_transport), std::move(media_transport)); + jsep_transport->SignalRtcpMuxActive.connect( this, &JsepTransportController::UpdateAggregateStates_n); jsep_transport->SignalMediaTransportStateChanged.connect( @@ -1508,20 +1643,25 @@ void JsepTransportController::OnDtlsHandshakeError( absl::optional JsepTransportController::GenerateOrGetLastMediaTransportOffer() { - if (media_transport_created_once_) { + if (media_transport_created_once_ || datagram_transport_created_once_) { RTC_LOG(LS_INFO) << "Not regenerating media transport for the new offer in " "existing session."; return media_transport_offer_settings_; } RTC_LOG(LS_INFO) << "Generating media transport offer!"; + + absl::optional transport_parameters; + // Check that media transport is supposed to be used. + // Note that ICE is not available when media transport is created. It will + // only be available in 'Connect'. This may be a potential server config, if + // we decide to use this peer connection as a caller, not as a callee. + // TODO(sukhanov): Avoid code duplication with CreateMedia/MediaTransport. if (config_.use_media_transport_for_media || config_.use_media_transport_for_data_channels) { RTC_DCHECK(config_.media_transport_factory != nullptr); - // ICE is not available when media transport is created. It will only be - // available in 'Connect'. This may be a potential server config, if we - // decide to use this peer connection as a caller, not as a callee. + RTC_DCHECK(!config_.use_datagram_transport); webrtc::MediaTransportSettings settings; settings.is_caller = true; settings.pre_shared_key = rtc::CreateRandomString(32); @@ -1532,19 +1672,37 @@ JsepTransportController::GenerateOrGetLastMediaTransportOffer() { if (media_transport_or_error.ok()) { offer_media_transport_ = std::move(media_transport_or_error.value()); + transport_parameters = + offer_media_transport_->GetTransportParametersOffer(); } else { RTC_LOG(LS_INFO) << "Unable to create media transport, error=" << media_transport_or_error.error().message(); } + } else if (config_.use_datagram_transport) { + webrtc::MediaTransportSettings settings; + settings.is_caller = true; + settings.pre_shared_key = rtc::CreateRandomString(32); + settings.event_log = config_.event_log; + auto datagram_transport_or_error = + config_.media_transport_factory->CreateDatagramTransport( + network_thread_, settings); + + if (datagram_transport_or_error.ok()) { + offer_datagram_transport_ = + std::move(datagram_transport_or_error.value()); + transport_parameters = + offer_datagram_transport_->GetTransportParametersOffer(); + } else { + RTC_LOG(LS_INFO) << "Unable to create media transport, error=" + << datagram_transport_or_error.error().message(); + } } - if (!offer_media_transport_) { - RTC_LOG(LS_INFO) << "Media transport doesn't exist"; + if (!offer_media_transport_ && !offer_datagram_transport_) { + RTC_LOG(LS_INFO) << "Media and data transports do not exist"; return absl::nullopt; } - absl::optional transport_parameters = - offer_media_transport_->GetTransportParametersOffer(); if (!transport_parameters) { RTC_LOG(LS_INFO) << "Media transport didn't generate the offer"; // Media transport didn't generate the offer, and is not supposed to be diff --git a/pc/jsep_transport_controller.h b/pc/jsep_transport_controller.h index fff08d1889..a79817c67b 100644 --- a/pc/jsep_transport_controller.h +++ b/pc/jsep_transport_controller.h @@ -19,6 +19,7 @@ #include "api/candidate.h" #include "api/crypto/crypto_options.h" +#include "api/media_transport_config.h" #include "api/media_transport_interface.h" #include "api/peer_connection_interface.h" #include "logging/rtc_event_log/rtc_event_log.h" @@ -93,6 +94,9 @@ class JsepTransportController : public sigslot::has_slots<> { // MediaTransportFactory is provided. bool use_rtp_media_transport = false; + // Use encrypted datagram transport to send packets. + bool use_datagram_transport = false; + // Optional media transport factory (experimental). If provided it will be // used to create media_transport (as long as either // |use_media_transport_for_media| or @@ -133,7 +137,16 @@ class JsepTransportController : public sigslot::has_slots<> { rtc::scoped_refptr LookupDtlsTransportByMid( const std::string& mid); - MediaTransportInterface* GetMediaTransport(const std::string& mid) const; + MediaTransportConfig GetMediaTransportConfig(const std::string& mid) const; + + MediaTransportInterface* GetMediaTransportForDataChannel( + const std::string& mid) const; + + // TODO(sukhanov): Deprecate, return only config. + MediaTransportInterface* GetMediaTransport(const std::string& mid) const { + return GetMediaTransportConfig(mid).media_transport; + } + MediaTransportState GetMediaTransportState(const std::string& mid) const; /********************* @@ -190,7 +203,8 @@ class JsepTransportController : public sigslot::has_slots<> { // you did not call 'GetMediaTransport' or 'MaybeCreateJsepTransport'. Once // Jsep transport is created, you can't change this setting. void SetMediaTransportSettings(bool use_media_transport_for_media, - bool use_media_transport_for_data_channels); + bool use_media_transport_for_data_channels, + bool use_datagram_transport); // If media transport is present enabled and supported, // when this method is called, it creates a media transport and generates its @@ -308,6 +322,17 @@ class JsepTransportController : public sigslot::has_slots<> { const cricket::ContentInfo& content_info, const cricket::SessionDescription& description, bool local); + + // Creates datagram transport if config wants to use it, and a=x-mt line is + // present for the current media transport. Returned + // DatagramTransportInterface is not connected, and must be connected to ICE. + // You must call |GenerateOrGetLastMediaTransportOffer| on the caller before + // calling MaybeCreateDatagramTransport. + std::unique_ptr + MaybeCreateDatagramTransport(const cricket::ContentInfo& content_info, + const cricket::SessionDescription& description, + bool local); + void MaybeDestroyJsepTransport(const std::string& mid); void DestroyAllJsepTransports_n(); @@ -320,7 +345,8 @@ class JsepTransportController : public sigslot::has_slots<> { bool local); std::unique_ptr CreateDtlsTransport( - std::unique_ptr ice); + std::unique_ptr ice, + std::unique_ptr datagram_transport); std::unique_ptr CreateIceTransport( const std::string transport_name, bool rtcp); @@ -399,6 +425,22 @@ class JsepTransportController : public sigslot::has_slots<> { absl::optional media_transport_offer_settings_; + // Early on in the call we don't know if datagram transport is going to be + // used, but we need to get the server-supported parameters to add to an SDP. + // This server datagram transport will be promoted to the used datagram + // transport after the local description is set, and the ownership will be + // transferred to the actual JsepTransport. This "offer" datagram transport is + // not created if it's done on the party that provides answer. This offer + // datagram transport is only created once at the beginning of the connection, + // and never again. + std::unique_ptr offer_datagram_transport_ = + nullptr; + + // Contains the offer of the |offer_datagram_transport_|, in case if it needs + // to be repeated. + absl::optional + datagram_transport_offer_settings_; + // When the new offer is regenerated (due to upgrade), we don't want to // re-create media transport. New streams might be created; but media // transport stays the same. This flag prevents re-creation of the transport @@ -411,6 +453,7 @@ class JsepTransportController : public sigslot::has_slots<> { // recreate the Offer (e.g. after adding streams in Plan B), and so we want to // prevent recreation of the media transport when that happens. bool media_transport_created_once_ = false; + bool datagram_transport_created_once_ = false; const cricket::SessionDescription* local_desc_ = nullptr; const cricket::SessionDescription* remote_desc_ = nullptr; diff --git a/pc/jsep_transport_controller_unittest.cc b/pc/jsep_transport_controller_unittest.cc index c0927b9db4..346168d1fa 100644 --- a/pc/jsep_transport_controller_unittest.cc +++ b/pc/jsep_transport_controller_unittest.cc @@ -442,7 +442,7 @@ TEST_F(JsepTransportControllerTest, .ok()); FakeMediaTransport* media_transport = static_cast( - transport_controller_->GetMediaTransport(kAudioMid1)); + transport_controller_->GetMediaTransportForDataChannel(kAudioMid1)); ASSERT_NE(nullptr, media_transport); @@ -451,7 +451,8 @@ TEST_F(JsepTransportControllerTest, EXPECT_TRUE(media_transport->pre_shared_key().has_value()); // Return nullptr for non-existing mids. - EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kVideoMid2)); + EXPECT_EQ(nullptr, + transport_controller_->GetMediaTransportForDataChannel(kVideoMid2)); EXPECT_EQ(cricket::ICE_CANDIDATE_COMPONENT_RTP, transport_controller_->GetDtlsTransport(kAudioMid1)->component()) @@ -563,8 +564,6 @@ TEST_F(JsepTransportControllerTest, GetMediaTransportInCallee) { EXPECT_EQ(absl::nullopt, media_transport->settings().pre_shared_key); EXPECT_TRUE(media_transport->is_connected()); - EXPECT_EQ("fake-remote-settings", - media_transport->remote_transport_parameters()); // Return nullptr for non-existing mids. EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kVideoMid2)); diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc index ca66a095b0..14c8683256 100644 --- a/pc/peer_connection.cc +++ b/pc/peer_connection.cc @@ -764,6 +764,7 @@ bool PeerConnectionInterface::RTCConfiguration::operator==( bool active_reset_srtp_params; bool use_media_transport; bool use_media_transport_for_data_channels; + bool use_datagram_transport; absl::optional crypto_options; bool offer_extmap_allow_mixed; }; @@ -822,6 +823,7 @@ bool PeerConnectionInterface::RTCConfiguration::operator==( use_media_transport == o.use_media_transport && use_media_transport_for_data_channels == o.use_media_transport_for_data_channels && + use_datagram_transport == o.use_datagram_transport && crypto_options == o.crypto_options && offer_extmap_allow_mixed == o.offer_extmap_allow_mixed; } @@ -1021,7 +1023,8 @@ bool PeerConnection::Initialize( #endif config.active_reset_srtp_params = configuration.active_reset_srtp_params; - if (configuration.use_media_transport || + if (configuration.use_datagram_transport || + configuration.use_media_transport || configuration.use_media_transport_for_data_channels) { if (!factory_->media_transport_factory()) { RTC_DCHECK(false) @@ -1051,6 +1054,7 @@ bool PeerConnection::Initialize( config.use_media_transport_for_media = configuration.use_media_transport; config.use_media_transport_for_data_channels = configuration.use_media_transport_for_data_channels; + config.use_datagram_transport = configuration.use_datagram_transport; config.media_transport_factory = factory_->media_transport_factory(); } @@ -3412,8 +3416,23 @@ bool PeerConnection::SetConfiguration(const RTCConfiguration& configuration, return SafeSetError(RTCErrorType::INVALID_MODIFICATION, error); } + if (local_description() && configuration.use_datagram_transport != + configuration_.use_datagram_transport) { + RTC_LOG(LS_ERROR) << "Can't change use_datagram_transport " + "after calling SetLocalDescription."; + return SafeSetError(RTCErrorType::INVALID_MODIFICATION, error); + } + + if (remote_description() && configuration.use_datagram_transport != + configuration_.use_datagram_transport) { + RTC_LOG(LS_ERROR) << "Can't change use_datagram_transport " + "after calling SetRemoteDescription."; + return SafeSetError(RTCErrorType::INVALID_MODIFICATION, error); + } + if (configuration.use_media_transport_for_data_channels || - configuration.use_media_transport) { + configuration.use_media_transport || + configuration.use_datagram_transport) { RTC_CHECK(configuration.bundle_policy == kBundlePolicyMaxBundle) << "Media transport requires MaxBundle policy."; } @@ -3506,7 +3525,8 @@ bool PeerConnection::SetConfiguration(const RTCConfiguration& configuration, transport_controller_->SetIceConfig(ParseIceConfig(modified_config)); transport_controller_->SetMediaTransportSettings( modified_config.use_media_transport, - modified_config.use_media_transport_for_data_channels); + modified_config.use_media_transport_for_data_channels, + modified_config.use_datagram_transport); if (configuration_.active_reset_srtp_params != modified_config.active_reset_srtp_params) { @@ -6317,15 +6337,13 @@ RTCError PeerConnection::CreateChannels(const SessionDescription& desc) { cricket::VoiceChannel* PeerConnection::CreateVoiceChannel( const std::string& mid) { RtpTransportInternal* rtp_transport = GetRtpTransport(mid); - MediaTransportInterface* media_transport = nullptr; - if (configuration_.use_media_transport) { - media_transport = GetMediaTransport(mid); - } + MediaTransportConfig media_transport_config = + transport_controller_->GetMediaTransportConfig(mid); cricket::VoiceChannel* voice_channel = channel_manager()->CreateVoiceChannel( call_ptr_, configuration_.media_config, rtp_transport, - MediaTransportConfig(media_transport), signaling_thread(), mid, - SrtpRequired(), GetCryptoOptions(), &ssrc_generator_, audio_options_); + media_transport_config, signaling_thread(), mid, SrtpRequired(), + GetCryptoOptions(), &ssrc_generator_, audio_options_); if (!voice_channel) { return nullptr; } @@ -6342,15 +6360,13 @@ cricket::VoiceChannel* PeerConnection::CreateVoiceChannel( cricket::VideoChannel* PeerConnection::CreateVideoChannel( const std::string& mid) { RtpTransportInternal* rtp_transport = GetRtpTransport(mid); - MediaTransportInterface* media_transport = nullptr; - if (configuration_.use_media_transport) { - media_transport = GetMediaTransport(mid); - } + MediaTransportConfig media_transport_config = + transport_controller_->GetMediaTransportConfig(mid); cricket::VideoChannel* video_channel = channel_manager()->CreateVideoChannel( call_ptr_, configuration_.media_config, rtp_transport, - MediaTransportConfig(media_transport), signaling_thread(), mid, - SrtpRequired(), GetCryptoOptions(), &ssrc_generator_, video_options_, + media_transport_config, signaling_thread(), mid, SrtpRequired(), + GetCryptoOptions(), &ssrc_generator_, video_options_, video_bitrate_allocator_factory_.get()); if (!video_channel) { return nullptr; @@ -6529,7 +6545,8 @@ void PeerConnection::OnSctpClosingProcedureComplete_n(int sid) { bool PeerConnection::SetupMediaTransportForDataChannels_n( const std::string& mid) { - media_transport_ = transport_controller_->GetMediaTransport(mid); + media_transport_ = + transport_controller_->GetMediaTransportForDataChannel(mid); if (!media_transport_) { RTC_LOG(LS_ERROR) << "Media transport is not available for data channels, mid=" << mid; @@ -6886,8 +6903,9 @@ bool PeerConnection::ReadyToUseRemoteCandidate( } bool PeerConnection::SrtpRequired() const { - return dtls_enabled_ || - webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED; + return !configuration_.use_datagram_transport && + (dtls_enabled_ || + webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED); } void PeerConnection::OnTransportControllerGatheringState( diff --git a/pc/peer_connection.h b/pc/peer_connection.h index d9c625cea1..7287b7c150 100644 --- a/pc/peer_connection.h +++ b/pc/peer_connection.h @@ -1079,22 +1079,6 @@ class PeerConnection : public PeerConnectionInternal, return rtp_transport; } - // Returns media transport, if PeerConnection was created with configuration - // to use media transport. Otherwise returns nullptr. - MediaTransportInterface* GetMediaTransport(const std::string& mid) - RTC_RUN_ON(signaling_thread()) { - auto media_transport = transport_controller_->GetMediaTransport(mid); - RTC_DCHECK((configuration_.use_media_transport || - configuration_.use_media_transport_for_data_channels) == - (media_transport != nullptr)) - << "configuration_.use_media_transport=" - << configuration_.use_media_transport - << ", configuration_.use_media_transport_for_data_channels=" - << configuration_.use_media_transport_for_data_channels - << ", (media_transport != nullptr)=" << (media_transport != nullptr); - return media_transport; - } - void UpdateNegotiationNeeded(); bool CheckIfNegotiationIsNeeded();