From 6b539c83616655ca996a314df1a4fabc2cb75fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Bostr=C3=B6m?= Date: Fri, 3 Mar 2023 17:50:47 +0100 Subject: [PATCH] Add API layer tests for SVC reject and SVC fallback paths. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attempting to SVC can fail for two reasons: 1. If codec preferences does not contain a codec that supports SVC, setParameters() rejects, leaving scalabilityMode undefined. 2. If codec preferences does contain a codec that support SVC, setParameters() accepts the scalabilityMode, but if a codec is configured in response to negotiation that does not support SVC, fallback happens. In the 1) path, undefined scalabilityMode results in VP8 L1T1. In the 2) path, SVC fallback results in scalabilityMode being set to L1T2, resulting in VP8 L1T2. Whether we fail late or early resulting in different configurations may not be obvious so its good to test these. Bug: webrtc:14884 Change-Id: Ic5502b90c1628310a7a78ade2ad9fa0d81d91502 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/295872 Reviewed-by: Ilya Nikolaevskiy Reviewed-by: Evan Shrubsole Commit-Queue: Henrik Boström Cr-Commit-Position: refs/heads/main@{#39474} --- pc/peer_connection_simulcast_unittest.cc | 103 +++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/pc/peer_connection_simulcast_unittest.cc b/pc/peer_connection_simulcast_unittest.cc index ee6ce771bb..2c17868700 100644 --- a/pc/peer_connection_simulcast_unittest.cc +++ b/pc/peer_connection_simulcast_unittest.cc @@ -1154,6 +1154,109 @@ TEST_F(PeerConnectionSimulcastWithMediaFlowTests, EXPECT_THAT(*outbound_rtps[2]->scalability_mode, StrEq("L1T3")); } +TEST_F(PeerConnectionSimulcastWithMediaFlowTests, + SendingOneEncoding_VP8_RejectsSVCWhenNotPossibleAndDefaultsToL1T1) { + rtc::scoped_refptr local_pc_wrapper = CreatePc(); + rtc::scoped_refptr remote_pc_wrapper = CreatePc(); + ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); + + std::vector layers = CreateLayers({"f"}, /*active=*/true); + rtc::scoped_refptr transceiver = + AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, + layers); + // Restricting codecs restricts what SetParameters() will accept or reject. + std::vector codecs = + GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP8"); + transceiver->SetCodecPreferences(codecs); + // Attempt SVC (L3T3_KEY). This is not possible because only VP8 is up for + // negotiation and VP8 does not support it. + rtc::scoped_refptr sender = transceiver->sender(); + RtpParameters parameters = sender->GetParameters(); + ASSERT_EQ(parameters.encodings.size(), 1u); + parameters.encodings[0].scalability_mode = "L3T3_KEY"; + EXPECT_FALSE(sender->SetParameters(parameters).ok()); + // `scalability_mode` remains unset because SetParameters() failed. + parameters = sender->GetParameters(); + ASSERT_EQ(parameters.encodings.size(), 1u); + EXPECT_THAT(parameters.encodings[0].scalability_mode, Eq(absl::nullopt)); + + NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper, layers); + local_pc_wrapper->WaitForConnection(); + remote_pc_wrapper->WaitForConnection(); + + // Wait until media is flowing. + EXPECT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 1u), + kDefaultTimeout.ms()); + // When `scalability_mode` is not set, VP8 defaults to L1T1. + rtc::scoped_refptr report = GetStats(local_pc_wrapper); + std::vector outbound_rtps = + report->GetStatsOfType(); + ASSERT_THAT(outbound_rtps, SizeIs(1u)); + EXPECT_THAT(GetCurrentCodecMimeType(report, *outbound_rtps[0]), + StrCaseEq("video/VP8")); + EXPECT_THAT(*outbound_rtps[0]->scalability_mode, StrEq("L1T1")); + // GetParameters() confirms `scalability_mode` is still not set. + parameters = sender->GetParameters(); + ASSERT_EQ(parameters.encodings.size(), 1u); + EXPECT_THAT(parameters.encodings[0].scalability_mode, Eq(absl::nullopt)); +} + +TEST_F(PeerConnectionSimulcastWithMediaFlowTests, + SendingOneEncoding_VP8_FallbackFromSVCResultsInL1T2) { + rtc::scoped_refptr local_pc_wrapper = CreatePc(); + rtc::scoped_refptr remote_pc_wrapper = CreatePc(); + ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); + + std::vector layers = CreateLayers({"f"}, /*active=*/true); + rtc::scoped_refptr transceiver = + AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, + layers); + // Verify test assumption that VP8 is first in the list, but don't modify the + // codec preferences because we want the sender to think SVC is a possibility. + std::vector codecs = + local_pc_wrapper->pc_factory() + ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO) + .codecs; + EXPECT_THAT(codecs[0].name, StrCaseEq("VP8")); + // Attempt SVC (L3T3_KEY), which is not possible with VP8, but the sender does + // not yet know which codec we'll use so the parameters will be accepted. + rtc::scoped_refptr sender = transceiver->sender(); + RtpParameters parameters = sender->GetParameters(); + ASSERT_EQ(parameters.encodings.size(), 1u); + parameters.encodings[0].scalability_mode = "L3T3_KEY"; + EXPECT_TRUE(sender->SetParameters(parameters).ok()); + // Verify fallback has not happened yet. + parameters = sender->GetParameters(); + ASSERT_EQ(parameters.encodings.size(), 1u); + EXPECT_THAT(parameters.encodings[0].scalability_mode, + Optional(std::string("L3T3_KEY"))); + + // Negotiate, this results in VP8 being picked and fallback happening. + NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper, layers); + local_pc_wrapper->WaitForConnection(); + remote_pc_wrapper->WaitForConnection(); + // `scalaiblity_mode` is assigned the fallback value "L1T2" which is different + // than the default of absl::nullopt. + parameters = sender->GetParameters(); + ASSERT_EQ(parameters.encodings.size(), 1u); + EXPECT_THAT(parameters.encodings[0].scalability_mode, + Optional(std::string("L1T2"))); + + // Wait until media is flowing, no significant time needed because we only + // have one layer. + EXPECT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 1u), + kDefaultTimeout.ms()); + // GetStats() confirms "L1T2" is used which is different than the "L1T1" + // default or the "L3T3_KEY" that was attempted. + rtc::scoped_refptr report = GetStats(local_pc_wrapper); + std::vector outbound_rtps = + report->GetStatsOfType(); + ASSERT_THAT(outbound_rtps, SizeIs(1u)); + EXPECT_THAT(GetCurrentCodecMimeType(report, *outbound_rtps[0]), + StrCaseEq("video/VP8")); + EXPECT_THAT(*outbound_rtps[0]->scalability_mode, StrEq("L1T2")); +} + #if defined(WEBRTC_USE_H264) TEST_F(PeerConnectionSimulcastWithMediaFlowTests,