[Interop 2026] Apply simulcast scaling defaults Apply scale_resolution_down_by defaults to video encodings passed to AddTransceiver. If any encoding provides a value, fill missing values with 1.0 before trimming the list. Otherwise, assign descending powers of two after trimming. This aligns sender parameters with the WebRTC addTransceiver algorithm and fixes two Interop 2026 WPT subtests in Chromium. R=perkj@webrtc.org, tommi@webrtc.org Bug: chromium:344943229 Change-Id: I490175e44f1b840e537d4e24718ca7cbef45389a Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/497360 Reviewed-by: Henrik Boström <hbos@webrtc.org> Reviewed-by: Tomas Gunnarsson <tommi@webrtc.org> Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org> Cr-Commit-Position: refs/heads/main@{#48439}
diff --git a/AUTHORS b/AUTHORS index fc3625b..bc63173 100644 --- a/AUTHORS +++ b/AUTHORS
@@ -97,6 +97,7 @@ Maxim Potapov <vopatop.skam@gmail.com> Michael Iedema <michael@kapsulate.com> Michael Klingbeil <klingm@amazon.com> +Michael Reeves <michael.reeves077@gmail.com> Michał Zarach <michalzaq12@gmail.com> Michel Promonet <michel.promonet.1@gmail.com> Miguel Paris <mparisdiaz@gmail.com>
diff --git a/pc/BUILD.gn b/pc/BUILD.gn index 2430287..eb91c0f 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn
@@ -3263,6 +3263,7 @@ "../api/audio_codecs:builtin_audio_encoder_factory", "../api/units:data_rate", "../api/video:render_resolution", + "../api/video:video_codec_constants", "../api/video_codecs:video_codecs_api", "../api/video_codecs:video_decoder_factory_template", "../api/video_codecs:video_decoder_factory_template_dav1d_adapter",
diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc index 8c60c14..d91dd1a 100644 --- a/pc/peer_connection.cc +++ b/pc/peer_connection.cc
@@ -1064,6 +1064,20 @@ RtpParameters parameters; parameters.encodings = init.send_encodings; + const bool has_scale_resolution_down_by = + media_type == MediaType::VIDEO && + absl::c_any_of(parameters.encodings, + [](const RtpEncodingParameters& encoding) { + return encoding.scale_resolution_down_by.has_value(); + }); + if (has_scale_resolution_down_by) { + for (RtpEncodingParameters& encoding : parameters.encodings) { + if (!encoding.scale_resolution_down_by.has_value()) { + encoding.scale_resolution_down_by = 1.0; + } + } + } + // Encodings are dropped from the tail if too many are provided. size_t max_simulcast_streams = media_type == MediaType::VIDEO ? kMaxSimulcastStreams : 1u; @@ -1093,6 +1107,15 @@ parameters.encodings.push_back({}); } + if (media_type == MediaType::VIDEO && !has_scale_resolution_down_by) { + double scale_resolution_down_by = 1.0; + for (auto encoding = parameters.encodings.rbegin(); + encoding != parameters.encodings.rend(); ++encoding) { + encoding->scale_resolution_down_by = scale_resolution_down_by; + scale_resolution_down_by *= 2.0; + } + } + if (UnimplementedRtpParameterHasValue(parameters)) { return RTC_LOG_ERROR( RTCError(RTCErrorType::UNSUPPORTED_PARAMETER)
diff --git a/pc/peer_connection_encodings_integrationtest.cc b/pc/peer_connection_encodings_integrationtest.cc index 31c83fb..daa20af 100644 --- a/pc/peer_connection_encodings_integrationtest.cc +++ b/pc/peer_connection_encodings_integrationtest.cc
@@ -657,6 +657,14 @@ GetCapabilitiesAndRestrictToCodec(remote_pc_wrapper, "VP9"); transceiver->SetCodecPreferences(codecs); + scoped_refptr<RtpSenderInterface> sender = transceiver->sender(); + RtpParameters parameters = sender->GetParameters(); + // Keep this legacy SVC test independent of simulcast's default scaling. + for (RtpEncodingParameters& encoding : parameters.encodings) { + encoding.scale_resolution_down_by = 1.0; + } + ASSERT_TRUE(sender->SetParameters(parameters).ok()); + NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper); local_pc_wrapper->WaitForConnection(); remote_pc_wrapper->WaitForConnection(); @@ -678,7 +686,6 @@ // Despite SVC being used on a single RTP stream, GetParameters() returns the // three encodings that we configured earlier (this is not spec-compliant but // it is how legacy SVC behaves). - scoped_refptr<RtpSenderInterface> sender = transceiver->sender(); std::vector<RtpEncodingParameters> encodings = sender->GetParameters().encodings; ASSERT_EQ(encodings.size(), 3u); @@ -1229,6 +1236,10 @@ // disabling the bottom two spatial layers resulting in L1T3. scoped_refptr<RtpSenderInterface> sender = transceiver->sender(); RtpParameters parameters = sender->GetParameters(); + // Keep this legacy SVC test independent of simulcast's default scaling. + for (RtpEncodingParameters& encoding : parameters.encodings) { + encoding.scale_resolution_down_by = 1.0; + } parameters.encodings[0].active = false; parameters.encodings[1].active = false; parameters.encodings[2].active = true;
diff --git a/pc/peer_connection_rtp_unittest.cc b/pc/peer_connection_rtp_unittest.cc index c5a6dbb..daa826884 100644 --- a/pc/peer_connection_rtp_unittest.cc +++ b/pc/peer_connection_rtp_unittest.cc
@@ -36,6 +36,7 @@ #include "api/set_remote_description_observer_interface.h" #include "api/units/data_rate.h" #include "api/video/render_resolution.h" +#include "api/video/video_codec_constants.h" #include "api/video_codecs/sdp_video_format.h" #include "api/video_codecs/video_decoder_factory_template.h" #include "api/video_codecs/video_decoder_factory_template_dav1d_adapter.h" @@ -75,6 +76,7 @@ using RTCConfiguration = PeerConnectionInterface::RTCConfiguration; using ::testing::ElementsAre; +using ::testing::Eq; using ::testing::UnorderedElementsAre; using ::testing::Values; @@ -1843,6 +1845,57 @@ init.send_encodings = default_send_encodings; } +TEST_F(PeerConnectionRtpTestUnifiedPlan, + DefaultsMissingScaleResolutionDownByToOne) { + auto caller = CreatePeerConnection(); + + RtpTransceiverInit init; + init.send_encodings.resize(2); + init.send_encodings[1].scale_resolution_down_by = 3.0; + + auto result = caller->pc()->AddTransceiver(MediaType::VIDEO, init); + ASSERT_TRUE(result.ok()); + + const auto parameters = result.value()->sender()->GetParameters(); + ASSERT_EQ(parameters.encodings.size(), 2u); + EXPECT_THAT(parameters.encodings[0].scale_resolution_down_by, Eq(1.0)); + EXPECT_THAT(parameters.encodings[1].scale_resolution_down_by, Eq(3.0)); +} + +TEST_F(PeerConnectionRtpTestUnifiedPlan, + DefaultsScaleResolutionDownByToPowersOfTwo) { + auto caller = CreatePeerConnection(); + + RtpTransceiverInit init; + init.send_encodings.resize(2); + + auto result = caller->pc()->AddTransceiver(MediaType::VIDEO, init); + ASSERT_TRUE(result.ok()); + + const auto parameters = result.value()->sender()->GetParameters(); + ASSERT_EQ(parameters.encodings.size(), 2u); + EXPECT_THAT(parameters.encodings[0].scale_resolution_down_by, Eq(2.0)); + EXPECT_THAT(parameters.encodings[1].scale_resolution_down_by, Eq(1.0)); +} + +TEST_F(PeerConnectionRtpTestUnifiedPlan, + DefaultsScaleResolutionDownByBeforeTrimmingEncodings) { + auto caller = CreatePeerConnection(); + + RtpTransceiverInit init; + init.send_encodings.resize(kMaxSimulcastStreams + 1); + init.send_encodings.back().scale_resolution_down_by = 3.0; + + auto result = caller->pc()->AddTransceiver(MediaType::VIDEO, init); + ASSERT_TRUE(result.ok()); + + const auto parameters = result.value()->sender()->GetParameters(); + ASSERT_EQ(parameters.encodings.size(), kMaxSimulcastStreams); + for (const RtpEncodingParameters& encoding : parameters.encodings) { + EXPECT_THAT(encoding.scale_resolution_down_by, Eq(1.0)); + } +} + // Test that AddTransceiver transfers the send_encodings to the sender and they // are retained after SetLocalDescription(). TEST_F(PeerConnectionRtpTestUnifiedPlan, SendEncodingsPassedToSender) {