Index: content/browser/media/media_internals.cc |
diff --git a/content/browser/media/media_internals.cc b/content/browser/media/media_internals.cc |
index 7f4d62ec96203080e2d9dc8065a1a2ff204c0560..d5348975a2d428361677be0f7d563430ce2845f1 100644 |
--- a/content/browser/media/media_internals.cc |
+++ b/content/browser/media/media_internals.cc |
@@ -9,8 +9,6 @@ |
#include "base/strings/string_number_conversions.h" |
#include "base/strings/stringprintf.h" |
#include "content/public/browser/browser_thread.h" |
-#include "content/public/browser/notification_observer.h" |
-#include "content/public/browser/notification_registrar.h" |
#include "content/public/browser/notification_service.h" |
#include "content/public/browser/notification_types.h" |
#include "content/public/browser/render_frame_host.h" |
@@ -249,22 +247,18 @@ void AudioLogImpl::StoreComponentMetadata(int component_id, |
dict->SetInteger("component_type", component_); |
} |
-class MediaInternals::MediaInternalsUMAHandler : public NotificationObserver { |
+class MediaInternals::MediaInternalsUMAHandler { |
public: |
MediaInternalsUMAHandler(); |
- // NotificationObserver implementation. |
- void Observe(int type, |
- const NotificationSource& source, |
- const NotificationDetails& details) override; |
- |
- // Reports the pipeline status to UMA for every player |
- // associated with the renderer process and then deletes the player state. |
- void LogAndClearPlayersInRenderer(int render_process_id); |
+ // Called when a render process is terminated. Reports the pipeline status to |
+ // UMA for every player associated with the renderer process and then deletes |
+ // the player state. |
+ void OnProcessTerminated(int render_process_id); |
// Helper function to save the event payload to RendererPlayerMap. |
- void SavePlayerState(const media::MediaLogEvent& event, |
- int render_process_id); |
+ void SavePlayerState(int render_process_id, |
+ const media::MediaLogEvent& event); |
private: |
struct PipelineInfo { |
@@ -299,38 +293,15 @@ class MediaInternals::MediaInternalsUMAHandler : public NotificationObserver { |
// Stores player information per renderer |
RendererPlayerMap renderer_info_; |
- NotificationRegistrar registrar_; |
- |
DISALLOW_COPY_AND_ASSIGN(MediaInternalsUMAHandler); |
}; |
MediaInternals::MediaInternalsUMAHandler::MediaInternalsUMAHandler() { |
- registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED, |
- NotificationService::AllBrowserContextsAndSources()); |
-} |
- |
-void MediaInternals::MediaInternalsUMAHandler::Observe( |
- int type, |
- const NotificationSource& source, |
- const NotificationDetails& details) { |
- DCHECK_CURRENTLY_ON(BrowserThread::UI); |
- DCHECK_EQ(type, NOTIFICATION_RENDERER_PROCESS_TERMINATED); |
- RenderProcessHost* process = Source<RenderProcessHost>(source).ptr(); |
- |
- // Post the task to the IO thread to avoid race in updating renderer_info_ map |
- // by both SavePlayerState & LogAndClearPlayersInRenderer from different |
- // threads. |
- // Using base::Unretained() on MediaInternalsUMAHandler is safe since |
- // it is owned by MediaInternals and share the same lifetime |
- BrowserThread::PostTask( |
- BrowserThread::IO, FROM_HERE, |
- base::Bind(&MediaInternalsUMAHandler::LogAndClearPlayersInRenderer, |
- base::Unretained(this), process->GetID())); |
} |
void MediaInternals::MediaInternalsUMAHandler::SavePlayerState( |
- const media::MediaLogEvent& event, |
- int render_process_id) { |
+ int render_process_id, |
+ const media::MediaLogEvent& event) { |
DCHECK_CURRENTLY_ON(BrowserThread::IO); |
PlayerInfoMap& player_info = renderer_info_[render_process_id]; |
switch (event.type) { |
@@ -439,9 +410,20 @@ void MediaInternals::MediaInternalsUMAHandler::ReportUMAForPipelineStatus( |
} |
} |
-void MediaInternals::MediaInternalsUMAHandler::LogAndClearPlayersInRenderer( |
+void MediaInternals::MediaInternalsUMAHandler::OnProcessTerminated( |
int render_process_id) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
+ // Ensures to run on the IO thread to avoid race in |updating renderer_info_| |
+ // by both SavePlayerState and OnProcessTerminated. |
+ // Using base::Unretained() on MediaInternalsUMAHandler is safe since |
+ // it is owned by MediaInternals and shares the same lifetime. |
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { |
+ BrowserThread::PostTask( |
+ BrowserThread::IO, FROM_HERE, |
+ base::Bind(&MediaInternalsUMAHandler::OnProcessTerminated, |
+ base::Unretained(this), render_process_id)); |
+ return; |
+ } |
+ |
auto players_it = renderer_info_.find(render_process_id); |
if (players_it == renderer_info_.end()) |
return; |
@@ -450,6 +432,7 @@ void MediaInternals::MediaInternalsUMAHandler::LogAndClearPlayersInRenderer( |
ReportUMAForPipelineStatus(it->second); |
players_it->second.erase(it++); |
} |
+ renderer_info_.erase(players_it); |
} |
MediaInternals* MediaInternals::GetInstance() { |
@@ -460,44 +443,74 @@ MediaInternals::MediaInternals() |
: can_update_(false), |
owner_ids_(), |
uma_handler_(new MediaInternalsUMAHandler()) { |
+ registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED, |
+ NotificationService::AllBrowserContextsAndSources()); |
} |
MediaInternals::~MediaInternals() {} |
+void MediaInternals::Observe(int type, |
+ const NotificationSource& source, |
+ const NotificationDetails& details) { |
+ DCHECK_CURRENTLY_ON(BrowserThread::UI); |
+ DCHECK_EQ(type, NOTIFICATION_RENDERER_PROCESS_TERMINATED); |
+ RenderProcessHost* process = Source<RenderProcessHost>(source).ptr(); |
+ |
+ uma_handler_->OnProcessTerminated(process->GetID()); |
+ |
+ base::AutoLock auto_lock(lock_); |
+ pending_events_map_.erase(process->GetID()); |
+} |
+ |
+// Converts the |event| to a |update|. Returns whether the conversion succeeded. |
+static bool ConvertEventToUpdate(int render_process_id, |
+ const media::MediaLogEvent& event, |
+ base::string16* update) { |
+ DCHECK(update); |
+ |
+ base::DictionaryValue dict; |
+ dict.SetInteger("renderer", render_process_id); |
+ dict.SetInteger("player", event.id); |
+ dict.SetString("type", media::MediaLog::EventTypeToString(event.type)); |
+ |
+ // TODO(dalecurtis): This is technically not correct. TimeTicks "can't" be |
+ // converted to to a human readable time format. See base/time/time.h. |
+ const double ticks = event.time.ToInternalValue(); |
+ const double ticks_millis = ticks / base::Time::kMicrosecondsPerMillisecond; |
+ dict.SetDouble("ticksMillis", ticks_millis); |
+ |
+ // Convert PipelineStatus to human readable string |
+ if (event.type == media::MediaLogEvent::PIPELINE_ERROR) { |
+ int status; |
+ if (!event.params.GetInteger("pipeline_error", &status) || |
+ status < static_cast<int>(media::PIPELINE_OK) || |
+ status > static_cast<int>(media::PIPELINE_STATUS_MAX)) { |
+ return false; |
+ } |
+ media::PipelineStatus error = static_cast<media::PipelineStatus>(status); |
+ dict.SetString("params.pipeline_error", |
+ media::MediaLog::PipelineStatusToString(error)); |
+ } else { |
+ dict.Set("params", event.params.DeepCopy()); |
+ } |
+ |
+ *update = SerializeUpdate("media.onMediaEvent", &dict); |
+ return true; |
+} |
+ |
void MediaInternals::OnMediaEvents( |
int render_process_id, const std::vector<media::MediaLogEvent>& events) { |
DCHECK_CURRENTLY_ON(BrowserThread::IO); |
// Notify observers that |event| has occurred. |
- for (auto event = events.begin(); event != events.end(); ++event) { |
- base::DictionaryValue dict; |
- dict.SetInteger("renderer", render_process_id); |
- dict.SetInteger("player", event->id); |
- dict.SetString("type", media::MediaLog::EventTypeToString(event->type)); |
- |
- // TODO(dalecurtis): This is technically not correct. TimeTicks "can't" be |
- // converted to to a human readable time format. See base/time/time.h. |
- const double ticks = event->time.ToInternalValue(); |
- const double ticks_millis = ticks / base::Time::kMicrosecondsPerMillisecond; |
- dict.SetDouble("ticksMillis", ticks_millis); |
- |
- // Convert PipelineStatus to human readable string |
- if (event->type == media::MediaLogEvent::PIPELINE_ERROR) { |
- int status; |
- if (!event->params.GetInteger("pipeline_error", &status) || |
- status < static_cast<int>(media::PIPELINE_OK) || |
- status > static_cast<int>(media::PIPELINE_STATUS_MAX)) { |
- continue; |
- } |
- media::PipelineStatus error = static_cast<media::PipelineStatus>(status); |
- dict.SetString("params.pipeline_error", |
- media::MediaLog::PipelineStatusToString(error)); |
- } else { |
- dict.Set("params", event->params.DeepCopy()); |
+ for (const auto& event : events) { |
+ if (CanUpdate()) { |
+ base::string16 update; |
+ if (ConvertEventToUpdate(render_process_id, event, &update)) |
+ SendUpdate(update); |
} |
- if (CanUpdate()) |
- SendUpdate(SerializeUpdate("media.onMediaEvent", &dict)); |
- uma_handler_->SavePlayerState(*event, render_process_id); |
+ SaveEvent(render_process_id, event); |
+ uma_handler_->SavePlayerState(render_process_id, event); |
} |
} |
@@ -527,6 +540,20 @@ bool MediaInternals::CanUpdate() { |
return can_update_; |
} |
+void MediaInternals::SendHistoricalMediaEvents() { |
+ DCHECK_CURRENTLY_ON(BrowserThread::UI); |
+ base::AutoLock auto_lock(lock_); |
+ for (const auto& pending_events : pending_events_map_) { |
+ for (const auto& event : pending_events.second) { |
+ base::string16 update; |
+ if (ConvertEventToUpdate(pending_events.first, event, &update)) |
+ SendUpdate(update); |
+ } |
+ } |
+ // Do not clear the map/list here so that refreshing the UI or opening a |
+ // second UI still works nicely! |
+} |
+ |
void MediaInternals::SendAudioStreamData() { |
base::string16 audio_stream_update; |
{ |
@@ -601,6 +628,29 @@ void MediaInternals::SendUpdate(const base::string16& update) { |
update_callbacks_[i].Run(update); |
} |
+void MediaInternals::SaveEvent(int process_id, |
+ const media::MediaLogEvent& event) { |
+ DCHECK_CURRENTLY_ON(BrowserThread::IO); |
+ |
+ // Max number of saved updates allowed for one process. |
+ const size_t kMaxNumEvents = 128; |
+ |
+ // Do not save instantaneous events that happen frequently and have little |
+ // value in the future. |
+ if (event.type == media::MediaLogEvent::NETWORK_ACTIVITY_SET || |
+ event.type == media::MediaLogEvent::BUFFERED_EXTENTS_CHANGED) { |
+ return; |
+ } |
+ |
+ base::AutoLock auto_lock(lock_); |
+ auto& pending_events = pending_events_map_[process_id]; |
+ // TODO(xhwang): Notify user that some old logs could have been truncated. |
+ // See http://crbug.com/498520 |
+ if (pending_events.size() >= kMaxNumEvents) |
+ pending_events.pop_front(); |
+ pending_events.push_back(event); |
+} |
+ |
void MediaInternals::SendAudioLogUpdate(AudioLogUpdateType type, |
const std::string& cache_key, |
const std::string& function, |