1 /*
2 * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
3 *
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
11 #include "video/frame_cadence_adapter.h"
12
13 #include <atomic>
14 #include <deque>
15 #include <memory>
16 #include <utility>
17 #include <vector>
18
19 #include "absl/algorithm/container.h"
20 #include "absl/base/attributes.h"
21 #include "api/sequence_checker.h"
22 #include "api/task_queue/pending_task_safety_flag.h"
23 #include "api/task_queue/task_queue_base.h"
24 #include "api/units/time_delta.h"
25 #include "api/units/timestamp.h"
26 #include "api/video/video_frame.h"
27 #include "rtc_base/checks.h"
28 #include "rtc_base/logging.h"
29 #include "rtc_base/race_checker.h"
30 #include "rtc_base/rate_statistics.h"
31 #include "rtc_base/synchronization/mutex.h"
32 #include "rtc_base/system/no_unique_address.h"
33 #include "rtc_base/task_utils/repeating_task.h"
34 #include "rtc_base/thread_annotations.h"
35 #include "rtc_base/time_utils.h"
36 #include "system_wrappers/include/clock.h"
37 #include "system_wrappers/include/metrics.h"
38 #include "system_wrappers/include/ntp_time.h"
39
40 namespace webrtc {
41 namespace {
42
43 // Abstracts concrete modes of the cadence adapter.
44 class AdapterMode {
45 public:
46 virtual ~AdapterMode() = default;
47
48 // Called on the worker thread for every frame that enters.
49 virtual void OnFrame(Timestamp post_time,
50 int frames_scheduled_for_processing,
51 const VideoFrame& frame) = 0;
52
53 // Returns the currently estimated input framerate.
54 virtual absl::optional<uint32_t> GetInputFrameRateFps() = 0;
55
56 // Updates the frame rate.
57 virtual void UpdateFrameRate() = 0;
58 };
59
60 // Implements a pass-through adapter. Single-threaded.
61 class PassthroughAdapterMode : public AdapterMode {
62 public:
PassthroughAdapterMode(Clock * clock,FrameCadenceAdapterInterface::Callback * callback)63 PassthroughAdapterMode(Clock* clock,
64 FrameCadenceAdapterInterface::Callback* callback)
65 : clock_(clock), callback_(callback) {
66 sequence_checker_.Detach();
67 }
68
69 // Adapter overrides.
OnFrame(Timestamp post_time,int frames_scheduled_for_processing,const VideoFrame & frame)70 void OnFrame(Timestamp post_time,
71 int frames_scheduled_for_processing,
72 const VideoFrame& frame) override {
73 RTC_DCHECK_RUN_ON(&sequence_checker_);
74 callback_->OnFrame(post_time, frames_scheduled_for_processing, frame);
75 }
76
GetInputFrameRateFps()77 absl::optional<uint32_t> GetInputFrameRateFps() override {
78 RTC_DCHECK_RUN_ON(&sequence_checker_);
79 return input_framerate_.Rate(clock_->TimeInMilliseconds());
80 }
81
UpdateFrameRate()82 void UpdateFrameRate() override {
83 RTC_DCHECK_RUN_ON(&sequence_checker_);
84 input_framerate_.Update(1, clock_->TimeInMilliseconds());
85 }
86
87 private:
88 Clock* const clock_;
89 FrameCadenceAdapterInterface::Callback* const callback_;
90 RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_;
91 // Input frame rate statistics for use when not in zero-hertz mode.
RTC_GUARDED_BY(sequence_checker_)92 RateStatistics input_framerate_ RTC_GUARDED_BY(sequence_checker_){
93 FrameCadenceAdapterInterface::kFrameRateAveragingWindowSizeMs, 1000};
94 };
95
96 // Implements a frame cadence adapter supporting zero-hertz input.
97 class ZeroHertzAdapterMode : public AdapterMode {
98 public:
99 ZeroHertzAdapterMode(TaskQueueBase* queue,
100 Clock* clock,
101 FrameCadenceAdapterInterface::Callback* callback,
102 double max_fps);
103
104 // Reconfigures according to parameters.
105 // All spatial layer trackers are initialized as unconverged by this method.
106 void ReconfigureParameters(
107 const FrameCadenceAdapterInterface::ZeroHertzModeParams& params);
108
109 // Updates spatial layer quality convergence status.
110 void UpdateLayerQualityConvergence(size_t spatial_index,
111 bool quality_converged);
112
113 // Updates spatial layer enabled status.
114 void UpdateLayerStatus(size_t spatial_index, bool enabled);
115
116 // Adapter overrides.
117 void OnFrame(Timestamp post_time,
118 int frames_scheduled_for_processing,
119 const VideoFrame& frame) override;
120 absl::optional<uint32_t> GetInputFrameRateFps() override;
UpdateFrameRate()121 void UpdateFrameRate() override {}
122
123 // Notified on dropped frames.
124 void OnDiscardedFrame();
125
126 // Conditionally requests a refresh frame via
127 // Callback::RequestRefreshFrame.
128 void ProcessKeyFrameRequest();
129
130 private:
131 // The tracking state of each spatial layer. Used for determining when to
132 // stop repeating frames.
133 struct SpatialLayerTracker {
134 // If unset, the layer is disabled. Otherwise carries the quality
135 // convergence status of the layer.
136 absl::optional<bool> quality_converged;
137 };
138 // The state of a scheduled repeat.
139 struct ScheduledRepeat {
ScheduledRepeatwebrtc::__anon924c31040111::ZeroHertzAdapterMode::ScheduledRepeat140 ScheduledRepeat(Timestamp origin,
141 int64_t origin_timestamp_us,
142 int64_t origin_ntp_time_ms)
143 : scheduled(origin),
144 idle(false),
145 origin(origin),
146 origin_timestamp_us(origin_timestamp_us),
147 origin_ntp_time_ms(origin_ntp_time_ms) {}
148 // The instant when the repeat was scheduled.
149 Timestamp scheduled;
150 // True if the repeat was scheduled as an idle repeat (long), false
151 // otherwise.
152 bool idle;
153 // The moment we decided to start repeating.
154 Timestamp origin;
155 // The timestamp_us of the frame when we started repeating.
156 int64_t origin_timestamp_us;
157 // The ntp_times_ms of the frame when we started repeating.
158 int64_t origin_ntp_time_ms;
159 };
160
161 // Returns true if all spatial layers can be considered to be converged in
162 // terms of quality.
163 // Convergence means QP has dropped to a low-enough level to warrant ceasing
164 // to send identical frames at high frequency.
165 bool HasQualityConverged() const RTC_RUN_ON(sequence_checker_);
166 // Resets quality convergence information. HasQualityConverged() returns false
167 // after this call.
168 void ResetQualityConvergenceInfo() RTC_RUN_ON(sequence_checker_);
169 // Processes incoming frames on a delayed cadence.
170 void ProcessOnDelayedCadence() RTC_RUN_ON(sequence_checker_);
171 // Schedules a later repeat with delay depending on state of layer trackers.
172 // If true is passed in `idle_repeat`, the repeat is going to be
173 // kZeroHertzIdleRepeatRatePeriod. Otherwise it'll be the value of
174 // `frame_delay`.
175 void ScheduleRepeat(int frame_id, bool idle_repeat)
176 RTC_RUN_ON(sequence_checker_);
177 // Repeats a frame in the abscence of incoming frames. Slows down when quality
178 // convergence is attained, and stops the cadence terminally when new frames
179 // have arrived.
180 void ProcessRepeatedFrameOnDelayedCadence(int frame_id)
181 RTC_RUN_ON(sequence_checker_);
182 // Sends a frame, updating the timestamp to the current time.
183 void SendFrameNow(const VideoFrame& frame) const
184 RTC_RUN_ON(sequence_checker_);
185 // Returns the repeat duration depending on if it's an idle repeat or not.
186 TimeDelta RepeatDuration(bool idle_repeat) const
187 RTC_RUN_ON(sequence_checker_);
188 // Unless timer already running, starts repeatedly requesting refresh frames
189 // after a grace_period. If a frame appears before the grace_period has
190 // passed, the request is cancelled.
191 void MaybeStartRefreshFrameRequester() RTC_RUN_ON(sequence_checker_);
192
193 TaskQueueBase* const queue_;
194 Clock* const clock_;
195 FrameCadenceAdapterInterface::Callback* const callback_;
196
197 // The configured max_fps.
198 // TODO(crbug.com/1255737): support max_fps updates.
199 const double max_fps_;
200 // How much the incoming frame sequence is delayed by.
201 const TimeDelta frame_delay_ = TimeDelta::Seconds(1) / max_fps_;
202
203 RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_;
204 // A queue of incoming frames and repeated frames.
205 std::deque<VideoFrame> queued_frames_ RTC_GUARDED_BY(sequence_checker_);
206 // The current frame ID to use when starting to repeat frames. This is used
207 // for cancelling deferred repeated frame processing happening.
208 int current_frame_id_ RTC_GUARDED_BY(sequence_checker_) = 0;
209 // Has content when we are repeating frames.
210 absl::optional<ScheduledRepeat> scheduled_repeat_
211 RTC_GUARDED_BY(sequence_checker_);
212 // Convergent state of each of the configured simulcast layers.
213 std::vector<SpatialLayerTracker> layer_trackers_
214 RTC_GUARDED_BY(sequence_checker_);
215 // Repeating task handle used for requesting refresh frames until arrival, as
216 // they can be dropped in various places in the capture pipeline.
217 RepeatingTaskHandle refresh_frame_requester_
218 RTC_GUARDED_BY(sequence_checker_);
219
220 ScopedTaskSafety safety_;
221 };
222
223 class FrameCadenceAdapterImpl : public FrameCadenceAdapterInterface {
224 public:
225 FrameCadenceAdapterImpl(Clock* clock,
226 TaskQueueBase* queue,
227 const FieldTrialsView& field_trials);
228 ~FrameCadenceAdapterImpl();
229
230 // FrameCadenceAdapterInterface overrides.
231 void Initialize(Callback* callback) override;
232 void SetZeroHertzModeEnabled(
233 absl::optional<ZeroHertzModeParams> params) override;
234 absl::optional<uint32_t> GetInputFrameRateFps() override;
235 void UpdateFrameRate() override;
236 void UpdateLayerQualityConvergence(size_t spatial_index,
237 bool quality_converged) override;
238 void UpdateLayerStatus(size_t spatial_index, bool enabled) override;
239 void ProcessKeyFrameRequest() override;
240
241 // VideoFrameSink overrides.
242 void OnFrame(const VideoFrame& frame) override;
243 void OnDiscardedFrame() override;
244 void OnConstraintsChanged(
245 const VideoTrackSourceConstraints& constraints) override;
246
247 private:
248 // Called from OnFrame in zero-hertz mode.
249 void OnFrameOnMainQueue(Timestamp post_time,
250 int frames_scheduled_for_processing,
251 const VideoFrame& frame) RTC_RUN_ON(queue_);
252
253 // Returns true under all of the following conditions:
254 // - constraints min fps set to 0
255 // - constraints max fps set and greater than 0,
256 // - field trial enabled
257 // - zero-hertz mode enabled
258 bool IsZeroHertzScreenshareEnabled() const RTC_RUN_ON(queue_);
259
260 // Handles adapter creation on configuration changes.
261 void MaybeReconfigureAdapters(bool was_zero_hertz_enabled) RTC_RUN_ON(queue_);
262
263 // Called to report on constraint UMAs.
264 void MaybeReportFrameRateConstraintUmas() RTC_RUN_ON(queue_);
265
266 Clock* const clock_;
267 TaskQueueBase* const queue_;
268
269 // True if we support frame entry for screenshare with a minimum frequency of
270 // 0 Hz.
271 const bool zero_hertz_screenshare_enabled_;
272
273 // The two possible modes we're under.
274 absl::optional<PassthroughAdapterMode> passthrough_adapter_;
275 absl::optional<ZeroHertzAdapterMode> zero_hertz_adapter_;
276 // If set, zero-hertz mode has been enabled.
277 absl::optional<ZeroHertzModeParams> zero_hertz_params_;
278 // Cache for the current adapter mode.
279 AdapterMode* current_adapter_mode_ = nullptr;
280
281 // Timestamp for statistics reporting.
282 absl::optional<Timestamp> zero_hertz_adapter_created_timestamp_
283 RTC_GUARDED_BY(queue_);
284
285 // Set up during Initialize.
286 Callback* callback_ = nullptr;
287
288 // The source's constraints.
289 absl::optional<VideoTrackSourceConstraints> source_constraints_
290 RTC_GUARDED_BY(queue_);
291
292 // Race checker for incoming frames. This is the network thread in chromium,
293 // but may vary from test contexts.
294 rtc::RaceChecker incoming_frame_race_checker_;
295 bool has_reported_screenshare_frame_rate_umas_ RTC_GUARDED_BY(queue_) = false;
296
297 // Number of frames that are currently scheduled for processing on the
298 // `queue_`.
299 std::atomic<int> frames_scheduled_for_processing_{0};
300
301 ScopedTaskSafetyDetached safety_;
302 };
303
ZeroHertzAdapterMode(TaskQueueBase * queue,Clock * clock,FrameCadenceAdapterInterface::Callback * callback,double max_fps)304 ZeroHertzAdapterMode::ZeroHertzAdapterMode(
305 TaskQueueBase* queue,
306 Clock* clock,
307 FrameCadenceAdapterInterface::Callback* callback,
308 double max_fps)
309 : queue_(queue), clock_(clock), callback_(callback), max_fps_(max_fps) {
310 sequence_checker_.Detach();
311 MaybeStartRefreshFrameRequester();
312 }
313
ReconfigureParameters(const FrameCadenceAdapterInterface::ZeroHertzModeParams & params)314 void ZeroHertzAdapterMode::ReconfigureParameters(
315 const FrameCadenceAdapterInterface::ZeroHertzModeParams& params) {
316 RTC_DCHECK_RUN_ON(&sequence_checker_);
317 RTC_DLOG(LS_INFO) << __func__ << " this " << this << " num_simulcast_layers "
318 << params.num_simulcast_layers;
319
320 // Start as unconverged.
321 layer_trackers_.clear();
322 layer_trackers_.resize(params.num_simulcast_layers,
323 SpatialLayerTracker{false});
324 }
325
UpdateLayerQualityConvergence(size_t spatial_index,bool quality_converged)326 void ZeroHertzAdapterMode::UpdateLayerQualityConvergence(
327 size_t spatial_index,
328 bool quality_converged) {
329 RTC_DCHECK_RUN_ON(&sequence_checker_);
330 RTC_LOG(LS_INFO) << __func__ << " this " << this << " layer " << spatial_index
331 << " quality has converged: " << quality_converged;
332 if (spatial_index >= layer_trackers_.size())
333 return;
334 if (layer_trackers_[spatial_index].quality_converged.has_value())
335 layer_trackers_[spatial_index].quality_converged = quality_converged;
336 }
337
UpdateLayerStatus(size_t spatial_index,bool enabled)338 void ZeroHertzAdapterMode::UpdateLayerStatus(size_t spatial_index,
339 bool enabled) {
340 RTC_DCHECK_RUN_ON(&sequence_checker_);
341 if (spatial_index >= layer_trackers_.size())
342 return;
343 if (enabled) {
344 if (!layer_trackers_[spatial_index].quality_converged.has_value()) {
345 // Assume quality has not converged until hearing otherwise.
346 layer_trackers_[spatial_index].quality_converged = false;
347 }
348 } else {
349 layer_trackers_[spatial_index].quality_converged = absl::nullopt;
350 }
351 RTC_LOG(LS_INFO)
352 << __func__ << " this " << this << " layer " << spatial_index
353 << (enabled
354 ? (layer_trackers_[spatial_index].quality_converged.has_value()
355 ? " enabled."
356 : " enabled and it's assumed quality has not converged.")
357 : " disabled.");
358 }
359
OnFrame(Timestamp post_time,int frames_scheduled_for_processing,const VideoFrame & frame)360 void ZeroHertzAdapterMode::OnFrame(Timestamp post_time,
361 int frames_scheduled_for_processing,
362 const VideoFrame& frame) {
363 RTC_DCHECK_RUN_ON(&sequence_checker_);
364 RTC_DLOG(LS_VERBOSE) << "ZeroHertzAdapterMode::" << __func__ << " this "
365 << this;
366 refresh_frame_requester_.Stop();
367
368 // Assume all enabled layers are unconverged after frame entry.
369 ResetQualityConvergenceInfo();
370
371 // Remove stored repeating frame if needed.
372 if (scheduled_repeat_.has_value()) {
373 RTC_DCHECK(queued_frames_.size() == 1);
374 RTC_DLOG(LS_VERBOSE) << __func__ << " this " << this
375 << " cancel repeat and restart with original";
376 queued_frames_.pop_front();
377 }
378
379 // Store the frame in the queue and schedule deferred processing.
380 queued_frames_.push_back(frame);
381 current_frame_id_++;
382 scheduled_repeat_ = absl::nullopt;
383 queue_->PostDelayedHighPrecisionTask(
384 SafeTask(safety_.flag(),
385 [this] {
386 RTC_DCHECK_RUN_ON(&sequence_checker_);
387 ProcessOnDelayedCadence();
388 }),
389 frame_delay_);
390 }
391
OnDiscardedFrame()392 void ZeroHertzAdapterMode::OnDiscardedFrame() {
393 RTC_DCHECK_RUN_ON(&sequence_checker_);
394 RTC_DLOG(LS_VERBOSE) << "ZeroHertzAdapterMode::" << __func__;
395
396 // Under zero hertz source delivery, a discarded frame ending a sequence of
397 // frames which happened to contain important information can be seen as a
398 // capture freeze. Avoid this by starting requesting refresh frames after a
399 // grace period.
400 MaybeStartRefreshFrameRequester();
401 }
402
GetInputFrameRateFps()403 absl::optional<uint32_t> ZeroHertzAdapterMode::GetInputFrameRateFps() {
404 RTC_DCHECK_RUN_ON(&sequence_checker_);
405 return max_fps_;
406 }
407
ProcessKeyFrameRequest()408 void ZeroHertzAdapterMode::ProcessKeyFrameRequest() {
409 RTC_DCHECK_RUN_ON(&sequence_checker_);
410
411 // If we're new and don't have a frame, there's no need to request refresh
412 // frames as this was being triggered for us when zero-hz mode was set up.
413 //
414 // The next frame encoded will be a key frame. Reset quality convergence so we
415 // don't get idle repeats shortly after, because key frames need a lot of
416 // refinement frames.
417 ResetQualityConvergenceInfo();
418
419 // If we're not repeating, or we're repeating with short duration, we will
420 // very soon send out a frame and don't need a refresh frame.
421 if (!scheduled_repeat_.has_value() || !scheduled_repeat_->idle) {
422 RTC_LOG(LS_INFO) << __func__ << " this " << this
423 << " not requesting refresh frame because of recently "
424 "incoming frame or short repeating.";
425 return;
426 }
427
428 // If the repeat is scheduled within a short (i.e. frame_delay_) interval, we
429 // will very soon send out a frame and don't need a refresh frame.
430 Timestamp now = clock_->CurrentTime();
431 if (scheduled_repeat_->scheduled + RepeatDuration(/*idle_repeat=*/true) -
432 now <=
433 frame_delay_) {
434 RTC_LOG(LS_INFO) << __func__ << " this " << this
435 << " not requesting refresh frame because of soon "
436 "happening idle repeat";
437 return;
438 }
439
440 // Cancel the current repeat and reschedule a short repeat now. No need for a
441 // new refresh frame.
442 RTC_LOG(LS_INFO) << __func__ << " this " << this
443 << " not requesting refresh frame and scheduling a short "
444 "repeat due to key frame request";
445 ScheduleRepeat(++current_frame_id_, /*idle_repeat=*/false);
446 return;
447 }
448
HasQualityConverged() const449 bool ZeroHertzAdapterMode::HasQualityConverged() const {
450 RTC_DCHECK_RUN_ON(&sequence_checker_);
451 // 1. Define ourselves as unconverged with no spatial layers configured. This
452 // is to keep short repeating until the layer configuration comes.
453 // 2. Unset layers implicitly imply that they're converged to support
454 // disabling layers when they're not needed.
455 const bool quality_converged =
456 !layer_trackers_.empty() &&
457 absl::c_all_of(layer_trackers_, [](const SpatialLayerTracker& tracker) {
458 return tracker.quality_converged.value_or(true);
459 });
460 return quality_converged;
461 }
462
ResetQualityConvergenceInfo()463 void ZeroHertzAdapterMode::ResetQualityConvergenceInfo() {
464 RTC_DCHECK_RUN_ON(&sequence_checker_);
465 RTC_DLOG(LS_INFO) << __func__ << " this " << this;
466 for (auto& layer_tracker : layer_trackers_) {
467 if (layer_tracker.quality_converged.has_value())
468 layer_tracker.quality_converged = false;
469 }
470 }
471
ProcessOnDelayedCadence()472 void ZeroHertzAdapterMode::ProcessOnDelayedCadence() {
473 RTC_DCHECK_RUN_ON(&sequence_checker_);
474 RTC_DCHECK(!queued_frames_.empty());
475 RTC_DLOG(LS_VERBOSE) << __func__ << " this " << this;
476
477 SendFrameNow(queued_frames_.front());
478
479 // If there were two or more frames stored, we do not have to schedule repeats
480 // of the front frame.
481 if (queued_frames_.size() > 1) {
482 queued_frames_.pop_front();
483 return;
484 }
485
486 // There's only one frame to send. Schedule a repeat sequence, which is
487 // cancelled by `current_frame_id_` getting incremented should new frames
488 // arrive.
489 ScheduleRepeat(current_frame_id_, HasQualityConverged());
490 }
491
ScheduleRepeat(int frame_id,bool idle_repeat)492 void ZeroHertzAdapterMode::ScheduleRepeat(int frame_id, bool idle_repeat) {
493 RTC_DCHECK_RUN_ON(&sequence_checker_);
494 RTC_DLOG(LS_VERBOSE) << __func__ << " this " << this << " frame_id "
495 << frame_id;
496 Timestamp now = clock_->CurrentTime();
497 if (!scheduled_repeat_.has_value()) {
498 scheduled_repeat_.emplace(now, queued_frames_.front().timestamp_us(),
499 queued_frames_.front().ntp_time_ms());
500 }
501 scheduled_repeat_->scheduled = now;
502 scheduled_repeat_->idle = idle_repeat;
503
504 TimeDelta repeat_delay = RepeatDuration(idle_repeat);
505 queue_->PostDelayedHighPrecisionTask(
506 SafeTask(safety_.flag(),
507 [this, frame_id] {
508 RTC_DCHECK_RUN_ON(&sequence_checker_);
509 ProcessRepeatedFrameOnDelayedCadence(frame_id);
510 }),
511 repeat_delay);
512 }
513
ProcessRepeatedFrameOnDelayedCadence(int frame_id)514 void ZeroHertzAdapterMode::ProcessRepeatedFrameOnDelayedCadence(int frame_id) {
515 RTC_DCHECK_RUN_ON(&sequence_checker_);
516 RTC_DLOG(LS_VERBOSE) << __func__ << " this " << this << " frame_id "
517 << frame_id;
518 RTC_DCHECK(!queued_frames_.empty());
519
520 // Cancel this invocation if new frames turned up.
521 if (frame_id != current_frame_id_)
522 return;
523 RTC_DCHECK(scheduled_repeat_.has_value());
524
525 VideoFrame& frame = queued_frames_.front();
526
527 // Since this is a repeated frame, nothing changed compared to before.
528 VideoFrame::UpdateRect empty_update_rect;
529 empty_update_rect.MakeEmptyUpdate();
530 frame.set_update_rect(empty_update_rect);
531
532 // Adjust timestamps of the frame of the repeat, accounting for the actual
533 // delay since we started repeating.
534 //
535 // NOTE: No need to update the RTP timestamp as the VideoStreamEncoder
536 // overwrites it based on its chosen NTP timestamp source.
537 TimeDelta total_delay = clock_->CurrentTime() - scheduled_repeat_->origin;
538 if (frame.timestamp_us() > 0) {
539 frame.set_timestamp_us(scheduled_repeat_->origin_timestamp_us +
540 total_delay.us());
541 }
542 if (frame.ntp_time_ms()) {
543 frame.set_ntp_time_ms(scheduled_repeat_->origin_ntp_time_ms +
544 total_delay.ms());
545 }
546 SendFrameNow(frame);
547
548 // Schedule another repeat.
549 ScheduleRepeat(frame_id, HasQualityConverged());
550 }
551
SendFrameNow(const VideoFrame & frame) const552 void ZeroHertzAdapterMode::SendFrameNow(const VideoFrame& frame) const {
553 RTC_DCHECK_RUN_ON(&sequence_checker_);
554 RTC_DLOG(LS_VERBOSE) << __func__ << " this " << this << " timestamp "
555 << frame.timestamp() << " timestamp_us "
556 << frame.timestamp_us() << " ntp_time_ms "
557 << frame.ntp_time_ms();
558 // TODO(crbug.com/1255737): figure out if frames_scheduled_for_processing
559 // makes sense to compute in this implementation.
560 callback_->OnFrame(/*post_time=*/clock_->CurrentTime(),
561 /*frames_scheduled_for_processing=*/1, frame);
562 }
563
RepeatDuration(bool idle_repeat) const564 TimeDelta ZeroHertzAdapterMode::RepeatDuration(bool idle_repeat) const {
565 RTC_DCHECK_RUN_ON(&sequence_checker_);
566 return idle_repeat
567 ? FrameCadenceAdapterInterface::kZeroHertzIdleRepeatRatePeriod
568 : frame_delay_;
569 }
570
MaybeStartRefreshFrameRequester()571 void ZeroHertzAdapterMode::MaybeStartRefreshFrameRequester() {
572 RTC_DCHECK_RUN_ON(&sequence_checker_);
573 RTC_DLOG(LS_VERBOSE) << __func__;
574 if (!refresh_frame_requester_.Running()) {
575 refresh_frame_requester_ = RepeatingTaskHandle::DelayedStart(
576 queue_,
577 FrameCadenceAdapterInterface::kOnDiscardedFrameRefreshFramePeriod *
578 frame_delay_,
579 [this] {
580 RTC_DLOG(LS_VERBOSE) << __func__ << " RequestRefreshFrame";
581 if (callback_)
582 callback_->RequestRefreshFrame();
583 return frame_delay_;
584 });
585 }
586 }
587
FrameCadenceAdapterImpl(Clock * clock,TaskQueueBase * queue,const FieldTrialsView & field_trials)588 FrameCadenceAdapterImpl::FrameCadenceAdapterImpl(
589 Clock* clock,
590 TaskQueueBase* queue,
591 const FieldTrialsView& field_trials)
592 : clock_(clock),
593 queue_(queue),
594 zero_hertz_screenshare_enabled_(
595 !field_trials.IsDisabled("WebRTC-ZeroHertzScreenshare")) {}
596
~FrameCadenceAdapterImpl()597 FrameCadenceAdapterImpl::~FrameCadenceAdapterImpl() {
598 RTC_DLOG(LS_VERBOSE) << __func__ << " this " << this;
599 }
600
Initialize(Callback * callback)601 void FrameCadenceAdapterImpl::Initialize(Callback* callback) {
602 callback_ = callback;
603 passthrough_adapter_.emplace(clock_, callback);
604 current_adapter_mode_ = &passthrough_adapter_.value();
605 }
606
SetZeroHertzModeEnabled(absl::optional<ZeroHertzModeParams> params)607 void FrameCadenceAdapterImpl::SetZeroHertzModeEnabled(
608 absl::optional<ZeroHertzModeParams> params) {
609 RTC_DCHECK_RUN_ON(queue_);
610 bool was_zero_hertz_enabled = zero_hertz_params_.has_value();
611 if (params.has_value() && !was_zero_hertz_enabled)
612 has_reported_screenshare_frame_rate_umas_ = false;
613 zero_hertz_params_ = params;
614 MaybeReconfigureAdapters(was_zero_hertz_enabled);
615 }
616
GetInputFrameRateFps()617 absl::optional<uint32_t> FrameCadenceAdapterImpl::GetInputFrameRateFps() {
618 RTC_DCHECK_RUN_ON(queue_);
619 return current_adapter_mode_->GetInputFrameRateFps();
620 }
621
UpdateFrameRate()622 void FrameCadenceAdapterImpl::UpdateFrameRate() {
623 RTC_DCHECK_RUN_ON(queue_);
624 // The frame rate need not be updated for the zero-hertz adapter. The
625 // passthrough adapter however uses it. Always pass frames into the
626 // passthrough to keep the estimation alive should there be an adapter switch.
627 passthrough_adapter_->UpdateFrameRate();
628 }
629
UpdateLayerQualityConvergence(size_t spatial_index,bool quality_converged)630 void FrameCadenceAdapterImpl::UpdateLayerQualityConvergence(
631 size_t spatial_index,
632 bool quality_converged) {
633 if (zero_hertz_adapter_.has_value())
634 zero_hertz_adapter_->UpdateLayerQualityConvergence(spatial_index,
635 quality_converged);
636 }
637
UpdateLayerStatus(size_t spatial_index,bool enabled)638 void FrameCadenceAdapterImpl::UpdateLayerStatus(size_t spatial_index,
639 bool enabled) {
640 if (zero_hertz_adapter_.has_value())
641 zero_hertz_adapter_->UpdateLayerStatus(spatial_index, enabled);
642 }
643
ProcessKeyFrameRequest()644 void FrameCadenceAdapterImpl::ProcessKeyFrameRequest() {
645 RTC_DCHECK_RUN_ON(queue_);
646 if (zero_hertz_adapter_)
647 zero_hertz_adapter_->ProcessKeyFrameRequest();
648 }
649
OnFrame(const VideoFrame & frame)650 void FrameCadenceAdapterImpl::OnFrame(const VideoFrame& frame) {
651 // This method is called on the network thread under Chromium, or other
652 // various contexts in test.
653 RTC_DCHECK_RUNS_SERIALIZED(&incoming_frame_race_checker_);
654 RTC_DLOG(LS_VERBOSE) << "FrameCadenceAdapterImpl::" << __func__ << " this "
655 << this;
656
657 // Local time in webrtc time base.
658 Timestamp post_time = clock_->CurrentTime();
659 frames_scheduled_for_processing_.fetch_add(1, std::memory_order_relaxed);
660 queue_->PostTask(SafeTask(safety_.flag(), [this, post_time, frame] {
661 RTC_DCHECK_RUN_ON(queue_);
662 if (zero_hertz_adapter_created_timestamp_.has_value()) {
663 TimeDelta time_until_first_frame =
664 clock_->CurrentTime() - *zero_hertz_adapter_created_timestamp_;
665 zero_hertz_adapter_created_timestamp_ = absl::nullopt;
666 RTC_HISTOGRAM_COUNTS_10000(
667 "WebRTC.Screenshare.ZeroHz.TimeUntilFirstFrameMs",
668 time_until_first_frame.ms());
669 }
670
671 const int frames_scheduled_for_processing =
672 frames_scheduled_for_processing_.fetch_sub(1,
673 std::memory_order_relaxed);
674 OnFrameOnMainQueue(post_time, frames_scheduled_for_processing,
675 std::move(frame));
676 MaybeReportFrameRateConstraintUmas();
677 }));
678 }
679
OnDiscardedFrame()680 void FrameCadenceAdapterImpl::OnDiscardedFrame() {
681 callback_->OnDiscardedFrame();
682 queue_->PostTask(SafeTask(safety_.flag(), [this] {
683 RTC_DCHECK_RUN_ON(queue_);
684 if (zero_hertz_adapter_) {
685 zero_hertz_adapter_->OnDiscardedFrame();
686 }
687 }));
688 }
689
OnConstraintsChanged(const VideoTrackSourceConstraints & constraints)690 void FrameCadenceAdapterImpl::OnConstraintsChanged(
691 const VideoTrackSourceConstraints& constraints) {
692 RTC_LOG(LS_INFO) << __func__ << " this " << this << " min_fps "
693 << constraints.min_fps.value_or(-1) << " max_fps "
694 << constraints.max_fps.value_or(-1);
695 queue_->PostTask(SafeTask(safety_.flag(), [this, constraints] {
696 RTC_DCHECK_RUN_ON(queue_);
697 bool was_zero_hertz_enabled = IsZeroHertzScreenshareEnabled();
698 source_constraints_ = constraints;
699 MaybeReconfigureAdapters(was_zero_hertz_enabled);
700 }));
701 }
702
OnFrameOnMainQueue(Timestamp post_time,int frames_scheduled_for_processing,const VideoFrame & frame)703 void FrameCadenceAdapterImpl::OnFrameOnMainQueue(
704 Timestamp post_time,
705 int frames_scheduled_for_processing,
706 const VideoFrame& frame) {
707 RTC_DCHECK_RUN_ON(queue_);
708 current_adapter_mode_->OnFrame(post_time, frames_scheduled_for_processing,
709 frame);
710 }
711
IsZeroHertzScreenshareEnabled() const712 bool FrameCadenceAdapterImpl::IsZeroHertzScreenshareEnabled() const {
713 RTC_DCHECK_RUN_ON(queue_);
714 return zero_hertz_screenshare_enabled_ && source_constraints_.has_value() &&
715 source_constraints_->max_fps.value_or(-1) > 0 &&
716 source_constraints_->min_fps.value_or(-1) == 0 &&
717 zero_hertz_params_.has_value();
718 }
719
MaybeReconfigureAdapters(bool was_zero_hertz_enabled)720 void FrameCadenceAdapterImpl::MaybeReconfigureAdapters(
721 bool was_zero_hertz_enabled) {
722 RTC_DCHECK_RUN_ON(queue_);
723 bool is_zero_hertz_enabled = IsZeroHertzScreenshareEnabled();
724 if (is_zero_hertz_enabled) {
725 if (!was_zero_hertz_enabled) {
726 zero_hertz_adapter_.emplace(queue_, clock_, callback_,
727 source_constraints_->max_fps.value());
728 RTC_LOG(LS_INFO) << "Zero hertz mode activated.";
729 zero_hertz_adapter_created_timestamp_ = clock_->CurrentTime();
730 }
731 zero_hertz_adapter_->ReconfigureParameters(zero_hertz_params_.value());
732 current_adapter_mode_ = &zero_hertz_adapter_.value();
733 } else {
734 if (was_zero_hertz_enabled)
735 zero_hertz_adapter_ = absl::nullopt;
736 current_adapter_mode_ = &passthrough_adapter_.value();
737 }
738 }
739
MaybeReportFrameRateConstraintUmas()740 void FrameCadenceAdapterImpl::MaybeReportFrameRateConstraintUmas() {
741 RTC_DCHECK_RUN_ON(queue_);
742 if (has_reported_screenshare_frame_rate_umas_)
743 return;
744 has_reported_screenshare_frame_rate_umas_ = true;
745 if (!zero_hertz_params_.has_value())
746 return;
747 RTC_HISTOGRAM_BOOLEAN("WebRTC.Screenshare.FrameRateConstraints.Exists",
748 source_constraints_.has_value());
749 if (!source_constraints_.has_value())
750 return;
751 RTC_HISTOGRAM_BOOLEAN("WebRTC.Screenshare.FrameRateConstraints.Min.Exists",
752 source_constraints_->min_fps.has_value());
753 if (source_constraints_->min_fps.has_value()) {
754 RTC_HISTOGRAM_COUNTS_100(
755 "WebRTC.Screenshare.FrameRateConstraints.Min.Value",
756 source_constraints_->min_fps.value());
757 }
758 RTC_HISTOGRAM_BOOLEAN("WebRTC.Screenshare.FrameRateConstraints.Max.Exists",
759 source_constraints_->max_fps.has_value());
760 if (source_constraints_->max_fps.has_value()) {
761 RTC_HISTOGRAM_COUNTS_100(
762 "WebRTC.Screenshare.FrameRateConstraints.Max.Value",
763 source_constraints_->max_fps.value());
764 }
765 if (!source_constraints_->min_fps.has_value()) {
766 if (source_constraints_->max_fps.has_value()) {
767 RTC_HISTOGRAM_COUNTS_100(
768 "WebRTC.Screenshare.FrameRateConstraints.MinUnset.Max",
769 source_constraints_->max_fps.value());
770 }
771 } else if (source_constraints_->max_fps.has_value()) {
772 if (source_constraints_->min_fps.value() <
773 source_constraints_->max_fps.value()) {
774 RTC_HISTOGRAM_COUNTS_100(
775 "WebRTC.Screenshare.FrameRateConstraints.MinLessThanMax.Min",
776 source_constraints_->min_fps.value());
777 RTC_HISTOGRAM_COUNTS_100(
778 "WebRTC.Screenshare.FrameRateConstraints.MinLessThanMax.Max",
779 source_constraints_->max_fps.value());
780 }
781 // Multi-dimensional histogram for min and max FPS making it possible to
782 // uncover min and max combinations. See
783 // https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md#multidimensional-histograms
784 constexpr int kMaxBucketCount =
785 60 * /*max min_fps=*/60 + /*max max_fps=*/60 - 1;
786 RTC_HISTOGRAM_ENUMERATION_SPARSE(
787 "WebRTC.Screenshare.FrameRateConstraints.60MinPlusMaxMinusOne",
788 source_constraints_->min_fps.value() * 60 +
789 source_constraints_->max_fps.value() - 1,
790 /*boundary=*/kMaxBucketCount);
791 }
792 }
793
794 } // namespace
795
796 std::unique_ptr<FrameCadenceAdapterInterface>
Create(Clock * clock,TaskQueueBase * queue,const FieldTrialsView & field_trials)797 FrameCadenceAdapterInterface::Create(Clock* clock,
798 TaskQueueBase* queue,
799 const FieldTrialsView& field_trials) {
800 return std::make_unique<FrameCadenceAdapterImpl>(clock, queue, field_trials);
801 }
802
803 } // namespace webrtc
804