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