Replace AdaptCount with a single counter.
There is still a counter for the active counts for the
scaling, but these will be removed at a later date.
BUG=webrtc:11392
Change-Id: Ie9bcf3f744a0bbac601f0da61197f4bac1e9f879
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/169447
Reviewed-by: Åsa Persson <asapersson@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Commit-Queue: Evan Shrubsole <eshr@google.com>
Cr-Commit-Position: refs/heads/master@{#30701}
diff --git a/video/BUILD.gn b/video/BUILD.gn
index 2f7ec17..a3e7d07 100644
--- a/video/BUILD.gn
+++ b/video/BUILD.gn
@@ -504,6 +504,7 @@
"end_to_end_tests/stats_tests.cc",
"end_to_end_tests/transport_feedback_tests.cc",
"frame_encode_metadata_writer_unittest.cc",
+ "overuse_frame_detector_resource_adaptation_unittest.cc",
"overuse_frame_detector_unittest.cc",
"picture_id_tests.cc",
"quality_limitation_reason_tracker_unittest.cc",
diff --git a/video/overuse_frame_detector_resource_adaptation_module.cc b/video/overuse_frame_detector_resource_adaptation_module.cc
index 1b88bb3..792abc5 100644
--- a/video/overuse_frame_detector_resource_adaptation_module.cc
+++ b/video/overuse_frame_detector_resource_adaptation_module.cc
@@ -67,8 +67,57 @@
return source_restrictions;
}
+// Returns AdaptationCounters where constraints that don't apply to the
+// degradation preference are cleared. This behaviour must reflect that of
+// ApplyDegradationPreference for SourceRestrictions. Any changed to that
+// method must also change this one.
+AdaptationCounters ApplyDegradationPreference(
+ AdaptationCounters counters,
+ DegradationPreference degradation_preference) {
+ switch (degradation_preference) {
+ case DegradationPreference::BALANCED:
+ break;
+ case DegradationPreference::MAINTAIN_FRAMERATE:
+ counters.fps_adaptations = 0;
+ break;
+ case DegradationPreference::MAINTAIN_RESOLUTION:
+ counters.resolution_adaptations = 0;
+ break;
+ case DegradationPreference::DISABLED:
+ counters.resolution_adaptations = 0;
+ counters.fps_adaptations = 0;
+ break;
+ default:
+ RTC_NOTREACHED();
+ }
+ return counters;
+}
+
} // namespace
+bool AdaptationCounters::operator==(const AdaptationCounters& rhs) const {
+ return fps_adaptations == rhs.fps_adaptations &&
+ resolution_adaptations == rhs.resolution_adaptations;
+}
+
+bool AdaptationCounters::operator!=(const AdaptationCounters& rhs) const {
+ return !(rhs == *this);
+}
+
+AdaptationCounters AdaptationCounters::operator+(
+ const AdaptationCounters& other) const {
+ return AdaptationCounters(
+ resolution_adaptations + other.resolution_adaptations,
+ fps_adaptations + other.fps_adaptations);
+}
+
+AdaptationCounters AdaptationCounters::operator-(
+ const AdaptationCounters& other) const {
+ return AdaptationCounters(
+ resolution_adaptations - other.resolution_adaptations,
+ fps_adaptations - other.fps_adaptations);
+}
+
// VideoSourceRestrictor is responsible for keeping track of current
// VideoSourceRestrictions and how to modify them in response to adapting up or
// down. It is not reponsible for determining when we should adapt up or down -
@@ -115,8 +164,10 @@
VideoSourceRestrictions source_restrictions() {
return source_restrictions_;
}
+ const AdaptationCounters& adaptation_counters() const { return adaptations_; }
void ClearRestrictions() {
source_restrictions_ = VideoSourceRestrictions();
+ adaptations_ = AdaptationCounters();
}
bool CanDecreaseResolutionTo(int target_pixels, int min_pixels_per_frame) {
@@ -135,6 +186,7 @@
? absl::optional<size_t>(target_pixels)
: absl::nullopt);
source_restrictions_.set_target_pixels_per_frame(absl::nullopt);
+ ++adaptations_.resolution_adaptations;
}
bool CanIncreaseResolutionTo(int target_pixels) {
@@ -157,6 +209,8 @@
max_pixels_wanted != std::numeric_limits<int>::max()
? absl::optional<size_t>(target_pixels)
: absl::nullopt);
+ --adaptations_.resolution_adaptations;
+ RTC_DCHECK_GE(adaptations_.resolution_adaptations, 0);
}
bool CanDecreaseFrameRateTo(int max_frame_rate) {
@@ -173,6 +227,7 @@
max_frame_rate != std::numeric_limits<int>::max()
? absl::optional<double>(max_frame_rate)
: absl::nullopt);
+ ++adaptations_.fps_adaptations;
}
bool CanIncreaseFrameRateTo(int max_frame_rate) {
@@ -187,6 +242,8 @@
max_frame_rate != std::numeric_limits<int>::max()
? absl::optional<double>(max_frame_rate)
: absl::nullopt);
+ --adaptations_.fps_adaptations;
+ RTC_DCHECK_GE(adaptations_.fps_adaptations, 0);
}
private:
@@ -207,113 +264,11 @@
}
VideoSourceRestrictions source_restrictions_;
+ AdaptationCounters adaptations_;
RTC_DISALLOW_COPY_AND_ASSIGN(VideoSourceRestrictor);
};
-class OveruseFrameDetectorResourceAdaptationModule::AdaptCounter final {
- public:
- AdaptCounter() {
- fps_counters_.resize(AdaptationObserverInterface::kScaleReasonSize);
- resolution_counters_.resize(AdaptationObserverInterface::kScaleReasonSize);
- static_assert(AdaptationObserverInterface::kScaleReasonSize == 2,
- "Update MoveCount.");
- }
- ~AdaptCounter() = default;
-
- // Get number of adaptation downscales for |reason|.
- VideoStreamEncoderObserver::AdaptationSteps Counts(int reason) const {
- VideoStreamEncoderObserver::AdaptationSteps counts;
- counts.num_framerate_reductions = fps_counters_[reason];
- counts.num_resolution_reductions = resolution_counters_[reason];
- return counts;
- }
-
- std::string ToString() const {
- rtc::StringBuilder ss;
- ss << "Downgrade counts: fps: {" << ToString(fps_counters_);
- ss << "}, resolution: {" << ToString(resolution_counters_) << "}";
- return ss.Release();
- }
-
- void IncrementFramerate(int reason) { ++(fps_counters_[reason]); }
- void IncrementResolution(int reason) { ++(resolution_counters_[reason]); }
- void DecrementFramerate(int reason) {
- if (fps_counters_[reason] == 0) {
- // Balanced mode: Adapt up is in a different order, switch reason.
- // E.g. framerate adapt down: quality (2), framerate adapt up: cpu (3).
- // 1. Down resolution (cpu): res={quality:0,cpu:1}, fps={quality:0,cpu:0}
- // 2. Down fps (quality): res={quality:0,cpu:1}, fps={quality:1,cpu:0}
- // 3. Up fps (cpu): res={quality:1,cpu:0}, fps={quality:0,cpu:0}
- // 4. Up resolution (quality):res={quality:0,cpu:0}, fps={quality:0,cpu:0}
- RTC_DCHECK_GT(TotalCount(reason), 0) << "No downgrade for reason.";
- RTC_DCHECK_GT(FramerateCount(), 0) << "Framerate not downgraded.";
- MoveCount(&resolution_counters_, reason);
- MoveCount(&fps_counters_,
- (reason + 1) % AdaptationObserverInterface::kScaleReasonSize);
- }
- --(fps_counters_[reason]);
- RTC_DCHECK_GE(fps_counters_[reason], 0);
- }
-
- void DecrementResolution(int reason) {
- if (resolution_counters_[reason] == 0) {
- // Balanced mode: Adapt up is in a different order, switch reason.
- RTC_DCHECK_GT(TotalCount(reason), 0) << "No downgrade for reason.";
- RTC_DCHECK_GT(ResolutionCount(), 0) << "Resolution not downgraded.";
- MoveCount(&fps_counters_, reason);
- MoveCount(&resolution_counters_,
- (reason + 1) % AdaptationObserverInterface::kScaleReasonSize);
- }
- --(resolution_counters_[reason]);
- RTC_DCHECK_GE(resolution_counters_[reason], 0);
- }
-
- void DecrementFramerate(int reason, int cur_fps) {
- DecrementFramerate(reason);
- // Reset if at max fps (i.e. in case of fewer steps up than down).
- if (cur_fps == std::numeric_limits<int>::max())
- absl::c_fill(fps_counters_, 0);
- }
-
- // Gets the total number of downgrades (for all adapt reasons).
- int FramerateCount() const { return Count(fps_counters_); }
- int ResolutionCount() const { return Count(resolution_counters_); }
-
- // Gets the total number of downgrades for |reason|.
- int FramerateCount(int reason) const { return fps_counters_[reason]; }
- int ResolutionCount(int reason) const { return resolution_counters_[reason]; }
- int TotalCount(int reason) const {
- return FramerateCount(reason) + ResolutionCount(reason);
- }
-
- private:
- std::string ToString(const std::vector<int>& counters) const {
- rtc::StringBuilder ss;
- for (size_t reason = 0;
- reason < AdaptationObserverInterface::kScaleReasonSize; ++reason) {
- ss << (reason ? " cpu" : "quality") << ":" << counters[reason];
- }
- return ss.Release();
- }
-
- int Count(const std::vector<int>& counters) const {
- return absl::c_accumulate(counters, 0);
- }
-
- void MoveCount(std::vector<int>* counters, int from_reason) {
- int to_reason =
- (from_reason + 1) % AdaptationObserverInterface::kScaleReasonSize;
- ++((*counters)[to_reason]);
- --((*counters)[from_reason]);
- }
-
- // Degradation counters holding number of framerate/resolution reductions
- // per adapt reason.
- std::vector<int> fps_counters_;
- std::vector<int> resolution_counters_;
-};
-
class OveruseFrameDetectorResourceAdaptationModule::InitialFrameDropper {
public:
explicit InitialFrameDropper(QualityScalerResource* quality_scaler_resource)
@@ -402,7 +357,6 @@
experiment_cpu_load_estimator_(experiment_cpu_load_estimator),
has_input_video_(false),
degradation_preference_(DegradationPreference::DISABLED),
- adapt_counters_(),
balanced_settings_(),
last_adaptation_request_(absl::nullopt),
source_restrictor_(std::make_unique<VideoSourceRestrictor>()),
@@ -418,10 +372,10 @@
quality_rampup_done_(false),
quality_rampup_experiment_(QualityRampupExperiment::ParseSettings()),
encoder_settings_(absl::nullopt),
- encoder_stats_observer_(encoder_stats_observer) {
+ encoder_stats_observer_(encoder_stats_observer),
+ active_counts_() {
RTC_DCHECK(adaptation_listener_);
RTC_DCHECK(encoder_stats_observer_);
- ClearAdaptCounters();
AddResource(encode_usage_resource_.get(),
AdaptationObserverInterface::AdaptReason::kCpu);
AddResource(quality_scaler_resource_.get(),
@@ -488,10 +442,8 @@
last_adaptation_request_.reset();
if (degradation_preference == DegradationPreference::BALANCED ||
degradation_preference_ == DegradationPreference::BALANCED) {
- // TODO(asapersson): Consider removing |adapt_counters_| map and use one
- // AdaptCounter for all modes.
source_restrictor_->ClearRestrictions();
- ClearAdaptCounters();
+ active_counts_.fill(AdaptationCounters());
}
}
degradation_preference_ = degradation_preference;
@@ -533,7 +485,7 @@
ResetVideoSourceRestrictions() {
last_adaptation_request_.reset();
source_restrictor_->ClearRestrictions();
- ClearAdaptCounters();
+ active_counts_.fill(AdaptationCounters());
MaybeUpdateVideoSourceRestrictions();
}
@@ -543,19 +495,17 @@
}
void OveruseFrameDetectorResourceAdaptationModule::OnFrameDroppedDueToSize() {
- int fps_count = GetConstAdaptCounter().FramerateCount(
- AdaptationObserverInterface::AdaptReason::kQuality);
- int res_count = GetConstAdaptCounter().ResolutionCount(
- AdaptationObserverInterface::AdaptReason::kQuality);
+ AdaptationCounters counters_before =
+ source_restrictor_->adaptation_counters();
OnResourceOveruse(AdaptationObserverInterface::AdaptReason::kQuality);
if (degradation_preference() == DegradationPreference::BALANCED &&
- GetConstAdaptCounter().FramerateCount(
- AdaptationObserverInterface::AdaptReason::kQuality) > fps_count) {
+ source_restrictor_->adaptation_counters().fps_adaptations >
+ counters_before.fps_adaptations) {
// Adapt framerate in same step as resolution.
OnResourceOveruse(AdaptationObserverInterface::AdaptReason::kQuality);
}
- if (GetConstAdaptCounter().ResolutionCount(
- AdaptationObserverInterface::AdaptReason::kQuality) > res_count) {
+ if (source_restrictor_->adaptation_counters().resolution_adaptations >
+ counters_before.resolution_adaptations) {
encoder_stats_observer_->OnInitialQualityResolutionAdaptDown();
}
initial_frame_dropper_->OnFrameDroppedDueToSize();
@@ -690,7 +640,14 @@
if (!has_input_video_)
return absl::nullopt;
// 1. We can't adapt up if we're already at the highest setting.
- int num_downgrades = GetConstAdaptCounter().TotalCount(reason);
+ // Note that this only includes counts relevant to the current degradation
+ // preference. e.g. we previously adapted resolution, now prefer adpating fps,
+ // only count the fps adaptations and not the previous resolution adaptations.
+ // TODO(https://crbug.com/webrtc/11394): Checking the counts for reason should
+ // be replaced with checking the overuse state of all resources.
+ int num_downgrades = ApplyDegradationPreference(active_counts_[reason],
+ degradation_preference_)
+ .Total();
RTC_DCHECK_GE(num_downgrades, 0);
if (num_downgrades == 0)
return absl::nullopt;
@@ -745,7 +702,8 @@
}
// Attempt to increase pixel count.
int target_pixels = input_pixels;
- if (GetConstAdaptCounter().ResolutionCount() == 1) {
+ if (source_restrictor_->adaptation_counters().resolution_adaptations ==
+ 1) {
RTC_LOG(LS_INFO) << "Removing resolution down-scaling setting.";
target_pixels = std::numeric_limits<int>::max();
}
@@ -759,7 +717,7 @@
case DegradationPreference::MAINTAIN_RESOLUTION: {
// Scale up framerate.
int target_fps = input_fps;
- if (GetConstAdaptCounter().FramerateCount() == 1) {
+ if (source_restrictor_->adaptation_counters().fps_adaptations == 1) {
RTC_LOG(LS_INFO) << "Removing framerate down-scaling setting.";
target_fps = std::numeric_limits<int>::max();
}
@@ -777,8 +735,7 @@
OveruseFrameDetectorResourceAdaptationModule::GetAdaptDownTarget(
int input_pixels,
int input_fps,
- int min_pixels_per_frame,
- AdaptationObserverInterface::AdaptReason reason) const {
+ int min_pixels_per_frame) const {
// Preconditions for being able to adapt down:
if (!has_input_video_)
return absl::nullopt;
@@ -860,22 +817,19 @@
switch (target.action) {
case AdaptationAction::kIncreaseResolution:
source_restrictor_->IncreaseResolutionTo(target.value);
- GetAdaptCounter().DecrementResolution(reason);
return;
case AdaptationAction::kDecreaseResolution:
source_restrictor_->DecreaseResolutionTo(target.value,
min_pixels_per_frame);
- GetAdaptCounter().IncrementResolution(reason);
return;
case AdaptationAction::kIncreaseFrameRate:
source_restrictor_->IncreaseFrameRateTo(target.value);
- GetAdaptCounter().DecrementFramerate(reason, target.value);
// TODO(https://crbug.com/webrtc/11222): Don't adapt in two steps.
// GetAdaptUpTarget() should tell us the correct value, but BALANCED logic
// in DecrementFramerate() makes it hard to predict whether this will be
// the last step. Remove the dependency on GetConstAdaptCounter().
if (EffectiveDegradationPreference() == DegradationPreference::BALANCED &&
- GetConstAdaptCounter().FramerateCount() == 0 &&
+ source_restrictor_->adaptation_counters().fps_adaptations == 0 &&
target.value != std::numeric_limits<int>::max()) {
RTC_LOG(LS_INFO) << "Removing framerate down-scaling setting.";
source_restrictor_->IncreaseFrameRateTo(
@@ -884,7 +838,6 @@
return;
case AdaptationAction::kDecreaseFrameRate:
source_restrictor_->DecreaseFrameRateTo(target.value);
- GetAdaptCounter().IncrementFramerate(reason);
return;
}
}
@@ -908,7 +861,7 @@
MaybeUpdateVideoSourceRestrictions();
// Stats and logging.
UpdateAdaptationStats(reason);
- RTC_LOG(LS_INFO) << GetConstAdaptCounter().ToString();
+ RTC_LOG(LS_INFO) << ActiveCountsToString();
}
ResourceListenerResponse
@@ -921,7 +874,7 @@
int min_pixels_per_frame = MinPixelsPerFrame();
// Should we adapt, if so to what target?
absl::optional<AdaptationTarget> target =
- GetAdaptDownTarget(input_pixels, input_fps, min_pixels_per_frame, reason);
+ GetAdaptDownTarget(input_pixels, input_fps, min_pixels_per_frame);
if (!target.has_value())
return ResourceListenerResponse::kNothing;
// Apply target.
@@ -933,7 +886,7 @@
MaybeUpdateVideoSourceRestrictions();
// Stats and logging.
UpdateAdaptationStats(reason);
- RTC_LOG(LS_INFO) << GetConstAdaptCounter().ToString();
+ RTC_LOG(INFO) << ActiveCountsToString();
// In BALANCED, if requested FPS is higher or close to input FPS to the target
// we tell the QualityScaler to increase its frequency.
if (EffectiveDegradationPreference() == DegradationPreference::BALANCED &&
@@ -1035,9 +988,74 @@
encode_usage_resource_->SetTargetFrameRate(target_frame_rate);
}
+void OveruseFrameDetectorResourceAdaptationModule::OnAdaptationCountChanged(
+ const AdaptationCounters& adaptation_count,
+ AdaptationCounters* active_count,
+ AdaptationCounters* other_active) {
+ RTC_DCHECK(active_count);
+ RTC_DCHECK(other_active);
+ const int active_total = active_count->Total();
+ const int other_total = other_active->Total();
+ const AdaptationCounters prev_total = *active_count + *other_active;
+ const AdaptationCounters delta = adaptation_count - prev_total;
+
+ RTC_DCHECK_EQ(
+ std::abs(delta.resolution_adaptations) + std::abs(delta.fps_adaptations),
+ 1)
+ << "Adaptation took more than one step!";
+
+ if (delta.resolution_adaptations > 0) {
+ ++active_count->resolution_adaptations;
+ } else if (delta.resolution_adaptations < 0) {
+ if (active_count->resolution_adaptations == 0) {
+ RTC_DCHECK_GT(active_count->fps_adaptations, 0) << "No downgrades left";
+ RTC_DCHECK_GT(other_active->resolution_adaptations, 0)
+ << "No resolution adaptation to borrow from";
+ // Lend an fps adaptation to other and take one resolution adaptation.
+ --active_count->fps_adaptations;
+ ++other_active->fps_adaptations;
+ --other_active->resolution_adaptations;
+ } else {
+ --active_count->resolution_adaptations;
+ }
+ }
+ if (delta.fps_adaptations > 0) {
+ ++active_count->fps_adaptations;
+ } else if (delta.fps_adaptations < 0) {
+ if (active_count->fps_adaptations == 0) {
+ RTC_DCHECK_GT(active_count->resolution_adaptations, 0)
+ << "No downgrades left";
+ RTC_DCHECK_GT(other_active->fps_adaptations, 0)
+ << "No fps adaptation to borrow from";
+ // Lend a resolution adaptation to other and take one fps adaptation.
+ --active_count->resolution_adaptations;
+ ++other_active->resolution_adaptations;
+ --other_active->fps_adaptations;
+ } else {
+ --active_count->fps_adaptations;
+ }
+ }
+
+ RTC_DCHECK(*active_count + *other_active == adaptation_count);
+ RTC_DCHECK_EQ(other_active->Total(), other_total);
+ RTC_DCHECK_EQ(active_count->Total(), active_total + delta.Total());
+ RTC_DCHECK_GE(active_count->resolution_adaptations, 0);
+ RTC_DCHECK_GE(active_count->fps_adaptations, 0);
+ RTC_DCHECK_GE(other_active->resolution_adaptations, 0);
+ RTC_DCHECK_GE(other_active->fps_adaptations, 0);
+}
+
// TODO(nisse): Delete, once AdaptReason and AdaptationReason are merged.
void OveruseFrameDetectorResourceAdaptationModule::UpdateAdaptationStats(
AdaptationObserverInterface::AdaptReason reason) {
+ // Update active counts
+ AdaptationCounters& active_count = active_counts_[reason];
+ AdaptationCounters& other_active = active_counts_[(reason + 1) % 2];
+ const AdaptationCounters total_counts =
+ source_restrictor_->adaptation_counters();
+
+ OnAdaptationCountChanged(total_counts, &active_count, &other_active);
+
switch (reason) {
case AdaptationObserverInterface::AdaptReason::kCpu:
encoder_stats_observer_->OnAdaptationChanged(
@@ -1057,8 +1075,14 @@
VideoStreamEncoderObserver::AdaptationSteps
OveruseFrameDetectorResourceAdaptationModule::GetActiveCounts(
AdaptationObserverInterface::AdaptReason reason) {
+ // TODO(https://crbug.com/webrtc/11392) Ideally this shuold be moved out of
+ // this class and into the encoder_stats_observer_.
+ const AdaptationCounters counters = active_counts_[reason];
+
VideoStreamEncoderObserver::AdaptationSteps counts =
- GetConstAdaptCounter().Counts(reason);
+ VideoStreamEncoderObserver::AdaptationSteps();
+ counts.num_resolution_reductions = counters.resolution_adaptations;
+ counts.num_framerate_reductions = counters.fps_adaptations;
switch (reason) {
case AdaptationObserverInterface::AdaptReason::kCpu:
if (!IsFramerateScalingEnabled(degradation_preference_))
@@ -1095,30 +1119,6 @@
: degradation_preference_;
}
-OveruseFrameDetectorResourceAdaptationModule::AdaptCounter&
-OveruseFrameDetectorResourceAdaptationModule::GetAdaptCounter() {
- return adapt_counters_[degradation_preference_];
-}
-
-void OveruseFrameDetectorResourceAdaptationModule::ClearAdaptCounters() {
- adapt_counters_.clear();
- adapt_counters_.insert(
- std::make_pair(DegradationPreference::DISABLED, AdaptCounter()));
- adapt_counters_.insert(std::make_pair(
- DegradationPreference::MAINTAIN_FRAMERATE, AdaptCounter()));
- adapt_counters_.insert(std::make_pair(
- DegradationPreference::MAINTAIN_RESOLUTION, AdaptCounter()));
- adapt_counters_.insert(
- std::make_pair(DegradationPreference::BALANCED, AdaptCounter()));
-}
-
-const OveruseFrameDetectorResourceAdaptationModule::AdaptCounter&
-OveruseFrameDetectorResourceAdaptationModule::GetConstAdaptCounter() const {
- auto it = adapt_counters_.find(degradation_preference_);
- RTC_DCHECK(it != adapt_counters_.cend());
- return it->second;
-}
-
bool OveruseFrameDetectorResourceAdaptationModule::CanAdaptUpResolution(
int pixels,
uint32_t bitrate_bps) const {
@@ -1158,15 +1158,36 @@
try_quality_rampup = true;
}
}
- if (try_quality_rampup &&
- GetConstAdaptCounter().ResolutionCount(
- AdaptationObserverInterface::AdaptReason::kQuality) > 0 &&
- GetConstAdaptCounter().TotalCount(
- AdaptationObserverInterface::AdaptReason::kCpu) == 0) {
+ // TODO(https://crbug.com/webrtc/11392): See if we can rely on the total
+ // counts or the stats, and not the active counts.
+ const AdaptationCounters& qp_counts =
+ std::get<AdaptationObserverInterface::kQuality>(active_counts_);
+ const AdaptationCounters& cpu_counts =
+ std::get<AdaptationObserverInterface::kCpu>(active_counts_);
+ if (try_quality_rampup && qp_counts.resolution_adaptations > 0 &&
+ cpu_counts.Total() == 0) {
RTC_LOG(LS_INFO) << "Reset quality limitations.";
ResetVideoSourceRestrictions();
quality_rampup_done_ = true;
}
}
+std::string OveruseFrameDetectorResourceAdaptationModule::ActiveCountsToString()
+ const {
+ rtc::StringBuilder ss;
+
+ ss << "Downgrade counts: fps: {";
+ for (size_t reason = 0; reason < active_counts_.size(); ++reason) {
+ ss << (reason ? " cpu" : "quality") << ":";
+ ss << active_counts_[reason].fps_adaptations;
+ }
+ ss << "}, resolution {";
+ for (size_t reason = 0; reason < active_counts_.size(); ++reason) {
+ ss << (reason ? " cpu" : "quality") << ":";
+ ss << active_counts_[reason].resolution_adaptations;
+ }
+ ss << "}";
+
+ return ss.Release();
+}
} // namespace webrtc
diff --git a/video/overuse_frame_detector_resource_adaptation_module.h b/video/overuse_frame_detector_resource_adaptation_module.h
index 6e846d7..30f0aa3 100644
--- a/video/overuse_frame_detector_resource_adaptation_module.h
+++ b/video/overuse_frame_detector_resource_adaptation_module.h
@@ -30,6 +30,7 @@
#include "rtc_base/experiments/balanced_degradation_settings.h"
#include "rtc_base/experiments/quality_rampup_experiment.h"
#include "rtc_base/experiments/quality_scaler_settings.h"
+#include "rtc_base/strings/string_builder.h"
#include "system_wrappers/include/clock.h"
#include "video/encode_usage_resource.h"
#include "video/overuse_frame_detector.h"
@@ -37,6 +38,26 @@
namespace webrtc {
+// Counts the number of adaptations have resulted due to resource overuse.
+// Today we can adapt resolution and fps.
+struct AdaptationCounters {
+ AdaptationCounters() : resolution_adaptations(0), fps_adaptations(0) {}
+ AdaptationCounters(int resolution_adaptations, int fps_adaptations)
+ : resolution_adaptations(resolution_adaptations),
+ fps_adaptations(fps_adaptations) {}
+
+ int Total() const { return fps_adaptations + resolution_adaptations; }
+
+ bool operator==(const AdaptationCounters& rhs) const;
+ bool operator!=(const AdaptationCounters& rhs) const;
+
+ AdaptationCounters operator+(const AdaptationCounters& other) const;
+ AdaptationCounters operator-(const AdaptationCounters& other) const;
+
+ int resolution_adaptations;
+ int fps_adaptations;
+};
+
class VideoStreamEncoder;
// This class is used by the VideoStreamEncoder and is responsible for adapting
@@ -114,9 +135,20 @@
ResourceListenerResponse OnResourceUsageStateMeasured(
const Resource& resource) override;
+ // For reasons of adaptation and statistics, we not only count the total
+ // number of adaptations, but we also count the number of adaptations per
+ // reason.
+ // This method takes the new total number of adaptations and allocates that to
+ // the "active" count - number of adaptations for the current reason.
+ // The "other" count is the number of adaptations for the other reason.
+ // This must be called for each adaptation step made.
+ static void OnAdaptationCountChanged(
+ const AdaptationCounters& adaptation_count,
+ AdaptationCounters* active_count,
+ AdaptationCounters* other_active);
+
private:
class VideoSourceRestrictor;
- class AdaptCounter;
class InitialFrameDropper;
enum class State { kStopped, kStarted };
@@ -160,8 +192,7 @@
absl::optional<AdaptationTarget> GetAdaptDownTarget(
int input_pixels,
int input_fps,
- int min_pixels_per_frame,
- AdaptationObserverInterface::AdaptReason reason) const;
+ int min_pixels_per_frame) const;
// Applies the |target| to |source_restrictor_|.
void ApplyAdaptationTarget(const AdaptationTarget& target,
int min_pixels_per_frame,
@@ -179,8 +210,6 @@
int MinPixelsPerFrame() const;
VideoStreamEncoderObserver::AdaptationSteps GetActiveCounts(
AdaptationObserverInterface::AdaptReason reason);
- void ClearAdaptCounters();
- const AdaptCounter& GetConstAdaptCounter() const;
// Makes |video_source_restrictions_| up-to-date and informs the
// |adaptation_listener_| if restrictions are changed, allowing the listener
@@ -196,7 +225,6 @@
void UpdateAdaptationStats(AdaptationObserverInterface::AdaptReason reason);
DegradationPreference EffectiveDegradationPreference() const;
- AdaptCounter& GetAdaptCounter();
bool CanAdaptUpResolution(int pixels, uint32_t bitrate_bps) const;
// Checks to see if we should execute the quality rampup experiment. The
@@ -207,6 +235,8 @@
void MaybePerformQualityRampupExperiment();
void ResetVideoSourceRestrictions();
+ std::string ActiveCountsToString() const;
+
ResourceAdaptationModuleListener* const adaptation_listener_;
Clock* clock_;
State state_;
@@ -215,12 +245,6 @@
VideoSourceRestrictions video_source_restrictions_;
bool has_input_video_;
DegradationPreference degradation_preference_;
- // 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 DegradationPreference, AdaptCounter> adapt_counters_;
const BalancedDegradationSettings balanced_settings_;
// Stores a snapshot of the last adaptation request triggered by an AdaptUp
// or AdaptDown signal.
@@ -253,6 +277,15 @@
const AdaptationObserverInterface::AdaptReason reason;
};
std::vector<ResourceAndReason> resources_;
+ // One AdaptationCounter for each reason, tracking the number of times we have
+ // adapted for each reason. The sum of active_counts_ MUST always equal the
+ // total adaptation provided by the VideoSourceRestrictions.
+ // TODO(https://crbug.com/webrtc/11392): Move all active count logic to
+ // encoder_stats_observer_; Counters used for deciding if the video resolution
+ // or framerate is currently restricted, and if so, why, on a per degradation
+ // preference basis.
+ std::array<AdaptationCounters, AdaptationObserverInterface::kScaleReasonSize>
+ active_counts_;
};
} // namespace webrtc
diff --git a/video/overuse_frame_detector_resource_adaptation_unittest.cc b/video/overuse_frame_detector_resource_adaptation_unittest.cc
new file mode 100644
index 0000000..428618b
--- /dev/null
+++ b/video/overuse_frame_detector_resource_adaptation_unittest.cc
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2020 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 "video/overuse_frame_detector_resource_adaptation_module.h"
+
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(AdaptationCountersTest, Addition) {
+ AdaptationCounters a;
+ AdaptationCounters b(1, 2);
+ AdaptationCounters total = a + b;
+ EXPECT_EQ(1, total.resolution_adaptations);
+ EXPECT_EQ(2, total.fps_adaptations);
+}
+
+TEST(AdaptationCountersTest, Subtraction) {
+ AdaptationCounters a(0, 1);
+ AdaptationCounters b(2, 1);
+ AdaptationCounters diff = a - b;
+ EXPECT_EQ(-2, diff.resolution_adaptations);
+ EXPECT_EQ(0, diff.fps_adaptations);
+}
+
+TEST(AdaptationCountersTest, Equality) {
+ AdaptationCounters a(1, 2);
+ AdaptationCounters b(2, 1);
+ EXPECT_EQ(a, a);
+ EXPECT_NE(a, b);
+}
+
+TEST(AdaptationCountersTest, SelfAdditionSubtraction) {
+ AdaptationCounters a(1, 0);
+ AdaptationCounters b(0, 1);
+
+ EXPECT_EQ(a, a + b - b);
+ EXPECT_EQ(a, b + a - b);
+ EXPECT_EQ(a, a - b + b);
+ EXPECT_EQ(a, b - b + a);
+}
+
+TEST(OveruseFrameDetectorResourceAdaptationModuleTest,
+ FirstAdaptationDown_Fps) {
+ AdaptationCounters cpu;
+ AdaptationCounters qp;
+ AdaptationCounters total(0, 1);
+
+ OveruseFrameDetectorResourceAdaptationModule::OnAdaptationCountChanged(
+ total, &cpu, &qp);
+ AdaptationCounters expected_cpu(0, 1);
+ AdaptationCounters expected_qp;
+ EXPECT_EQ(expected_cpu, cpu);
+ EXPECT_EQ(expected_qp, qp);
+}
+
+TEST(OveruseFrameDetectorResourceAdaptationModuleTest,
+ FirstAdaptationDown_Resolution) {
+ AdaptationCounters cpu;
+ AdaptationCounters qp;
+ AdaptationCounters total(1, 0);
+
+ OveruseFrameDetectorResourceAdaptationModule::OnAdaptationCountChanged(
+ total, &cpu, &qp);
+ AdaptationCounters expected_cpu(1, 0);
+ AdaptationCounters expected_qp;
+ EXPECT_EQ(expected_cpu, cpu);
+ EXPECT_EQ(expected_qp, qp);
+}
+
+TEST(OveruseFrameDetectorResourceAdaptationModuleTest, LastAdaptUp_Fps) {
+ AdaptationCounters cpu(0, 1);
+ AdaptationCounters qp;
+ AdaptationCounters total;
+
+ OveruseFrameDetectorResourceAdaptationModule::OnAdaptationCountChanged(
+ total, &cpu, &qp);
+ AdaptationCounters expected_cpu;
+ AdaptationCounters expected_qp;
+ EXPECT_EQ(expected_cpu, cpu);
+ EXPECT_EQ(expected_qp, qp);
+}
+
+TEST(OveruseFrameDetectorResourceAdaptationModuleTest, LastAdaptUp_Resolution) {
+ AdaptationCounters cpu(1, 0);
+ AdaptationCounters qp;
+ AdaptationCounters total;
+
+ OveruseFrameDetectorResourceAdaptationModule::OnAdaptationCountChanged(
+ total, &cpu, &qp);
+ AdaptationCounters expected_cpu;
+ AdaptationCounters expected_qp;
+ EXPECT_EQ(expected_cpu, cpu);
+ EXPECT_EQ(expected_qp, qp);
+}
+
+TEST(OveruseFrameDetectorResourceAdaptationModuleTest,
+ AdaptUpWithBorrow_Resolution) {
+ AdaptationCounters cpu(0, 1);
+ AdaptationCounters qp(1, 0);
+ AdaptationCounters total(0, 1);
+
+ // CPU adaptation for resolution, but no
+ // resolution adaptation left from CPU.
+ // We then borrow the resolution
+ // adaptation from qp, and give qp the
+ // fps adaptation from CPU.
+ OveruseFrameDetectorResourceAdaptationModule::OnAdaptationCountChanged(
+ total, &cpu, &qp);
+
+ AdaptationCounters expected_cpu(0, 0);
+ AdaptationCounters expected_qp(0, 1);
+ EXPECT_EQ(expected_cpu, cpu);
+ EXPECT_EQ(expected_qp, qp);
+}
+
+TEST(OveruseFrameDetectorResourceAdaptationModuleTest, AdaptUpWithBorrow_Fps) {
+ AdaptationCounters cpu(1, 0);
+ AdaptationCounters qp(0, 1);
+ AdaptationCounters total(1, 0);
+
+ // CPU adaptation for fps, but no
+ // fps adaptation left from CPU. We
+ // then borrow the fps adaptation
+ // from qp, and give qp the
+ // resolution adaptation from CPU.
+ OveruseFrameDetectorResourceAdaptationModule::OnAdaptationCountChanged(
+ total, &cpu, &qp);
+
+ AdaptationCounters expected_cpu(0, 0);
+ AdaptationCounters expected_qp(1, 0);
+ EXPECT_EQ(expected_cpu, cpu);
+ EXPECT_EQ(expected_qp, qp);
+}
+
+} // namespace webrtc
diff --git a/video/video_stream_encoder_unittest.cc b/video/video_stream_encoder_unittest.cc
index e48ccd8..9d6925e 100644
--- a/video/video_stream_encoder_unittest.cc
+++ b/video/video_stream_encoder_unittest.cc
@@ -2236,6 +2236,73 @@
}
TEST_F(VideoStreamEncoderTest,
+ StatsTracksCpuAdaptationStatsWhenSwitchingSource_Balanced) {
+ video_stream_encoder_->OnBitrateUpdated(
+ DataRate::BitsPerSec(kTargetBitrateBps),
+ DataRate::BitsPerSec(kTargetBitrateBps),
+ DataRate::BitsPerSec(kTargetBitrateBps), 0, 0, 0);
+
+ const int kWidth = 1280;
+ const int kHeight = 720;
+ int sequence = 1;
+
+ // Enable BALANCED preference, no initial limitation.
+ test::FrameForwarder source;
+ video_stream_encoder_->SetSource(&source,
+ webrtc::DegradationPreference::BALANCED);
+ source.IncomingCapturedFrame(CreateFrame(sequence, kWidth, kHeight));
+ WaitForEncodedFrame(sequence++);
+ VideoSendStream::Stats stats = stats_proxy_->GetStats();
+ EXPECT_FALSE(stats.cpu_limited_resolution);
+ EXPECT_FALSE(stats.cpu_limited_framerate);
+ EXPECT_EQ(0, stats.number_of_cpu_adapt_changes);
+
+ // Trigger CPU overuse, should now adapt down.
+ video_stream_encoder_->TriggerCpuOveruse();
+ source.IncomingCapturedFrame(CreateFrame(sequence, kWidth, kHeight));
+ WaitForEncodedFrame(sequence++);
+ stats = stats_proxy_->GetStats();
+ EXPECT_EQ(1, stats.number_of_cpu_adapt_changes);
+
+ // Set new degradation preference should clear restrictions since we changed
+ // from BALANCED.
+ video_stream_encoder_->SetSource(
+ &source, webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
+ source.IncomingCapturedFrame(CreateFrame(sequence, kWidth, kHeight));
+ WaitForEncodedFrame(sequence++);
+ stats = stats_proxy_->GetStats();
+ 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 from.
+ VideoSendStream::Stats mock_stats = stats_proxy_->GetStats();
+ mock_stats.input_frame_rate = 30;
+ stats_proxy_->SetMockStats(mock_stats);
+ video_stream_encoder_->TriggerCpuOveruse();
+ stats_proxy_->ResetMockStats();
+ source.IncomingCapturedFrame(CreateFrame(sequence, kWidth, kHeight));
+ WaitForEncodedFrame(sequence++);
+
+ // We have now adapted once.
+ stats = stats_proxy_->GetStats();
+ EXPECT_EQ(2, stats.number_of_cpu_adapt_changes);
+
+ // Back to BALANCED, should clear the restrictions again.
+ video_stream_encoder_->SetSource(&source,
+ webrtc::DegradationPreference::BALANCED);
+ source.IncomingCapturedFrame(CreateFrame(sequence, kWidth, kHeight));
+ WaitForEncodedFrame(sequence++);
+ stats = stats_proxy_->GetStats();
+ EXPECT_FALSE(stats.cpu_limited_resolution);
+ EXPECT_FALSE(stats.cpu_limited_framerate);
+ EXPECT_EQ(2, stats.number_of_cpu_adapt_changes);
+
+ video_stream_encoder_->Stop();
+}
+
+TEST_F(VideoStreamEncoderTest,
StatsTracksCpuAdaptationStatsWhenSwitchingSource) {
video_stream_encoder_->OnBitrateUpdated(
DataRate::BitsPerSec(kTargetBitrateBps),