From 4d4f62f6e7f6c55393e2d23386997e59d70d67b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=85sa=20Persson?= Date: Sun, 12 Sep 2021 16:14:48 +0200 Subject: [PATCH] VideoSendStreamTest: Add tests for encoder reconfiguration. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: none Change-Id: I1d976eb77357c7050ed6ca7d0eee9153f9ef0251 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/231000 Reviewed-by: Erik Språng Commit-Queue: Åsa Persson Cr-Commit-Position: refs/heads/main@{#34978} --- media/engine/webrtc_video_engine.cc | 10 +- video/video_stream_encoder.cc | 4 - video/video_stream_encoder_unittest.cc | 156 +++++++++++++++++++++++-- 3 files changed, 151 insertions(+), 19 deletions(-) diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc index a20b37e98a..b7c998393d 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc @@ -2340,19 +2340,15 @@ webrtc::RTCError WebRtcVideoChannel::WebRtcVideoSendStream::SetRtpParameters( new_degradation_preference = true; } - // TODO(bugs.webrtc.org/8807): The bitrate priority really doesn't require an - // entire encoder reconfiguration, it just needs to update the bitrate - // allocator. + // Some fields (e.g. bitrate priority) only need to update the bitrate + // allocator which is updated via ReconfigureEncoder (however, note that the + // actual encoder should only be reconfigured if needed). bool reconfigure_encoder = new_param || (new_parameters.encodings[0].bitrate_priority != rtp_parameters_.encodings[0].bitrate_priority) || new_parameters.encodings[0].scalability_mode != rtp_parameters_.encodings[0].scalability_mode; - // TODO(bugs.webrtc.org/8807): The active field as well should not require - // a full encoder reconfiguration, but it needs to update both the bitrate - // allocator and the video bitrate allocator. - // // Note that the simulcast encoder adapter relies on the fact that layers // de/activation triggers encoder reinitialization. bool new_send_state = false; diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc index 15855da1f2..c057882340 100644 --- a/video/video_stream_encoder.cc +++ b/video/video_stream_encoder.cc @@ -844,10 +844,6 @@ void VideoStreamEncoder::ConfigureEncoder(VideoEncoderConfig config, }); } -// TODO(bugs.webrtc.org/8807): Currently this always does a hard -// reconfiguration, but this isn't always necessary. Add in logic to only update -// the VideoBitrateAllocator and call OnEncoderConfigurationChanged with a -// "soft" reconfiguration. void VideoStreamEncoder::ReconfigureEncoder() { // Running on the encoder queue. RTC_DCHECK(pending_encoder_reconfiguration_); diff --git a/video/video_stream_encoder_unittest.cc b/video/video_stream_encoder_unittest.cc index c1dc8a3be8..d64341901e 100644 --- a/video/video_stream_encoder_unittest.cc +++ b/video/video_stream_encoder_unittest.cc @@ -7565,7 +7565,7 @@ TEST_F(VideoStreamEncoderTest, BandwidthAllocationLowerBound) { /*stable_target_bitrate=*/DataRate::KilobitsPerSec(300), /*link_allocation=*/DataRate::KilobitsPerSec(300), /*fraction_lost=*/0, - /*rtt_ms=*/0, + /*round_trip_time_ms=*/0, /*cwnd_reduce_ratio=*/0); // Insert a first video frame so that encoder gets configured. @@ -7584,7 +7584,7 @@ TEST_F(VideoStreamEncoderTest, BandwidthAllocationLowerBound) { /*stable_target_bitrate=*/target_rate, /*link_allocation=*/target_rate, /*fraction_lost=*/0, - /*rtt_ms=*/0, + /*round_trip_time_ms=*/0, /*cwnd_reduce_ratio=*/0); video_stream_encoder_->WaitUntilTaskQueueIsIdle(); @@ -7698,7 +7698,7 @@ TEST_F(VideoStreamEncoderTest, EncoderSelectorBitrateSwitch) { /*stable_target_bitrate=*/DataRate::KilobitsPerSec(kDontCare), /*link_allocation=*/DataRate::KilobitsPerSec(kDontCare), /*fraction_lost=*/0, - /*rtt_ms=*/0, + /*round_trip_time_ms=*/0, /*cwnd_reduce_ratio=*/0); AdvanceTime(TimeDelta::Millis(0)); @@ -7730,7 +7730,7 @@ TEST_F(VideoStreamEncoderTest, EncoderSelectorBrokenEncoderSwitch) { DataRate::KilobitsPerSec(kSufficientBitrateToNotDrop), /*link_allocation=*/DataRate::KilobitsPerSec(kSufficientBitrateToNotDrop), /*fraction_lost=*/0, - /*rtt_ms=*/0, + /*round_trip_time_ms=*/0, /*cwnd_reduce_ratio=*/0); ON_CALL(video_encoder, Encode(_, _)) @@ -7772,7 +7772,7 @@ TEST_F(VideoStreamEncoderTest, /*stable_target_bitrate=*/rate, /*link_allocation=*/rate, /*fraction_lost=*/0, - /*rtt_ms=*/0, + /*round_trip_time_ms=*/0, /*cwnd_reduce_ratio=*/0); // Insert a first video frame so that encoder gets configured. @@ -7790,7 +7790,7 @@ TEST_F(VideoStreamEncoderTest, /*stable_target_bitrate=*/new_stable_rate, /*link_allocation=*/rate, /*fraction_lost=*/0, - /*rtt_ms=*/0, + /*round_trip_time_ms=*/0, /*cwnd_reduce_ratio=*/0); video_stream_encoder_->WaitUntilTaskQueueIsIdle(); EXPECT_EQ(2, fake_encoder_.GetNumSetRates()); @@ -7809,7 +7809,7 @@ TEST_F(VideoStreamEncoderTest, /*stable_target_bitrate=*/rate, /*link_allocation=*/rate, /*fraction_lost=*/0, - /*rtt_ms=*/0, + /*round_trip_time_ms=*/0, /*cwnd_reduce_ratio=*/0); // Insert a first video frame so that encoder gets configured. @@ -7828,7 +7828,7 @@ TEST_F(VideoStreamEncoderTest, /*stable_target_bitrate=*/new_stable_rate, /*link_allocation=*/rate, /*fraction_lost=*/0, - /*rtt_ms=*/0, + /*round_trip_time_ms=*/0, /*cwnd_reduce_ratio=*/0); video_stream_encoder_->WaitUntilTaskQueueIsIdle(); EXPECT_EQ(1, fake_encoder_.GetNumSetRates()); @@ -8641,4 +8641,144 @@ INSTANTIATE_TEST_SUITE_P( TestParametersVideoCodecAndAllowI420ConversionToString); #endif +class ReconfigureEncoderTest : public VideoStreamEncoderTest { + protected: + void RunTest(const std::vector& configs, + const int expected_num_init_encode) { + ConfigureEncoder(configs[0]); + OnBitrateUpdated(DataRate::BitsPerSec(kTargetBitrateBps)); + InsertFrameAndWaitForEncoded(); + EXPECT_EQ(1, sink_.number_of_reconfigurations()); + ExpectEqual(bitrate_allocator_factory_.codec_config(), configs[0]); + EXPECT_EQ(1, fake_encoder_.GetNumEncoderInitializations()); + ExpectEqual(fake_encoder_.video_codec(), configs[0]); + + // Reconfigure encoder, the encoder should only be reconfigured if needed. + ConfigureEncoder(configs[1]); + InsertFrameAndWaitForEncoded(); + EXPECT_EQ(2, sink_.number_of_reconfigurations()); + ExpectEqual(bitrate_allocator_factory_.codec_config(), configs[1]); + EXPECT_EQ(expected_num_init_encode, + fake_encoder_.GetNumEncoderInitializations()); + if (expected_num_init_encode > 1) + ExpectEqual(fake_encoder_.video_codec(), configs[1]); + + video_stream_encoder_->Stop(); + } + + void ConfigureEncoder(const VideoStream& stream) { + VideoEncoderConfig config; + test::FillEncoderConfiguration(kVideoCodecVP8, /*num_streams=*/1, &config); + config.max_bitrate_bps = stream.max_bitrate_bps; + config.simulcast_layers[0] = stream; + config.video_stream_factory = + rtc::make_ref_counted( + /*codec_name=*/"VP8", /*max_qp=*/0, /*is_screenshare=*/false, + /*conference_mode=*/false); + video_stream_encoder_->ConfigureEncoder(std::move(config), + kMaxPayloadLength); + } + + void OnBitrateUpdated(DataRate bitrate) { + video_stream_encoder_->OnBitrateUpdatedAndWaitForManagedResources( + bitrate, bitrate, bitrate, 0, 0, 0); + } + + void InsertFrameAndWaitForEncoded() { + timestamp_ms_ += kFrameIntervalMs; + video_source_.IncomingCapturedFrame( + CreateFrame(timestamp_ms_, kWidth, kHeight)); + sink_.WaitForEncodedFrame(timestamp_ms_); + } + + void ExpectEqual(const VideoCodec& actual, + const VideoStream& expected) const { + EXPECT_EQ(actual.numberOfSimulcastStreams, 1); + EXPECT_EQ(actual.simulcastStream[0].maxFramerate, expected.max_framerate); + EXPECT_EQ(actual.simulcastStream[0].minBitrate * 1000, + static_cast(expected.min_bitrate_bps)); + EXPECT_EQ(actual.simulcastStream[0].maxBitrate * 1000, + static_cast(expected.max_bitrate_bps)); + EXPECT_EQ(actual.simulcastStream[0].width, + kWidth / expected.scale_resolution_down_by); + EXPECT_EQ(actual.simulcastStream[0].height, + kHeight / expected.scale_resolution_down_by); + EXPECT_EQ(actual.simulcastStream[0].numberOfTemporalLayers, + expected.num_temporal_layers); + EXPECT_EQ(actual.ScalabilityMode(), expected.scalability_mode); + } + + VideoStream DefaultConfig() const { + VideoStream stream; + stream.max_framerate = 25; + stream.min_bitrate_bps = 35000; + stream.max_bitrate_bps = 900000; + stream.scale_resolution_down_by = 1.0; + stream.num_temporal_layers = 1; + stream.bitrate_priority = 1.0; + stream.scalability_mode = ""; + return stream; + } + + const int kWidth = 640; + const int kHeight = 360; + int64_t timestamp_ms_ = 0; +}; + +TEST_F(ReconfigureEncoderTest, NotReconfiguredIfMaxFramerateChanges) { + VideoStream config1 = DefaultConfig(); + VideoStream config2 = config1; + config2.max_framerate++; + + RunTest({config1, config2}, /*expected_num_init_encode=*/1); +} + +TEST_F(ReconfigureEncoderTest, NotReconfiguredIfMinBitrateChanges) { + VideoStream config1 = DefaultConfig(); + VideoStream config2 = config1; + config2.min_bitrate_bps += 10000; + + RunTest({config1, config2}, /*expected_num_init_encode=*/1); +} + +TEST_F(ReconfigureEncoderTest, NotReconfiguredIfMaxBitrateChanges) { + VideoStream config1 = DefaultConfig(); + VideoStream config2 = config1; + config2.max_bitrate_bps += 100000; + + RunTest({config1, config2}, /*expected_num_init_encode=*/1); +} + +TEST_F(ReconfigureEncoderTest, NotReconfiguredIfBitratePriorityChanges) { + VideoStream config1 = DefaultConfig(); + VideoStream config2 = config1; + config2.bitrate_priority = config1.bitrate_priority.value() * 2.0; + + RunTest({config1, config2}, /*expected_num_init_encode=*/1); +} + +TEST_F(ReconfigureEncoderTest, ReconfiguredIfResolutionChanges) { + VideoStream config1 = DefaultConfig(); + VideoStream config2 = config1; + config2.scale_resolution_down_by *= 2; + + RunTest({config1, config2}, /*expected_num_init_encode=*/2); +} + +TEST_F(ReconfigureEncoderTest, ReconfiguredIfNumTemporalLayerChanges) { + VideoStream config1 = DefaultConfig(); + VideoStream config2 = config1; + config2.num_temporal_layers = config1.num_temporal_layers.value() + 1; + + RunTest({config1, config2}, /*expected_num_init_encode=*/2); +} + +TEST_F(ReconfigureEncoderTest, ReconfiguredIfScalabilityModeChanges) { + VideoStream config1 = DefaultConfig(); + VideoStream config2 = config1; + config2.scalability_mode = "L1T2"; + + RunTest({config1, config2}, /*expected_num_init_encode=*/2); +} + } // namespace webrtc