Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(150)

Side by Side Diff: media/blink/watch_time_reporter.cc

Issue 2160963002: Add watch time metrics for HTML5 media playback. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Comments. Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/blink/watch_time_reporter.h ('k') | media/blink/watch_time_reporter_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/blink/watch_time_reporter.h"
6
7 #include "base/power_monitor/power_monitor.h"
8
9 namespace media {
10
11 // The minimum amount of media playback which can elapse before we'll report
12 // watch time metrics for a playback.
13 constexpr base::TimeDelta kMinimumElapsedWatchTime =
14 base::TimeDelta::FromSeconds(7);
15
16 // The minimum width and height of videos to report watch time metrics for.
17 constexpr gfx::Size kMinimumVideoSize = gfx::Size(200, 200);
18
19 static bool IsOnBatteryPower() {
20 if (base::PowerMonitor* pm = base::PowerMonitor::Get())
21 return pm->IsOnBatteryPower();
22 return false;
23 }
24
25 WatchTimeReporter::WatchTimeReporter(bool has_audio,
26 bool has_video,
27 bool is_mse,
28 bool is_encrypted,
29 scoped_refptr<MediaLog> media_log,
30 const gfx::Size& initial_video_size,
31 const GetMediaTimeCB& get_media_time_cb)
32 : has_audio_(has_audio),
33 has_video_(has_video),
34 is_mse_(is_mse),
35 is_encrypted_(is_encrypted),
36 media_log_(std::move(media_log)),
37 initial_video_size_(initial_video_size),
38 get_media_time_cb_(get_media_time_cb) {
39 DCHECK(!get_media_time_cb_.is_null());
40 DCHECK(has_audio_ || has_video_);
41 if (has_video_)
42 DCHECK(!initial_video_size_.IsEmpty());
43
44 if (base::PowerMonitor* pm = base::PowerMonitor::Get())
45 pm->AddObserver(this);
46 }
47
48 WatchTimeReporter::~WatchTimeReporter() {
49 // If the timer is still running, finalize immediately, this is our last
50 // chance to capture metrics.
51 if (reporting_timer_.IsRunning())
52 MaybeFinalizeWatchTime(FinalizeTime::IMMEDIATELY);
53
54 if (base::PowerMonitor* pm = base::PowerMonitor::Get())
55 pm->RemoveObserver(this);
56 }
57
58 void WatchTimeReporter::OnPlaying() {
59 is_playing_ = true;
60 MaybeStartReportingTimer(get_media_time_cb_.Run());
61 }
62
63 void WatchTimeReporter::OnPaused() {
64 is_playing_ = false;
65 MaybeFinalizeWatchTime(FinalizeTime::ON_NEXT_UPDATE);
66 }
67
68 void WatchTimeReporter::OnSeeking() {
69 if (!reporting_timer_.IsRunning())
70 return;
71
72 // Seek is a special case that does not have hysteresis, when this is called
73 // the seek is imminent, so finalize the previous playback immediately.
74
75 // Don't trample an existing end timestamp.
76 if (end_timestamp_ == kNoTimestamp)
77 end_timestamp_ = get_media_time_cb_.Run();
78 UpdateWatchTime();
79 }
80
81 void WatchTimeReporter::OnVolumeChange(double volume) {
82 const double old_volume = volume_;
83 volume_ = volume;
84
85 // We're only interesting in transitions in and out of the muted state.
86 if (!old_volume && volume)
87 MaybeStartReportingTimer(get_media_time_cb_.Run());
88 else if (old_volume && !volume_)
89 MaybeFinalizeWatchTime(FinalizeTime::ON_NEXT_UPDATE);
90 }
91
92 void WatchTimeReporter::OnShown() {
93 is_visible_ = true;
94 MaybeStartReportingTimer(get_media_time_cb_.Run());
95 }
96
97 void WatchTimeReporter::OnHidden() {
98 is_visible_ = false;
99 MaybeFinalizeWatchTime(FinalizeTime::ON_NEXT_UPDATE);
100 }
101
102 void WatchTimeReporter::OnPowerStateChange(bool on_battery_power) {
103 if (!reporting_timer_.IsRunning())
104 return;
105
106 // Defer changing |is_on_battery_power_| until the next watch time report to
107 // avoid momentary power changes from affecting the results.
108 if (is_on_battery_power_ != on_battery_power) {
109 end_timestamp_for_power_ = get_media_time_cb_.Run();
110
111 // Restart the reporting timer so the full hysteresis is afforded.
112 reporting_timer_.Start(FROM_HERE, reporting_interval_, this,
113 &WatchTimeReporter::UpdateWatchTime);
114 return;
115 }
116
117 end_timestamp_for_power_ = kNoTimestamp;
118 }
119
120 bool WatchTimeReporter::ShouldReportWatchTime() {
121 // Only report watch time for media of sufficient size with both audio and
122 // video tracks present.
123 return has_audio_ && has_video_ &&
124 initial_video_size_.height() >= kMinimumVideoSize.height() &&
125 initial_video_size_.width() >= kMinimumVideoSize.width();
126 }
127
128 void WatchTimeReporter::MaybeStartReportingTimer(
129 base::TimeDelta start_timestamp) {
130 // Don't start the timer if any of our state indicates we shouldn't; this
131 // check is important since the various event handlers do not have to care
132 // about the state of other events.
133 if (!ShouldReportWatchTime() || !is_playing_ || !volume_ || !is_visible_) {
134 // If we reach this point the timer should already have been stopped or
135 // there is a pending finalize in flight.
136 DCHECK(!reporting_timer_.IsRunning() || end_timestamp_ != kNoTimestamp);
137 return;
138 }
139
140 // If we haven't finalized the last watch time metrics yet, count this
141 // playback as a continuation of the previous metrics.
142 if (end_timestamp_ != kNoTimestamp) {
143 DCHECK(reporting_timer_.IsRunning());
144 end_timestamp_ = kNoTimestamp;
145 return;
146 }
147
148 // Don't restart the timer if it's already running.
149 if (reporting_timer_.IsRunning())
150 return;
151
152 last_media_timestamp_ = end_timestamp_for_power_ = kNoTimestamp;
153 is_on_battery_power_ = IsOnBatteryPower();
154 start_timestamp_ = start_timestamp_for_power_ = start_timestamp;
155 reporting_timer_.Start(FROM_HERE, reporting_interval_, this,
156 &WatchTimeReporter::UpdateWatchTime);
157 }
158
159 void WatchTimeReporter::MaybeFinalizeWatchTime(FinalizeTime finalize_time) {
160 // Don't finalize if the timer is already stopped.
161 if (!reporting_timer_.IsRunning())
162 return;
163
164 // Don't trample an existing finalize; the first takes precedence.
165 if (end_timestamp_ == kNoTimestamp)
166 end_timestamp_ = get_media_time_cb_.Run();
167
168 if (finalize_time == FinalizeTime::IMMEDIATELY) {
169 UpdateWatchTime();
170 return;
171 }
172
173 // Always restart the timer when finalizing, so that we allow for the full
174 // length of |kReportingInterval| to elapse for hysteresis purposes.
175 DCHECK_EQ(finalize_time, FinalizeTime::ON_NEXT_UPDATE);
176 reporting_timer_.Start(FROM_HERE, reporting_interval_, this,
177 &WatchTimeReporter::UpdateWatchTime);
178 }
179
180 void WatchTimeReporter::UpdateWatchTime() {
181 DCHECK(ShouldReportWatchTime());
182
183 const bool is_finalizing = end_timestamp_ != kNoTimestamp;
184 const bool is_power_change_pending = end_timestamp_for_power_ != kNoTimestamp;
185
186 // If we're finalizing the log, use the media time value at the time of
187 // finalization.
188 const base::TimeDelta current_timestamp =
189 is_finalizing ? end_timestamp_ : get_media_time_cb_.Run();
190 const base::TimeDelta elapsed = current_timestamp - start_timestamp_;
191
192 // Only report watch time after some minimum amount has elapsed. Don't update
193 // watch time if media time hasn't changed since the last run; this may occur
194 // if a seek is taking some time to complete or the playback is stalled for
195 // some reason.
196 if (elapsed >= kMinimumElapsedWatchTime &&
197 last_media_timestamp_ != current_timestamp) {
198 last_media_timestamp_ = current_timestamp;
199
200 std::unique_ptr<MediaLogEvent> log_event =
201 media_log_->CreateEvent(MediaLogEvent::Type::WATCH_TIME_UPDATE);
202
203 log_event->params.SetDoubleWithoutPathExpansion(
204 MediaLog::kWatchTimeAudioVideoAll, elapsed.InSecondsF());
205 if (is_mse_) {
206 log_event->params.SetDoubleWithoutPathExpansion(
207 MediaLog::kWatchTimeAudioVideoMse, elapsed.InSecondsF());
208 } else {
209 log_event->params.SetDoubleWithoutPathExpansion(
210 MediaLog::kWatchTimeAudioVideoSrc, elapsed.InSecondsF());
211 }
212 if (is_encrypted_) {
213 log_event->params.SetDoubleWithoutPathExpansion(
214 MediaLog::kWatchTimeAudioVideoEme, elapsed.InSecondsF());
215 }
216
217 // Record watch time using the last known value for |is_on_battery_power_|;
218 // if there's a |pending_power_change_| use that to accurately finalize the
219 // last bits of time in the previous bucket.
220 const base::TimeDelta elapsed_power =
221 (is_power_change_pending ? end_timestamp_for_power_
222 : current_timestamp) -
223 start_timestamp_for_power_;
224
225 // Again, only update watch time if enough time has elapsed; we need to
226 // recheck the elapsed time here since the power source can change anytime.
227 if (elapsed_power >= kMinimumElapsedWatchTime) {
228 if (is_on_battery_power_) {
229 log_event->params.SetDoubleWithoutPathExpansion(
230 MediaLog::kWatchTimeAudioVideoBattery, elapsed_power.InSecondsF());
231 } else {
232 log_event->params.SetDoubleWithoutPathExpansion(
233 MediaLog::kWatchTimeAudioVideoAc, elapsed_power.InSecondsF());
234 }
235 }
236
237 if (is_finalizing)
238 log_event->params.SetBoolean(MediaLog::kWatchTimeFinalize, true);
239 else if (is_power_change_pending)
240 log_event->params.SetBoolean(MediaLog::kWatchTimeFinalizePower, true);
241
242 DVLOG(2) << "Sending watch time update.";
243 media_log_->AddEvent(std::move(log_event));
244 }
245
246 if (is_power_change_pending) {
247 // Invert battery power status here instead of using the value returned by
248 // the PowerObserver since there may be a pending OnPowerStateChange().
249 is_on_battery_power_ = !is_on_battery_power_;
250
251 start_timestamp_for_power_ = end_timestamp_for_power_;
252 end_timestamp_for_power_ = kNoTimestamp;
253 }
254
255 // Stop the timer if this is supposed to be our last tick.
256 if (is_finalizing) {
257 end_timestamp_ = kNoTimestamp;
258 reporting_timer_.Stop();
259 }
260 }
261
262 } // namespace media
OLDNEW
« no previous file with comments | « media/blink/watch_time_reporter.h ('k') | media/blink/watch_time_reporter_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698