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_