xref: /aosp_15_r20/external/webrtc/video/frame_cadence_adapter.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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