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