| // Copyright (c) 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef MEDIA_CAPTURE_ANIMATED_CONTENT_SAMPLER_H_ |
| #define MEDIA_CAPTURE_ANIMATED_CONTENT_SAMPLER_H_ |
| |
| #include <deque> |
| |
| #include "base/time/time.h" |
| #include "media/base/media_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 MEDIA_EXPORT AnimatedContentSampler { |
| public: |
| explicit AnimatedContentSampler(base::TimeDelta min_capture_period); |
| ~AnimatedContentSampler(); |
| |
| // 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) {} |
| }; |
| typedef std::deque<Observation> ObservationFifo; |
| |
| // 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. |
| const 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_ANIMATED_CONTENT_SAMPLER_H_ |