// 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 CONTENT_BROWSER_MEDIA_CAPTURE_ANIMATED_CONTENT_SAMPLER_H_ #define CONTENT_BROWSER_MEDIA_CAPTURE_ANIMATED_CONTENT_SAMPLER_H_ #include #include "base/time/time.h" #include "content/common/content_export.h" #include "ui/gfx/geometry/rect.h" namespace content { // 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 CONTENT_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 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 content #endif // CONTENT_BROWSER_MEDIA_CAPTURE_ANIMATED_CONTENT_SAMPLER_H_