Allow receive-only use of datagram transport for data channels.
Adds a field trial and configuration parameter to control whether
datagram transport may be used for data channels in a receive-only
manner. By default, if use_datagram_transport_for_data_channels is
enabled, PeerConnection will create a datagram transport and offer its
use for outgoing calls as well as accept incoming offers with compatible
datagram transport parameters.
With this change, a receive_only mode is added for datagram transport
data channels. When receive_only is set, the PeerConnection will not
create or offer datagram transports for outgoing calls, but will accept
incoming calls that offer compatible datagram transport parameters.
Bug: webrtc:9719
Change-Id: I35667bcc408ea4bbc61155898e6d2472dd262711
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/154463
Reviewed-by: Seth Hampson <shampson@webrtc.org>
Commit-Queue: Bjorn Mellem <mellem@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29327}
diff --git a/api/peer_connection_interface.h b/api/peer_connection_interface.h
index f2cc696..a417641 100644
--- a/api/peer_connection_interface.h
+++ b/api/peer_connection_interface.h
@@ -632,6 +632,14 @@
// of SCTP-DTLS.
absl::optional<bool> use_datagram_transport_for_data_channels;
+ // If true, this PeerConnection will only use datagram transport for data
+ // channels when receiving an incoming offer that includes datagram
+ // transport parameters. It will not request use of a datagram transport
+ // when it creates the initial, outgoing offer.
+ // This setting only applies when |use_datagram_transport_for_data_channels|
+ // is true.
+ absl::optional<bool> use_datagram_transport_for_data_channels_receive_only;
+
// Defines advanced optional cryptographic settings related to SRTP and
// frame encryption for native WebRTC. Setting this will overwrite any
// settings set in PeerConnectionFactory (which is deprecated).
diff --git a/pc/jsep_transport_controller.cc b/pc/jsep_transport_controller.cc
index 75f5d9d..c9ff0a7 100644
--- a/pc/jsep_transport_controller.cc
+++ b/pc/jsep_transport_controller.cc
@@ -447,7 +447,8 @@
bool use_media_transport_for_media,
bool use_media_transport_for_data_channels,
bool use_datagram_transport,
- bool use_datagram_transport_for_data_channels) {
+ bool use_datagram_transport_for_data_channels,
+ bool use_datagram_transport_for_data_channels_receive_only) {
RTC_DCHECK(use_media_transport_for_media ==
config_.use_media_transport_for_media ||
jsep_transports_by_name_.empty())
@@ -466,6 +467,8 @@
config_.use_datagram_transport = use_datagram_transport;
config_.use_datagram_transport_for_data_channels =
use_datagram_transport_for_data_channels;
+ config_.use_datagram_transport_for_data_channels_receive_only =
+ use_datagram_transport_for_data_channels_receive_only;
}
std::unique_ptr<cricket::IceTransportInternal>
@@ -1795,6 +1798,10 @@
RTC_DCHECK(!local_desc_ && !remote_desc_)
<< "JsepTransport should exist for every mid once any description is set";
+ if (config_.use_datagram_transport_for_data_channels_receive_only) {
+ return absl::nullopt;
+ }
+
// Need to generate a transport for the offer.
if (!offer_datagram_transport_) {
webrtc::MediaTransportSettings settings;
diff --git a/pc/jsep_transport_controller.h b/pc/jsep_transport_controller.h
index 12bcebc..af3c82c 100644
--- a/pc/jsep_transport_controller.h
+++ b/pc/jsep_transport_controller.h
@@ -120,6 +120,11 @@
// Use datagram transport's implementation of data channels instead of SCTP.
bool use_datagram_transport_for_data_channels = false;
+ // Whether |use_datagram_transport_for_data_channels| applies to outgoing
+ // calls. If true, |use_datagram_transport_for_data_channels| applies only
+ // to incoming calls.
+ bool use_datagram_transport_for_data_channels_receive_only = 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
@@ -227,10 +232,12 @@
// media transport configuration on the jsep transport controller, as long as
// 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_datagram_transport,
- bool use_datagram_transport_for_data_channels);
+ void SetMediaTransportSettings(
+ bool use_media_transport_for_media,
+ bool use_media_transport_for_data_channels,
+ bool use_datagram_transport,
+ bool use_datagram_transport_for_data_channels,
+ bool use_datagram_transport_for_data_channels_receive_only);
// If media transport is present enabled and supported,
// when this method is called, it creates a media transport and generates its
diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc
index 4149081..2679800 100644
--- a/pc/peer_connection.cc
+++ b/pc/peer_connection.cc
@@ -778,6 +778,7 @@
bool use_media_transport_for_data_channels;
absl::optional<bool> use_datagram_transport;
absl::optional<bool> use_datagram_transport_for_data_channels;
+ absl::optional<bool> use_datagram_transport_for_data_channels_receive_only;
absl::optional<CryptoOptions> crypto_options;
bool offer_extmap_allow_mixed;
std::string turn_logging_id;
@@ -842,6 +843,8 @@
use_datagram_transport == o.use_datagram_transport &&
use_datagram_transport_for_data_channels ==
o.use_datagram_transport_for_data_channels &&
+ use_datagram_transport_for_data_channels_receive_only ==
+ o.use_datagram_transport_for_data_channels_receive_only &&
crypto_options == o.crypto_options &&
offer_extmap_allow_mixed == o.offer_extmap_allow_mixed &&
turn_logging_id == o.turn_logging_id;
@@ -1080,6 +1083,9 @@
datagram_transport_data_channel_config_.enabled &&
configuration.use_datagram_transport_for_data_channels.value_or(
datagram_transport_data_channel_config_.default_value);
+ use_datagram_transport_for_data_channels_receive_only_ =
+ configuration.use_datagram_transport_for_data_channels_receive_only
+ .value_or(datagram_transport_data_channel_config_.receive_only);
if (use_datagram_transport_ || use_datagram_transport_for_data_channels_ ||
configuration.use_media_transport ||
configuration.use_media_transport_for_data_channels) {
@@ -1114,6 +1120,8 @@
config.use_datagram_transport = use_datagram_transport_;
config.use_datagram_transport_for_data_channels =
use_datagram_transport_for_data_channels_;
+ config.use_datagram_transport_for_data_channels_receive_only =
+ use_datagram_transport_for_data_channels_receive_only_;
config.media_transport_factory = factory_->media_transport_factory();
}
@@ -3573,6 +3581,26 @@
"after calling SetRemoteDescription.");
}
+ if (local_description() &&
+ configuration.use_datagram_transport_for_data_channels_receive_only !=
+ configuration_
+ .use_datagram_transport_for_data_channels_receive_only) {
+ LOG_AND_RETURN_ERROR(
+ RTCErrorType::INVALID_MODIFICATION,
+ "Can't change use_datagram_transport_for_data_channels_receive_only "
+ "after calling SetLocalDescription.");
+ }
+
+ if (remote_description() &&
+ configuration.use_datagram_transport_for_data_channels_receive_only !=
+ configuration_
+ .use_datagram_transport_for_data_channels_receive_only) {
+ LOG_AND_RETURN_ERROR(
+ RTCErrorType::INVALID_MODIFICATION,
+ "Can't change use_datagram_transport_for_data_channels_receive_only "
+ "after calling SetRemoteDescription.");
+ }
+
if (configuration.use_media_transport_for_data_channels ||
configuration.use_media_transport ||
(configuration.use_datagram_transport &&
@@ -3616,6 +3644,8 @@
modified_config.use_datagram_transport = configuration.use_datagram_transport;
modified_config.use_datagram_transport_for_data_channels =
configuration.use_datagram_transport_for_data_channels;
+ modified_config.use_datagram_transport_for_data_channels_receive_only =
+ configuration.use_datagram_transport_for_data_channels_receive_only;
modified_config.turn_logging_id = configuration.turn_logging_id;
if (configuration != modified_config) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
@@ -3688,10 +3718,14 @@
datagram_transport_data_channel_config_.enabled &&
modified_config.use_datagram_transport_for_data_channels.value_or(
datagram_transport_data_channel_config_.default_value);
+ use_datagram_transport_for_data_channels_receive_only_ =
+ modified_config.use_datagram_transport_for_data_channels_receive_only
+ .value_or(datagram_transport_data_channel_config_.receive_only);
transport_controller_->SetMediaTransportSettings(
modified_config.use_media_transport,
modified_config.use_media_transport_for_data_channels,
- use_datagram_transport_, use_datagram_transport_for_data_channels_);
+ use_datagram_transport_, use_datagram_transport_for_data_channels_,
+ use_datagram_transport_for_data_channels_receive_only_);
if (configuration_.active_reset_srtp_params !=
modified_config.active_reset_srtp_params) {
diff --git a/pc/peer_connection.h b/pc/peer_connection.h
index 393beed..c783ae9 100644
--- a/pc/peer_connection.h
+++ b/pc/peer_connection.h
@@ -365,8 +365,10 @@
// Field-trial based configuration for datagram transport data channels.
struct DatagramTransportDataChannelConfig {
explicit DatagramTransportDataChannelConfig(const std::string& field_trial)
- : enabled("enabled", true), default_value("default_value", false) {
- ParseFieldTrial({&enabled, &default_value}, field_trial);
+ : enabled("enabled", true),
+ default_value("default_value", false),
+ receive_only("receive_only", false) {
+ ParseFieldTrial({&enabled, &default_value, &receive_only}, field_trial);
}
// Whether datagram transport data channel support is enabled at all.
@@ -382,6 +384,11 @@
// applications will use the datagram transport by default (but may still
// explicitly configure themselves not to use it through RTCConfiguration).
FieldTrialFlag default_value;
+
+ // Whether the datagram transport is enabled in receive-only mode. If true,
+ // and if the datagram transport is enabled, it will only be used when
+ // receiving incoming calls, not when placing outgoing calls.
+ FieldTrialFlag receive_only;
};
// Implements MessageHandler.
@@ -1196,7 +1203,8 @@
const DatagramTransportConfig datagram_transport_config_;
// Field-trial based configuration for datagram transport data channels.
- const DatagramTransportConfig datagram_transport_data_channel_config_;
+ const DatagramTransportDataChannelConfig
+ datagram_transport_data_channel_config_;
// Final, resolved value for whether datagram transport is in use.
bool use_datagram_transport_ RTC_GUARDED_BY(signaling_thread()) = false;
@@ -1206,6 +1214,10 @@
bool use_datagram_transport_for_data_channels_
RTC_GUARDED_BY(signaling_thread()) = false;
+ // Resolved value of whether to use data channels only for incoming calls.
+ bool use_datagram_transport_for_data_channels_receive_only_
+ RTC_GUARDED_BY(signaling_thread()) = false;
+
// Cache configuration_.use_media_transport so that we can access it from
// other threads.
// TODO(bugs.webrtc.org/9987): Caching just this bool and allowing the data
diff --git a/pc/peer_connection_integrationtest.cc b/pc/peer_connection_integrationtest.cc
index 465dca1..6b2d830 100644
--- a/pc/peer_connection_integrationtest.cc
+++ b/pc/peer_connection_integrationtest.cc
@@ -3647,8 +3647,101 @@
ASSERT_TRUE(ExpectNewFrames(media_expectations));
}
+// Tests that data channels use SCTP instead of datagram transport if datagram
+// transport is configured in receive-only mode on the caller.
+TEST_P(PeerConnectionIntegrationTest,
+ DatagramTransportDataChannelReceiveOnlyOnCallerUsesSctp) {
+ PeerConnectionInterface::RTCConfiguration rtc_config;
+ rtc_config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
+ rtc_config.bundle_policy = PeerConnectionInterface::kBundlePolicyMaxBundle;
+ rtc_config.use_datagram_transport_for_data_channels = true;
+ rtc_config.use_datagram_transport_for_data_channels_receive_only = true;
+
+ ASSERT_TRUE(CreatePeerConnectionWrappersWithConfigAndMediaTransportFactory(
+ rtc_config, rtc_config, loopback_media_transports()->first_factory(),
+ loopback_media_transports()->second_factory()));
+ ConnectFakeSignaling();
+
+ // The caller should offer a data channel using SCTP.
+ caller()->CreateDataChannel();
+ caller()->AddAudioVideoTracks();
+ callee()->AddAudioVideoTracks();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+
+ ASSERT_NE(nullptr, caller()->data_channel());
+ ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout);
+ EXPECT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
+ EXPECT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+
+ // SCTP transports should be present, since they are in use.
+ EXPECT_NE(caller()->pc()->GetSctpTransport(), nullptr);
+ EXPECT_NE(callee()->pc()->GetSctpTransport(), nullptr);
+
+ // Ensure data can be sent in both directions.
+ std::string data = "hello world";
+ caller()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(data, callee()->data_observer()->last_message(),
+ kDefaultTimeout);
+ callee()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(data, caller()->data_observer()->last_message(),
+ kDefaultTimeout);
+}
+
#endif // HAVE_SCTP
+// Tests that a callee configured for receive-only use of datagram transport
+// data channels accepts them on incoming calls.
+TEST_P(PeerConnectionIntegrationTest,
+ DatagramTransportDataChannelReceiveOnlyOnCallee) {
+ PeerConnectionInterface::RTCConfiguration offerer_config;
+ offerer_config.rtcp_mux_policy =
+ PeerConnectionInterface::kRtcpMuxPolicyRequire;
+ offerer_config.bundle_policy =
+ PeerConnectionInterface::kBundlePolicyMaxBundle;
+ offerer_config.use_datagram_transport_for_data_channels = true;
+
+ PeerConnectionInterface::RTCConfiguration answerer_config;
+ answerer_config.rtcp_mux_policy =
+ PeerConnectionInterface::kRtcpMuxPolicyRequire;
+ answerer_config.bundle_policy =
+ PeerConnectionInterface::kBundlePolicyMaxBundle;
+ answerer_config.use_datagram_transport_for_data_channels = true;
+ answerer_config.use_datagram_transport_for_data_channels_receive_only = true;
+
+ ASSERT_TRUE(CreatePeerConnectionWrappersWithConfigAndMediaTransportFactory(
+ offerer_config, answerer_config,
+ loopback_media_transports()->first_factory(),
+ loopback_media_transports()->second_factory()));
+ ConnectFakeSignaling();
+
+ caller()->CreateDataChannel();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+
+ // Ensure that the data channel transport is ready.
+ loopback_media_transports()->SetState(webrtc::MediaTransportState::kWritable);
+ loopback_media_transports()->FlushAsyncInvokes();
+
+ ASSERT_NE(nullptr, caller()->data_channel());
+ ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout);
+ EXPECT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
+ EXPECT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+
+ // SCTP transports should not be present, since datagram transport is used.
+ EXPECT_EQ(caller()->pc()->GetSctpTransport(), nullptr);
+ EXPECT_EQ(callee()->pc()->GetSctpTransport(), nullptr);
+
+ // Ensure data can be sent in both directions.
+ std::string data = "hello world";
+ caller()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(data, callee()->data_observer()->last_message(),
+ kDefaultTimeout);
+ callee()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(data, caller()->data_observer()->last_message(),
+ kDefaultTimeout);
+}
+
// This test sets up a call between two parties with a datagram transport data
// channel.
TEST_P(PeerConnectionIntegrationTest, DatagramTransportDataChannelEndToEnd) {