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

Side by Side Diff: content/browser/media/media_internals.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
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 "content/browser/media/media_internals.h" 5 #include "content/browser/media/media_internals.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include <memory> 9 #include <memory>
10 #include <utility> 10 #include <utility>
11 11
12 #include "base/containers/flat_map.h"
13 #include "base/containers/flat_set.h"
12 #include "base/macros.h" 14 #include "base/macros.h"
13 #include "base/metrics/histogram_macros.h" 15 #include "base/metrics/histogram_macros.h"
14 #include "base/strings/string16.h" 16 #include "base/strings/string16.h"
15 #include "base/strings/string_number_conversions.h" 17 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/stringprintf.h" 18 #include "base/strings/stringprintf.h"
17 #include "build/build_config.h" 19 #include "build/build_config.h"
18 #include "content/browser/renderer_host/media/media_stream_manager.h" 20 #include "content/browser/renderer_host/media/media_stream_manager.h"
19 #include "content/public/browser/browser_thread.h" 21 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/notification_service.h" 22 #include "content/public/browser/notification_service.h"
21 #include "content/public/browser/notification_types.h" 23 #include "content/public/browser/notification_types.h"
(...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after
283 // Called when a render process is terminated. Reports the pipeline status to 285 // Called when a render process is terminated. Reports the pipeline status to
284 // UMA for every player associated with the renderer process and then deletes 286 // UMA for every player associated with the renderer process and then deletes
285 // the player state. 287 // the player state.
286 void OnProcessTerminated(int render_process_id); 288 void OnProcessTerminated(int render_process_id);
287 289
288 // Helper function to save the event payload to RendererPlayerMap. 290 // Helper function to save the event payload to RendererPlayerMap.
289 void SavePlayerState(int render_process_id, 291 void SavePlayerState(int render_process_id,
290 const media::MediaLogEvent& event); 292 const media::MediaLogEvent& event);
291 293
292 private: 294 private:
293 struct WatchTimeInfo { 295 using WatchTimeInfo = base::flat_map<base::StringPiece, base::TimeDelta>;
294 base::TimeDelta all_watch_time = media::kNoTimestamp;
295 base::TimeDelta mse_watch_time = media::kNoTimestamp;
296 base::TimeDelta eme_watch_time = media::kNoTimestamp;
297 base::TimeDelta src_watch_time = media::kNoTimestamp;
298 base::TimeDelta ac_watch_time = media::kNoTimestamp;
299 base::TimeDelta battery_watch_time = media::kNoTimestamp;
300 base::TimeDelta embedded_experience_watch_time = media::kNoTimestamp;
301 };
302
303 struct PipelineInfo { 296 struct PipelineInfo {
304 bool has_pipeline = false; 297 bool has_pipeline = false;
305 bool has_ever_played = false; 298 bool has_ever_played = false;
306 bool has_reached_have_enough = false; 299 bool has_reached_have_enough = false;
307 media::PipelineStatus last_pipeline_status = media::PIPELINE_OK; 300 media::PipelineStatus last_pipeline_status = media::PIPELINE_OK;
308 bool has_audio = false; 301 bool has_audio = false;
309 bool has_video = false; 302 bool has_video = false;
310 bool video_dds = false; 303 bool video_dds = false;
311 bool video_decoder_changed = false; 304 bool video_decoder_changed = false;
312 std::string audio_codec_name; 305 std::string audio_codec_name;
313 std::string video_codec_name; 306 std::string video_codec_name;
314 std::string video_decoder; 307 std::string video_decoder;
315 WatchTimeInfo watch_time_info; 308 WatchTimeInfo watch_time_info;
316 }; 309 };
317 310
318 // Helper function to report PipelineStatus associated with a player to UMA. 311 // Helper function to report PipelineStatus associated with a player to UMA.
319 void ReportUMAForPipelineStatus(const PipelineInfo& player_info); 312 void ReportUMAForPipelineStatus(const PipelineInfo& player_info);
320 313
321 // Helper to generate PipelineStatus UMA name for AudioVideo streams. 314 // Helper to generate PipelineStatus UMA name for AudioVideo streams.
322 std::string GetUMANameForAVStream(const PipelineInfo& player_info); 315 std::string GetUMANameForAVStream(const PipelineInfo& player_info);
323 316
324 // Saves the watch time info from |event| under |key| at |watch_time| if |key| 317 void RecordWatchTime(const base::StringPiece& key, base::TimeDelta value) {
325 // is present in |event.params|. 318 base::Histogram::FactoryTimeGet(
326 void MaybeSaveWatchTime(const media::MediaLogEvent& event, 319 key.as_string(), base::TimeDelta::FromSeconds(7),
327 const char* key, 320 base::TimeDelta::FromHours(10), 50,
328 base::TimeDelta* watch_time) { 321 base::HistogramBase::kUmaTargetedHistogramFlag)
329 if (!event.params.HasKey(key)) 322 ->AddTime(value);
330 return;
331
332 double in_seconds;
333 const bool result =
334 event.params.GetDoubleWithoutPathExpansion(key, &in_seconds);
335 DCHECK(result);
336 *watch_time = base::TimeDelta::FromSecondsD(in_seconds);
337
338 DVLOG(2) << "Saved watch time for " << key << " of " << *watch_time;
339 } 323 }
340 324
341 enum class FinalizeType { EVERYTHING, POWER_ONLY }; 325 enum class FinalizeType { EVERYTHING, POWER_ONLY };
342 void FinalizeWatchTime(bool has_video, 326 void FinalizeWatchTime(bool has_video,
343 WatchTimeInfo* watch_time_info, 327 WatchTimeInfo* watch_time_info,
344 FinalizeType finalize_type) { 328 FinalizeType finalize_type) {
345 // Use a macro instead of a function so we can use the histogram macro (which 329 if (finalize_type == FinalizeType::EVERYTHING) {
346 // checks that the uma name is a static value). We use a custom time range for 330 for (auto& kv : *watch_time_info)
347 // the histogram macro to capitalize on common expected watch times. 331 RecordWatchTime(kv.first, kv.second);
348 #define MAYBE_RECORD_WATCH_TIME(uma_name, watch_time) \ 332 watch_time_info->clear();
349 if (watch_time_info->watch_time != media::kNoTimestamp) { \ 333 return;
350 UMA_HISTOGRAM_CUSTOM_TIMES( \ 334 }
351 media::MediaLog::uma_name, watch_time_info->watch_time, \
352 base::TimeDelta::FromSeconds(7), base::TimeDelta::FromHours(10), 50); \
353 watch_time_info->watch_time = media::kNoTimestamp; \
354 }
355 335
356 if (has_video) { 336 DCHECK_EQ(finalize_type, FinalizeType::POWER_ONLY);
357 if (finalize_type == FinalizeType::EVERYTHING) { 337 for (auto power_key : watch_time_power_keys_) {
358 MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioVideoAll, all_watch_time); 338 auto it = watch_time_info->find(power_key);
359 MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioVideoMse, mse_watch_time); 339 if (it == watch_time_info->end())
360 MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioVideoEme, eme_watch_time); 340 continue;
361 MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioVideoSrc, src_watch_time); 341 RecordWatchTime(it->first, it->second);
362 MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioVideoEmbeddedExperience, 342 watch_time_info->erase(it);
363 embedded_experience_watch_time);
364 } else {
365 DCHECK_EQ(finalize_type, FinalizeType::POWER_ONLY);
366 }
367 MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioVideoBattery, battery_watch_time);
368 MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioVideoAc, ac_watch_time);
369 } else {
370 if (finalize_type == FinalizeType::EVERYTHING) {
371 MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioAll, all_watch_time);
372 MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioMse, mse_watch_time);
373 MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioEme, eme_watch_time);
374 MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioSrc, src_watch_time);
375 MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioEmbeddedExperience,
376 embedded_experience_watch_time);
377 } else {
378 DCHECK_EQ(finalize_type, FinalizeType::POWER_ONLY);
379 }
380 MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioBattery, battery_watch_time);
381 MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioAc, ac_watch_time);
382 } 343 }
383 #undef MAYBE_RECORD_WATCH_TIME
384 } 344 }
385 345
386 // Key is player id. 346 // Key is player id.
387 typedef std::map<int, PipelineInfo> PlayerInfoMap; 347 typedef std::map<int, PipelineInfo> PlayerInfoMap;
388 348
389 // Key is renderer id. 349 // Key is renderer id.
390 typedef std::map<int, PlayerInfoMap> RendererPlayerMap; 350 typedef std::map<int, PlayerInfoMap> RendererPlayerMap;
391 351
392 // Stores player information per renderer. 352 // Stores player information per renderer.
393 RendererPlayerMap renderer_info_; 353 RendererPlayerMap renderer_info_;
394 354
355 const base::flat_set<base::StringPiece> watch_time_keys_;
356 const base::flat_set<base::StringPiece> watch_time_power_keys_;
357
395 DISALLOW_COPY_AND_ASSIGN(MediaInternalsUMAHandler); 358 DISALLOW_COPY_AND_ASSIGN(MediaInternalsUMAHandler);
396 }; 359 };
397 360
398 MediaInternals::MediaInternalsUMAHandler::MediaInternalsUMAHandler() { 361 MediaInternals::MediaInternalsUMAHandler::MediaInternalsUMAHandler()
399 } 362 : watch_time_keys_(media::MediaLog::GetWatchTimeKeys()),
363 watch_time_power_keys_(media::MediaLog::GetWatchTimePowerKeys()) {}
400 364
401 void MediaInternals::MediaInternalsUMAHandler::SavePlayerState( 365 void MediaInternals::MediaInternalsUMAHandler::SavePlayerState(
402 int render_process_id, 366 int render_process_id,
403 const media::MediaLogEvent& event) { 367 const media::MediaLogEvent& event) {
404 DCHECK_CURRENTLY_ON(BrowserThread::UI); 368 DCHECK_CURRENTLY_ON(BrowserThread::UI);
405 PlayerInfoMap& player_info = renderer_info_[render_process_id]; 369 PlayerInfoMap& player_info = renderer_info_[render_process_id];
406 switch (event.type) { 370 switch (event.type) {
407 case media::MediaLogEvent::PLAY: { 371 case media::MediaLogEvent::PLAY: {
408 player_info[event.id].has_ever_played = true; 372 player_info[event.id].has_ever_played = true;
409 break; 373 break;
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
451 if (event.params.HasKey("pipeline_buffering_state")) { 415 if (event.params.HasKey("pipeline_buffering_state")) {
452 std::string buffering_state; 416 std::string buffering_state;
453 event.params.GetString("pipeline_buffering_state", &buffering_state); 417 event.params.GetString("pipeline_buffering_state", &buffering_state);
454 if (buffering_state == "BUFFERING_HAVE_ENOUGH") 418 if (buffering_state == "BUFFERING_HAVE_ENOUGH")
455 player_info[event.id].has_reached_have_enough = true; 419 player_info[event.id].has_reached_have_enough = true;
456 } 420 }
457 break; 421 break;
458 case media::MediaLogEvent::Type::WATCH_TIME_UPDATE: { 422 case media::MediaLogEvent::Type::WATCH_TIME_UPDATE: {
459 DVLOG(2) << "Processing watch time update."; 423 DVLOG(2) << "Processing watch time update.";
460 PipelineInfo& info = player_info[event.id]; 424 PipelineInfo& info = player_info[event.id];
461 WatchTimeInfo& wti = info.watch_time_info;
462 // Save audio only watch time information.
463 MaybeSaveWatchTime(event, media::MediaLog::kWatchTimeAudioAll,
464 &wti.all_watch_time);
465 MaybeSaveWatchTime(event, media::MediaLog::kWatchTimeAudioMse,
466 &wti.mse_watch_time);
467 MaybeSaveWatchTime(event, media::MediaLog::kWatchTimeAudioEme,
468 &wti.eme_watch_time);
469 MaybeSaveWatchTime(event, media::MediaLog::kWatchTimeAudioSrc,
470 &wti.src_watch_time);
471 MaybeSaveWatchTime(event, media::MediaLog::kWatchTimeAudioBattery,
472 &wti.battery_watch_time);
473 MaybeSaveWatchTime(event, media::MediaLog::kWatchTimeAudioAc,
474 &wti.ac_watch_time);
475 MaybeSaveWatchTime(event,
476 media::MediaLog::kWatchTimeAudioEmbeddedExperience,
477 &wti.embedded_experience_watch_time);
478 425
479 // Save audio+video watch time information. 426 for (base::DictionaryValue::Iterator it(event.params); !it.IsAtEnd();
480 MaybeSaveWatchTime(event, media::MediaLog::kWatchTimeAudioVideoAll, 427 it.Advance()) {
481 &wti.all_watch_time); 428 // Don't log random histogram keys from the untrusted renderer, instead
482 MaybeSaveWatchTime(event, media::MediaLog::kWatchTimeAudioVideoMse, 429 // ensure they are from our list of known keys. Use |key_name| from the
483 &wti.mse_watch_time); 430 // key map, since otherwise we'll end up storing a StringPiece which
484 MaybeSaveWatchTime(event, media::MediaLog::kWatchTimeAudioVideoEme, 431 // points into the soon-to-be-destructed DictionaryValue.
485 &wti.eme_watch_time); 432 auto key_name = watch_time_keys_.find(it.key());
486 MaybeSaveWatchTime(event, media::MediaLog::kWatchTimeAudioVideoSrc, 433 if (key_name == watch_time_keys_.end())
487 &wti.src_watch_time); 434 continue;
488 MaybeSaveWatchTime(event, media::MediaLog::kWatchTimeAudioVideoBattery, 435 info.watch_time_info[*key_name] =
489 &wti.battery_watch_time); 436 base::TimeDelta::FromSecondsD(it.value().GetDouble());
490 MaybeSaveWatchTime(event, media::MediaLog::kWatchTimeAudioVideoAc, 437 }
491 &wti.ac_watch_time);
492 MaybeSaveWatchTime(
493 event, media::MediaLog::kWatchTimeAudioVideoEmbeddedExperience,
494 &wti.embedded_experience_watch_time);
495 438
496 if (event.params.HasKey(media::MediaLog::kWatchTimeFinalize)) { 439 if (event.params.HasKey(media::MediaLog::kWatchTimeFinalize)) {
497 bool should_finalize; 440 bool should_finalize;
498 DCHECK(event.params.GetBoolean(media::MediaLog::kWatchTimeFinalize, 441 DCHECK(event.params.GetBoolean(media::MediaLog::kWatchTimeFinalize,
499 &should_finalize) && 442 &should_finalize) &&
500 should_finalize); 443 should_finalize);
501 FinalizeWatchTime(info.has_video, &wti, FinalizeType::EVERYTHING); 444 FinalizeWatchTime(info.has_video, &info.watch_time_info,
445 FinalizeType::EVERYTHING);
502 } else if (event.params.HasKey( 446 } else if (event.params.HasKey(
503 media::MediaLog::kWatchTimeFinalizePower)) { 447 media::MediaLog::kWatchTimeFinalizePower)) {
504 bool should_finalize; 448 bool should_finalize;
505 DCHECK(event.params.GetBoolean(media::MediaLog::kWatchTimeFinalizePower, 449 DCHECK(event.params.GetBoolean(media::MediaLog::kWatchTimeFinalizePower,
506 &should_finalize) && 450 &should_finalize) &&
507 should_finalize); 451 should_finalize);
508 FinalizeWatchTime(info.has_video, &wti, FinalizeType::POWER_ONLY); 452 FinalizeWatchTime(info.has_video, &info.watch_time_info,
453 FinalizeType::POWER_ONLY);
509 } 454 }
510 break; 455 break;
511 } 456 }
512 case media::MediaLogEvent::Type::WEBMEDIAPLAYER_DESTROYED: { 457 case media::MediaLogEvent::Type::WEBMEDIAPLAYER_DESTROYED: {
513 // Upon player destruction report UMA data; if the player is not torn down 458 // Upon player destruction report UMA data; if the player is not torn down
514 // before process exit, it will be logged during OnProcessTerminated(). 459 // before process exit, it will be logged during OnProcessTerminated().
515 auto it = player_info.find(event.id); 460 auto it = player_info.find(event.id);
516 if (it == player_info.end()) 461 if (it == player_info.end())
517 break; 462 break;
518 463
519 ReportUMAForPipelineStatus(it->second); 464 ReportUMAForPipelineStatus(it->second);
465 FinalizeWatchTime(it->second.has_video, &(it->second.watch_time_info),
466 FinalizeType::EVERYTHING);
520 player_info.erase(it); 467 player_info.erase(it);
521 } 468 }
522 default: 469 default:
523 break; 470 break;
524 } 471 }
525 return; 472 return;
526 } 473 }
527 474
528 std::string MediaInternals::MediaInternalsUMAHandler::GetUMANameForAVStream( 475 std::string MediaInternals::MediaInternalsUMAHandler::GetUMANameForAVStream(
529 const PipelineInfo& player_info) { 476 const PipelineInfo& player_info) {
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after
803 750
804 void MediaInternals::SetWebContentsTitleForAudioLogEntry( 751 void MediaInternals::SetWebContentsTitleForAudioLogEntry(
805 int component_id, 752 int component_id,
806 int render_process_id, 753 int render_process_id,
807 int render_frame_id, 754 int render_frame_id,
808 media::AudioLog* audio_log) { 755 media::AudioLog* audio_log) {
809 static_cast<AudioLogImpl*>(audio_log) 756 static_cast<AudioLogImpl*>(audio_log)
810 ->SendWebContentsTitle(component_id, render_process_id, render_frame_id); 757 ->SendWebContentsTitle(component_id, render_process_id, render_frame_id);
811 } 758 }
812 759
760 void MediaInternals::OnProcessTerminatedForTesting(int process_id) {
761 uma_handler_->OnProcessTerminated(process_id);
762 }
763
813 void MediaInternals::SendUpdate(const base::string16& update) { 764 void MediaInternals::SendUpdate(const base::string16& update) {
814 // SendUpdate() may be called from any thread, but must run on the UI thread. 765 // SendUpdate() may be called from any thread, but must run on the UI thread.
815 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 766 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
816 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( 767 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
817 &MediaInternals::SendUpdate, base::Unretained(this), update)); 768 &MediaInternals::SendUpdate, base::Unretained(this), update));
818 return; 769 return;
819 } 770 }
820 771
821 for (size_t i = 0; i < update_callbacks_.size(); i++) 772 for (size_t i = 0; i < update_callbacks_.size(); i++)
822 update_callbacks_[i].Run(update); 773 update_callbacks_[i].Run(update);
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
872 audio_streams_cached_data_.GetDictionary(cache_key, &existing_dict)); 823 audio_streams_cached_data_.GetDictionary(cache_key, &existing_dict));
873 existing_dict->MergeDictionary(value); 824 existing_dict->MergeDictionary(value);
874 } 825 }
875 } 826 }
876 827
877 if (CanUpdate()) 828 if (CanUpdate())
878 SendUpdate(SerializeUpdate(function, value)); 829 SendUpdate(SerializeUpdate(function, value));
879 } 830 }
880 831
881 } // namespace content 832 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/media/media_internals.h ('k') | content/browser/media/media_internals_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698