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
|
// 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_
|