diff --git a/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc b/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc index c38f26708a..288894c9ca 100644 --- a/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc +++ b/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc @@ -490,6 +490,78 @@ TEST_F(TestVp9Impl, EncoderAcceptsSvcLikeSimulcast) { encoder_->InitEncode(&codec_settings_, kSettings)); } +TEST_F(TestVp9Impl, SvcSimulcastThenSinglecastWithCorrectSimulcastIndex) { + const int kTargetBitrate = 1200; + const int kMaxFramerate = 30; + + // Configure 720p 4:2:1 + codec_settings_.VP9()->numberOfTemporalLayers = 1; + codec_settings_.VP9()->numberOfSpatialLayers = 1; + codec_settings_.numberOfSimulcastStreams = 3; + codec_settings_.width = 1280; + codec_settings_.height = 720; + codec_settings_.simulcastStream[0].width = codec_settings_.width / 4; + codec_settings_.simulcastStream[0].height = codec_settings_.height / 4; + codec_settings_.simulcastStream[0].maxFramerate = kMaxFramerate; + codec_settings_.simulcastStream[0].minBitrate = kTargetBitrate / 2; + codec_settings_.simulcastStream[0].maxBitrate = kTargetBitrate; + codec_settings_.simulcastStream[0].targetBitrate = kTargetBitrate; + codec_settings_.simulcastStream[0].active = true; + codec_settings_.simulcastStream[1].width = codec_settings_.width / 2; + codec_settings_.simulcastStream[1].height = codec_settings_.height / 2; + codec_settings_.simulcastStream[1].maxFramerate = kMaxFramerate; + codec_settings_.simulcastStream[1].minBitrate = kTargetBitrate / 2; + codec_settings_.simulcastStream[1].maxBitrate = kTargetBitrate; + codec_settings_.simulcastStream[1].targetBitrate = kTargetBitrate; + codec_settings_.simulcastStream[1].active = true; + codec_settings_.simulcastStream[2].width = codec_settings_.width; + codec_settings_.simulcastStream[2].height = codec_settings_.height; + codec_settings_.simulcastStream[2].maxFramerate = kMaxFramerate; + codec_settings_.simulcastStream[2].minBitrate = kTargetBitrate / 2; + codec_settings_.simulcastStream[2].maxBitrate = kTargetBitrate; + codec_settings_.simulcastStream[2].targetBitrate = kTargetBitrate; + codec_settings_.simulcastStream[2].active = true; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder_->InitEncode(&codec_settings_, kSettings)); + + // Bitrate must be set for all layers to be produced. + VideoBitrateAllocation bitrate_allocation; + bitrate_allocation.SetBitrate(0, 0, kTargetBitrate * 1000); + bitrate_allocation.SetBitrate(1, 0, kTargetBitrate * 1000); + bitrate_allocation.SetBitrate(2, 0, kTargetBitrate * 1000); + encoder_->SetRates( + VideoEncoder::RateControlParameters(bitrate_allocation, kMaxFramerate)); + + // Encode a frame and confirm simulcast index is set for all layers. + { + SetWaitForEncodedFramesThreshold(3); + std::vector encoded_frame; + std::vector codec_specific_info; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder_->Encode(NextInputFrame(), nullptr)); + ASSERT_TRUE(WaitForEncodedFrames(&encoded_frame, &codec_specific_info)); + EXPECT_EQ(encoded_frame[0].SimulcastIndex().value_or(-1), 0); + EXPECT_EQ(encoded_frame[1].SimulcastIndex().value_or(-1), 1); + EXPECT_EQ(encoded_frame[2].SimulcastIndex().value_or(-1), 2); + } + + // Reconfigure 720p singlecast. + codec_settings_.numberOfSimulcastStreams = 1; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder_->InitEncode(&codec_settings_, kSettings)); + + // Encode a frame and confirm simulcast index is not set. + { + SetWaitForEncodedFramesThreshold(1); + std::vector encoded_frame; + std::vector codec_specific_info; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder_->Encode(NextInputFrame(), nullptr)); + ASSERT_TRUE(WaitForEncodedFrames(&encoded_frame, &codec_specific_info)); + EXPECT_FALSE(encoded_frame[0].SimulcastIndex().has_value()); + } +} + TEST_F(TestVp9Impl, EnableDisableSpatialLayers) { // Configure encoder to produce N spatial layers. Encode frames of layer 0 // then enable layer 1 and encode more frames and so on until layer N-1. diff --git a/pc/peer_connection_encodings_integrationtest.cc b/pc/peer_connection_encodings_integrationtest.cc index 8f57c15974..d97e06b856 100644 --- a/pc/peer_connection_encodings_integrationtest.cc +++ b/pc/peer_connection_encodings_integrationtest.cc @@ -2480,9 +2480,91 @@ TEST_P(PeerConnectionEncodingsIntegrationParameterizedTest, Resolution({.width = 960, .height = 540}), kLongTimeoutForRampingUp.ms()); - // Ensure 180p frames continue to be encoded post reconfiguration. - int frames_encoded = EncodedFrames(local_pc_wrapper, "q"); - ASSERT_TRUE_WAIT(EncodedFrames(local_pc_wrapper, "q") > frames_encoded, + // Ensure frames continue to be encoded post reconfiguration. + int q_frames_encoded = EncodedFrames(local_pc_wrapper, "q"); + ASSERT_TRUE_WAIT(EncodedFrames(local_pc_wrapper, "q") > q_frames_encoded, + kLongTimeoutForRampingUp.ms()); + int h_frames_encoded = EncodedFrames(local_pc_wrapper, "h"); + ASSERT_TRUE_WAIT(EncodedFrames(local_pc_wrapper, "h") > h_frames_encoded, + kLongTimeoutForRampingUp.ms()); + int f_frames_encoded = EncodedFrames(local_pc_wrapper, "f"); + ASSERT_TRUE_WAIT(EncodedFrames(local_pc_wrapper, "f") > f_frames_encoded, + kLongTimeoutForRampingUp.ms()); +} + +// Simulcast starting in 720p 4:2:1 then changing to {180p, 360p, 540p} using +// the `requested_resolution` API. +TEST_P(PeerConnectionEncodingsIntegrationParameterizedTest, + SimulcastRequestedResolutionNoLongerPowerOfTwo) { + rtc::scoped_refptr local_pc_wrapper = CreatePc(); + if (SkipTestDueToAv1Missing(local_pc_wrapper)) { + return; + } + rtc::scoped_refptr remote_pc_wrapper = CreatePc(); + ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); + + std::vector layers = + CreateLayers({"q", "h", "f"}, /*active=*/true); + rtc::scoped_refptr transceiver = + AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, + layers); + std::vector codecs = + GetCapabilitiesAndRestrictToCodec(remote_pc_wrapper, codec_name_); + transceiver->SetCodecPreferences(codecs); + rtc::scoped_refptr sender = transceiver->sender(); + + // Configure {180p, 360p, 720p}. + RtpParameters parameters = sender->GetParameters(); + ASSERT_THAT(parameters.encodings, SizeIs(3)); + parameters.encodings[0].scalability_mode = "L1T1"; + parameters.encodings[0].requested_resolution = {.width = 320, .height = 180}; + parameters.encodings[1].scalability_mode = "L1T1"; + parameters.encodings[1].requested_resolution = {.width = 640, .height = 360}; + parameters.encodings[2].scalability_mode = "L1T1"; + parameters.encodings[2].requested_resolution = {.width = 1280, .height = 720}; + sender->SetParameters(parameters); + + NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper); + local_pc_wrapper->WaitForConnection(); + remote_pc_wrapper->WaitForConnection(); + + // Wait for media to flow on all layers. + // Needed repro step of https://crbug.com/webrtc/369654168: When the same + // LibvpxVp9Encoder instance was used to first produce simulcast and later for + // a single encoding, the previously used simulcast index (= 2) would still be + // set when producing 180p since non-simulcast config does not reset this, + // resulting in the 180p encoding freezing and the 540p encoding having double + // frame rate and toggling between 180p and 540p in resolution. + ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 3u), + kLongTimeoutForRampingUp.ms()); + + // Configure {180p, 360p, 540p}. + parameters = sender->GetParameters(); + parameters.encodings[0].requested_resolution = {.width = 320, .height = 180}; + parameters.encodings[1].requested_resolution = {.width = 640, .height = 360}; + parameters.encodings[2].requested_resolution = {.width = 960, .height = 540}; + sender->SetParameters(parameters); + + // Wait for the new resolutions to be produced. + ASSERT_TRUE_WAIT(GetEncodingResolution(local_pc_wrapper, "q") == + Resolution({.width = 320, .height = 180}), + kLongTimeoutForRampingUp.ms()); + ASSERT_TRUE_WAIT(GetEncodingResolution(local_pc_wrapper, "h") == + Resolution({.width = 640, .height = 360}), + kLongTimeoutForRampingUp.ms()); + ASSERT_TRUE_WAIT(GetEncodingResolution(local_pc_wrapper, "f") == + Resolution({.width = 960, .height = 540}), + kLongTimeoutForRampingUp.ms()); + + // Ensure frames continue to be encoded post reconfiguration. + int q_frames_encoded = EncodedFrames(local_pc_wrapper, "q"); + ASSERT_TRUE_WAIT(EncodedFrames(local_pc_wrapper, "q") > q_frames_encoded, + kLongTimeoutForRampingUp.ms()); + int h_frames_encoded = EncodedFrames(local_pc_wrapper, "h"); + ASSERT_TRUE_WAIT(EncodedFrames(local_pc_wrapper, "h") > h_frames_encoded, + kLongTimeoutForRampingUp.ms()); + int f_frames_encoded = EncodedFrames(local_pc_wrapper, "f"); + ASSERT_TRUE_WAIT(EncodedFrames(local_pc_wrapper, "f") > f_frames_encoded, kLongTimeoutForRampingUp.ms()); }