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 #include "media/mojo/services/playback_events_recorder.h"
7 #include "base/metrics/user_metrics.h"
8 #include "base/strings/strcat.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/stringprintf.h"
11 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
17 void RecordEventWithValueAt(const char* name,
19 base::TimeTicks time) {
20 base::RecordComputedActionAt(base::StrCat({"WebEngine.Media.", name, ":",
21 base::NumberToString(value)}),
25 void RecordEventWithValue(const char* name, int64_t value) {
26 RecordEventWithValueAt(name, value, base::TimeTicks::Now());
29 constexpr base::TimeDelta kBitrateReportPeriod = base::Seconds(5);
33 PlaybackEventsRecorder::BitrateEstimator::BitrateEstimator() {}
34 PlaybackEventsRecorder::BitrateEstimator::~BitrateEstimator() {}
36 void PlaybackEventsRecorder::BitrateEstimator::Update(
37 const PipelineStatistics& stats) {
38 base::TimeTicks now = base::TimeTicks::Now();
40 // The code below trusts that |stats| are valid even though they came from an
41 // untrusted process. That's accepable because the stats are used only to
44 time_elapsed_ += now - last_stats_time_;
46 stats.audio_bytes_decoded - last_stats_->audio_bytes_decoded;
48 stats.video_bytes_decoded - last_stats_->video_bytes_decoded;
49 if (time_elapsed_ >= kBitrateReportPeriod) {
50 size_t audio_bitrate_kbps =
51 8 * audio_bytes_ / time_elapsed_.InMilliseconds();
52 RecordEventWithValueAt("AudioBitrate", audio_bitrate_kbps, now);
54 size_t video_bitrate_kbps =
55 8 * video_bytes_ / time_elapsed_.InMilliseconds();
56 RecordEventWithValueAt("VideoBitrate", video_bitrate_kbps, now);
58 time_elapsed_ = base::TimeDelta();
65 last_stats_time_ = now;
68 void PlaybackEventsRecorder::BitrateEstimator::OnPause() {
73 void PlaybackEventsRecorder::Create(
74 mojo::PendingReceiver<mojom::PlaybackEventsRecorder> receiver) {
75 mojo::MakeSelfOwnedReceiver(std::make_unique<PlaybackEventsRecorder>(),
79 PlaybackEventsRecorder::PlaybackEventsRecorder() = default;
80 PlaybackEventsRecorder::~PlaybackEventsRecorder() = default;
82 void PlaybackEventsRecorder::OnPlaying() {
83 base::RecordComputedAction("WebEngine.Media.Playing");
86 void PlaybackEventsRecorder::OnPaused() {
87 base::RecordComputedAction("WebEngine.Media.Pause");
88 bitrate_estimator_.OnPause();
91 void PlaybackEventsRecorder::OnSeeking() {
92 buffering_state_ = BufferingState::kInitialBuffering;
95 void PlaybackEventsRecorder::OnEnded() {
96 base::RecordComputedAction("WebEngine.Media.Ended");
99 void PlaybackEventsRecorder::OnBuffering() {
100 DCHECK(buffering_state_ == BufferingState::kBuffered);
102 buffering_start_time_ = base::TimeTicks::Now();
103 buffering_state_ = BufferingState::kBuffering;
105 bitrate_estimator_.OnPause();
108 void PlaybackEventsRecorder::OnBufferingComplete() {
109 auto now = base::TimeTicks::Now();
111 if (buffering_state_ == BufferingState::kBuffering) {
112 base::TimeDelta time_between_buffering =
113 buffering_start_time_ - last_buffering_end_time_;
114 RecordEventWithValueAt("PlayTimeBeforeAutoPause",
115 time_between_buffering.InMilliseconds(), now);
117 base::TimeDelta buffering_user_time = now - buffering_start_time_;
118 RecordEventWithValueAt("AutoPauseTime",
119 buffering_user_time.InMilliseconds(), now);
122 buffering_state_ = BufferingState::kBuffered;
123 last_buffering_end_time_ = now;
126 void PlaybackEventsRecorder::OnError(const PipelineStatus& status) {
127 RecordEventWithValue("Error", status.code());
130 void PlaybackEventsRecorder::OnNaturalSizeChanged(const gfx::Size& size) {
131 int encoded_video_resolution = (size.width() << 16) | size.height();
132 base::RecordComputedAction(base::StringPrintf(
133 "WebEngine.Media.VideoResolution:%d", encoded_video_resolution));
136 void PlaybackEventsRecorder::OnPipelineStatistics(
137 const PipelineStatistics& stats) {
138 bitrate_estimator_.Update(stats);