blob: b0d450f67ebf00d4c795c14568c4935013d18065 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
stefan@webrtc.org07b45a52012-02-02 08:37:48 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "video/video_stream_encoder.h"
mflodman@webrtc.org84d17832011-12-01 17:02:23 +000012
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +000013#include <algorithm>
perkj57c21f92016-06-17 07:27:16 -070014#include <limits>
sprangc5d62e22017-04-02 23:53:04 -070015#include <numeric>
Per512ecb32016-09-23 15:52:06 +020016#include <utility>
niklase@google.com470e71d2011-07-07 08:21:25 +000017
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "api/video/i420_buffer.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "common_video/include/video_frame.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "modules/video_coding/include/video_codec_initializer.h"
21#include "modules/video_coding/include/video_coding.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020022#include "rtc_base/arraysize.h"
23#include "rtc_base/checks.h"
Åsa Perssona945aee2018-04-24 16:53:25 +020024#include "rtc_base/experiments/quality_scaling_experiment.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020025#include "rtc_base/location.h"
26#include "rtc_base/logging.h"
Karl Wiberg80ba3332018-02-05 10:33:35 +010027#include "rtc_base/system/fallthrough.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020028#include "rtc_base/timeutils.h"
29#include "rtc_base/trace_event.h"
30#include "video/overuse_frame_detector.h"
nisseea3a7982017-05-15 02:42:11 -070031
niklase@google.com470e71d2011-07-07 08:21:25 +000032namespace webrtc {
33
perkj26091b12016-09-01 01:17:40 -070034namespace {
sprangb1ca0732017-02-01 08:38:12 -080035
asapersson6ffb67d2016-09-12 00:10:45 -070036// Time interval for logging frame counts.
37const int64_t kFrameLogIntervalMs = 60000;
sprangc5d62e22017-04-02 23:53:04 -070038const int kMinFramerateFps = 2;
sprangfda496a2017-06-15 04:21:07 -070039const int kMaxFramerateFps = 120;
perkj26091b12016-09-01 01:17:40 -070040
Sebastian Janssona3177052018-04-10 13:05:49 +020041// Time to keep a single cached pending frame in paused state.
42const int64_t kPendingFrameTimeoutMs = 1000;
43
kthelgason2bc68642017-02-07 07:02:22 -080044// The maximum number of frames to drop at beginning of stream
45// to try and achieve desired bitrate.
46const int kMaxInitialFramedrop = 4;
47
Taylor Brandstetter49fcc102018-05-16 14:20:41 -070048// Initial limits for BALANCED degradation preference.
asaperssonf7e294d2017-06-13 23:25:22 -070049int MinFps(int pixels) {
50 if (pixels <= 320 * 240) {
51 return 7;
52 } else if (pixels <= 480 * 270) {
53 return 10;
54 } else if (pixels <= 640 * 480) {
55 return 15;
56 } else {
57 return std::numeric_limits<int>::max();
58 }
59}
60
61int MaxFps(int pixels) {
62 if (pixels <= 320 * 240) {
63 return 10;
64 } else if (pixels <= 480 * 270) {
65 return 15;
66 } else {
67 return std::numeric_limits<int>::max();
68 }
69}
70
Taylor Brandstetter49fcc102018-05-16 14:20:41 -070071bool IsResolutionScalingEnabled(DegradationPreference degradation_preference) {
72 return degradation_preference == DegradationPreference::MAINTAIN_FRAMERATE ||
73 degradation_preference == DegradationPreference::BALANCED;
asapersson09f05612017-05-15 23:40:18 -070074}
75
Taylor Brandstetter49fcc102018-05-16 14:20:41 -070076bool IsFramerateScalingEnabled(DegradationPreference degradation_preference) {
77 return degradation_preference == DegradationPreference::MAINTAIN_RESOLUTION ||
78 degradation_preference == DegradationPreference::BALANCED;
asapersson09f05612017-05-15 23:40:18 -070079}
80
Niels Möllerd1f7eb62018-03-28 16:40:58 +020081// TODO(pbos): Lower these thresholds (to closer to 100%) when we handle
82// pipelining encoders better (multiple input frames before something comes
83// out). This should effectively turn off CPU adaptations for systems that
84// remotely cope with the load right now.
85CpuOveruseOptions GetCpuOveruseOptions(
Niels Möller213618e2018-07-24 09:29:58 +020086 const VideoStreamEncoderSettings& settings,
Niels Möller4db138e2018-04-19 09:04:13 +020087 bool full_overuse_time) {
Niels Möllerd1f7eb62018-03-28 16:40:58 +020088 CpuOveruseOptions options;
89
Niels Möller4db138e2018-04-19 09:04:13 +020090 if (full_overuse_time) {
Niels Möllerd1f7eb62018-03-28 16:40:58 +020091 options.low_encode_usage_threshold_percent = 150;
92 options.high_encode_usage_threshold_percent = 200;
93 }
94 if (settings.experiment_cpu_load_estimator) {
95 options.filter_time_ms = 5 * rtc::kNumMillisecsPerSec;
96 }
97
98 return options;
99}
100
perkj26091b12016-09-01 01:17:40 -0700101} // namespace
102
perkja49cbd32016-09-16 07:53:41 -0700103// VideoSourceProxy is responsible ensuring thread safety between calls to
mflodmancc3d4422017-08-03 08:27:51 -0700104// VideoStreamEncoder::SetSource that will happen on libjingle's worker thread
105// when a video capturer is connected to the encoder and the encoder task queue
perkja49cbd32016-09-16 07:53:41 -0700106// (encoder_queue_) where the encoder reports its VideoSinkWants.
mflodmancc3d4422017-08-03 08:27:51 -0700107class VideoStreamEncoder::VideoSourceProxy {
perkja49cbd32016-09-16 07:53:41 -0700108 public:
mflodmancc3d4422017-08-03 08:27:51 -0700109 explicit VideoSourceProxy(VideoStreamEncoder* video_stream_encoder)
110 : video_stream_encoder_(video_stream_encoder),
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700111 degradation_preference_(DegradationPreference::DISABLED),
perkj803d97f2016-11-01 11:45:46 -0700112 source_(nullptr) {}
perkja49cbd32016-09-16 07:53:41 -0700113
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700114 void SetSource(rtc::VideoSourceInterface<VideoFrame>* source,
115 const DegradationPreference& degradation_preference) {
perkj803d97f2016-11-01 11:45:46 -0700116 // Called on libjingle's worker thread.
perkja49cbd32016-09-16 07:53:41 -0700117 RTC_DCHECK_CALLED_SEQUENTIALLY(&main_checker_);
118 rtc::VideoSourceInterface<VideoFrame>* old_source = nullptr;
perkj803d97f2016-11-01 11:45:46 -0700119 rtc::VideoSinkWants wants;
perkja49cbd32016-09-16 07:53:41 -0700120 {
121 rtc::CritScope lock(&crit_);
sprangc5d62e22017-04-02 23:53:04 -0700122 degradation_preference_ = degradation_preference;
perkja49cbd32016-09-16 07:53:41 -0700123 old_source = source_;
124 source_ = source;
sprangfda496a2017-06-15 04:21:07 -0700125 wants = GetActiveSinkWantsInternal();
perkja49cbd32016-09-16 07:53:41 -0700126 }
127
128 if (old_source != source && old_source != nullptr) {
mflodmancc3d4422017-08-03 08:27:51 -0700129 old_source->RemoveSink(video_stream_encoder_);
perkja49cbd32016-09-16 07:53:41 -0700130 }
131
132 if (!source) {
133 return;
134 }
135
mflodmancc3d4422017-08-03 08:27:51 -0700136 source->AddOrUpdateSink(video_stream_encoder_, wants);
perkja49cbd32016-09-16 07:53:41 -0700137 }
138
perkj803d97f2016-11-01 11:45:46 -0700139 void SetWantsRotationApplied(bool rotation_applied) {
140 rtc::CritScope lock(&crit_);
141 sink_wants_.rotation_applied = rotation_applied;
sprangc5d62e22017-04-02 23:53:04 -0700142 if (source_)
mflodmancc3d4422017-08-03 08:27:51 -0700143 source_->AddOrUpdateSink(video_stream_encoder_, sink_wants_);
sprangc5d62e22017-04-02 23:53:04 -0700144 }
145
sprangfda496a2017-06-15 04:21:07 -0700146 rtc::VideoSinkWants GetActiveSinkWants() {
147 rtc::CritScope lock(&crit_);
148 return GetActiveSinkWantsInternal();
perkj803d97f2016-11-01 11:45:46 -0700149 }
150
asaperssonf7e294d2017-06-13 23:25:22 -0700151 void ResetPixelFpsCount() {
152 rtc::CritScope lock(&crit_);
153 sink_wants_.max_pixel_count = std::numeric_limits<int>::max();
154 sink_wants_.target_pixel_count.reset();
155 sink_wants_.max_framerate_fps = std::numeric_limits<int>::max();
156 if (source_)
mflodmancc3d4422017-08-03 08:27:51 -0700157 source_->AddOrUpdateSink(video_stream_encoder_, sink_wants_);
asaperssonf7e294d2017-06-13 23:25:22 -0700158 }
159
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100160 bool RequestResolutionLowerThan(int pixel_count,
161 int min_pixels_per_frame,
162 bool* min_pixels_reached) {
perkj803d97f2016-11-01 11:45:46 -0700163 // Called on the encoder task queue.
164 rtc::CritScope lock(&crit_);
asapersson13874762017-06-07 00:01:02 -0700165 if (!source_ || !IsResolutionScalingEnabled(degradation_preference_)) {
asapersson02465b82017-04-10 01:12:52 -0700166 // This can happen since |degradation_preference_| is set on libjingle's
167 // worker thread but the adaptation is done on the encoder task queue.
asaperssond0de2952017-04-21 01:47:31 -0700168 return false;
perkj803d97f2016-11-01 11:45:46 -0700169 }
asapersson13874762017-06-07 00:01:02 -0700170 // The input video frame size will have a resolution less than or equal to
171 // |max_pixel_count| depending on how the source can scale the frame size.
kthelgason5e13d412016-12-01 03:59:51 -0800172 const int pixels_wanted = (pixel_count * 3) / 5;
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100173 if (pixels_wanted >= sink_wants_.max_pixel_count) {
174 return false;
175 }
176 if (pixels_wanted < min_pixels_per_frame) {
177 *min_pixels_reached = true;
asaperssond0de2952017-04-21 01:47:31 -0700178 return false;
asapersson13874762017-06-07 00:01:02 -0700179 }
Mirko Bonadei675513b2017-11-09 11:09:25 +0100180 RTC_LOG(LS_INFO) << "Scaling down resolution, max pixels: "
181 << pixels_wanted;
sprangc5d62e22017-04-02 23:53:04 -0700182 sink_wants_.max_pixel_count = pixels_wanted;
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200183 sink_wants_.target_pixel_count = absl::nullopt;
mflodmancc3d4422017-08-03 08:27:51 -0700184 source_->AddOrUpdateSink(video_stream_encoder_,
185 GetActiveSinkWantsInternal());
asaperssond0de2952017-04-21 01:47:31 -0700186 return true;
sprangc5d62e22017-04-02 23:53:04 -0700187 }
188
sprangfda496a2017-06-15 04:21:07 -0700189 int RequestFramerateLowerThan(int fps) {
sprangc5d62e22017-04-02 23:53:04 -0700190 // Called on the encoder task queue.
asapersson13874762017-06-07 00:01:02 -0700191 // The input video frame rate will be scaled down to 2/3, rounding down.
sprangfda496a2017-06-15 04:21:07 -0700192 int framerate_wanted = (fps * 2) / 3;
193 return RestrictFramerate(framerate_wanted) ? framerate_wanted : -1;
perkj803d97f2016-11-01 11:45:46 -0700194 }
195
asapersson13874762017-06-07 00:01:02 -0700196 bool RequestHigherResolutionThan(int pixel_count) {
197 // Called on the encoder task queue.
perkj803d97f2016-11-01 11:45:46 -0700198 rtc::CritScope lock(&crit_);
asapersson13874762017-06-07 00:01:02 -0700199 if (!source_ || !IsResolutionScalingEnabled(degradation_preference_)) {
asapersson02465b82017-04-10 01:12:52 -0700200 // This can happen since |degradation_preference_| is set on libjingle's
201 // worker thread but the adaptation is done on the encoder task queue.
asapersson13874762017-06-07 00:01:02 -0700202 return false;
perkj803d97f2016-11-01 11:45:46 -0700203 }
asapersson13874762017-06-07 00:01:02 -0700204 int max_pixels_wanted = pixel_count;
205 if (max_pixels_wanted != std::numeric_limits<int>::max())
206 max_pixels_wanted = pixel_count * 4;
sprangc5d62e22017-04-02 23:53:04 -0700207
asapersson13874762017-06-07 00:01:02 -0700208 if (max_pixels_wanted <= sink_wants_.max_pixel_count)
209 return false;
210
211 sink_wants_.max_pixel_count = max_pixels_wanted;
212 if (max_pixels_wanted == std::numeric_limits<int>::max()) {
sprangc5d62e22017-04-02 23:53:04 -0700213 // Remove any constraints.
214 sink_wants_.target_pixel_count.reset();
sprangc5d62e22017-04-02 23:53:04 -0700215 } else {
216 // On step down we request at most 3/5 the pixel count of the previous
217 // resolution, so in order to take "one step up" we request a resolution
218 // as close as possible to 5/3 of the current resolution. The actual pixel
219 // count selected depends on the capabilities of the source. In order to
220 // not take a too large step up, we cap the requested pixel count to be at
221 // most four time the current number of pixels.
Oskar Sundbom8e07c132018-01-08 16:45:42 +0100222 sink_wants_.target_pixel_count = (pixel_count * 5) / 3;
sprangc5d62e22017-04-02 23:53:04 -0700223 }
Mirko Bonadei675513b2017-11-09 11:09:25 +0100224 RTC_LOG(LS_INFO) << "Scaling up resolution, max pixels: "
225 << max_pixels_wanted;
mflodmancc3d4422017-08-03 08:27:51 -0700226 source_->AddOrUpdateSink(video_stream_encoder_,
227 GetActiveSinkWantsInternal());
asapersson13874762017-06-07 00:01:02 -0700228 return true;
sprangc5d62e22017-04-02 23:53:04 -0700229 }
230
sprangfda496a2017-06-15 04:21:07 -0700231 // Request upgrade in framerate. Returns the new requested frame, or -1 if
232 // no change requested. Note that maxint may be returned if limits due to
233 // adaptation requests are removed completely. In that case, consider
234 // |max_framerate_| to be the current limit (assuming the capturer complies).
235 int RequestHigherFramerateThan(int fps) {
asapersson13874762017-06-07 00:01:02 -0700236 // Called on the encoder task queue.
237 // The input frame rate will be scaled up to the last step, with rounding.
238 int framerate_wanted = fps;
239 if (fps != std::numeric_limits<int>::max())
240 framerate_wanted = (fps * 3) / 2;
241
sprangfda496a2017-06-15 04:21:07 -0700242 return IncreaseFramerate(framerate_wanted) ? framerate_wanted : -1;
asapersson13874762017-06-07 00:01:02 -0700243 }
244
245 bool RestrictFramerate(int fps) {
sprangc5d62e22017-04-02 23:53:04 -0700246 // Called on the encoder task queue.
247 rtc::CritScope lock(&crit_);
asapersson13874762017-06-07 00:01:02 -0700248 if (!source_ || !IsFramerateScalingEnabled(degradation_preference_))
249 return false;
250
251 const int fps_wanted = std::max(kMinFramerateFps, fps);
252 if (fps_wanted >= sink_wants_.max_framerate_fps)
253 return false;
254
Mirko Bonadei675513b2017-11-09 11:09:25 +0100255 RTC_LOG(LS_INFO) << "Scaling down framerate: " << fps_wanted;
asapersson13874762017-06-07 00:01:02 -0700256 sink_wants_.max_framerate_fps = fps_wanted;
mflodmancc3d4422017-08-03 08:27:51 -0700257 source_->AddOrUpdateSink(video_stream_encoder_,
258 GetActiveSinkWantsInternal());
asapersson13874762017-06-07 00:01:02 -0700259 return true;
260 }
261
262 bool IncreaseFramerate(int fps) {
263 // Called on the encoder task queue.
264 rtc::CritScope lock(&crit_);
265 if (!source_ || !IsFramerateScalingEnabled(degradation_preference_))
266 return false;
267
268 const int fps_wanted = std::max(kMinFramerateFps, fps);
269 if (fps_wanted <= sink_wants_.max_framerate_fps)
270 return false;
271
Mirko Bonadei675513b2017-11-09 11:09:25 +0100272 RTC_LOG(LS_INFO) << "Scaling up framerate: " << fps_wanted;
asapersson13874762017-06-07 00:01:02 -0700273 sink_wants_.max_framerate_fps = fps_wanted;
mflodmancc3d4422017-08-03 08:27:51 -0700274 source_->AddOrUpdateSink(video_stream_encoder_,
275 GetActiveSinkWantsInternal());
asapersson13874762017-06-07 00:01:02 -0700276 return true;
perkj803d97f2016-11-01 11:45:46 -0700277 }
278
perkja49cbd32016-09-16 07:53:41 -0700279 private:
sprangfda496a2017-06-15 04:21:07 -0700280 rtc::VideoSinkWants GetActiveSinkWantsInternal()
danilchapa37de392017-09-09 04:17:22 -0700281 RTC_EXCLUSIVE_LOCKS_REQUIRED(&crit_) {
sprangfda496a2017-06-15 04:21:07 -0700282 rtc::VideoSinkWants wants = sink_wants_;
283 // Clear any constraints from the current sink wants that don't apply to
284 // the used degradation_preference.
285 switch (degradation_preference_) {
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700286 case DegradationPreference::BALANCED:
sprangfda496a2017-06-15 04:21:07 -0700287 break;
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700288 case DegradationPreference::MAINTAIN_FRAMERATE:
sprangfda496a2017-06-15 04:21:07 -0700289 wants.max_framerate_fps = std::numeric_limits<int>::max();
290 break;
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700291 case DegradationPreference::MAINTAIN_RESOLUTION:
sprangfda496a2017-06-15 04:21:07 -0700292 wants.max_pixel_count = std::numeric_limits<int>::max();
293 wants.target_pixel_count.reset();
294 break;
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700295 case DegradationPreference::DISABLED:
sprangfda496a2017-06-15 04:21:07 -0700296 wants.max_pixel_count = std::numeric_limits<int>::max();
297 wants.target_pixel_count.reset();
298 wants.max_framerate_fps = std::numeric_limits<int>::max();
299 }
300 return wants;
301 }
302
perkja49cbd32016-09-16 07:53:41 -0700303 rtc::CriticalSection crit_;
304 rtc::SequencedTaskChecker main_checker_;
mflodmancc3d4422017-08-03 08:27:51 -0700305 VideoStreamEncoder* const video_stream_encoder_;
danilchapa37de392017-09-09 04:17:22 -0700306 rtc::VideoSinkWants sink_wants_ RTC_GUARDED_BY(&crit_);
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700307 DegradationPreference degradation_preference_ RTC_GUARDED_BY(&crit_);
danilchapa37de392017-09-09 04:17:22 -0700308 rtc::VideoSourceInterface<VideoFrame>* source_ RTC_GUARDED_BY(&crit_);
perkja49cbd32016-09-16 07:53:41 -0700309
310 RTC_DISALLOW_COPY_AND_ASSIGN(VideoSourceProxy);
311};
312
Åsa Persson0122e842017-10-16 12:19:23 +0200313VideoStreamEncoder::VideoStreamEncoder(
314 uint32_t number_of_cores,
Niels Möller213618e2018-07-24 09:29:58 +0200315 VideoStreamEncoderObserver* encoder_stats_observer,
316 const VideoStreamEncoderSettings& settings,
Åsa Persson0122e842017-10-16 12:19:23 +0200317 rtc::VideoSinkInterface<VideoFrame>* pre_encode_callback,
Åsa Persson0122e842017-10-16 12:19:23 +0200318 std::unique_ptr<OveruseFrameDetector> overuse_detector)
perkj26091b12016-09-01 01:17:40 -0700319 : shutdown_event_(true /* manual_reset */, false),
320 number_of_cores_(number_of_cores),
kthelgason2bc68642017-02-07 07:02:22 -0800321 initial_rampup_(0),
Åsa Perssona945aee2018-04-24 16:53:25 +0200322 quality_scaling_experiment_enabled_(QualityScalingExperiment::Enabled()),
perkja49cbd32016-09-16 07:53:41 -0700323 source_proxy_(new VideoSourceProxy(this)),
Pera48ddb72016-09-29 11:48:50 +0200324 sink_(nullptr),
perkj26091b12016-09-01 01:17:40 -0700325 settings_(settings),
Niels Möllera0565992017-10-24 11:37:08 +0200326 video_sender_(Clock::GetRealTimeClock(), this),
Niels Möller73f29cb2018-01-31 16:09:31 +0100327 overuse_detector_(std::move(overuse_detector)),
Niels Möller213618e2018-07-24 09:29:58 +0200328 encoder_stats_observer_(encoder_stats_observer),
perkj26091b12016-09-01 01:17:40 -0700329 pre_encode_callback_(pre_encode_callback),
sprangfda496a2017-06-15 04:21:07 -0700330 max_framerate_(-1),
perkjfa10b552016-10-02 23:45:26 -0700331 pending_encoder_reconfiguration_(false),
Niels Möller4db138e2018-04-19 09:04:13 +0200332 pending_encoder_creation_(false),
perkj26091b12016-09-01 01:17:40 -0700333 encoder_start_bitrate_bps_(0),
Pera48ddb72016-09-29 11:48:50 +0200334 max_data_payload_length_(0),
pbos@webrtc.org143451d2015-03-18 14:40:03 +0000335 last_observed_bitrate_bps_(0),
stefan@webrtc.org792f1a12015-03-04 12:24:26 +0000336 encoder_paused_and_dropped_frame_(false),
perkj26091b12016-09-01 01:17:40 -0700337 clock_(Clock::GetRealTimeClock()),
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700338 degradation_preference_(DegradationPreference::DISABLED),
Yuwei Huangd9f99c12017-10-24 15:40:52 -0700339 posted_frames_waiting_for_encode_(0),
perkj26091b12016-09-01 01:17:40 -0700340 last_captured_timestamp_(0),
341 delta_ntp_internal_ms_(clock_->CurrentNtpInMilliseconds() -
342 clock_->TimeInMilliseconds()),
asapersson6ffb67d2016-09-12 00:10:45 -0700343 last_frame_log_ms_(clock_->TimeInMilliseconds()),
344 captured_frame_count_(0),
345 dropped_frame_count_(0),
sprang1a646ee2016-12-01 06:34:11 -0800346 bitrate_observer_(nullptr),
perkj26091b12016-09-01 01:17:40 -0700347 encoder_queue_("EncoderQueue") {
Niels Möller213618e2018-07-24 09:29:58 +0200348 RTC_DCHECK(encoder_stats_observer);
Niels Möller73f29cb2018-01-31 16:09:31 +0100349 RTC_DCHECK(overuse_detector_);
mflodman@webrtc.org02270cd2015-02-06 13:10:19 +0000350}
351
mflodmancc3d4422017-08-03 08:27:51 -0700352VideoStreamEncoder::~VideoStreamEncoder() {
perkja49cbd32016-09-16 07:53:41 -0700353 RTC_DCHECK_RUN_ON(&thread_checker_);
perkj26091b12016-09-01 01:17:40 -0700354 RTC_DCHECK(shutdown_event_.Wait(0))
355 << "Must call ::Stop() before destruction.";
356}
357
mflodmancc3d4422017-08-03 08:27:51 -0700358void VideoStreamEncoder::Stop() {
perkja49cbd32016-09-16 07:53:41 -0700359 RTC_DCHECK_RUN_ON(&thread_checker_);
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700360 source_proxy_->SetSource(nullptr, DegradationPreference());
perkja49cbd32016-09-16 07:53:41 -0700361 encoder_queue_.PostTask([this] {
362 RTC_DCHECK_RUN_ON(&encoder_queue_);
sprangfda496a2017-06-15 04:21:07 -0700363 overuse_detector_->StopCheckForOveruse();
Erik Språng08127a92016-11-16 16:41:30 +0100364 rate_allocator_.reset();
sprang1a646ee2016-12-01 06:34:11 -0800365 bitrate_observer_ = nullptr;
Niels Möllerbf3dbb42018-03-16 13:38:46 +0100366 video_sender_.RegisterExternalEncoder(nullptr, false);
kthelgason876222f2016-11-29 01:44:11 -0800367 quality_scaler_ = nullptr;
perkja49cbd32016-09-16 07:53:41 -0700368 shutdown_event_.Set();
369 });
370
371 shutdown_event_.Wait(rtc::Event::kForever);
perkj26091b12016-09-01 01:17:40 -0700372}
373
Niels Möller0327c2d2018-05-21 14:09:31 +0200374void VideoStreamEncoder::SetBitrateAllocationObserver(
sprang1a646ee2016-12-01 06:34:11 -0800375 VideoBitrateAllocationObserver* bitrate_observer) {
376 RTC_DCHECK_RUN_ON(&thread_checker_);
377 encoder_queue_.PostTask([this, bitrate_observer] {
378 RTC_DCHECK_RUN_ON(&encoder_queue_);
379 RTC_DCHECK(!bitrate_observer_);
380 bitrate_observer_ = bitrate_observer;
381 });
382}
383
mflodmancc3d4422017-08-03 08:27:51 -0700384void VideoStreamEncoder::SetSource(
perkj803d97f2016-11-01 11:45:46 -0700385 rtc::VideoSourceInterface<VideoFrame>* source,
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700386 const DegradationPreference& degradation_preference) {
perkja49cbd32016-09-16 07:53:41 -0700387 RTC_DCHECK_RUN_ON(&thread_checker_);
perkj803d97f2016-11-01 11:45:46 -0700388 source_proxy_->SetSource(source, degradation_preference);
389 encoder_queue_.PostTask([this, degradation_preference] {
390 RTC_DCHECK_RUN_ON(&encoder_queue_);
sprangc5d62e22017-04-02 23:53:04 -0700391 if (degradation_preference_ != degradation_preference) {
392 // Reset adaptation state, so that we're not tricked into thinking there's
393 // an already pending request of the same type.
394 last_adaptation_request_.reset();
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700395 if (degradation_preference == DegradationPreference::BALANCED ||
396 degradation_preference_ == DegradationPreference::BALANCED) {
asaperssonf7e294d2017-06-13 23:25:22 -0700397 // TODO(asapersson): Consider removing |adapt_counters_| map and use one
398 // AdaptCounter for all modes.
399 source_proxy_->ResetPixelFpsCount();
400 adapt_counters_.clear();
401 }
sprangc5d62e22017-04-02 23:53:04 -0700402 }
sprangb1ca0732017-02-01 08:38:12 -0800403 degradation_preference_ = degradation_preference;
asapersson91914e22017-06-01 00:34:08 -0700404 bool allow_scaling = IsResolutionScalingEnabled(degradation_preference_);
sprangc5d62e22017-04-02 23:53:04 -0700405 initial_rampup_ = allow_scaling ? 0 : kMaxInitialFramedrop;
Niels Möller4db138e2018-04-19 09:04:13 +0200406
Niels Möller2d061182018-04-24 09:13:08 +0200407 if (encoder_)
408 ConfigureQualityScaler();
Niels Möller4db138e2018-04-19 09:04:13 +0200409
Niels Möller7dc26b72017-12-06 10:27:48 +0100410 if (!IsFramerateScalingEnabled(degradation_preference) &&
411 max_framerate_ != -1) {
412 // If frame rate scaling is no longer allowed, remove any potential
413 // allowance for longer frame intervals.
414 overuse_detector_->OnTargetFramerateUpdated(max_framerate_);
415 }
perkj803d97f2016-11-01 11:45:46 -0700416 });
perkja49cbd32016-09-16 07:53:41 -0700417}
418
mflodmancc3d4422017-08-03 08:27:51 -0700419void VideoStreamEncoder::SetSink(EncoderSink* sink, bool rotation_applied) {
perkj803d97f2016-11-01 11:45:46 -0700420 source_proxy_->SetWantsRotationApplied(rotation_applied);
perkj26091b12016-09-01 01:17:40 -0700421 encoder_queue_.PostTask([this, sink] {
422 RTC_DCHECK_RUN_ON(&encoder_queue_);
423 sink_ = sink;
424 });
mflodman@webrtc.org84d17832011-12-01 17:02:23 +0000425}
426
mflodmancc3d4422017-08-03 08:27:51 -0700427void VideoStreamEncoder::SetStartBitrate(int start_bitrate_bps) {
perkj26091b12016-09-01 01:17:40 -0700428 encoder_queue_.PostTask([this, start_bitrate_bps] {
429 RTC_DCHECK_RUN_ON(&encoder_queue_);
430 encoder_start_bitrate_bps_ = start_bitrate_bps;
431 });
mflodman@webrtc.org84d17832011-12-01 17:02:23 +0000432}
Peter Boström00b9d212016-05-19 16:59:03 +0200433
mflodmancc3d4422017-08-03 08:27:51 -0700434void VideoStreamEncoder::ConfigureEncoder(VideoEncoderConfig config,
Niels Möllerf1338562018-04-26 09:51:47 +0200435 size_t max_data_payload_length) {
Sebastian Jansson3dc01252018-03-19 19:27:44 +0100436 // TODO(srte): This struct should be replaced by a lambda with move capture
437 // when C++14 lambda is allowed.
438 struct ConfigureEncoderTask {
439 void operator()() {
Yves Gerey665174f2018-06-19 15:03:05 +0200440 encoder->ConfigureEncoderOnTaskQueue(std::move(config),
441 max_data_payload_length);
Sebastian Jansson3dc01252018-03-19 19:27:44 +0100442 }
443 VideoStreamEncoder* encoder;
444 VideoEncoderConfig config;
445 size_t max_data_payload_length;
Sebastian Jansson3dc01252018-03-19 19:27:44 +0100446 };
Yves Gerey665174f2018-06-19 15:03:05 +0200447 encoder_queue_.PostTask(
448 ConfigureEncoderTask{this, std::move(config), max_data_payload_length});
perkj26091b12016-09-01 01:17:40 -0700449}
450
mflodmancc3d4422017-08-03 08:27:51 -0700451void VideoStreamEncoder::ConfigureEncoderOnTaskQueue(
452 VideoEncoderConfig config,
Niels Möllerf1338562018-04-26 09:51:47 +0200453 size_t max_data_payload_length) {
perkj26091b12016-09-01 01:17:40 -0700454 RTC_DCHECK_RUN_ON(&encoder_queue_);
perkj26091b12016-09-01 01:17:40 -0700455 RTC_DCHECK(sink_);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100456 RTC_LOG(LS_INFO) << "ConfigureEncoder requested.";
Pera48ddb72016-09-29 11:48:50 +0200457
458 max_data_payload_length_ = max_data_payload_length;
Niels Möller4db138e2018-04-19 09:04:13 +0200459 pending_encoder_creation_ =
460 (!encoder_ || encoder_config_.video_format != config.video_format);
Pera48ddb72016-09-29 11:48:50 +0200461 encoder_config_ = std::move(config);
perkjfa10b552016-10-02 23:45:26 -0700462 pending_encoder_reconfiguration_ = true;
Pera48ddb72016-09-29 11:48:50 +0200463
perkjfa10b552016-10-02 23:45:26 -0700464 // Reconfigure the encoder now if the encoder has an internal source or
Per21d45d22016-10-30 21:37:57 +0100465 // if the frame resolution is known. Otherwise, the reconfiguration is
466 // deferred until the next frame to minimize the number of reconfigurations.
467 // The codec configuration depends on incoming video frame size.
468 if (last_frame_info_) {
469 ReconfigureEncoder();
Yves Gerey665174f2018-06-19 15:03:05 +0200470 } else if (settings_.encoder_factory
471 ->QueryVideoEncoder(encoder_config_.video_format)
472 .has_internal_source) {
Niels Moller0d650b42018-04-18 07:17:07 +0000473 last_frame_info_ = VideoFrameInfo(176, 144, false);
474 ReconfigureEncoder();
perkjfa10b552016-10-02 23:45:26 -0700475 }
476}
perkj26091b12016-09-01 01:17:40 -0700477
Seth Hampsoncc7125f2018-02-02 08:46:16 -0800478// TODO(bugs.webrtc.org/8807): Currently this always does a hard
479// reconfiguration, but this isn't always necessary. Add in logic to only update
480// the VideoBitrateAllocator and call OnEncoderConfigurationChanged with a
481// "soft" reconfiguration.
mflodmancc3d4422017-08-03 08:27:51 -0700482void VideoStreamEncoder::ReconfigureEncoder() {
perkjfa10b552016-10-02 23:45:26 -0700483 RTC_DCHECK(pending_encoder_reconfiguration_);
484 std::vector<VideoStream> streams =
485 encoder_config_.video_stream_factory->CreateEncoderStreams(
486 last_frame_info_->width, last_frame_info_->height, encoder_config_);
perkj26091b12016-09-01 01:17:40 -0700487
ilnik6b826ef2017-06-16 06:53:48 -0700488 // TODO(ilnik): If configured resolution is significantly less than provided,
489 // e.g. because there are not enough SSRCs for all simulcast streams,
490 // signal new resolutions via SinkWants to video source.
491
492 // Stream dimensions may be not equal to given because of a simulcast
493 // restrictions.
494 int highest_stream_width = static_cast<int>(streams.back().width);
495 int highest_stream_height = static_cast<int>(streams.back().height);
496 // Dimension may be reduced to be, e.g. divisible by 4.
497 RTC_CHECK_GE(last_frame_info_->width, highest_stream_width);
498 RTC_CHECK_GE(last_frame_info_->height, highest_stream_height);
499 crop_width_ = last_frame_info_->width - highest_stream_width;
500 crop_height_ = last_frame_info_->height - highest_stream_height;
501
Erik Språng08127a92016-11-16 16:41:30 +0100502 VideoCodec codec;
Yves Gerey665174f2018-06-19 15:03:05 +0200503 if (!VideoCodecInitializer::SetupCodec(encoder_config_, streams, &codec,
504 &rate_allocator_)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100505 RTC_LOG(LS_ERROR) << "Failed to create encoder configuration.";
Erik Språng08127a92016-11-16 16:41:30 +0100506 }
perkjfa10b552016-10-02 23:45:26 -0700507
“Michael277a6562018-06-01 14:09:19 -0500508 // Set min_bitrate_bps, max_bitrate_bps, and max padding bit rate for VP9.
509 if (encoder_config_.codec_type == kVideoCodecVP9) {
510 RTC_DCHECK_EQ(1U, streams.size());
511 int max_encoder_bitrate_kbps = 0;
512 for (int i = 0; i < codec.VP9()->numberOfSpatialLayers; ++i) {
513 max_encoder_bitrate_kbps += codec.spatialLayers[i].maxBitrate;
514 }
515 // Lower max bitrate to the level codec actually can produce.
516 streams[0].max_bitrate_bps =
517 std::min(streams[0].max_bitrate_bps, max_encoder_bitrate_kbps * 1000);
518 streams[0].min_bitrate_bps = codec.spatialLayers[0].minBitrate * 1000;
519 // Pass along the value of maximum padding bit rate from
520 // spatialLayers[].targetBitrate to streams[0].target_bitrate_bps.
521 // TODO(ssilkin): There should be some margin between max padding bitrate
522 // and max encoder bitrate. With the current logic they can be equal.
523 streams[0].target_bitrate_bps =
524 std::min(static_cast<unsigned int>(streams[0].max_bitrate_bps),
525 codec.spatialLayers[codec.VP9()->numberOfSpatialLayers - 1]
526 .targetBitrate *
527 1000);
528 }
529
perkjfa10b552016-10-02 23:45:26 -0700530 codec.startBitrate =
531 std::max(encoder_start_bitrate_bps_ / 1000, codec.minBitrate);
532 codec.startBitrate = std::min(codec.startBitrate, codec.maxBitrate);
533 codec.expect_encode_from_texture = last_frame_info_->is_texture;
sprangfda496a2017-06-15 04:21:07 -0700534 max_framerate_ = codec.maxFramerate;
535 RTC_DCHECK_LE(max_framerate_, kMaxFramerateFps);
Stefan Holmere5904162015-03-26 11:11:06 +0100536
Niels Möller4db138e2018-04-19 09:04:13 +0200537 // Keep the same encoder, as long as the video_format is unchanged.
538 if (pending_encoder_creation_) {
539 pending_encoder_creation_ = false;
540 if (encoder_) {
541 video_sender_.RegisterExternalEncoder(nullptr, false);
542 }
543
Ilya Nikolaevskiyfc9dcb62018-06-11 10:04:54 +0200544 encoder_ = settings_.encoder_factory->CreateVideoEncoder(
545 encoder_config_.video_format);
Niels Möller4db138e2018-04-19 09:04:13 +0200546 // TODO(nisse): What to do if creating the encoder fails? Crash,
547 // or just discard incoming frames?
548 RTC_CHECK(encoder_);
549
Niels Möller4db138e2018-04-19 09:04:13 +0200550 const webrtc::VideoEncoderFactory::CodecInfo info =
551 settings_.encoder_factory->QueryVideoEncoder(
552 encoder_config_.video_format);
553
554 overuse_detector_->StopCheckForOveruse();
555 overuse_detector_->StartCheckForOveruse(
556 GetCpuOveruseOptions(settings_, info.is_hardware_accelerated), this);
557
558 video_sender_.RegisterExternalEncoder(encoder_.get(),
559 info.has_internal_source);
560 }
561 // RegisterSendCodec implies an unconditional call to
562 // encoder_->InitEncode().
Peter Boströmcd5c25c2016-04-21 16:48:08 +0200563 bool success = video_sender_.RegisterSendCodec(
perkjfa10b552016-10-02 23:45:26 -0700564 &codec, number_of_cores_,
565 static_cast<uint32_t>(max_data_payload_length_)) == VCM_OK;
Peter Boström905f8e72016-03-02 16:59:56 +0100566 if (!success) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100567 RTC_LOG(LS_ERROR) << "Failed to configure encoder.";
sprangfe627f32017-03-29 08:24:59 -0700568 rate_allocator_.reset();
mflodman@webrtc.org84d17832011-12-01 17:02:23 +0000569 }
Peter Boström905f8e72016-03-02 16:59:56 +0100570
Niels Möller96d7f762018-01-30 11:27:16 +0100571 video_sender_.UpdateChannelParameters(rate_allocator_.get(),
ilnik35b7de42017-03-15 04:24:21 -0700572 bitrate_observer_);
573
Niels Möller213618e2018-07-24 09:29:58 +0200574 encoder_stats_observer_->OnEncoderReconfigured(encoder_config_, streams);
Per512ecb32016-09-23 15:52:06 +0200575
perkjfa10b552016-10-02 23:45:26 -0700576 pending_encoder_reconfiguration_ = false;
Erik Språng08127a92016-11-16 16:41:30 +0100577
Pera48ddb72016-09-29 11:48:50 +0200578 sink_->OnEncoderConfigurationChanged(
perkjfa10b552016-10-02 23:45:26 -0700579 std::move(streams), encoder_config_.min_transmit_bitrate_bps);
kthelgason876222f2016-11-29 01:44:11 -0800580
Niels Möller7dc26b72017-12-06 10:27:48 +0100581 // Get the current target framerate, ie the maximum framerate as specified by
582 // the current codec configuration, or any limit imposed by cpu adaption in
583 // maintain-resolution or balanced mode. This is used to make sure overuse
584 // detection doesn't needlessly trigger in low and/or variable framerate
585 // scenarios.
586 int target_framerate = std::min(
587 max_framerate_, source_proxy_->GetActiveSinkWants().max_framerate_fps);
588 overuse_detector_->OnTargetFramerateUpdated(target_framerate);
Niels Möller2d061182018-04-24 09:13:08 +0200589
590 ConfigureQualityScaler();
kthelgason2bc68642017-02-07 07:02:22 -0800591}
592
mflodmancc3d4422017-08-03 08:27:51 -0700593void VideoStreamEncoder::ConfigureQualityScaler() {
kthelgason2bc68642017-02-07 07:02:22 -0800594 RTC_DCHECK_RUN_ON(&encoder_queue_);
Niels Möller4db138e2018-04-19 09:04:13 +0200595 const auto scaling_settings = encoder_->GetScalingSettings();
asapersson36e9eb42017-03-31 05:29:12 -0700596 const bool quality_scaling_allowed =
asapersson91914e22017-06-01 00:34:08 -0700597 IsResolutionScalingEnabled(degradation_preference_) &&
Niels Möller225c7872018-02-22 15:03:53 +0100598 scaling_settings.thresholds;
kthelgason3af6cc02017-03-22 00:25:28 -0700599
asapersson36e9eb42017-03-31 05:29:12 -0700600 if (quality_scaling_allowed) {
asapersson09f05612017-05-15 23:40:18 -0700601 if (quality_scaler_.get() == nullptr) {
602 // Quality scaler has not already been configured.
603 // Drop frames and scale down until desired quality is achieved.
Niels Möller225c7872018-02-22 15:03:53 +0100604
Åsa Perssona945aee2018-04-24 16:53:25 +0200605 // Use experimental thresholds if available.
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200606 absl::optional<VideoEncoder::QpThresholds> experimental_thresholds;
Åsa Perssona945aee2018-04-24 16:53:25 +0200607 if (quality_scaling_experiment_enabled_) {
608 experimental_thresholds = QualityScalingExperiment::GetQpThresholds(
609 encoder_config_.codec_type);
610 }
Karl Wiberg918f50c2018-07-05 11:40:33 +0200611 // Since the interface is non-public, absl::make_unique can't do this
612 // upcast.
Niels Möller225c7872018-02-22 15:03:53 +0100613 AdaptationObserverInterface* observer = this;
Karl Wiberg918f50c2018-07-05 11:40:33 +0200614 quality_scaler_ = absl::make_unique<QualityScaler>(
Åsa Perssona945aee2018-04-24 16:53:25 +0200615 observer, experimental_thresholds ? *experimental_thresholds
616 : *(scaling_settings.thresholds));
kthelgason876222f2016-11-29 01:44:11 -0800617 }
618 } else {
619 quality_scaler_.reset(nullptr);
kthelgasonad9010c2017-02-14 00:46:51 -0800620 initial_rampup_ = kMaxInitialFramedrop;
kthelgason876222f2016-11-29 01:44:11 -0800621 }
asapersson09f05612017-05-15 23:40:18 -0700622
Niels Möller213618e2018-07-24 09:29:58 +0200623 encoder_stats_observer_->OnAdaptationChanged(
624 VideoStreamEncoderObserver::AdaptationReason::kNone,
625 GetActiveCounts(kCpu), GetActiveCounts(kQuality));
mflodman@webrtc.org84d17832011-12-01 17:02:23 +0000626}
627
mflodmancc3d4422017-08-03 08:27:51 -0700628void VideoStreamEncoder::OnFrame(const VideoFrame& video_frame) {
perkj26091b12016-09-01 01:17:40 -0700629 RTC_DCHECK_RUNS_SERIALIZED(&incoming_frame_race_checker_);
perkj26091b12016-09-01 01:17:40 -0700630 VideoFrame incoming_frame = video_frame;
631
632 // Local time in webrtc time base.
ilnik04f4d122017-06-19 07:18:55 -0700633 int64_t current_time_us = clock_->TimeInMicroseconds();
634 int64_t current_time_ms = current_time_us / rtc::kNumMicrosecsPerMillisec;
635 // In some cases, e.g., when the frame from decoder is fed to encoder,
636 // the timestamp may be set to the future. As the encoding pipeline assumes
637 // capture time to be less than present time, we should reset the capture
638 // timestamps here. Otherwise there may be issues with RTP send stream.
639 if (incoming_frame.timestamp_us() > current_time_us)
640 incoming_frame.set_timestamp_us(current_time_us);
perkj26091b12016-09-01 01:17:40 -0700641
642 // Capture time may come from clock with an offset and drift from clock_.
643 int64_t capture_ntp_time_ms;
nisse891419f2017-01-12 10:02:22 -0800644 if (video_frame.ntp_time_ms() > 0) {
perkj26091b12016-09-01 01:17:40 -0700645 capture_ntp_time_ms = video_frame.ntp_time_ms();
646 } else if (video_frame.render_time_ms() != 0) {
647 capture_ntp_time_ms = video_frame.render_time_ms() + delta_ntp_internal_ms_;
648 } else {
nisse1c0dea82017-01-30 02:43:18 -0800649 capture_ntp_time_ms = current_time_ms + delta_ntp_internal_ms_;
perkj26091b12016-09-01 01:17:40 -0700650 }
651 incoming_frame.set_ntp_time_ms(capture_ntp_time_ms);
652
653 // Convert NTP time, in ms, to RTP timestamp.
654 const int kMsToRtpTimestamp = 90;
655 incoming_frame.set_timestamp(
656 kMsToRtpTimestamp * static_cast<uint32_t>(incoming_frame.ntp_time_ms()));
657
658 if (incoming_frame.ntp_time_ms() <= last_captured_timestamp_) {
659 // We don't allow the same capture time for two frames, drop this one.
Mirko Bonadei675513b2017-11-09 11:09:25 +0100660 RTC_LOG(LS_WARNING) << "Same/old NTP timestamp ("
661 << incoming_frame.ntp_time_ms()
662 << " <= " << last_captured_timestamp_
663 << ") for incoming frame. Dropping.";
perkj26091b12016-09-01 01:17:40 -0700664 return;
665 }
666
asapersson6ffb67d2016-09-12 00:10:45 -0700667 bool log_stats = false;
nisse1c0dea82017-01-30 02:43:18 -0800668 if (current_time_ms - last_frame_log_ms_ > kFrameLogIntervalMs) {
669 last_frame_log_ms_ = current_time_ms;
asapersson6ffb67d2016-09-12 00:10:45 -0700670 log_stats = true;
671 }
672
perkj26091b12016-09-01 01:17:40 -0700673 last_captured_timestamp_ = incoming_frame.ntp_time_ms();
Sebastian Jansson3ab5c402018-04-05 12:30:50 +0200674
675 int64_t post_time_us = rtc::TimeMicros();
676 ++posted_frames_waiting_for_encode_;
677
678 encoder_queue_.PostTask(
679 [this, incoming_frame, post_time_us, log_stats]() {
680 RTC_DCHECK_RUN_ON(&encoder_queue_);
Niels Möller213618e2018-07-24 09:29:58 +0200681 encoder_stats_observer_->OnIncomingFrame(incoming_frame.width(),
682 incoming_frame.height());
Sebastian Jansson3ab5c402018-04-05 12:30:50 +0200683 ++captured_frame_count_;
684 const int posted_frames_waiting_for_encode =
685 posted_frames_waiting_for_encode_.fetch_sub(1);
686 RTC_DCHECK_GT(posted_frames_waiting_for_encode, 0);
687 if (posted_frames_waiting_for_encode == 1) {
Sebastian Janssona3177052018-04-10 13:05:49 +0200688 MaybeEncodeVideoFrame(incoming_frame, post_time_us);
Sebastian Jansson3ab5c402018-04-05 12:30:50 +0200689 } else {
690 // There is a newer frame in flight. Do not encode this frame.
691 RTC_LOG(LS_VERBOSE)
692 << "Incoming frame dropped due to that the encoder is blocked.";
693 ++dropped_frame_count_;
Niels Möller213618e2018-07-24 09:29:58 +0200694 encoder_stats_observer_->OnFrameDropped(
695 VideoStreamEncoderObserver::DropReason::kEncoderQueue);
Sebastian Jansson3ab5c402018-04-05 12:30:50 +0200696 }
697 if (log_stats) {
698 RTC_LOG(LS_INFO) << "Number of frames: captured "
699 << captured_frame_count_
700 << ", dropped (due to encoder blocked) "
701 << dropped_frame_count_ << ", interval_ms "
702 << kFrameLogIntervalMs;
703 captured_frame_count_ = 0;
704 dropped_frame_count_ = 0;
705 }
706 });
perkj26091b12016-09-01 01:17:40 -0700707}
708
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200709void VideoStreamEncoder::OnDiscardedFrame() {
Niels Möller213618e2018-07-24 09:29:58 +0200710 encoder_stats_observer_->OnFrameDropped(
711 VideoStreamEncoderObserver::DropReason::kSource);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200712}
713
mflodmancc3d4422017-08-03 08:27:51 -0700714bool VideoStreamEncoder::EncoderPaused() const {
perkj26091b12016-09-01 01:17:40 -0700715 RTC_DCHECK_RUN_ON(&encoder_queue_);
pwestin@webrtc.org91563e42013-04-25 22:20:08 +0000716 // Pause video if paused by caller or as long as the network is down or the
717 // pacer queue has grown too large in buffered mode.
perkj57c21f92016-06-17 07:27:16 -0700718 // If the pacer queue has grown too large or the network is down,
perkjfea93092016-05-14 00:58:48 -0700719 // last_observed_bitrate_bps_ will be 0.
perkj26091b12016-09-01 01:17:40 -0700720 return last_observed_bitrate_bps_ == 0;
stefan@webrtc.orgbfacda62013-03-27 16:36:01 +0000721}
722
mflodmancc3d4422017-08-03 08:27:51 -0700723void VideoStreamEncoder::TraceFrameDropStart() {
perkj26091b12016-09-01 01:17:40 -0700724 RTC_DCHECK_RUN_ON(&encoder_queue_);
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000725 // Start trace event only on the first frame after encoder is paused.
726 if (!encoder_paused_and_dropped_frame_) {
727 TRACE_EVENT_ASYNC_BEGIN0("webrtc", "EncoderPaused", this);
728 }
729 encoder_paused_and_dropped_frame_ = true;
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000730}
731
mflodmancc3d4422017-08-03 08:27:51 -0700732void VideoStreamEncoder::TraceFrameDropEnd() {
perkj26091b12016-09-01 01:17:40 -0700733 RTC_DCHECK_RUN_ON(&encoder_queue_);
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000734 // End trace event on first frame after encoder resumes, if frame was dropped.
735 if (encoder_paused_and_dropped_frame_) {
736 TRACE_EVENT_ASYNC_END0("webrtc", "EncoderPaused", this);
737 }
738 encoder_paused_and_dropped_frame_ = false;
739}
740
Sebastian Janssona3177052018-04-10 13:05:49 +0200741void VideoStreamEncoder::MaybeEncodeVideoFrame(const VideoFrame& video_frame,
742 int64_t time_when_posted_us) {
perkj26091b12016-09-01 01:17:40 -0700743 RTC_DCHECK_RUN_ON(&encoder_queue_);
kthelgason876222f2016-11-29 01:44:11 -0800744
perkj26091b12016-09-01 01:17:40 -0700745 if (pre_encode_callback_)
746 pre_encode_callback_->OnFrame(video_frame);
747
Per21d45d22016-10-30 21:37:57 +0100748 if (!last_frame_info_ || video_frame.width() != last_frame_info_->width ||
perkjfa10b552016-10-02 23:45:26 -0700749 video_frame.height() != last_frame_info_->height ||
perkjfa10b552016-10-02 23:45:26 -0700750 video_frame.is_texture() != last_frame_info_->is_texture) {
751 pending_encoder_reconfiguration_ = true;
Oskar Sundbom8e07c132018-01-08 16:45:42 +0100752 last_frame_info_ = VideoFrameInfo(video_frame.width(), video_frame.height(),
753 video_frame.is_texture());
Mirko Bonadei675513b2017-11-09 11:09:25 +0100754 RTC_LOG(LS_INFO) << "Video frame parameters changed: dimensions="
755 << last_frame_info_->width << "x"
756 << last_frame_info_->height
757 << ", texture=" << last_frame_info_->is_texture << ".";
perkjfa10b552016-10-02 23:45:26 -0700758 }
759
Niels Möller4db138e2018-04-19 09:04:13 +0200760 // We have to create then encoder before the frame drop logic,
761 // because the latter depends on encoder_->GetScalingSettings.
762 // According to the testcase
763 // InitialFrameDropOffWhenEncoderDisabledScaling, the return value
764 // from GetScalingSettings should enable or disable the frame drop.
765
766 int64_t now_ms = clock_->TimeInMilliseconds();
767 if (pending_encoder_reconfiguration_) {
768 ReconfigureEncoder();
769 last_parameters_update_ms_.emplace(now_ms);
770 } else if (!last_parameters_update_ms_ ||
771 now_ms - *last_parameters_update_ms_ >=
772 vcm::VCMProcessTimer::kDefaultProcessIntervalMs) {
773 video_sender_.UpdateChannelParameters(rate_allocator_.get(),
774 bitrate_observer_);
775 last_parameters_update_ms_.emplace(now_ms);
776 }
777
Sebastian Janssona3177052018-04-10 13:05:49 +0200778 if (DropDueToSize(video_frame.size())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100779 RTC_LOG(LS_INFO) << "Dropping frame. Too large for target bitrate.";
Åsa Persson875841d2018-01-08 08:49:53 +0100780 int count = GetConstAdaptCounter().ResolutionCount(kQuality);
kthelgason2bc68642017-02-07 07:02:22 -0800781 AdaptDown(kQuality);
Åsa Persson875841d2018-01-08 08:49:53 +0100782 if (GetConstAdaptCounter().ResolutionCount(kQuality) > count) {
Niels Möller213618e2018-07-24 09:29:58 +0200783 encoder_stats_observer_->OnInitialQualityResolutionAdaptDown();
Åsa Persson875841d2018-01-08 08:49:53 +0100784 }
kthelgason2bc68642017-02-07 07:02:22 -0800785 ++initial_rampup_;
Sebastian Jansson0d70e372018-04-17 13:57:13 +0200786 // Storing references to a native buffer risks blocking frame capture.
787 if (video_frame.video_frame_buffer()->type() !=
788 VideoFrameBuffer::Type::kNative) {
789 pending_frame_ = video_frame;
790 pending_frame_post_time_us_ = time_when_posted_us;
791 } else {
792 // Ensure that any previously stored frame is dropped.
793 pending_frame_.reset();
794 }
kthelgason2bc68642017-02-07 07:02:22 -0800795 return;
796 }
797 initial_rampup_ = kMaxInitialFramedrop;
798
perkj26091b12016-09-01 01:17:40 -0700799 if (EncoderPaused()) {
Sebastian Jansson0d70e372018-04-17 13:57:13 +0200800 // Storing references to a native buffer risks blocking frame capture.
801 if (video_frame.video_frame_buffer()->type() !=
802 VideoFrameBuffer::Type::kNative) {
803 if (pending_frame_)
804 TraceFrameDropStart();
805 pending_frame_ = video_frame;
806 pending_frame_post_time_us_ = time_when_posted_us;
807 } else {
808 // Ensure that any previously stored frame is dropped.
809 pending_frame_.reset();
Sebastian Janssona3177052018-04-10 13:05:49 +0200810 TraceFrameDropStart();
Sebastian Jansson0d70e372018-04-17 13:57:13 +0200811 }
perkj26091b12016-09-01 01:17:40 -0700812 return;
mflodman@webrtc.org84d17832011-12-01 17:02:23 +0000813 }
Sebastian Janssona3177052018-04-10 13:05:49 +0200814
815 pending_frame_.reset();
816 EncodeVideoFrame(video_frame, time_when_posted_us);
817}
818
819void VideoStreamEncoder::EncodeVideoFrame(const VideoFrame& video_frame,
820 int64_t time_when_posted_us) {
821 RTC_DCHECK_RUN_ON(&encoder_queue_);
perkj26091b12016-09-01 01:17:40 -0700822 TraceFrameDropEnd();
niklase@google.com470e71d2011-07-07 08:21:25 +0000823
ilnik6b826ef2017-06-16 06:53:48 -0700824 VideoFrame out_frame(video_frame);
825 // Crop frame if needed.
826 if (crop_width_ > 0 || crop_height_ > 0) {
827 int cropped_width = video_frame.width() - crop_width_;
828 int cropped_height = video_frame.height() - crop_height_;
829 rtc::scoped_refptr<I420Buffer> cropped_buffer =
830 I420Buffer::Create(cropped_width, cropped_height);
831 // TODO(ilnik): Remove scaling if cropping is too big, as it should never
832 // happen after SinkWants signaled correctly from ReconfigureEncoder.
833 if (crop_width_ < 4 && crop_height_ < 4) {
834 cropped_buffer->CropAndScaleFrom(
835 *video_frame.video_frame_buffer()->ToI420(), crop_width_ / 2,
836 crop_height_ / 2, cropped_width, cropped_height);
837 } else {
838 cropped_buffer->ScaleFrom(
839 *video_frame.video_frame_buffer()->ToI420().get());
840 }
841 out_frame =
842 VideoFrame(cropped_buffer, video_frame.timestamp(),
843 video_frame.render_time_ms(), video_frame.rotation());
844 out_frame.set_ntp_time_ms(video_frame.ntp_time_ms());
845 }
846
Magnus Jedvert26679d62015-04-07 14:07:41 +0200847 TRACE_EVENT_ASYNC_STEP0("webrtc", "Video", video_frame.render_time_ms(),
hclam@chromium.org1a7b9b92013-07-08 21:31:18 +0000848 "Encode");
pbos@webrtc.orgfe1ef932013-10-21 10:34:43 +0000849
Niels Möller7dc26b72017-12-06 10:27:48 +0100850 overuse_detector_->FrameCaptured(out_frame, time_when_posted_us);
perkjd52063f2016-09-07 06:32:18 -0700851
ilnik6b826ef2017-06-16 06:53:48 -0700852 video_sender_.AddVideoFrame(out_frame, nullptr);
niklase@google.com470e71d2011-07-07 08:21:25 +0000853}
niklase@google.com470e71d2011-07-07 08:21:25 +0000854
mflodmancc3d4422017-08-03 08:27:51 -0700855void VideoStreamEncoder::SendKeyFrame() {
perkj26091b12016-09-01 01:17:40 -0700856 if (!encoder_queue_.IsCurrent()) {
857 encoder_queue_.PostTask([this] { SendKeyFrame(); });
858 return;
859 }
860 RTC_DCHECK_RUN_ON(&encoder_queue_);
Niels Möller1c9aa1e2018-02-16 10:27:23 +0100861 TRACE_EVENT0("webrtc", "OnKeyFrameRequest");
Peter Boströmcd5c25c2016-04-21 16:48:08 +0200862 video_sender_.IntraFrameRequest(0);
stefan@webrtc.org07b45a52012-02-02 08:37:48 +0000863}
864
mflodmancc3d4422017-08-03 08:27:51 -0700865EncodedImageCallback::Result VideoStreamEncoder::OnEncodedImage(
Sergey Ulanov525df3f2016-08-02 17:46:41 -0700866 const EncodedImage& encoded_image,
867 const CodecSpecificInfo* codec_specific_info,
868 const RTPFragmentationHeader* fragmentation) {
perkj26091b12016-09-01 01:17:40 -0700869 // Encoded is called on whatever thread the real encoder implementation run
870 // on. In the case of hardware encoders, there might be several encoders
871 // running in parallel on different threads.
Niels Möller213618e2018-07-24 09:29:58 +0200872 encoder_stats_observer_->OnSendEncodedImage(encoded_image,
873 codec_specific_info);
sprang3911c262016-04-15 01:24:14 -0700874
Sergey Ulanov525df3f2016-08-02 17:46:41 -0700875 EncodedImageCallback::Result result =
876 sink_->OnEncodedImage(encoded_image, codec_specific_info, fragmentation);
perkjbc75d972016-05-02 06:31:25 -0700877
Niels Möller7dc26b72017-12-06 10:27:48 +0100878 int64_t time_sent_us = rtc::TimeMicros();
879 uint32_t timestamp = encoded_image._timeStamp;
kthelgason876222f2016-11-29 01:44:11 -0800880 const int qp = encoded_image.qp_;
Niels Möller83dbeac2017-12-14 16:39:44 +0100881 int64_t capture_time_us =
882 encoded_image.capture_time_ms_ * rtc::kNumMicrosecsPerMillisec;
883
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200884 absl::optional<int> encode_duration_us;
Ilya Nikolaevskiyb6c462d2018-06-05 15:21:32 +0200885 if (encoded_image.timing_.flags != VideoSendTiming::kInvalid) {
Niels Möller83dbeac2017-12-14 16:39:44 +0100886 encode_duration_us.emplace(
887 // TODO(nisse): Maybe use capture_time_ms_ rather than encode_start_ms_?
888 rtc::kNumMicrosecsPerMillisec *
889 (encoded_image.timing_.encode_finish_ms -
890 encoded_image.timing_.encode_start_ms));
891 }
892
893 encoder_queue_.PostTask(
894 [this, timestamp, time_sent_us, qp, capture_time_us, encode_duration_us] {
895 RTC_DCHECK_RUN_ON(&encoder_queue_);
896 overuse_detector_->FrameSent(timestamp, time_sent_us, capture_time_us,
897 encode_duration_us);
898 if (quality_scaler_ && qp >= 0)
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200899 quality_scaler_->ReportQp(qp);
Niels Möller83dbeac2017-12-14 16:39:44 +0100900 });
perkj803d97f2016-11-01 11:45:46 -0700901
Sergey Ulanov525df3f2016-08-02 17:46:41 -0700902 return result;
Peter Boströmb7d9a972015-12-18 16:01:11 +0100903}
904
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200905void VideoStreamEncoder::OnDroppedFrame(DropReason reason) {
906 switch (reason) {
907 case DropReason::kDroppedByMediaOptimizations:
Niels Möller213618e2018-07-24 09:29:58 +0200908 encoder_stats_observer_->OnFrameDropped(
909 VideoStreamEncoderObserver::DropReason::kMediaOptimization);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200910 encoder_queue_.PostTask([this] {
911 RTC_DCHECK_RUN_ON(&encoder_queue_);
912 if (quality_scaler_)
Åsa Perssona945aee2018-04-24 16:53:25 +0200913 quality_scaler_->ReportDroppedFrameByMediaOpt();
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200914 });
915 break;
916 case DropReason::kDroppedByEncoder:
Niels Möller213618e2018-07-24 09:29:58 +0200917 encoder_stats_observer_->OnFrameDropped(
918 VideoStreamEncoderObserver::DropReason::kEncoder);
Åsa Perssona945aee2018-04-24 16:53:25 +0200919 encoder_queue_.PostTask([this] {
920 RTC_DCHECK_RUN_ON(&encoder_queue_);
921 if (quality_scaler_)
922 quality_scaler_->ReportDroppedFrameByEncoder();
923 });
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200924 break;
925 }
kthelgason876222f2016-11-29 01:44:11 -0800926}
927
mflodmancc3d4422017-08-03 08:27:51 -0700928void VideoStreamEncoder::OnBitrateUpdated(uint32_t bitrate_bps,
929 uint8_t fraction_lost,
930 int64_t round_trip_time_ms) {
perkj26091b12016-09-01 01:17:40 -0700931 if (!encoder_queue_.IsCurrent()) {
932 encoder_queue_.PostTask(
933 [this, bitrate_bps, fraction_lost, round_trip_time_ms] {
934 OnBitrateUpdated(bitrate_bps, fraction_lost, round_trip_time_ms);
935 });
936 return;
937 }
938 RTC_DCHECK_RUN_ON(&encoder_queue_);
939 RTC_DCHECK(sink_) << "sink_ must be set before the encoder is active.";
940
Mirko Bonadei675513b2017-11-09 11:09:25 +0100941 RTC_LOG(LS_VERBOSE) << "OnBitrateUpdated, bitrate " << bitrate_bps
942 << " packet loss " << static_cast<int>(fraction_lost)
943 << " rtt " << round_trip_time_ms;
perkj26091b12016-09-01 01:17:40 -0700944
Peter Boströmcd5c25c2016-04-21 16:48:08 +0200945 video_sender_.SetChannelParameters(bitrate_bps, fraction_lost,
sprang1a646ee2016-12-01 06:34:11 -0800946 round_trip_time_ms, rate_allocator_.get(),
947 bitrate_observer_);
perkj26091b12016-09-01 01:17:40 -0700948
949 encoder_start_bitrate_bps_ =
950 bitrate_bps != 0 ? bitrate_bps : encoder_start_bitrate_bps_;
mflodman101f2502016-06-09 17:21:19 +0200951 bool video_is_suspended = bitrate_bps == 0;
Erik Språng08127a92016-11-16 16:41:30 +0100952 bool video_suspension_changed = video_is_suspended != EncoderPaused();
perkj26091b12016-09-01 01:17:40 -0700953 last_observed_bitrate_bps_ = bitrate_bps;
Peter Boströmd153a372015-11-10 15:27:12 +0000954
sprang552c7c72017-02-13 04:41:45 -0800955 if (video_suspension_changed) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100956 RTC_LOG(LS_INFO) << "Video suspend state changed to: "
957 << (video_is_suspended ? "suspended" : "not suspended");
Niels Möller213618e2018-07-24 09:29:58 +0200958 encoder_stats_observer_->OnSuspendChange(video_is_suspended);
mflodman101f2502016-06-09 17:21:19 +0200959 }
Sebastian Janssona3177052018-04-10 13:05:49 +0200960 if (video_suspension_changed && !video_is_suspended && pending_frame_ &&
961 !DropDueToSize(pending_frame_->size())) {
962 int64_t pending_time_us = rtc::TimeMicros() - pending_frame_post_time_us_;
963 if (pending_time_us < kPendingFrameTimeoutMs * 1000)
964 EncodeVideoFrame(*pending_frame_, pending_frame_post_time_us_);
965 pending_frame_.reset();
966 }
967}
968
969bool VideoStreamEncoder::DropDueToSize(uint32_t pixel_count) const {
970 if (initial_rampup_ < kMaxInitialFramedrop &&
971 encoder_start_bitrate_bps_ > 0) {
972 if (encoder_start_bitrate_bps_ < 300000 /* qvga */) {
973 return pixel_count > 320 * 240;
974 } else if (encoder_start_bitrate_bps_ < 500000 /* vga */) {
975 return pixel_count > 640 * 480;
976 }
977 }
978 return false;
niklase@google.com470e71d2011-07-07 08:21:25 +0000979}
980
mflodmancc3d4422017-08-03 08:27:51 -0700981void VideoStreamEncoder::AdaptDown(AdaptReason reason) {
perkjd52063f2016-09-07 06:32:18 -0700982 RTC_DCHECK_RUN_ON(&encoder_queue_);
sprangc5d62e22017-04-02 23:53:04 -0700983 AdaptationRequest adaptation_request = {
984 last_frame_info_->pixel_count(),
Niels Möller213618e2018-07-24 09:29:58 +0200985 encoder_stats_observer_->GetInputFrameRate(),
sprangc5d62e22017-04-02 23:53:04 -0700986 AdaptationRequest::Mode::kAdaptDown};
asapersson09f05612017-05-15 23:40:18 -0700987
sprangc5d62e22017-04-02 23:53:04 -0700988 bool downgrade_requested =
989 last_adaptation_request_ &&
990 last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptDown;
991
sprangc5d62e22017-04-02 23:53:04 -0700992 switch (degradation_preference_) {
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700993 case DegradationPreference::BALANCED:
asaperssonf7e294d2017-06-13 23:25:22 -0700994 break;
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700995 case DegradationPreference::MAINTAIN_FRAMERATE:
sprangc5d62e22017-04-02 23:53:04 -0700996 if (downgrade_requested &&
997 adaptation_request.input_pixel_count_ >=
998 last_adaptation_request_->input_pixel_count_) {
999 // Don't request lower resolution if the current resolution is not
1000 // lower than the last time we asked for the resolution to be lowered.
1001 return;
1002 }
1003 break;
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001004 case DegradationPreference::MAINTAIN_RESOLUTION:
sprangc5d62e22017-04-02 23:53:04 -07001005 if (adaptation_request.framerate_fps_ <= 0 ||
1006 (downgrade_requested &&
1007 adaptation_request.framerate_fps_ < kMinFramerateFps)) {
1008 // If no input fps estimate available, can't determine how to scale down
1009 // framerate. Otherwise, don't request lower framerate if we don't have
1010 // a valid frame rate. Since framerate, unlike resolution, is a measure
1011 // we have to estimate, and can fluctuate naturally over time, don't
1012 // make the same kind of limitations as for resolution, but trust the
1013 // overuse detector to not trigger too often.
1014 return;
1015 }
1016 break;
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001017 case DegradationPreference::DISABLED:
sprangc5d62e22017-04-02 23:53:04 -07001018 return;
sprang84a37592017-02-10 07:04:27 -08001019 }
sprangc5d62e22017-04-02 23:53:04 -07001020
sprangc5d62e22017-04-02 23:53:04 -07001021 switch (degradation_preference_) {
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001022 case DegradationPreference::BALANCED: {
asaperssonf7e294d2017-06-13 23:25:22 -07001023 // Try scale down framerate, if lower.
1024 int fps = MinFps(last_frame_info_->pixel_count());
1025 if (source_proxy_->RestrictFramerate(fps)) {
1026 GetAdaptCounter().IncrementFramerate(reason);
1027 break;
1028 }
1029 // Scale down resolution.
Karl Wiberg80ba3332018-02-05 10:33:35 +01001030 RTC_FALLTHROUGH();
asaperssonf7e294d2017-06-13 23:25:22 -07001031 }
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001032 case DegradationPreference::MAINTAIN_FRAMERATE: {
asapersson13874762017-06-07 00:01:02 -07001033 // Scale down resolution.
Åsa Perssonc3ed6302017-11-16 14:04:52 +01001034 bool min_pixels_reached = false;
asaperssond0de2952017-04-21 01:47:31 -07001035 if (!source_proxy_->RequestResolutionLowerThan(
asapersson142fcc92017-08-17 08:58:54 -07001036 adaptation_request.input_pixel_count_,
Niels Möller4db138e2018-04-19 09:04:13 +02001037 encoder_->GetScalingSettings().min_pixels_per_frame,
Åsa Perssonc3ed6302017-11-16 14:04:52 +01001038 &min_pixels_reached)) {
1039 if (min_pixels_reached)
Niels Möller213618e2018-07-24 09:29:58 +02001040 encoder_stats_observer_->OnMinPixelLimitReached();
asaperssond0de2952017-04-21 01:47:31 -07001041 return;
1042 }
asaperssonf7e294d2017-06-13 23:25:22 -07001043 GetAdaptCounter().IncrementResolution(reason);
sprangc5d62e22017-04-02 23:53:04 -07001044 break;
Åsa Perssonc3ed6302017-11-16 14:04:52 +01001045 }
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001046 case DegradationPreference::MAINTAIN_RESOLUTION: {
asapersson13874762017-06-07 00:01:02 -07001047 // Scale down framerate.
sprangfda496a2017-06-15 04:21:07 -07001048 const int requested_framerate = source_proxy_->RequestFramerateLowerThan(
1049 adaptation_request.framerate_fps_);
1050 if (requested_framerate == -1)
asapersson13874762017-06-07 00:01:02 -07001051 return;
sprangfda496a2017-06-15 04:21:07 -07001052 RTC_DCHECK_NE(max_framerate_, -1);
Niels Möller7dc26b72017-12-06 10:27:48 +01001053 overuse_detector_->OnTargetFramerateUpdated(
1054 std::min(max_framerate_, requested_framerate));
asaperssonf7e294d2017-06-13 23:25:22 -07001055 GetAdaptCounter().IncrementFramerate(reason);
sprangc5d62e22017-04-02 23:53:04 -07001056 break;
sprangfda496a2017-06-15 04:21:07 -07001057 }
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001058 case DegradationPreference::DISABLED:
sprangc5d62e22017-04-02 23:53:04 -07001059 RTC_NOTREACHED();
1060 }
1061
asaperssond0de2952017-04-21 01:47:31 -07001062 last_adaptation_request_.emplace(adaptation_request);
1063
asapersson09f05612017-05-15 23:40:18 -07001064 UpdateAdaptationStats(reason);
asaperssond0de2952017-04-21 01:47:31 -07001065
Mirko Bonadei675513b2017-11-09 11:09:25 +01001066 RTC_LOG(LS_INFO) << GetConstAdaptCounter().ToString();
perkj26091b12016-09-01 01:17:40 -07001067}
1068
mflodmancc3d4422017-08-03 08:27:51 -07001069void VideoStreamEncoder::AdaptUp(AdaptReason reason) {
perkjd52063f2016-09-07 06:32:18 -07001070 RTC_DCHECK_RUN_ON(&encoder_queue_);
asapersson09f05612017-05-15 23:40:18 -07001071
1072 const AdaptCounter& adapt_counter = GetConstAdaptCounter();
1073 int num_downgrades = adapt_counter.TotalCount(reason);
1074 if (num_downgrades == 0)
perkj803d97f2016-11-01 11:45:46 -07001075 return;
asapersson09f05612017-05-15 23:40:18 -07001076 RTC_DCHECK_GT(num_downgrades, 0);
1077
sprangc5d62e22017-04-02 23:53:04 -07001078 AdaptationRequest adaptation_request = {
1079 last_frame_info_->pixel_count(),
Niels Möller213618e2018-07-24 09:29:58 +02001080 encoder_stats_observer_->GetInputFrameRate(),
sprangc5d62e22017-04-02 23:53:04 -07001081 AdaptationRequest::Mode::kAdaptUp};
1082
1083 bool adapt_up_requested =
1084 last_adaptation_request_ &&
1085 last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptUp;
asapersson09f05612017-05-15 23:40:18 -07001086
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001087 if (degradation_preference_ == DegradationPreference::MAINTAIN_FRAMERATE) {
asaperssonf7e294d2017-06-13 23:25:22 -07001088 if (adapt_up_requested &&
1089 adaptation_request.input_pixel_count_ <=
1090 last_adaptation_request_->input_pixel_count_) {
1091 // Don't request higher resolution if the current resolution is not
1092 // higher than the last time we asked for the resolution to be higher.
sprangc5d62e22017-04-02 23:53:04 -07001093 return;
asaperssonf7e294d2017-06-13 23:25:22 -07001094 }
sprangb1ca0732017-02-01 08:38:12 -08001095 }
sprangc5d62e22017-04-02 23:53:04 -07001096
sprangc5d62e22017-04-02 23:53:04 -07001097 switch (degradation_preference_) {
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001098 case DegradationPreference::BALANCED: {
asaperssonf7e294d2017-06-13 23:25:22 -07001099 // Try scale up framerate, if higher.
1100 int fps = MaxFps(last_frame_info_->pixel_count());
1101 if (source_proxy_->IncreaseFramerate(fps)) {
1102 GetAdaptCounter().DecrementFramerate(reason, fps);
1103 // Reset framerate in case of fewer fps steps down than up.
1104 if (adapt_counter.FramerateCount() == 0 &&
1105 fps != std::numeric_limits<int>::max()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001106 RTC_LOG(LS_INFO) << "Removing framerate down-scaling setting.";
asaperssonf7e294d2017-06-13 23:25:22 -07001107 source_proxy_->IncreaseFramerate(std::numeric_limits<int>::max());
1108 }
1109 break;
1110 }
1111 // Scale up resolution.
Karl Wiberg80ba3332018-02-05 10:33:35 +01001112 RTC_FALLTHROUGH();
asaperssonf7e294d2017-06-13 23:25:22 -07001113 }
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001114 case DegradationPreference::MAINTAIN_FRAMERATE: {
asapersson13874762017-06-07 00:01:02 -07001115 // Scale up resolution.
1116 int pixel_count = adaptation_request.input_pixel_count_;
1117 if (adapt_counter.ResolutionCount() == 1) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001118 RTC_LOG(LS_INFO) << "Removing resolution down-scaling setting.";
asapersson13874762017-06-07 00:01:02 -07001119 pixel_count = std::numeric_limits<int>::max();
sprangc5d62e22017-04-02 23:53:04 -07001120 }
asapersson13874762017-06-07 00:01:02 -07001121 if (!source_proxy_->RequestHigherResolutionThan(pixel_count))
1122 return;
asaperssonf7e294d2017-06-13 23:25:22 -07001123 GetAdaptCounter().DecrementResolution(reason);
sprangc5d62e22017-04-02 23:53:04 -07001124 break;
asapersson13874762017-06-07 00:01:02 -07001125 }
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001126 case DegradationPreference::MAINTAIN_RESOLUTION: {
asapersson13874762017-06-07 00:01:02 -07001127 // Scale up framerate.
1128 int fps = adaptation_request.framerate_fps_;
1129 if (adapt_counter.FramerateCount() == 1) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001130 RTC_LOG(LS_INFO) << "Removing framerate down-scaling setting.";
asapersson13874762017-06-07 00:01:02 -07001131 fps = std::numeric_limits<int>::max();
sprangc5d62e22017-04-02 23:53:04 -07001132 }
sprangfda496a2017-06-15 04:21:07 -07001133
1134 const int requested_framerate =
1135 source_proxy_->RequestHigherFramerateThan(fps);
1136 if (requested_framerate == -1) {
Niels Möller7dc26b72017-12-06 10:27:48 +01001137 overuse_detector_->OnTargetFramerateUpdated(max_framerate_);
asapersson13874762017-06-07 00:01:02 -07001138 return;
sprangfda496a2017-06-15 04:21:07 -07001139 }
Niels Möller7dc26b72017-12-06 10:27:48 +01001140 overuse_detector_->OnTargetFramerateUpdated(
1141 std::min(max_framerate_, requested_framerate));
asaperssonf7e294d2017-06-13 23:25:22 -07001142 GetAdaptCounter().DecrementFramerate(reason);
sprangc5d62e22017-04-02 23:53:04 -07001143 break;
asapersson13874762017-06-07 00:01:02 -07001144 }
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001145 case DegradationPreference::DISABLED:
asaperssonf7e294d2017-06-13 23:25:22 -07001146 return;
sprangc5d62e22017-04-02 23:53:04 -07001147 }
1148
asaperssond0de2952017-04-21 01:47:31 -07001149 last_adaptation_request_.emplace(adaptation_request);
1150
asapersson09f05612017-05-15 23:40:18 -07001151 UpdateAdaptationStats(reason);
1152
Mirko Bonadei675513b2017-11-09 11:09:25 +01001153 RTC_LOG(LS_INFO) << adapt_counter.ToString();
asapersson09f05612017-05-15 23:40:18 -07001154}
1155
Niels Möller213618e2018-07-24 09:29:58 +02001156// TODO(nisse): Delete, once AdaptReason and AdaptationReason are merged.
mflodmancc3d4422017-08-03 08:27:51 -07001157void VideoStreamEncoder::UpdateAdaptationStats(AdaptReason reason) {
asaperssond0de2952017-04-21 01:47:31 -07001158 switch (reason) {
asaperssond0de2952017-04-21 01:47:31 -07001159 case kCpu:
Niels Möller213618e2018-07-24 09:29:58 +02001160 encoder_stats_observer_->OnAdaptationChanged(
1161 VideoStreamEncoderObserver::AdaptationReason::kCpu,
1162 GetActiveCounts(kCpu), GetActiveCounts(kQuality));
asapersson09f05612017-05-15 23:40:18 -07001163 break;
1164 case kQuality:
Niels Möller213618e2018-07-24 09:29:58 +02001165 encoder_stats_observer_->OnAdaptationChanged(
1166 VideoStreamEncoderObserver::AdaptationReason::kQuality,
1167 GetActiveCounts(kCpu), GetActiveCounts(kQuality));
asaperssond0de2952017-04-21 01:47:31 -07001168 break;
1169 }
perkj26091b12016-09-01 01:17:40 -07001170}
1171
Niels Möller213618e2018-07-24 09:29:58 +02001172VideoStreamEncoderObserver::AdaptationSteps VideoStreamEncoder::GetActiveCounts(
mflodmancc3d4422017-08-03 08:27:51 -07001173 AdaptReason reason) {
Niels Möller213618e2018-07-24 09:29:58 +02001174 VideoStreamEncoderObserver::AdaptationSteps counts =
mflodmancc3d4422017-08-03 08:27:51 -07001175 GetConstAdaptCounter().Counts(reason);
asapersson09f05612017-05-15 23:40:18 -07001176 switch (reason) {
1177 case kCpu:
1178 if (!IsFramerateScalingEnabled(degradation_preference_))
Niels Möller213618e2018-07-24 09:29:58 +02001179 counts.num_framerate_reductions = absl::nullopt;
asapersson09f05612017-05-15 23:40:18 -07001180 if (!IsResolutionScalingEnabled(degradation_preference_))
Niels Möller213618e2018-07-24 09:29:58 +02001181 counts.num_resolution_reductions = absl::nullopt;
asapersson09f05612017-05-15 23:40:18 -07001182 break;
1183 case kQuality:
1184 if (!IsFramerateScalingEnabled(degradation_preference_) ||
1185 !quality_scaler_) {
Niels Möller213618e2018-07-24 09:29:58 +02001186 counts.num_framerate_reductions = absl::nullopt;
asapersson09f05612017-05-15 23:40:18 -07001187 }
1188 if (!IsResolutionScalingEnabled(degradation_preference_) ||
1189 !quality_scaler_) {
Niels Möller213618e2018-07-24 09:29:58 +02001190 counts.num_resolution_reductions = absl::nullopt;
asapersson09f05612017-05-15 23:40:18 -07001191 }
1192 break;
sprangc5d62e22017-04-02 23:53:04 -07001193 }
asapersson09f05612017-05-15 23:40:18 -07001194 return counts;
sprangc5d62e22017-04-02 23:53:04 -07001195}
1196
mflodmancc3d4422017-08-03 08:27:51 -07001197VideoStreamEncoder::AdaptCounter& VideoStreamEncoder::GetAdaptCounter() {
asapersson09f05612017-05-15 23:40:18 -07001198 return adapt_counters_[degradation_preference_];
1199}
1200
mflodmancc3d4422017-08-03 08:27:51 -07001201const VideoStreamEncoder::AdaptCounter&
1202VideoStreamEncoder::GetConstAdaptCounter() {
asapersson09f05612017-05-15 23:40:18 -07001203 return adapt_counters_[degradation_preference_];
1204}
1205
1206// Class holding adaptation information.
mflodmancc3d4422017-08-03 08:27:51 -07001207VideoStreamEncoder::AdaptCounter::AdaptCounter() {
asapersson09f05612017-05-15 23:40:18 -07001208 fps_counters_.resize(kScaleReasonSize);
1209 resolution_counters_.resize(kScaleReasonSize);
asaperssonf7e294d2017-06-13 23:25:22 -07001210 static_assert(kScaleReasonSize == 2, "Update MoveCount.");
asapersson09f05612017-05-15 23:40:18 -07001211}
1212
mflodmancc3d4422017-08-03 08:27:51 -07001213VideoStreamEncoder::AdaptCounter::~AdaptCounter() {}
asapersson09f05612017-05-15 23:40:18 -07001214
mflodmancc3d4422017-08-03 08:27:51 -07001215std::string VideoStreamEncoder::AdaptCounter::ToString() const {
asapersson09f05612017-05-15 23:40:18 -07001216 std::stringstream ss;
1217 ss << "Downgrade counts: fps: {" << ToString(fps_counters_);
1218 ss << "}, resolution: {" << ToString(resolution_counters_) << "}";
1219 return ss.str();
1220}
1221
Niels Möller213618e2018-07-24 09:29:58 +02001222VideoStreamEncoderObserver::AdaptationSteps
1223VideoStreamEncoder::AdaptCounter::Counts(int reason) const {
1224 VideoStreamEncoderObserver::AdaptationSteps counts;
1225 counts.num_framerate_reductions = fps_counters_[reason];
1226 counts.num_resolution_reductions = resolution_counters_[reason];
asapersson09f05612017-05-15 23:40:18 -07001227 return counts;
1228}
1229
mflodmancc3d4422017-08-03 08:27:51 -07001230void VideoStreamEncoder::AdaptCounter::IncrementFramerate(int reason) {
asaperssonf7e294d2017-06-13 23:25:22 -07001231 ++(fps_counters_[reason]);
asapersson09f05612017-05-15 23:40:18 -07001232}
1233
mflodmancc3d4422017-08-03 08:27:51 -07001234void VideoStreamEncoder::AdaptCounter::IncrementResolution(int reason) {
asaperssonf7e294d2017-06-13 23:25:22 -07001235 ++(resolution_counters_[reason]);
1236}
1237
mflodmancc3d4422017-08-03 08:27:51 -07001238void VideoStreamEncoder::AdaptCounter::DecrementFramerate(int reason) {
asaperssonf7e294d2017-06-13 23:25:22 -07001239 if (fps_counters_[reason] == 0) {
1240 // Balanced mode: Adapt up is in a different order, switch reason.
1241 // E.g. framerate adapt down: quality (2), framerate adapt up: cpu (3).
1242 // 1. Down resolution (cpu): res={quality:0,cpu:1}, fps={quality:0,cpu:0}
1243 // 2. Down fps (quality): res={quality:0,cpu:1}, fps={quality:1,cpu:0}
1244 // 3. Up fps (cpu): res={quality:1,cpu:0}, fps={quality:0,cpu:0}
1245 // 4. Up resolution (quality): res={quality:0,cpu:0}, fps={quality:0,cpu:0}
1246 RTC_DCHECK_GT(TotalCount(reason), 0) << "No downgrade for reason.";
1247 RTC_DCHECK_GT(FramerateCount(), 0) << "Framerate not downgraded.";
1248 MoveCount(&resolution_counters_, reason);
1249 MoveCount(&fps_counters_, (reason + 1) % kScaleReasonSize);
1250 }
1251 --(fps_counters_[reason]);
1252 RTC_DCHECK_GE(fps_counters_[reason], 0);
1253}
1254
mflodmancc3d4422017-08-03 08:27:51 -07001255void VideoStreamEncoder::AdaptCounter::DecrementResolution(int reason) {
asaperssonf7e294d2017-06-13 23:25:22 -07001256 if (resolution_counters_[reason] == 0) {
1257 // Balanced mode: Adapt up is in a different order, switch reason.
1258 RTC_DCHECK_GT(TotalCount(reason), 0) << "No downgrade for reason.";
1259 RTC_DCHECK_GT(ResolutionCount(), 0) << "Resolution not downgraded.";
1260 MoveCount(&fps_counters_, reason);
1261 MoveCount(&resolution_counters_, (reason + 1) % kScaleReasonSize);
1262 }
1263 --(resolution_counters_[reason]);
1264 RTC_DCHECK_GE(resolution_counters_[reason], 0);
1265}
1266
mflodmancc3d4422017-08-03 08:27:51 -07001267void VideoStreamEncoder::AdaptCounter::DecrementFramerate(int reason,
1268 int cur_fps) {
asaperssonf7e294d2017-06-13 23:25:22 -07001269 DecrementFramerate(reason);
1270 // Reset if at max fps (i.e. in case of fewer steps up than down).
1271 if (cur_fps == std::numeric_limits<int>::max())
1272 std::fill(fps_counters_.begin(), fps_counters_.end(), 0);
asapersson09f05612017-05-15 23:40:18 -07001273}
1274
mflodmancc3d4422017-08-03 08:27:51 -07001275int VideoStreamEncoder::AdaptCounter::FramerateCount() const {
asapersson09f05612017-05-15 23:40:18 -07001276 return Count(fps_counters_);
1277}
1278
mflodmancc3d4422017-08-03 08:27:51 -07001279int VideoStreamEncoder::AdaptCounter::ResolutionCount() const {
asapersson09f05612017-05-15 23:40:18 -07001280 return Count(resolution_counters_);
1281}
1282
mflodmancc3d4422017-08-03 08:27:51 -07001283int VideoStreamEncoder::AdaptCounter::FramerateCount(int reason) const {
asapersson09f05612017-05-15 23:40:18 -07001284 return fps_counters_[reason];
1285}
1286
mflodmancc3d4422017-08-03 08:27:51 -07001287int VideoStreamEncoder::AdaptCounter::ResolutionCount(int reason) const {
asapersson09f05612017-05-15 23:40:18 -07001288 return resolution_counters_[reason];
1289}
1290
mflodmancc3d4422017-08-03 08:27:51 -07001291int VideoStreamEncoder::AdaptCounter::TotalCount(int reason) const {
asapersson09f05612017-05-15 23:40:18 -07001292 return FramerateCount(reason) + ResolutionCount(reason);
1293}
1294
mflodmancc3d4422017-08-03 08:27:51 -07001295int VideoStreamEncoder::AdaptCounter::Count(
1296 const std::vector<int>& counters) const {
asapersson09f05612017-05-15 23:40:18 -07001297 return std::accumulate(counters.begin(), counters.end(), 0);
1298}
1299
mflodmancc3d4422017-08-03 08:27:51 -07001300void VideoStreamEncoder::AdaptCounter::MoveCount(std::vector<int>* counters,
1301 int from_reason) {
asaperssonf7e294d2017-06-13 23:25:22 -07001302 int to_reason = (from_reason + 1) % kScaleReasonSize;
1303 ++((*counters)[to_reason]);
1304 --((*counters)[from_reason]);
1305}
1306
mflodmancc3d4422017-08-03 08:27:51 -07001307std::string VideoStreamEncoder::AdaptCounter::ToString(
asapersson09f05612017-05-15 23:40:18 -07001308 const std::vector<int>& counters) const {
1309 std::stringstream ss;
1310 for (size_t reason = 0; reason < kScaleReasonSize; ++reason) {
1311 ss << (reason ? " cpu" : "quality") << ":" << counters[reason];
sprangc5d62e22017-04-02 23:53:04 -07001312 }
asapersson09f05612017-05-15 23:40:18 -07001313 return ss.str();
sprangc5d62e22017-04-02 23:53:04 -07001314}
1315
mflodman@webrtc.org84d17832011-12-01 17:02:23 +00001316} // namespace webrtc