Allow non-identical datagram transport parameters.
Currently, datagram transports must report identical transport parameters in order to negotiate use of the datagram transport. This is not strictly necessary, they just need parameters that fit some notion of "compatability" (eg. both ends share some mutually-supported version of the datagram protocol). This change allows datagram transports to implement their own notion of compatible transport parameters, by adding a SetRemoteTransportParameters method to DatagramTransportInterface which checks if the remote parameters are compatible with the local endpoint and returns an error if they are not. Bug: webrtc:9719 Change-Id: I166c787b468b89d9082d7e3c9995a6ed50a1650a Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/167741 Commit-Queue: Bjorn Mellem <mellem@webrtc.org> Reviewed-by: Steve Anton <steveanton@webrtc.org> Cr-Commit-Position: refs/heads/master@{#30412}
This commit is contained in:
parent
4356490b7b
commit
0cda7b832a
@ -26,9 +26,14 @@ constexpr size_t kMaxFakeDatagramSize = 1000;
|
|||||||
// or sending data. Only used for tests that need to stub out a transport.
|
// or sending data. Only used for tests that need to stub out a transport.
|
||||||
class FakeDatagramTransport : public DatagramTransportInterface {
|
class FakeDatagramTransport : public DatagramTransportInterface {
|
||||||
public:
|
public:
|
||||||
FakeDatagramTransport(const MediaTransportSettings& settings,
|
FakeDatagramTransport(
|
||||||
std::string transport_parameters)
|
const MediaTransportSettings& settings,
|
||||||
: settings_(settings), transport_parameters_(transport_parameters) {}
|
std::string transport_parameters,
|
||||||
|
const std::function<bool(absl::string_view, absl::string_view)>&
|
||||||
|
are_parameters_compatible)
|
||||||
|
: settings_(settings),
|
||||||
|
transport_parameters_(transport_parameters),
|
||||||
|
are_parameters_compatible_(are_parameters_compatible) {}
|
||||||
|
|
||||||
~FakeDatagramTransport() override { RTC_DCHECK(!state_callback_); }
|
~FakeDatagramTransport() override { RTC_DCHECK(!state_callback_); }
|
||||||
|
|
||||||
@ -63,6 +68,16 @@ class FakeDatagramTransport : public DatagramTransportInterface {
|
|||||||
return transport_parameters_;
|
return transport_parameters_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RTCError SetRemoteTransportParameters(
|
||||||
|
absl::string_view remote_parameters) override {
|
||||||
|
if (are_parameters_compatible_(GetTransportParameters(),
|
||||||
|
remote_parameters)) {
|
||||||
|
return RTCError::OK();
|
||||||
|
}
|
||||||
|
return RTCError(RTCErrorType::UNSUPPORTED_PARAMETER,
|
||||||
|
"Incompatible remote transport parameters");
|
||||||
|
}
|
||||||
|
|
||||||
RTCError OpenChannel(int channel_id) override {
|
RTCError OpenChannel(int channel_id) override {
|
||||||
return RTCError(RTCErrorType::UNSUPPORTED_OPERATION);
|
return RTCError(RTCErrorType::UNSUPPORTED_OPERATION);
|
||||||
}
|
}
|
||||||
@ -94,6 +109,8 @@ class FakeDatagramTransport : public DatagramTransportInterface {
|
|||||||
private:
|
private:
|
||||||
const MediaTransportSettings settings_;
|
const MediaTransportSettings settings_;
|
||||||
const std::string transport_parameters_;
|
const std::string transport_parameters_;
|
||||||
|
const std::function<bool(absl::string_view, absl::string_view)>
|
||||||
|
are_parameters_compatible_;
|
||||||
|
|
||||||
rtc::PacketTransportInternal* packet_transport_ = nullptr;
|
rtc::PacketTransportInternal* packet_transport_ = nullptr;
|
||||||
MediaTransportStateCallback* state_callback_ = nullptr;
|
MediaTransportStateCallback* state_callback_ = nullptr;
|
||||||
|
|||||||
@ -51,11 +51,22 @@ class FakeMediaTransportFactory : public MediaTransportFactory {
|
|||||||
CreateDatagramTransport(rtc::Thread* network_thread,
|
CreateDatagramTransport(rtc::Thread* network_thread,
|
||||||
const MediaTransportSettings& settings) override {
|
const MediaTransportSettings& settings) override {
|
||||||
return std::unique_ptr<DatagramTransportInterface>(
|
return std::unique_ptr<DatagramTransportInterface>(
|
||||||
new FakeDatagramTransport(settings, transport_offer_.value_or("")));
|
new FakeDatagramTransport(settings, transport_offer_.value_or(""),
|
||||||
|
transport_parameters_comparison_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_transport_parameters_comparison(
|
||||||
|
std::function<bool(absl::string_view, absl::string_view)> comparison) {
|
||||||
|
transport_parameters_comparison_ = std::move(comparison);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const absl::optional<std::string> transport_offer_;
|
const absl::optional<std::string> transport_offer_;
|
||||||
|
std::function<bool(absl::string_view, absl::string_view)>
|
||||||
|
transport_parameters_comparison_ =
|
||||||
|
[](absl::string_view local, absl::string_view remote) {
|
||||||
|
return local == remote;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -57,6 +57,10 @@ class WrapperDatagramTransport : public DatagramTransportInterface {
|
|||||||
return wrapped_->GetTransportParameters();
|
return wrapped_->GetTransportParameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RTCError SetRemoteTransportParameters(absl::string_view parameters) override {
|
||||||
|
return wrapped_->SetRemoteTransportParameters(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
// Data channel overrides.
|
// Data channel overrides.
|
||||||
RTCError OpenChannel(int channel_id) override {
|
RTCError OpenChannel(int channel_id) override {
|
||||||
return wrapped_->OpenChannel(channel_id);
|
return wrapped_->OpenChannel(channel_id);
|
||||||
@ -299,6 +303,18 @@ MediaTransportPair::LoopbackDatagramTransport::GetTransportParameters() const {
|
|||||||
return transport_parameters_;
|
return transport_parameters_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RTCError
|
||||||
|
MediaTransportPair::LoopbackDatagramTransport::SetRemoteTransportParameters(
|
||||||
|
absl::string_view remote_parameters) {
|
||||||
|
RTC_DCHECK_RUN_ON(thread_);
|
||||||
|
if (transport_parameters_comparison_(GetTransportParameters(),
|
||||||
|
remote_parameters)) {
|
||||||
|
return RTCError::OK();
|
||||||
|
}
|
||||||
|
return RTCError(RTCErrorType::UNSUPPORTED_PARAMETER,
|
||||||
|
"Incompatible remote transport parameters");
|
||||||
|
}
|
||||||
|
|
||||||
RTCError MediaTransportPair::LoopbackDatagramTransport::OpenChannel(
|
RTCError MediaTransportPair::LoopbackDatagramTransport::OpenChannel(
|
||||||
int channel_id) {
|
int channel_id) {
|
||||||
return dc_transport_.OpenChannel(channel_id);
|
return dc_transport_.OpenChannel(channel_id);
|
||||||
|
|||||||
@ -115,6 +115,22 @@ class MediaTransportPair {
|
|||||||
first_datagram_transport_.set_transport_parameters(params);
|
first_datagram_transport_.set_transport_parameters(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetSecondDatagramTransportParameters(const std::string& params) {
|
||||||
|
second_datagram_transport_.set_transport_parameters(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetFirstDatagramTransportParametersComparison(
|
||||||
|
std::function<bool(absl::string_view, absl::string_view)> comparison) {
|
||||||
|
first_datagram_transport_.set_transport_parameters_comparison(
|
||||||
|
std::move(comparison));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSecondDatagramTransportParametersComparison(
|
||||||
|
std::function<bool(absl::string_view, absl::string_view)> comparison) {
|
||||||
|
second_datagram_transport_.set_transport_parameters_comparison(
|
||||||
|
std::move(comparison));
|
||||||
|
}
|
||||||
|
|
||||||
void FlushAsyncInvokes() {
|
void FlushAsyncInvokes() {
|
||||||
first_datagram_transport_.FlushAsyncInvokes();
|
first_datagram_transport_.FlushAsyncInvokes();
|
||||||
second_datagram_transport_.FlushAsyncInvokes();
|
second_datagram_transport_.FlushAsyncInvokes();
|
||||||
@ -186,6 +202,8 @@ class MediaTransportPair {
|
|||||||
size_t GetLargestDatagramSize() const override;
|
size_t GetLargestDatagramSize() const override;
|
||||||
void SetDatagramSink(DatagramSinkInterface* sink) override;
|
void SetDatagramSink(DatagramSinkInterface* sink) override;
|
||||||
std::string GetTransportParameters() const override;
|
std::string GetTransportParameters() const override;
|
||||||
|
RTCError SetRemoteTransportParameters(
|
||||||
|
absl::string_view remote_parameters) override;
|
||||||
|
|
||||||
// Data channel overrides.
|
// Data channel overrides.
|
||||||
RTCError OpenChannel(int channel_id) override;
|
RTCError OpenChannel(int channel_id) override;
|
||||||
@ -208,6 +226,15 @@ class MediaTransportPair {
|
|||||||
transport_parameters_ = value;
|
transport_parameters_ = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_transport_parameters_comparison(
|
||||||
|
std::function<bool(absl::string_view, absl::string_view)> comparison) {
|
||||||
|
thread_->Invoke<void>(
|
||||||
|
RTC_FROM_HERE, [this, comparison = std::move(comparison)] {
|
||||||
|
RTC_DCHECK_RUN_ON(thread_);
|
||||||
|
transport_parameters_comparison_ = std::move(comparison);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DeliverDatagram(rtc::CopyOnWriteBuffer buffer);
|
void DeliverDatagram(rtc::CopyOnWriteBuffer buffer);
|
||||||
|
|
||||||
@ -222,6 +249,9 @@ class MediaTransportPair {
|
|||||||
LoopbackDatagramTransport* other_;
|
LoopbackDatagramTransport* other_;
|
||||||
|
|
||||||
std::string transport_parameters_;
|
std::string transport_parameters_;
|
||||||
|
std::function<bool(absl::string_view, absl::string_view)>
|
||||||
|
transport_parameters_comparison_ RTC_GUARDED_BY(thread_) =
|
||||||
|
[](absl::string_view a, absl::string_view b) { return a == b; };
|
||||||
|
|
||||||
absl::optional<MediaTransportState> state_after_connect_;
|
absl::optional<MediaTransportState> state_after_connect_;
|
||||||
|
|
||||||
|
|||||||
@ -128,6 +128,22 @@ class DatagramTransportInterface : public DataChannelTransportInterface {
|
|||||||
// the client, possibly removing any fields or parameters which the client
|
// the client, possibly removing any fields or parameters which the client
|
||||||
// does not understand.
|
// does not understand.
|
||||||
virtual std::string GetTransportParameters() const = 0;
|
virtual std::string GetTransportParameters() const = 0;
|
||||||
|
|
||||||
|
// Sets remote transport parameters. |remote_params| is a serialized string
|
||||||
|
// of opaque parameters, understood by the datagram transport implementation.
|
||||||
|
// Returns an error if |remote_params| are not compatible with this transport.
|
||||||
|
//
|
||||||
|
// TODO(mellem): Make pure virtual. The default implementation maintains
|
||||||
|
// original negotiation behavior (negotiation falls back to RTP if the
|
||||||
|
// remote datagram transport fails to echo exactly the local parameters).
|
||||||
|
virtual RTCError SetRemoteTransportParameters(
|
||||||
|
absl::string_view remote_params) {
|
||||||
|
if (remote_params == GetTransportParameters()) {
|
||||||
|
return RTCError::OK();
|
||||||
|
}
|
||||||
|
return RTCError(RTCErrorType::UNSUPPORTED_PARAMETER,
|
||||||
|
"Local and remote transport parameters do not match");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -110,10 +110,10 @@ std::unique_ptr<TransportDescription> TransportDescriptionFactory::CreateAnswer(
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Answers may only attach opaque parameters that exactly match parameters
|
// Answers may only attach opaque parameters if the offer contained them as
|
||||||
// present in the offer. If the answerer cannot fully understand or accept
|
// well. The answer's parameters may differ, and it's up to the opaque
|
||||||
// the offered transport, it must reject it and fall back.
|
// transport implementation to decide if the difference is acceptable.
|
||||||
if (offer->opaque_parameters == options.opaque_parameters) {
|
if (offer->opaque_parameters && options.opaque_parameters) {
|
||||||
desc->opaque_parameters = options.opaque_parameters;
|
desc->opaque_parameters = options.opaque_parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -259,30 +259,6 @@ TEST_F(TransportDescriptionFactoryTest, TestAnswerNoOpaqueTransportParameters) {
|
|||||||
EXPECT_EQ(answer->opaque_parameters, absl::nullopt);
|
EXPECT_EQ(answer->opaque_parameters, absl::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TransportDescriptionFactoryTest,
|
|
||||||
TestAnswerDifferentOpaqueTransportParameters) {
|
|
||||||
OpaqueTransportParameters offer_params;
|
|
||||||
offer_params.protocol = "fake";
|
|
||||||
offer_params.parameters = "foobar";
|
|
||||||
|
|
||||||
TransportOptions options;
|
|
||||||
options.opaque_parameters = offer_params;
|
|
||||||
|
|
||||||
std::unique_ptr<TransportDescription> offer =
|
|
||||||
f1_.CreateOffer(options, NULL, &ice_credentials_);
|
|
||||||
|
|
||||||
OpaqueTransportParameters answer_params;
|
|
||||||
answer_params.protocol = "fake";
|
|
||||||
answer_params.parameters = "baz";
|
|
||||||
|
|
||||||
options.opaque_parameters = answer_params;
|
|
||||||
std::unique_ptr<TransportDescription> answer =
|
|
||||||
f2_.CreateAnswer(offer.get(), options, true, NULL, &ice_credentials_);
|
|
||||||
|
|
||||||
CheckDesc(answer.get(), "", "", "", "");
|
|
||||||
EXPECT_EQ(answer->opaque_parameters, absl::nullopt);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TransportDescriptionFactoryTest,
|
TEST_F(TransportDescriptionFactoryTest,
|
||||||
TestAnswerNoOpaqueTransportParametersInOffer) {
|
TestAnswerNoOpaqueTransportParametersInOffer) {
|
||||||
std::unique_ptr<TransportDescription> offer =
|
std::unique_ptr<TransportDescription> offer =
|
||||||
|
|||||||
@ -765,10 +765,19 @@ void JsepTransport::NegotiateDatagramTransport(SdpType type) {
|
|||||||
return; // No need to negotiate the use of datagram transport.
|
return; // No need to negotiate the use of datagram transport.
|
||||||
}
|
}
|
||||||
|
|
||||||
bool compatible_datagram_transport =
|
bool compatible_datagram_transport = false;
|
||||||
remote_description_->transport_desc.opaque_parameters &&
|
if (datagram_transport_ &&
|
||||||
remote_description_->transport_desc.opaque_parameters ==
|
local_description_->transport_desc.opaque_parameters &&
|
||||||
local_description_->transport_desc.opaque_parameters;
|
remote_description_->transport_desc.opaque_parameters) {
|
||||||
|
// If both descriptions have datagram transport parameters, and the remote
|
||||||
|
// parameters are accepted by the datagram transport, then use the datagram
|
||||||
|
// transport. Otherwise, fall back to RTP.
|
||||||
|
compatible_datagram_transport =
|
||||||
|
datagram_transport_
|
||||||
|
->SetRemoteTransportParameters(remote_description_->transport_desc
|
||||||
|
.opaque_parameters->parameters)
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
bool use_datagram_transport_for_media =
|
bool use_datagram_transport_for_media =
|
||||||
compatible_datagram_transport &&
|
compatible_datagram_transport &&
|
||||||
|
|||||||
@ -1116,8 +1116,19 @@ JsepTransportController::MaybeCreateDatagramTransport(
|
|||||||
config_.media_transport_factory->CreateDatagramTransport(network_thread_,
|
config_.media_transport_factory->CreateDatagramTransport(network_thread_,
|
||||||
settings);
|
settings);
|
||||||
|
|
||||||
// TODO(sukhanov): Proper error handling.
|
if (!datagram_transport_result.ok()) {
|
||||||
RTC_CHECK(datagram_transport_result.ok());
|
// Datagram transport negotiation will fail and we'll fall back to RTP.
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!datagram_transport_result.value()
|
||||||
|
->SetRemoteTransportParameters(
|
||||||
|
transport_description->opaque_parameters->parameters)
|
||||||
|
.ok()) {
|
||||||
|
// Datagram transport negotiation failed (parameters are incompatible).
|
||||||
|
// Fall back to RTP.
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
return datagram_transport_result.MoveValue();
|
return datagram_transport_result.MoveValue();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1827,6 +1827,65 @@ TEST_P(JsepTransportControllerDatagramTest, OfferHasWrongTransportName) {
|
|||||||
absl::nullopt);
|
absl::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_P(JsepTransportControllerDatagramTest, IncompatibleAnswer) {
|
||||||
|
// Transport will claim that no parameters are compatible, even if they match
|
||||||
|
// exactly.
|
||||||
|
fake_media_transport_factory_.set_transport_parameters_comparison(
|
||||||
|
[](absl::string_view, absl::string_view) { return false; });
|
||||||
|
|
||||||
|
cricket::OpaqueTransportParameters fake_params = CreateTransportParameters();
|
||||||
|
if (IsOfferer()) {
|
||||||
|
EXPECT_EQ(transport_controller_->GetTransportParameters(kAudioMid1),
|
||||||
|
fake_params);
|
||||||
|
EXPECT_EQ(transport_controller_->GetTransportParameters(kVideoMid1),
|
||||||
|
fake_params);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto offer = CreateSessionDescriptionForDatagramTransport(fake_params);
|
||||||
|
EXPECT_TRUE(SetDescription(SdpType::kOffer, offer.get()).ok());
|
||||||
|
|
||||||
|
auto answer = CreateSessionDescriptionForDatagramTransport(fake_params);
|
||||||
|
EXPECT_TRUE(SetDescription(SdpType::kAnswer, answer.get()).ok());
|
||||||
|
|
||||||
|
// The offerer and answerer have incompatible parameters, so the answerer
|
||||||
|
// rejects the offered parameters.
|
||||||
|
EXPECT_EQ(transport_controller_->GetTransportParameters(kAudioMid1),
|
||||||
|
absl::nullopt);
|
||||||
|
EXPECT_EQ(transport_controller_->GetTransportParameters(kVideoMid1),
|
||||||
|
absl::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(JsepTransportControllerDatagramTest, CompatibleAnswer) {
|
||||||
|
// Transport will claim that no parameters are compatible, even if they are
|
||||||
|
// completely different.
|
||||||
|
fake_media_transport_factory_.set_transport_parameters_comparison(
|
||||||
|
[](absl::string_view, absl::string_view) { return true; });
|
||||||
|
|
||||||
|
cricket::OpaqueTransportParameters fake_params = CreateTransportParameters();
|
||||||
|
if (IsOfferer()) {
|
||||||
|
EXPECT_EQ(transport_controller_->GetTransportParameters(kAudioMid1),
|
||||||
|
fake_params);
|
||||||
|
EXPECT_EQ(transport_controller_->GetTransportParameters(kVideoMid1),
|
||||||
|
fake_params);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto offer = CreateSessionDescriptionForDatagramTransport(fake_params);
|
||||||
|
EXPECT_TRUE(SetDescription(SdpType::kOffer, offer.get()).ok());
|
||||||
|
|
||||||
|
cricket::OpaqueTransportParameters answer_params;
|
||||||
|
answer_params.protocol = fake_params.protocol;
|
||||||
|
answer_params.parameters = "something different from offer";
|
||||||
|
auto answer = CreateSessionDescriptionForDatagramTransport(answer_params);
|
||||||
|
EXPECT_TRUE(SetDescription(SdpType::kAnswer, answer.get()).ok());
|
||||||
|
|
||||||
|
// The offerer and answerer have compatible parameters, so the answerer
|
||||||
|
// accepts the offered parameters.
|
||||||
|
EXPECT_EQ(transport_controller_->GetTransportParameters(kAudioMid1),
|
||||||
|
fake_params);
|
||||||
|
EXPECT_EQ(transport_controller_->GetTransportParameters(kVideoMid1),
|
||||||
|
fake_params);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_P(JsepTransportControllerDatagramTest, AnswerRejectsDatagram) {
|
TEST_P(JsepTransportControllerDatagramTest, AnswerRejectsDatagram) {
|
||||||
cricket::OpaqueTransportParameters fake_params = CreateTransportParameters();
|
cricket::OpaqueTransportParameters fake_params = CreateTransportParameters();
|
||||||
if (IsOfferer()) {
|
if (IsOfferer()) {
|
||||||
|
|||||||
@ -3696,6 +3696,180 @@ TEST_P(PeerConnectionIntegrationTest,
|
|||||||
ASSERT_TRUE(ExpectNewFrames(media_expectations));
|
ASSERT_TRUE(ExpectNewFrames(media_expectations));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests that the datagram transport to SCTP fallback works correctly when
|
||||||
|
// datagram transports do not advertise compatible transport parameters.
|
||||||
|
TEST_P(PeerConnectionIntegrationTest,
|
||||||
|
DatagramTransportIncompatibleParametersFallsBackToSctp) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
// By default, only equal parameters are compatible.
|
||||||
|
loopback_media_transports()->SetFirstDatagramTransportParameters("foo");
|
||||||
|
loopback_media_transports()->SetSecondDatagramTransportParameters("bar");
|
||||||
|
|
||||||
|
// Configure one endpoint to use datagram transport for data channels while
|
||||||
|
// the other does not.
|
||||||
|
ASSERT_TRUE(CreatePeerConnectionWrappersWithConfigAndMediaTransportFactory(
|
||||||
|
rtc_config, rtc_config, loopback_media_transports()->first_factory(),
|
||||||
|
loopback_media_transports()->second_factory()));
|
||||||
|
ConnectFakeSignaling();
|
||||||
|
|
||||||
|
// The caller offers a data channel using either datagram transport or SCTP.
|
||||||
|
caller()->CreateDataChannel();
|
||||||
|
caller()->AddAudioVideoTracks();
|
||||||
|
callee()->AddAudioVideoTracks();
|
||||||
|
caller()->CreateAndSetAndSignalOffer();
|
||||||
|
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
|
||||||
|
|
||||||
|
// Negotiation should fallback to SCTP, allowing the data channel to be
|
||||||
|
// established.
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Both endpoints should agree to use SCTP for data channels.
|
||||||
|
EXPECT_NE(nullptr, caller()->pc()->GetSctpTransport());
|
||||||
|
EXPECT_NE(nullptr, callee()->pc()->GetSctpTransport());
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Ensure that failure of the datagram negotiation doesn't impede media flow.
|
||||||
|
MediaExpectations media_expectations;
|
||||||
|
media_expectations.ExpectBidirectionalAudioAndVideo();
|
||||||
|
ASSERT_TRUE(ExpectNewFrames(media_expectations));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that the datagram transport to SCTP fallback works correctly when
|
||||||
|
// only the answerer believes datagram transport parameters are incompatible.
|
||||||
|
TEST_P(PeerConnectionIntegrationTest,
|
||||||
|
DatagramTransportIncompatibleParametersOnAnswererFallsBackToSctp) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
// By default, only equal parameters are compatible.
|
||||||
|
loopback_media_transports()->SetFirstDatagramTransportParameters("foo");
|
||||||
|
loopback_media_transports()->SetSecondDatagramTransportParameters("bar");
|
||||||
|
|
||||||
|
// Set the offerer to accept different parameters, while the answerer rejects
|
||||||
|
// them.
|
||||||
|
loopback_media_transports()->SetFirstDatagramTransportParametersComparison(
|
||||||
|
[](absl::string_view a, absl::string_view b) { return true; });
|
||||||
|
loopback_media_transports()->SetSecondDatagramTransportParametersComparison(
|
||||||
|
[](absl::string_view a, absl::string_view b) { return false; });
|
||||||
|
|
||||||
|
// Configure one endpoint to use datagram transport for data channels while
|
||||||
|
// the other does not.
|
||||||
|
ASSERT_TRUE(CreatePeerConnectionWrappersWithConfigAndMediaTransportFactory(
|
||||||
|
rtc_config, rtc_config, loopback_media_transports()->first_factory(),
|
||||||
|
loopback_media_transports()->second_factory()));
|
||||||
|
ConnectFakeSignaling();
|
||||||
|
|
||||||
|
// The caller offers a data channel using either datagram transport or SCTP.
|
||||||
|
caller()->CreateDataChannel();
|
||||||
|
caller()->AddAudioVideoTracks();
|
||||||
|
callee()->AddAudioVideoTracks();
|
||||||
|
caller()->CreateAndSetAndSignalOffer();
|
||||||
|
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
|
||||||
|
|
||||||
|
// Negotiation should fallback to SCTP, allowing the data channel to be
|
||||||
|
// established.
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Both endpoints should agree to use SCTP for data channels.
|
||||||
|
EXPECT_NE(nullptr, caller()->pc()->GetSctpTransport());
|
||||||
|
EXPECT_NE(nullptr, callee()->pc()->GetSctpTransport());
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Ensure that failure of the datagram negotiation doesn't impede media flow.
|
||||||
|
MediaExpectations media_expectations;
|
||||||
|
media_expectations.ExpectBidirectionalAudioAndVideo();
|
||||||
|
ASSERT_TRUE(ExpectNewFrames(media_expectations));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that the data channel transport works correctly when datagram
|
||||||
|
// transports provide different, but compatible, transport parameters.
|
||||||
|
TEST_P(PeerConnectionIntegrationTest,
|
||||||
|
DatagramTransportCompatibleParametersDoNotFallbackToSctp) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
// By default, only equal parameters are compatible.
|
||||||
|
loopback_media_transports()->SetFirstDatagramTransportParameters("foo");
|
||||||
|
loopback_media_transports()->SetSecondDatagramTransportParameters("bar");
|
||||||
|
|
||||||
|
// Change the comparison used to treat these transport parameters are
|
||||||
|
// compatible (on both sides).
|
||||||
|
loopback_media_transports()->SetFirstDatagramTransportParametersComparison(
|
||||||
|
[](absl::string_view a, absl::string_view b) { return true; });
|
||||||
|
loopback_media_transports()->SetSecondDatagramTransportParametersComparison(
|
||||||
|
[](absl::string_view a, absl::string_view b) { return true; });
|
||||||
|
|
||||||
|
ASSERT_TRUE(CreatePeerConnectionWrappersWithConfigAndMediaTransportFactory(
|
||||||
|
rtc_config, rtc_config, loopback_media_transports()->first_factory(),
|
||||||
|
loopback_media_transports()->second_factory()));
|
||||||
|
ConnectFakeSignaling();
|
||||||
|
|
||||||
|
// The caller offers a data channel using either datagram transport or SCTP.
|
||||||
|
caller()->CreateDataChannel();
|
||||||
|
caller()->AddAudioVideoTracks();
|
||||||
|
callee()->AddAudioVideoTracks();
|
||||||
|
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();
|
||||||
|
|
||||||
|
// Negotiation should succeed, allowing the data channel to be established.
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Both endpoints should agree to use datagram transport for data channels.
|
||||||
|
EXPECT_EQ(nullptr, caller()->pc()->GetSctpTransport());
|
||||||
|
EXPECT_EQ(nullptr, callee()->pc()->GetSctpTransport());
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Ensure that failure of the datagram negotiation doesn't impede media flow.
|
||||||
|
MediaExpectations media_expectations;
|
||||||
|
media_expectations.ExpectBidirectionalAudioAndVideo();
|
||||||
|
ASSERT_TRUE(ExpectNewFrames(media_expectations));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_P(PeerConnectionIntegrationTest,
|
TEST_P(PeerConnectionIntegrationTest,
|
||||||
DatagramTransportDataChannelWithMediaOnCaller) {
|
DatagramTransportDataChannelWithMediaOnCaller) {
|
||||||
// Configure the caller to attempt use of datagram transport for media and
|
// Configure the caller to attempt use of datagram transport for media and
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user