1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
media / capture / content / video_capture_oracle.h [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MEDIA_CAPTURE_CONTENT_VIDEO_CAPTURE_ORACLE_H_
#define MEDIA_CAPTURE_CONTENT_VIDEO_CAPTURE_ORACLE_H_
#include <string>
#include "base/functional/callback.h"
#include "base/time/time.h"
#include "media/base/feedback_signal_accumulator.h"
#include "media/capture/capture_export.h"
#include "media/capture/content/animated_content_sampler.h"
#include "media/capture/content/capture_resolution_chooser.h"
#include "media/capture/content/smooth_event_sampler.h"
#include "media/capture/video/video_capture_feedback.h"
#include "ui/gfx/geometry/rect.h"
namespace media {
// VideoCaptureOracle manages the producer-side throttling of captured frames
// from a video capture device. It is informed of every update by the device;
// this empowers it to look into the future and decide if a particular frame
// ought to be captured in order to achieve its target frame rate.
class CAPTURE_EXPORT VideoCaptureOracle {
public:
enum Event {
kCompositorUpdate,
// A "refresh request" means that we want to update to keep things
// relatively fresh and in sync, and thus should capture a frame as long as
// it's not happening too frequently (in practice this ends up being 1-5
// frames per second).
kRefreshRequest,
// A "refresh demand" means that we know we have new information, such as a
// mouse cursor location change, and thus generating a frame is higher
// priority than a "refresh request."
kRefreshDemand,
kNumEvents,
};
// Constructs a VideoCaptureOracle with a default min capture period and
// capture size constraints. Clients should call SetMinCapturePeriod() and
// SetCaptureSizeConstraints() to provide more-accurate hard limits.
//
// See SetAutoThrottlingEnabled() for |enable_auto_throttling| semantics.
explicit VideoCaptureOracle(bool enable_auto_throttling);
virtual ~VideoCaptureOracle();
// Get/Update the minimum capture period.
base::TimeDelta min_capture_period() const {
return smoothing_sampler_.min_capture_period();
}
void SetMinCapturePeriod(base::TimeDelta period);
// Sets the range of acceptable capture sizes and whether a fixed aspect ratio
// is required. If a fixed aspect ratio is required, the aspect ratio of
// |max_size| is used.
void SetCaptureSizeConstraints(const gfx::Size& min_size,
const gfx::Size& max_size,
bool use_fixed_aspect_ratio);
// Specifies whether the oracle should propose varying capture sizes, in
// response to consumer feedback. If not |enabled|, capture_size() will always
// return the source_size().
//
// See: SetMinSizeChangePeriod().
void SetAutoThrottlingEnabled(bool enabled);
// Get/Update the source content size. Changes may not have an immediate
// effect on the proposed capture size, as the oracle will prevent too-
// frequent changes from occurring.
gfx::Size source_size() const { return resolution_chooser_.source_size(); }
void SetSourceSize(const gfx::Size& source_size);
// Record a event of type |event|, and decide whether the caller should do a
// frame capture. |damage_rect| is the region of a frame about to be drawn,
// and may be an empty Rect, if this is not known. If the caller accepts the
// oracle's proposal, it should call RecordCapture() to indicate this.
bool ObserveEventAndDecideCapture(Event event,
const gfx::Rect& damage_rect,
base::TimeTicks event_time);
// Returns the |frame_number| to be used with CompleteCapture().
int next_frame_number() const { return next_frame_number_; }
// Record and update internal state based on whether the frame capture will be
// started. |pool_utilization| is a value in the range 0.0 to 1.0 to indicate
// the current buffer pool utilization relative to a sustainable maximum (not
// the absolute maximum). This method should only be called if the last call
// to ObserveEventAndDecideCapture() returned true.
void RecordCapture(float pool_utilization);
void RecordWillNotCapture(float pool_utilization);
// Notify of the completion of a capture, and whether it was successful.
// Returns true iff the captured frame should be delivered. |frame_timestamp|
// is set to the timestamp that should be provided to the consumer of the
// frame.
virtual bool CompleteCapture(int frame_number,
bool capture_was_successful,
base::TimeTicks* frame_timestamp);
// Notify that all in-flight captures have been canceled. This has the same
// effect as calling CompleteCapture() with a non-success status for all
// outstanding frames.
void CancelAllCaptures();
// Record the resource utilization feedback for a frame that was processed by
// the consumer. This allows the oracle to reduce/increase future data volume
// if the consumer is overloaded/under-utilized. |resource_utilization| is a
// value in the range 0.0 to 1.0 to indicate the current consumer resource
// utilization relative to a sustainable maximum (not the absolute maximum).
// This method should only be called for frames where CompleteCapture()
// returned true.
void RecordConsumerFeedback(int frame_number,
const media::VideoCaptureFeedback& feedback);
// Sets the minimum amount of time that must pass between changes to the
// capture size due to autothrottling. This throttles the rate of size
// changes, to avoid stressing consumers and to allow the end-to-end system
// sufficient time to stabilize before re-evaluating the capture size.
void SetMinSizeChangePeriod(base::TimeDelta period);
// Returns the oracle's estimate of the duration of the next frame. This
// should be called just after ObserveEventAndDecideCapture(), and will only
// be non-zero if the call returned true.
base::TimeDelta estimated_frame_duration() const {
return duration_of_next_frame_;
}
// Returns the capture frame size the client should use. This is updated by
// calls to ObserveEventAndDecideCapture(). The oracle prevents too-frequent
// changes to the capture size, to avoid stressing the end-to-end pipeline.
virtual gfx::Size capture_size() const;
// Returns the oracle's estimate of the last time animation was detected.
base::TimeTicks last_time_animation_was_detected() const {
return last_time_animation_was_detected_;
}
// Returns a NUL-terminated string containing a short, human-readable form of
// |event|.
static const char* EventAsString(Event event);
// Default minimum capture period. This is a rather low framerate for safety.
// Clients are expected to set a better minimum capture period after
// VideoCaptureOracle is constructed.
static constexpr base::TimeDelta kDefaultMinCapturePeriod =
base::Milliseconds(200); // 5 FPS
// Default minimum size change period if SetMinSizeChangePeriod is not called.
static constexpr base::TimeDelta kDefaultMinSizeChangePeriod =
base::Seconds(3);
void SetLogCallback(
base::RepeatingCallback<void(const std::string&)> emit_log_cb);
void Log(const std::string& message);
private:
// Retrieve/Assign a frame timestamp by capture |frame_number|. Only valid
// when IsFrameInRecentHistory(frame_number) returns true.
base::TimeTicks GetFrameTimestamp(int frame_number) const;
void SetFrameTimestamp(int frame_number, base::TimeTicks timestamp);
// Returns true if the frame timestamp ring-buffer currently includes a
// slot for the given |frame_number|.
bool IsFrameInRecentHistory(int frame_number) const;
// Queries the ResolutionChooser to update |capture_size_|, and resets all the
// FeedbackSignalAccumulator members to stable-state starting values. The
// accumulators are reset such that they can only apply feedback updates for
// future frames (those with a timestamp after |last_frame_time|).
void CommitCaptureSizeAndReset(base::TimeTicks last_frame_time);
// Called after a capture or no-capture decision was recorded. This analyzes
// current state and may result in a future change to the capture size.
void AnalyzeAndAdjust(base::TimeTicks analyze_time);
// Analyzes current feedback signal accumulators for an indication that the
// capture size should be decreased. Returns either a decreased area, or -1
// if no decrease should be made.
int AnalyzeForDecreasedArea(base::TimeTicks analyze_time);
// Analyzes current feedback signal accumulators for an indication that the
// the system has had excellent long-term performance and the capture size
// should be increased to improve quality. Returns either an increased area,
// or -1 if no increase should be made.
int AnalyzeForIncreasedArea(base::TimeTicks analyze_time);
// Returns the amount of time, since the source size last changed, to allow
// frequent increases in capture area. This allows the system a period of
// time to quickly explore up and down to find an ideal point before being
// more careful about capture size increases.
base::TimeDelta GetExplorationPeriodAfterSourceSizeChange();
// Returns true if updates have been accumulated by |accumulator| for a
// sufficient amount of time and the latest update was fairly recent, relative
// to |now|.
bool HasSufficientRecentFeedback(
const FeedbackSignalAccumulator<base::TimeTicks>& accumulator,
base::TimeTicks now);
// Set to disabled/enabled via SetAutoThrottlingEnabled(). Data collection and
// analysis for capture size changes only occurs while in "active" mode, which
// is only engaged when in "enabled" mode and consumer feedback is received
// for the first time.
enum {
kThrottlingDisabled,
kThrottlingEnabled,
kThrottlingActive
} capture_size_throttling_mode_;
// The minimum amount of time that must pass between changes to the capture
// size.
base::TimeDelta min_size_change_period_;
// Incremented every time RecordCapture() is called.
int next_frame_number_;
// Stores the last |event_time| from the last observation/decision. Used to
// sanity-check that event times are monotonically non-decreasing.
base::TimeTicks last_event_time_[kNumEvents];
// Updated by the last call to ObserveEventAndDecideCapture() with the
// estimated duration of the next frame to sample. This is zero if the method
// returned false.
base::TimeDelta duration_of_next_frame_;
// Stores the frame number from the last successfully delivered frame.
int last_successfully_delivered_frame_number_;
// Stores the number of pending frame captures.
int num_frames_pending_;
// These track present/paint history and propose whether to sample each event
// for capture. |smoothing_sampler_| uses a "works for all" heuristic, while
// |content_sampler_| specifically detects animated content (e.g., video
// playback) and decides which events to sample to "lock into" that content.
SmoothEventSampler smoothing_sampler_;
AnimatedContentSampler content_sampler_;
// This is the overall setting, provided in the last call to
// SetMinCapturePeriod(), but the live setting in |smoothing_sampler_| and
// |content_sampler_| could be greater if the consumer feedback requests it.
base::TimeDelta min_capture_period_;
// Determines video capture frame sizes.
CaptureResolutionChooser resolution_chooser_;
// The timestamp of the frame just before the last call to SetSourceSize().
base::TimeTicks source_size_change_time_;
// The current capture size. |resolution_chooser_| may hold an updated value
// because the oracle prevents this size from changing too frequently. This
// avoids over-stressing consumers (e.g., when a window is being actively
// drag-resized) and allowing the end-to-end system time to stabilize.
gfx::Size capture_size_;
// Recent history of frame timestamps proposed by VideoCaptureOracle. This is
// a ring-buffer, and should only be accessed by the Get/SetFrameTimestamp()
// methods.
enum { kMaxFrameTimestamps = 16 };
base::TimeTicks frame_timestamps_[kMaxFrameTimestamps];
// Recent average buffer pool utilization for capture.
FeedbackSignalAccumulator<base::TimeTicks> buffer_pool_utilization_;
// Estimated maximum frame area that currently can be handled by the consumer,
// in number of pixels per frame. This is used to adjust the capture size up
// or down to a data volume the consumer can handle. Note that some consumers
// do not provide feedback, and the analysis logic should account for that.
FeedbackSignalAccumulator<base::TimeTicks> estimated_capable_area_;
// The time of the first analysis which concluded the end-to-end system was
// under-utilized. If null, the system is not currently under-utilized. This
// is used to determine when a proving period has elapsed that justifies an
// increase in capture size.
base::TimeTicks start_time_of_underutilization_;
// The timestamp of the frame where |content_sampler_| last detected
// animation. This determines whether capture size increases will be
// aggressive (because content is not animating).
base::TimeTicks last_time_animation_was_detected_;
// Callback for webrtc native log. It should check for corresponding feature
// inside.
base::RepeatingCallback<void(const std::string&)> emit_log_message_cb_;
};
} // namespace media
#endif // MEDIA_CAPTURE_CONTENT_VIDEO_CAPTURE_ORACLE_H_