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 <sukhanov@webrtc.org>
Reviewed-by: Bjorn Mellem <mellem@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28047}
diff --git a/pc/jsep_transport_controller.cc b/pc/jsep_transport_controller.cc
index fd9551a..55f1d1c 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 @@
   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::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 @@
   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<cricket::IceTransportInternal>
@@ -439,16 +474,25 @@
 
 std::unique_ptr<cricket::DtlsTransportInternal>
 JsepTransportController::CreateDtlsTransport(
-    std::unique_ptr<cricket::IceTransportInternal> ice) {
+    std::unique_ptr<cricket::IceTransportInternal> ice,
+    std::unique_ptr<DatagramTransportInterface> datagram_transport) {
   RTC_DCHECK(network_thread_->IsCurrent());
 
   std::unique_ptr<cricket::DtlsTransportInternal> 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<cricket::DatagramDtlsAdaptor>(
+        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<cricket::NoOpDtlsTransport>(
         std::move(ice), config_.crypto_options);
   } else if (config_.external_transport_factory) {
@@ -1024,6 +1068,72 @@
   return media_transport_result.MoveValue();
 }
 
+// TODO(sukhanov): Refactor to avoid code duplication for Media and Datagram
+// transports setup.
+std::unique_ptr<webrtc::DatagramTransportInterface>
+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 @@
     media_transport->Connect(ice.get());
   }
 
+  std::unique_ptr<DatagramTransportInterface> datagram_transport =
+      MaybeCreateDatagramTransport(content_info, description, local);
+  if (datagram_transport) {
+    datagram_transport_created_once_ = true;
+    datagram_transport->Connect(ice.get());
+  }
+
   std::unique_ptr<cricket::DtlsTransportInternal> rtp_dtls_transport =
-      CreateDtlsTransport(std::move(ice));
+      CreateDtlsTransport(std::move(ice), std::move(datagram_transport));
 
   std::unique_ptr<cricket::DtlsTransportInternal> rtcp_dtls_transport;
   std::unique_ptr<RtpTransport> unencrypted_rtp_transport;
@@ -1064,19 +1181,36 @@
           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 @@
           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 @@
 
 absl::optional<cricket::SessionDescription::MediaTransportSetting>
 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<std::string> 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 @@
 
     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<std::string> 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