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

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

Issue 2780533004: Start recording background video watch time. (Closed)
Patch Set: Fix copy-init issues. Created 3 years, 8 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
1 // Copyright 2016 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/blink/watch_time_reporter.h" 5 #include "media/blink/watch_time_reporter.h"
6 6
7 #include "base/power_monitor/power_monitor.h" 7 #include "base/power_monitor/power_monitor.h"
8 8
9 namespace media { 9 namespace media {
10 10
(...skipping 12 matching lines...) Expand all
23 } 23 }
24 24
25 WatchTimeReporter::WatchTimeReporter(bool has_audio, 25 WatchTimeReporter::WatchTimeReporter(bool has_audio,
26 bool has_video, 26 bool has_video,
27 bool is_mse, 27 bool is_mse,
28 bool is_encrypted, 28 bool is_encrypted,
29 bool is_embedded_media_experience_enabled, 29 bool is_embedded_media_experience_enabled,
30 scoped_refptr<MediaLog> media_log, 30 scoped_refptr<MediaLog> media_log,
31 const gfx::Size& initial_video_size, 31 const gfx::Size& initial_video_size,
32 const GetMediaTimeCB& get_media_time_cb) 32 const GetMediaTimeCB& get_media_time_cb)
33 : WatchTimeReporter(has_audio,
34 has_video,
35 is_mse,
36 is_encrypted,
37 is_embedded_media_experience_enabled,
38 std::move(media_log),
39 initial_video_size,
40 get_media_time_cb,
41 false) {}
42
43 WatchTimeReporter::WatchTimeReporter(bool has_audio,
44 bool has_video,
45 bool is_mse,
46 bool is_encrypted,
47 bool is_embedded_media_experience_enabled,
48 scoped_refptr<MediaLog> media_log,
49 const gfx::Size& initial_video_size,
50 const GetMediaTimeCB& get_media_time_cb,
51 bool is_background)
33 : has_audio_(has_audio), 52 : has_audio_(has_audio),
34 has_video_(has_video), 53 has_video_(has_video),
35 is_mse_(is_mse), 54 is_mse_(is_mse),
36 is_encrypted_(is_encrypted), 55 is_encrypted_(is_encrypted),
37 is_embedded_media_experience_enabled_( 56 is_embedded_media_experience_enabled_(
38 is_embedded_media_experience_enabled), 57 is_embedded_media_experience_enabled),
39 media_log_(std::move(media_log)), 58 media_log_(std::move(media_log)),
40 initial_video_size_(initial_video_size), 59 initial_video_size_(initial_video_size),
41 get_media_time_cb_(get_media_time_cb) { 60 get_media_time_cb_(get_media_time_cb),
61 is_background_(is_background) {
42 DCHECK(!get_media_time_cb_.is_null()); 62 DCHECK(!get_media_time_cb_.is_null());
43 DCHECK(has_audio_ || has_video_); 63 DCHECK(has_audio_ || has_video_);
44 64
45 if (base::PowerMonitor* pm = base::PowerMonitor::Get()) 65 if (base::PowerMonitor* pm = base::PowerMonitor::Get())
46 pm->AddObserver(this); 66 pm->AddObserver(this);
67
68 if (is_background_) {
69 DCHECK(has_audio_);
70 DCHECK(!has_video_);
71 return;
72 }
73
74 // Background watch time is reported by creating an audio only watch time
75 // reporter which receives play when hidden and pause when shown. This avoids
76 // unnecessary complexity inside the UpdateWatchTime() for handling this case.
77 if (has_video_ && has_audio_ && ShouldReportWatchTime()) {
78 background_reporter_.reset(
79 new WatchTimeReporter(has_audio_, false, is_mse_, is_encrypted_,
80 is_embedded_media_experience_enabled_, media_log_,
81 initial_video_size_, get_media_time_cb_, true));
82 }
47 } 83 }
48 84
49 WatchTimeReporter::~WatchTimeReporter() { 85 WatchTimeReporter::~WatchTimeReporter() {
86 background_reporter_.reset();
87
50 // If the timer is still running, finalize immediately, this is our last 88 // If the timer is still running, finalize immediately, this is our last
51 // chance to capture metrics. 89 // chance to capture metrics.
52 if (reporting_timer_.IsRunning()) 90 if (reporting_timer_.IsRunning())
53 MaybeFinalizeWatchTime(FinalizeTime::IMMEDIATELY); 91 MaybeFinalizeWatchTime(FinalizeTime::IMMEDIATELY);
54 92
55 if (base::PowerMonitor* pm = base::PowerMonitor::Get()) 93 if (base::PowerMonitor* pm = base::PowerMonitor::Get())
56 pm->RemoveObserver(this); 94 pm->RemoveObserver(this);
57 } 95 }
58 96
59 void WatchTimeReporter::OnPlaying() { 97 void WatchTimeReporter::OnPlaying() {
98 if (background_reporter_ && !is_visible_)
99 background_reporter_->OnPlaying();
100
60 is_playing_ = true; 101 is_playing_ = true;
61 MaybeStartReportingTimer(get_media_time_cb_.Run()); 102 MaybeStartReportingTimer(get_media_time_cb_.Run());
62 } 103 }
63 104
64 void WatchTimeReporter::OnPaused() { 105 void WatchTimeReporter::OnPaused() {
106 if (background_reporter_)
107 background_reporter_->OnPaused();
108
65 is_playing_ = false; 109 is_playing_ = false;
66 MaybeFinalizeWatchTime(FinalizeTime::ON_NEXT_UPDATE); 110 MaybeFinalizeWatchTime(FinalizeTime::ON_NEXT_UPDATE);
67 } 111 }
68 112
69 void WatchTimeReporter::OnSeeking() { 113 void WatchTimeReporter::OnSeeking() {
114 if (background_reporter_)
115 background_reporter_->OnSeeking();
116
70 if (!reporting_timer_.IsRunning()) 117 if (!reporting_timer_.IsRunning())
71 return; 118 return;
72 119
73 // Seek is a special case that does not have hysteresis, when this is called 120 // Seek is a special case that does not have hysteresis, when this is called
74 // the seek is imminent, so finalize the previous playback immediately. 121 // the seek is imminent, so finalize the previous playback immediately.
75 122
76 // Don't trample an existing end timestamp. 123 // Don't trample an existing end timestamp.
77 if (end_timestamp_ == kNoTimestamp) 124 if (end_timestamp_ == kNoTimestamp)
78 end_timestamp_ = get_media_time_cb_.Run(); 125 end_timestamp_ = get_media_time_cb_.Run();
79 UpdateWatchTime(); 126 UpdateWatchTime();
80 } 127 }
81 128
82 void WatchTimeReporter::OnVolumeChange(double volume) { 129 void WatchTimeReporter::OnVolumeChange(double volume) {
130 if (background_reporter_)
131 background_reporter_->OnVolumeChange(volume);
132
83 const double old_volume = volume_; 133 const double old_volume = volume_;
84 volume_ = volume; 134 volume_ = volume;
85 135
86 // We're only interesting in transitions in and out of the muted state. 136 // We're only interesting in transitions in and out of the muted state.
87 if (!old_volume && volume) 137 if (!old_volume && volume)
88 MaybeStartReportingTimer(get_media_time_cb_.Run()); 138 MaybeStartReportingTimer(get_media_time_cb_.Run());
89 else if (old_volume && !volume_) 139 else if (old_volume && !volume_)
90 MaybeFinalizeWatchTime(FinalizeTime::ON_NEXT_UPDATE); 140 MaybeFinalizeWatchTime(FinalizeTime::ON_NEXT_UPDATE);
91 } 141 }
92 142
93 void WatchTimeReporter::OnShown() { 143 void WatchTimeReporter::OnShown() {
144 if (background_reporter_)
145 background_reporter_->OnPaused();
146
94 if (!has_video_) 147 if (!has_video_)
95 return; 148 return;
96 149
97 is_visible_ = true; 150 is_visible_ = true;
98 MaybeStartReportingTimer(get_media_time_cb_.Run()); 151 MaybeStartReportingTimer(get_media_time_cb_.Run());
99 } 152 }
100 153
101 void WatchTimeReporter::OnHidden() { 154 void WatchTimeReporter::OnHidden() {
155 if (background_reporter_ && is_playing_)
156 background_reporter_->OnPlaying();
157
102 if (!has_video_) 158 if (!has_video_)
103 return; 159 return;
104 160
105 is_visible_ = false; 161 is_visible_ = false;
106 MaybeFinalizeWatchTime(FinalizeTime::ON_NEXT_UPDATE); 162 MaybeFinalizeWatchTime(FinalizeTime::ON_NEXT_UPDATE);
107 } 163 }
108 164
109 void WatchTimeReporter::OnPowerStateChange(bool on_battery_power) { 165 void WatchTimeReporter::OnPowerStateChange(bool on_battery_power) {
110 if (!reporting_timer_.IsRunning()) 166 if (!reporting_timer_.IsRunning())
111 return; 167 return;
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
150 if (end_timestamp_ != kNoTimestamp) { 206 if (end_timestamp_ != kNoTimestamp) {
151 DCHECK(reporting_timer_.IsRunning()); 207 DCHECK(reporting_timer_.IsRunning());
152 end_timestamp_ = kNoTimestamp; 208 end_timestamp_ = kNoTimestamp;
153 return; 209 return;
154 } 210 }
155 211
156 // Don't restart the timer if it's already running. 212 // Don't restart the timer if it's already running.
157 if (reporting_timer_.IsRunning()) 213 if (reporting_timer_.IsRunning())
158 return; 214 return;
159 215
160 last_media_timestamp_ = end_timestamp_for_power_ = kNoTimestamp; 216 last_media_timestamp_ = last_media_power_timestamp_ =
217 end_timestamp_for_power_ = kNoTimestamp;
161 is_on_battery_power_ = IsOnBatteryPower(); 218 is_on_battery_power_ = IsOnBatteryPower();
162 start_timestamp_ = start_timestamp_for_power_ = start_timestamp; 219 start_timestamp_ = start_timestamp_for_power_ = start_timestamp;
163 reporting_timer_.Start(FROM_HERE, reporting_interval_, this, 220 reporting_timer_.Start(FROM_HERE, reporting_interval_, this,
164 &WatchTimeReporter::UpdateWatchTime); 221 &WatchTimeReporter::UpdateWatchTime);
165 } 222 }
166 223
167 void WatchTimeReporter::MaybeFinalizeWatchTime(FinalizeTime finalize_time) { 224 void WatchTimeReporter::MaybeFinalizeWatchTime(FinalizeTime finalize_time) {
168 // Don't finalize if the timer is already stopped. 225 // Don't finalize if the timer is already stopped.
169 if (!reporting_timer_.IsRunning()) 226 if (!reporting_timer_.IsRunning())
170 return; 227 return;
(...skipping 19 matching lines...) Expand all
190 247
191 const bool is_finalizing = end_timestamp_ != kNoTimestamp; 248 const bool is_finalizing = end_timestamp_ != kNoTimestamp;
192 const bool is_power_change_pending = end_timestamp_for_power_ != kNoTimestamp; 249 const bool is_power_change_pending = end_timestamp_for_power_ != kNoTimestamp;
193 250
194 // If we're finalizing the log, use the media time value at the time of 251 // If we're finalizing the log, use the media time value at the time of
195 // finalization. 252 // finalization.
196 const base::TimeDelta current_timestamp = 253 const base::TimeDelta current_timestamp =
197 is_finalizing ? end_timestamp_ : get_media_time_cb_.Run(); 254 is_finalizing ? end_timestamp_ : get_media_time_cb_.Run();
198 const base::TimeDelta elapsed = current_timestamp - start_timestamp_; 255 const base::TimeDelta elapsed = current_timestamp - start_timestamp_;
199 256
257 std::unique_ptr<MediaLogEvent> log_event =
258 media_log_->CreateEvent(MediaLogEvent::Type::WATCH_TIME_UPDATE);
259
260 #define RECORD_WATCH_TIME(key, value) \
261 do { \
262 log_event->params.SetDoubleWithoutPathExpansion( \
263 has_video_ \
264 ? MediaLog::kWatchTimeAudioVideo##key \
265 : (is_background_ ? MediaLog::kWatchTimeAudioVideoBackground##key \
266 : MediaLog::kWatchTimeAudio##key), \
267 value.InSecondsF()); \
268 } while (0)
269
200 // Only report watch time after some minimum amount has elapsed. Don't update 270 // Only report watch time after some minimum amount has elapsed. Don't update
201 // watch time if media time hasn't changed since the last run; this may occur 271 // watch time if media time hasn't changed since the last run; this may occur
202 // if a seek is taking some time to complete or the playback is stalled for 272 // if a seek is taking some time to complete or the playback is stalled for
203 // some reason. 273 // some reason.
204 if (elapsed >= kMinimumElapsedWatchTime && 274 if (last_media_timestamp_ != current_timestamp) {
205 last_media_timestamp_ != current_timestamp) {
206 last_media_timestamp_ = current_timestamp; 275 last_media_timestamp_ = current_timestamp;
207 276
208 std::unique_ptr<MediaLogEvent> log_event = 277 if (elapsed >= kMinimumElapsedWatchTime) {
209 media_log_->CreateEvent(MediaLogEvent::Type::WATCH_TIME_UPDATE); 278 RECORD_WATCH_TIME(All, elapsed);
279 if (is_mse_)
280 RECORD_WATCH_TIME(Mse, elapsed);
281 else
282 RECORD_WATCH_TIME(Src, elapsed);
210 283
211 log_event->params.SetDoubleWithoutPathExpansion( 284 if (is_encrypted_)
212 has_video_ ? MediaLog::kWatchTimeAudioVideoAll 285 RECORD_WATCH_TIME(Eme, elapsed);
213 : MediaLog::kWatchTimeAudioAll, 286
214 elapsed.InSecondsF()); 287 if (is_embedded_media_experience_enabled_)
215 if (is_mse_) { 288 RECORD_WATCH_TIME(EmbeddedExperience, elapsed);
216 log_event->params.SetDoubleWithoutPathExpansion(
217 has_video_ ? MediaLog::kWatchTimeAudioVideoMse
218 : MediaLog::kWatchTimeAudioMse,
219 elapsed.InSecondsF());
220 } else {
221 log_event->params.SetDoubleWithoutPathExpansion(
222 has_video_ ? MediaLog::kWatchTimeAudioVideoSrc
223 : MediaLog::kWatchTimeAudioSrc,
224 elapsed.InSecondsF());
225 } 289 }
226 if (is_encrypted_) { 290 }
227 log_event->params.SetDoubleWithoutPathExpansion(
228 has_video_ ? MediaLog::kWatchTimeAudioVideoEme
229 : MediaLog::kWatchTimeAudioEme,
230 elapsed.InSecondsF());
231 }
232 291
233 if (is_embedded_media_experience_enabled_) { 292 if (last_media_power_timestamp_ != current_timestamp) {
234 log_event->params.SetDoubleWithoutPathExpansion( 293 // We need a separate |last_media_power_timestamp_| since we don't always
235 has_video_ ? MediaLog::kWatchTimeAudioVideoEmbeddedExperience 294 // base the last watch time calculation on the current timestamp.
236 : MediaLog::kWatchTimeAudioEmbeddedExperience, 295 last_media_power_timestamp_ =
237 elapsed.InSecondsF()); 296 is_power_change_pending ? end_timestamp_for_power_ : current_timestamp;
238 }
239 297
240 // Record watch time using the last known value for |is_on_battery_power_|; 298 // Record watch time using the last known value for |is_on_battery_power_|;
241 // if there's a |pending_power_change_| use that to accurately finalize the 299 // if there's a |pending_power_change_| use that to accurately finalize the
242 // last bits of time in the previous bucket. 300 // last bits of time in the previous bucket.
243 const base::TimeDelta elapsed_power = 301 const base::TimeDelta elapsed_power =
244 (is_power_change_pending ? end_timestamp_for_power_ 302 last_media_power_timestamp_ - start_timestamp_for_power_;
245 : current_timestamp) -
246 start_timestamp_for_power_;
247 303
248 // Again, only update watch time if enough time has elapsed; we need to 304 // Again, only update watch time if enough time has elapsed; we need to
249 // recheck the elapsed time here since the power source can change anytime. 305 // recheck the elapsed time here since the power source can change anytime.
250 if (elapsed_power >= kMinimumElapsedWatchTime) { 306 if (elapsed_power >= kMinimumElapsedWatchTime) {
251 if (is_on_battery_power_) { 307 if (is_on_battery_power_)
252 log_event->params.SetDoubleWithoutPathExpansion( 308 RECORD_WATCH_TIME(Battery, elapsed_power);
253 has_video_ ? MediaLog::kWatchTimeAudioVideoBattery 309 else
254 : MediaLog::kWatchTimeAudioBattery, 310 RECORD_WATCH_TIME(Ac, elapsed_power);
255 elapsed_power.InSecondsF());
256 } else {
257 log_event->params.SetDoubleWithoutPathExpansion(
258 has_video_ ? MediaLog::kWatchTimeAudioVideoAc
259 : MediaLog::kWatchTimeAudioAc,
260 elapsed_power.InSecondsF());
261 }
262 } 311 }
312 }
313 #undef RECORD_WATCH_TIME
263 314
264 if (is_finalizing) 315 // Always send finalize, even if we don't currently have any data, it's
265 log_event->params.SetBoolean(MediaLog::kWatchTimeFinalize, true); 316 // harmless to send since nothing will be logged if we've already finalized.
266 else if (is_power_change_pending) 317 if (is_finalizing)
267 log_event->params.SetBoolean(MediaLog::kWatchTimeFinalizePower, true); 318 log_event->params.SetBoolean(MediaLog::kWatchTimeFinalize, true);
319 else if (is_power_change_pending)
320 log_event->params.SetBoolean(MediaLog::kWatchTimeFinalizePower, true);
268 321
269 DVLOG(2) << "Sending watch time update."; 322 if (!log_event->params.empty())
270 media_log_->AddEvent(std::move(log_event)); 323 media_log_->AddEvent(std::move(log_event));
271 }
272 324
273 if (is_power_change_pending) { 325 if (is_power_change_pending) {
274 // Invert battery power status here instead of using the value returned by 326 // Invert battery power status here instead of using the value returned by
275 // the PowerObserver since there may be a pending OnPowerStateChange(). 327 // the PowerObserver since there may be a pending OnPowerStateChange().
276 is_on_battery_power_ = !is_on_battery_power_; 328 is_on_battery_power_ = !is_on_battery_power_;
277 329
278 start_timestamp_for_power_ = end_timestamp_for_power_; 330 start_timestamp_for_power_ = end_timestamp_for_power_;
279 end_timestamp_for_power_ = kNoTimestamp; 331 end_timestamp_for_power_ = kNoTimestamp;
280 } 332 }
281 333
282 // Stop the timer if this is supposed to be our last tick. 334 // Stop the timer if this is supposed to be our last tick.
283 if (is_finalizing) { 335 if (is_finalizing) {
284 end_timestamp_ = kNoTimestamp; 336 end_timestamp_ = kNoTimestamp;
285 reporting_timer_.Stop(); 337 reporting_timer_.Stop();
286 } 338 }
287 } 339 }
288 340
289 } // namespace media 341 } // 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