Commit bea39391 authored by Eugene Zemtsov's avatar Eugene Zemtsov Committed by Commit Bot

Implementation of VideoPlaybackRoughnessReporter

VideoPlaybackRoughnessReporter tracks moments when each frame was
submitted and when it was displayed. Then series of frames are
split into groups of consecutive frames, where each group takes
about 1/2 second of palyback.

Such groups are 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 and then a window with
95-percentile-max-roughness is reported via the provided callback.

Currently roughness is not reported via any server side metrics.
Integration with UMA and UKM coming soon.

Bug: 1042111
Change-Id: I5a12e400f00f91fc0b63d15e88bca5c0ab09d5f2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2097951
Commit-Queue: Eugene Zemtsov <eugene@chromium.org>
Reviewed-by: default avatarDavid Bokan <bokan@chromium.org>
Reviewed-by: default avatarFrank Liberato <liberato@chromium.org>
Cr-Commit-Position: refs/heads/master@{#750823}
parent f7277fef
...@@ -167,6 +167,8 @@ cc_component("cc") { ...@@ -167,6 +167,8 @@ cc_component("cc") {
"metrics/latency_ukm_reporter.h", "metrics/latency_ukm_reporter.h",
"metrics/throughput_ukm_reporter.cc", "metrics/throughput_ukm_reporter.cc",
"metrics/throughput_ukm_reporter.h", "metrics/throughput_ukm_reporter.h",
"metrics/video_playback_roughness_reporter.cc",
"metrics/video_playback_roughness_reporter.h",
"raster/bitmap_raster_buffer_provider.cc", "raster/bitmap_raster_buffer_provider.cc",
"raster/bitmap_raster_buffer_provider.h", "raster/bitmap_raster_buffer_provider.h",
"raster/gpu_raster_buffer_provider.cc", "raster/gpu_raster_buffer_provider.cc",
...@@ -655,6 +657,7 @@ cc_test("cc_unittests") { ...@@ -655,6 +657,7 @@ cc_test("cc_unittests") {
"metrics/events_metrics_manager_unittest.cc", "metrics/events_metrics_manager_unittest.cc",
"metrics/frame_sequence_metrics_unittest.cc", "metrics/frame_sequence_metrics_unittest.cc",
"metrics/frame_sequence_tracker_unittest.cc", "metrics/frame_sequence_tracker_unittest.cc",
"metrics/video_playback_roughness_reporter_unittest.cc",
"mojo_embedder/async_layer_tree_frame_sink_unittest.cc", "mojo_embedder/async_layer_tree_frame_sink_unittest.cc",
"paint/discardable_image_map_unittest.cc", "paint/discardable_image_map_unittest.cc",
"paint/display_item_list_unittest.cc", "paint/display_item_list_unittest.cc",
......
// Copyright 2020 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.
#include "cc/metrics/video_playback_roughness_reporter.h"
#include "base/bind_helpers.h"
#include "base/metrics/histogram_macros.h"
#include "components/viz/common/quads/compositor_frame_metadata.h"
namespace {
constexpr int max_worst_windows_size() {
constexpr int size =
1 + cc::VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit *
(100 - cc::VideoPlaybackRoughnessReporter::kPercentileToSubmit) /
100;
static_assert(size > 1, "worst_windows_ is too small");
static_assert(size < 25, "worst_windows_ is too big");
return size;
}
} // namespace
namespace cc {
constexpr int VideoPlaybackRoughnessReporter::kMinWindowSize;
constexpr int VideoPlaybackRoughnessReporter::kMaxWindowSize;
constexpr int VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit;
constexpr int VideoPlaybackRoughnessReporter::kMinWindowsBeforeSubmit;
constexpr int VideoPlaybackRoughnessReporter::kPercentileToSubmit;
VideoPlaybackRoughnessReporter::VideoPlaybackRoughnessReporter(
PlaybackRoughnessReportingCallback reporting_cb)
: reporting_cb_(reporting_cb) {}
VideoPlaybackRoughnessReporter::~VideoPlaybackRoughnessReporter() = default;
double VideoPlaybackRoughnessReporter::ConsecutiveFramesWindow::roughness()
const {
return root_mean_square_error.InMicrosecondsF() /
intended_duration.InMicrosecondsF();
}
VideoPlaybackRoughnessReporter::FrameInfo::FrameInfo() = default;
VideoPlaybackRoughnessReporter::FrameInfo::FrameInfo(const FrameInfo&) =
default;
void VideoPlaybackRoughnessReporter::FrameSubmitted(
TokenType token,
const media::VideoFrame& frame,
base::TimeDelta render_interval) {
if (!frames_.empty() && viz::FrameTokenGT(frames_.back().token, token)) {
DCHECK(false) << "Frames submitted out of order.";
return;
}
FrameInfo info;
info.token = token;
info.decode_time.emplace();
if (!frame.metadata()->GetTimeTicks(
media::VideoFrameMetadata::DECODE_END_TIME,
&info.decode_time.value())) {
info.decode_time.reset();
}
info.intended_duration.emplace();
if (frame.metadata()->GetTimeDelta(
media::VideoFrameMetadata::WALLCLOCK_FRAME_DURATION,
&info.intended_duration.value())) {
if (render_interval > info.intended_duration.value()) {
// In videos with FPS higher than display refresh rate we acknowledge
// the fact that some frames will be dropped upstream and frame's intended
// duration can't be less than refresh interval.
info.intended_duration = render_interval;
}
// Adjust frame window size to fit about 0.5 seconds of playback
double win_size =
std::round(0.5 / info.intended_duration.value().InSecondsF());
frames_window_size_ = std::max(kMinWindowSize, static_cast<int>(win_size));
frames_window_size_ = std::min(frames_window_size_, kMaxWindowSize);
} else {
info.intended_duration.reset();
}
frames_.push_back(info);
}
void VideoPlaybackRoughnessReporter::FramePresented(TokenType token,
base::TimeTicks timestamp) {
for (auto it = frames_.rbegin(); it != frames_.rend(); it++) {
FrameInfo& info = *it;
if (token == it->token) {
info.presentation_time = timestamp;
if (info.decode_time.has_value()) {
auto time_since_decode = timestamp - info.decode_time.value();
UMA_HISTOGRAM_TIMES("Media.VideoFrameSubmitter", time_since_decode);
}
break;
}
if (viz::FrameTokenGT(token, it->token))
break;
}
}
void VideoPlaybackRoughnessReporter::SubmitPlaybackRoughness() {
// 0-based index, how many times to step away from the begin().
int index_to_submit = windows_seen_ * (100 - kPercentileToSubmit) / 100;
if (index_to_submit < 0 ||
index_to_submit >= static_cast<int>(worst_windows_.size())) {
DCHECK(false);
return;
}
auto it = worst_windows_.begin() + index_to_submit;
reporting_cb_.Run(it->size, it->intended_duration, it->roughness());
worst_windows_.clear();
windows_seen_ = 0;
}
void VideoPlaybackRoughnessReporter::ReportWindow(
const ConsecutiveFramesWindow& win) {
if (win.max_single_frame_error >= win.intended_duration ||
win.root_mean_square_error >= win.intended_duration) {
// Most likely it's just an effect of the video being paused for some time.
return;
}
worst_windows_.insert(win);
if (worst_windows_.size() > max_worst_windows_size())
worst_windows_.erase(std::prev(worst_windows_.end()));
windows_seen_++;
if (windows_seen_ >= kMaxWindowsBeforeSubmit)
SubmitPlaybackRoughness();
}
void VideoPlaybackRoughnessReporter::ProcessFrameWindow() {
if (static_cast<int>(frames_.size()) <= frames_window_size_) {
// There is no window to speak of, let's wait and process it later.
return;
}
// If possible populate duration for frames that don't have it yet.
auto cur_frame_it = frames_.begin();
auto next_frame_it = std::next(cur_frame_it);
for (; next_frame_it != frames_.end(); cur_frame_it++, next_frame_it++) {
FrameInfo& cur_frame = *cur_frame_it;
const FrameInfo& next_frame = *next_frame_it;
if (cur_frame.actual_duration.has_value())
continue;
if (!cur_frame.presentation_time.has_value() ||
!next_frame.presentation_time.has_value()) {
// We reached a frame that hasn't been presented yet, there is
// no way to keep processing the window.
break;
}
cur_frame.actual_duration = next_frame.presentation_time.value() -
cur_frame.presentation_time.value();
}
int items_to_discard = 0;
const int max_buffer_size = 2 * frames_window_size_;
// There is sufficient number of frames with populated |actual_duration|
// let's calculate window metrics and report it.
if (next_frame_it - frames_.begin() > frames_window_size_) {
ConsecutiveFramesWindow win;
double mean_square_error_ms2 = 0.0;
base::TimeDelta total_error;
if (frames_.front().presentation_time.has_value())
win.first_frame_time = frames_.front().presentation_time.value();
for (auto i = 0; i < frames_window_size_; i++) {
FrameInfo& frame = frames_[i];
base::TimeDelta error;
if (frame.actual_duration.has_value() &&
frame.intended_duration.has_value()) {
error = frame.actual_duration.value() - frame.intended_duration.value();
win.intended_duration += frame.intended_duration.value();
}
total_error += error;
win.max_single_frame_error =
std::max(win.max_single_frame_error, error.magnitude());
mean_square_error_ms2 +=
total_error.InMillisecondsF() * total_error.InMillisecondsF();
}
win.size = frames_window_size_;
win.root_mean_square_error = base::TimeDelta::FromMillisecondsD(
std::sqrt(mean_square_error_ms2 / frames_window_size_));
ReportWindow(win);
// The frames in the window have been reported,
// no need to keep them around any longer.
items_to_discard = frames_window_size_;
} else if (static_cast<int>(frames_.size()) > max_buffer_size) {
// |frames_| grew too much, because apparently we're not getting consistent
// FramePresented() calls and no smoothness windows can be reported.
// Nevertheless, we can't allow |frames_| to grow too big, let's drop
// the oldest items beyond |max_buffer_size|;
items_to_discard = frames_.size() - max_buffer_size;
}
frames_.erase(frames_.begin(), frames_.begin() + items_to_discard);
}
void VideoPlaybackRoughnessReporter::Reset() {
if (windows_seen_ >= kMinWindowsBeforeSubmit)
SubmitPlaybackRoughness();
frames_.clear();
worst_windows_.clear();
windows_seen_ = 0;
}
} // namespace cc
// Copyright 2020 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 CC_METRICS_VIDEO_PLAYBACK_ROUGHNESS_REPORTER_H_
#define CC_METRICS_VIDEO_PLAYBACK_ROUGHNESS_REPORTER_H_
#include "base/callback.h"
#include "base/containers/circular_deque.h"
#include "base/containers/flat_set.h"
#include "base/optional.h"
#include "base/time/time.h"
#include "cc/cc_export.h"
#include "media/base/video_frame.h"
#include "media/base/video_types.h"
namespace cc {
// Callback to report video playback roughness on a particularly bumpy interval.
// |frames| - number of frames in the interval
// |duration| - intended wallclock duration of the interval
// |roughness| - roughness of the interval
using PlaybackRoughnessReportingCallback = base::RepeatingCallback<
void(int frames, base::TimeDelta duration, double roughness)>;
// 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 1/2 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 playbackminutes 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:
using TokenType = uint32_t;
explicit VideoPlaybackRoughnessReporter(
PlaybackRoughnessReportingCallback 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);
void ProcessFrameWindow();
void Reset();
// A lower bund on how many frames can be in ConsecutiveFramesWindow
static constexpr int kMinWindowSize = 6;
// An upper bund on how many frames can be in ConsecutiveFramesWindow
static constexpr int kMaxWindowSize = 40;
// How many frame windows should be observed before reporting smoothness
// due to playback time.
// 1/2 a second per window, 200 windows. It means smoothness will be reported
// for every 100 seconds of playback.
static constexpr int kMaxWindowsBeforeSubmit = 200;
// 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;
base::Optional<base::TimeTicks> decode_time;
base::Optional<base::TimeTicks> presentation_time;
base::Optional<base::TimeDelta> actual_duration;
base::Optional<base::TimeDelta> intended_duration;
};
struct ConsecutiveFramesWindow {
int size;
base::TimeTicks first_frame_time;
base::TimeDelta intended_duration;
// Worst case difference between a frame's intended duration and
// actual duration, calculated for all frames in the window.
base::TimeDelta max_single_frame_error;
// 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_;
PlaybackRoughnessReportingCallback reporting_cb_;
int windows_seen_ = 0;
int frames_window_size_ = kMinWindowSize;
};
} // namespace cc
#endif // CC_METRICS_VIDEO_PLAYBACK_ROUGHNESS_REPORTER_H_
This diff is collapsed.
...@@ -147,6 +147,11 @@ void PostContextProviderToCallback( ...@@ -147,6 +147,11 @@ void PostContextProviderToCallback(
unwanted_context_provider)); unwanted_context_provider));
} }
void LogRoughness(int size, base::TimeDelta duration, double roughness) {
double fps = size / duration.InSecondsF();
DVLOG(1) << "Video playback roughness: " << roughness << " FPS: " << fps;
}
} // namespace } // namespace
namespace content { namespace content {
...@@ -266,7 +271,7 @@ std::unique_ptr<blink::WebVideoFrameSubmitter> MediaFactory::CreateSubmitter( ...@@ -266,7 +271,7 @@ std::unique_ptr<blink::WebVideoFrameSubmitter> MediaFactory::CreateSubmitter(
base::BindRepeating( base::BindRepeating(
&PostContextProviderToCallback, &PostContextProviderToCallback,
RenderThreadImpl::current()->GetCompositorMainThreadTaskRunner()), RenderThreadImpl::current()->GetCompositorMainThreadTaskRunner()),
settings, use_sync_primitives); base::BindRepeating(LogRoughness), settings, use_sync_primitives);
} }
DCHECK(*video_frame_compositor_task_runner); DCHECK(*video_frame_compositor_task_runner);
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_VIDEO_FRAME_SUBMITTER_H_ #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_VIDEO_FRAME_SUBMITTER_H_
#include "cc/layers/video_frame_provider.h" #include "cc/layers/video_frame_provider.h"
#include "cc/metrics/video_playback_roughness_reporter.h"
#include "components/viz/common/surfaces/surface_id.h" #include "components/viz/common/surfaces/surface_id.h"
#include "third_party/blink/public/platform/web_common.h" #include "third_party/blink/public/platform/web_common.h"
...@@ -41,6 +42,7 @@ class BLINK_PLATFORM_EXPORT WebVideoFrameSubmitter ...@@ -41,6 +42,7 @@ class BLINK_PLATFORM_EXPORT WebVideoFrameSubmitter
public: public:
static std::unique_ptr<WebVideoFrameSubmitter> Create( static std::unique_ptr<WebVideoFrameSubmitter> Create(
WebContextProviderCallback, WebContextProviderCallback,
cc::PlaybackRoughnessReportingCallback,
const cc::LayerTreeSettings&, const cc::LayerTreeSettings&,
bool use_sync_primitives); bool use_sync_primitives);
~WebVideoFrameSubmitter() override = default; ~WebVideoFrameSubmitter() override = default;
......
...@@ -26,10 +26,12 @@ namespace blink { ...@@ -26,10 +26,12 @@ namespace blink {
std::unique_ptr<WebVideoFrameSubmitter> WebVideoFrameSubmitter::Create( std::unique_ptr<WebVideoFrameSubmitter> WebVideoFrameSubmitter::Create(
WebContextProviderCallback context_provider_callback, WebContextProviderCallback context_provider_callback,
cc::PlaybackRoughnessReportingCallback roughness_reporting_callback,
const cc::LayerTreeSettings& settings, const cc::LayerTreeSettings& settings,
bool use_sync_primitives) { bool use_sync_primitives) {
return std::make_unique<VideoFrameSubmitter>( return std::make_unique<VideoFrameSubmitter>(
std::move(context_provider_callback), std::move(context_provider_callback),
std::move(roughness_reporting_callback),
std::make_unique<VideoFrameResourceProvider>(settings, std::make_unique<VideoFrameResourceProvider>(settings,
use_sync_primitives)); use_sync_primitives));
} }
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/sequenced_task_runner_handle.h"
#include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event.h"
#include "cc/metrics/video_playback_roughness_reporter.h"
#include "components/viz/common/resources/resource_id.h" #include "components/viz/common/resources/resource_id.h"
#include "components/viz/common/resources/returned_resource.h" #include "components/viz/common/resources/returned_resource.h"
#include "media/base/video_frame.h" #include "media/base/video_frame.h"
...@@ -28,10 +29,13 @@ namespace blink { ...@@ -28,10 +29,13 @@ namespace blink {
VideoFrameSubmitter::VideoFrameSubmitter( VideoFrameSubmitter::VideoFrameSubmitter(
WebContextProviderCallback context_provider_callback, WebContextProviderCallback context_provider_callback,
cc::PlaybackRoughnessReportingCallback roughness_reporting_callback,
std::unique_ptr<VideoFrameResourceProvider> resource_provider) std::unique_ptr<VideoFrameResourceProvider> resource_provider)
: context_provider_callback_(context_provider_callback), : context_provider_callback_(context_provider_callback),
resource_provider_(std::move(resource_provider)), resource_provider_(std::move(resource_provider)),
rotation_(media::VIDEO_ROTATION_0), rotation_(media::VIDEO_ROTATION_0),
roughness_reporter_(std::make_unique<cc::VideoPlaybackRoughnessReporter>(
std::move(roughness_reporting_callback))),
frame_trackers_(false, nullptr) { frame_trackers_(false, nullptr) {
DETACH_FROM_THREAD(thread_checker_); DETACH_FROM_THREAD(thread_checker_);
} }
...@@ -72,6 +76,7 @@ void VideoFrameSubmitter::StopRendering() { ...@@ -72,6 +76,7 @@ void VideoFrameSubmitter::StopRendering() {
is_rendering_ = false; is_rendering_ = false;
frame_trackers_.StopSequence(cc::FrameSequenceTrackerType::kVideo); frame_trackers_.StopSequence(cc::FrameSequenceTrackerType::kVideo);
roughness_reporter_->Reset();
UpdateSubmissionState(); UpdateSubmissionState();
} }
...@@ -182,34 +187,38 @@ void VideoFrameSubmitter::OnBeginFrame( ...@@ -182,34 +187,38 @@ void VideoFrameSubmitter::OnBeginFrame(
if (viz::FrameTokenGT(pair.key, *next_frame_token_)) if (viz::FrameTokenGT(pair.key, *next_frame_token_))
continue; continue;
if (base::Contains(frame_token_to_timestamp_map_, pair.key) && #ifdef OS_LINUX
!(pair.value->presentation_feedback->flags & // TODO: On Linux failure flag is unreliable, and perfectly rendered frames
gfx::PresentationFeedback::kFailure)) { // are reported as failures all the time.
if (!ignorable_submitted_frames_.contains(pair.key)) { bool presentation_failure = false;
frame_trackers_.NotifyFramePresented( #else
pair.key, gfx::PresentationFeedback( bool presentation_failure = !!(pair.value->presentation_feedback->flags &
pair.value->presentation_feedback->timestamp, gfx::PresentationFeedback::kFailure);
pair.value->presentation_feedback->interval, #endif
pair.value->presentation_feedback->flags)); if (!presentation_failure &&
} !ignorable_submitted_frames_.contains(pair.key)) {
UMA_HISTOGRAM_TIMES("Media.VideoFrameSubmitter", frame_trackers_.NotifyFramePresented(
pair.value->presentation_feedback->timestamp - pair.key, gfx::PresentationFeedback(
frame_token_to_timestamp_map_[pair.key]); pair.value->presentation_feedback->timestamp,
frame_token_to_timestamp_map_.erase(pair.key); pair.value->presentation_feedback->interval,
pair.value->presentation_feedback->flags));
roughness_reporter_->FramePresented(
pair.key, pair.value->presentation_feedback->timestamp);
} }
ignorable_submitted_frames_.erase(pair.key); ignorable_submitted_frames_.erase(pair.key);
TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0( TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
"media", "VideoFrameSubmitter", TRACE_ID_LOCAL(pair.key), "media", "VideoFrameSubmitter", TRACE_ID_LOCAL(pair.key),
pair.value->presentation_feedback->timestamp); pair.value->presentation_feedback->timestamp);
} }
frame_trackers_.NotifyBeginImplFrame(args); frame_trackers_.NotifyBeginImplFrame(args);
base::ScopedClosureRunner end_frame( base::ScopedClosureRunner end_frame(
base::BindOnce(&cc::FrameSequenceTrackerCollection::NotifyFrameEnd, base::BindOnce(&cc::FrameSequenceTrackerCollection::NotifyFrameEnd,
base::Unretained(&frame_trackers_), args, args)); base::Unretained(&frame_trackers_), args, args));
base::ScopedClosureRunner roughness_processing(
base::BindOnce(&cc::VideoPlaybackRoughnessReporter::ProcessFrameWindow,
base::Unretained(roughness_reporter_.get())));
// Don't call UpdateCurrentFrame() for MISSED BeginFrames. Also don't call it // Don't call UpdateCurrentFrame() for MISSED BeginFrames. Also don't call it
// after StopRendering() has been called (forbidden by API contract). // after StopRendering() has been called (forbidden by API contract).
...@@ -443,8 +452,15 @@ bool VideoFrameSubmitter::SubmitFrame( ...@@ -443,8 +452,15 @@ bool VideoFrameSubmitter::SubmitFrame(
frame_size_ = frame_size; frame_size_ = frame_size;
} }
auto compositor_frame = auto frame_token = ++next_frame_token_;
CreateCompositorFrame(begin_frame_ack, std::move(video_frame)); auto source_id = begin_frame_ack.frame_id.source_id;
if (source_id != viz::BeginFrameArgs::kManualSourceId) {
// Roughness reporter only cares about true video frames.
roughness_reporter_->FrameSubmitted(frame_token, *video_frame.get(),
last_begin_frame_args_.interval);
}
auto compositor_frame = CreateCompositorFrame(frame_token, begin_frame_ack,
std::move(video_frame));
WebVector<viz::ResourceId> resources; WebVector<viz::ResourceId> resources;
const auto& quad_list = compositor_frame.render_pass_list.back()->quad_list; const auto& quad_list = compositor_frame.render_pass_list.back()->quad_list;
...@@ -459,7 +475,6 @@ bool VideoFrameSubmitter::SubmitFrame( ...@@ -459,7 +475,6 @@ bool VideoFrameSubmitter::SubmitFrame(
// We can pass nullptr for the HitTestData as the CompositorFram will not // We can pass nullptr for the HitTestData as the CompositorFram will not
// contain any SurfaceDrawQuads. // contain any SurfaceDrawQuads.
auto frame_token = compositor_frame.metadata.frame_token;
compositor_frame_sink_->SubmitCompositorFrame( compositor_frame_sink_->SubmitCompositorFrame(
child_local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation() child_local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation()
.local_surface_id(), .local_surface_id(),
...@@ -485,9 +500,10 @@ void VideoFrameSubmitter::SubmitEmptyFrame() { ...@@ -485,9 +500,10 @@ void VideoFrameSubmitter::SubmitEmptyFrame() {
last_frame_id_.reset(); last_frame_id_.reset();
auto begin_frame_ack = viz::BeginFrameAck::CreateManualAckWithDamage(); auto begin_frame_ack = viz::BeginFrameAck::CreateManualAckWithDamage();
auto compositor_frame = CreateCompositorFrame(begin_frame_ack, nullptr); auto frame_token = ++next_frame_token_;
auto compositor_frame =
CreateCompositorFrame(frame_token, begin_frame_ack, nullptr);
auto frame_token = compositor_frame.metadata.frame_token;
compositor_frame_sink_->SubmitCompositorFrame( compositor_frame_sink_->SubmitCompositorFrame(
child_local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation() child_local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation()
.local_surface_id(), .local_surface_id(),
...@@ -526,13 +542,14 @@ bool VideoFrameSubmitter::ShouldSubmit() const { ...@@ -526,13 +542,14 @@ bool VideoFrameSubmitter::ShouldSubmit() const {
} }
viz::CompositorFrame VideoFrameSubmitter::CreateCompositorFrame( viz::CompositorFrame VideoFrameSubmitter::CreateCompositorFrame(
uint32_t frame_token,
const viz::BeginFrameAck& begin_frame_ack, const viz::BeginFrameAck& begin_frame_ack,
scoped_refptr<media::VideoFrame> video_frame) { scoped_refptr<media::VideoFrame> video_frame) {
DCHECK(!frame_size_.IsEmpty()); DCHECK(!frame_size_.IsEmpty());
viz::CompositorFrame compositor_frame; viz::CompositorFrame compositor_frame;
compositor_frame.metadata.begin_frame_ack = begin_frame_ack; compositor_frame.metadata.begin_frame_ack = begin_frame_ack;
compositor_frame.metadata.frame_token = ++next_frame_token_; compositor_frame.metadata.frame_token = frame_token;
compositor_frame.metadata.preferred_frame_interval = compositor_frame.metadata.preferred_frame_interval =
video_frame_provider_ video_frame_provider_
? video_frame_provider_->GetPreferredRenderInterval() ? video_frame_provider_->GetPreferredRenderInterval()
...@@ -542,25 +559,21 @@ viz::CompositorFrame VideoFrameSubmitter::CreateCompositorFrame( ...@@ -542,25 +559,21 @@ viz::CompositorFrame VideoFrameSubmitter::CreateCompositorFrame(
if (video_frame && video_frame->metadata()->GetTimeTicks( if (video_frame && video_frame->metadata()->GetTimeTicks(
media::VideoFrameMetadata::DECODE_END_TIME, &value)) { media::VideoFrameMetadata::DECODE_END_TIME, &value)) {
TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0( TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
"media", "VideoFrameSubmitter", TRACE_ID_LOCAL(*next_frame_token_), "media", "VideoFrameSubmitter", TRACE_ID_LOCAL(frame_token), value);
value);
TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0( TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
"media", "Pre-submit buffering", TRACE_ID_LOCAL(*next_frame_token_), "media", "Pre-submit buffering", TRACE_ID_LOCAL(frame_token), value);
value);
TRACE_EVENT_NESTABLE_ASYNC_END0("media", "Pre-submit buffering", TRACE_EVENT_NESTABLE_ASYNC_END0("media", "Pre-submit buffering",
TRACE_ID_LOCAL(*next_frame_token_)); TRACE_ID_LOCAL(frame_token));
frame_token_to_timestamp_map_[*next_frame_token_] = value;
if (begin_frame_ack.frame_id.source_id == if (begin_frame_ack.frame_id.source_id ==
viz::BeginFrameArgs::kManualSourceId) viz::BeginFrameArgs::kManualSourceId)
ignorable_submitted_frames_.insert(*next_frame_token_); ignorable_submitted_frames_.insert(frame_token);
UMA_HISTOGRAM_TIMES("Media.VideoFrameSubmitter.PreSubmitBuffering", UMA_HISTOGRAM_TIMES("Media.VideoFrameSubmitter.PreSubmitBuffering",
base::TimeTicks::Now() - value); base::TimeTicks::Now() - value);
} else { } else {
TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("media", "VideoFrameSubmitter", TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("media", "VideoFrameSubmitter",
TRACE_ID_LOCAL(*next_frame_token_), TRACE_ID_LOCAL(frame_token),
"empty video frame?", !video_frame); "empty video frame?", !video_frame);
} }
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "cc/metrics/frame_sequence_tracker.h" #include "cc/metrics/frame_sequence_tracker.h"
#include "cc/metrics/video_playback_roughness_reporter.h"
#include "components/viz/client/shared_bitmap_reporter.h" #include "components/viz/client/shared_bitmap_reporter.h"
#include "components/viz/common/gpu/context_provider.h" #include "components/viz/common/gpu/context_provider.h"
#include "components/viz/common/resources/shared_bitmap.h" #include "components/viz/common/resources/shared_bitmap.h"
...@@ -44,6 +45,7 @@ class PLATFORM_EXPORT VideoFrameSubmitter ...@@ -44,6 +45,7 @@ class PLATFORM_EXPORT VideoFrameSubmitter
public viz::mojom::blink::CompositorFrameSinkClient { public viz::mojom::blink::CompositorFrameSinkClient {
public: public:
VideoFrameSubmitter(WebContextProviderCallback, VideoFrameSubmitter(WebContextProviderCallback,
cc::PlaybackRoughnessReportingCallback,
std::unique_ptr<VideoFrameResourceProvider>); std::unique_ptr<VideoFrameResourceProvider>);
~VideoFrameSubmitter() override; ~VideoFrameSubmitter() override;
...@@ -127,6 +129,7 @@ class PLATFORM_EXPORT VideoFrameSubmitter ...@@ -127,6 +129,7 @@ class PLATFORM_EXPORT VideoFrameSubmitter
// Helper method for creating viz::CompositorFrame. If |video_frame| is null // Helper method for creating viz::CompositorFrame. If |video_frame| is null
// then the frame will be empty. // then the frame will be empty.
viz::CompositorFrame CreateCompositorFrame( viz::CompositorFrame CreateCompositorFrame(
uint32_t frame_token,
const viz::BeginFrameAck& begin_frame_ack, const viz::BeginFrameAck& begin_frame_ack,
scoped_refptr<media::VideoFrame> video_frame); scoped_refptr<media::VideoFrame> video_frame);
...@@ -172,9 +175,7 @@ class PLATFORM_EXPORT VideoFrameSubmitter ...@@ -172,9 +175,7 @@ class PLATFORM_EXPORT VideoFrameSubmitter
viz::FrameTokenGenerator next_frame_token_; viz::FrameTokenGenerator next_frame_token_;
// Timestamps indexed by frame token for histogram purposes. std::unique_ptr<cc::VideoPlaybackRoughnessReporter> roughness_reporter_;
using FrameTokenType = decltype(*std::declval<viz::FrameTokenGenerator>());
base::flat_map<FrameTokenType, base::TimeTicks> frame_token_to_timestamp_map_;
base::OneShotTimer empty_frame_timer_; base::OneShotTimer empty_frame_timer_;
......
...@@ -169,11 +169,13 @@ class VideoFrameSubmitterTest : public testing::Test { ...@@ -169,11 +169,13 @@ class VideoFrameSubmitterTest : public testing::Test {
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
} }
void MakeSubmitter() { void MakeSubmitter() { MakeSubmitter(base::DoNothing()); }
void MakeSubmitter(cc::PlaybackRoughnessReportingCallback reporting_cb) {
resource_provider_ = new StrictMock<MockVideoFrameResourceProvider>( resource_provider_ = new StrictMock<MockVideoFrameResourceProvider>(
context_provider_.get(), nullptr); context_provider_.get(), nullptr);
submitter_ = std::make_unique<VideoFrameSubmitter>( submitter_ = std::make_unique<VideoFrameSubmitter>(
base::DoNothing(), base::DoNothing(), reporting_cb,
base::WrapUnique<MockVideoFrameResourceProvider>(resource_provider_)); base::WrapUnique<MockVideoFrameResourceProvider>(resource_provider_));
submitter_->Initialize(video_frame_provider_.get()); submitter_->Initialize(video_frame_provider_.get());
...@@ -685,7 +687,7 @@ TEST_F(VideoFrameSubmitterTest, StopUsingProviderDuringContextLost) { ...@@ -685,7 +687,7 @@ TEST_F(VideoFrameSubmitterTest, StopUsingProviderDuringContextLost) {
} }
// Test the behaviour of the ChildLocalSurfaceIdAllocator instance. It checks // Test the behaviour of the ChildLocalSurfaceIdAllocator instance. It checks
// that the LocalSurfaceId is propoerly set at creation and updated when the // that the LocalSurfaceId is properly set at creation and updated when the
// video frames change. // video frames change.
TEST_F(VideoFrameSubmitterTest, FrameSizeChangeUpdatesLocalSurfaceId) { TEST_F(VideoFrameSubmitterTest, FrameSizeChangeUpdatesLocalSurfaceId) {
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment