Update adaptation stats to support degradations in both resolution and framerate.

Add AdaptCounter class which holds the number of downgrade counts per degradation way (resolution/fps) and reason (cpu/quality).

BUG=webrtc:7607

Review-Url: https://codereview.webrtc.org/2871623002
Cr-Commit-Position: refs/heads/master@{#18156}
This commit is contained in:
asapersson 2017-05-15 23:40:18 -07:00 committed by Commit bot
parent 9a6f4d4316
commit 09f0561675
7 changed files with 594 additions and 277 deletions

View File

@ -305,8 +305,8 @@ void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms(
}
if (first_rtp_stats_time_ms_ != -1) {
quality_scaling_timer_.Stop(clock_->TimeInMilliseconds());
int64_t elapsed_sec = quality_scaling_timer_.total_ms / 1000;
quality_adapt_timer_.Stop(clock_->TimeInMilliseconds());
int64_t elapsed_sec = quality_adapt_timer_.total_ms / 1000;
if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
int quality_changes = current_stats.number_of_quality_adapt_changes -
start_stats_.number_of_quality_adapt_changes;
@ -314,8 +314,8 @@ void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms(
uma_prefix_ + "AdaptChangesPerMinute.Quality",
quality_changes * 60 / elapsed_sec);
}
cpu_scaling_timer_.Stop(clock_->TimeInMilliseconds());
elapsed_sec = cpu_scaling_timer_.total_ms / 1000;
cpu_adapt_timer_.Stop(clock_->TimeInMilliseconds());
elapsed_sec = cpu_adapt_timer_.total_ms / 1000;
if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
int cpu_changes = current_stats.number_of_cpu_adapt_changes -
start_stats_.number_of_cpu_adapt_changes;
@ -510,14 +510,14 @@ void SendStatisticsProxy::OnSuspendChange(bool is_suspended) {
uma_container_->retransmit_byte_counter_.ProcessAndPauseForDuration(kMinMs);
uma_container_->fec_byte_counter_.ProcessAndPauseForDuration(kMinMs);
// Stop adaptation stats.
uma_container_->cpu_scaling_timer_.Stop(now_ms);
uma_container_->quality_scaling_timer_.Stop(now_ms);
uma_container_->cpu_adapt_timer_.Stop(now_ms);
uma_container_->quality_adapt_timer_.Stop(now_ms);
} else {
// Start adaptation stats if scaling is enabled.
if (cpu_downscales_ >= 0)
uma_container_->cpu_scaling_timer_.Start(now_ms);
uma_container_->cpu_adapt_timer_.Start(now_ms);
if (quality_downscales_ >= 0)
uma_container_->quality_scaling_timer_.Start(now_ms);
uma_container_->quality_adapt_timer_.Start(now_ms);
// Stop pause explicitly for stats that may be zero/not updated for some
// time.
uma_container_->rtx_byte_counter_.ProcessAndStopPause();
@ -734,50 +734,53 @@ void SendStatisticsProxy::OnIncomingFrame(int width, int height) {
"ssrc", rtp_config_.ssrcs[0]);
}
void SendStatisticsProxy::SetCpuScalingStats(int num_cpu_downscales) {
void SendStatisticsProxy::SetAdaptationStats(
const ViEEncoder::AdaptCounts& cpu_counts,
const ViEEncoder::AdaptCounts& quality_counts) {
rtc::CritScope lock(&crit_);
cpu_downscales_ = num_cpu_downscales;
stats_.cpu_limited_resolution = num_cpu_downscales > 0;
if (num_cpu_downscales >= 0) {
// Scaling enabled.
if (!stats_.suspended)
uma_container_->cpu_scaling_timer_.Start(clock_->TimeInMilliseconds());
return;
}
uma_container_->cpu_scaling_timer_.Stop(clock_->TimeInMilliseconds());
SetAdaptTimer(cpu_counts, &uma_container_->cpu_adapt_timer_);
SetAdaptTimer(quality_counts, &uma_container_->quality_adapt_timer_);
UpdateAdaptationStats(cpu_counts, quality_counts);
}
void SendStatisticsProxy::SetQualityScalingStats(int num_quality_downscales) {
void SendStatisticsProxy::OnCpuAdaptationChanged(
const ViEEncoder::AdaptCounts& cpu_counts,
const ViEEncoder::AdaptCounts& quality_counts) {
rtc::CritScope lock(&crit_);
quality_downscales_ = num_quality_downscales;
stats_.bw_limited_resolution = quality_downscales_ > 0;
if (num_quality_downscales >= 0) {
// Scaling enabled.
if (!stats_.suspended) {
uma_container_->quality_scaling_timer_.Start(
clock_->TimeInMilliseconds());
}
return;
}
uma_container_->quality_scaling_timer_.Stop(clock_->TimeInMilliseconds());
}
void SendStatisticsProxy::OnCpuRestrictedResolutionChanged(
bool cpu_restricted_resolution) {
rtc::CritScope lock(&crit_);
stats_.cpu_limited_resolution = cpu_restricted_resolution;
++stats_.number_of_cpu_adapt_changes;
UpdateAdaptationStats(cpu_counts, quality_counts);
TRACE_EVENT_INSTANT0("webrtc_stats", "WebRTC.Video.CpuAdaptationChanges");
}
void SendStatisticsProxy::OnQualityRestrictedResolutionChanged(
int num_quality_downscales) {
void SendStatisticsProxy::OnQualityAdaptationChanged(
const ViEEncoder::AdaptCounts& cpu_counts,
const ViEEncoder::AdaptCounts& quality_counts) {
rtc::CritScope lock(&crit_);
++stats_.number_of_quality_adapt_changes;
quality_downscales_ = num_quality_downscales;
stats_.bw_limited_resolution = quality_downscales_ > 0;
UpdateAdaptationStats(cpu_counts, quality_counts);
}
void SendStatisticsProxy::UpdateAdaptationStats(
const ViEEncoder::AdaptCounts& cpu_counts,
const ViEEncoder::AdaptCounts& quality_counts) {
cpu_downscales_ = cpu_counts.resolution;
quality_downscales_ = quality_counts.resolution;
stats_.cpu_limited_resolution = cpu_counts.resolution > 0;
stats_.cpu_limited_framerate = cpu_counts.fps > 0;
stats_.bw_limited_resolution = quality_counts.resolution > 0;
stats_.bw_limited_framerate = quality_counts.fps > 0;
}
void SendStatisticsProxy::SetAdaptTimer(const ViEEncoder::AdaptCounts& counts,
StatsTimer* timer) {
if (counts.resolution >= 0 || counts.fps >= 0) {
// Adaptation enabled.
if (!stats_.suspended)
timer->Start(clock_->TimeInMilliseconds());
return;
}
timer->Stop(clock_->TimeInMilliseconds());
}
void SendStatisticsProxy::RtcpPacketTypesCounterUpdated(
@ -833,8 +836,8 @@ void SendStatisticsProxy::DataCountersUpdated(
if (uma_container_->first_rtp_stats_time_ms_ == -1) {
int64_t now_ms = clock_->TimeInMilliseconds();
uma_container_->first_rtp_stats_time_ms_ = now_ms;
uma_container_->cpu_scaling_timer_.Restart(now_ms);
uma_container_->quality_scaling_timer_.Restart(now_ms);
uma_container_->cpu_adapt_timer_.Restart(now_ms);
uma_container_->quality_adapt_timer_.Restart(now_ms);
}
uma_container_->total_byte_counter_.Set(counters.transmitted.TotalBytes(),

View File

@ -57,10 +57,14 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver,
// Used to update incoming frame rate.
void OnIncomingFrame(int width, int height);
void OnCpuRestrictedResolutionChanged(bool cpu_restricted_resolution);
void OnQualityRestrictedResolutionChanged(int num_quality_downscales);
void SetCpuScalingStats(int num_cpu_downscales); // -1: disabled.
void SetQualityScalingStats(int num_quality_downscales); // -1: disabled.
// Adaptation stats.
void SetAdaptationStats(const ViEEncoder::AdaptCounts& cpu_counts,
const ViEEncoder::AdaptCounts& quality_counts);
void OnCpuAdaptationChanged(const ViEEncoder::AdaptCounts& cpu_counts,
const ViEEncoder::AdaptCounts& quality_counts);
void OnQualityAdaptationChanged(
const ViEEncoder::AdaptCounts& cpu_counts,
const ViEEncoder::AdaptCounts& quality_counts);
void OnEncoderStatsUpdate(uint32_t framerate, uint32_t bitrate);
void OnSuspendChange(bool is_suspended);
@ -160,6 +164,12 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver,
VideoSendStream::StreamStats* GetStatsEntry(uint32_t ssrc)
EXCLUSIVE_LOCKS_REQUIRED(crit_);
void SetAdaptTimer(const ViEEncoder::AdaptCounts& counts, StatsTimer* timer)
EXCLUSIVE_LOCKS_REQUIRED(crit_);
void UpdateAdaptationStats(const ViEEncoder::AdaptCounts& cpu_counts,
const ViEEncoder::AdaptCounts& quality_counts)
EXCLUSIVE_LOCKS_REQUIRED(crit_);
Clock* const clock_;
const std::string payload_name_;
const VideoSendStream::Config::Rtp rtp_config_;
@ -215,8 +225,8 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver,
RateAccCounter fec_byte_counter_;
int64_t first_rtcp_stats_time_ms_;
int64_t first_rtp_stats_time_ms_;
StatsTimer cpu_scaling_timer_;
StatsTimer quality_scaling_timer_;
StatsTimer cpu_adapt_timer_;
StatsTimer quality_adapt_timer_;
BoolSampleCounter paused_time_counter_;
TargetRateUpdates target_rate_updates_;
ReportBlockStats report_block_stats_;

View File

@ -367,61 +367,99 @@ TEST_F(SendStatisticsProxyTest, OnSendEncodedImageWithoutQpQpSumWontExist) {
EXPECT_EQ(rtc::Optional<uint64_t>(), statistics_proxy_->GetStats().qp_sum);
}
TEST_F(SendStatisticsProxyTest, SetCpuScalingUpdatesStats) {
TEST_F(SendStatisticsProxyTest, GetCpuAdaptationStats) {
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
EXPECT_FALSE(statistics_proxy_->GetStats().cpu_limited_framerate);
EXPECT_FALSE(statistics_proxy_->GetStats().cpu_limited_resolution);
statistics_proxy_->SetCpuScalingStats(-1);
cpu_counts.fps = 1;
cpu_counts.resolution = 0;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
EXPECT_TRUE(statistics_proxy_->GetStats().cpu_limited_framerate);
EXPECT_FALSE(statistics_proxy_->GetStats().cpu_limited_resolution);
statistics_proxy_->SetCpuScalingStats(0);
EXPECT_FALSE(statistics_proxy_->GetStats().cpu_limited_resolution);
statistics_proxy_->SetCpuScalingStats(1);
cpu_counts.fps = 0;
cpu_counts.resolution = 1;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
EXPECT_FALSE(statistics_proxy_->GetStats().cpu_limited_framerate);
EXPECT_TRUE(statistics_proxy_->GetStats().cpu_limited_resolution);
}
TEST_F(SendStatisticsProxyTest, SetQualityScalingUpdatesStats) {
EXPECT_FALSE(statistics_proxy_->GetStats().bw_limited_resolution);
statistics_proxy_->SetQualityScalingStats(-1);
EXPECT_FALSE(statistics_proxy_->GetStats().bw_limited_resolution);
statistics_proxy_->SetQualityScalingStats(0);
EXPECT_FALSE(statistics_proxy_->GetStats().bw_limited_resolution);
statistics_proxy_->SetQualityScalingStats(1);
EXPECT_TRUE(statistics_proxy_->GetStats().bw_limited_resolution);
}
TEST_F(SendStatisticsProxyTest, GetStatsReportsCpuResolutionChanges) {
cpu_counts.fps = 1;
cpu_counts.resolution = -1;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
EXPECT_TRUE(statistics_proxy_->GetStats().cpu_limited_framerate);
EXPECT_FALSE(statistics_proxy_->GetStats().cpu_limited_resolution);
cpu_counts.fps = -1;
cpu_counts.resolution = -1;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
EXPECT_FALSE(statistics_proxy_->GetStats().cpu_limited_framerate);
EXPECT_FALSE(statistics_proxy_->GetStats().cpu_limited_resolution);
}
TEST_F(SendStatisticsProxyTest, GetQualityAdaptationStats) {
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
EXPECT_FALSE(statistics_proxy_->GetStats().bw_limited_framerate);
EXPECT_FALSE(statistics_proxy_->GetStats().bw_limited_resolution);
quality_counts.fps = 1;
quality_counts.resolution = 0;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
EXPECT_TRUE(statistics_proxy_->GetStats().bw_limited_framerate);
EXPECT_FALSE(statistics_proxy_->GetStats().bw_limited_resolution);
quality_counts.fps = 0;
quality_counts.resolution = 1;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
EXPECT_FALSE(statistics_proxy_->GetStats().bw_limited_framerate);
EXPECT_TRUE(statistics_proxy_->GetStats().bw_limited_resolution);
quality_counts.fps = 1;
quality_counts.resolution = -1;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
EXPECT_TRUE(statistics_proxy_->GetStats().bw_limited_framerate);
EXPECT_FALSE(statistics_proxy_->GetStats().bw_limited_resolution);
quality_counts.fps = -1;
quality_counts.resolution = -1;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
EXPECT_FALSE(statistics_proxy_->GetStats().bw_limited_framerate);
EXPECT_FALSE(statistics_proxy_->GetStats().bw_limited_resolution);
}
TEST_F(SendStatisticsProxyTest, GetStatsReportsCpuAdaptChanges) {
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
EXPECT_EQ(0, statistics_proxy_->GetStats().number_of_cpu_adapt_changes);
statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
cpu_counts.resolution = 1;
statistics_proxy_->OnCpuAdaptationChanged(cpu_counts, quality_counts);
EXPECT_FALSE(statistics_proxy_->GetStats().cpu_limited_framerate);
EXPECT_TRUE(statistics_proxy_->GetStats().cpu_limited_resolution);
EXPECT_EQ(1, statistics_proxy_->GetStats().number_of_cpu_adapt_changes);
statistics_proxy_->OnCpuRestrictedResolutionChanged(false);
EXPECT_FALSE(statistics_proxy_->GetStats().cpu_limited_resolution);
cpu_counts.resolution = 2;
statistics_proxy_->OnCpuAdaptationChanged(cpu_counts, quality_counts);
EXPECT_FALSE(statistics_proxy_->GetStats().cpu_limited_framerate);
EXPECT_TRUE(statistics_proxy_->GetStats().cpu_limited_resolution);
EXPECT_EQ(2, statistics_proxy_->GetStats().number_of_cpu_adapt_changes);
EXPECT_EQ(0, statistics_proxy_->GetStats().number_of_quality_adapt_changes);
}
TEST_F(SendStatisticsProxyTest, GetStatsReportsQualityResolutionChanges) {
EXPECT_FALSE(statistics_proxy_->GetStats().bw_limited_resolution);
TEST_F(SendStatisticsProxyTest, GetStatsReportsQualityAdaptChanges) {
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
EXPECT_EQ(0, statistics_proxy_->GetStats().number_of_quality_adapt_changes);
statistics_proxy_->OnQualityRestrictedResolutionChanged(1);
EXPECT_TRUE(statistics_proxy_->GetStats().bw_limited_resolution);
quality_counts.fps = 1;
statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
EXPECT_TRUE(statistics_proxy_->GetStats().bw_limited_framerate);
EXPECT_FALSE(statistics_proxy_->GetStats().bw_limited_resolution);
EXPECT_EQ(1, statistics_proxy_->GetStats().number_of_quality_adapt_changes);
statistics_proxy_->OnQualityRestrictedResolutionChanged(2);
EXPECT_TRUE(statistics_proxy_->GetStats().bw_limited_resolution);
EXPECT_EQ(2, statistics_proxy_->GetStats().number_of_quality_adapt_changes);
statistics_proxy_->OnQualityRestrictedResolutionChanged(1);
EXPECT_TRUE(statistics_proxy_->GetStats().bw_limited_resolution);
EXPECT_EQ(3, statistics_proxy_->GetStats().number_of_quality_adapt_changes);
statistics_proxy_->OnQualityRestrictedResolutionChanged(0);
quality_counts.fps = 0;
statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
EXPECT_FALSE(statistics_proxy_->GetStats().bw_limited_framerate);
EXPECT_FALSE(statistics_proxy_->GetStats().bw_limited_resolution);
EXPECT_EQ(4, statistics_proxy_->GetStats().number_of_quality_adapt_changes);
EXPECT_EQ(2, statistics_proxy_->GetStats().number_of_quality_adapt_changes);
EXPECT_EQ(0, statistics_proxy_->GetStats().number_of_cpu_adapt_changes);
}
TEST_F(SendStatisticsProxyTest, AdaptChangesNotReported_ScalingNotEnabled) {
TEST_F(SendStatisticsProxyTest, AdaptChangesNotReported_AdaptationNotEnabled) {
// First RTP packet sent.
UpdateDataCounters(kFirstSsrc);
// Min runtime has passed.
@ -435,9 +473,10 @@ TEST_F(SendStatisticsProxyTest, AdaptChangesNotReported_ScalingNotEnabled) {
TEST_F(SendStatisticsProxyTest, AdaptChangesNotReported_MinRuntimeNotPassed) {
// First RTP packet sent.
UpdateDataCounters(kFirstSsrc);
// Enable scaling.
statistics_proxy_->SetQualityScalingStats(0);
statistics_proxy_->SetCpuScalingStats(0);
// Enable adaptation.
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
// Min runtime has not passed.
fake_clock_.AdvanceTimeMilliseconds(metrics::kMinRunTimeInSeconds * 1000 - 1);
statistics_proxy_.reset();
@ -446,26 +485,18 @@ TEST_F(SendStatisticsProxyTest, AdaptChangesNotReported_MinRuntimeNotPassed) {
metrics::NumSamples("WebRTC.Video.AdaptChangesPerMinute.Quality"));
}
TEST_F(SendStatisticsProxyTest, ZeroCpuAdaptChangesReported) {
TEST_F(SendStatisticsProxyTest, ZeroAdaptChangesReported) {
// First RTP packet sent.
UpdateDataCounters(kFirstSsrc);
// Enable scaling.
statistics_proxy_->SetCpuScalingStats(0);
// Enable adaptation.
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
// Min runtime has passed.
fake_clock_.AdvanceTimeMilliseconds(metrics::kMinRunTimeInSeconds * 1000);
statistics_proxy_.reset();
EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.AdaptChangesPerMinute.Cpu"));
EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.AdaptChangesPerMinute.Cpu", 0));
}
TEST_F(SendStatisticsProxyTest, ZeroQualityAdaptChangesReported) {
// First RTP packet sent.
UpdateDataCounters(kFirstSsrc);
// Enable scaling.
statistics_proxy_->SetQualityScalingStats(0);
// Min runtime has passed.
fake_clock_.AdvanceTimeMilliseconds(metrics::kMinRunTimeInSeconds * 1000);
statistics_proxy_.reset();
EXPECT_EQ(1,
metrics::NumSamples("WebRTC.Video.AdaptChangesPerMinute.Quality"));
EXPECT_EQ(
@ -475,10 +506,12 @@ TEST_F(SendStatisticsProxyTest, ZeroQualityAdaptChangesReported) {
TEST_F(SendStatisticsProxyTest, CpuAdaptChangesReported) {
// First RTP packet sent.
UpdateDataCounters(kFirstSsrc);
// Enable scaling.
// Enable adaptation.
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
// Adapt changes: 1, elapsed time: 10 sec => 6 per minute.
statistics_proxy_->SetCpuScalingStats(0);
statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
statistics_proxy_->OnCpuAdaptationChanged(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(10000);
statistics_proxy_.reset();
EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.AdaptChangesPerMinute.Cpu"));
@ -489,34 +522,42 @@ TEST_F(SendStatisticsProxyTest, AdaptChangesStatsExcludesDisabledTime) {
// First RTP packet sent.
UpdateDataCounters(kFirstSsrc);
// Disable scaling.
statistics_proxy_->SetQualityScalingStats(-1);
// Disable quality adaptation.
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
quality_counts.fps = -1;
quality_counts.resolution = -1;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(10000);
// Enable scaling.
// Enable quality adaptation.
// Adapt changes: 2, elapsed time: 20 sec.
statistics_proxy_->SetQualityScalingStats(0);
quality_counts.fps = 0;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(5000);
statistics_proxy_->SetQualityScalingStats(1);
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(9000);
statistics_proxy_->OnQualityRestrictedResolutionChanged(1);
statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(6000);
statistics_proxy_->OnQualityRestrictedResolutionChanged(2);
statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
// Disable scaling.
statistics_proxy_->SetQualityScalingStats(-1);
// Disable quality adaptation.
quality_counts.fps = -1;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(30000);
// Enable scaling.
// Enable quality adaptation.
// Adapt changes: 1, elapsed time: 10 sec.
statistics_proxy_->SetQualityScalingStats(0);
statistics_proxy_->OnQualityRestrictedResolutionChanged(1);
quality_counts.resolution = 0;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(10000);
// Disable scaling.
statistics_proxy_->SetQualityScalingStats(-1);
// Disable quality adaptation.
quality_counts.resolution = -1;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(5000);
statistics_proxy_->SetQualityScalingStats(-1);
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(20000);
// Adapt changes: 3, elapsed time: 30 sec => 6 per minute.
@ -549,12 +590,14 @@ TEST_F(SendStatisticsProxyTest, QualityAdaptChangesStatsExcludesSuspendedTime) {
// First RTP packet sent.
UpdateDataCounters(kFirstSsrc);
// Enable scaling.
// Enable adaptation.
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
// Adapt changes: 2, elapsed time: 20 sec.
statistics_proxy_->SetQualityScalingStats(0);
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(20000);
statistics_proxy_->OnQualityRestrictedResolutionChanged(1);
statistics_proxy_->OnQualityRestrictedResolutionChanged(2);
statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
// Suspend and resume video.
statistics_proxy_->OnSuspendChange(true);
@ -562,7 +605,7 @@ TEST_F(SendStatisticsProxyTest, QualityAdaptChangesStatsExcludesSuspendedTime) {
statistics_proxy_->OnSuspendChange(false);
// Adapt changes: 1, elapsed time: 10 sec.
statistics_proxy_->OnQualityRestrictedResolutionChanged(3);
statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(10000);
// Adapt changes: 3, elapsed time: 30 sec => 6 per minute.
@ -581,18 +624,22 @@ TEST_F(SendStatisticsProxyTest, CpuAdaptChangesStatsExcludesSuspendedTime) {
statistics_proxy_->OnSuspendChange(false);
fake_clock_.AdvanceTimeMilliseconds(30000);
// Enable scaling.
// Enable adaptation.
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
// Adapt changes: 1, elapsed time: 20 sec.
statistics_proxy_->SetCpuScalingStats(0);
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(10000);
statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
statistics_proxy_->OnCpuAdaptationChanged(cpu_counts, quality_counts);
// Video not suspended, stats time already started.
statistics_proxy_->OnSuspendChange(false);
fake_clock_.AdvanceTimeMilliseconds(10000);
// Disable scaling.
statistics_proxy_->SetCpuScalingStats(-1);
// Disable adaptation.
cpu_counts.fps = -1;
cpu_counts.resolution = -1;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(30000);
// Suspend and resume video, stats time not started when scaling not enabled.
@ -601,11 +648,13 @@ TEST_F(SendStatisticsProxyTest, CpuAdaptChangesStatsExcludesSuspendedTime) {
statistics_proxy_->OnSuspendChange(false);
fake_clock_.AdvanceTimeMilliseconds(30000);
// Enable scaling.
// Enable adaptation.
// Adapt changes: 1, elapsed time: 10 sec.
statistics_proxy_->SetCpuScalingStats(0);
cpu_counts.fps = 0;
cpu_counts.resolution = 0;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(10000);
statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
statistics_proxy_->OnCpuAdaptationChanged(cpu_counts, quality_counts);
// Adapt changes: 2, elapsed time: 30 sec => 4 per minute.
statistics_proxy_.reset();
@ -620,15 +669,17 @@ TEST_F(SendStatisticsProxyTest, AdaptChangesStatsNotStartedIfVideoSuspended) {
// Video suspended.
statistics_proxy_->OnSuspendChange(true);
// Enable scaling, stats time not started when suspended.
statistics_proxy_->SetCpuScalingStats(0);
// Enable adaptation, stats time not started when suspended.
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(10000);
// Resume video, stats time started.
// Adapt changes: 1, elapsed time: 10 sec.
statistics_proxy_->OnSuspendChange(false);
fake_clock_.AdvanceTimeMilliseconds(10000);
statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
statistics_proxy_->OnCpuAdaptationChanged(cpu_counts, quality_counts);
// Adapt changes: 1, elapsed time: 10 sec => 6 per minute.
statistics_proxy_.reset();
@ -637,15 +688,17 @@ TEST_F(SendStatisticsProxyTest, AdaptChangesStatsNotStartedIfVideoSuspended) {
}
TEST_F(SendStatisticsProxyTest, AdaptChangesStatsRestartsOnFirstSentPacket) {
// Send first packet, scaling enabled.
// Send first packet, adaptation enabled.
// Elapsed time before first packet is sent should be excluded.
statistics_proxy_->SetQualityScalingStats(0);
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(10000);
UpdateDataCounters(kFirstSsrc);
// Adapt changes: 1, elapsed time: 10 sec.
fake_clock_.AdvanceTimeMilliseconds(10000);
statistics_proxy_->OnQualityRestrictedResolutionChanged(1);
statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
UpdateDataCounters(kFirstSsrc);
// Adapt changes: 1, elapsed time: 10 sec => 6 per minute.
@ -657,24 +710,29 @@ TEST_F(SendStatisticsProxyTest, AdaptChangesStatsRestartsOnFirstSentPacket) {
}
TEST_F(SendStatisticsProxyTest, AdaptChangesStatsStartedAfterFirstSentPacket) {
// Enable and disable scaling.
statistics_proxy_->SetCpuScalingStats(0);
// Enable and disable adaptation.
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(60000);
statistics_proxy_->SetCpuScalingStats(-1);
cpu_counts.fps = -1;
cpu_counts.resolution = -1;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
// Send first packet, scaling disabled.
// Elapsed time before first packet is sent should be excluded.
UpdateDataCounters(kFirstSsrc);
fake_clock_.AdvanceTimeMilliseconds(60000);
// Enable scaling.
statistics_proxy_->SetCpuScalingStats(0);
// Enable adaptation.
cpu_counts.resolution = 0;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(10000);
UpdateDataCounters(kFirstSsrc);
// Adapt changes: 1, elapsed time: 20 sec.
fake_clock_.AdvanceTimeMilliseconds(10000);
statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
statistics_proxy_->OnCpuAdaptationChanged(cpu_counts, quality_counts);
// Adapt changes: 1, elapsed time: 20 sec => 3 per minute.
statistics_proxy_.reset();
@ -683,14 +741,18 @@ TEST_F(SendStatisticsProxyTest, AdaptChangesStatsStartedAfterFirstSentPacket) {
}
TEST_F(SendStatisticsProxyTest, AdaptChangesReportedAfterContentSwitch) {
// First RTP packet sent, scaling enabled.
// First RTP packet sent, cpu adaptation enabled.
UpdateDataCounters(kFirstSsrc);
statistics_proxy_->SetCpuScalingStats(0);
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
quality_counts.fps = -1;
quality_counts.resolution = -1;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
// Adapt changes: 2, elapsed time: 15 sec => 8 per minute.
statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
statistics_proxy_->OnCpuAdaptationChanged(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(6000);
statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
statistics_proxy_->OnCpuAdaptationChanged(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(9000);
// Switch content type, real-time stats should be updated.
@ -704,13 +766,13 @@ TEST_F(SendStatisticsProxyTest, AdaptChangesReportedAfterContentSwitch) {
// First RTP packet sent, scaling enabled.
UpdateDataCounters(kFirstSsrc);
statistics_proxy_->SetCpuScalingStats(0);
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
// Adapt changes: 4, elapsed time: 120 sec => 2 per minute.
statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
statistics_proxy_->OnCpuAdaptationChanged(cpu_counts, quality_counts);
statistics_proxy_->OnCpuAdaptationChanged(cpu_counts, quality_counts);
statistics_proxy_->OnCpuAdaptationChanged(cpu_counts, quality_counts);
statistics_proxy_->OnCpuAdaptationChanged(cpu_counts, quality_counts);
fake_clock_.AdvanceTimeMilliseconds(120000);
statistics_proxy_.reset();
@ -842,8 +904,10 @@ TEST_F(SendStatisticsProxyTest, SentFpsHistogramExcludesSuspendedTime) {
}
TEST_F(SendStatisticsProxyTest, CpuLimitedHistogramNotUpdatedWhenDisabled) {
const int kNumDownscales = -1;
statistics_proxy_->SetQualityScalingStats(kNumDownscales);
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
cpu_counts.resolution = -1;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
for (int i = 0; i < SendStatisticsProxy::kMinRequiredMetricsSamples; ++i)
statistics_proxy_->OnIncomingFrame(kWidth, kHeight);
@ -854,13 +918,16 @@ TEST_F(SendStatisticsProxyTest, CpuLimitedHistogramNotUpdatedWhenDisabled) {
}
TEST_F(SendStatisticsProxyTest, CpuLimitedHistogramUpdated) {
const int kNumDownscales = 0;
statistics_proxy_->SetCpuScalingStats(kNumDownscales);
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
cpu_counts.resolution = 0;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
for (int i = 0; i < SendStatisticsProxy::kMinRequiredMetricsSamples; ++i)
statistics_proxy_->OnIncomingFrame(kWidth, kHeight);
statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
cpu_counts.resolution = 1;
statistics_proxy_->OnCpuAdaptationChanged(cpu_counts, quality_counts);
for (int i = 0; i < SendStatisticsProxy::kMinRequiredMetricsSamples; ++i)
statistics_proxy_->OnIncomingFrame(kWidth, kHeight);
@ -1149,9 +1216,11 @@ TEST_F(SendStatisticsProxyTest,
TEST_F(SendStatisticsProxyTest,
QualityLimitedHistogramsNotUpdatedWhenDisabled) {
const int kNumDownscales = -1;
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
quality_counts.resolution = -1;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
EncodedImage encoded_image;
statistics_proxy_->SetQualityScalingStats(kNumDownscales);
for (int i = 0; i < SendStatisticsProxy::kMinRequiredMetricsSamples; ++i)
statistics_proxy_->OnSendEncodedImage(encoded_image, &kDefaultCodecInfo);
@ -1165,9 +1234,11 @@ TEST_F(SendStatisticsProxyTest,
TEST_F(SendStatisticsProxyTest,
QualityLimitedHistogramsUpdatedWhenEnabled_NoResolutionDownscale) {
const int kNumDownscales = 0;
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
quality_counts.resolution = 0;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
EncodedImage encoded_image;
statistics_proxy_->SetQualityScalingStats(kNumDownscales);
for (int i = 0; i < SendStatisticsProxy::kMinRequiredMetricsSamples; ++i)
statistics_proxy_->OnSendEncodedImage(encoded_image, &kDefaultCodecInfo);
@ -1185,8 +1256,11 @@ TEST_F(SendStatisticsProxyTest,
TEST_F(SendStatisticsProxyTest,
QualityLimitedHistogramsUpdatedWhenEnabled_TwoResolutionDownscales) {
const int kDownscales = 2;
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
quality_counts.resolution = kDownscales;
statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
EncodedImage encoded_image;
statistics_proxy_->OnQualityRestrictedResolutionChanged(kDownscales);
for (int i = 0; i < SendStatisticsProxy::kMinRequiredMetricsSamples; ++i)
statistics_proxy_->OnSendEncodedImage(encoded_image, &kDefaultCodecInfo);
// Histograms are updated when the statistics_proxy_ is deleted.
@ -1221,7 +1295,10 @@ TEST_F(SendStatisticsProxyTest, GetStatsReportsBandwidthLimitedResolution) {
EXPECT_FALSE(statistics_proxy_->GetStats().bw_limited_resolution);
// Resolution scaled due to quality.
statistics_proxy_->OnQualityRestrictedResolutionChanged(1);
ViEEncoder::AdaptCounts cpu_counts;
ViEEncoder::AdaptCounts quality_counts;
quality_counts.resolution = 1;
statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
statistics_proxy_->OnSendEncodedImage(encoded_image, nullptr);
EXPECT_TRUE(statistics_proxy_->GetStats().bw_limited_resolution);
}

View File

@ -73,6 +73,22 @@ uint32_t MaximumFrameSizeForBitrate(uint32_t kbps) {
return std::numeric_limits<uint32_t>::max();
}
bool IsResolutionScalingEnabled(
VideoSendStream::DegradationPreference degradation_preference) {
return degradation_preference ==
VideoSendStream::DegradationPreference::kMaintainFramerate ||
degradation_preference ==
VideoSendStream::DegradationPreference::kBalanced;
}
bool IsFramerateScalingEnabled(
VideoSendStream::DegradationPreference degradation_preference) {
return degradation_preference ==
VideoSendStream::DegradationPreference::kMaintainResolution ||
degradation_preference ==
VideoSendStream::DegradationPreference::kBalanced;
}
} // namespace
class ViEEncoder::ConfigureEncoderTask : public rtc::QueuedTask {
@ -420,8 +436,7 @@ void ViEEncoder::SetBitrateObserver(
void ViEEncoder::SetSource(
rtc::VideoSourceInterface<VideoFrame>* source,
const VideoSendStream::VideoSendStream::DegradationPreference&
degradation_preference) {
const VideoSendStream::DegradationPreference& degradation_preference) {
RTC_DCHECK_RUN_ON(&thread_checker_);
source_proxy_->SetSource(source, degradation_preference);
encoder_queue_.PostTask([this, degradation_preference] {
@ -547,27 +562,24 @@ void ViEEncoder::ConfigureQualityScaler() {
const bool quality_scaling_allowed =
degradation_preference_allows_scaling && scaling_settings.enabled;
const std::vector<int>& scale_counters = GetScaleCounters();
stats_proxy_->SetCpuScalingStats(
degradation_preference_allows_scaling ? scale_counters[kCpu] : -1);
stats_proxy_->SetQualityScalingStats(
quality_scaling_allowed ? scale_counters[kQuality] : -1);
if (quality_scaling_allowed) {
// Abort if quality scaler has already been configured.
if (quality_scaler_.get() != nullptr)
return;
// Drop frames and scale down until desired quality is achieved.
if (scaling_settings.thresholds) {
quality_scaler_.reset(
new QualityScaler(this, *(scaling_settings.thresholds)));
} else {
quality_scaler_.reset(new QualityScaler(this, codec_type_));
if (quality_scaler_.get() == nullptr) {
// Quality scaler has not already been configured.
// Drop frames and scale down until desired quality is achieved.
if (scaling_settings.thresholds) {
quality_scaler_.reset(
new QualityScaler(this, *(scaling_settings.thresholds)));
} else {
quality_scaler_.reset(new QualityScaler(this, codec_type_));
}
}
} else {
quality_scaler_.reset(nullptr);
initial_rampup_ = kMaxInitialFramedrop;
}
stats_proxy_->SetAdaptationStats(GetActiveCounts(kCpu),
GetActiveCounts(kQuality));
}
void ViEEncoder::OnFrame(const VideoFrame& video_frame) {
@ -797,6 +809,7 @@ void ViEEncoder::AdaptDown(AdaptReason reason) {
last_frame_info_->pixel_count(),
stats_proxy_->GetStats().input_frame_rate,
AdaptationRequest::Mode::kAdaptDown};
bool downgrade_requested =
last_adaptation_request_ &&
last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptDown;
@ -834,8 +847,7 @@ void ViEEncoder::AdaptDown(AdaptReason reason) {
}
if (reason == kCpu) {
const int cpu_scale_counter = GetScaleCounters()[reason];
if (cpu_scale_counter >= max_downgrades)
if (GetConstAdaptCounter().TotalCount(kCpu) >= max_downgrades)
return;
}
@ -848,11 +860,13 @@ void ViEEncoder::AdaptDown(AdaptReason reason) {
return;
}
LOG(LS_INFO) << "Scaling down resolution.";
GetAdaptCounter().IncrementResolution(reason, 1);
break;
case VideoSendStream::DegradationPreference::kMaintainResolution:
source_proxy_->RequestFramerateLowerThan(
adaptation_request.framerate_fps_);
LOG(LS_INFO) << "Scaling down framerate.";
GetAdaptCounter().IncrementFramerate(reason, 1);
break;
case VideoSendStream::DegradationPreference::kDegradationDisabled:
RTC_NOTREACHED();
@ -860,32 +874,20 @@ void ViEEncoder::AdaptDown(AdaptReason reason) {
last_adaptation_request_.emplace(adaptation_request);
IncrementScaleCounter(reason, 1);
UpdateAdaptationStats(reason);
// Update stats.
const std::vector<int>& scale_counters = GetScaleCounters();
switch (reason) {
case kQuality:
stats_proxy_->OnQualityRestrictedResolutionChanged(
scale_counters[reason]);
break;
case kCpu:
stats_proxy_->OnCpuRestrictedResolutionChanged(true);
break;
}
for (size_t i = 0; i < kScaleReasonSize; ++i) {
LOG(LS_INFO) << "Scaled " << scale_counters[i]
<< " times for reason: " << (i ? "cpu" : "quality");
}
LOG(LS_INFO) << GetConstAdaptCounter().ToString();
}
void ViEEncoder::AdaptUp(AdaptReason reason) {
RTC_DCHECK_RUN_ON(&encoder_queue_);
int scale_counter = GetScaleCounters()[reason];
if (scale_counter == 0)
const AdaptCounter& adapt_counter = GetConstAdaptCounter();
int num_downgrades = adapt_counter.TotalCount(reason);
if (num_downgrades == 0)
return;
RTC_DCHECK_GT(scale_counter, 0);
RTC_DCHECK_GT(num_downgrades, 0);
AdaptationRequest adaptation_request = {
last_frame_info_->pixel_count(),
stats_proxy_->GetStats().input_frame_rate,
@ -894,6 +896,7 @@ void ViEEncoder::AdaptUp(AdaptReason reason) {
bool adapt_up_requested =
last_adaptation_request_ &&
last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptUp;
switch (degradation_preference_) {
case VideoSendStream::DegradationPreference::kBalanced:
FALLTHROUGH();
@ -914,20 +917,11 @@ void ViEEncoder::AdaptUp(AdaptReason reason) {
return;
}
// Decrease counter of how many times we have scaled down, for this
// degradation preference mode and reason.
IncrementScaleCounter(reason, -1);
// Get a sum of how many times have scaled down, in total, for this
// degradation preference mode. If it is 0, remove any restraints.
const std::vector<int>& scale_counters = GetScaleCounters();
const int scale_sum =
std::accumulate(scale_counters.begin(), scale_counters.end(), 0);
switch (degradation_preference_) {
case VideoSendStream::DegradationPreference::kBalanced:
FALLTHROUGH();
case VideoSendStream::DegradationPreference::kMaintainFramerate:
if (scale_sum == 0) {
if (adapt_counter.TotalCount() == 1) {
LOG(LS_INFO) << "Removing resolution down-scaling setting.";
source_proxy_->RequestHigherResolutionThan(
std::numeric_limits<int>::max());
@ -936,9 +930,10 @@ void ViEEncoder::AdaptUp(AdaptReason reason) {
adaptation_request.input_pixel_count_);
LOG(LS_INFO) << "Scaling up resolution.";
}
GetAdaptCounter().IncrementResolution(reason, -1);
break;
case VideoSendStream::DegradationPreference::kMaintainResolution:
if (scale_sum == 0) {
if (adapt_counter.TotalCount() == 1) {
LOG(LS_INFO) << "Removing framerate down-scaling setting.";
source_proxy_->RequestHigherFramerateThan(
std::numeric_limits<int>::max());
@ -947,6 +942,7 @@ void ViEEncoder::AdaptUp(AdaptReason reason) {
adaptation_request.framerate_fps_);
LOG(LS_INFO) << "Scaling up framerate.";
}
GetAdaptCounter().IncrementFramerate(reason, -1);
break;
case VideoSendStream::DegradationPreference::kDegradationDisabled:
RTC_NOTREACHED();
@ -954,40 +950,120 @@ void ViEEncoder::AdaptUp(AdaptReason reason) {
last_adaptation_request_.emplace(adaptation_request);
// Update stats.
UpdateAdaptationStats(reason);
LOG(LS_INFO) << adapt_counter.ToString();
}
void ViEEncoder::UpdateAdaptationStats(AdaptReason reason) {
switch (reason) {
case kQuality:
stats_proxy_->OnQualityRestrictedResolutionChanged(
scale_counters[reason]);
break;
case kCpu:
stats_proxy_->OnCpuRestrictedResolutionChanged(scale_counters[reason] >
0);
stats_proxy_->OnCpuAdaptationChanged(GetActiveCounts(kCpu),
GetActiveCounts(kQuality));
break;
case kQuality:
stats_proxy_->OnQualityAdaptationChanged(GetActiveCounts(kCpu),
GetActiveCounts(kQuality));
break;
}
for (size_t i = 0; i < kScaleReasonSize; ++i) {
LOG(LS_INFO) << "Scaled " << scale_counters[i]
<< " times for reason: " << (i ? "cpu" : "quality");
}
}
const std::vector<int>& ViEEncoder::GetScaleCounters() {
auto it = scale_counters_.find(degradation_preference_);
if (it == scale_counters_.end()) {
scale_counters_[degradation_preference_].resize(kScaleReasonSize);
return scale_counters_[degradation_preference_];
ViEEncoder::AdaptCounts ViEEncoder::GetActiveCounts(AdaptReason reason) {
ViEEncoder::AdaptCounts counts = GetConstAdaptCounter().Counts(reason);
switch (reason) {
case kCpu:
if (!IsFramerateScalingEnabled(degradation_preference_))
counts.fps = -1;
if (!IsResolutionScalingEnabled(degradation_preference_))
counts.resolution = -1;
break;
case kQuality:
if (!IsFramerateScalingEnabled(degradation_preference_) ||
!quality_scaler_) {
counts.fps = -1;
}
if (!IsResolutionScalingEnabled(degradation_preference_) ||
!quality_scaler_) {
counts.resolution = -1;
}
break;
}
return it->second;
return counts;
}
void ViEEncoder::IncrementScaleCounter(int reason, int delta) {
// Get the counters and validate. This may also lazily initialize the state.
const std::vector<int>& counter = GetScaleCounters();
if (delta < 0) {
RTC_DCHECK_GE(counter[reason], delta);
ViEEncoder::AdaptCounter& ViEEncoder::GetAdaptCounter() {
return adapt_counters_[degradation_preference_];
}
const ViEEncoder::AdaptCounter& ViEEncoder::GetConstAdaptCounter() {
return adapt_counters_[degradation_preference_];
}
// Class holding adaptation information.
ViEEncoder::AdaptCounter::AdaptCounter() {
fps_counters_.resize(kScaleReasonSize);
resolution_counters_.resize(kScaleReasonSize);
}
ViEEncoder::AdaptCounter::~AdaptCounter() {}
std::string ViEEncoder::AdaptCounter::ToString() const {
std::stringstream ss;
ss << "Downgrade counts: fps: {" << ToString(fps_counters_);
ss << "}, resolution: {" << ToString(resolution_counters_) << "}";
return ss.str();
}
ViEEncoder::AdaptCounts ViEEncoder::AdaptCounter::Counts(int reason) const {
AdaptCounts counts;
counts.fps = fps_counters_[reason];
counts.resolution = resolution_counters_[reason];
return counts;
}
void ViEEncoder::AdaptCounter::IncrementFramerate(int reason, int delta) {
fps_counters_[reason] += delta;
}
void ViEEncoder::AdaptCounter::IncrementResolution(int reason, int delta) {
resolution_counters_[reason] += delta;
}
int ViEEncoder::AdaptCounter::FramerateCount() const {
return Count(fps_counters_);
}
int ViEEncoder::AdaptCounter::ResolutionCount() const {
return Count(resolution_counters_);
}
int ViEEncoder::AdaptCounter::TotalCount() const {
return FramerateCount() + ResolutionCount();
}
int ViEEncoder::AdaptCounter::FramerateCount(int reason) const {
return fps_counters_[reason];
}
int ViEEncoder::AdaptCounter::ResolutionCount(int reason) const {
return resolution_counters_[reason];
}
int ViEEncoder::AdaptCounter::TotalCount(int reason) const {
return FramerateCount(reason) + ResolutionCount(reason);
}
int ViEEncoder::AdaptCounter::Count(const std::vector<int>& counters) const {
return std::accumulate(counters.begin(), counters.end(), 0);
}
std::string ViEEncoder::AdaptCounter::ToString(
const std::vector<int>& counters) const {
std::stringstream ss;
for (size_t reason = 0; reason < kScaleReasonSize; ++reason) {
ss << (reason ? " cpu" : "quality") << ":" << counters[reason];
}
scale_counters_[degradation_preference_][reason] += delta;
return ss.str();
}
} // namespace webrtc

View File

@ -62,6 +62,12 @@ class ViEEncoder : public rtc::VideoSinkInterface<VideoFrame>,
int min_transmit_bitrate_bps) = 0;
};
// Number of resolution and framerate reductions (-1: disabled).
struct AdaptCounts {
int resolution = 0;
int fps = 0;
};
// Downscale resolution at most 2 times for CPU reasons.
static const int kMaxCpuResolutionDowngrades = 2;
// Downscale framerate at most 4 times.
@ -172,10 +178,44 @@ class ViEEncoder : public rtc::VideoSinkInterface<VideoFrame>,
void TraceFrameDropStart();
void TraceFrameDropEnd();
const std::vector<int>& GetScaleCounters()
EXCLUSIVE_LOCKS_REQUIRED(&encoder_queue_);
void IncrementScaleCounter(int reason, int delta)
EXCLUSIVE_LOCKS_REQUIRED(&encoder_queue_);
// Class holding adaptation information.
class AdaptCounter final {
public:
AdaptCounter();
~AdaptCounter();
// Get number of adaptation downscales for |reason|.
AdaptCounts Counts(int reason) const;
std::string ToString() const;
void IncrementFramerate(int reason, int delta);
void IncrementResolution(int reason, int delta);
// Gets the total number of downgrades (for all adapt reasons).
int FramerateCount() const;
int ResolutionCount() const;
int TotalCount() const;
// Gets the total number of downgrades for |reason|.
int FramerateCount(int reason) const;
int ResolutionCount(int reason) const;
int TotalCount(int reason) const;
private:
std::string ToString(const std::vector<int>& counters) const;
int Count(const std::vector<int>& counters) const;
// Degradation counters holding number of framerate/resolution reductions
// per adapt reason.
std::vector<int> fps_counters_;
std::vector<int> resolution_counters_;
};
AdaptCounter& GetAdaptCounter() RUN_ON(&encoder_queue_);
const AdaptCounter& GetConstAdaptCounter() RUN_ON(&encoder_queue_);
void UpdateAdaptationStats(AdaptReason reason) RUN_ON(&encoder_queue_);
AdaptCounts GetActiveCounts(AdaptReason reason) RUN_ON(&encoder_queue_);
rtc::Event shutdown_event_;
@ -214,13 +254,14 @@ class ViEEncoder : public rtc::VideoSinkInterface<VideoFrame>,
uint32_t last_observed_bitrate_bps_ ACCESS_ON(&encoder_queue_);
bool encoder_paused_and_dropped_frame_ ACCESS_ON(&encoder_queue_);
Clock* const clock_;
// Counters used for deciding if the video resolution is currently
// restricted, and if so, why, on a per degradation preference basis.
// Counters used for deciding if the video resolution or framerate is
// currently restricted, and if so, why, on a per degradation preference
// basis.
// TODO(sprang): Replace this with a state holding a relative overuse measure
// instead, that can be translated into suitable down-scale or fps limit.
std::map<const VideoSendStream::DegradationPreference, std::vector<int>>
scale_counters_ ACCESS_ON(&encoder_queue_);
// Set depending on degradation preferences
std::map<const VideoSendStream::DegradationPreference, AdaptCounter>
adapt_counters_ ACCESS_ON(&encoder_queue_);
// Set depending on degradation preferences.
VideoSendStream::DegradationPreference degradation_preference_
ACCESS_ON(&encoder_queue_);

View File

@ -139,6 +139,11 @@ class AdaptingFrameForwarder : public test::FrameForwarder {
return adaptation_enabled_;
}
rtc::VideoSinkWants last_wants() const {
rtc::CritScope cs(&crit_);
return last_wants_;
}
void IncomingCapturedFrame(const VideoFrame& video_frame) override {
int cropped_width = 0;
int cropped_height = 0;
@ -163,14 +168,15 @@ class AdaptingFrameForwarder : public test::FrameForwarder {
void AddOrUpdateSink(rtc::VideoSinkInterface<VideoFrame>* sink,
const rtc::VideoSinkWants& wants) override {
rtc::CritScope cs(&crit_);
last_wants_ = sink_wants();
adapter_.OnResolutionFramerateRequest(wants.target_pixel_count,
wants.max_pixel_count,
wants.max_framerate_fps);
test::FrameForwarder::AddOrUpdateSink(sink, wants);
}
cricket::VideoAdapter adapter_;
bool adaptation_enabled_ GUARDED_BY(crit_);
rtc::VideoSinkWants last_wants_ GUARDED_BY(crit_);
};
class MockableSendStatisticsProxy : public SendStatisticsProxy {
@ -281,16 +287,41 @@ class ViEEncoderTest : public ::testing::Test {
}
void VerifyNoLimitation(const rtc::VideoSinkWants& wants) {
EXPECT_FALSE(wants.target_pixel_count);
EXPECT_EQ(std::numeric_limits<int>::max(), wants.max_pixel_count);
EXPECT_EQ(std::numeric_limits<int>::max(), wants.max_framerate_fps);
EXPECT_EQ(std::numeric_limits<int>::max(), wants.max_pixel_count);
EXPECT_FALSE(wants.target_pixel_count);
}
void VerifyResolutionLimitationLessThan(const rtc::VideoSinkWants& wants,
int pixel_count) {
void VerifyFpsEqResolutionEq(const rtc::VideoSinkWants& wants1,
const rtc::VideoSinkWants& wants2) {
EXPECT_EQ(wants1.max_framerate_fps, wants2.max_framerate_fps);
EXPECT_EQ(wants1.max_pixel_count, wants2.max_pixel_count);
}
void VerifyFpsMaxResolutionLt(const rtc::VideoSinkWants& wants1,
const rtc::VideoSinkWants& wants2) {
EXPECT_EQ(std::numeric_limits<int>::max(), wants1.max_framerate_fps);
EXPECT_LT(wants1.max_pixel_count, wants2.max_pixel_count);
EXPECT_GT(wants1.max_pixel_count, 0);
}
void VerifyFpsMaxResolutionGt(const rtc::VideoSinkWants& wants1,
const rtc::VideoSinkWants& wants2) {
EXPECT_EQ(std::numeric_limits<int>::max(), wants1.max_framerate_fps);
EXPECT_GT(wants1.max_pixel_count, wants2.max_pixel_count);
}
void VerifyFpsMaxResolutionLt(const rtc::VideoSinkWants& wants,
int pixel_count) {
EXPECT_EQ(std::numeric_limits<int>::max(), wants.max_framerate_fps);
EXPECT_LT(wants.max_pixel_count, pixel_count);
EXPECT_GT(wants.max_pixel_count, 0);
EXPECT_EQ(std::numeric_limits<int>::max(), wants.max_framerate_fps);
}
void VerifyFpsLtResolutionMax(const rtc::VideoSinkWants& wants, int fps) {
EXPECT_LT(wants.max_framerate_fps, fps);
EXPECT_EQ(std::numeric_limits<int>::max(), wants.max_pixel_count);
EXPECT_FALSE(wants.target_pixel_count);
}
class TestEncoder : public test::FakeEncoder {
@ -946,6 +977,7 @@ TEST_F(ViEEncoderTest, SwitchingSourceKeepsCpuAdaptation) {
video_source_.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
sink_.WaitForEncodedFrame(1);
VideoSendStream::Stats stats = stats_proxy_->GetStats();
EXPECT_FALSE(stats.bw_limited_resolution);
EXPECT_FALSE(stats.cpu_limited_resolution);
EXPECT_EQ(0, stats.number_of_cpu_adapt_changes);
@ -954,6 +986,7 @@ TEST_F(ViEEncoderTest, SwitchingSourceKeepsCpuAdaptation) {
video_source_.IncomingCapturedFrame(CreateFrame(2, kWidth, kHeight));
sink_.WaitForEncodedFrame(2);
stats = stats_proxy_->GetStats();
EXPECT_FALSE(stats.bw_limited_resolution);
EXPECT_TRUE(stats.cpu_limited_resolution);
EXPECT_EQ(1, stats.number_of_cpu_adapt_changes);
@ -966,6 +999,7 @@ TEST_F(ViEEncoderTest, SwitchingSourceKeepsCpuAdaptation) {
new_video_source.IncomingCapturedFrame(CreateFrame(3, kWidth, kHeight));
sink_.WaitForEncodedFrame(3);
stats = stats_proxy_->GetStats();
EXPECT_FALSE(stats.bw_limited_resolution);
EXPECT_TRUE(stats.cpu_limited_resolution);
EXPECT_EQ(1, stats.number_of_cpu_adapt_changes);
@ -977,6 +1011,7 @@ TEST_F(ViEEncoderTest, SwitchingSourceKeepsCpuAdaptation) {
new_video_source.IncomingCapturedFrame(CreateFrame(4, kWidth, kHeight));
sink_.WaitForEncodedFrame(4);
stats = stats_proxy_->GetStats();
EXPECT_FALSE(stats.bw_limited_resolution);
EXPECT_FALSE(stats.cpu_limited_resolution);
EXPECT_EQ(1, stats.number_of_cpu_adapt_changes);
@ -988,6 +1023,7 @@ TEST_F(ViEEncoderTest, SwitchingSourceKeepsCpuAdaptation) {
new_video_source.IncomingCapturedFrame(CreateFrame(5, kWidth, kHeight));
sink_.WaitForEncodedFrame(5);
stats = stats_proxy_->GetStats();
EXPECT_FALSE(stats.bw_limited_resolution);
EXPECT_TRUE(stats.cpu_limited_resolution);
EXPECT_EQ(1, stats.number_of_cpu_adapt_changes);
@ -996,6 +1032,7 @@ TEST_F(ViEEncoderTest, SwitchingSourceKeepsCpuAdaptation) {
new_video_source.IncomingCapturedFrame(CreateFrame(6, kWidth, kHeight));
sink_.WaitForEncodedFrame(6);
stats = stats_proxy_->GetStats();
EXPECT_FALSE(stats.bw_limited_resolution);
EXPECT_FALSE(stats.cpu_limited_resolution);
EXPECT_EQ(2, stats.number_of_cpu_adapt_changes);
EXPECT_EQ(0, stats.number_of_quality_adapt_changes);
@ -1138,6 +1175,7 @@ TEST_F(ViEEncoderTest, StatsTracksCpuAdaptationStatsWhenSwitchingSource) {
sink_.WaitForEncodedFrame(sequence++);
stats = stats_proxy_->GetStats();
EXPECT_TRUE(stats.cpu_limited_resolution);
EXPECT_FALSE(stats.cpu_limited_framerate);
EXPECT_EQ(1, stats.number_of_cpu_adapt_changes);
// Set cpu adaptation by frame dropping.
@ -1150,10 +1188,11 @@ TEST_F(ViEEncoderTest, StatsTracksCpuAdaptationStatsWhenSwitchingSource) {
stats = stats_proxy_->GetStats();
// Not adapted at first.
EXPECT_FALSE(stats.cpu_limited_resolution);
EXPECT_FALSE(stats.cpu_limited_framerate);
EXPECT_EQ(1, stats.number_of_cpu_adapt_changes);
// Force an input frame rate to be available, or the adaptation call won't
// know what framerate to adapt form.
// know what framerate to adapt from.
VideoSendStream::Stats mock_stats = stats_proxy_->GetStats();
mock_stats.input_frame_rate = 30;
stats_proxy_->SetMockStats(mock_stats);
@ -1166,7 +1205,8 @@ TEST_F(ViEEncoderTest, StatsTracksCpuAdaptationStatsWhenSwitchingSource) {
// Framerate now adapted.
stats = stats_proxy_->GetStats();
EXPECT_TRUE(stats.cpu_limited_resolution);
EXPECT_FALSE(stats.cpu_limited_resolution);
EXPECT_TRUE(stats.cpu_limited_framerate);
EXPECT_EQ(2, stats.number_of_cpu_adapt_changes);
// Disable CPU adaptation.
@ -1314,7 +1354,7 @@ TEST_F(ViEEncoderTest, SkipsSameAdaptDownRequest_MaintainFramerateMode) {
// Trigger adapt down, expect scaled down resolution.
vie_encoder_->TriggerCpuOveruse();
VerifyResolutionLimitationLessThan(source.sink_wants(), kWidth * kHeight);
VerifyFpsMaxResolutionLt(source.sink_wants(), kWidth * kHeight);
const int kLastMaxPixelCount = source.sink_wants().max_pixel_count;
EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
EXPECT_EQ(1, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
@ -1366,18 +1406,45 @@ TEST_F(ViEEncoderTest, NoChangeForInitialNormalUsage_MaintainResolutionMode) {
source.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
sink_.WaitForEncodedFrame(kWidth, kHeight);
VerifyNoLimitation(source.sink_wants());
EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
EXPECT_EQ(0, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
// Trigger adapt up, expect no change.
vie_encoder_->TriggerCpuNormalUsage();
VerifyNoLimitation(source.sink_wants());
EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
EXPECT_EQ(0, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
vie_encoder_->Stop();
}
TEST_F(ViEEncoderTest, NoChangeForInitialNormalUsage_DisabledMode) {
const int kWidth = 1280;
const int kHeight = 720;
vie_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
// Enable kDegradationDisabled preference, no initial limitation.
test::FrameForwarder source;
vie_encoder_->SetSource(
&source, VideoSendStream::DegradationPreference::kDegradationDisabled);
source.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
sink_.WaitForEncodedFrame(kWidth, kHeight);
VerifyNoLimitation(source.sink_wants());
EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
// Trigger adapt up, expect no change.
vie_encoder_->TriggerQualityHigh();
VerifyNoLimitation(source.sink_wants());
EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
vie_encoder_->Stop();
}
TEST_F(ViEEncoderTest, AdaptsResolutionForLowQuality_MaintainFramerateMode) {
const int kWidth = 1280;
const int kHeight = 720;
@ -1399,7 +1466,7 @@ TEST_F(ViEEncoderTest, AdaptsResolutionForLowQuality_MaintainFramerateMode) {
vie_encoder_->TriggerQualityLow();
source.IncomingCapturedFrame(CreateFrame(2, kWidth, kHeight));
sink_.WaitForEncodedFrame(2);
VerifyResolutionLimitationLessThan(source.sink_wants(), kWidth * kHeight);
VerifyFpsMaxResolutionLt(source.sink_wants(), kWidth * kHeight);
EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
@ -1413,6 +1480,47 @@ TEST_F(ViEEncoderTest, AdaptsResolutionForLowQuality_MaintainFramerateMode) {
vie_encoder_->Stop();
}
TEST_F(ViEEncoderTest, AdaptsFramerateForLowQuality_MaintainResolutionMode) {
const int kWidth = 1280;
const int kHeight = 720;
const int kInputFps = 30;
vie_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
VideoSendStream::Stats stats = stats_proxy_->GetStats();
stats.input_frame_rate = kInputFps;
stats_proxy_->SetMockStats(stats);
// Expect no scaling to begin with (preference: kMaintainFramerate).
video_source_.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
sink_.WaitForEncodedFrame(1);
VerifyNoLimitation(video_source_.sink_wants());
// Trigger adapt down, expect scaled down resolution.
vie_encoder_->TriggerQualityLow();
video_source_.IncomingCapturedFrame(CreateFrame(2, kWidth, kHeight));
sink_.WaitForEncodedFrame(2);
VerifyFpsMaxResolutionLt(video_source_.sink_wants(), kWidth * kHeight);
// Enable kMaintainResolution preference.
test::FrameForwarder new_video_source;
vie_encoder_->SetSource(
&new_video_source,
VideoSendStream::DegradationPreference::kMaintainResolution);
VerifyNoLimitation(new_video_source.sink_wants());
// Trigger adapt down, expect reduced framerate.
vie_encoder_->TriggerQualityLow();
new_video_source.IncomingCapturedFrame(CreateFrame(3, kWidth, kHeight));
sink_.WaitForEncodedFrame(3);
VerifyFpsLtResolutionMax(new_video_source.sink_wants(), kInputFps);
// Trigger adapt up, expect no restriction.
vie_encoder_->TriggerQualityHigh();
VerifyNoLimitation(new_video_source.sink_wants());
vie_encoder_->Stop();
}
TEST_F(ViEEncoderTest, DoesNotScaleBelowSetResolutionLimit) {
const int kWidth = 1280;
const int kHeight = 720;
@ -1461,7 +1569,7 @@ TEST_F(ViEEncoderTest,
&source, VideoSendStream::DegradationPreference::kMaintainFramerate);
source.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
sink_.WaitForEncodedFrame(1);
sink_.WaitForEncodedFrame(kWidth, kHeight);
VerifyNoLimitation(source.sink_wants());
EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
EXPECT_EQ(0, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
@ -1470,14 +1578,14 @@ TEST_F(ViEEncoderTest,
vie_encoder_->TriggerCpuOveruse();
source.IncomingCapturedFrame(CreateFrame(2, kWidth, kHeight));
sink_.WaitForEncodedFrame(2);
VerifyResolutionLimitationLessThan(source.sink_wants(), kWidth * kHeight);
VerifyFpsMaxResolutionLt(source.sink_wants(), kWidth * kHeight);
EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
EXPECT_EQ(1, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
// Trigger adapt up, expect no restriction.
vie_encoder_->TriggerCpuNormalUsage();
source.IncomingCapturedFrame(CreateFrame(3, kWidth, kHeight));
sink_.WaitForEncodedFrame(3);
sink_.WaitForEncodedFrame(kWidth, kHeight);
VerifyNoLimitation(source.sink_wants());
EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
EXPECT_EQ(2, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
@ -1486,12 +1594,14 @@ TEST_F(ViEEncoderTest,
vie_encoder_->TriggerCpuOveruse();
source.IncomingCapturedFrame(CreateFrame(4, kWidth, kHeight));
sink_.WaitForEncodedFrame(4);
VerifyResolutionLimitationLessThan(source.sink_wants(), kWidth * kHeight);
VerifyFpsMaxResolutionLt(source.sink_wants(), kWidth * kHeight);
EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
EXPECT_EQ(3, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
// Trigger adapt up, expect no restriction.
vie_encoder_->TriggerCpuNormalUsage();
source.IncomingCapturedFrame(CreateFrame(5, kWidth, kHeight));
sink_.WaitForEncodedFrame(kWidth, kHeight);
VerifyNoLimitation(source.sink_wants());
EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
EXPECT_EQ(4, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
@ -1523,8 +1633,7 @@ TEST_F(ViEEncoderTest,
vie_encoder_->TriggerCpuOveruse();
source.IncomingCapturedFrame(CreateFrame(2, kWidth, kHeight));
sink_.WaitForEncodedFrame(2);
VerifyResolutionLimitationLessThan(source.sink_wants(), kWidth * kHeight);
rtc::VideoSinkWants last_wants = source.sink_wants();
VerifyFpsMaxResolutionLt(source.sink_wants(), kWidth * kHeight);
EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
EXPECT_EQ(1, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
@ -1534,8 +1643,8 @@ TEST_F(ViEEncoderTest,
vie_encoder_->TriggerCpuOveruse();
source.IncomingCapturedFrame(CreateFrame(3, kWidth, kHeight));
sink_.WaitForEncodedFrame(3);
EXPECT_LT(source.sink_wants().max_pixel_count, last_wants.max_pixel_count);
last_wants = source.sink_wants();
VerifyFpsMaxResolutionLt(source.sink_wants(), source.last_wants());
rtc::VideoSinkWants last_wants = source.sink_wants();
EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
EXPECT_EQ(2, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
@ -1545,7 +1654,7 @@ TEST_F(ViEEncoderTest,
vie_encoder_->TriggerCpuOveruse();
source.IncomingCapturedFrame(CreateFrame(4, kWidth, kHeight));
sink_.WaitForEncodedFrame(4);
EXPECT_EQ(last_wants.max_pixel_count, source.sink_wants().max_pixel_count);
VerifyFpsEqResolutionEq(source.sink_wants(), last_wants);
EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
EXPECT_EQ(2, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
@ -1555,8 +1664,7 @@ TEST_F(ViEEncoderTest,
vie_encoder_->TriggerQualityLow();
source.IncomingCapturedFrame(CreateFrame(5, kWidth, kHeight));
sink_.WaitForEncodedFrame(5);
EXPECT_LT(source.sink_wants().max_pixel_count, last_wants.max_pixel_count);
last_wants = source.sink_wants();
VerifyFpsMaxResolutionLt(source.sink_wants(), source.last_wants());
EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
EXPECT_EQ(2, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
@ -1566,8 +1674,7 @@ TEST_F(ViEEncoderTest,
vie_encoder_->TriggerCpuNormalUsage();
source.IncomingCapturedFrame(CreateFrame(6, kWidth, kHeight));
sink_.WaitForEncodedFrame(6);
EXPECT_GT(source.sink_wants().max_pixel_count, last_wants.max_pixel_count);
last_wants = source.sink_wants();
VerifyFpsMaxResolutionGt(source.sink_wants(), source.last_wants());
EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
EXPECT_EQ(3, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
@ -1577,7 +1684,7 @@ TEST_F(ViEEncoderTest,
vie_encoder_->TriggerCpuNormalUsage();
source.IncomingCapturedFrame(CreateFrame(7, kWidth, kHeight));
sink_.WaitForEncodedFrame(7);
EXPECT_GT(source.sink_wants().max_pixel_count, last_wants.max_pixel_count);
VerifyFpsMaxResolutionGt(source.sink_wants(), source.last_wants());
last_wants = source.sink_wants();
EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
@ -1588,7 +1695,7 @@ TEST_F(ViEEncoderTest,
vie_encoder_->TriggerCpuNormalUsage();
source.IncomingCapturedFrame(CreateFrame(8, kWidth, kHeight));
sink_.WaitForEncodedFrame(8);
EXPECT_EQ(last_wants.max_pixel_count, source.sink_wants().max_pixel_count);
VerifyFpsEqResolutionEq(source.sink_wants(), last_wants);
EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
EXPECT_EQ(4, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
@ -1598,7 +1705,7 @@ TEST_F(ViEEncoderTest,
vie_encoder_->TriggerQualityHigh();
source.IncomingCapturedFrame(CreateFrame(9, kWidth, kHeight));
sink_.WaitForEncodedFrame(kWidth, kHeight);
EXPECT_GT(source.sink_wants().max_pixel_count, last_wants.max_pixel_count);
VerifyFpsMaxResolutionGt(source.sink_wants(), source.last_wants());
VerifyNoLimitation(source.sink_wants());
EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
@ -1730,7 +1837,7 @@ TEST_F(ViEEncoderTest, DropsFramesAndScalesWhenBitrateIsTooLow) {
vie_encoder_->Stop();
}
TEST_F(ViEEncoderTest, NrOfDroppedFramesLimitedWhenBitrateIsTooLow) {
TEST_F(ViEEncoderTest, NumberOfDroppedFramesLimitedWhenBitrateIsTooLow) {
const int kTooLowBitrateForFrameSizeBps = 10000;
vie_encoder_->OnBitrateUpdated(kTooLowBitrateForFrameSizeBps, 0, 0);
const int kWidth = 640;
@ -1774,6 +1881,7 @@ TEST_F(ViEEncoderTest, InitialFrameDropOffWhenEncoderDisabledScaling) {
const int kHeight = 360;
fake_encoder_.SetQualityScaling(false);
vie_encoder_->OnBitrateUpdated(kLowTargetBitrateBps, 0, 0);
// Force quality scaler reconfiguration by resetting the source.
vie_encoder_->SetSource(&video_source_,
VideoSendStream::DegradationPreference::kBalanced);

View File

@ -69,6 +69,8 @@ class VideoSendStream {
bool suspended = false;
bool bw_limited_resolution = false;
bool cpu_limited_resolution = false;
bool bw_limited_framerate = false;
bool cpu_limited_framerate = false;
// Total number of times resolution as been requested to be changed due to
// CPU/quality adaptation.
int number_of_cpu_adapt_changes = 0;