| Index: webrtc/video/vie_encoder.cc
|
| diff --git a/webrtc/video/vie_encoder.cc b/webrtc/video/vie_encoder.cc
|
| index 9940963f835e39e4fe042b9b6e09d27fe1af2973..219b8ea7af62d8d16c6d949a71bf8f677eb327c4 100644
|
| --- a/webrtc/video/vie_encoder.cc
|
| +++ b/webrtc/video/vie_encoder.cc
|
| @@ -12,6 +12,7 @@
|
|
|
| #include <algorithm>
|
| #include <limits>
|
| +#include <numeric>
|
| #include <utility>
|
|
|
| #include "webrtc/base/arraysize.h"
|
| @@ -42,6 +43,7 @@ const int64_t kFrameLogIntervalMs = 60000;
|
| // on MediaCodec and fallback implementations are in place.
|
| // See https://bugs.chromium.org/p/webrtc/issues/detail?id=7206
|
| const int kMinPixelsPerFrame = 320 * 180;
|
| +const int kMinFramerateFps = 2;
|
|
|
| // The maximum number of frames to drop at beginning of stream
|
| // to try and achieve desired bitrate.
|
| @@ -150,7 +152,7 @@ class ViEEncoder::VideoSourceProxy {
|
| public:
|
| explicit VideoSourceProxy(ViEEncoder* vie_encoder)
|
| : vie_encoder_(vie_encoder),
|
| - degradation_preference_(DegradationPreference::kMaintainResolution),
|
| + degradation_preference_(DegradationPreference::kDegradationDisabled),
|
| source_(nullptr) {}
|
|
|
| void SetSource(rtc::VideoSourceInterface<VideoFrame>* source,
|
| @@ -161,10 +163,10 @@ class ViEEncoder::VideoSourceProxy {
|
| rtc::VideoSinkWants wants;
|
| {
|
| rtc::CritScope lock(&crit_);
|
| + degradation_preference_ = degradation_preference;
|
| old_source = source_;
|
| source_ = source;
|
| - degradation_preference_ = degradation_preference;
|
| - wants = current_wants();
|
| + wants = GetActiveSinkWants();
|
| }
|
|
|
| if (old_source != source && old_source != nullptr) {
|
| @@ -181,10 +183,30 @@ class ViEEncoder::VideoSourceProxy {
|
| void SetWantsRotationApplied(bool rotation_applied) {
|
| rtc::CritScope lock(&crit_);
|
| sink_wants_.rotation_applied = rotation_applied;
|
| - disabled_scaling_sink_wants_.rotation_applied = rotation_applied;
|
| - if (source_) {
|
| - source_->AddOrUpdateSink(vie_encoder_, current_wants());
|
| + if (source_)
|
| + source_->AddOrUpdateSink(vie_encoder_, sink_wants_);
|
| + }
|
| +
|
| + rtc::VideoSinkWants GetActiveSinkWants() EXCLUSIVE_LOCKS_REQUIRED(&crit_) {
|
| + rtc::VideoSinkWants wants = sink_wants_;
|
| + // Clear any constraints from the current sink wants that don't apply to
|
| + // the used degradation_preference.
|
| + switch (degradation_preference_) {
|
| + case DegradationPreference::kBalanced:
|
| + FALLTHROUGH();
|
| + case DegradationPreference::kMaintainFramerate:
|
| + wants.max_framerate_fps = std::numeric_limits<int>::max();
|
| + break;
|
| + case DegradationPreference::kMaintainResolution:
|
| + wants.max_pixel_count = std::numeric_limits<int>::max();
|
| + wants.target_pixel_count.reset();
|
| + break;
|
| + case DegradationPreference::kDegradationDisabled:
|
| + wants.max_pixel_count = std::numeric_limits<int>::max();
|
| + wants.target_pixel_count.reset();
|
| + wants.max_framerate_fps = std::numeric_limits<int>::max();
|
| }
|
| + return wants;
|
| }
|
|
|
| void RequestResolutionLowerThan(int pixel_count) {
|
| @@ -202,10 +224,28 @@ class ViEEncoder::VideoSourceProxy {
|
| const int pixels_wanted = (pixel_count * 3) / 5;
|
| if (pixels_wanted < kMinPixelsPerFrame)
|
| return;
|
| - sink_wants_.max_pixel_count = rtc::Optional<int>(pixels_wanted);
|
| + sink_wants_.max_pixel_count = pixels_wanted;
|
| sink_wants_.target_pixel_count = rtc::Optional<int>();
|
| if (source_)
|
| - source_->AddOrUpdateSink(vie_encoder_, sink_wants_);
|
| + source_->AddOrUpdateSink(vie_encoder_, GetActiveSinkWants());
|
| + }
|
| +
|
| + void RequestFramerateLowerThan(int framerate_fps) {
|
| + // Called on the encoder task queue.
|
| + rtc::CritScope lock(&crit_);
|
| + if (!IsFramerateScalingEnabledLocked()) {
|
| + // This can happen since |degradation_preference_| is set on
|
| + // libjingle's worker thread but the adaptation is done on the encoder
|
| + // task queue.
|
| + return;
|
| + }
|
| + // The input video frame rate will be scaled down to 2/3 of input fps,
|
| + // rounding down.
|
| + const int framerate_wanted =
|
| + std::max(kMinFramerateFps, (framerate_fps * 2) / 3);
|
| + sink_wants_.max_framerate_fps = framerate_wanted;
|
| + if (source_)
|
| + source_->AddOrUpdateSink(vie_encoder_, GetActiveSinkWants());
|
| }
|
|
|
| void RequestHigherResolutionThan(int pixel_count) {
|
| @@ -216,36 +256,67 @@ class ViEEncoder::VideoSourceProxy {
|
| // task queue.
|
| return;
|
| }
|
| - // On step down we request at most 3/5 the pixel count of the previous
|
| - // resolution, so in order to take "one step up" we request a resolution as
|
| - // close as possible to 5/3 of the current resolution. The actual pixel
|
| - // count selected depends on the capabilities of the source. In order to not
|
| - // take a too large step up, we cap the requested pixel count to be at most
|
| - // four time the current number of pixels.
|
| - sink_wants_.target_pixel_count = rtc::Optional<int>((pixel_count * 5) / 3);
|
| - sink_wants_.max_pixel_count = rtc::Optional<int>(pixel_count * 4);
|
| +
|
| + if (pixel_count == std::numeric_limits<int>::max()) {
|
| + // Remove any constraints.
|
| + sink_wants_.target_pixel_count.reset();
|
| + sink_wants_.max_pixel_count = std::numeric_limits<int>::max();
|
| + } else {
|
| + // On step down we request at most 3/5 the pixel count of the previous
|
| + // resolution, so in order to take "one step up" we request a resolution
|
| + // as close as possible to 5/3 of the current resolution. The actual pixel
|
| + // count selected depends on the capabilities of the source. In order to
|
| + // not take a too large step up, we cap the requested pixel count to be at
|
| + // most four time the current number of pixels.
|
| + sink_wants_.target_pixel_count =
|
| + rtc::Optional<int>((pixel_count * 5) / 3);
|
| + sink_wants_.max_pixel_count = pixel_count * 4;
|
| + }
|
| if (source_)
|
| - source_->AddOrUpdateSink(vie_encoder_, sink_wants_);
|
| + source_->AddOrUpdateSink(vie_encoder_, GetActiveSinkWants());
|
| + }
|
| +
|
| + void RequestHigherFramerateThan(int framerate_fps) {
|
| + // Called on the encoder task queue.
|
| + rtc::CritScope lock(&crit_);
|
| + if (!IsFramerateScalingEnabledLocked()) {
|
| + // This can happen since |degradation_preference_| is set on
|
| + // libjingle's worker thread but the adaptation is done on the encoder
|
| + // task queue.
|
| + return;
|
| + }
|
| + if (framerate_fps == std::numeric_limits<int>::max()) {
|
| + // Remove any restrains.
|
| + sink_wants_.max_framerate_fps = std::numeric_limits<int>::max();
|
| + } else {
|
| + // The input video frame rate will be scaled up to the last step, with
|
| + // rounding.
|
| + const int framerate_wanted = (framerate_fps * 3) / 2;
|
| + sink_wants_.max_framerate_fps = framerate_wanted;
|
| + }
|
| + if (source_)
|
| + source_->AddOrUpdateSink(vie_encoder_, GetActiveSinkWants());
|
| }
|
|
|
| private:
|
| bool IsResolutionScalingEnabledLocked() const
|
| EXCLUSIVE_LOCKS_REQUIRED(&crit_) {
|
| - return degradation_preference_ !=
|
| - DegradationPreference::kMaintainResolution;
|
| + return degradation_preference_ ==
|
| + DegradationPreference::kMaintainFramerate ||
|
| + degradation_preference_ == DegradationPreference::kBalanced;
|
| }
|
|
|
| - const rtc::VideoSinkWants& current_wants() const
|
| + bool IsFramerateScalingEnabledLocked() const
|
| EXCLUSIVE_LOCKS_REQUIRED(&crit_) {
|
| - return IsResolutionScalingEnabledLocked() ? sink_wants_
|
| - : disabled_scaling_sink_wants_;
|
| + // TODO(sprang): Also accept kBalanced here?
|
| + return degradation_preference_ ==
|
| + DegradationPreference::kMaintainResolution;
|
| }
|
|
|
| rtc::CriticalSection crit_;
|
| rtc::SequencedTaskChecker main_checker_;
|
| ViEEncoder* const vie_encoder_;
|
| rtc::VideoSinkWants sink_wants_ GUARDED_BY(&crit_);
|
| - rtc::VideoSinkWants disabled_scaling_sink_wants_ GUARDED_BY(&crit_);
|
| DegradationPreference degradation_preference_ GUARDED_BY(&crit_);
|
| rtc::VideoSourceInterface<VideoFrame>* source_ GUARDED_BY(&crit_);
|
|
|
| @@ -280,8 +351,7 @@ ViEEncoder::ViEEncoder(uint32_t number_of_cores,
|
| last_observed_bitrate_bps_(0),
|
| encoder_paused_and_dropped_frame_(false),
|
| clock_(Clock::GetRealTimeClock()),
|
| - scale_counter_(kScaleReasonSize, 0),
|
| - degradation_preference_(DegradationPreference::kMaintainResolution),
|
| + degradation_preference_(DegradationPreference::kDegradationDisabled),
|
| last_captured_timestamp_(0),
|
| delta_ntp_internal_ms_(clock_->CurrentNtpInMilliseconds() -
|
| clock_->TimeInMilliseconds()),
|
| @@ -352,12 +422,16 @@ void ViEEncoder::SetSource(
|
| source_proxy_->SetSource(source, degradation_preference);
|
| encoder_queue_.PostTask([this, degradation_preference] {
|
| RTC_DCHECK_RUN_ON(&encoder_queue_);
|
| -
|
| + if (degradation_preference_ != degradation_preference) {
|
| + // Reset adaptation state, so that we're not tricked into thinking there's
|
| + // an already pending request of the same type.
|
| + last_adaptation_request_.reset();
|
| + }
|
| degradation_preference_ = degradation_preference;
|
| - initial_rampup_ =
|
| - degradation_preference_ != DegradationPreference::kMaintainResolution
|
| - ? 0
|
| - : kMaxInitialFramedrop;
|
| + bool allow_scaling =
|
| + degradation_preference_ == DegradationPreference::kMaintainFramerate ||
|
| + degradation_preference_ == DegradationPreference::kBalanced;
|
| + initial_rampup_ = allow_scaling ? 0 : kMaxInitialFramedrop;
|
| ConfigureQualityScaler();
|
| });
|
| }
|
| @@ -460,11 +534,13 @@ void ViEEncoder::ConfigureQualityScaler() {
|
| RTC_DCHECK_RUN_ON(&encoder_queue_);
|
| const auto scaling_settings = settings_.encoder->GetScalingSettings();
|
| const bool degradation_preference_allows_scaling =
|
| - degradation_preference_ != DegradationPreference::kMaintainResolution;
|
| + degradation_preference_ == DegradationPreference::kMaintainFramerate ||
|
| + degradation_preference_ == DegradationPreference::kBalanced;
|
|
|
| + const std::vector<int>& scale_counters = GetScaleCounters();
|
| stats_proxy_->SetResolutionRestrictionStats(
|
| - degradation_preference_allows_scaling, scale_counter_[kCpu] > 0,
|
| - scale_counter_[kQuality]);
|
| + degradation_preference_allows_scaling, scale_counters[kCpu] > 0,
|
| + scale_counters[kQuality]);
|
|
|
| if (degradation_preference_allows_scaling &&
|
| scaling_settings.enabled) {
|
| @@ -710,79 +786,193 @@ void ViEEncoder::OnBitrateUpdated(uint32_t bitrate_bps,
|
|
|
| void ViEEncoder::AdaptDown(AdaptReason reason) {
|
| RTC_DCHECK_RUN_ON(&encoder_queue_);
|
| - if (degradation_preference_ != DegradationPreference::kBalanced)
|
| - return;
|
| - RTC_DCHECK(static_cast<bool>(last_frame_info_));
|
| - int current_pixel_count = last_frame_info_->pixel_count();
|
| - if (last_adaptation_request_ &&
|
| - last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptDown &&
|
| - current_pixel_count >= last_adaptation_request_->input_pixel_count_) {
|
| - // Don't request lower resolution if the current resolution is not lower
|
| - // than the last time we asked for the resolution to be lowered.
|
| - return;
|
| + AdaptationRequest adaptation_request = {
|
| + 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;
|
| +
|
| + int max_downgrades = 0;
|
| + switch (degradation_preference_) {
|
| + case DegradationPreference::kBalanced:
|
| + FALLTHROUGH();
|
| + case DegradationPreference::kMaintainFramerate:
|
| + max_downgrades = kMaxCpuResolutionDowngrades;
|
| + if (downgrade_requested &&
|
| + adaptation_request.input_pixel_count_ >=
|
| + last_adaptation_request_->input_pixel_count_) {
|
| + // Don't request lower resolution if the current resolution is not
|
| + // lower than the last time we asked for the resolution to be lowered.
|
| + return;
|
| + }
|
| + break;
|
| + case DegradationPreference::kMaintainResolution:
|
| + max_downgrades = kMaxCpuFramerateDowngrades;
|
| + if (adaptation_request.framerate_fps_ <= 0 ||
|
| + (downgrade_requested &&
|
| + adaptation_request.framerate_fps_ < kMinFramerateFps)) {
|
| + // If no input fps estimate available, can't determine how to scale down
|
| + // framerate. Otherwise, don't request lower framerate if we don't have
|
| + // a valid frame rate. Since framerate, unlike resolution, is a measure
|
| + // we have to estimate, and can fluctuate naturally over time, don't
|
| + // make the same kind of limitations as for resolution, but trust the
|
| + // overuse detector to not trigger too often.
|
| + return;
|
| + }
|
| + break;
|
| + case DegradationPreference::kDegradationDisabled:
|
| + return;
|
| }
|
| - last_adaptation_request_.emplace(AdaptationRequest{
|
| - current_pixel_count, AdaptationRequest::Mode::kAdaptDown});
|
| +
|
| + last_adaptation_request_.emplace(adaptation_request);
|
| + const std::vector<int>& scale_counter = GetScaleCounters();
|
|
|
| switch (reason) {
|
| case kQuality:
|
| - stats_proxy_->OnQualityRestrictedResolutionChanged(
|
| - scale_counter_[reason] + 1);
|
| + stats_proxy_->OnQualityRestrictedResolutionChanged(scale_counter[reason] +
|
| + 1);
|
| break;
|
| case kCpu:
|
| - if (scale_counter_[reason] >= kMaxCpuDowngrades)
|
| + if (scale_counter[reason] >= max_downgrades)
|
| return;
|
| // Update stats accordingly.
|
| stats_proxy_->OnCpuRestrictedResolutionChanged(true);
|
| break;
|
| }
|
| - ++scale_counter_[reason];
|
| - source_proxy_->RequestResolutionLowerThan(current_pixel_count);
|
| - LOG(LS_INFO) << "Scaling down resolution.";
|
| +
|
| + IncrementScaleCounter(reason, 1);
|
| +
|
| + switch (degradation_preference_) {
|
| + case DegradationPreference::kBalanced:
|
| + FALLTHROUGH();
|
| + case DegradationPreference::kMaintainFramerate:
|
| + source_proxy_->RequestResolutionLowerThan(
|
| + adaptation_request.input_pixel_count_);
|
| + LOG(LS_INFO) << "Scaling down resolution.";
|
| + break;
|
| + case DegradationPreference::kMaintainResolution:
|
| + source_proxy_->RequestFramerateLowerThan(
|
| + adaptation_request.framerate_fps_);
|
| + LOG(LS_INFO) << "Scaling down framerate.";
|
| + break;
|
| + case DegradationPreference::kDegradationDisabled:
|
| + RTC_NOTREACHED();
|
| + }
|
| +
|
| for (size_t i = 0; i < kScaleReasonSize; ++i) {
|
| - LOG(LS_INFO) << "Scaled " << scale_counter_[i]
|
| + LOG(LS_INFO) << "Scaled " << GetScaleCounters()[i]
|
| << " times for reason: " << (i ? "cpu" : "quality");
|
| }
|
| }
|
|
|
| void ViEEncoder::AdaptUp(AdaptReason reason) {
|
| RTC_DCHECK_RUN_ON(&encoder_queue_);
|
| - if (scale_counter_[reason] == 0 ||
|
| - degradation_preference_ != DegradationPreference::kBalanced) {
|
| - return;
|
| - }
|
| - // Only scale if resolution is higher than last time we requested higher
|
| - // resolution.
|
| - RTC_DCHECK(static_cast<bool>(last_frame_info_));
|
| - int current_pixel_count = last_frame_info_->pixel_count();
|
| - if (last_adaptation_request_ &&
|
| - last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptUp &&
|
| - current_pixel_count <= last_adaptation_request_->input_pixel_count_) {
|
| - // Don't request higher resolution if the current resolution is not higher
|
| - // than the last time we asked for the resolution to be higher.
|
| + int scale_counter = GetScaleCounters()[reason];
|
| + if (scale_counter == 0)
|
| return;
|
| + RTC_DCHECK_GT(scale_counter, 0);
|
| + AdaptationRequest adaptation_request = {
|
| + last_frame_info_->pixel_count(),
|
| + stats_proxy_->GetStats().input_frame_rate,
|
| + AdaptationRequest::Mode::kAdaptUp};
|
| +
|
| + bool adapt_up_requested =
|
| + last_adaptation_request_ &&
|
| + last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptUp;
|
| + switch (degradation_preference_) {
|
| + case DegradationPreference::kBalanced:
|
| + FALLTHROUGH();
|
| + case DegradationPreference::kMaintainFramerate:
|
| + if (adapt_up_requested &&
|
| + adaptation_request.input_pixel_count_ <=
|
| + last_adaptation_request_->input_pixel_count_) {
|
| + // Don't request higher resolution if the current resolution is not
|
| + // higher than the last time we asked for the resolution to be higher.
|
| + return;
|
| + }
|
| + break;
|
| + case DegradationPreference::kMaintainResolution:
|
| + // TODO(sprang): Don't request higher framerate if we are already at
|
| + // max requested fps?
|
| + break;
|
| + case DegradationPreference::kDegradationDisabled:
|
| + return;
|
| }
|
| - last_adaptation_request_.emplace(AdaptationRequest{
|
| - current_pixel_count, AdaptationRequest::Mode::kAdaptUp});
|
| +
|
| + last_adaptation_request_.emplace(adaptation_request);
|
|
|
| switch (reason) {
|
| case kQuality:
|
| - stats_proxy_->OnQualityRestrictedResolutionChanged(
|
| - scale_counter_[reason] - 1);
|
| + stats_proxy_->OnQualityRestrictedResolutionChanged(scale_counter - 1);
|
| break;
|
| case kCpu:
|
| // Update stats accordingly.
|
| - stats_proxy_->OnCpuRestrictedResolutionChanged(scale_counter_[reason] >
|
| - 1);
|
| + stats_proxy_->OnCpuRestrictedResolutionChanged(scale_counter > 1);
|
| break;
|
| }
|
| - --scale_counter_[reason];
|
| - source_proxy_->RequestHigherResolutionThan(current_pixel_count);
|
| - LOG(LS_INFO) << "Scaling up resolution.";
|
| +
|
| + // 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>& current_scale_counters = GetScaleCounters();
|
| + const int scale_sum = std::accumulate(current_scale_counters.begin(),
|
| + current_scale_counters.end(), 0);
|
| + switch (degradation_preference_) {
|
| + case DegradationPreference::kBalanced:
|
| + FALLTHROUGH();
|
| + case DegradationPreference::kMaintainFramerate:
|
| + if (scale_sum == 0) {
|
| + LOG(LS_INFO) << "Removing resolution down-scaling setting.";
|
| + source_proxy_->RequestHigherResolutionThan(
|
| + std::numeric_limits<int>::max());
|
| + } else {
|
| + source_proxy_->RequestHigherResolutionThan(
|
| + adaptation_request.input_pixel_count_);
|
| + LOG(LS_INFO) << "Scaling up resolution.";
|
| + }
|
| + break;
|
| + case DegradationPreference::kMaintainResolution:
|
| + if (scale_sum == 0) {
|
| + LOG(LS_INFO) << "Removing framerate down-scaling setting.";
|
| + source_proxy_->RequestHigherFramerateThan(
|
| + std::numeric_limits<int>::max());
|
| + } else {
|
| + source_proxy_->RequestHigherFramerateThan(
|
| + adaptation_request.framerate_fps_);
|
| + LOG(LS_INFO) << "Scaling up framerate.";
|
| + }
|
| + break;
|
| + case DegradationPreference::kDegradationDisabled:
|
| + RTC_NOTREACHED();
|
| + }
|
| +
|
| for (size_t i = 0; i < kScaleReasonSize; ++i) {
|
| - LOG(LS_INFO) << "Scaled " << scale_counter_[i]
|
| + LOG(LS_INFO) << "Scaled " << current_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_];
|
| + }
|
| + return it->second;
|
| +}
|
| +
|
| +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);
|
| + }
|
| + scale_counters_[degradation_preference_][reason] += delta;
|
| +}
|
| +
|
| } // namespace webrtc
|
|
|