Don't restrict max simulcast layers when requested_resolution is used.

The code that restricts the maximum number of simulcast layers based on
resolution is a spec-compliance bug and doesn't make much sense: if the
app asks for 3 layers it should get 3 layers. Since the app knows the
size of the track, it could very easily ask for 1 layer when resolution
is small if that is the behavior it wanted. If the app doesn't ask to
disable layers, WebRTC shouldn't disable layers on its behalf.

This behavior makes even less sense with this "new" API since the app
is explicitly controlling the send resolution in absolute terms.

Removing this behavior in the general case is out of scope since it
would break backwards compatibility, but since `requested_resolution`
has not been exposed to the web yet and existing usage is small, this
is an opportunity to fix the compliance bug for this API.

This CL makes the last web platform test for "scaleResolutionDownTo"
pass.

Bug: chromium:363544347
Change-Id: Ic6fadf3cad69d3beec4ae03d3d031e8062382ad9
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/363100
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43061}
This commit is contained in:
Henrik Boström 2024-09-20 11:45:17 +02:00 committed by WebRTC LUCI CQ
parent 2f106d683a
commit b6ee51b7a5
2 changed files with 68 additions and 3 deletions

View File

@ -2417,6 +2417,68 @@ TEST_P(PeerConnectionEncodingsIntegrationParameterizedTest,
EXPECT_THAT(*outbound_rtps[2]->scalability_mode, StrEq("L1T3"));
}
// The code path that disables layers based on resolution size should NOT run
// when `requested_resolution` is specified. (It shouldn't run in any case but
// that is an existing legacy code and non-compliance problem that we don't have
// to repeat here.)
TEST_P(PeerConnectionEncodingsIntegrationParameterizedTest,
LowResolutionSimulcastWithRequestedResolution) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
if (SkipTestDueToAv1Missing(local_pc_wrapper)) {
return;
}
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
std::vector<cricket::SimulcastLayer> layers =
CreateLayers({"q", "h", "f"}, /*active=*/true);
// Configure {20p,40p,80p} with 2:1 aspect ratio.
RtpTransceiverInit init;
RtpEncodingParameters encoding;
encoding.scalability_mode = "L1T3";
encoding.rid = "q";
encoding.requested_resolution = {.width = 40, .height = 20};
init.send_encodings.push_back(encoding);
encoding.rid = "h";
encoding.requested_resolution = {.width = 80, .height = 40};
init.send_encodings.push_back(encoding);
encoding.rid = "f";
encoding.requested_resolution = {.width = 160, .height = 80};
init.send_encodings.push_back(encoding);
rtc::scoped_refptr<MediaStreamInterface> stream =
local_pc_wrapper->GetUserMedia(
/*audio=*/false, {}, /*video=*/true, {.width = 160, .height = 80});
rtc::scoped_refptr<VideoTrackInterface> track = stream->GetVideoTracks()[0];
auto transceiver_or_error =
local_pc_wrapper->pc()->AddTransceiver(track, init);
ASSERT_TRUE(transceiver_or_error.ok());
rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
transceiver_or_error.value();
std::vector<RtpCodecCapability> codecs =
GetCapabilitiesAndRestrictToCodec(remote_pc_wrapper, codec_name_);
transceiver->SetCodecPreferences(codecs);
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
local_pc_wrapper->WaitForConnection();
remote_pc_wrapper->WaitForConnection();
// Wait for media to flow on all layers.
ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 3u),
kLongTimeoutForRampingUp.ms());
// q=20p, h=40p, f=80p.
ASSERT_TRUE_WAIT(GetEncodingResolution(local_pc_wrapper, "q") ==
Resolution({.width = 40, .height = 20}),
kLongTimeoutForRampingUp.ms());
ASSERT_TRUE_WAIT(GetEncodingResolution(local_pc_wrapper, "h") ==
Resolution({.width = 80, .height = 40}),
kLongTimeoutForRampingUp.ms());
ASSERT_TRUE_WAIT(GetEncodingResolution(local_pc_wrapper, "f") ==
Resolution({.width = 160, .height = 80}),
kLongTimeoutForRampingUp.ms());
}
TEST_P(PeerConnectionEncodingsIntegrationParameterizedTest,
SimulcastEncodingStopWhenRtpEncodingChangeToInactive) {
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();

View File

@ -527,9 +527,12 @@ std::vector<webrtc::Resolution> EncoderStreamFactory::GetStreamResolutions(
}
} else {
size_t min_num_layers = FindRequiredActiveLayers(encoder_config);
size_t max_num_layers = LimitSimulcastLayerCount(
min_num_layers, encoder_config.number_of_streams, width, height, trials,
encoder_config.codec_type);
size_t max_num_layers =
!encoder_config.HasRequestedResolution()
? LimitSimulcastLayerCount(
min_num_layers, encoder_config.number_of_streams, width,
height, trials, encoder_config.codec_type)
: encoder_config.number_of_streams;
RTC_DCHECK_LE(max_num_layers, encoder_config.number_of_streams);
const bool has_scale_resolution_down_by = absl::c_any_of(