1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_VIDEO_CAPTURE_ORACLE_H_
6#define CONTENT_BROWSER_MEDIA_CAPTURE_VIDEO_CAPTURE_ORACLE_H_
7
8#include <deque>
9
10#include "base/callback_forward.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/time/time.h"
13#include "content/common/content_export.h"
14#include "ui/gfx/geometry/rect.h"
15
16namespace content {
17
18// Filters a sequence of events to achieve a target frequency.
19class CONTENT_EXPORT SmoothEventSampler {
20 public:
21  SmoothEventSampler(base::TimeDelta min_capture_period,
22                     bool events_are_reliable,
23                     int redundant_capture_goal);
24
25  base::TimeDelta min_capture_period() const { return min_capture_period_; }
26
27  // Add a new event to the event history, and consider whether it ought to be
28  // sampled. The event is not recorded as a sample until RecordSample() is
29  // called.
30  void ConsiderPresentationEvent(base::TimeTicks event_time);
31
32  // Returns true if the last event considered should be sampled.
33  bool ShouldSample() const;
34
35  // Operates on the last event added by ConsiderPresentationEvent(), marking
36  // it as sampled. After this point we are current in the stream of events, as
37  // we have sampled the most recent event.
38  void RecordSample();
39
40  // Returns true if, at time |event_time|, sampling should occur because too
41  // much time will have passed relative to the last event and/or sample.
42  bool IsOverdueForSamplingAt(base::TimeTicks event_time) const;
43
44  // Returns true if ConsiderPresentationEvent() has been called since the last
45  // call to RecordSample().
46  bool HasUnrecordedEvent() const;
47
48 private:
49  const bool events_are_reliable_;
50  const base::TimeDelta min_capture_period_;
51  const int redundant_capture_goal_;
52  const base::TimeDelta token_bucket_capacity_;
53
54  base::TimeTicks current_event_;
55  base::TimeTicks last_sample_;
56  int overdue_sample_count_;
57  base::TimeDelta token_bucket_;
58
59  DISALLOW_COPY_AND_ASSIGN(SmoothEventSampler);
60};
61
62// Analyzes a sequence of events to detect the presence of constant frame rate
63// animated content.  In the case where there are multiple regions of animated
64// content, AnimatedContentSampler will propose sampling the one having the
65// largest "smoothness" impact, according to human perception (e.g., a 24 FPS
66// video versus a 60 FPS busy spinner).
67//
68// In addition, AnimatedContentSampler will provide rewritten frame timestamps,
69// for downstream consumers, that are "truer" to the source content than to the
70// local presentation hardware.
71class CONTENT_EXPORT AnimatedContentSampler {
72 public:
73  explicit AnimatedContentSampler(base::TimeDelta min_capture_period);
74  ~AnimatedContentSampler();
75
76  // Examines the given presentation event metadata, along with recent history,
77  // to detect animated content, updating the state of this sampler.
78  // |damage_rect| is the region of a frame about to be drawn, while
79  // |event_time| refers to the frame's estimated presentation time.
80  void ConsiderPresentationEvent(const gfx::Rect& damage_rect,
81                                 base::TimeTicks event_time);
82
83  // Returns true if animated content has been detected and a decision has been
84  // made about whether to sample the last event.
85  bool HasProposal() const;
86
87  // Returns true if the last event considered should be sampled.
88  bool ShouldSample() const;
89
90  // Returns a frame timestamp to provide to consumers of the sampled frame.
91  // Only valid when should_sample() returns true.
92  base::TimeTicks frame_timestamp() const { return frame_timestamp_; }
93
94  // Accessors to currently-detected animating region/period, for logging.
95  const gfx::Rect& detected_region() const { return detected_region_; }
96  base::TimeDelta detected_period() const { return detected_period_; }
97
98  // Records that a frame with the given |frame_timestamp| was sampled.  This
99  // method should be called when *any* sampling is taken, even if it was not
100  // proposed by AnimatedContentSampler.
101  void RecordSample(base::TimeTicks frame_timestamp);
102
103 private:
104  friend class AnimatedContentSamplerTest;
105
106  // Data structure for efficient online analysis of recent event history.
107  struct Observation {
108    gfx::Rect damage_rect;
109    base::TimeTicks event_time;
110
111    Observation(const gfx::Rect& d, base::TimeTicks e)
112        : damage_rect(d), event_time(e) {}
113  };
114  typedef std::deque<Observation> ObservationFifo;
115
116  // Adds an observation to |observations_|, and prunes-out the old ones.
117  void AddObservation(const gfx::Rect& damage_rect, base::TimeTicks event_time);
118
119  // Returns the damage Rect that is responsible for the majority of the pixel
120  // damage in recent event history, if there is such a Rect.  If there isn't,
121  // this method could still return any Rect, so the caller must confirm the
122  // returned Rect really is responsible for the majority of pixel damage.
123  gfx::Rect ElectMajorityDamageRect() const;
124
125  // Analyzes the observations relative to the current |event_time| to detect
126  // stable animating content.  If detected, returns true and sets the output
127  // arguments to the region of the animating content and its mean frame
128  // duration.
129  bool AnalyzeObservations(base::TimeTicks event_time,
130                           gfx::Rect* rect,
131                           base::TimeDelta* period) const;
132
133  // Called by ConsiderPresentationEvent() when the current event is part of a
134  // detected animation, to update |frame_timestamp_|.
135  void UpdateFrameTimestamp(base::TimeTicks event_time);
136
137  // The client expects frame timestamps to be at least this far apart.
138  const base::TimeDelta min_capture_period_;
139
140  // A recent history of observations in chronological order, maintained by
141  // AddObservation().
142  ObservationFifo observations_;
143
144  // The region of currently-detected animated content.  If empty, that means
145  // "not detected."
146  gfx::Rect detected_region_;
147
148  // The mean frame duration of currently-detected animated content.  If zero,
149  // that means "not detected."
150  base::TimeDelta detected_period_;
151
152  // The rewritten frame timestamp for the latest event.
153  base::TimeTicks frame_timestamp_;
154
155  // The frame timestamp provided in the last call to RecordSample().  This
156  // timestamp may or may not have been one proposed by AnimatedContentSampler.
157  base::TimeTicks recorded_frame_timestamp_;
158
159  // Accumulates all the time advancements since the last call to
160  // RecordSample().  When this is greater than zero, there have been one or
161  // more events proposed for sampling, but not yet recorded.  This accounts for
162  // the cases where AnimatedContentSampler indicates a frame should be sampled,
163  // but the client chooses not to do so.
164  base::TimeDelta sequence_offset_;
165
166  // A token bucket that is used to decide which frames to drop whenever
167  // |detected_period_| is less than |min_capture_period_|.
168  base::TimeDelta borrowed_time_;
169};
170
171// VideoCaptureOracle manages the producer-side throttling of captured frames
172// from a video capture device.  It is informed of every update by the device;
173// this empowers it to look into the future and decide if a particular frame
174// ought to be captured in order to achieve its target frame rate.
175class CONTENT_EXPORT VideoCaptureOracle {
176 public:
177  enum Event {
178    kTimerPoll,
179    kCompositorUpdate,
180    kSoftwarePaint,
181    kNumEvents,
182  };
183
184  VideoCaptureOracle(base::TimeDelta min_capture_period,
185                     bool events_are_reliable);
186  virtual ~VideoCaptureOracle();
187
188  // Record a event of type |event|, and decide whether the caller should do a
189  // frame capture.  |damage_rect| is the region of a frame about to be drawn,
190  // and may be an empty Rect, if this is not known.  If the caller accepts the
191  // oracle's proposal, it should call RecordCapture() to indicate this.
192  bool ObserveEventAndDecideCapture(Event event,
193                                    const gfx::Rect& damage_rect,
194                                    base::TimeTicks event_time);
195
196  // Record the start of a capture.  Returns a frame_number to be used with
197  // CompleteCapture().
198  int RecordCapture();
199
200  // Notify of the completion of a capture.  Returns true iff the captured frame
201  // should be delivered.  |frame_timestamp| is set to the timestamp that should
202  // be provided to the consumer of the frame.
203  bool CompleteCapture(int frame_number, base::TimeTicks* frame_timestamp);
204
205  base::TimeDelta min_capture_period() const {
206    return smoothing_sampler_.min_capture_period();
207  }
208
209 private:
210  // Retrieve/Assign a frame timestamp by capture |frame_number|.
211  base::TimeTicks GetFrameTimestamp(int frame_number) const;
212  void SetFrameTimestamp(int frame_number, base::TimeTicks timestamp);
213
214  // Incremented every time a paint or update event occurs.
215  int frame_number_;
216
217  // Stores the last |event_time| from the last observation/decision.  Used to
218  // sanity-check that event times are monotonically non-decreasing.
219  base::TimeTicks last_event_time_[kNumEvents];
220
221  // Stores the frame number from the last delivered frame.
222  int last_delivered_frame_number_;
223
224  // These track present/paint history and propose whether to sample each event
225  // for capture.  |smoothing_sampler_| uses a "works for all" heuristic, while
226  // |content_sampler_| specifically detects animated content (e.g., video
227  // playback) and decides which events to sample to "lock into" that content.
228  SmoothEventSampler smoothing_sampler_;
229  AnimatedContentSampler content_sampler_;
230
231  // Recent history of frame timestamps proposed by VideoCaptureOracle.  This is
232  // a ring-buffer, and should only be accessed by the Get/SetFrameTimestamp()
233  // methods.
234  enum { kMaxFrameTimestamps = 16 };
235  base::TimeTicks frame_timestamps_[kMaxFrameTimestamps];
236};
237
238}  // namespace content
239
240#endif  // CONTENT_BROWSER_MEDIA_CAPTURE_VIDEO_CAPTURE_ORACLE_H_
241