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

Side by Side Diff: content/browser/media/media_internals.cc

Issue 1169983003: media-internals: Store updates when cannot update. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: comments addressed Created 5 years, 6 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 | « content/browser/media/media_internals.h ('k') | content/browser/media/media_internals_proxy.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 (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 "base/metrics/histogram.h" 7 #include "base/metrics/histogram.h"
8 #include "base/strings/string16.h" 8 #include "base/strings/string16.h"
9 #include "base/strings/string_number_conversions.h" 9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/stringprintf.h" 10 #include "base/strings/stringprintf.h"
11 #include "content/public/browser/browser_thread.h" 11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/notification_observer.h"
13 #include "content/public/browser/notification_registrar.h"
14 #include "content/public/browser/notification_service.h" 12 #include "content/public/browser/notification_service.h"
15 #include "content/public/browser/notification_types.h" 13 #include "content/public/browser/notification_types.h"
16 #include "content/public/browser/render_frame_host.h" 14 #include "content/public/browser/render_frame_host.h"
17 #include "content/public/browser/render_process_host.h" 15 #include "content/public/browser/render_process_host.h"
18 #include "content/public/browser/web_contents.h" 16 #include "content/public/browser/web_contents.h"
19 #include "content/public/browser/web_ui.h" 17 #include "content/public/browser/web_ui.h"
20 #include "media/audio/audio_parameters.h" 18 #include "media/audio/audio_parameters.h"
21 #include "media/base/media_log_event.h" 19 #include "media/base/media_log_event.h"
22 #include "media/filters/decrypting_video_decoder.h" 20 #include "media/filters/decrypting_video_decoder.h"
23 #include "media/filters/gpu_video_decoder.h" 21 #include "media/filters/gpu_video_decoder.h"
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
242 kAudioLogUpdateFunction, &dict); 240 kAudioLogUpdateFunction, &dict);
243 } 241 }
244 242
245 void AudioLogImpl::StoreComponentMetadata(int component_id, 243 void AudioLogImpl::StoreComponentMetadata(int component_id,
246 base::DictionaryValue* dict) { 244 base::DictionaryValue* dict) {
247 dict->SetInteger("owner_id", owner_id_); 245 dict->SetInteger("owner_id", owner_id_);
248 dict->SetInteger("component_id", component_id); 246 dict->SetInteger("component_id", component_id);
249 dict->SetInteger("component_type", component_); 247 dict->SetInteger("component_type", component_);
250 } 248 }
251 249
252 class MediaInternals::MediaInternalsUMAHandler : public NotificationObserver { 250 class MediaInternals::MediaInternalsUMAHandler {
253 public: 251 public:
254 MediaInternalsUMAHandler(); 252 MediaInternalsUMAHandler();
255 253
256 // NotificationObserver implementation. 254 // Called when a render process is terminated. Reports the pipeline status to
257 void Observe(int type, 255 // UMA for every player associated with the renderer process and then deletes
258 const NotificationSource& source, 256 // the player state.
259 const NotificationDetails& details) override; 257 void OnProcessTerminated(int render_process_id);
260
261 // Reports the pipeline status to UMA for every player
262 // associated with the renderer process and then deletes the player state.
263 void LogAndClearPlayersInRenderer(int render_process_id);
264 258
265 // Helper function to save the event payload to RendererPlayerMap. 259 // Helper function to save the event payload to RendererPlayerMap.
266 void SavePlayerState(const media::MediaLogEvent& event, 260 void SavePlayerState(int render_process_id,
267 int render_process_id); 261 const media::MediaLogEvent& event);
268 262
269 private: 263 private:
270 struct PipelineInfo { 264 struct PipelineInfo {
271 media::PipelineStatus last_pipeline_status; 265 media::PipelineStatus last_pipeline_status;
272 bool has_audio; 266 bool has_audio;
273 bool has_video; 267 bool has_video;
274 bool video_dds; 268 bool video_dds;
275 bool video_decoder_changed; 269 bool video_decoder_changed;
276 std::string audio_codec_name; 270 std::string audio_codec_name;
277 std::string video_codec_name; 271 std::string video_codec_name;
(...skipping 14 matching lines...) Expand all
292 286
293 // Key is playerid 287 // Key is playerid
294 typedef std::map<int, PipelineInfo> PlayerInfoMap; 288 typedef std::map<int, PipelineInfo> PlayerInfoMap;
295 289
296 // Key is renderer id 290 // Key is renderer id
297 typedef std::map<int, PlayerInfoMap> RendererPlayerMap; 291 typedef std::map<int, PlayerInfoMap> RendererPlayerMap;
298 292
299 // Stores player information per renderer 293 // Stores player information per renderer
300 RendererPlayerMap renderer_info_; 294 RendererPlayerMap renderer_info_;
301 295
302 NotificationRegistrar registrar_;
303
304 DISALLOW_COPY_AND_ASSIGN(MediaInternalsUMAHandler); 296 DISALLOW_COPY_AND_ASSIGN(MediaInternalsUMAHandler);
305 }; 297 };
306 298
307 MediaInternals::MediaInternalsUMAHandler::MediaInternalsUMAHandler() { 299 MediaInternals::MediaInternalsUMAHandler::MediaInternalsUMAHandler() {
308 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
309 NotificationService::AllBrowserContextsAndSources());
310 }
311
312 void MediaInternals::MediaInternalsUMAHandler::Observe(
313 int type,
314 const NotificationSource& source,
315 const NotificationDetails& details) {
316 DCHECK_CURRENTLY_ON(BrowserThread::UI);
317 DCHECK_EQ(type, NOTIFICATION_RENDERER_PROCESS_TERMINATED);
318 RenderProcessHost* process = Source<RenderProcessHost>(source).ptr();
319
320 // Post the task to the IO thread to avoid race in updating renderer_info_ map
321 // by both SavePlayerState & LogAndClearPlayersInRenderer from different
322 // threads.
323 // Using base::Unretained() on MediaInternalsUMAHandler is safe since
324 // it is owned by MediaInternals and share the same lifetime
325 BrowserThread::PostTask(
326 BrowserThread::IO, FROM_HERE,
327 base::Bind(&MediaInternalsUMAHandler::LogAndClearPlayersInRenderer,
328 base::Unretained(this), process->GetID()));
329 } 300 }
330 301
331 void MediaInternals::MediaInternalsUMAHandler::SavePlayerState( 302 void MediaInternals::MediaInternalsUMAHandler::SavePlayerState(
332 const media::MediaLogEvent& event, 303 int render_process_id,
333 int render_process_id) { 304 const media::MediaLogEvent& event) {
334 DCHECK_CURRENTLY_ON(BrowserThread::IO); 305 DCHECK_CURRENTLY_ON(BrowserThread::IO);
335 PlayerInfoMap& player_info = renderer_info_[render_process_id]; 306 PlayerInfoMap& player_info = renderer_info_[render_process_id];
336 switch (event.type) { 307 switch (event.type) {
337 case media::MediaLogEvent::PIPELINE_ERROR: { 308 case media::MediaLogEvent::PIPELINE_ERROR: {
338 int status; 309 int status;
339 event.params.GetInteger("pipeline_error", &status); 310 event.params.GetInteger("pipeline_error", &status);
340 player_info[event.id].last_pipeline_status = 311 player_info[event.id].last_pipeline_status =
341 static_cast<media::PipelineStatus>(status); 312 static_cast<media::PipelineStatus>(status);
342 break; 313 break;
343 } 314 }
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
432 media::PIPELINE_STATUS_MAX + 1); 403 media::PIPELINE_STATUS_MAX + 1);
433 } 404 }
434 // Report whether video decoder fallback happened, but only if a video decoder 405 // Report whether video decoder fallback happened, but only if a video decoder
435 // was reported. 406 // was reported.
436 if (!player_info.video_decoder.empty()) { 407 if (!player_info.video_decoder.empty()) {
437 UMA_HISTOGRAM_BOOLEAN("Media.VideoDecoderFallback", 408 UMA_HISTOGRAM_BOOLEAN("Media.VideoDecoderFallback",
438 player_info.video_decoder_changed); 409 player_info.video_decoder_changed);
439 } 410 }
440 } 411 }
441 412
442 void MediaInternals::MediaInternalsUMAHandler::LogAndClearPlayersInRenderer( 413 void MediaInternals::MediaInternalsUMAHandler::OnProcessTerminated(
443 int render_process_id) { 414 int render_process_id) {
444 DCHECK_CURRENTLY_ON(BrowserThread::IO); 415 // Ensures to run on the IO thread to avoid race in |updating renderer_info_|
416 // by both SavePlayerState and OnProcessTerminated.
417 // Using base::Unretained() on MediaInternalsUMAHandler is safe since
418 // it is owned by MediaInternals and shares the same lifetime.
419 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
420 BrowserThread::PostTask(
421 BrowserThread::IO, FROM_HERE,
422 base::Bind(&MediaInternalsUMAHandler::OnProcessTerminated,
423 base::Unretained(this), render_process_id));
424 return;
425 }
426
445 auto players_it = renderer_info_.find(render_process_id); 427 auto players_it = renderer_info_.find(render_process_id);
446 if (players_it == renderer_info_.end()) 428 if (players_it == renderer_info_.end())
447 return; 429 return;
448 auto it = players_it->second.begin(); 430 auto it = players_it->second.begin();
449 while (it != players_it->second.end()) { 431 while (it != players_it->second.end()) {
450 ReportUMAForPipelineStatus(it->second); 432 ReportUMAForPipelineStatus(it->second);
451 players_it->second.erase(it++); 433 players_it->second.erase(it++);
452 } 434 }
435 renderer_info_.erase(players_it);
453 } 436 }
454 437
455 MediaInternals* MediaInternals::GetInstance() { 438 MediaInternals* MediaInternals::GetInstance() {
456 return g_media_internals.Pointer(); 439 return g_media_internals.Pointer();
457 } 440 }
458 441
459 MediaInternals::MediaInternals() 442 MediaInternals::MediaInternals()
460 : can_update_(false), 443 : can_update_(false),
461 owner_ids_(), 444 owner_ids_(),
462 uma_handler_(new MediaInternalsUMAHandler()) { 445 uma_handler_(new MediaInternalsUMAHandler()) {
446 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
447 NotificationService::AllBrowserContextsAndSources());
463 } 448 }
464 449
465 MediaInternals::~MediaInternals() {} 450 MediaInternals::~MediaInternals() {}
466 451
452 void MediaInternals::Observe(int type,
453 const NotificationSource& source,
454 const NotificationDetails& details) {
455 DCHECK_CURRENTLY_ON(BrowserThread::UI);
456 DCHECK_EQ(type, NOTIFICATION_RENDERER_PROCESS_TERMINATED);
457 RenderProcessHost* process = Source<RenderProcessHost>(source).ptr();
458
459 uma_handler_->OnProcessTerminated(process->GetID());
460
461 base::AutoLock auto_lock(lock_);
462 pending_events_map_.erase(process->GetID());
463 }
464
465 // Converts the |event| to a |update|. Returns whether the conversion succeeded.
466 static bool ConvertEventToUpdate(int render_process_id,
467 const media::MediaLogEvent& event,
468 base::string16* update) {
469 DCHECK(update);
470
471 base::DictionaryValue dict;
472 dict.SetInteger("renderer", render_process_id);
473 dict.SetInteger("player", event.id);
474 dict.SetString("type", media::MediaLog::EventTypeToString(event.type));
475
476 // TODO(dalecurtis): This is technically not correct. TimeTicks "can't" be
477 // converted to to a human readable time format. See base/time/time.h.
478 const double ticks = event.time.ToInternalValue();
479 const double ticks_millis = ticks / base::Time::kMicrosecondsPerMillisecond;
480 dict.SetDouble("ticksMillis", ticks_millis);
481
482 // Convert PipelineStatus to human readable string
483 if (event.type == media::MediaLogEvent::PIPELINE_ERROR) {
484 int status;
485 if (!event.params.GetInteger("pipeline_error", &status) ||
486 status < static_cast<int>(media::PIPELINE_OK) ||
487 status > static_cast<int>(media::PIPELINE_STATUS_MAX)) {
488 return false;
489 }
490 media::PipelineStatus error = static_cast<media::PipelineStatus>(status);
491 dict.SetString("params.pipeline_error",
492 media::MediaLog::PipelineStatusToString(error));
493 } else {
494 dict.Set("params", event.params.DeepCopy());
495 }
496
497 *update = SerializeUpdate("media.onMediaEvent", &dict);
498 return true;
499 }
500
467 void MediaInternals::OnMediaEvents( 501 void MediaInternals::OnMediaEvents(
468 int render_process_id, const std::vector<media::MediaLogEvent>& events) { 502 int render_process_id, const std::vector<media::MediaLogEvent>& events) {
469 DCHECK_CURRENTLY_ON(BrowserThread::IO); 503 DCHECK_CURRENTLY_ON(BrowserThread::IO);
470 // Notify observers that |event| has occurred. 504 // Notify observers that |event| has occurred.
471 for (auto event = events.begin(); event != events.end(); ++event) { 505 for (const auto& event : events) {
472 base::DictionaryValue dict; 506 if (CanUpdate()) {
473 dict.SetInteger("renderer", render_process_id); 507 base::string16 update;
474 dict.SetInteger("player", event->id); 508 if (ConvertEventToUpdate(render_process_id, event, &update))
475 dict.SetString("type", media::MediaLog::EventTypeToString(event->type)); 509 SendUpdate(update);
476
477 // TODO(dalecurtis): This is technically not correct. TimeTicks "can't" be
478 // converted to to a human readable time format. See base/time/time.h.
479 const double ticks = event->time.ToInternalValue();
480 const double ticks_millis = ticks / base::Time::kMicrosecondsPerMillisecond;
481 dict.SetDouble("ticksMillis", ticks_millis);
482
483 // Convert PipelineStatus to human readable string
484 if (event->type == media::MediaLogEvent::PIPELINE_ERROR) {
485 int status;
486 if (!event->params.GetInteger("pipeline_error", &status) ||
487 status < static_cast<int>(media::PIPELINE_OK) ||
488 status > static_cast<int>(media::PIPELINE_STATUS_MAX)) {
489 continue;
490 }
491 media::PipelineStatus error = static_cast<media::PipelineStatus>(status);
492 dict.SetString("params.pipeline_error",
493 media::MediaLog::PipelineStatusToString(error));
494 } else {
495 dict.Set("params", event->params.DeepCopy());
496 } 510 }
497 511
498 if (CanUpdate()) 512 SaveEvent(render_process_id, event);
499 SendUpdate(SerializeUpdate("media.onMediaEvent", &dict)); 513 uma_handler_->SavePlayerState(render_process_id, event);
500 uma_handler_->SavePlayerState(*event, render_process_id);
501 } 514 }
502 } 515 }
503 516
504 void MediaInternals::AddUpdateCallback(const UpdateCallback& callback) { 517 void MediaInternals::AddUpdateCallback(const UpdateCallback& callback) {
505 DCHECK_CURRENTLY_ON(BrowserThread::UI); 518 DCHECK_CURRENTLY_ON(BrowserThread::UI);
506 update_callbacks_.push_back(callback); 519 update_callbacks_.push_back(callback);
507 520
508 base::AutoLock auto_lock(lock_); 521 base::AutoLock auto_lock(lock_);
509 can_update_ = true; 522 can_update_ = true;
510 } 523 }
511 524
512 void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) { 525 void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) {
513 DCHECK_CURRENTLY_ON(BrowserThread::UI); 526 DCHECK_CURRENTLY_ON(BrowserThread::UI);
514 for (size_t i = 0; i < update_callbacks_.size(); ++i) { 527 for (size_t i = 0; i < update_callbacks_.size(); ++i) {
515 if (update_callbacks_[i].Equals(callback)) { 528 if (update_callbacks_[i].Equals(callback)) {
516 update_callbacks_.erase(update_callbacks_.begin() + i); 529 update_callbacks_.erase(update_callbacks_.begin() + i);
517 break; 530 break;
518 } 531 }
519 } 532 }
520 533
521 base::AutoLock auto_lock(lock_); 534 base::AutoLock auto_lock(lock_);
522 can_update_ = !update_callbacks_.empty(); 535 can_update_ = !update_callbacks_.empty();
523 } 536 }
524 537
525 bool MediaInternals::CanUpdate() { 538 bool MediaInternals::CanUpdate() {
526 base::AutoLock auto_lock(lock_); 539 base::AutoLock auto_lock(lock_);
527 return can_update_; 540 return can_update_;
528 } 541 }
529 542
543 void MediaInternals::SendHistoricalMediaEvents() {
544 DCHECK_CURRENTLY_ON(BrowserThread::UI);
545 base::AutoLock auto_lock(lock_);
546 for (const auto& pending_events : pending_events_map_) {
547 for (const auto& event : pending_events.second) {
548 base::string16 update;
549 if (ConvertEventToUpdate(pending_events.first, event, &update))
550 SendUpdate(update);
551 }
552 }
553 // Do not clear the map/list here so that refreshing the UI or opening a
554 // second UI still works nicely!
555 }
556
530 void MediaInternals::SendAudioStreamData() { 557 void MediaInternals::SendAudioStreamData() {
531 base::string16 audio_stream_update; 558 base::string16 audio_stream_update;
532 { 559 {
533 base::AutoLock auto_lock(lock_); 560 base::AutoLock auto_lock(lock_);
534 audio_stream_update = SerializeUpdate( 561 audio_stream_update = SerializeUpdate(
535 "media.onReceiveAudioStreamData", &audio_streams_cached_data_); 562 "media.onReceiveAudioStreamData", &audio_streams_cached_data_);
536 } 563 }
537 SendUpdate(audio_stream_update); 564 SendUpdate(audio_stream_update);
538 } 565 }
539 566
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
594 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 621 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
595 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( 622 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
596 &MediaInternals::SendUpdate, base::Unretained(this), update)); 623 &MediaInternals::SendUpdate, base::Unretained(this), update));
597 return; 624 return;
598 } 625 }
599 626
600 for (size_t i = 0; i < update_callbacks_.size(); i++) 627 for (size_t i = 0; i < update_callbacks_.size(); i++)
601 update_callbacks_[i].Run(update); 628 update_callbacks_[i].Run(update);
602 } 629 }
603 630
631 void MediaInternals::SaveEvent(int process_id,
632 const media::MediaLogEvent& event) {
633 DCHECK_CURRENTLY_ON(BrowserThread::IO);
634
635 // Max number of saved updates allowed for one process.
636 const size_t kMaxNumEvents = 128;
637
638 // Do not save instantaneous events that happen frequently and have little
639 // value in the future.
640 if (event.type == media::MediaLogEvent::NETWORK_ACTIVITY_SET ||
641 event.type == media::MediaLogEvent::BUFFERED_EXTENTS_CHANGED) {
642 return;
643 }
644
645 base::AutoLock auto_lock(lock_);
646 auto& pending_events = pending_events_map_[process_id];
647 // TODO(xhwang): Notify user that some old logs could have been truncated.
648 // See http://crbug.com/498520
649 if (pending_events.size() >= kMaxNumEvents)
650 pending_events.pop_front();
651 pending_events.push_back(event);
652 }
653
604 void MediaInternals::SendAudioLogUpdate(AudioLogUpdateType type, 654 void MediaInternals::SendAudioLogUpdate(AudioLogUpdateType type,
605 const std::string& cache_key, 655 const std::string& cache_key,
606 const std::string& function, 656 const std::string& function,
607 const base::DictionaryValue* value) { 657 const base::DictionaryValue* value) {
608 if (!CanUpdate()) 658 if (!CanUpdate())
609 return; 659 return;
610 660
611 { 661 {
612 base::AutoLock auto_lock(lock_); 662 base::AutoLock auto_lock(lock_);
613 const bool has_entry = audio_streams_cached_data_.HasKey(cache_key); 663 const bool has_entry = audio_streams_cached_data_.HasKey(cache_key);
(...skipping 10 matching lines...) Expand all
624 CHECK( 674 CHECK(
625 audio_streams_cached_data_.GetDictionary(cache_key, &existing_dict)); 675 audio_streams_cached_data_.GetDictionary(cache_key, &existing_dict));
626 existing_dict->MergeDictionary(value); 676 existing_dict->MergeDictionary(value);
627 } 677 }
628 } 678 }
629 679
630 SendUpdate(SerializeUpdate(function, value)); 680 SendUpdate(SerializeUpdate(function, value));
631 } 681 }
632 682
633 } // namespace content 683 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/media/media_internals.h ('k') | content/browser/media/media_internals_proxy.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698