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:
deadbeef 2017-05-25 09:38:55 -07:00 committed by Commit bot
parent edd6eea542
commit 8b7e9ad554
3 changed files with 105 additions and 28 deletions

View File

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

View File

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

View File

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