Support "UDP/DTLS/SCTP" and "TCP/DTLS/SCTP" profile strings.
This CL doesn't yet offer these protos; it just accepts them if they're seen in a remote offer. It also doesn't verify that the ICE candidate protocol matches the m= section protocol (UDP vs. TCP), since we don't do this elsewhere and don't really have a reason to care. This CL also adds an integration test that receives a spec-compliant SCTP offer and attempts to send data bidirectionally. BUG=webrtc:7706 Review-Url: https://codereview.webrtc.org/2902213002 Cr-Commit-Position: refs/heads/master@{#18265}
This commit is contained in:
parent
edd6eea542
commit
8b7e9ad554
@ -67,6 +67,36 @@ const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
|
||||
const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP";
|
||||
const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP";
|
||||
|
||||
// Note that the below functions support some protocol strings purely for
|
||||
// legacy compatibility, as required by JSEP in Section 5.1.2, Profile Names
|
||||
// and Interoperability.
|
||||
|
||||
static bool IsDtlsRtp(const std::string& protocol) {
|
||||
// Most-likely values first.
|
||||
return protocol == "UDP/TLS/RTP/SAVPF" || protocol == "TCP/TLS/RTP/SAVPF" ||
|
||||
protocol == "UDP/TLS/RTP/SAVP" || protocol == "TCP/TLS/RTP/SAVP";
|
||||
}
|
||||
|
||||
static bool IsPlainRtp(const std::string& protocol) {
|
||||
// Most-likely values first.
|
||||
return protocol == "RTP/SAVPF" || protocol == "RTP/AVPF" ||
|
||||
protocol == "RTP/SAVP" || protocol == "RTP/AVP";
|
||||
}
|
||||
|
||||
static bool IsDtlsSctp(const std::string& protocol) {
|
||||
return protocol == kMediaProtocolDtlsSctp ||
|
||||
protocol == kMediaProtocolUdpDtlsSctp ||
|
||||
protocol == kMediaProtocolTcpDtlsSctp;
|
||||
}
|
||||
|
||||
static bool IsPlainSctp(const std::string& protocol) {
|
||||
return protocol == kMediaProtocolSctp;
|
||||
}
|
||||
|
||||
static bool IsSctp(const std::string& protocol) {
|
||||
return IsPlainSctp(protocol) || IsDtlsSctp(protocol);
|
||||
}
|
||||
|
||||
RtpTransceiverDirection RtpTransceiverDirection::FromMediaContentDirection(
|
||||
MediaContentDirection md) {
|
||||
const bool send = (md == MD_SENDRECV || md == MD_SENDONLY);
|
||||
@ -398,11 +428,6 @@ class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> {
|
||||
private:
|
||||
};
|
||||
|
||||
static bool IsSctp(const MediaContentDescription* desc) {
|
||||
return ((desc->protocol() == kMediaProtocolSctp) ||
|
||||
(desc->protocol() == kMediaProtocolDtlsSctp));
|
||||
}
|
||||
|
||||
// Adds a StreamParams for each Stream in Streams with media type
|
||||
// media_type to content_description.
|
||||
// |current_params| - All currently known StreamParams of any media type.
|
||||
@ -413,7 +438,7 @@ static bool AddStreamParams(MediaType media_type,
|
||||
MediaContentDescriptionImpl<C>* content_description,
|
||||
const bool add_legacy_stream) {
|
||||
// SCTP streams are not negotiated using SDP/ContentDescriptions.
|
||||
if (IsSctp(content_description)) {
|
||||
if (IsSctp(content_description->protocol())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1085,26 +1110,6 @@ static bool CreateMediaContentAnswer(
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IsDtlsRtp(const std::string& protocol) {
|
||||
// Most-likely values first.
|
||||
return protocol == "UDP/TLS/RTP/SAVPF" || protocol == "TCP/TLS/RTP/SAVPF" ||
|
||||
protocol == "UDP/TLS/RTP/SAVP" || protocol == "TCP/TLS/RTP/SAVP";
|
||||
}
|
||||
|
||||
static bool IsPlainRtp(const std::string& protocol) {
|
||||
// Most-likely values first.
|
||||
return protocol == "RTP/SAVPF" || protocol == "RTP/AVPF" ||
|
||||
protocol == "RTP/SAVP" || protocol == "RTP/AVP";
|
||||
}
|
||||
|
||||
static bool IsDtlsSctp(const std::string& protocol) {
|
||||
return protocol == "DTLS/SCTP";
|
||||
}
|
||||
|
||||
static bool IsPlainSctp(const std::string& protocol) {
|
||||
return protocol == "SCTP";
|
||||
}
|
||||
|
||||
static bool IsMediaProtocolSupported(MediaType type,
|
||||
const std::string& protocol,
|
||||
bool secure_transport) {
|
||||
@ -1357,8 +1362,8 @@ SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
|
||||
video_added = true;
|
||||
} else if (IsMediaContentOfType(&*it, MEDIA_TYPE_DATA)) {
|
||||
MediaSessionOptions options_copy(options);
|
||||
if (IsSctp(static_cast<const MediaContentDescription*>(
|
||||
it->description))) {
|
||||
if (IsSctp(static_cast<const MediaContentDescription*>(it->description)
|
||||
->protocol())) {
|
||||
options_copy.data_channel_type = DCT_SCTP;
|
||||
}
|
||||
if (!AddDataContentForOffer(options_copy, current_description,
|
||||
@ -1797,6 +1802,9 @@ bool MediaSessionDescriptionFactory::AddDataContentForOffer(
|
||||
// before we call CreateMediaContentOffer. Otherwise,
|
||||
// CreateMediaContentOffer won't know this is SCTP and will
|
||||
// generate SSRCs rather than SIDs.
|
||||
// TODO(deadbeef): Offer kMediaProtocolUdpDtlsSctp (or TcpDtlsSctp), once
|
||||
// it's safe to do so. Older versions of webrtc would reject these
|
||||
// protocols; see https://bugs.chromium.org/p/webrtc/issues/detail?id=7706.
|
||||
data->set_protocol(
|
||||
secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
|
||||
} else {
|
||||
|
||||
@ -1008,6 +1008,41 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateDataAnswerWithoutSctpmap) {
|
||||
EXPECT_FALSE(dcd_answer->use_sctpmap());
|
||||
}
|
||||
|
||||
// Test that a valid answer will be created for "DTLS/SCTP", "UDP/DTLS/SCTP"
|
||||
// and "TCP/DTLS/SCTP" offers.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest,
|
||||
TestCreateDataAnswerToDifferentOfferedProtos) {
|
||||
// Need to enable DTLS offer/answer generation (disabled by default in this
|
||||
// test).
|
||||
f1_.set_secure(SEC_ENABLED);
|
||||
f2_.set_secure(SEC_ENABLED);
|
||||
tdf1_.set_secure(SEC_ENABLED);
|
||||
tdf2_.set_secure(SEC_ENABLED);
|
||||
|
||||
MediaSessionOptions opts;
|
||||
opts.data_channel_type = cricket::DCT_SCTP;
|
||||
std::unique_ptr<SessionDescription> offer(f1_.CreateOffer(opts, nullptr));
|
||||
ASSERT_TRUE(offer.get() != nullptr);
|
||||
ContentInfo* dc_offer = offer->GetContentByName("data");
|
||||
ASSERT_TRUE(dc_offer != nullptr);
|
||||
DataContentDescription* dcd_offer =
|
||||
static_cast<DataContentDescription*>(dc_offer->description);
|
||||
|
||||
std::vector<std::string> protos = {"DTLS/SCTP", "UDP/DTLS/SCTP",
|
||||
"TCP/DTLS/SCTP"};
|
||||
for (const std::string& proto : protos) {
|
||||
dcd_offer->set_protocol(proto);
|
||||
std::unique_ptr<SessionDescription> answer(
|
||||
f2_.CreateAnswer(offer.get(), opts, nullptr));
|
||||
const ContentInfo* dc_answer = answer->GetContentByName("data");
|
||||
ASSERT_TRUE(dc_answer != nullptr);
|
||||
const DataContentDescription* dcd_answer =
|
||||
static_cast<const DataContentDescription*>(dc_answer->description);
|
||||
EXPECT_FALSE(dc_answer->rejected);
|
||||
EXPECT_EQ(proto, dcd_answer->protocol());
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies that the order of the media contents in the offer is preserved in
|
||||
// the answer.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAnswerContentOrder) {
|
||||
|
||||
@ -2482,6 +2482,40 @@ TEST_F(PeerConnectionIntegrationTest, SctpDataChannelToAudioVideoUpgrade) {
|
||||
kMaxWaitForFramesMs);
|
||||
}
|
||||
|
||||
static void MakeSpecCompliantSctpOffer(cricket::SessionDescription* desc) {
|
||||
const ContentInfo* dc_offer = GetFirstDataContent(desc);
|
||||
ASSERT_NE(nullptr, dc_offer);
|
||||
cricket::DataContentDescription* dcd_offer =
|
||||
static_cast<cricket::DataContentDescription*>(dc_offer->description);
|
||||
dcd_offer->set_use_sctpmap(false);
|
||||
dcd_offer->set_protocol("UDP/DTLS/SCTP");
|
||||
}
|
||||
|
||||
// Test that the data channel works when a spec-compliant SCTP m= section is
|
||||
// offered (using "a=sctp-port" instead of "a=sctpmap", and using
|
||||
// "UDP/DTLS/SCTP" as the protocol).
|
||||
TEST_F(PeerConnectionIntegrationTest,
|
||||
DataChannelWorksWhenSpecCompliantSctpOfferReceived) {
|
||||
ASSERT_TRUE(CreatePeerConnectionWrappers());
|
||||
ConnectFakeSignaling();
|
||||
caller()->CreateDataChannel();
|
||||
caller()->SetGeneratedSdpMunger(MakeSpecCompliantSctpOffer);
|
||||
caller()->CreateAndSetAndSignalOffer();
|
||||
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
|
||||
ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout);
|
||||
EXPECT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
|
||||
EXPECT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
|
||||
|
||||
// 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
|
||||
|
||||
// Test that the ICE connection and gathering states eventually reach
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user