Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 key : watch_time_power_keys_) { |
|
sandersd (OOO until July 31)
2017/03/31 20:51:06
Recommend against using 'auto key' when the iterab
DaleCurtis
2017/04/01 00:51:35
Changed to power_key.
| |
| 358 MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioVideoAll, all_watch_time); | 338 auto it = watch_time_info->find(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 Loading... | |
| 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 342 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 872 audio_streams_cached_data_.GetDictionary(cache_key, &existing_dict)); | 819 audio_streams_cached_data_.GetDictionary(cache_key, &existing_dict)); |
| 873 existing_dict->MergeDictionary(value); | 820 existing_dict->MergeDictionary(value); |
| 874 } | 821 } |
| 875 } | 822 } |
| 876 | 823 |
| 877 if (CanUpdate()) | 824 if (CanUpdate()) |
| 878 SendUpdate(SerializeUpdate(function, value)); | 825 SendUpdate(SerializeUpdate(function, value)); |
| 879 } | 826 } |
| 880 | 827 |
| 881 } // namespace content | 828 } // namespace content |
| OLD | NEW |