Index: webrtc/video/vie_encoder.cc |
diff --git a/webrtc/video/vie_encoder.cc b/webrtc/video/vie_encoder.cc |
index a3b071ac00fb058cad52b3290d00d163550744df..f507b04a726f1732482ac410579cb7621b0e0f05 100644 |
--- a/webrtc/video/vie_encoder.cc |
+++ b/webrtc/video/vie_encoder.cc |
@@ -73,6 +73,49 @@ uint32_t MaximumFrameSizeForBitrate(uint32_t kbps) { |
return std::numeric_limits<uint32_t>::max(); |
} |
+// Limits for kBalanced degradation preference. |
+// >640x480: | max | |
+// <=640x480: | 15fps | max | |
+// <=480x270: | 10fps | 15fps | |
+// <=320x240: | 7fps | 10fps | |
+// ---------------------------------------------------------- |
+// kbps: 0 60 130 500 | |
+ |
+int MinFps(int pixels) { |
+ if (pixels <= 320 * 240) |
+ return 7; |
+ else if (pixels <= 480 * 270) |
+ return 10; |
+ else if (pixels <= 640 * 480) |
+ return 15; |
+ else |
+ return std::numeric_limits<int>::max(); |
+} |
+ |
+int MaxFps(int pixels) { |
+ if (pixels <= 320 * 240) |
+ return 10; |
+ else if (pixels <= 480 * 270) |
+ return 15; |
+ else |
+ return std::numeric_limits<int>::max(); |
+} |
+ |
+uint32_t BitrateLimit(int pixels) { |
+ if (pixels <= 320 * 240) |
+ return 60000; |
+ else if (pixels <= 480 * 270) |
+ return 130000; |
+ else if (pixels <= 640 * 480) |
+ return 500000; |
+ else |
+ return std::numeric_limits<uint32_t>::max(); |
+} |
+ |
+int MaxFpsForBitrateAndResolution(uint32_t bps, int pixels) { |
+ return (bps <= BitrateLimit(pixels)) ? MinFps(pixels) : MaxFps(pixels); |
+} |
+ |
bool IsResolutionScalingEnabled( |
VideoSendStream::DegradationPreference degradation_preference) { |
return degradation_preference == |
@@ -211,7 +254,7 @@ class ViEEncoder::VideoSourceProxy { |
// the used degradation_preference. |
switch (degradation_preference_) { |
case VideoSendStream::DegradationPreference::kBalanced: |
- FALLTHROUGH(); |
+ break; |
case VideoSendStream::DegradationPreference::kMaintainFramerate: |
wants.max_framerate_fps = std::numeric_limits<int>::max(); |
break; |
@@ -230,54 +273,50 @@ class ViEEncoder::VideoSourceProxy { |
bool RequestResolutionLowerThan(int pixel_count) { |
// Called on the encoder task queue. |
rtc::CritScope lock(&crit_); |
- if (!IsResolutionScalingEnabledLocked()) { |
+ if (!source_ || !IsResolutionScalingEnabled(degradation_preference_)) { |
// This can happen since |degradation_preference_| is set on libjingle's |
// worker thread but the adaptation is done on the encoder task queue. |
return false; |
} |
- // The input video frame size will have a resolution with less than or |
- // equal to |max_pixel_count| depending on how the source can scale the |
- // input frame size. |
+ // The input video frame size will have a resolution less than or equal to |
+ // |max_pixel_count| depending on how the source can scale the frame size. |
const int pixels_wanted = (pixel_count * 3) / 5; |
- if (pixels_wanted < kMinPixelsPerFrame) |
+ if (pixels_wanted < kMinPixelsPerFrame || |
+ pixels_wanted >= sink_wants_.max_pixel_count) { |
return false; |
- |
+ } |
+ LOG(LS_INFO) << "Scaling down resolution"; |
sink_wants_.max_pixel_count = pixels_wanted; |
sink_wants_.target_pixel_count = rtc::Optional<int>(); |
- if (source_) |
- source_->AddOrUpdateSink(vie_encoder_, GetActiveSinkWants()); |
+ source_->AddOrUpdateSink(vie_encoder_, GetActiveSinkWants()); |
return true; |
} |
- void RequestFramerateLowerThan(int framerate_fps) { |
+ bool RequestFramerateLowerThan(int 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()); |
+ // The input video frame rate will be scaled down to 2/3, rounding down. |
+ return RestrictFramerate((fps * 2) / 3); |
} |
- void RequestHigherResolutionThan(int pixel_count) { |
+ bool RequestHigherResolutionThan(int pixel_count) { |
+ // Called on the encoder task queue. |
rtc::CritScope lock(&crit_); |
- if (!IsResolutionScalingEnabledLocked()) { |
+ if (!source_ || !IsResolutionScalingEnabled(degradation_preference_)) { |
// This can happen since |degradation_preference_| is set on libjingle's |
// worker thread but the adaptation is done on the encoder task queue. |
- return; |
+ return false; |
} |
+ int max_pixels_wanted = pixel_count; |
+ if (max_pixels_wanted != std::numeric_limits<int>::max()) |
+ max_pixels_wanted = pixel_count * 4; |
- if (pixel_count == std::numeric_limits<int>::max()) { |
+ if (max_pixels_wanted <= sink_wants_.max_pixel_count) |
+ return false; |
+ |
+ sink_wants_.max_pixel_count = max_pixels_wanted; |
+ if (max_pixels_wanted == 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 |
@@ -287,49 +326,55 @@ class ViEEncoder::VideoSourceProxy { |
// 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_, GetActiveSinkWants()); |
+ LOG(LS_INFO) << "Scaling up resolution"; |
+ source_->AddOrUpdateSink(vie_encoder_, GetActiveSinkWants()); |
+ return true; |
} |
- void RequestHigherFramerateThan(int framerate_fps) { |
+ bool RequestHigherFramerateThan(int 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()); |
+ // The input frame rate will be scaled up to the last step, with rounding. |
+ int framerate_wanted = fps; |
+ if (fps != std::numeric_limits<int>::max()) |
+ framerate_wanted = (fps * 3) / 2; |
+ |
+ return IncreaseFramerate(framerate_wanted); |
} |
- private: |
- bool IsResolutionScalingEnabledLocked() const |
- EXCLUSIVE_LOCKS_REQUIRED(&crit_) { |
- return degradation_preference_ == |
- VideoSendStream::DegradationPreference::kMaintainFramerate || |
- degradation_preference_ == |
- VideoSendStream::DegradationPreference::kBalanced; |
+ bool RestrictFramerate(int fps) { |
+ // Called on the encoder task queue. |
+ rtc::CritScope lock(&crit_); |
+ if (!source_ || !IsFramerateScalingEnabled(degradation_preference_)) |
+ return false; |
+ |
+ const int fps_wanted = std::max(kMinFramerateFps, fps); |
+ if (fps_wanted >= sink_wants_.max_framerate_fps) |
+ return false; |
+ |
+ LOG(LS_INFO) << "Scaling down framerate: " << fps_wanted; |
+ sink_wants_.max_framerate_fps = fps_wanted; |
+ source_->AddOrUpdateSink(vie_encoder_, GetActiveSinkWants()); |
+ return true; |
} |
- bool IsFramerateScalingEnabledLocked() const |
- EXCLUSIVE_LOCKS_REQUIRED(&crit_) { |
- // TODO(sprang): Also accept kBalanced here? |
- return degradation_preference_ == |
- VideoSendStream::DegradationPreference::kMaintainResolution; |
+ bool IncreaseFramerate(int fps) { |
+ // Called on the encoder task queue. |
+ rtc::CritScope lock(&crit_); |
+ if (!source_ || !IsFramerateScalingEnabled(degradation_preference_)) |
+ return false; |
+ |
+ const int fps_wanted = std::max(kMinFramerateFps, fps); |
+ if (fps_wanted <= sink_wants_.max_framerate_fps) |
+ return false; |
+ |
+ LOG(LS_INFO) << "Scaling up framerate: " << fps_wanted; |
+ sink_wants_.max_framerate_fps = fps_wanted; |
+ source_->AddOrUpdateSink(vie_encoder_, GetActiveSinkWants()); |
+ return true; |
} |
+ private: |
rtc::CriticalSection crit_; |
rtc::SequencedTaskChecker main_checker_; |
ViEEncoder* const vie_encoder_; |
@@ -645,7 +690,6 @@ void ViEEncoder::TraceFrameDropStart() { |
TRACE_EVENT_ASYNC_BEGIN0("webrtc", "EncoderPaused", this); |
} |
encoder_paused_and_dropped_frame_ = true; |
- return; |
} |
void ViEEncoder::TraceFrameDropEnd() { |
@@ -817,7 +861,8 @@ void ViEEncoder::AdaptDown(AdaptReason reason) { |
int max_downgrades = 0; |
switch (degradation_preference_) { |
case VideoSendStream::DegradationPreference::kBalanced: |
- FALLTHROUGH(); |
+ max_downgrades = kMaxCpuResolutionDowngrades; |
+ break; |
case VideoSendStream::DegradationPreference::kMaintainFramerate: |
max_downgrades = kMaxCpuResolutionDowngrades; |
if (downgrade_requested && |
@@ -852,21 +897,32 @@ void ViEEncoder::AdaptDown(AdaptReason reason) { |
} |
switch (degradation_preference_) { |
- case VideoSendStream::DegradationPreference::kBalanced: |
+ case VideoSendStream::DegradationPreference::kBalanced: { |
+ // Try scale down framerate, if lower. |
+ int max_fps = MaxFpsForBitrateAndResolution( |
+ last_observed_bitrate_bps_, last_frame_info_->pixel_count()); |
+ if (source_proxy_->RestrictFramerate(max_fps)) { |
+ GetAdaptCounter().IncrementFramerate(reason); |
+ break; |
+ } |
+ // Scale down resolution. |
FALLTHROUGH(); |
+ } |
case VideoSendStream::DegradationPreference::kMaintainFramerate: |
+ // Scale down resolution. |
if (!source_proxy_->RequestResolutionLowerThan( |
adaptation_request.input_pixel_count_)) { |
return; |
} |
- LOG(LS_INFO) << "Scaling down resolution."; |
- GetAdaptCounter().IncrementResolution(reason, 1); |
+ GetAdaptCounter().IncrementResolution(reason); |
break; |
case VideoSendStream::DegradationPreference::kMaintainResolution: |
- source_proxy_->RequestFramerateLowerThan( |
- adaptation_request.framerate_fps_); |
- LOG(LS_INFO) << "Scaling down framerate."; |
- GetAdaptCounter().IncrementFramerate(reason, 1); |
+ // Scale down framerate. |
+ if (!source_proxy_->RequestFramerateLowerThan( |
+ adaptation_request.framerate_fps_)) { |
+ return; |
+ } |
+ GetAdaptCounter().IncrementFramerate(reason); |
break; |
case VideoSendStream::DegradationPreference::kDegradationDisabled: |
RTC_NOTREACHED(); |
@@ -897,55 +953,54 @@ void ViEEncoder::AdaptUp(AdaptReason reason) { |
last_adaptation_request_ && |
last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptUp; |
- switch (degradation_preference_) { |
- case VideoSendStream::DegradationPreference::kBalanced: |
- FALLTHROUGH(); |
- case VideoSendStream::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 VideoSendStream::DegradationPreference::kMaintainResolution: |
- // TODO(sprang): Don't request higher framerate if we are already at |
- // max requested fps? |
- break; |
- case VideoSendStream::DegradationPreference::kDegradationDisabled: |
+ if (degradation_preference_ == |
+ VideoSendStream::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; |
+ } |
} |
switch (degradation_preference_) { |
- case VideoSendStream::DegradationPreference::kBalanced: |
+ case VideoSendStream::DegradationPreference::kBalanced: { |
+ // Try scale up framerate, if higher. |
+ int fps = MaxFps(last_frame_info_->pixel_count()); |
+ if (source_proxy_->IncreaseFramerate(fps)) { |
+ GetAdaptCounter().DecrementFramerate(reason); |
+ break; |
+ } |
+ // Scale up resolution. |
FALLTHROUGH(); |
- case VideoSendStream::DegradationPreference::kMaintainFramerate: |
- if (adapt_counter.TotalCount() == 1) { |
+ } |
+ case VideoSendStream::DegradationPreference::kMaintainFramerate: { |
+ // Scale up resolution. |
+ int pixel_count = adaptation_request.input_pixel_count_; |
+ if (adapt_counter.ResolutionCount() == 1) { |
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."; |
+ pixel_count = std::numeric_limits<int>::max(); |
} |
- GetAdaptCounter().IncrementResolution(reason, -1); |
+ if (!source_proxy_->RequestHigherResolutionThan(pixel_count)) |
+ return; |
+ GetAdaptCounter().DecrementResolution(reason); |
break; |
- case VideoSendStream::DegradationPreference::kMaintainResolution: |
- if (adapt_counter.TotalCount() == 1) { |
+ } |
+ case VideoSendStream::DegradationPreference::kMaintainResolution: { |
+ // Scale up framerate. |
+ int fps = adaptation_request.framerate_fps_; |
+ if (adapt_counter.FramerateCount() == 1) { |
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."; |
+ fps = std::numeric_limits<int>::max(); |
} |
- GetAdaptCounter().IncrementFramerate(reason, -1); |
+ if (!source_proxy_->RequestHigherFramerateThan(fps)) |
+ return; |
+ GetAdaptCounter().DecrementFramerate(reason); |
break; |
+ } |
case VideoSendStream::DegradationPreference::kDegradationDisabled: |
- RTC_NOTREACHED(); |
+ return; |
} |
last_adaptation_request_.emplace(adaptation_request); |
@@ -1003,6 +1058,8 @@ const ViEEncoder::AdaptCounter& ViEEncoder::GetConstAdaptCounter() { |
ViEEncoder::AdaptCounter::AdaptCounter() { |
fps_counters_.resize(kScaleReasonSize); |
resolution_counters_.resize(kScaleReasonSize); |
+ RTC_DCHECK_EQ(fps_counters_.size(), 2) << "Update MoveCount()"; |
kthelgason
2017/05/30 11:17:06
Perhaps it would be better to add a `static_assert
åsapersson
2017/06/08 13:55:33
Done.
|
+ RTC_DCHECK_EQ(resolution_counters_.size(), 2) << "Update MoveCount()"; |
} |
ViEEncoder::AdaptCounter::~AdaptCounter() {} |
@@ -1021,12 +1078,38 @@ ViEEncoder::AdaptCounts ViEEncoder::AdaptCounter::Counts(int reason) const { |
return counts; |
} |
-void ViEEncoder::AdaptCounter::IncrementFramerate(int reason, int delta) { |
- fps_counters_[reason] += delta; |
+void ViEEncoder::AdaptCounter::IncrementFramerate(int reason) { |
+ ++(fps_counters_[reason]); |
} |
-void ViEEncoder::AdaptCounter::IncrementResolution(int reason, int delta) { |
- resolution_counters_[reason] += delta; |
+void ViEEncoder::AdaptCounter::IncrementResolution(int reason) { |
+ ++(resolution_counters_[reason]); |
+} |
+ |
+void ViEEncoder::AdaptCounter::DecrementFramerate(int reason) { |
+ if (fps_counters_[reason] == 0) { |
+ // 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} |
+ MoveCount(&resolution_counters_, reason); |
+ MoveCount(&fps_counters_, (reason + 1) % kScaleReasonSize); |
+ } |
+ --(fps_counters_[reason]); |
+ RTC_DCHECK_GE(fps_counters_[reason], 0); |
+} |
+ |
+void ViEEncoder::AdaptCounter::DecrementResolution(int reason) { |
+ if (resolution_counters_[reason] == 0) { |
+ // Adapt up is in a different order, switch reason. |
+ // E.g. resolution adapt down: quality, resolution adapt up: cpu. |
+ MoveCount(&fps_counters_, reason); |
+ MoveCount(&resolution_counters_, (reason + 1) % kScaleReasonSize); |
+ } |
+ --(resolution_counters_[reason]); |
+ RTC_DCHECK_GE(resolution_counters_[reason], 0); |
} |
int ViEEncoder::AdaptCounter::FramerateCount() const { |
@@ -1057,6 +1140,14 @@ int ViEEncoder::AdaptCounter::Count(const std::vector<int>& counters) const { |
return std::accumulate(counters.begin(), counters.end(), 0); |
} |
+void ViEEncoder::AdaptCounter::MoveCount(std::vector<int>* counters, |
+ int from_reason) { |
+ int to_reason = (from_reason + 1) % kScaleReasonSize; |
+ ++((*counters)[to_reason]); |
+ --((*counters)[from_reason]); |
+ RTC_DCHECK_GE((*counters)[from_reason], 0); |
kthelgason
2017/05/30 11:17:06
This postcondition is not at all clear to me. If t
åsapersson
2017/06/08 13:55:33
Done.
|
+} |
+ |
std::string ViEEncoder::AdaptCounter::ToString( |
const std::vector<int>& counters) const { |
std::stringstream ss; |