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}
This commit is contained in:
Bjorn A Mellem 2019-09-26 11:02:11 -07:00 committed by Commit Bot
parent 63173d5bef
commit 7da4e563b7
6 changed files with 170 additions and 9 deletions

View File

@ -632,6 +632,14 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface {
// 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).

View File

@ -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<cricket::IceTransportInternal>
@ -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;

View File

@ -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

View File

@ -778,6 +778,7 @@ bool PeerConnectionInterface::RTCConfiguration::operator==(
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 @@ 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) {

View File

@ -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

View File

@ -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) {