blob: 786ded4c82591fe72ac68420bc1b19130bb114de [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
Niels Möller4dc66c52018-10-05 14:17:58 +020018#include "api/video/encoded_image.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "api/video/i420_buffer.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"
Jonas Olsson366a50c2018-09-06 13:41:30 +020027#include "rtc_base/strings/string_builder.h"
Karl Wiberg80ba3332018-02-05 10:33:35 +010028#include "rtc_base/system/fallthrough.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020029#include "rtc_base/timeutils.h"
30#include "rtc_base/trace_event.h"
Kári Tristan Helgason639602a2018-08-02 10:51:40 +020031#include "system_wrappers/include/field_trial.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020032#include "video/overuse_frame_detector.h"
nisseea3a7982017-05-15 02:42:11 -070033
niklase@google.com470e71d2011-07-07 08:21:25 +000034namespace webrtc {
35
perkj26091b12016-09-01 01:17:40 -070036namespace {
sprangb1ca0732017-02-01 08:38:12 -080037
asapersson6ffb67d2016-09-12 00:10:45 -070038// Time interval for logging frame counts.
39const int64_t kFrameLogIntervalMs = 60000;
sprangc5d62e22017-04-02 23:53:04 -070040const int kMinFramerateFps = 2;
perkj26091b12016-09-01 01:17:40 -070041
Sebastian Janssona3177052018-04-10 13:05:49 +020042// Time to keep a single cached pending frame in paused state.
43const int64_t kPendingFrameTimeoutMs = 1000;
44
Kári Tristan Helgason639602a2018-08-02 10:51:40 +020045const char kInitialFramedropFieldTrial[] = "WebRTC-InitialFramedrop";
46
kthelgason2bc68642017-02-07 07:02:22 -080047// The maximum number of frames to drop at beginning of stream
48// to try and achieve desired bitrate.
49const int kMaxInitialFramedrop = 4;
Kári Tristan Helgason639602a2018-08-02 10:51:40 +020050// When the first change in BWE above this threshold occurs,
51// enable DropFrameDueToSize logic.
52const float kFramedropThreshold = 0.3;
kthelgason2bc68642017-02-07 07:02:22 -080053
Taylor Brandstetter49fcc102018-05-16 14:20:41 -070054// Initial limits for BALANCED degradation preference.
asaperssonf7e294d2017-06-13 23:25:22 -070055int MinFps(int pixels) {
56 if (pixels <= 320 * 240) {
57 return 7;
58 } else if (pixels <= 480 * 270) {
59 return 10;
60 } else if (pixels <= 640 * 480) {
61 return 15;
62 } else {
63 return std::numeric_limits<int>::max();
64 }
65}
66
67int MaxFps(int pixels) {
68 if (pixels <= 320 * 240) {
69 return 10;
70 } else if (pixels <= 480 * 270) {
71 return 15;
72 } else {
73 return std::numeric_limits<int>::max();
74 }
75}
76
Kári Tristan Helgason639602a2018-08-02 10:51:40 +020077uint32_t abs_diff(uint32_t a, uint32_t b) {
78 return (a < b) ? b - a : a - b;
79}
80
Taylor Brandstetter49fcc102018-05-16 14:20:41 -070081bool IsResolutionScalingEnabled(DegradationPreference degradation_preference) {
82 return degradation_preference == DegradationPreference::MAINTAIN_FRAMERATE ||
83 degradation_preference == DegradationPreference::BALANCED;
asapersson09f05612017-05-15 23:40:18 -070084}
85
Taylor Brandstetter49fcc102018-05-16 14:20:41 -070086bool IsFramerateScalingEnabled(DegradationPreference degradation_preference) {
87 return degradation_preference == DegradationPreference::MAINTAIN_RESOLUTION ||
88 degradation_preference == DegradationPreference::BALANCED;
asapersson09f05612017-05-15 23:40:18 -070089}
90
Niels Möllerd1f7eb62018-03-28 16:40:58 +020091// TODO(pbos): Lower these thresholds (to closer to 100%) when we handle
92// pipelining encoders better (multiple input frames before something comes
93// out). This should effectively turn off CPU adaptations for systems that
94// remotely cope with the load right now.
95CpuOveruseOptions GetCpuOveruseOptions(
Niels Möller213618e2018-07-24 09:29:58 +020096 const VideoStreamEncoderSettings& settings,
Niels Möller4db138e2018-04-19 09:04:13 +020097 bool full_overuse_time) {
Niels Möllerd1f7eb62018-03-28 16:40:58 +020098 CpuOveruseOptions options;
99
Niels Möller4db138e2018-04-19 09:04:13 +0200100 if (full_overuse_time) {
Niels Möllerd1f7eb62018-03-28 16:40:58 +0200101 options.low_encode_usage_threshold_percent = 150;
102 options.high_encode_usage_threshold_percent = 200;
103 }
104 if (settings.experiment_cpu_load_estimator) {
105 options.filter_time_ms = 5 * rtc::kNumMillisecsPerSec;
106 }
107
108 return options;
109}
110
perkj26091b12016-09-01 01:17:40 -0700111} // namespace
112
perkja49cbd32016-09-16 07:53:41 -0700113// VideoSourceProxy is responsible ensuring thread safety between calls to
mflodmancc3d4422017-08-03 08:27:51 -0700114// VideoStreamEncoder::SetSource that will happen on libjingle's worker thread
115// when a video capturer is connected to the encoder and the encoder task queue
perkja49cbd32016-09-16 07:53:41 -0700116// (encoder_queue_) where the encoder reports its VideoSinkWants.
mflodmancc3d4422017-08-03 08:27:51 -0700117class VideoStreamEncoder::VideoSourceProxy {
perkja49cbd32016-09-16 07:53:41 -0700118 public:
mflodmancc3d4422017-08-03 08:27:51 -0700119 explicit VideoSourceProxy(VideoStreamEncoder* video_stream_encoder)
120 : video_stream_encoder_(video_stream_encoder),
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700121 degradation_preference_(DegradationPreference::DISABLED),
Åsa Persson8c1bf952018-09-13 10:42:19 +0200122 source_(nullptr),
123 max_framerate_(std::numeric_limits<int>::max()) {}
perkja49cbd32016-09-16 07:53:41 -0700124
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700125 void SetSource(rtc::VideoSourceInterface<VideoFrame>* source,
126 const DegradationPreference& degradation_preference) {
perkj803d97f2016-11-01 11:45:46 -0700127 // Called on libjingle's worker thread.
perkja49cbd32016-09-16 07:53:41 -0700128 RTC_DCHECK_CALLED_SEQUENTIALLY(&main_checker_);
129 rtc::VideoSourceInterface<VideoFrame>* old_source = nullptr;
perkj803d97f2016-11-01 11:45:46 -0700130 rtc::VideoSinkWants wants;
perkja49cbd32016-09-16 07:53:41 -0700131 {
132 rtc::CritScope lock(&crit_);
sprangc5d62e22017-04-02 23:53:04 -0700133 degradation_preference_ = degradation_preference;
perkja49cbd32016-09-16 07:53:41 -0700134 old_source = source_;
135 source_ = source;
sprangfda496a2017-06-15 04:21:07 -0700136 wants = GetActiveSinkWantsInternal();
perkja49cbd32016-09-16 07:53:41 -0700137 }
138
139 if (old_source != source && old_source != nullptr) {
mflodmancc3d4422017-08-03 08:27:51 -0700140 old_source->RemoveSink(video_stream_encoder_);
perkja49cbd32016-09-16 07:53:41 -0700141 }
142
143 if (!source) {
144 return;
145 }
146
mflodmancc3d4422017-08-03 08:27:51 -0700147 source->AddOrUpdateSink(video_stream_encoder_, wants);
perkja49cbd32016-09-16 07:53:41 -0700148 }
149
Åsa Persson8c1bf952018-09-13 10:42:19 +0200150 void SetMaxFramerate(int max_framerate) {
151 RTC_DCHECK_GT(max_framerate, 0);
152 rtc::CritScope lock(&crit_);
153 if (max_framerate == max_framerate_)
154 return;
155
156 RTC_LOG(LS_INFO) << "Set max framerate: " << max_framerate;
157 max_framerate_ = max_framerate;
158 if (source_) {
159 source_->AddOrUpdateSink(video_stream_encoder_,
160 GetActiveSinkWantsInternal());
161 }
162 }
163
perkj803d97f2016-11-01 11:45:46 -0700164 void SetWantsRotationApplied(bool rotation_applied) {
165 rtc::CritScope lock(&crit_);
166 sink_wants_.rotation_applied = rotation_applied;
Åsa Persson8c1bf952018-09-13 10:42:19 +0200167 if (source_) {
168 source_->AddOrUpdateSink(video_stream_encoder_,
169 GetActiveSinkWantsInternal());
170 }
sprangc5d62e22017-04-02 23:53:04 -0700171 }
172
sprangfda496a2017-06-15 04:21:07 -0700173 rtc::VideoSinkWants GetActiveSinkWants() {
174 rtc::CritScope lock(&crit_);
175 return GetActiveSinkWantsInternal();
perkj803d97f2016-11-01 11:45:46 -0700176 }
177
asaperssonf7e294d2017-06-13 23:25:22 -0700178 void ResetPixelFpsCount() {
179 rtc::CritScope lock(&crit_);
180 sink_wants_.max_pixel_count = std::numeric_limits<int>::max();
181 sink_wants_.target_pixel_count.reset();
182 sink_wants_.max_framerate_fps = std::numeric_limits<int>::max();
183 if (source_)
Åsa Persson8c1bf952018-09-13 10:42:19 +0200184 source_->AddOrUpdateSink(video_stream_encoder_,
185 GetActiveSinkWantsInternal());
asaperssonf7e294d2017-06-13 23:25:22 -0700186 }
187
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100188 bool RequestResolutionLowerThan(int pixel_count,
189 int min_pixels_per_frame,
190 bool* min_pixels_reached) {
perkj803d97f2016-11-01 11:45:46 -0700191 // Called on the encoder task queue.
192 rtc::CritScope lock(&crit_);
asapersson13874762017-06-07 00:01:02 -0700193 if (!source_ || !IsResolutionScalingEnabled(degradation_preference_)) {
asapersson02465b82017-04-10 01:12:52 -0700194 // This can happen since |degradation_preference_| is set on libjingle's
195 // worker thread but the adaptation is done on the encoder task queue.
asaperssond0de2952017-04-21 01:47:31 -0700196 return false;
perkj803d97f2016-11-01 11:45:46 -0700197 }
asapersson13874762017-06-07 00:01:02 -0700198 // The input video frame size will have a resolution less than or equal to
199 // |max_pixel_count| depending on how the source can scale the frame size.
kthelgason5e13d412016-12-01 03:59:51 -0800200 const int pixels_wanted = (pixel_count * 3) / 5;
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100201 if (pixels_wanted >= sink_wants_.max_pixel_count) {
202 return false;
203 }
204 if (pixels_wanted < min_pixels_per_frame) {
205 *min_pixels_reached = true;
asaperssond0de2952017-04-21 01:47:31 -0700206 return false;
asapersson13874762017-06-07 00:01:02 -0700207 }
Mirko Bonadei675513b2017-11-09 11:09:25 +0100208 RTC_LOG(LS_INFO) << "Scaling down resolution, max pixels: "
209 << pixels_wanted;
sprangc5d62e22017-04-02 23:53:04 -0700210 sink_wants_.max_pixel_count = pixels_wanted;
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200211 sink_wants_.target_pixel_count = absl::nullopt;
mflodmancc3d4422017-08-03 08:27:51 -0700212 source_->AddOrUpdateSink(video_stream_encoder_,
213 GetActiveSinkWantsInternal());
asaperssond0de2952017-04-21 01:47:31 -0700214 return true;
sprangc5d62e22017-04-02 23:53:04 -0700215 }
216
sprangfda496a2017-06-15 04:21:07 -0700217 int RequestFramerateLowerThan(int fps) {
sprangc5d62e22017-04-02 23:53:04 -0700218 // Called on the encoder task queue.
asapersson13874762017-06-07 00:01:02 -0700219 // The input video frame rate will be scaled down to 2/3, rounding down.
sprangfda496a2017-06-15 04:21:07 -0700220 int framerate_wanted = (fps * 2) / 3;
221 return RestrictFramerate(framerate_wanted) ? framerate_wanted : -1;
perkj803d97f2016-11-01 11:45:46 -0700222 }
223
asapersson13874762017-06-07 00:01:02 -0700224 bool RequestHigherResolutionThan(int pixel_count) {
225 // Called on the encoder task queue.
perkj803d97f2016-11-01 11:45:46 -0700226 rtc::CritScope lock(&crit_);
asapersson13874762017-06-07 00:01:02 -0700227 if (!source_ || !IsResolutionScalingEnabled(degradation_preference_)) {
asapersson02465b82017-04-10 01:12:52 -0700228 // This can happen since |degradation_preference_| is set on libjingle's
229 // worker thread but the adaptation is done on the encoder task queue.
asapersson13874762017-06-07 00:01:02 -0700230 return false;
perkj803d97f2016-11-01 11:45:46 -0700231 }
asapersson13874762017-06-07 00:01:02 -0700232 int max_pixels_wanted = pixel_count;
233 if (max_pixels_wanted != std::numeric_limits<int>::max())
234 max_pixels_wanted = pixel_count * 4;
sprangc5d62e22017-04-02 23:53:04 -0700235
asapersson13874762017-06-07 00:01:02 -0700236 if (max_pixels_wanted <= sink_wants_.max_pixel_count)
237 return false;
238
239 sink_wants_.max_pixel_count = max_pixels_wanted;
240 if (max_pixels_wanted == std::numeric_limits<int>::max()) {
sprangc5d62e22017-04-02 23:53:04 -0700241 // Remove any constraints.
242 sink_wants_.target_pixel_count.reset();
sprangc5d62e22017-04-02 23:53:04 -0700243 } else {
244 // On step down we request at most 3/5 the pixel count of the previous
245 // resolution, so in order to take "one step up" we request a resolution
246 // as close as possible to 5/3 of the current resolution. The actual pixel
247 // count selected depends on the capabilities of the source. In order to
248 // not take a too large step up, we cap the requested pixel count to be at
249 // most four time the current number of pixels.
Oskar Sundbom8e07c132018-01-08 16:45:42 +0100250 sink_wants_.target_pixel_count = (pixel_count * 5) / 3;
sprangc5d62e22017-04-02 23:53:04 -0700251 }
Mirko Bonadei675513b2017-11-09 11:09:25 +0100252 RTC_LOG(LS_INFO) << "Scaling up resolution, max pixels: "
253 << max_pixels_wanted;
mflodmancc3d4422017-08-03 08:27:51 -0700254 source_->AddOrUpdateSink(video_stream_encoder_,
255 GetActiveSinkWantsInternal());
asapersson13874762017-06-07 00:01:02 -0700256 return true;
sprangc5d62e22017-04-02 23:53:04 -0700257 }
258
sprangfda496a2017-06-15 04:21:07 -0700259 // Request upgrade in framerate. Returns the new requested frame, or -1 if
260 // no change requested. Note that maxint may be returned if limits due to
261 // adaptation requests are removed completely. In that case, consider
262 // |max_framerate_| to be the current limit (assuming the capturer complies).
263 int RequestHigherFramerateThan(int fps) {
asapersson13874762017-06-07 00:01:02 -0700264 // Called on the encoder task queue.
265 // The input frame rate will be scaled up to the last step, with rounding.
266 int framerate_wanted = fps;
267 if (fps != std::numeric_limits<int>::max())
268 framerate_wanted = (fps * 3) / 2;
269
sprangfda496a2017-06-15 04:21:07 -0700270 return IncreaseFramerate(framerate_wanted) ? framerate_wanted : -1;
asapersson13874762017-06-07 00:01:02 -0700271 }
272
273 bool RestrictFramerate(int fps) {
sprangc5d62e22017-04-02 23:53:04 -0700274 // Called on the encoder task queue.
275 rtc::CritScope lock(&crit_);
asapersson13874762017-06-07 00:01:02 -0700276 if (!source_ || !IsFramerateScalingEnabled(degradation_preference_))
277 return false;
278
279 const int fps_wanted = std::max(kMinFramerateFps, fps);
280 if (fps_wanted >= sink_wants_.max_framerate_fps)
281 return false;
282
Mirko Bonadei675513b2017-11-09 11:09:25 +0100283 RTC_LOG(LS_INFO) << "Scaling down framerate: " << fps_wanted;
asapersson13874762017-06-07 00:01:02 -0700284 sink_wants_.max_framerate_fps = fps_wanted;
mflodmancc3d4422017-08-03 08:27:51 -0700285 source_->AddOrUpdateSink(video_stream_encoder_,
286 GetActiveSinkWantsInternal());
asapersson13874762017-06-07 00:01:02 -0700287 return true;
288 }
289
290 bool IncreaseFramerate(int fps) {
291 // Called on the encoder task queue.
292 rtc::CritScope lock(&crit_);
293 if (!source_ || !IsFramerateScalingEnabled(degradation_preference_))
294 return false;
295
296 const int fps_wanted = std::max(kMinFramerateFps, fps);
297 if (fps_wanted <= sink_wants_.max_framerate_fps)
298 return false;
299
Mirko Bonadei675513b2017-11-09 11:09:25 +0100300 RTC_LOG(LS_INFO) << "Scaling up framerate: " << fps_wanted;
asapersson13874762017-06-07 00:01:02 -0700301 sink_wants_.max_framerate_fps = fps_wanted;
mflodmancc3d4422017-08-03 08:27:51 -0700302 source_->AddOrUpdateSink(video_stream_encoder_,
303 GetActiveSinkWantsInternal());
asapersson13874762017-06-07 00:01:02 -0700304 return true;
perkj803d97f2016-11-01 11:45:46 -0700305 }
306
perkja49cbd32016-09-16 07:53:41 -0700307 private:
sprangfda496a2017-06-15 04:21:07 -0700308 rtc::VideoSinkWants GetActiveSinkWantsInternal()
danilchapa37de392017-09-09 04:17:22 -0700309 RTC_EXCLUSIVE_LOCKS_REQUIRED(&crit_) {
sprangfda496a2017-06-15 04:21:07 -0700310 rtc::VideoSinkWants wants = sink_wants_;
311 // Clear any constraints from the current sink wants that don't apply to
312 // the used degradation_preference.
313 switch (degradation_preference_) {
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700314 case DegradationPreference::BALANCED:
sprangfda496a2017-06-15 04:21:07 -0700315 break;
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700316 case DegradationPreference::MAINTAIN_FRAMERATE:
sprangfda496a2017-06-15 04:21:07 -0700317 wants.max_framerate_fps = std::numeric_limits<int>::max();
318 break;
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700319 case DegradationPreference::MAINTAIN_RESOLUTION:
sprangfda496a2017-06-15 04:21:07 -0700320 wants.max_pixel_count = std::numeric_limits<int>::max();
321 wants.target_pixel_count.reset();
322 break;
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700323 case DegradationPreference::DISABLED:
sprangfda496a2017-06-15 04:21:07 -0700324 wants.max_pixel_count = std::numeric_limits<int>::max();
325 wants.target_pixel_count.reset();
326 wants.max_framerate_fps = std::numeric_limits<int>::max();
327 }
Åsa Persson8c1bf952018-09-13 10:42:19 +0200328 // Limit to configured max framerate.
329 wants.max_framerate_fps = std::min(max_framerate_, wants.max_framerate_fps);
sprangfda496a2017-06-15 04:21:07 -0700330 return wants;
331 }
332
perkja49cbd32016-09-16 07:53:41 -0700333 rtc::CriticalSection crit_;
334 rtc::SequencedTaskChecker main_checker_;
mflodmancc3d4422017-08-03 08:27:51 -0700335 VideoStreamEncoder* const video_stream_encoder_;
danilchapa37de392017-09-09 04:17:22 -0700336 rtc::VideoSinkWants sink_wants_ RTC_GUARDED_BY(&crit_);
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700337 DegradationPreference degradation_preference_ RTC_GUARDED_BY(&crit_);
danilchapa37de392017-09-09 04:17:22 -0700338 rtc::VideoSourceInterface<VideoFrame>* source_ RTC_GUARDED_BY(&crit_);
Åsa Persson8c1bf952018-09-13 10:42:19 +0200339 int max_framerate_ RTC_GUARDED_BY(&crit_);
perkja49cbd32016-09-16 07:53:41 -0700340
341 RTC_DISALLOW_COPY_AND_ASSIGN(VideoSourceProxy);
342};
343
Åsa Persson0122e842017-10-16 12:19:23 +0200344VideoStreamEncoder::VideoStreamEncoder(
345 uint32_t number_of_cores,
Niels Möller213618e2018-07-24 09:29:58 +0200346 VideoStreamEncoderObserver* encoder_stats_observer,
347 const VideoStreamEncoderSettings& settings,
Åsa Persson0122e842017-10-16 12:19:23 +0200348 rtc::VideoSinkInterface<VideoFrame>* pre_encode_callback,
Åsa Persson0122e842017-10-16 12:19:23 +0200349 std::unique_ptr<OveruseFrameDetector> overuse_detector)
perkj26091b12016-09-01 01:17:40 -0700350 : shutdown_event_(true /* manual_reset */, false),
351 number_of_cores_(number_of_cores),
Kári Tristan Helgason639602a2018-08-02 10:51:40 +0200352 initial_framedrop_(0),
353 initial_framedrop_on_bwe_enabled_(
354 webrtc::field_trial::IsEnabled(kInitialFramedropFieldTrial)),
Åsa Perssona945aee2018-04-24 16:53:25 +0200355 quality_scaling_experiment_enabled_(QualityScalingExperiment::Enabled()),
perkja49cbd32016-09-16 07:53:41 -0700356 source_proxy_(new VideoSourceProxy(this)),
Pera48ddb72016-09-29 11:48:50 +0200357 sink_(nullptr),
perkj26091b12016-09-01 01:17:40 -0700358 settings_(settings),
Niels Möllera0565992017-10-24 11:37:08 +0200359 video_sender_(Clock::GetRealTimeClock(), this),
Niels Möller73f29cb2018-01-31 16:09:31 +0100360 overuse_detector_(std::move(overuse_detector)),
Niels Möller213618e2018-07-24 09:29:58 +0200361 encoder_stats_observer_(encoder_stats_observer),
perkj26091b12016-09-01 01:17:40 -0700362 pre_encode_callback_(pre_encode_callback),
sprangfda496a2017-06-15 04:21:07 -0700363 max_framerate_(-1),
perkjfa10b552016-10-02 23:45:26 -0700364 pending_encoder_reconfiguration_(false),
Niels Möller4db138e2018-04-19 09:04:13 +0200365 pending_encoder_creation_(false),
perkj26091b12016-09-01 01:17:40 -0700366 encoder_start_bitrate_bps_(0),
Pera48ddb72016-09-29 11:48:50 +0200367 max_data_payload_length_(0),
pbos@webrtc.org143451d2015-03-18 14:40:03 +0000368 last_observed_bitrate_bps_(0),
stefan@webrtc.org792f1a12015-03-04 12:24:26 +0000369 encoder_paused_and_dropped_frame_(false),
perkj26091b12016-09-01 01:17:40 -0700370 clock_(Clock::GetRealTimeClock()),
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700371 degradation_preference_(DegradationPreference::DISABLED),
Yuwei Huangd9f99c12017-10-24 15:40:52 -0700372 posted_frames_waiting_for_encode_(0),
perkj26091b12016-09-01 01:17:40 -0700373 last_captured_timestamp_(0),
374 delta_ntp_internal_ms_(clock_->CurrentNtpInMilliseconds() -
375 clock_->TimeInMilliseconds()),
asapersson6ffb67d2016-09-12 00:10:45 -0700376 last_frame_log_ms_(clock_->TimeInMilliseconds()),
377 captured_frame_count_(0),
378 dropped_frame_count_(0),
sprang1a646ee2016-12-01 06:34:11 -0800379 bitrate_observer_(nullptr),
perkj26091b12016-09-01 01:17:40 -0700380 encoder_queue_("EncoderQueue") {
Niels Möller213618e2018-07-24 09:29:58 +0200381 RTC_DCHECK(encoder_stats_observer);
Niels Möller73f29cb2018-01-31 16:09:31 +0100382 RTC_DCHECK(overuse_detector_);
mflodman@webrtc.org02270cd2015-02-06 13:10:19 +0000383}
384
mflodmancc3d4422017-08-03 08:27:51 -0700385VideoStreamEncoder::~VideoStreamEncoder() {
perkja49cbd32016-09-16 07:53:41 -0700386 RTC_DCHECK_RUN_ON(&thread_checker_);
perkj26091b12016-09-01 01:17:40 -0700387 RTC_DCHECK(shutdown_event_.Wait(0))
388 << "Must call ::Stop() before destruction.";
389}
390
mflodmancc3d4422017-08-03 08:27:51 -0700391void VideoStreamEncoder::Stop() {
perkja49cbd32016-09-16 07:53:41 -0700392 RTC_DCHECK_RUN_ON(&thread_checker_);
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700393 source_proxy_->SetSource(nullptr, DegradationPreference());
perkja49cbd32016-09-16 07:53:41 -0700394 encoder_queue_.PostTask([this] {
395 RTC_DCHECK_RUN_ON(&encoder_queue_);
sprangfda496a2017-06-15 04:21:07 -0700396 overuse_detector_->StopCheckForOveruse();
Erik Språng08127a92016-11-16 16:41:30 +0100397 rate_allocator_.reset();
sprang1a646ee2016-12-01 06:34:11 -0800398 bitrate_observer_ = nullptr;
Niels Möllerbf3dbb42018-03-16 13:38:46 +0100399 video_sender_.RegisterExternalEncoder(nullptr, false);
kthelgason876222f2016-11-29 01:44:11 -0800400 quality_scaler_ = nullptr;
perkja49cbd32016-09-16 07:53:41 -0700401 shutdown_event_.Set();
402 });
403
404 shutdown_event_.Wait(rtc::Event::kForever);
perkj26091b12016-09-01 01:17:40 -0700405}
406
Niels Möller0327c2d2018-05-21 14:09:31 +0200407void VideoStreamEncoder::SetBitrateAllocationObserver(
sprang1a646ee2016-12-01 06:34:11 -0800408 VideoBitrateAllocationObserver* bitrate_observer) {
409 RTC_DCHECK_RUN_ON(&thread_checker_);
410 encoder_queue_.PostTask([this, bitrate_observer] {
411 RTC_DCHECK_RUN_ON(&encoder_queue_);
412 RTC_DCHECK(!bitrate_observer_);
413 bitrate_observer_ = bitrate_observer;
414 });
415}
416
mflodmancc3d4422017-08-03 08:27:51 -0700417void VideoStreamEncoder::SetSource(
perkj803d97f2016-11-01 11:45:46 -0700418 rtc::VideoSourceInterface<VideoFrame>* source,
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700419 const DegradationPreference& degradation_preference) {
perkja49cbd32016-09-16 07:53:41 -0700420 RTC_DCHECK_RUN_ON(&thread_checker_);
perkj803d97f2016-11-01 11:45:46 -0700421 source_proxy_->SetSource(source, degradation_preference);
422 encoder_queue_.PostTask([this, degradation_preference] {
423 RTC_DCHECK_RUN_ON(&encoder_queue_);
sprangc5d62e22017-04-02 23:53:04 -0700424 if (degradation_preference_ != degradation_preference) {
425 // Reset adaptation state, so that we're not tricked into thinking there's
426 // an already pending request of the same type.
427 last_adaptation_request_.reset();
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700428 if (degradation_preference == DegradationPreference::BALANCED ||
429 degradation_preference_ == DegradationPreference::BALANCED) {
asaperssonf7e294d2017-06-13 23:25:22 -0700430 // TODO(asapersson): Consider removing |adapt_counters_| map and use one
431 // AdaptCounter for all modes.
432 source_proxy_->ResetPixelFpsCount();
433 adapt_counters_.clear();
434 }
sprangc5d62e22017-04-02 23:53:04 -0700435 }
sprangb1ca0732017-02-01 08:38:12 -0800436 degradation_preference_ = degradation_preference;
Niels Möller4db138e2018-04-19 09:04:13 +0200437
Niels Möller2d061182018-04-24 09:13:08 +0200438 if (encoder_)
439 ConfigureQualityScaler();
Niels Möller4db138e2018-04-19 09:04:13 +0200440
Niels Möller7dc26b72017-12-06 10:27:48 +0100441 if (!IsFramerateScalingEnabled(degradation_preference) &&
442 max_framerate_ != -1) {
443 // If frame rate scaling is no longer allowed, remove any potential
444 // allowance for longer frame intervals.
445 overuse_detector_->OnTargetFramerateUpdated(max_framerate_);
446 }
perkj803d97f2016-11-01 11:45:46 -0700447 });
perkja49cbd32016-09-16 07:53:41 -0700448}
449
mflodmancc3d4422017-08-03 08:27:51 -0700450void VideoStreamEncoder::SetSink(EncoderSink* sink, bool rotation_applied) {
perkj803d97f2016-11-01 11:45:46 -0700451 source_proxy_->SetWantsRotationApplied(rotation_applied);
perkj26091b12016-09-01 01:17:40 -0700452 encoder_queue_.PostTask([this, sink] {
453 RTC_DCHECK_RUN_ON(&encoder_queue_);
454 sink_ = sink;
455 });
mflodman@webrtc.org84d17832011-12-01 17:02:23 +0000456}
457
mflodmancc3d4422017-08-03 08:27:51 -0700458void VideoStreamEncoder::SetStartBitrate(int start_bitrate_bps) {
perkj26091b12016-09-01 01:17:40 -0700459 encoder_queue_.PostTask([this, start_bitrate_bps] {
460 RTC_DCHECK_RUN_ON(&encoder_queue_);
461 encoder_start_bitrate_bps_ = start_bitrate_bps;
462 });
mflodman@webrtc.org84d17832011-12-01 17:02:23 +0000463}
Peter Boström00b9d212016-05-19 16:59:03 +0200464
mflodmancc3d4422017-08-03 08:27:51 -0700465void VideoStreamEncoder::ConfigureEncoder(VideoEncoderConfig config,
Niels Möllerf1338562018-04-26 09:51:47 +0200466 size_t max_data_payload_length) {
Sebastian Jansson3dc01252018-03-19 19:27:44 +0100467 // TODO(srte): This struct should be replaced by a lambda with move capture
468 // when C++14 lambda is allowed.
469 struct ConfigureEncoderTask {
470 void operator()() {
Yves Gerey665174f2018-06-19 15:03:05 +0200471 encoder->ConfigureEncoderOnTaskQueue(std::move(config),
472 max_data_payload_length);
Sebastian Jansson3dc01252018-03-19 19:27:44 +0100473 }
474 VideoStreamEncoder* encoder;
475 VideoEncoderConfig config;
476 size_t max_data_payload_length;
Sebastian Jansson3dc01252018-03-19 19:27:44 +0100477 };
Yves Gerey665174f2018-06-19 15:03:05 +0200478 encoder_queue_.PostTask(
479 ConfigureEncoderTask{this, std::move(config), max_data_payload_length});
perkj26091b12016-09-01 01:17:40 -0700480}
481
mflodmancc3d4422017-08-03 08:27:51 -0700482void VideoStreamEncoder::ConfigureEncoderOnTaskQueue(
483 VideoEncoderConfig config,
Niels Möllerf1338562018-04-26 09:51:47 +0200484 size_t max_data_payload_length) {
perkj26091b12016-09-01 01:17:40 -0700485 RTC_DCHECK_RUN_ON(&encoder_queue_);
perkj26091b12016-09-01 01:17:40 -0700486 RTC_DCHECK(sink_);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100487 RTC_LOG(LS_INFO) << "ConfigureEncoder requested.";
Pera48ddb72016-09-29 11:48:50 +0200488
489 max_data_payload_length_ = max_data_payload_length;
Niels Möller4db138e2018-04-19 09:04:13 +0200490 pending_encoder_creation_ =
491 (!encoder_ || encoder_config_.video_format != config.video_format);
Pera48ddb72016-09-29 11:48:50 +0200492 encoder_config_ = std::move(config);
perkjfa10b552016-10-02 23:45:26 -0700493 pending_encoder_reconfiguration_ = true;
Pera48ddb72016-09-29 11:48:50 +0200494
perkjfa10b552016-10-02 23:45:26 -0700495 // Reconfigure the encoder now if the encoder has an internal source or
Per21d45d22016-10-30 21:37:57 +0100496 // if the frame resolution is known. Otherwise, the reconfiguration is
497 // deferred until the next frame to minimize the number of reconfigurations.
498 // The codec configuration depends on incoming video frame size.
499 if (last_frame_info_) {
500 ReconfigureEncoder();
Yves Gerey665174f2018-06-19 15:03:05 +0200501 } else if (settings_.encoder_factory
502 ->QueryVideoEncoder(encoder_config_.video_format)
503 .has_internal_source) {
Niels Moller0d650b42018-04-18 07:17:07 +0000504 last_frame_info_ = VideoFrameInfo(176, 144, false);
505 ReconfigureEncoder();
perkjfa10b552016-10-02 23:45:26 -0700506 }
507}
perkj26091b12016-09-01 01:17:40 -0700508
Seth Hampsoncc7125f2018-02-02 08:46:16 -0800509// TODO(bugs.webrtc.org/8807): Currently this always does a hard
510// reconfiguration, but this isn't always necessary. Add in logic to only update
511// the VideoBitrateAllocator and call OnEncoderConfigurationChanged with a
512// "soft" reconfiguration.
mflodmancc3d4422017-08-03 08:27:51 -0700513void VideoStreamEncoder::ReconfigureEncoder() {
perkjfa10b552016-10-02 23:45:26 -0700514 RTC_DCHECK(pending_encoder_reconfiguration_);
515 std::vector<VideoStream> streams =
516 encoder_config_.video_stream_factory->CreateEncoderStreams(
517 last_frame_info_->width, last_frame_info_->height, encoder_config_);
perkj26091b12016-09-01 01:17:40 -0700518
ilnik6b826ef2017-06-16 06:53:48 -0700519 // TODO(ilnik): If configured resolution is significantly less than provided,
520 // e.g. because there are not enough SSRCs for all simulcast streams,
521 // signal new resolutions via SinkWants to video source.
522
523 // Stream dimensions may be not equal to given because of a simulcast
524 // restrictions.
525 int highest_stream_width = static_cast<int>(streams.back().width);
526 int highest_stream_height = static_cast<int>(streams.back().height);
527 // Dimension may be reduced to be, e.g. divisible by 4.
528 RTC_CHECK_GE(last_frame_info_->width, highest_stream_width);
529 RTC_CHECK_GE(last_frame_info_->height, highest_stream_height);
530 crop_width_ = last_frame_info_->width - highest_stream_width;
531 crop_height_ = last_frame_info_->height - highest_stream_height;
532
Erik Språng08127a92016-11-16 16:41:30 +0100533 VideoCodec codec;
Yves Gerey665174f2018-06-19 15:03:05 +0200534 if (!VideoCodecInitializer::SetupCodec(encoder_config_, streams, &codec,
535 &rate_allocator_)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100536 RTC_LOG(LS_ERROR) << "Failed to create encoder configuration.";
Erik Språng08127a92016-11-16 16:41:30 +0100537 }
perkjfa10b552016-10-02 23:45:26 -0700538
“Michael277a6562018-06-01 14:09:19 -0500539 // Set min_bitrate_bps, max_bitrate_bps, and max padding bit rate for VP9.
540 if (encoder_config_.codec_type == kVideoCodecVP9) {
541 RTC_DCHECK_EQ(1U, streams.size());
542 int max_encoder_bitrate_kbps = 0;
543 for (int i = 0; i < codec.VP9()->numberOfSpatialLayers; ++i) {
544 max_encoder_bitrate_kbps += codec.spatialLayers[i].maxBitrate;
545 }
546 // Lower max bitrate to the level codec actually can produce.
547 streams[0].max_bitrate_bps =
548 std::min(streams[0].max_bitrate_bps, max_encoder_bitrate_kbps * 1000);
549 streams[0].min_bitrate_bps = codec.spatialLayers[0].minBitrate * 1000;
550 // Pass along the value of maximum padding bit rate from
551 // spatialLayers[].targetBitrate to streams[0].target_bitrate_bps.
552 // TODO(ssilkin): There should be some margin between max padding bitrate
553 // and max encoder bitrate. With the current logic they can be equal.
554 streams[0].target_bitrate_bps =
555 std::min(static_cast<unsigned int>(streams[0].max_bitrate_bps),
556 codec.spatialLayers[codec.VP9()->numberOfSpatialLayers - 1]
557 .targetBitrate *
558 1000);
559 }
560
perkjfa10b552016-10-02 23:45:26 -0700561 codec.startBitrate =
562 std::max(encoder_start_bitrate_bps_ / 1000, codec.minBitrate);
563 codec.startBitrate = std::min(codec.startBitrate, codec.maxBitrate);
564 codec.expect_encode_from_texture = last_frame_info_->is_texture;
sprangfda496a2017-06-15 04:21:07 -0700565 max_framerate_ = codec.maxFramerate;
Åsa Persson8c1bf952018-09-13 10:42:19 +0200566
567 // Inform source about max configured framerate.
568 int max_framerate = 0;
569 for (const auto& stream : streams) {
570 max_framerate = std::max(stream.max_framerate, max_framerate);
571 }
572 source_proxy_->SetMaxFramerate(max_framerate);
Stefan Holmere5904162015-03-26 11:11:06 +0100573
Niels Möller4db138e2018-04-19 09:04:13 +0200574 // Keep the same encoder, as long as the video_format is unchanged.
575 if (pending_encoder_creation_) {
576 pending_encoder_creation_ = false;
577 if (encoder_) {
578 video_sender_.RegisterExternalEncoder(nullptr, false);
579 }
580
Ilya Nikolaevskiyfc9dcb62018-06-11 10:04:54 +0200581 encoder_ = settings_.encoder_factory->CreateVideoEncoder(
582 encoder_config_.video_format);
Niels Möller4db138e2018-04-19 09:04:13 +0200583 // TODO(nisse): What to do if creating the encoder fails? Crash,
584 // or just discard incoming frames?
585 RTC_CHECK(encoder_);
586
Niels Möller4db138e2018-04-19 09:04:13 +0200587 const webrtc::VideoEncoderFactory::CodecInfo info =
588 settings_.encoder_factory->QueryVideoEncoder(
589 encoder_config_.video_format);
590
591 overuse_detector_->StopCheckForOveruse();
592 overuse_detector_->StartCheckForOveruse(
593 GetCpuOveruseOptions(settings_, info.is_hardware_accelerated), this);
594
595 video_sender_.RegisterExternalEncoder(encoder_.get(),
596 info.has_internal_source);
597 }
598 // RegisterSendCodec implies an unconditional call to
599 // encoder_->InitEncode().
Peter Boströmcd5c25c2016-04-21 16:48:08 +0200600 bool success = video_sender_.RegisterSendCodec(
perkjfa10b552016-10-02 23:45:26 -0700601 &codec, number_of_cores_,
602 static_cast<uint32_t>(max_data_payload_length_)) == VCM_OK;
Peter Boström905f8e72016-03-02 16:59:56 +0100603 if (!success) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100604 RTC_LOG(LS_ERROR) << "Failed to configure encoder.";
sprangfe627f32017-03-29 08:24:59 -0700605 rate_allocator_.reset();
mflodman@webrtc.org84d17832011-12-01 17:02:23 +0000606 }
Peter Boström905f8e72016-03-02 16:59:56 +0100607
Niels Möller96d7f762018-01-30 11:27:16 +0100608 video_sender_.UpdateChannelParameters(rate_allocator_.get(),
ilnik35b7de42017-03-15 04:24:21 -0700609 bitrate_observer_);
610
Niels Möller213618e2018-07-24 09:29:58 +0200611 encoder_stats_observer_->OnEncoderReconfigured(encoder_config_, streams);
Per512ecb32016-09-23 15:52:06 +0200612
perkjfa10b552016-10-02 23:45:26 -0700613 pending_encoder_reconfiguration_ = false;
Erik Språng08127a92016-11-16 16:41:30 +0100614
Pera48ddb72016-09-29 11:48:50 +0200615 sink_->OnEncoderConfigurationChanged(
perkjfa10b552016-10-02 23:45:26 -0700616 std::move(streams), encoder_config_.min_transmit_bitrate_bps);
kthelgason876222f2016-11-29 01:44:11 -0800617
Niels Möller7dc26b72017-12-06 10:27:48 +0100618 // Get the current target framerate, ie the maximum framerate as specified by
619 // the current codec configuration, or any limit imposed by cpu adaption in
620 // maintain-resolution or balanced mode. This is used to make sure overuse
621 // detection doesn't needlessly trigger in low and/or variable framerate
622 // scenarios.
623 int target_framerate = std::min(
624 max_framerate_, source_proxy_->GetActiveSinkWants().max_framerate_fps);
625 overuse_detector_->OnTargetFramerateUpdated(target_framerate);
Niels Möller2d061182018-04-24 09:13:08 +0200626
627 ConfigureQualityScaler();
kthelgason2bc68642017-02-07 07:02:22 -0800628}
629
mflodmancc3d4422017-08-03 08:27:51 -0700630void VideoStreamEncoder::ConfigureQualityScaler() {
kthelgason2bc68642017-02-07 07:02:22 -0800631 RTC_DCHECK_RUN_ON(&encoder_queue_);
Niels Möller4db138e2018-04-19 09:04:13 +0200632 const auto scaling_settings = encoder_->GetScalingSettings();
asapersson36e9eb42017-03-31 05:29:12 -0700633 const bool quality_scaling_allowed =
asapersson91914e22017-06-01 00:34:08 -0700634 IsResolutionScalingEnabled(degradation_preference_) &&
Niels Möller225c7872018-02-22 15:03:53 +0100635 scaling_settings.thresholds;
kthelgason3af6cc02017-03-22 00:25:28 -0700636
asapersson36e9eb42017-03-31 05:29:12 -0700637 if (quality_scaling_allowed) {
asapersson09f05612017-05-15 23:40:18 -0700638 if (quality_scaler_.get() == nullptr) {
639 // Quality scaler has not already been configured.
Niels Möller225c7872018-02-22 15:03:53 +0100640
Åsa Perssona945aee2018-04-24 16:53:25 +0200641 // Use experimental thresholds if available.
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200642 absl::optional<VideoEncoder::QpThresholds> experimental_thresholds;
Åsa Perssona945aee2018-04-24 16:53:25 +0200643 if (quality_scaling_experiment_enabled_) {
644 experimental_thresholds = QualityScalingExperiment::GetQpThresholds(
645 encoder_config_.codec_type);
646 }
Karl Wiberg918f50c2018-07-05 11:40:33 +0200647 // Since the interface is non-public, absl::make_unique can't do this
648 // upcast.
Niels Möller225c7872018-02-22 15:03:53 +0100649 AdaptationObserverInterface* observer = this;
Karl Wiberg918f50c2018-07-05 11:40:33 +0200650 quality_scaler_ = absl::make_unique<QualityScaler>(
Åsa Perssona945aee2018-04-24 16:53:25 +0200651 observer, experimental_thresholds ? *experimental_thresholds
652 : *(scaling_settings.thresholds));
Kári Tristan Helgason639602a2018-08-02 10:51:40 +0200653 has_seen_first_significant_bwe_change_ = false;
654 initial_framedrop_ = 0;
kthelgason876222f2016-11-29 01:44:11 -0800655 }
656 } else {
657 quality_scaler_.reset(nullptr);
Kári Tristan Helgason639602a2018-08-02 10:51:40 +0200658 initial_framedrop_ = kMaxInitialFramedrop;
kthelgason876222f2016-11-29 01:44:11 -0800659 }
asapersson09f05612017-05-15 23:40:18 -0700660
Niels Möller213618e2018-07-24 09:29:58 +0200661 encoder_stats_observer_->OnAdaptationChanged(
662 VideoStreamEncoderObserver::AdaptationReason::kNone,
663 GetActiveCounts(kCpu), GetActiveCounts(kQuality));
mflodman@webrtc.org84d17832011-12-01 17:02:23 +0000664}
665
mflodmancc3d4422017-08-03 08:27:51 -0700666void VideoStreamEncoder::OnFrame(const VideoFrame& video_frame) {
perkj26091b12016-09-01 01:17:40 -0700667 RTC_DCHECK_RUNS_SERIALIZED(&incoming_frame_race_checker_);
perkj26091b12016-09-01 01:17:40 -0700668 VideoFrame incoming_frame = video_frame;
669
670 // Local time in webrtc time base.
ilnik04f4d122017-06-19 07:18:55 -0700671 int64_t current_time_us = clock_->TimeInMicroseconds();
672 int64_t current_time_ms = current_time_us / rtc::kNumMicrosecsPerMillisec;
673 // In some cases, e.g., when the frame from decoder is fed to encoder,
674 // the timestamp may be set to the future. As the encoding pipeline assumes
675 // capture time to be less than present time, we should reset the capture
676 // timestamps here. Otherwise there may be issues with RTP send stream.
677 if (incoming_frame.timestamp_us() > current_time_us)
678 incoming_frame.set_timestamp_us(current_time_us);
perkj26091b12016-09-01 01:17:40 -0700679
680 // Capture time may come from clock with an offset and drift from clock_.
681 int64_t capture_ntp_time_ms;
nisse891419f2017-01-12 10:02:22 -0800682 if (video_frame.ntp_time_ms() > 0) {
perkj26091b12016-09-01 01:17:40 -0700683 capture_ntp_time_ms = video_frame.ntp_time_ms();
684 } else if (video_frame.render_time_ms() != 0) {
685 capture_ntp_time_ms = video_frame.render_time_ms() + delta_ntp_internal_ms_;
686 } else {
nisse1c0dea82017-01-30 02:43:18 -0800687 capture_ntp_time_ms = current_time_ms + delta_ntp_internal_ms_;
perkj26091b12016-09-01 01:17:40 -0700688 }
689 incoming_frame.set_ntp_time_ms(capture_ntp_time_ms);
690
691 // Convert NTP time, in ms, to RTP timestamp.
692 const int kMsToRtpTimestamp = 90;
693 incoming_frame.set_timestamp(
694 kMsToRtpTimestamp * static_cast<uint32_t>(incoming_frame.ntp_time_ms()));
695
696 if (incoming_frame.ntp_time_ms() <= last_captured_timestamp_) {
697 // We don't allow the same capture time for two frames, drop this one.
Mirko Bonadei675513b2017-11-09 11:09:25 +0100698 RTC_LOG(LS_WARNING) << "Same/old NTP timestamp ("
699 << incoming_frame.ntp_time_ms()
700 << " <= " << last_captured_timestamp_
701 << ") for incoming frame. Dropping.";
perkj26091b12016-09-01 01:17:40 -0700702 return;
703 }
704
asapersson6ffb67d2016-09-12 00:10:45 -0700705 bool log_stats = false;
nisse1c0dea82017-01-30 02:43:18 -0800706 if (current_time_ms - last_frame_log_ms_ > kFrameLogIntervalMs) {
707 last_frame_log_ms_ = current_time_ms;
asapersson6ffb67d2016-09-12 00:10:45 -0700708 log_stats = true;
709 }
710
perkj26091b12016-09-01 01:17:40 -0700711 last_captured_timestamp_ = incoming_frame.ntp_time_ms();
Sebastian Jansson3ab5c402018-04-05 12:30:50 +0200712
713 int64_t post_time_us = rtc::TimeMicros();
714 ++posted_frames_waiting_for_encode_;
715
716 encoder_queue_.PostTask(
717 [this, incoming_frame, post_time_us, log_stats]() {
718 RTC_DCHECK_RUN_ON(&encoder_queue_);
Niels Möller213618e2018-07-24 09:29:58 +0200719 encoder_stats_observer_->OnIncomingFrame(incoming_frame.width(),
720 incoming_frame.height());
Sebastian Jansson3ab5c402018-04-05 12:30:50 +0200721 ++captured_frame_count_;
722 const int posted_frames_waiting_for_encode =
723 posted_frames_waiting_for_encode_.fetch_sub(1);
724 RTC_DCHECK_GT(posted_frames_waiting_for_encode, 0);
725 if (posted_frames_waiting_for_encode == 1) {
Sebastian Janssona3177052018-04-10 13:05:49 +0200726 MaybeEncodeVideoFrame(incoming_frame, post_time_us);
Sebastian Jansson3ab5c402018-04-05 12:30:50 +0200727 } else {
728 // There is a newer frame in flight. Do not encode this frame.
729 RTC_LOG(LS_VERBOSE)
730 << "Incoming frame dropped due to that the encoder is blocked.";
731 ++dropped_frame_count_;
Niels Möller213618e2018-07-24 09:29:58 +0200732 encoder_stats_observer_->OnFrameDropped(
733 VideoStreamEncoderObserver::DropReason::kEncoderQueue);
Sebastian Jansson3ab5c402018-04-05 12:30:50 +0200734 }
735 if (log_stats) {
736 RTC_LOG(LS_INFO) << "Number of frames: captured "
737 << captured_frame_count_
738 << ", dropped (due to encoder blocked) "
739 << dropped_frame_count_ << ", interval_ms "
740 << kFrameLogIntervalMs;
741 captured_frame_count_ = 0;
742 dropped_frame_count_ = 0;
743 }
744 });
perkj26091b12016-09-01 01:17:40 -0700745}
746
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200747void VideoStreamEncoder::OnDiscardedFrame() {
Niels Möller213618e2018-07-24 09:29:58 +0200748 encoder_stats_observer_->OnFrameDropped(
749 VideoStreamEncoderObserver::DropReason::kSource);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200750}
751
mflodmancc3d4422017-08-03 08:27:51 -0700752bool VideoStreamEncoder::EncoderPaused() const {
perkj26091b12016-09-01 01:17:40 -0700753 RTC_DCHECK_RUN_ON(&encoder_queue_);
pwestin@webrtc.org91563e42013-04-25 22:20:08 +0000754 // Pause video if paused by caller or as long as the network is down or the
755 // pacer queue has grown too large in buffered mode.
perkj57c21f92016-06-17 07:27:16 -0700756 // If the pacer queue has grown too large or the network is down,
perkjfea93092016-05-14 00:58:48 -0700757 // last_observed_bitrate_bps_ will be 0.
perkj26091b12016-09-01 01:17:40 -0700758 return last_observed_bitrate_bps_ == 0;
stefan@webrtc.orgbfacda62013-03-27 16:36:01 +0000759}
760
mflodmancc3d4422017-08-03 08:27:51 -0700761void VideoStreamEncoder::TraceFrameDropStart() {
perkj26091b12016-09-01 01:17:40 -0700762 RTC_DCHECK_RUN_ON(&encoder_queue_);
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000763 // Start trace event only on the first frame after encoder is paused.
764 if (!encoder_paused_and_dropped_frame_) {
765 TRACE_EVENT_ASYNC_BEGIN0("webrtc", "EncoderPaused", this);
766 }
767 encoder_paused_and_dropped_frame_ = true;
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000768}
769
mflodmancc3d4422017-08-03 08:27:51 -0700770void VideoStreamEncoder::TraceFrameDropEnd() {
perkj26091b12016-09-01 01:17:40 -0700771 RTC_DCHECK_RUN_ON(&encoder_queue_);
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000772 // End trace event on first frame after encoder resumes, if frame was dropped.
773 if (encoder_paused_and_dropped_frame_) {
774 TRACE_EVENT_ASYNC_END0("webrtc", "EncoderPaused", this);
775 }
776 encoder_paused_and_dropped_frame_ = false;
777}
778
Sebastian Janssona3177052018-04-10 13:05:49 +0200779void VideoStreamEncoder::MaybeEncodeVideoFrame(const VideoFrame& video_frame,
780 int64_t time_when_posted_us) {
perkj26091b12016-09-01 01:17:40 -0700781 RTC_DCHECK_RUN_ON(&encoder_queue_);
kthelgason876222f2016-11-29 01:44:11 -0800782
perkj26091b12016-09-01 01:17:40 -0700783 if (pre_encode_callback_)
784 pre_encode_callback_->OnFrame(video_frame);
785
Per21d45d22016-10-30 21:37:57 +0100786 if (!last_frame_info_ || video_frame.width() != last_frame_info_->width ||
perkjfa10b552016-10-02 23:45:26 -0700787 video_frame.height() != last_frame_info_->height ||
perkjfa10b552016-10-02 23:45:26 -0700788 video_frame.is_texture() != last_frame_info_->is_texture) {
789 pending_encoder_reconfiguration_ = true;
Oskar Sundbom8e07c132018-01-08 16:45:42 +0100790 last_frame_info_ = VideoFrameInfo(video_frame.width(), video_frame.height(),
791 video_frame.is_texture());
Mirko Bonadei675513b2017-11-09 11:09:25 +0100792 RTC_LOG(LS_INFO) << "Video frame parameters changed: dimensions="
793 << last_frame_info_->width << "x"
794 << last_frame_info_->height
795 << ", texture=" << last_frame_info_->is_texture << ".";
perkjfa10b552016-10-02 23:45:26 -0700796 }
797
Niels Möller4db138e2018-04-19 09:04:13 +0200798 // We have to create then encoder before the frame drop logic,
799 // because the latter depends on encoder_->GetScalingSettings.
800 // According to the testcase
801 // InitialFrameDropOffWhenEncoderDisabledScaling, the return value
802 // from GetScalingSettings should enable or disable the frame drop.
803
804 int64_t now_ms = clock_->TimeInMilliseconds();
805 if (pending_encoder_reconfiguration_) {
806 ReconfigureEncoder();
807 last_parameters_update_ms_.emplace(now_ms);
808 } else if (!last_parameters_update_ms_ ||
809 now_ms - *last_parameters_update_ms_ >=
810 vcm::VCMProcessTimer::kDefaultProcessIntervalMs) {
811 video_sender_.UpdateChannelParameters(rate_allocator_.get(),
812 bitrate_observer_);
813 last_parameters_update_ms_.emplace(now_ms);
814 }
815
Sebastian Janssona3177052018-04-10 13:05:49 +0200816 if (DropDueToSize(video_frame.size())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100817 RTC_LOG(LS_INFO) << "Dropping frame. Too large for target bitrate.";
Åsa Persson875841d2018-01-08 08:49:53 +0100818 int count = GetConstAdaptCounter().ResolutionCount(kQuality);
kthelgason2bc68642017-02-07 07:02:22 -0800819 AdaptDown(kQuality);
Åsa Persson875841d2018-01-08 08:49:53 +0100820 if (GetConstAdaptCounter().ResolutionCount(kQuality) > count) {
Niels Möller213618e2018-07-24 09:29:58 +0200821 encoder_stats_observer_->OnInitialQualityResolutionAdaptDown();
Åsa Persson875841d2018-01-08 08:49:53 +0100822 }
Kári Tristan Helgason639602a2018-08-02 10:51:40 +0200823 ++initial_framedrop_;
Sebastian Jansson0d70e372018-04-17 13:57:13 +0200824 // Storing references to a native buffer risks blocking frame capture.
825 if (video_frame.video_frame_buffer()->type() !=
826 VideoFrameBuffer::Type::kNative) {
827 pending_frame_ = video_frame;
828 pending_frame_post_time_us_ = time_when_posted_us;
829 } else {
830 // Ensure that any previously stored frame is dropped.
831 pending_frame_.reset();
832 }
kthelgason2bc68642017-02-07 07:02:22 -0800833 return;
834 }
Kári Tristan Helgason639602a2018-08-02 10:51:40 +0200835 initial_framedrop_ = kMaxInitialFramedrop;
kthelgason2bc68642017-02-07 07:02:22 -0800836
perkj26091b12016-09-01 01:17:40 -0700837 if (EncoderPaused()) {
Sebastian Jansson0d70e372018-04-17 13:57:13 +0200838 // Storing references to a native buffer risks blocking frame capture.
839 if (video_frame.video_frame_buffer()->type() !=
840 VideoFrameBuffer::Type::kNative) {
841 if (pending_frame_)
842 TraceFrameDropStart();
843 pending_frame_ = video_frame;
844 pending_frame_post_time_us_ = time_when_posted_us;
845 } else {
846 // Ensure that any previously stored frame is dropped.
847 pending_frame_.reset();
Sebastian Janssona3177052018-04-10 13:05:49 +0200848 TraceFrameDropStart();
Sebastian Jansson0d70e372018-04-17 13:57:13 +0200849 }
perkj26091b12016-09-01 01:17:40 -0700850 return;
mflodman@webrtc.org84d17832011-12-01 17:02:23 +0000851 }
Sebastian Janssona3177052018-04-10 13:05:49 +0200852
853 pending_frame_.reset();
854 EncodeVideoFrame(video_frame, time_when_posted_us);
855}
856
857void VideoStreamEncoder::EncodeVideoFrame(const VideoFrame& video_frame,
858 int64_t time_when_posted_us) {
859 RTC_DCHECK_RUN_ON(&encoder_queue_);
perkj26091b12016-09-01 01:17:40 -0700860 TraceFrameDropEnd();
niklase@google.com470e71d2011-07-07 08:21:25 +0000861
ilnik6b826ef2017-06-16 06:53:48 -0700862 VideoFrame out_frame(video_frame);
863 // Crop frame if needed.
864 if (crop_width_ > 0 || crop_height_ > 0) {
865 int cropped_width = video_frame.width() - crop_width_;
866 int cropped_height = video_frame.height() - crop_height_;
867 rtc::scoped_refptr<I420Buffer> cropped_buffer =
868 I420Buffer::Create(cropped_width, cropped_height);
869 // TODO(ilnik): Remove scaling if cropping is too big, as it should never
870 // happen after SinkWants signaled correctly from ReconfigureEncoder.
871 if (crop_width_ < 4 && crop_height_ < 4) {
872 cropped_buffer->CropAndScaleFrom(
873 *video_frame.video_frame_buffer()->ToI420(), crop_width_ / 2,
874 crop_height_ / 2, cropped_width, cropped_height);
875 } else {
876 cropped_buffer->ScaleFrom(
877 *video_frame.video_frame_buffer()->ToI420().get());
878 }
879 out_frame =
880 VideoFrame(cropped_buffer, video_frame.timestamp(),
881 video_frame.render_time_ms(), video_frame.rotation());
882 out_frame.set_ntp_time_ms(video_frame.ntp_time_ms());
883 }
884
Magnus Jedvert26679d62015-04-07 14:07:41 +0200885 TRACE_EVENT_ASYNC_STEP0("webrtc", "Video", video_frame.render_time_ms(),
hclam@chromium.org1a7b9b92013-07-08 21:31:18 +0000886 "Encode");
pbos@webrtc.orgfe1ef932013-10-21 10:34:43 +0000887
Niels Möller7dc26b72017-12-06 10:27:48 +0100888 overuse_detector_->FrameCaptured(out_frame, time_when_posted_us);
perkjd52063f2016-09-07 06:32:18 -0700889
ilnik6b826ef2017-06-16 06:53:48 -0700890 video_sender_.AddVideoFrame(out_frame, nullptr);
niklase@google.com470e71d2011-07-07 08:21:25 +0000891}
niklase@google.com470e71d2011-07-07 08:21:25 +0000892
mflodmancc3d4422017-08-03 08:27:51 -0700893void VideoStreamEncoder::SendKeyFrame() {
perkj26091b12016-09-01 01:17:40 -0700894 if (!encoder_queue_.IsCurrent()) {
895 encoder_queue_.PostTask([this] { SendKeyFrame(); });
896 return;
897 }
898 RTC_DCHECK_RUN_ON(&encoder_queue_);
Niels Möller1c9aa1e2018-02-16 10:27:23 +0100899 TRACE_EVENT0("webrtc", "OnKeyFrameRequest");
Peter Boströmcd5c25c2016-04-21 16:48:08 +0200900 video_sender_.IntraFrameRequest(0);
stefan@webrtc.org07b45a52012-02-02 08:37:48 +0000901}
902
mflodmancc3d4422017-08-03 08:27:51 -0700903EncodedImageCallback::Result VideoStreamEncoder::OnEncodedImage(
Sergey Ulanov525df3f2016-08-02 17:46:41 -0700904 const EncodedImage& encoded_image,
905 const CodecSpecificInfo* codec_specific_info,
906 const RTPFragmentationHeader* fragmentation) {
perkj26091b12016-09-01 01:17:40 -0700907 // Encoded is called on whatever thread the real encoder implementation run
908 // on. In the case of hardware encoders, there might be several encoders
909 // running in parallel on different threads.
Niels Möller213618e2018-07-24 09:29:58 +0200910 encoder_stats_observer_->OnSendEncodedImage(encoded_image,
911 codec_specific_info);
sprang3911c262016-04-15 01:24:14 -0700912
Sergey Ulanov525df3f2016-08-02 17:46:41 -0700913 EncodedImageCallback::Result result =
914 sink_->OnEncodedImage(encoded_image, codec_specific_info, fragmentation);
perkjbc75d972016-05-02 06:31:25 -0700915
Niels Möller7dc26b72017-12-06 10:27:48 +0100916 int64_t time_sent_us = rtc::TimeMicros();
Niels Möller23775882018-08-16 10:24:12 +0200917 uint32_t timestamp = encoded_image.Timestamp();
kthelgason876222f2016-11-29 01:44:11 -0800918 const int qp = encoded_image.qp_;
Niels Möller83dbeac2017-12-14 16:39:44 +0100919 int64_t capture_time_us =
920 encoded_image.capture_time_ms_ * rtc::kNumMicrosecsPerMillisec;
921
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200922 absl::optional<int> encode_duration_us;
Ilya Nikolaevskiyb6c462d2018-06-05 15:21:32 +0200923 if (encoded_image.timing_.flags != VideoSendTiming::kInvalid) {
Niels Möller83dbeac2017-12-14 16:39:44 +0100924 encode_duration_us.emplace(
925 // TODO(nisse): Maybe use capture_time_ms_ rather than encode_start_ms_?
926 rtc::kNumMicrosecsPerMillisec *
927 (encoded_image.timing_.encode_finish_ms -
928 encoded_image.timing_.encode_start_ms));
929 }
930
931 encoder_queue_.PostTask(
932 [this, timestamp, time_sent_us, qp, capture_time_us, encode_duration_us] {
933 RTC_DCHECK_RUN_ON(&encoder_queue_);
934 overuse_detector_->FrameSent(timestamp, time_sent_us, capture_time_us,
935 encode_duration_us);
936 if (quality_scaler_ && qp >= 0)
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200937 quality_scaler_->ReportQp(qp);
Niels Möller83dbeac2017-12-14 16:39:44 +0100938 });
perkj803d97f2016-11-01 11:45:46 -0700939
Sergey Ulanov525df3f2016-08-02 17:46:41 -0700940 return result;
Peter Boströmb7d9a972015-12-18 16:01:11 +0100941}
942
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200943void VideoStreamEncoder::OnDroppedFrame(DropReason reason) {
944 switch (reason) {
945 case DropReason::kDroppedByMediaOptimizations:
Niels Möller213618e2018-07-24 09:29:58 +0200946 encoder_stats_observer_->OnFrameDropped(
947 VideoStreamEncoderObserver::DropReason::kMediaOptimization);
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200948 encoder_queue_.PostTask([this] {
949 RTC_DCHECK_RUN_ON(&encoder_queue_);
950 if (quality_scaler_)
Åsa Perssona945aee2018-04-24 16:53:25 +0200951 quality_scaler_->ReportDroppedFrameByMediaOpt();
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200952 });
953 break;
954 case DropReason::kDroppedByEncoder:
Niels Möller213618e2018-07-24 09:29:58 +0200955 encoder_stats_observer_->OnFrameDropped(
956 VideoStreamEncoderObserver::DropReason::kEncoder);
Åsa Perssona945aee2018-04-24 16:53:25 +0200957 encoder_queue_.PostTask([this] {
958 RTC_DCHECK_RUN_ON(&encoder_queue_);
959 if (quality_scaler_)
960 quality_scaler_->ReportDroppedFrameByEncoder();
961 });
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200962 break;
963 }
kthelgason876222f2016-11-29 01:44:11 -0800964}
965
mflodmancc3d4422017-08-03 08:27:51 -0700966void VideoStreamEncoder::OnBitrateUpdated(uint32_t bitrate_bps,
967 uint8_t fraction_lost,
968 int64_t round_trip_time_ms) {
perkj26091b12016-09-01 01:17:40 -0700969 if (!encoder_queue_.IsCurrent()) {
970 encoder_queue_.PostTask(
971 [this, bitrate_bps, fraction_lost, round_trip_time_ms] {
972 OnBitrateUpdated(bitrate_bps, fraction_lost, round_trip_time_ms);
973 });
974 return;
975 }
976 RTC_DCHECK_RUN_ON(&encoder_queue_);
977 RTC_DCHECK(sink_) << "sink_ must be set before the encoder is active.";
978
Mirko Bonadei675513b2017-11-09 11:09:25 +0100979 RTC_LOG(LS_VERBOSE) << "OnBitrateUpdated, bitrate " << bitrate_bps
980 << " packet loss " << static_cast<int>(fraction_lost)
981 << " rtt " << round_trip_time_ms;
Kári Tristan Helgason639602a2018-08-02 10:51:40 +0200982 // On significant changes to BWE at the start of the call,
983 // enable frame drops to quickly react to jumps in available bandwidth.
984 if (encoder_start_bitrate_bps_ != 0 &&
985 !has_seen_first_significant_bwe_change_ && quality_scaler_ &&
986 initial_framedrop_on_bwe_enabled_ &&
987 abs_diff(bitrate_bps, encoder_start_bitrate_bps_) >=
988 kFramedropThreshold * encoder_start_bitrate_bps_) {
989 // Reset initial framedrop feature when first real BW estimate arrives.
990 // TODO(kthelgason): Update BitrateAllocator to not call OnBitrateUpdated
991 // without an actual BW estimate.
992 initial_framedrop_ = 0;
993 has_seen_first_significant_bwe_change_ = true;
994 }
perkj26091b12016-09-01 01:17:40 -0700995
Peter Boströmcd5c25c2016-04-21 16:48:08 +0200996 video_sender_.SetChannelParameters(bitrate_bps, fraction_lost,
sprang1a646ee2016-12-01 06:34:11 -0800997 round_trip_time_ms, rate_allocator_.get(),
998 bitrate_observer_);
perkj26091b12016-09-01 01:17:40 -0700999
1000 encoder_start_bitrate_bps_ =
1001 bitrate_bps != 0 ? bitrate_bps : encoder_start_bitrate_bps_;
mflodman101f2502016-06-09 17:21:19 +02001002 bool video_is_suspended = bitrate_bps == 0;
Erik Språng08127a92016-11-16 16:41:30 +01001003 bool video_suspension_changed = video_is_suspended != EncoderPaused();
perkj26091b12016-09-01 01:17:40 -07001004 last_observed_bitrate_bps_ = bitrate_bps;
Peter Boströmd153a372015-11-10 15:27:12 +00001005
sprang552c7c72017-02-13 04:41:45 -08001006 if (video_suspension_changed) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001007 RTC_LOG(LS_INFO) << "Video suspend state changed to: "
1008 << (video_is_suspended ? "suspended" : "not suspended");
Niels Möller213618e2018-07-24 09:29:58 +02001009 encoder_stats_observer_->OnSuspendChange(video_is_suspended);
mflodman101f2502016-06-09 17:21:19 +02001010 }
Sebastian Janssona3177052018-04-10 13:05:49 +02001011 if (video_suspension_changed && !video_is_suspended && pending_frame_ &&
1012 !DropDueToSize(pending_frame_->size())) {
1013 int64_t pending_time_us = rtc::TimeMicros() - pending_frame_post_time_us_;
1014 if (pending_time_us < kPendingFrameTimeoutMs * 1000)
1015 EncodeVideoFrame(*pending_frame_, pending_frame_post_time_us_);
1016 pending_frame_.reset();
1017 }
1018}
1019
1020bool VideoStreamEncoder::DropDueToSize(uint32_t pixel_count) const {
Kári Tristan Helgason639602a2018-08-02 10:51:40 +02001021 if (initial_framedrop_ < kMaxInitialFramedrop &&
Sebastian Janssona3177052018-04-10 13:05:49 +02001022 encoder_start_bitrate_bps_ > 0) {
1023 if (encoder_start_bitrate_bps_ < 300000 /* qvga */) {
1024 return pixel_count > 320 * 240;
1025 } else if (encoder_start_bitrate_bps_ < 500000 /* vga */) {
1026 return pixel_count > 640 * 480;
1027 }
1028 }
1029 return false;
niklase@google.com470e71d2011-07-07 08:21:25 +00001030}
1031
mflodmancc3d4422017-08-03 08:27:51 -07001032void VideoStreamEncoder::AdaptDown(AdaptReason reason) {
perkjd52063f2016-09-07 06:32:18 -07001033 RTC_DCHECK_RUN_ON(&encoder_queue_);
sprangc5d62e22017-04-02 23:53:04 -07001034 AdaptationRequest adaptation_request = {
1035 last_frame_info_->pixel_count(),
Niels Möller213618e2018-07-24 09:29:58 +02001036 encoder_stats_observer_->GetInputFrameRate(),
sprangc5d62e22017-04-02 23:53:04 -07001037 AdaptationRequest::Mode::kAdaptDown};
asapersson09f05612017-05-15 23:40:18 -07001038
sprangc5d62e22017-04-02 23:53:04 -07001039 bool downgrade_requested =
1040 last_adaptation_request_ &&
1041 last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptDown;
1042
sprangc5d62e22017-04-02 23:53:04 -07001043 switch (degradation_preference_) {
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001044 case DegradationPreference::BALANCED:
asaperssonf7e294d2017-06-13 23:25:22 -07001045 break;
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001046 case DegradationPreference::MAINTAIN_FRAMERATE:
sprangc5d62e22017-04-02 23:53:04 -07001047 if (downgrade_requested &&
1048 adaptation_request.input_pixel_count_ >=
1049 last_adaptation_request_->input_pixel_count_) {
1050 // Don't request lower resolution if the current resolution is not
1051 // lower than the last time we asked for the resolution to be lowered.
1052 return;
1053 }
1054 break;
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001055 case DegradationPreference::MAINTAIN_RESOLUTION:
sprangc5d62e22017-04-02 23:53:04 -07001056 if (adaptation_request.framerate_fps_ <= 0 ||
1057 (downgrade_requested &&
1058 adaptation_request.framerate_fps_ < kMinFramerateFps)) {
1059 // If no input fps estimate available, can't determine how to scale down
1060 // framerate. Otherwise, don't request lower framerate if we don't have
1061 // a valid frame rate. Since framerate, unlike resolution, is a measure
1062 // we have to estimate, and can fluctuate naturally over time, don't
1063 // make the same kind of limitations as for resolution, but trust the
1064 // overuse detector to not trigger too often.
1065 return;
1066 }
1067 break;
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001068 case DegradationPreference::DISABLED:
sprangc5d62e22017-04-02 23:53:04 -07001069 return;
sprang84a37592017-02-10 07:04:27 -08001070 }
sprangc5d62e22017-04-02 23:53:04 -07001071
sprangc5d62e22017-04-02 23:53:04 -07001072 switch (degradation_preference_) {
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001073 case DegradationPreference::BALANCED: {
asaperssonf7e294d2017-06-13 23:25:22 -07001074 // Try scale down framerate, if lower.
1075 int fps = MinFps(last_frame_info_->pixel_count());
1076 if (source_proxy_->RestrictFramerate(fps)) {
1077 GetAdaptCounter().IncrementFramerate(reason);
1078 break;
1079 }
1080 // Scale down resolution.
Karl Wiberg80ba3332018-02-05 10:33:35 +01001081 RTC_FALLTHROUGH();
asaperssonf7e294d2017-06-13 23:25:22 -07001082 }
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001083 case DegradationPreference::MAINTAIN_FRAMERATE: {
asapersson13874762017-06-07 00:01:02 -07001084 // Scale down resolution.
Åsa Perssonc3ed6302017-11-16 14:04:52 +01001085 bool min_pixels_reached = false;
asaperssond0de2952017-04-21 01:47:31 -07001086 if (!source_proxy_->RequestResolutionLowerThan(
asapersson142fcc92017-08-17 08:58:54 -07001087 adaptation_request.input_pixel_count_,
Niels Möller4db138e2018-04-19 09:04:13 +02001088 encoder_->GetScalingSettings().min_pixels_per_frame,
Åsa Perssonc3ed6302017-11-16 14:04:52 +01001089 &min_pixels_reached)) {
1090 if (min_pixels_reached)
Niels Möller213618e2018-07-24 09:29:58 +02001091 encoder_stats_observer_->OnMinPixelLimitReached();
asaperssond0de2952017-04-21 01:47:31 -07001092 return;
1093 }
asaperssonf7e294d2017-06-13 23:25:22 -07001094 GetAdaptCounter().IncrementResolution(reason);
sprangc5d62e22017-04-02 23:53:04 -07001095 break;
Åsa Perssonc3ed6302017-11-16 14:04:52 +01001096 }
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001097 case DegradationPreference::MAINTAIN_RESOLUTION: {
asapersson13874762017-06-07 00:01:02 -07001098 // Scale down framerate.
sprangfda496a2017-06-15 04:21:07 -07001099 const int requested_framerate = source_proxy_->RequestFramerateLowerThan(
1100 adaptation_request.framerate_fps_);
1101 if (requested_framerate == -1)
asapersson13874762017-06-07 00:01:02 -07001102 return;
sprangfda496a2017-06-15 04:21:07 -07001103 RTC_DCHECK_NE(max_framerate_, -1);
Niels Möller7dc26b72017-12-06 10:27:48 +01001104 overuse_detector_->OnTargetFramerateUpdated(
1105 std::min(max_framerate_, requested_framerate));
asaperssonf7e294d2017-06-13 23:25:22 -07001106 GetAdaptCounter().IncrementFramerate(reason);
sprangc5d62e22017-04-02 23:53:04 -07001107 break;
sprangfda496a2017-06-15 04:21:07 -07001108 }
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001109 case DegradationPreference::DISABLED:
sprangc5d62e22017-04-02 23:53:04 -07001110 RTC_NOTREACHED();
1111 }
1112
asaperssond0de2952017-04-21 01:47:31 -07001113 last_adaptation_request_.emplace(adaptation_request);
1114
asapersson09f05612017-05-15 23:40:18 -07001115 UpdateAdaptationStats(reason);
asaperssond0de2952017-04-21 01:47:31 -07001116
Mirko Bonadei675513b2017-11-09 11:09:25 +01001117 RTC_LOG(LS_INFO) << GetConstAdaptCounter().ToString();
perkj26091b12016-09-01 01:17:40 -07001118}
1119
mflodmancc3d4422017-08-03 08:27:51 -07001120void VideoStreamEncoder::AdaptUp(AdaptReason reason) {
perkjd52063f2016-09-07 06:32:18 -07001121 RTC_DCHECK_RUN_ON(&encoder_queue_);
asapersson09f05612017-05-15 23:40:18 -07001122
1123 const AdaptCounter& adapt_counter = GetConstAdaptCounter();
1124 int num_downgrades = adapt_counter.TotalCount(reason);
1125 if (num_downgrades == 0)
perkj803d97f2016-11-01 11:45:46 -07001126 return;
asapersson09f05612017-05-15 23:40:18 -07001127 RTC_DCHECK_GT(num_downgrades, 0);
1128
sprangc5d62e22017-04-02 23:53:04 -07001129 AdaptationRequest adaptation_request = {
1130 last_frame_info_->pixel_count(),
Niels Möller213618e2018-07-24 09:29:58 +02001131 encoder_stats_observer_->GetInputFrameRate(),
sprangc5d62e22017-04-02 23:53:04 -07001132 AdaptationRequest::Mode::kAdaptUp};
1133
1134 bool adapt_up_requested =
1135 last_adaptation_request_ &&
1136 last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptUp;
asapersson09f05612017-05-15 23:40:18 -07001137
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001138 if (degradation_preference_ == DegradationPreference::MAINTAIN_FRAMERATE) {
asaperssonf7e294d2017-06-13 23:25:22 -07001139 if (adapt_up_requested &&
1140 adaptation_request.input_pixel_count_ <=
1141 last_adaptation_request_->input_pixel_count_) {
1142 // Don't request higher resolution if the current resolution is not
1143 // higher than the last time we asked for the resolution to be higher.
sprangc5d62e22017-04-02 23:53:04 -07001144 return;
asaperssonf7e294d2017-06-13 23:25:22 -07001145 }
sprangb1ca0732017-02-01 08:38:12 -08001146 }
sprangc5d62e22017-04-02 23:53:04 -07001147
sprangc5d62e22017-04-02 23:53:04 -07001148 switch (degradation_preference_) {
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001149 case DegradationPreference::BALANCED: {
asaperssonf7e294d2017-06-13 23:25:22 -07001150 // Try scale up framerate, if higher.
1151 int fps = MaxFps(last_frame_info_->pixel_count());
1152 if (source_proxy_->IncreaseFramerate(fps)) {
1153 GetAdaptCounter().DecrementFramerate(reason, fps);
1154 // Reset framerate in case of fewer fps steps down than up.
1155 if (adapt_counter.FramerateCount() == 0 &&
1156 fps != std::numeric_limits<int>::max()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001157 RTC_LOG(LS_INFO) << "Removing framerate down-scaling setting.";
asaperssonf7e294d2017-06-13 23:25:22 -07001158 source_proxy_->IncreaseFramerate(std::numeric_limits<int>::max());
1159 }
1160 break;
1161 }
1162 // Scale up resolution.
Karl Wiberg80ba3332018-02-05 10:33:35 +01001163 RTC_FALLTHROUGH();
asaperssonf7e294d2017-06-13 23:25:22 -07001164 }
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001165 case DegradationPreference::MAINTAIN_FRAMERATE: {
asapersson13874762017-06-07 00:01:02 -07001166 // Scale up resolution.
1167 int pixel_count = adaptation_request.input_pixel_count_;
1168 if (adapt_counter.ResolutionCount() == 1) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001169 RTC_LOG(LS_INFO) << "Removing resolution down-scaling setting.";
asapersson13874762017-06-07 00:01:02 -07001170 pixel_count = std::numeric_limits<int>::max();
sprangc5d62e22017-04-02 23:53:04 -07001171 }
asapersson13874762017-06-07 00:01:02 -07001172 if (!source_proxy_->RequestHigherResolutionThan(pixel_count))
1173 return;
asaperssonf7e294d2017-06-13 23:25:22 -07001174 GetAdaptCounter().DecrementResolution(reason);
sprangc5d62e22017-04-02 23:53:04 -07001175 break;
asapersson13874762017-06-07 00:01:02 -07001176 }
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001177 case DegradationPreference::MAINTAIN_RESOLUTION: {
asapersson13874762017-06-07 00:01:02 -07001178 // Scale up framerate.
1179 int fps = adaptation_request.framerate_fps_;
1180 if (adapt_counter.FramerateCount() == 1) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001181 RTC_LOG(LS_INFO) << "Removing framerate down-scaling setting.";
asapersson13874762017-06-07 00:01:02 -07001182 fps = std::numeric_limits<int>::max();
sprangc5d62e22017-04-02 23:53:04 -07001183 }
sprangfda496a2017-06-15 04:21:07 -07001184
1185 const int requested_framerate =
1186 source_proxy_->RequestHigherFramerateThan(fps);
1187 if (requested_framerate == -1) {
Niels Möller7dc26b72017-12-06 10:27:48 +01001188 overuse_detector_->OnTargetFramerateUpdated(max_framerate_);
asapersson13874762017-06-07 00:01:02 -07001189 return;
sprangfda496a2017-06-15 04:21:07 -07001190 }
Niels Möller7dc26b72017-12-06 10:27:48 +01001191 overuse_detector_->OnTargetFramerateUpdated(
1192 std::min(max_framerate_, requested_framerate));
asaperssonf7e294d2017-06-13 23:25:22 -07001193 GetAdaptCounter().DecrementFramerate(reason);
sprangc5d62e22017-04-02 23:53:04 -07001194 break;
asapersson13874762017-06-07 00:01:02 -07001195 }
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001196 case DegradationPreference::DISABLED:
asaperssonf7e294d2017-06-13 23:25:22 -07001197 return;
sprangc5d62e22017-04-02 23:53:04 -07001198 }
1199
asaperssond0de2952017-04-21 01:47:31 -07001200 last_adaptation_request_.emplace(adaptation_request);
1201
asapersson09f05612017-05-15 23:40:18 -07001202 UpdateAdaptationStats(reason);
1203
Mirko Bonadei675513b2017-11-09 11:09:25 +01001204 RTC_LOG(LS_INFO) << adapt_counter.ToString();
asapersson09f05612017-05-15 23:40:18 -07001205}
1206
Niels Möller213618e2018-07-24 09:29:58 +02001207// TODO(nisse): Delete, once AdaptReason and AdaptationReason are merged.
mflodmancc3d4422017-08-03 08:27:51 -07001208void VideoStreamEncoder::UpdateAdaptationStats(AdaptReason reason) {
asaperssond0de2952017-04-21 01:47:31 -07001209 switch (reason) {
asaperssond0de2952017-04-21 01:47:31 -07001210 case kCpu:
Niels Möller213618e2018-07-24 09:29:58 +02001211 encoder_stats_observer_->OnAdaptationChanged(
1212 VideoStreamEncoderObserver::AdaptationReason::kCpu,
1213 GetActiveCounts(kCpu), GetActiveCounts(kQuality));
asapersson09f05612017-05-15 23:40:18 -07001214 break;
1215 case kQuality:
Niels Möller213618e2018-07-24 09:29:58 +02001216 encoder_stats_observer_->OnAdaptationChanged(
1217 VideoStreamEncoderObserver::AdaptationReason::kQuality,
1218 GetActiveCounts(kCpu), GetActiveCounts(kQuality));
asaperssond0de2952017-04-21 01:47:31 -07001219 break;
1220 }
perkj26091b12016-09-01 01:17:40 -07001221}
1222
Niels Möller213618e2018-07-24 09:29:58 +02001223VideoStreamEncoderObserver::AdaptationSteps VideoStreamEncoder::GetActiveCounts(
mflodmancc3d4422017-08-03 08:27:51 -07001224 AdaptReason reason) {
Niels Möller213618e2018-07-24 09:29:58 +02001225 VideoStreamEncoderObserver::AdaptationSteps counts =
mflodmancc3d4422017-08-03 08:27:51 -07001226 GetConstAdaptCounter().Counts(reason);
asapersson09f05612017-05-15 23:40:18 -07001227 switch (reason) {
1228 case kCpu:
1229 if (!IsFramerateScalingEnabled(degradation_preference_))
Niels Möller213618e2018-07-24 09:29:58 +02001230 counts.num_framerate_reductions = absl::nullopt;
asapersson09f05612017-05-15 23:40:18 -07001231 if (!IsResolutionScalingEnabled(degradation_preference_))
Niels Möller213618e2018-07-24 09:29:58 +02001232 counts.num_resolution_reductions = absl::nullopt;
asapersson09f05612017-05-15 23:40:18 -07001233 break;
1234 case kQuality:
1235 if (!IsFramerateScalingEnabled(degradation_preference_) ||
1236 !quality_scaler_) {
Niels Möller213618e2018-07-24 09:29:58 +02001237 counts.num_framerate_reductions = absl::nullopt;
asapersson09f05612017-05-15 23:40:18 -07001238 }
1239 if (!IsResolutionScalingEnabled(degradation_preference_) ||
1240 !quality_scaler_) {
Niels Möller213618e2018-07-24 09:29:58 +02001241 counts.num_resolution_reductions = absl::nullopt;
asapersson09f05612017-05-15 23:40:18 -07001242 }
1243 break;
sprangc5d62e22017-04-02 23:53:04 -07001244 }
asapersson09f05612017-05-15 23:40:18 -07001245 return counts;
sprangc5d62e22017-04-02 23:53:04 -07001246}
1247
mflodmancc3d4422017-08-03 08:27:51 -07001248VideoStreamEncoder::AdaptCounter& VideoStreamEncoder::GetAdaptCounter() {
asapersson09f05612017-05-15 23:40:18 -07001249 return adapt_counters_[degradation_preference_];
1250}
1251
mflodmancc3d4422017-08-03 08:27:51 -07001252const VideoStreamEncoder::AdaptCounter&
1253VideoStreamEncoder::GetConstAdaptCounter() {
asapersson09f05612017-05-15 23:40:18 -07001254 return adapt_counters_[degradation_preference_];
1255}
1256
1257// Class holding adaptation information.
mflodmancc3d4422017-08-03 08:27:51 -07001258VideoStreamEncoder::AdaptCounter::AdaptCounter() {
asapersson09f05612017-05-15 23:40:18 -07001259 fps_counters_.resize(kScaleReasonSize);
1260 resolution_counters_.resize(kScaleReasonSize);
asaperssonf7e294d2017-06-13 23:25:22 -07001261 static_assert(kScaleReasonSize == 2, "Update MoveCount.");
asapersson09f05612017-05-15 23:40:18 -07001262}
1263
mflodmancc3d4422017-08-03 08:27:51 -07001264VideoStreamEncoder::AdaptCounter::~AdaptCounter() {}
asapersson09f05612017-05-15 23:40:18 -07001265
mflodmancc3d4422017-08-03 08:27:51 -07001266std::string VideoStreamEncoder::AdaptCounter::ToString() const {
Jonas Olsson366a50c2018-09-06 13:41:30 +02001267 rtc::StringBuilder ss;
asapersson09f05612017-05-15 23:40:18 -07001268 ss << "Downgrade counts: fps: {" << ToString(fps_counters_);
1269 ss << "}, resolution: {" << ToString(resolution_counters_) << "}";
Jonas Olsson84df1c72018-09-14 16:59:32 +02001270 return ss.Release();
asapersson09f05612017-05-15 23:40:18 -07001271}
1272
Niels Möller213618e2018-07-24 09:29:58 +02001273VideoStreamEncoderObserver::AdaptationSteps
1274VideoStreamEncoder::AdaptCounter::Counts(int reason) const {
1275 VideoStreamEncoderObserver::AdaptationSteps counts;
1276 counts.num_framerate_reductions = fps_counters_[reason];
1277 counts.num_resolution_reductions = resolution_counters_[reason];
asapersson09f05612017-05-15 23:40:18 -07001278 return counts;
1279}
1280
mflodmancc3d4422017-08-03 08:27:51 -07001281void VideoStreamEncoder::AdaptCounter::IncrementFramerate(int reason) {
asaperssonf7e294d2017-06-13 23:25:22 -07001282 ++(fps_counters_[reason]);
asapersson09f05612017-05-15 23:40:18 -07001283}
1284
mflodmancc3d4422017-08-03 08:27:51 -07001285void VideoStreamEncoder::AdaptCounter::IncrementResolution(int reason) {
asaperssonf7e294d2017-06-13 23:25:22 -07001286 ++(resolution_counters_[reason]);
1287}
1288
mflodmancc3d4422017-08-03 08:27:51 -07001289void VideoStreamEncoder::AdaptCounter::DecrementFramerate(int reason) {
asaperssonf7e294d2017-06-13 23:25:22 -07001290 if (fps_counters_[reason] == 0) {
1291 // Balanced mode: Adapt up is in a different order, switch reason.
1292 // E.g. framerate adapt down: quality (2), framerate adapt up: cpu (3).
1293 // 1. Down resolution (cpu): res={quality:0,cpu:1}, fps={quality:0,cpu:0}
1294 // 2. Down fps (quality): res={quality:0,cpu:1}, fps={quality:1,cpu:0}
1295 // 3. Up fps (cpu): res={quality:1,cpu:0}, fps={quality:0,cpu:0}
1296 // 4. Up resolution (quality): res={quality:0,cpu:0}, fps={quality:0,cpu:0}
1297 RTC_DCHECK_GT(TotalCount(reason), 0) << "No downgrade for reason.";
1298 RTC_DCHECK_GT(FramerateCount(), 0) << "Framerate not downgraded.";
1299 MoveCount(&resolution_counters_, reason);
1300 MoveCount(&fps_counters_, (reason + 1) % kScaleReasonSize);
1301 }
1302 --(fps_counters_[reason]);
1303 RTC_DCHECK_GE(fps_counters_[reason], 0);
1304}
1305
mflodmancc3d4422017-08-03 08:27:51 -07001306void VideoStreamEncoder::AdaptCounter::DecrementResolution(int reason) {
asaperssonf7e294d2017-06-13 23:25:22 -07001307 if (resolution_counters_[reason] == 0) {
1308 // Balanced mode: Adapt up is in a different order, switch reason.
1309 RTC_DCHECK_GT(TotalCount(reason), 0) << "No downgrade for reason.";
1310 RTC_DCHECK_GT(ResolutionCount(), 0) << "Resolution not downgraded.";
1311 MoveCount(&fps_counters_, reason);
1312 MoveCount(&resolution_counters_, (reason + 1) % kScaleReasonSize);
1313 }
1314 --(resolution_counters_[reason]);
1315 RTC_DCHECK_GE(resolution_counters_[reason], 0);
1316}
1317
mflodmancc3d4422017-08-03 08:27:51 -07001318void VideoStreamEncoder::AdaptCounter::DecrementFramerate(int reason,
1319 int cur_fps) {
asaperssonf7e294d2017-06-13 23:25:22 -07001320 DecrementFramerate(reason);
1321 // Reset if at max fps (i.e. in case of fewer steps up than down).
1322 if (cur_fps == std::numeric_limits<int>::max())
1323 std::fill(fps_counters_.begin(), fps_counters_.end(), 0);
asapersson09f05612017-05-15 23:40:18 -07001324}
1325
mflodmancc3d4422017-08-03 08:27:51 -07001326int VideoStreamEncoder::AdaptCounter::FramerateCount() const {
asapersson09f05612017-05-15 23:40:18 -07001327 return Count(fps_counters_);
1328}
1329
mflodmancc3d4422017-08-03 08:27:51 -07001330int VideoStreamEncoder::AdaptCounter::ResolutionCount() const {
asapersson09f05612017-05-15 23:40:18 -07001331 return Count(resolution_counters_);
1332}
1333
mflodmancc3d4422017-08-03 08:27:51 -07001334int VideoStreamEncoder::AdaptCounter::FramerateCount(int reason) const {
asapersson09f05612017-05-15 23:40:18 -07001335 return fps_counters_[reason];
1336}
1337
mflodmancc3d4422017-08-03 08:27:51 -07001338int VideoStreamEncoder::AdaptCounter::ResolutionCount(int reason) const {
asapersson09f05612017-05-15 23:40:18 -07001339 return resolution_counters_[reason];
1340}
1341
mflodmancc3d4422017-08-03 08:27:51 -07001342int VideoStreamEncoder::AdaptCounter::TotalCount(int reason) const {
asapersson09f05612017-05-15 23:40:18 -07001343 return FramerateCount(reason) + ResolutionCount(reason);
1344}
1345
mflodmancc3d4422017-08-03 08:27:51 -07001346int VideoStreamEncoder::AdaptCounter::Count(
1347 const std::vector<int>& counters) const {
asapersson09f05612017-05-15 23:40:18 -07001348 return std::accumulate(counters.begin(), counters.end(), 0);
1349}
1350
mflodmancc3d4422017-08-03 08:27:51 -07001351void VideoStreamEncoder::AdaptCounter::MoveCount(std::vector<int>* counters,
1352 int from_reason) {
asaperssonf7e294d2017-06-13 23:25:22 -07001353 int to_reason = (from_reason + 1) % kScaleReasonSize;
1354 ++((*counters)[to_reason]);
1355 --((*counters)[from_reason]);
1356}
1357
mflodmancc3d4422017-08-03 08:27:51 -07001358std::string VideoStreamEncoder::AdaptCounter::ToString(
asapersson09f05612017-05-15 23:40:18 -07001359 const std::vector<int>& counters) const {
Jonas Olsson366a50c2018-09-06 13:41:30 +02001360 rtc::StringBuilder ss;
asapersson09f05612017-05-15 23:40:18 -07001361 for (size_t reason = 0; reason < kScaleReasonSize; ++reason) {
1362 ss << (reason ? " cpu" : "quality") << ":" << counters[reason];
sprangc5d62e22017-04-02 23:53:04 -07001363 }
Jonas Olsson84df1c72018-09-14 16:59:32 +02001364 return ss.Release();
sprangc5d62e22017-04-02 23:53:04 -07001365}
1366
mflodman@webrtc.org84d17832011-12-01 17:02:23 +00001367} // namespace webrtc