sdp: reject spec simulcast answers without the rid extension

which is mandatory to implement per
  https://datatracker.ietf.org/doc/html/rfc8853#section-5.5

BUG=chromium:1422258

Change-Id: I3639b15453aaa074fbe9f26b722f5997b439224a
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/306661
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Philipp Hancke <phancke@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#40208}
This commit is contained in:
Philipp Hancke 2023-06-01 19:07:50 +02:00 committed by WebRTC LUCI CQ
parent f785bd46e8
commit 3488726163
4 changed files with 85 additions and 6 deletions

View file

@ -41,7 +41,6 @@ struct RTC_EXPORT RtpTransceiverInit final {
// The added RtpTransceiver will be added to these streams.
std::vector<std::string> stream_ids;
// TODO(bugs.webrtc.org/7600): Not implemented.
std::vector<RtpEncodingParameters> send_encodings;
};

View file

@ -538,9 +538,9 @@ TEST_F(PeerConnectionSimulcastTests,
ValidateTransceiverParameters(transceiver, expected_layers);
}
// Tests that simulcast is disabled if the RID extension is not negotiated
// regardless of if the RIDs and simulcast attribute were negotiated properly.
TEST_F(PeerConnectionSimulcastTests, NegotiationDoesNotHaveRidExtension) {
// Tests that a simulcast answer is rejected if the RID extension is not
// negotiated.
TEST_F(PeerConnectionSimulcastTests, NegotiationDoesNotHaveRidExtensionFails) {
auto local = CreatePeerConnectionWrapper();
auto remote = CreatePeerConnectionWrapper();
auto layers = CreateLayers({"1", "2", "3"}, true);
@ -570,8 +570,7 @@ TEST_F(PeerConnectionSimulcastTests, NegotiationDoesNotHaveRidExtension) {
.receive_layers()
.GetAllLayers()
.size());
EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &err)) << err;
ValidateTransceiverParameters(transceiver, expected_layers);
EXPECT_FALSE(local->SetRemoteDescription(std::move(answer), &err)) << err;
}
TEST_F(PeerConnectionSimulcastTests, SimulcastAudioRejected) {

View file

@ -538,6 +538,30 @@ RTCError ValidateBundledRtpHeaderExtensions(
return RTCError::OK();
}
RTCError ValidateRtpHeaderExtensionsForSpecSimulcast(
const cricket::SessionDescription& description) {
for (const ContentInfo& content : description.contents()) {
if (content.type != MediaProtocolType::kRtp) {
continue;
}
const auto media_description = content.media_description();
if (!media_description->HasSimulcast()) {
continue;
}
auto extensions = media_description->rtp_header_extensions();
auto it = absl::c_find_if(extensions, [](const RtpExtension& ext) {
return ext.uri == RtpExtension::kRidUri;
});
if (it == extensions.end()) {
return RTCError(RTCErrorType::INVALID_PARAMETER,
"The media section with MID='" + content.mid() +
"' negotiates simulcast but does not negotiate "
"the RID RTP header extension.");
}
}
return RTCError::OK();
}
bool IsValidOfferToReceiveMedia(int value) {
typedef PeerConnectionInterface::RTCOfferAnswerOptions Options;
return (value >= Options::kUndefined) &&
@ -3602,6 +3626,12 @@ RTCError SdpOfferAnswerHandler::ValidateSessionDescription(
"which is not supported with Unified Plan.");
}
}
// Validate spec-simulcast which only works if the remote end negotiated the
// mid and rid header extension.
error = ValidateRtpHeaderExtensionsForSpecSimulcast(*sdesc->description());
if (!error.ok()) {
return error;
}
}
return RTCError::OK();

View file

@ -548,4 +548,55 @@ TEST_F(SdpOfferAnswerTest, RejectedDataChannelsDoGetReofferedWhenActive) {
#endif // WEBRTC_HAVE_SCTP
TEST_F(SdpOfferAnswerTest, SimulcastAnswerWithNoRidsIsRejected) {
auto pc = CreatePeerConnection();
RtpTransceiverInit init;
RtpEncodingParameters rid1;
rid1.rid = "1";
init.send_encodings.push_back(rid1);
RtpEncodingParameters rid2;
rid2.rid = "2";
init.send_encodings.push_back(rid2);
auto transceiver = pc->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
EXPECT_TRUE(pc->CreateOfferAndSetAsLocal());
auto mid = pc->pc()->local_description()->description()->contents()[0].mid();
// A SDP answer with simulcast but without mid/rid extensions.
std::string sdp =
"v=0\r\n"
"o=- 4131505339648218884 3 IN IP4 **-----**\r\n"
"s=-\r\n"
"t=0 0\r\n"
"a=ice-ufrag:zGWFZ+fVXDeN6UoI/136\r\n"
"a=ice-pwd:9AUNgUqRNI5LSIrC1qFD2iTR\r\n"
"a=fingerprint:sha-256 "
"AD:52:52:E0:B1:37:34:21:0E:15:8E:B7:56:56:7B:B4:39:0E:6D:1C:F5:84:A7:EE:"
"B5:27:3E:30:B1:7D:69:42\r\n"
"a=setup:passive\r\n"
"m=video 9 UDP/TLS/RTP/SAVPF 96\r\n"
"c=IN IP4 0.0.0.0\r\n"
"a=rtcp:9 IN IP4 0.0.0.0\r\n"
"a=mid:" +
mid +
"\r\n"
"a=recvonly\r\n"
"a=rtcp-mux\r\n"
"a=rtcp-rsize\r\n"
"a=rtpmap:96 VP8/90000\r\n"
"a=rid:1 recv\r\n"
"a=rid:2 recv\r\n"
"a=simulcast:recv 1;2\r\n";
std::string extensions =
"a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r\n"
"a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n";
auto answer = CreateSessionDescription(SdpType::kAnswer, sdp);
EXPECT_FALSE(pc->SetRemoteDescription(std::move(answer)));
auto answer_with_extensions =
CreateSessionDescription(SdpType::kAnswer, sdp + extensions);
EXPECT_TRUE(pc->SetRemoteDescription(std::move(answer_with_extensions)));
}
} // namespace webrtc