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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
cc / metrics / video_playback_roughness_reporter.h [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CC_METRICS_VIDEO_PLAYBACK_ROUGHNESS_REPORTER_H_
#define CC_METRICS_VIDEO_PLAYBACK_ROUGHNESS_REPORTER_H_
#include <optional>
#include "base/containers/circular_deque.h"
#include "base/containers/flat_set.h"
#include "base/functional/callback.h"
#include "base/time/time.h"
#include "cc/cc_export.h"
#include "media/base/video_frame.h"
#include "media/base/video_types.h"
#include "ui/gfx/geometry/size.h"
namespace cc {
// This class tracks moments when each frame was submitted
// and when it was displayed. Then series of frames split into groups
// of consecutive frames, where each group takes about one second of playback.
// Such groups also called 'frame windows'. Each windows is assigned a roughness
// score that measures how far playback smoothness was from the ideal playback.
//
// Information about several windows and their roughness score is aggregated
// for a couple of playback minutes ("measurement interval") and then a window
// with 95-percentile-max-roughness is reported via the provided callback.
//
// This sufficiently bad roughness window is deemed to represent overall
// playback quality.
class CC_EXPORT VideoPlaybackRoughnessReporter {
public:
struct Measurement {
// 95%-worst window measurements ========
// These are taken from the |kPercentileToSubmit| worst window in a
// measurement interval.
// |frames| - number of video frames in the window
int frames = 0;
// |duration| - intended wallclock duration of the window (~1s)
base::TimeDelta duration;
// |roughness| - roughness of the window
double roughness = 0;
// Per-measurement interval measurements ========
// These are measured over all windows in the measurement interval, without
// regard to which window was chosen above.
// |frame_size| - size of the video frames in the window
gfx::Size frame_size;
// |freezing| maximum amount of time that any VideoFrame in measurement
// interval was on-screen beyond the amount of time it should have been.
//
// TODO(liberato): Should this be expressed in terms of the playback rate?
// As in, "twice as long as it should have been"?
base::TimeDelta freezing;
// |refresh_rate_hz| - display refresh rate, usually 60Hz
int refresh_rate_hz = 0;
};
// Callback to report video playback roughness on a particularly bumpy
// interval.
using ReportingCallback = base::RepeatingCallback<void(const Measurement&)>;
using TokenType = uint32_t;
explicit VideoPlaybackRoughnessReporter(ReportingCallback reporting_cb);
VideoPlaybackRoughnessReporter(const VideoPlaybackRoughnessReporter&) =
delete;
VideoPlaybackRoughnessReporter& operator=(
const VideoPlaybackRoughnessReporter&) = delete;
~VideoPlaybackRoughnessReporter();
void FrameSubmitted(TokenType token,
const media::VideoFrame& frame,
base::TimeDelta render_interval);
void FramePresented(TokenType token,
base::TimeTicks timestamp,
bool reliable_timestamp);
void ProcessFrameWindow();
void Reset();
void set_is_media_stream(bool is_media_stream) {
is_media_stream_ = is_media_stream;
}
// A lower bound on how many frames can be in ConsecutiveFramesWindow
static constexpr int kMinWindowSize = 6;
// An upper bound on how many frames can be in ConsecutiveFramesWindow
static constexpr int kMaxWindowSize = 60;
// How many frame windows should be observed before reporting smoothness
// due to playback time.
// 1 second per window, 100 windows. It means smoothness will be reported
// for every 100 seconds of playback.
static constexpr int kMaxWindowsBeforeSubmit = 100;
// How many frame windows should be observed to report soothness on last
// time before the destruction of the reporter.
static constexpr int kMinWindowsBeforeSubmit = kMaxWindowsBeforeSubmit / 5;
// A frame window with this percentile of playback roughness gets reported.
// Lower value means more tolerance to rough playback stretches.
static constexpr int kPercentileToSubmit = 95;
static_assert(kPercentileToSubmit > 0 && kPercentileToSubmit < 100,
"invalid percentile value");
private:
friend class VideoPlaybackRoughnessReporterTest;
struct FrameInfo {
FrameInfo();
FrameInfo(const FrameInfo&);
TokenType token = 0;
std::optional<base::TimeTicks> decode_time;
std::optional<base::TimeTicks> presentation_time;
std::optional<base::TimeDelta> actual_duration;
std::optional<base::TimeDelta> intended_duration;
int refresh_rate_hz = 60;
gfx::Size size;
};
struct ConsecutiveFramesWindow {
int size;
base::TimeTicks first_frame_time;
base::TimeDelta intended_duration;
int refresh_rate_hz = 60;
gfx::Size frame_size;
// Root-mean-square error of the differences between the intended
// duration and the actual duration, calculated for all subwindows
// starting at the beginning of the smoothness window
// [1-2][1-3][1-4] ... [1-N].
base::TimeDelta root_mean_square_error;
double roughness() const;
bool operator<(const ConsecutiveFramesWindow& rhs) const {
double r1 = roughness();
double r2 = rhs.roughness();
if (r1 == r2) {
// If roughnesses are equal use window start time as a tie breaker.
// We don't want |flat_set worst_windows_| to dedup windows with
// the same roughness.
return first_frame_time > rhs.first_frame_time;
}
// Reverse sorting order to make sure that better windows go at the
// end of |worst_windows_| set. This way it's cheaper to remove them.
return r1 > r2;
}
};
void ReportWindow(const ConsecutiveFramesWindow& win);
void SubmitPlaybackRoughness();
base::circular_deque<FrameInfo> frames_;
base::flat_set<ConsecutiveFramesWindow> worst_windows_;
ReportingCallback reporting_cb_;
int windows_seen_ = 0;
int frames_window_size_ = kMinWindowSize;
// Worst case difference between a frame's intended duration and
// actual duration, calculated for all frames in the reporting interval.
base::TimeDelta max_single_frame_error_;
bool is_media_stream_ = false;
};
} // namespace cc
#endif // CC_METRICS_VIDEO_PLAYBACK_ROUGHNESS_REPORTER_H_