/* * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include #include #include #include #include #include "absl/memory/memory.h" #include "api/video_codecs/vp8_frame_config.h" #include "modules/video_coding/codecs/interface/common_constants.h" #include "modules/video_coding/codecs/vp8/libvpx_vp8_encoder.h" #include "modules/video_coding/codecs/vp8/screenshare_layers.h" #include "modules/video_coding/include/video_codec_interface.h" #include "rtc_base/checks.h" #include "rtc_base/fake_clock.h" #include "system_wrappers/include/metrics.h" #include "test/gmock.h" #include "test/gtest.h" #include "vpx/vp8cx.h" using ::testing::_; using ::testing::ElementsAre; using ::testing::NiceMock; namespace webrtc { namespace { // 5 frames per second at 90 kHz. const uint32_t kTimestampDelta5Fps = 90000 / 5; const int kDefaultQp = 54; const int kDefaultTl0BitrateKbps = 200; const int kDefaultTl1BitrateKbps = 2000; const int kFrameRate = 5; const int kSyncPeriodSeconds = 2; const int kMaxSyncPeriodSeconds = 4; // Expected flags for corresponding temporal layers. const int kTl0Flags = VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF; const int kTl1Flags = VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST; const int kTl1SyncFlags = VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST; const std::vector kDefault2TlBitratesBps = { kDefaultTl0BitrateKbps * 1000, (kDefaultTl1BitrateKbps - kDefaultTl0BitrateKbps) * 1000}; } // namespace class ScreenshareLayerTest : public ::testing::Test { protected: ScreenshareLayerTest() : min_qp_(2), max_qp_(kDefaultQp), frame_size_(-1), timestamp_(90), config_updated_(false) {} virtual ~ScreenshareLayerTest() {} void SetUp() override { layers_.reset(new ScreenshareLayers(2)); cfg_ = ConfigureBitrates(); } int EncodeFrame(bool base_sync, CodecSpecificInfo* info = nullptr) { CodecSpecificInfo ignored_info; if (!info) { info = &ignored_info; } int flags = ConfigureFrame(base_sync); if (flags != -1) layers_->OnEncodeDone(0, timestamp_, frame_size_, base_sync, kDefaultQp, info); return flags; } int ConfigureFrame(bool key_frame) { tl_config_ = NextFrameConfig(0, timestamp_); EXPECT_EQ(0, tl_config_.encoder_layer_id) << "ScreenshareLayers always encodes using the bitrate allocator for " "layer 0, but may reference different buffers and packetize " "differently."; if (tl_config_.drop_frame) { return -1; } const uint32_t prev_rc_target_bitrate = cfg_.rc_target_bitrate.value_or(-1); const uint32_t prev_rc_max_quantizer = cfg_.rc_max_quantizer.value_or(-1); cfg_ = layers_->UpdateConfiguration(0); config_updated_ = cfg_.temporal_layer_config.has_value() || (cfg_.rc_target_bitrate.has_value() && cfg_.rc_target_bitrate.value() != prev_rc_target_bitrate) || (cfg_.rc_max_quantizer.has_value() && cfg_.rc_max_quantizer.value() != prev_rc_max_quantizer) || cfg_.g_error_resilient.has_value(); int flags = LibvpxVp8Encoder::EncodeFlags(tl_config_); EXPECT_NE(-1, frame_size_); return flags; } Vp8FrameConfig NextFrameConfig(size_t stream_index, uint32_t timestamp) { int64_t timestamp_ms = timestamp / 90; clock_.AdvanceTime(TimeDelta::ms(timestamp_ms - rtc::TimeMillis())); return layers_->NextFrameConfig(stream_index, timestamp); } int FrameSizeForBitrate(int bitrate_kbps) { return ((bitrate_kbps * 1000) / 8) / kFrameRate; } Vp8EncoderConfig ConfigureBitrates() { layers_->SetQpLimits(0, min_qp_, max_qp_); layers_->OnRatesUpdated(0, kDefault2TlBitratesBps, kFrameRate); const Vp8EncoderConfig vp8_cfg = layers_->UpdateConfiguration(0); EXPECT_TRUE(vp8_cfg.rc_target_bitrate.has_value()); frame_size_ = FrameSizeForBitrate(vp8_cfg.rc_target_bitrate.value()); return vp8_cfg; } void WithQpLimits(int min_qp, int max_qp) { min_qp_ = min_qp; max_qp_ = max_qp; } // Runs a few initial frames and makes sure we have seen frames on both // temporal layers, including sync and non-sync frames. bool RunGracePeriod() { bool got_tl0 = false; bool got_tl1 = false; bool got_tl1_sync = false; for (int i = 0; i < 10; ++i) { CodecSpecificInfo info; EXPECT_NE(-1, EncodeFrame(false, &info)); timestamp_ += kTimestampDelta5Fps; if (info.codecSpecific.VP8.temporalIdx == 0) { got_tl0 = true; } else if (info.codecSpecific.VP8.layerSync) { got_tl1_sync = true; } else { got_tl1 = true; } if (got_tl0 && got_tl1 && got_tl1_sync) return true; } return false; } // Adds frames until we get one in the specified temporal layer. The last // FrameEncoded() call will be omitted and needs to be done by the caller. // Returns the flags for the last frame. int SkipUntilTl(int layer) { return SkipUntilTlAndSync(layer, absl::nullopt); } // Same as SkipUntilTl, but also waits until the sync bit condition is met. int SkipUntilTlAndSync(int layer, absl::optional sync) { int flags = 0; const int kMaxFramesToSkip = 1 + (sync.value_or(false) ? kMaxSyncPeriodSeconds : 1) * kFrameRate; for (int i = 0; i < kMaxFramesToSkip; ++i) { flags = ConfigureFrame(false); if (tl_config_.packetizer_temporal_idx != layer || (sync && *sync != tl_config_.layer_sync)) { if (flags != -1) { // If flags do not request a frame drop, report some default values // for frame size etc. CodecSpecificInfo info; layers_->OnEncodeDone(0, timestamp_, frame_size_, false, kDefaultQp, &info); } timestamp_ += kTimestampDelta5Fps; } else { // Found frame from sought after layer. return flags; } } ADD_FAILURE() << "Did not get a frame of TL" << layer << " in time."; return -1; } int min_qp_; uint32_t max_qp_; int frame_size_; rtc::ScopedFakeClock clock_; std::unique_ptr layers_; uint32_t timestamp_; Vp8FrameConfig tl_config_; Vp8EncoderConfig cfg_; bool config_updated_; CodecSpecificInfo* IgnoredCodecSpecificInfo() { ignored_codec_specific_info_ = absl::make_unique(); return ignored_codec_specific_info_.get(); } private: std::unique_ptr ignored_codec_specific_info_; }; TEST_F(ScreenshareLayerTest, 1Layer) { layers_.reset(new ScreenshareLayers(1)); ConfigureBitrates(); // One layer screenshare should not use the frame dropper as all frames will // belong to the base layer. const int kSingleLayerFlags = 0; auto info = absl::make_unique(); int flags = EncodeFrame(false, info.get()); timestamp_ += kTimestampDelta5Fps; EXPECT_EQ(static_cast(kNoTemporalIdx), info->codecSpecific.VP8.temporalIdx); EXPECT_FALSE(info->codecSpecific.VP8.layerSync); info = absl::make_unique(); flags = EncodeFrame(false, info.get()); EXPECT_EQ(kSingleLayerFlags, flags); EXPECT_EQ(static_cast(kNoTemporalIdx), info->codecSpecific.VP8.temporalIdx); EXPECT_FALSE(info->codecSpecific.VP8.layerSync); } TEST_F(ScreenshareLayerTest, 2LayersPeriodicSync) { std::vector sync_times; const int kNumFrames = kSyncPeriodSeconds * kFrameRate * 2 - 1; for (int i = 0; i < kNumFrames; ++i) { CodecSpecificInfo info; EncodeFrame(false, &info); timestamp_ += kTimestampDelta5Fps; if (info.codecSpecific.VP8.temporalIdx == 1 && info.codecSpecific.VP8.layerSync) { sync_times.push_back(timestamp_); } } ASSERT_EQ(2u, sync_times.size()); EXPECT_GE(sync_times[1] - sync_times[0], 90000 * kSyncPeriodSeconds); } TEST_F(ScreenshareLayerTest, 2LayersSyncAfterTimeout) { std::vector sync_times; const int kNumFrames = kMaxSyncPeriodSeconds * kFrameRate * 2 - 1; for (int i = 0; i < kNumFrames; ++i) { CodecSpecificInfo info; tl_config_ = NextFrameConfig(0, timestamp_); cfg_ = layers_->UpdateConfiguration(0); // Simulate TL1 being at least 8 qp steps better. if (tl_config_.packetizer_temporal_idx == 0) { layers_->OnEncodeDone(0, timestamp_, frame_size_, false, kDefaultQp, &info); } else { layers_->OnEncodeDone(0, timestamp_, frame_size_, false, kDefaultQp - 8, &info); } if (info.codecSpecific.VP8.temporalIdx == 1 && info.codecSpecific.VP8.layerSync) sync_times.push_back(timestamp_); timestamp_ += kTimestampDelta5Fps; } ASSERT_EQ(2u, sync_times.size()); EXPECT_GE(sync_times[1] - sync_times[0], 90000 * kMaxSyncPeriodSeconds); } TEST_F(ScreenshareLayerTest, 2LayersSyncAfterSimilarQP) { std::vector sync_times; const int kNumFrames = (kSyncPeriodSeconds + ((kMaxSyncPeriodSeconds - kSyncPeriodSeconds) / 2)) * kFrameRate; for (int i = 0; i < kNumFrames; ++i) { CodecSpecificInfo info; ConfigureFrame(false); // Simulate TL1 being at least 8 qp steps better. if (tl_config_.packetizer_temporal_idx == 0) { layers_->OnEncodeDone(0, timestamp_, frame_size_, false, kDefaultQp, &info); } else { layers_->OnEncodeDone(0, timestamp_, frame_size_, false, kDefaultQp - 8, &info); } if (info.codecSpecific.VP8.temporalIdx == 1 && info.codecSpecific.VP8.layerSync) sync_times.push_back(timestamp_); timestamp_ += kTimestampDelta5Fps; } ASSERT_EQ(1u, sync_times.size()); bool bumped_tl0_quality = false; for (int i = 0; i < 3; ++i) { CodecSpecificInfo info; int flags = ConfigureFrame(false); layers_->OnEncodeDone(0, timestamp_, frame_size_, false, kDefaultQp - 8, &info); if (info.codecSpecific.VP8.temporalIdx == 0) { // Bump TL0 to same quality as TL1. bumped_tl0_quality = true; } else { if (bumped_tl0_quality) { EXPECT_TRUE(info.codecSpecific.VP8.layerSync); EXPECT_EQ(kTl1SyncFlags, flags); return; } } timestamp_ += kTimestampDelta5Fps; } ADD_FAILURE() << "No TL1 frame arrived within time limit."; } TEST_F(ScreenshareLayerTest, 2LayersToggling) { EXPECT_TRUE(RunGracePeriod()); // Insert 50 frames. 2/5 should be TL0. int tl0_frames = 0; int tl1_frames = 0; for (int i = 0; i < 50; ++i) { CodecSpecificInfo info; EncodeFrame(false, &info); timestamp_ += kTimestampDelta5Fps; switch (info.codecSpecific.VP8.temporalIdx) { case 0: ++tl0_frames; break; case 1: ++tl1_frames; break; default: abort(); } } EXPECT_EQ(20, tl0_frames); EXPECT_EQ(30, tl1_frames); } TEST_F(ScreenshareLayerTest, AllFitsLayer0) { frame_size_ = FrameSizeForBitrate(kDefaultTl0BitrateKbps); // Insert 50 frames, small enough that all fits in TL0. for (int i = 0; i < 50; ++i) { CodecSpecificInfo info; int flags = EncodeFrame(false, &info); timestamp_ += kTimestampDelta5Fps; EXPECT_EQ(kTl0Flags, flags); EXPECT_EQ(0, info.codecSpecific.VP8.temporalIdx); } } TEST_F(ScreenshareLayerTest, TooHighBitrate) { frame_size_ = 2 * FrameSizeForBitrate(kDefaultTl1BitrateKbps); // Insert 100 frames. Half should be dropped. int tl0_frames = 0; int tl1_frames = 0; int dropped_frames = 0; for (int i = 0; i < 100; ++i) { CodecSpecificInfo info; int flags = EncodeFrame(false, &info); timestamp_ += kTimestampDelta5Fps; if (flags == -1) { ++dropped_frames; } else { switch (info.codecSpecific.VP8.temporalIdx) { case 0: ++tl0_frames; break; case 1: ++tl1_frames; break; default: ADD_FAILURE() << "Unexpected temporal id"; } } } EXPECT_NEAR(50, tl0_frames + tl1_frames, 1); EXPECT_NEAR(50, dropped_frames, 1); } TEST_F(ScreenshareLayerTest, TargetBitrateCappedByTL0) { const int kTl0_kbps = 100; const int kTl1_kbps = 1000; const std::vector layer_rates = {kTl0_kbps * 1000, (kTl1_kbps - kTl0_kbps) * 1000}; layers_->OnRatesUpdated(0, layer_rates, kFrameRate); cfg_ = layers_->UpdateConfiguration(0); EXPECT_EQ(static_cast( ScreenshareLayers::kMaxTL0FpsReduction * kTl0_kbps + 0.5), cfg_.rc_target_bitrate); } TEST_F(ScreenshareLayerTest, TargetBitrateCappedByTL1) { const int kTl0_kbps = 100; const int kTl1_kbps = 450; const std::vector layer_rates = {kTl0_kbps * 1000, (kTl1_kbps - kTl0_kbps) * 1000}; layers_->OnRatesUpdated(0, layer_rates, kFrameRate); cfg_ = layers_->UpdateConfiguration(0); EXPECT_EQ(static_cast( kTl1_kbps / ScreenshareLayers::kAcceptableTargetOvershoot), cfg_.rc_target_bitrate); } TEST_F(ScreenshareLayerTest, TargetBitrateBelowTL0) { const int kTl0_kbps = 100; const std::vector layer_rates = {kTl0_kbps * 1000}; layers_->OnRatesUpdated(0, layer_rates, kFrameRate); cfg_ = layers_->UpdateConfiguration(0); EXPECT_EQ(static_cast(kTl0_kbps), cfg_.rc_target_bitrate); } TEST_F(ScreenshareLayerTest, EncoderDrop) { EXPECT_TRUE(RunGracePeriod()); SkipUntilTl(0); // Size 0 indicates dropped frame. layers_->OnEncodeDone(0, timestamp_, 0, false, 0, IgnoredCodecSpecificInfo()); // Re-encode frame (so don't advance timestamp). int flags = EncodeFrame(false); timestamp_ += kTimestampDelta5Fps; EXPECT_FALSE(config_updated_); EXPECT_EQ(kTl0Flags, flags); // Next frame should have boosted quality... SkipUntilTl(0); EXPECT_TRUE(config_updated_); EXPECT_LT(cfg_.rc_max_quantizer, static_cast(kDefaultQp)); layers_->OnEncodeDone(0, timestamp_, frame_size_, false, kDefaultQp, IgnoredCodecSpecificInfo()); timestamp_ += kTimestampDelta5Fps; // ...then back to standard setup. SkipUntilTl(0); layers_->OnEncodeDone(0, timestamp_, frame_size_, false, kDefaultQp, IgnoredCodecSpecificInfo()); timestamp_ += kTimestampDelta5Fps; EXPECT_EQ(cfg_.rc_max_quantizer, static_cast(kDefaultQp)); // Next drop in TL1. SkipUntilTl(1); layers_->OnEncodeDone(0, timestamp_, 0, false, 0, IgnoredCodecSpecificInfo()); // Re-encode frame (so don't advance timestamp). flags = EncodeFrame(false); timestamp_ += kTimestampDelta5Fps; EXPECT_FALSE(config_updated_); EXPECT_EQ(kTl1Flags, flags); // Next frame should have boosted QP. SkipUntilTl(1); EXPECT_TRUE(config_updated_); EXPECT_LT(cfg_.rc_max_quantizer, static_cast(kDefaultQp)); layers_->OnEncodeDone(0, timestamp_, frame_size_, false, kDefaultQp, IgnoredCodecSpecificInfo()); timestamp_ += kTimestampDelta5Fps; // ...and back to normal. SkipUntilTl(1); EXPECT_EQ(cfg_.rc_max_quantizer, static_cast(kDefaultQp)); layers_->OnEncodeDone(0, timestamp_, frame_size_, false, kDefaultQp, IgnoredCodecSpecificInfo()); timestamp_ += kTimestampDelta5Fps; } TEST_F(ScreenshareLayerTest, RespectsMaxIntervalBetweenFrames) { const int kLowBitrateKbps = 50; const int kLargeFrameSizeBytes = 100000; const uint32_t kStartTimestamp = 1234; const std::vector layer_rates = {kLowBitrateKbps * 1000}; layers_->OnRatesUpdated(0, layer_rates, kFrameRate); cfg_ = layers_->UpdateConfiguration(0); EXPECT_EQ(kTl0Flags, LibvpxVp8Encoder::EncodeFlags(NextFrameConfig(0, kStartTimestamp))); layers_->OnEncodeDone(0, kStartTimestamp, kLargeFrameSizeBytes, false, kDefaultQp, IgnoredCodecSpecificInfo()); const uint32_t kTwoSecondsLater = kStartTimestamp + (ScreenshareLayers::kMaxFrameIntervalMs * 90); // Sanity check, repayment time should exceed kMaxFrameIntervalMs. ASSERT_GT(kStartTimestamp + 90 * (kLargeFrameSizeBytes * 8) / kLowBitrateKbps, kStartTimestamp + (ScreenshareLayers::kMaxFrameIntervalMs * 90)); // Expect drop one frame interval before the two second timeout. If we try // any later, the frame will be dropped anyway by the frame rate throttling // logic. EXPECT_TRUE( NextFrameConfig(0, kTwoSecondsLater - kTimestampDelta5Fps).drop_frame); // More than two seconds has passed since last frame, one should be emitted // even if bitrate target is then exceeded. EXPECT_EQ(kTl0Flags, LibvpxVp8Encoder::EncodeFlags( NextFrameConfig(0, kTwoSecondsLater + 90))); } TEST_F(ScreenshareLayerTest, UpdatesHistograms) { metrics::Reset(); bool trigger_drop = false; bool dropped_frame = false; bool overshoot = false; const int kTl0Qp = 35; const int kTl1Qp = 30; for (int64_t timestamp = 0; timestamp < kTimestampDelta5Fps * 5 * metrics::kMinRunTimeInSeconds; timestamp += kTimestampDelta5Fps) { tl_config_ = NextFrameConfig(0, timestamp); if (tl_config_.drop_frame) { dropped_frame = true; continue; } int flags = LibvpxVp8Encoder::EncodeFlags(tl_config_); if (flags != -1) cfg_ = layers_->UpdateConfiguration(0); if (timestamp >= kTimestampDelta5Fps * 5 && !overshoot && flags != -1) { // Simulate one overshoot. layers_->OnEncodeDone(0, timestamp, 0, false, 0, nullptr); overshoot = true; } if (flags == kTl0Flags) { if (timestamp >= kTimestampDelta5Fps * 20 && !trigger_drop) { // Simulate a too large frame, to cause frame drop. layers_->OnEncodeDone(0, timestamp, frame_size_ * 10, false, kTl0Qp, IgnoredCodecSpecificInfo()); trigger_drop = true; } else { layers_->OnEncodeDone(0, timestamp, frame_size_, false, kTl0Qp, IgnoredCodecSpecificInfo()); } } else if (flags == kTl1Flags || flags == kTl1SyncFlags) { layers_->OnEncodeDone(0, timestamp, frame_size_, false, kTl1Qp, IgnoredCodecSpecificInfo()); } else if (flags == -1) { dropped_frame = true; } else { RTC_NOTREACHED() << "Unexpected flags"; } clock_.AdvanceTime(TimeDelta::ms(1000 / 5)); } EXPECT_TRUE(overshoot); EXPECT_TRUE(dropped_frame); layers_.reset(); // Histograms are reported on destruction. EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.Screenshare.Layer0.FrameRate")); EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.Screenshare.Layer1.FrameRate")); EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.Screenshare.FramesPerDrop")); EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.Screenshare.FramesPerOvershoot")); EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.Screenshare.Layer0.Qp")); EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.Screenshare.Layer1.Qp")); EXPECT_EQ( 1, metrics::NumSamples("WebRTC.Video.Screenshare.Layer0.TargetBitrate")); EXPECT_EQ( 1, metrics::NumSamples("WebRTC.Video.Screenshare.Layer1.TargetBitrate")); EXPECT_GT(metrics::MinSample("WebRTC.Video.Screenshare.Layer0.FrameRate"), 1); EXPECT_GT(metrics::MinSample("WebRTC.Video.Screenshare.Layer1.FrameRate"), 1); EXPECT_GT(metrics::MinSample("WebRTC.Video.Screenshare.FramesPerDrop"), 1); EXPECT_GT(metrics::MinSample("WebRTC.Video.Screenshare.FramesPerOvershoot"), 1); EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.Screenshare.Layer0.Qp", kTl0Qp)); EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.Screenshare.Layer1.Qp", kTl1Qp)); EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.Screenshare.Layer0.TargetBitrate", kDefaultTl0BitrateKbps)); EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.Screenshare.Layer1.TargetBitrate", kDefaultTl1BitrateKbps)); } TEST_F(ScreenshareLayerTest, RespectsConfiguredFramerate) { int64_t kTestSpanMs = 2000; int64_t kFrameIntervalsMs = 1000 / kFrameRate; uint32_t timestamp = 1234; int num_input_frames = 0; int num_discarded_frames = 0; // Send at regular rate - no drops expected. for (int64_t i = 0; i < kTestSpanMs; i += kFrameIntervalsMs) { if (NextFrameConfig(0, timestamp).drop_frame) { ++num_discarded_frames; } else { size_t frame_size_bytes = kDefaultTl0BitrateKbps * kFrameIntervalsMs / 8; layers_->OnEncodeDone(0, timestamp, frame_size_bytes, false, kDefaultQp, IgnoredCodecSpecificInfo()); } timestamp += kFrameIntervalsMs * 90; clock_.AdvanceTime(TimeDelta::ms(kFrameIntervalsMs)); ++num_input_frames; } EXPECT_EQ(0, num_discarded_frames); // Send at twice the configured rate - drop every other frame. num_input_frames = 0; num_discarded_frames = 0; for (int64_t i = 0; i < kTestSpanMs; i += kFrameIntervalsMs / 2) { if (NextFrameConfig(0, timestamp).drop_frame) { ++num_discarded_frames; } else { size_t frame_size_bytes = kDefaultTl0BitrateKbps * kFrameIntervalsMs / 8; layers_->OnEncodeDone(0, timestamp, frame_size_bytes, false, kDefaultQp, IgnoredCodecSpecificInfo()); } timestamp += kFrameIntervalsMs * 90 / 2; clock_.AdvanceTime(TimeDelta::ms(kFrameIntervalsMs)); ++num_input_frames; } // Allow for some rounding errors in the measurements. EXPECT_NEAR(num_discarded_frames, num_input_frames / 2, 2); } TEST_F(ScreenshareLayerTest, 2LayersSyncAtOvershootDrop) { // Run grace period so we have existing frames in both TL0 and Tl1. EXPECT_TRUE(RunGracePeriod()); // Move ahead until we have a sync frame in TL1. EXPECT_EQ(kTl1SyncFlags, SkipUntilTlAndSync(1, true)); ASSERT_TRUE(tl_config_.layer_sync); // Simulate overshoot of this frame. layers_->OnEncodeDone(0, timestamp_, 0, false, 0, nullptr); cfg_ = layers_->UpdateConfiguration(0); EXPECT_EQ(kTl1SyncFlags, LibvpxVp8Encoder::EncodeFlags(tl_config_)); CodecSpecificInfo new_info; layers_->OnEncodeDone(0, timestamp_, frame_size_, false, kDefaultQp, &new_info); EXPECT_TRUE(new_info.codecSpecific.VP8.layerSync); } TEST_F(ScreenshareLayerTest, DropOnTooShortFrameInterval) { // Run grace period so we have existing frames in both TL0 and Tl1. EXPECT_TRUE(RunGracePeriod()); // Add a large gap, so there's plenty of room in the rate tracker. timestamp_ += kTimestampDelta5Fps * 3; EXPECT_FALSE(NextFrameConfig(0, timestamp_).drop_frame); layers_->OnEncodeDone(0, timestamp_, frame_size_, false, kDefaultQp, IgnoredCodecSpecificInfo()); // Frame interval below 90% if desired time is not allowed, try inserting // frame just before this limit. const int64_t kMinFrameInterval = (kTimestampDelta5Fps * 85) / 100; timestamp_ += kMinFrameInterval - 90; EXPECT_TRUE(NextFrameConfig(0, timestamp_).drop_frame); // Try again at the limit, now it should pass. timestamp_ += 90; EXPECT_FALSE(NextFrameConfig(0, timestamp_).drop_frame); } TEST_F(ScreenshareLayerTest, AdjustsBitrateWhenDroppingFrames) { const uint32_t kTimestampDelta10Fps = kTimestampDelta5Fps / 2; const int kNumFrames = 30; ASSERT_TRUE(cfg_.rc_target_bitrate.has_value()); const uint32_t default_bitrate = cfg_.rc_target_bitrate.value(); layers_->OnRatesUpdated(0, kDefault2TlBitratesBps, 10); int num_dropped_frames = 0; for (int i = 0; i < kNumFrames; ++i) { if (EncodeFrame(false) == -1) ++num_dropped_frames; timestamp_ += kTimestampDelta10Fps; } cfg_ = layers_->UpdateConfiguration(0); EXPECT_EQ(num_dropped_frames, kNumFrames / 2); EXPECT_EQ(cfg_.rc_target_bitrate, default_bitrate * 2); } TEST_F(ScreenshareLayerTest, UpdatesConfigurationAfterRateChange) { // Set inital rate again, no need to update configuration. layers_->OnRatesUpdated(0, kDefault2TlBitratesBps, kFrameRate); cfg_ = layers_->UpdateConfiguration(0); // Rate changed, now update config. std::vector bitrates = kDefault2TlBitratesBps; bitrates[1] -= 100000; layers_->OnRatesUpdated(0, bitrates, 5); cfg_ = layers_->UpdateConfiguration(0); // Changed rate, but then set changed rate again before trying to update // configuration, update should still apply. bitrates[1] -= 100000; layers_->OnRatesUpdated(0, bitrates, 5); layers_->OnRatesUpdated(0, bitrates, 5); cfg_ = layers_->UpdateConfiguration(0); } TEST_F(ScreenshareLayerTest, MaxQpRestoredAfterDoubleDrop) { // Run grace period so we have existing frames in both TL0 and Tl1. EXPECT_TRUE(RunGracePeriod()); // Move ahead until we have a sync frame in TL1. EXPECT_EQ(kTl1SyncFlags, SkipUntilTlAndSync(1, true)); ASSERT_TRUE(tl_config_.layer_sync); // Simulate overshoot of this frame. layers_->OnEncodeDone(0, timestamp_, 0, false, -1, nullptr); // Simulate re-encoded frame. layers_->OnEncodeDone(0, timestamp_, 1, false, max_qp_, IgnoredCodecSpecificInfo()); // Next frame, expect boosted quality. // Slightly alter bitrate between each frame. std::vector kDefault2TlBitratesBpsAlt = kDefault2TlBitratesBps; kDefault2TlBitratesBpsAlt[1] += 4000; layers_->OnRatesUpdated(0, kDefault2TlBitratesBpsAlt, kFrameRate); EXPECT_EQ(kTl1Flags, SkipUntilTlAndSync(1, false)); EXPECT_TRUE(config_updated_); EXPECT_LT(cfg_.rc_max_quantizer, max_qp_); ASSERT_TRUE(cfg_.rc_max_quantizer.has_value()); const uint32_t adjusted_qp = cfg_.rc_max_quantizer.value(); // Simulate overshoot of this frame. layers_->OnEncodeDone(0, timestamp_, 0, false, -1, nullptr); // Simulate re-encoded frame. layers_->OnEncodeDone(0, timestamp_, frame_size_, false, max_qp_, IgnoredCodecSpecificInfo()); // A third frame, expect boosted quality. layers_->OnRatesUpdated(0, kDefault2TlBitratesBps, kFrameRate); EXPECT_EQ(kTl1Flags, SkipUntilTlAndSync(1, false)); EXPECT_TRUE(config_updated_); EXPECT_LT(cfg_.rc_max_quantizer, max_qp_); EXPECT_EQ(adjusted_qp, cfg_.rc_max_quantizer); // Frame encoded. layers_->OnEncodeDone(0, timestamp_, frame_size_, false, max_qp_, IgnoredCodecSpecificInfo()); // A fourth frame, max qp should be restored. layers_->OnRatesUpdated(0, kDefault2TlBitratesBpsAlt, kFrameRate); EXPECT_EQ(kTl1Flags, SkipUntilTlAndSync(1, false)); EXPECT_EQ(cfg_.rc_max_quantizer, max_qp_); } } // namespace webrtc