1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #ifndef CC_METRICS_VIDEO_PLAYBACK_ROUGHNESS_REPORTER_H_
6 #define CC_METRICS_VIDEO_PLAYBACK_ROUGHNESS_REPORTER_H_
8 #include "base/callback.h"
9 #include "base/containers/circular_deque.h"
10 #include "base/containers/flat_set.h"
11 #include "base/time/time.h"
12 #include "cc/cc_export.h"
13 #include "media/base/video_frame.h"
14 #include "media/base/video_types.h"
15 #include "third_party/abseil-cpp/absl/types/optional.h"
16 #include "ui/gfx/geometry/size.h"
20 // This class tracks moments when each frame was submitted
21 // and when it was displayed. Then series of frames split into groups
22 // of consecutive frames, where each group takes about one second of playback.
23 // Such groups also called 'frame windows'. Each windows is assigned a roughness
24 // score that measures how far playback smoothness was from the ideal playback.
26 // Information about several windows and their roughness score is aggregated
27 // for a couple of playback minutes ("measurement interval") and then a window
28 // with 95-percentile-max-roughness is reported via the provided callback.
30 // This sufficiently bad roughness window is deemed to represent overall
32 class CC_EXPORT VideoPlaybackRoughnessReporter {
35 // 95%-worst window measurements ========
36 // These are taken from the |kPercentileToSubmit| worst window in a
37 // measurement interval.
39 // |frames| - number of video frames in the window
42 // |duration| - intended wallclock duration of the window (~1s)
43 base::TimeDelta duration;
45 // |roughness| - roughness of the window
48 // Per-measurement interval measurements ========
49 // These are measured over all windows in the measurement interval, without
50 // regard to which window was chosen above.
52 // |frame_size| - size of the video frames in the window
55 // |freezing| maximum amount of time that any VideoFrame in measurement
56 // interval was on-screen beyond the amount of time it should have been.
58 // TODO(liberato): Should this be expressed in terms of the playback rate?
59 // As in, "twice as long as it should have been"?
60 base::TimeDelta freezing;
62 // |refresh_rate_hz| - display refresh rate, usually 60Hz
63 int refresh_rate_hz = 0;
66 // Callback to report video playback roughness on a particularly bumpy
68 using ReportingCallback = base::RepeatingCallback<void(const Measurement&)>;
70 using TokenType = uint32_t;
71 explicit VideoPlaybackRoughnessReporter(ReportingCallback reporting_cb);
72 VideoPlaybackRoughnessReporter(const VideoPlaybackRoughnessReporter&) =
74 VideoPlaybackRoughnessReporter& operator=(
75 const VideoPlaybackRoughnessReporter&) = delete;
76 ~VideoPlaybackRoughnessReporter();
77 void FrameSubmitted(TokenType token,
78 const media::VideoFrame& frame,
79 base::TimeDelta render_interval);
80 void FramePresented(TokenType token,
81 base::TimeTicks timestamp,
82 bool reliable_timestamp);
83 void ProcessFrameWindow();
86 // A lower bund on how many frames can be in ConsecutiveFramesWindow
87 static constexpr int kMinWindowSize = 6;
89 // An upper bund on how many frames can be in ConsecutiveFramesWindow
90 static constexpr int kMaxWindowSize = 60;
92 // How many frame windows should be observed before reporting smoothness
93 // due to playback time.
94 // 1 second per window, 100 windows. It means smoothness will be reported
95 // for every 100 seconds of playback.
96 static constexpr int kMaxWindowsBeforeSubmit = 100;
98 // How many frame windows should be observed to report soothness on last
99 // time before the destruction of the reporter.
100 static constexpr int kMinWindowsBeforeSubmit = kMaxWindowsBeforeSubmit / 5;
102 // A frame window with this percentile of playback roughness gets reported.
103 // Lower value means more tolerance to rough playback stretches.
104 static constexpr int kPercentileToSubmit = 95;
105 static_assert(kPercentileToSubmit > 0 && kPercentileToSubmit < 100,
106 "invalid percentile value");
109 friend class VideoPlaybackRoughnessReporterTest;
112 FrameInfo(const FrameInfo&);
114 absl::optional<base::TimeTicks> decode_time;
115 absl::optional<base::TimeTicks> presentation_time;
116 absl::optional<base::TimeDelta> actual_duration;
117 absl::optional<base::TimeDelta> intended_duration;
118 int refresh_rate_hz = 60;
122 struct ConsecutiveFramesWindow {
124 base::TimeTicks first_frame_time;
125 base::TimeDelta intended_duration;
126 int refresh_rate_hz = 60;
127 gfx::Size frame_size;
129 // Root-mean-square error of the differences between the intended
130 // duration and the actual duration, calculated for all subwindows
131 // starting at the beginning of the smoothness window
132 // [1-2][1-3][1-4] ... [1-N].
133 base::TimeDelta root_mean_square_error;
135 double roughness() const;
137 bool operator<(const ConsecutiveFramesWindow& rhs) const {
138 double r1 = roughness();
139 double r2 = rhs.roughness();
141 // If roughnesses are equal use window start time as a tie breaker.
142 // We don't want |flat_set worst_windows_| to dedup windows with
143 // the same roughness.
144 return first_frame_time > rhs.first_frame_time;
147 // Reverse sorting order to make sure that better windows go at the
148 // end of |worst_windows_| set. This way it's cheaper to remove them.
153 void ReportWindow(const ConsecutiveFramesWindow& win);
154 void SubmitPlaybackRoughness();
156 base::circular_deque<FrameInfo> frames_;
157 base::flat_set<ConsecutiveFramesWindow> worst_windows_;
158 ReportingCallback reporting_cb_;
159 int windows_seen_ = 0;
160 int frames_window_size_ = kMinWindowSize;
162 // Worst case difference between a frame's intended duration and
163 // actual duration, calculated for all frames in the reporting interval.
164 base::TimeDelta max_single_frame_error_;
169 #endif // CC_METRICS_VIDEO_PLAYBACK_ROUGHNESS_REPORTER_H_