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
media / capture / content / animated_content_sampler.h [blame]
// Copyright 2015 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_ANIMATED_CONTENT_SAMPLER_H_
#define MEDIA_CAPTURE_CONTENT_ANIMATED_CONTENT_SAMPLER_H_
#include "base/containers/circular_deque.h"
#include "base/time/time.h"
#include "media/capture/capture_export.h"
#include "ui/gfx/geometry/rect.h"
namespace media {
// Analyzes a sequence of events to detect the presence of constant frame rate
// animated content. In the case where there are multiple regions of animated
// content, AnimatedContentSampler will propose sampling the one having the
// largest "smoothness" impact, according to human perception (e.g., a 24 FPS
// video versus a 60 FPS busy spinner).
//
// In addition, AnimatedContentSampler will provide rewritten frame timestamps,
// for downstream consumers, that are "truer" to the source content than to the
// local presentation hardware.
class CAPTURE_EXPORT AnimatedContentSampler {
public:
explicit AnimatedContentSampler(base::TimeDelta min_capture_period);
~AnimatedContentSampler();
// Sets a new minimum capture period.
void SetMinCapturePeriod(base::TimeDelta period);
// Get/Set the target sampling period. This is used to determine whether to
// subsample the frames of animated content.
base::TimeDelta target_sampling_period() const {
return target_sampling_period_;
}
void SetTargetSamplingPeriod(base::TimeDelta period);
// Examines the given presentation event metadata, along with recent history,
// to detect animated content, updating the state of this sampler.
// |damage_rect| is the region of a frame about to be drawn, while
// |event_time| refers to the frame's estimated presentation time.
void ConsiderPresentationEvent(const gfx::Rect& damage_rect,
base::TimeTicks event_time);
// Returns true if animated content has been detected and a decision has been
// made about whether to sample the last event.
bool HasProposal() const;
// Returns true if the last event considered should be sampled.
bool ShouldSample() const;
// Returns a frame timestamp to provide to consumers of the sampled frame.
// Only valid when ShouldSample() returns true.
base::TimeTicks frame_timestamp() const { return frame_timestamp_; }
// Returns the current sampling period. This can be treated as the estimated
// duration of the frame to be sampled. Only valid when HasProposal()
// returns true.
base::TimeDelta sampling_period() const { return sampling_period_; }
// Accessors to currently-detected animating region/period, for logging.
const gfx::Rect& detected_region() const { return detected_region_; }
base::TimeDelta detected_period() const { return detected_period_; }
// Records that a frame with the given |frame_timestamp| was sampled. This
// method should be called when *any* sampling is taken, even if it was not
// proposed by AnimatedContentSampler.
void RecordSample(base::TimeTicks frame_timestamp);
private:
friend class AnimatedContentSamplerTest;
// Data structure for efficient online analysis of recent event history.
struct Observation {
gfx::Rect damage_rect;
base::TimeTicks event_time;
Observation(const gfx::Rect& d, base::TimeTicks e)
: damage_rect(d), event_time(e) {}
};
using ObservationFifo = base::circular_deque<Observation>;
// Adds an observation to |observations_|, and prunes-out the old ones.
void AddObservation(const gfx::Rect& damage_rect, base::TimeTicks event_time);
// Returns the damage Rect that is responsible for the majority of the pixel
// damage in recent event history, if there is such a Rect. If there isn't,
// this method could still return any Rect, so the caller must confirm the
// returned Rect really is responsible for the majority of pixel damage.
gfx::Rect ElectMajorityDamageRect() const;
// Analyzes the observations relative to the current |event_time| to detect
// stable animating content. If detected, returns true and sets the output
// arguments to the region of the animating content and its mean frame
// duration.
bool AnalyzeObservations(base::TimeTicks event_time,
gfx::Rect* rect,
base::TimeDelta* period) const;
// Called by ConsiderPresentationEvent() when the current event is part of a
// detected animation, to update |frame_timestamp_|.
base::TimeTicks ComputeNextFrameTimestamp(base::TimeTicks event_time) const;
// When the animation frame rate is greater than the target sampling rate,
// this function determines an integer division of the animation frame rate
// that is closest to the target sampling rate. Returns the inverse of that
// result (the period). If the animation frame rate is slower or the same as
// the target sampling rate, this function just returns |animation_period|.
static base::TimeDelta ComputeSamplingPeriod(
base::TimeDelta animation_period,
base::TimeDelta target_sampling_period,
base::TimeDelta min_capture_period);
// The client expects frame timestamps to be at least this far apart.
base::TimeDelta min_capture_period_;
// A recent history of observations in chronological order, maintained by
// AddObservation().
ObservationFifo observations_;
// The region of currently-detected animated content. If empty, that means
// "not detected."
gfx::Rect detected_region_;
// The mean frame duration of currently-detected animated content. If zero,
// that means "not detected."
base::TimeDelta detected_period_;
// Target period between sampled frames. This can be changed by the client at
// any time (e.g., to sample high frame rate content at a lower rate).
base::TimeDelta target_sampling_period_;
// The sampling period computed during the last call to
// ConsiderPresentationEvent().
base::TimeDelta sampling_period_;
// Indicates whether the last event caused animated content to be detected and
// whether the current event should be sampled.
enum {
NOT_SAMPLING,
START_SAMPLING,
SHOULD_NOT_SAMPLE,
SHOULD_SAMPLE
} sampling_state_;
// A token bucket that is used to decide which subset of the frames containing
// the animated content should be sampled. Here, the smallest discrete unit
// of time (one microsecond) equals one token; and, tokens are only taken from
// the bucket when at least a full sampling period's worth are present.
base::TimeDelta token_bucket_;
// The rewritten frame timestamp for the latest event.
base::TimeTicks frame_timestamp_;
};
} // namespace media
#endif // MEDIA_CAPTURE_CONTENT_ANIMATED_CONTENT_SAMPLER_H_