diff --git a/api/peer_connection_interface.h b/api/peer_connection_interface.h index f2cc696e62..a4176418a2 100644 --- a/api/peer_connection_interface.h +++ b/api/peer_connection_interface.h @@ -632,6 +632,14 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { // of SCTP-DTLS. absl::optional 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 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 75f5d9db8c..c9ff0a7dce 100644 --- a/pc/jsep_transport_controller.cc +++ b/pc/jsep_transport_controller.cc @@ -447,7 +447,8 @@ void JsepTransportController::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, + 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 @@ void JsepTransportController::SetMediaTransportSettings( 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 @@ -1795,6 +1798,10 @@ JsepTransportController::GetTransportParameters(const std::string& mid) { 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 12bcebc335..af3c82ce47 100644 --- a/pc/jsep_transport_controller.h +++ b/pc/jsep_transport_controller.h @@ -120,6 +120,11 @@ class JsepTransportController : public sigslot::has_slots<> { // 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 @@ class JsepTransportController : public sigslot::has_slots<> { // 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 414908150c..2679800020 100644 --- a/pc/peer_connection.cc +++ b/pc/peer_connection.cc @@ -778,6 +778,7 @@ bool PeerConnectionInterface::RTCConfiguration::operator==( bool use_media_transport_for_data_channels; absl::optional use_datagram_transport; absl::optional use_datagram_transport_for_data_channels; + absl::optional use_datagram_transport_for_data_channels_receive_only; absl::optional crypto_options; bool offer_extmap_allow_mixed; std::string turn_logging_id; @@ -842,6 +843,8 @@ bool PeerConnectionInterface::RTCConfiguration::operator==( 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 @@ bool PeerConnection::Initialize( 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 @@ bool PeerConnection::Initialize( 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 @@ RTCError PeerConnection::SetConfiguration( "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 @@ RTCError PeerConnection::SetConfiguration( 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 @@ RTCError PeerConnection::SetConfiguration( 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 393beedaa3..c783ae9e21 100644 --- a/pc/peer_connection.h +++ b/pc/peer_connection.h @@ -365,8 +365,10 @@ class PeerConnection : public PeerConnectionInternal, // 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 @@ class PeerConnection : public PeerConnectionInternal, // 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 @@ class PeerConnection : public PeerConnectionInternal, 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 @@ class PeerConnection : public PeerConnectionInternal, 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 465dca12bc..6b2d8303d1 100644 --- a/pc/peer_connection_integrationtest.cc +++ b/pc/peer_connection_integrationtest.cc @@ -3647,8 +3647,101 @@ TEST_P(PeerConnectionIntegrationTest, 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) {