Update when/how `requested_resolution` throws for invalid parameters.
This CL makes `requested_resolution`, which is the C++ name for what
the spec calls scaleResolutionDownTo, align with the latest PR[1].
The PR says to ignore scaleResolutionDownBy when scaleResolutionDownTo
is specified as to be backwards compatible with scaleResolutionDownBy's
default scaling factors (e.g. 4:2:1). Ignoring is different than what
the code does today which is to throw an InvalidModificationError.
We don't want to throw or else get+setParameters() would throw by
default due to 4:2:1 defaults so the app would have to remember to
delete these attributes every time even though it never specified them
(Chrome has a bug here but fixing that would expose this problem, see
https://crbug.com/344943229).
[1] https://github.com/w3c/webrtc-extensions/pull/221
Bug: none
Change-Id: I21165c9b9f9ee7259d88b89f9ae58b862ea4521e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/362260
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43002}
diff --git a/media/base/media_engine.cc b/media/base/media_engine.cc
index acb95d1..dff4de0 100644
--- a/media/base/media_engine.cc
+++ b/media/base/media_engine.cc
@@ -145,6 +145,7 @@
const webrtc::FieldTrialsView& field_trials) {
using webrtc::RTCErrorType;
+ bool has_requested_resolution = false;
for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) {
if (rtp_parameters.encodings[i].bitrate_priority <= 0) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
@@ -183,11 +184,8 @@
}
}
- if (rtp_parameters.encodings[i].requested_resolution &&
- rtp_parameters.encodings[i].scale_resolution_down_by) {
- LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
- "Attempted to set scale_resolution_down_by and "
- "requested_resolution simultaniously.");
+ if (rtp_parameters.encodings[i].requested_resolution.has_value()) {
+ has_requested_resolution = true;
}
if (!field_trials.IsEnabled("WebRTC-MixedCodecSimulcast")) {
@@ -200,6 +198,16 @@
}
}
+ if (has_requested_resolution &&
+ absl::c_any_of(rtp_parameters.encodings,
+ [](const webrtc::RtpEncodingParameters& encoding) {
+ return !encoding.requested_resolution.has_value();
+ })) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
+ "If a resolution is specified on any encoding then "
+ "it must be specified on all encodings.");
+ }
+
return CheckScalabilityModeValues(rtp_parameters, send_codecs, send_codec);
}
diff --git a/pc/peer_connection_encodings_integrationtest.cc b/pc/peer_connection_encodings_integrationtest.cc
index bce27fc..552df13 100644
--- a/pc/peer_connection_encodings_integrationtest.cc
+++ b/pc/peer_connection_encodings_integrationtest.cc
@@ -187,6 +187,24 @@
local_pc_wrapper.get(), &PeerConnectionTestWrapper::AddIceCandidate);
}
+ // Negotiate without any tweaks (does not work for simulcast loopback).
+ void Negotiate(
+ rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper,
+ rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper) {
+ std::unique_ptr<SessionDescriptionInterface> offer =
+ CreateOffer(local_pc_wrapper);
+ rtc::scoped_refptr<MockSetSessionDescriptionObserver> p1 =
+ SetLocalDescription(local_pc_wrapper, offer.get());
+ rtc::scoped_refptr<MockSetSessionDescriptionObserver> p2 =
+ SetRemoteDescription(remote_pc_wrapper, offer.get());
+ EXPECT_TRUE(Await({p1, p2}));
+ std::unique_ptr<SessionDescriptionInterface> answer =
+ CreateAnswer(remote_pc_wrapper);
+ p1 = SetLocalDescription(remote_pc_wrapper, answer.get());
+ p2 = SetRemoteDescription(local_pc_wrapper, answer.get());
+ EXPECT_TRUE(Await({p1, p2}));
+ }
+
void NegotiateWithSimulcastTweaks(
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper,
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper) {
@@ -2098,6 +2116,86 @@
ASSERT_TRUE(transceiver_or_error.ok());
}
+TEST_F(PeerConnectionEncodingsIntegrationTest,
+ RequestedResolutionParameterChecking) {
+ rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper = CreatePc();
+
+ // AddTransceiver: If `requested_resolution` is specified on any encoding it
+ // must be specified on all encodings.
+ RtpTransceiverInit init;
+ RtpEncodingParameters encoding;
+ encoding.requested_resolution = std::nullopt;
+ init.send_encodings.push_back(encoding);
+ encoding.requested_resolution = {.width = 1280, .height = 720};
+ init.send_encodings.push_back(encoding);
+ auto transceiver_or_error =
+ pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
+ EXPECT_FALSE(transceiver_or_error.ok());
+ EXPECT_EQ(transceiver_or_error.error().type(),
+ RTCErrorType::UNSUPPORTED_OPERATION);
+
+ // AddTransceiver: Specifying both `requested_resolution` and
+ // `scale_resolution_down_by` is allowed (the latter is ignored).
+ init.send_encodings[0].requested_resolution = {.width = 640, .height = 480};
+ init.send_encodings[0].scale_resolution_down_by = 1.0;
+ init.send_encodings[1].requested_resolution = {.width = 1280, .height = 720};
+ init.send_encodings[1].scale_resolution_down_by = 2.0;
+ transceiver_or_error =
+ pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
+ ASSERT_TRUE(transceiver_or_error.ok());
+
+ // SetParameters: If `requested_resolution` is specified on any encoding it
+ // must be specified on all encodings.
+ auto sender = transceiver_or_error.value()->sender();
+ auto parameters = sender->GetParameters();
+ parameters.encodings[0].requested_resolution = {.width = 640, .height = 480};
+ parameters.encodings[1].requested_resolution = std::nullopt;
+ auto error = sender->SetParameters(parameters);
+ EXPECT_FALSE(error.ok());
+ EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
+
+ // SetParameters: Specifying both `requested_resolution` and
+ // `scale_resolution_down_by` is allowed (the latter is ignored).
+ parameters = sender->GetParameters();
+ parameters.encodings[0].requested_resolution = {.width = 640, .height = 480};
+ parameters.encodings[0].scale_resolution_down_by = 2.0;
+ parameters.encodings[1].requested_resolution = {.width = 1280, .height = 720};
+ parameters.encodings[1].scale_resolution_down_by = 1.0;
+ error = sender->SetParameters(parameters);
+ EXPECT_TRUE(error.ok());
+}
+
+TEST_F(PeerConnectionEncodingsIntegrationTest,
+ ScaleResolutionDownByIsIgnoredWhenRequestedResolutionIsSpecified) {
+ rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
+ rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
+
+ rtc::scoped_refptr<MediaStreamInterface> stream =
+ local_pc_wrapper->GetUserMedia(
+ /*audio=*/false, {}, /*video=*/true, {.width = 640, .height = 360});
+ rtc::scoped_refptr<VideoTrackInterface> track = stream->GetVideoTracks()[0];
+
+ // Configure contradicting scaling factors (180p vs 360p).
+ RtpTransceiverInit init;
+ RtpEncodingParameters encoding;
+ encoding.scale_resolution_down_by = 2.0;
+ encoding.requested_resolution = {.width = 640, .height = 360};
+ init.send_encodings.push_back(encoding);
+ auto transceiver_or_error =
+ local_pc_wrapper->pc()->AddTransceiver(track, init);
+
+ // Negotiate singlecast.
+ ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
+ Negotiate(local_pc_wrapper, remote_pc_wrapper);
+
+ // Confirm 640x360 is sent.
+ // If `scale_resolution_down_by` was not ignored we would never ramp up to
+ // full resolution.
+ ASSERT_TRUE_WAIT(GetEncodingResolution(local_pc_wrapper) ==
+ (Resolution{.width = 640, .height = 360}),
+ kLongTimeoutForRampingUp.ms());
+}
+
// Tests that use the standard path (specifying both `scalability_mode` and
// `scale_resolution_down_by` or `requested_resolution`) should pass for all
// codecs.