blob: c3b87ce94851c3ed495b92c0499e4e1862646d6b [file] [log] [blame]
[email protected]11158e2d2013-02-01 02:31:561// 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
3// found in the LICENSE file.
4
5#include "content/browser/media/media_internals.h"
6
prabhur53bb9182014-11-13 03:25:177#include "base/metrics/histogram.h"
[email protected]348fbaac2013-06-11 06:31:518#include "base/strings/string16.h"
[email protected]9367e032014-03-07 19:42:379#include "base/strings/string_number_conversions.h"
[email protected]348fbaac2013-06-11 06:31:5110#include "base/strings/stringprintf.h"
[email protected]11158e2d2013-02-01 02:31:5611#include "content/public/browser/browser_thread.h"
prabhur53bb9182014-11-13 03:25:1712#include "content/public/browser/notification_service.h"
13#include "content/public/browser/notification_types.h"
dalecurtise6aa75f2015-03-31 02:39:3814#include "content/public/browser/render_frame_host.h"
prabhur53bb9182014-11-13 03:25:1715#include "content/public/browser/render_process_host.h"
dalecurtise6aa75f2015-03-31 02:39:3816#include "content/public/browser/web_contents.h"
[email protected]11158e2d2013-02-01 02:31:5617#include "content/public/browser/web_ui.h"
[email protected]e2fd1f72013-08-16 00:34:2518#include "media/audio/audio_parameters.h"
[email protected]11158e2d2013-02-01 02:31:5619#include "media/base/media_log_event.h"
prabhurc4812392014-12-05 19:59:4220#include "media/filters/decrypting_video_decoder.h"
prabhur957d46c72014-11-19 03:01:1621#include "media/filters/gpu_video_decoder.h"
[email protected]11158e2d2013-02-01 02:31:5622
[email protected]69946cf2013-11-27 00:11:4223namespace {
24
25static base::LazyInstance<content::MediaInternals>::Leaky g_media_internals =
26 LAZY_INSTANCE_INITIALIZER;
27
[email protected]fcf75d42013-12-03 20:11:2628base::string16 SerializeUpdate(const std::string& function,
29 const base::Value* value) {
[email protected]69946cf2013-11-27 00:11:4230 return content::WebUI::GetJavascriptCall(
31 function, std::vector<const base::Value*>(1, value));
32}
33
[email protected]9367e032014-03-07 19:42:3734std::string EffectsToString(int effects) {
35 if (effects == media::AudioParameters::NO_EFFECTS)
36 return "NO_EFFECTS";
37
38 struct {
39 int flag;
40 const char* name;
41 } flags[] = {
42 { media::AudioParameters::ECHO_CANCELLER, "ECHO_CANCELLER" },
43 { media::AudioParameters::DUCKING, "DUCKING" },
[email protected]81495eb2014-03-19 06:08:1844 { media::AudioParameters::KEYBOARD_MIC, "KEYBOARD_MIC" },
dalecurtisb9a6b7872015-02-06 22:41:5545 { media::AudioParameters::HOTWORD, "HOTWORD" },
[email protected]9367e032014-03-07 19:42:3746 };
47
48 std::string ret;
viettrungluu2dfaba72014-10-16 05:30:2549 for (size_t i = 0; i < arraysize(flags); ++i) {
[email protected]9367e032014-03-07 19:42:3750 if (effects & flags[i].flag) {
51 if (!ret.empty())
52 ret += " | ";
53 ret += flags[i].name;
54 effects &= ~flags[i].flag;
55 }
56 }
57
58 if (effects) {
59 if (!ret.empty())
60 ret += " | ";
61 ret += base::IntToString(effects);
62 }
63
64 return ret;
65}
66
dalecurtisb9a6b7872015-02-06 22:41:5567std::string FormatToString(media::AudioParameters::Format format) {
68 switch (format) {
69 case media::AudioParameters::AUDIO_PCM_LINEAR:
70 return "pcm_linear";
71 case media::AudioParameters::AUDIO_PCM_LOW_LATENCY:
72 return "pcm_low_latency";
73 case media::AudioParameters::AUDIO_FAKE:
74 return "fake";
75 case media::AudioParameters::AUDIO_LAST_FORMAT:
76 break;
77 }
78
79 NOTREACHED();
80 return "unknown";
81}
82
[email protected]69946cf2013-11-27 00:11:4283const char kAudioLogStatusKey[] = "status";
84const char kAudioLogUpdateFunction[] = "media.updateAudioComponent";
85
86} // namespace
87
[email protected]11158e2d2013-02-01 02:31:5688namespace content {
89
[email protected]69946cf2013-11-27 00:11:4290class AudioLogImpl : public media::AudioLog {
91 public:
92 AudioLogImpl(int owner_id,
93 media::AudioLogFactory::AudioComponent component,
94 content::MediaInternals* media_internals);
dchengc2282aa2014-10-21 12:07:5895 ~AudioLogImpl() override;
[email protected]69946cf2013-11-27 00:11:4296
dchengc2282aa2014-10-21 12:07:5897 void OnCreated(int component_id,
98 const media::AudioParameters& params,
99 const std::string& device_id) override;
100 void OnStarted(int component_id) override;
101 void OnStopped(int component_id) override;
102 void OnClosed(int component_id) override;
103 void OnError(int component_id) override;
104 void OnSetVolume(int component_id, double volume) override;
[email protected]69946cf2013-11-27 00:11:42105
dalecurtise6aa75f2015-03-31 02:39:38106 // Called by MediaInternals to update the WebContents title for a stream.
107 void SendWebContentsTitle(int component_id,
108 int render_process_id,
109 int render_frame_id);
110
[email protected]69946cf2013-11-27 00:11:42111 private:
112 void SendSingleStringUpdate(int component_id,
113 const std::string& key,
114 const std::string& value);
115 void StoreComponentMetadata(int component_id, base::DictionaryValue* dict);
116 std::string FormatCacheKey(int component_id);
117
dalecurtise6aa75f2015-03-31 02:39:38118 static void SendWebContentsTitleHelper(const std::string& cache_key,
119 scoped_ptr<base::DictionaryValue> dict,
120 int render_process_id,
121 int render_frame_id);
122
[email protected]69946cf2013-11-27 00:11:42123 const int owner_id_;
124 const media::AudioLogFactory::AudioComponent component_;
125 content::MediaInternals* const media_internals_;
126
127 DISALLOW_COPY_AND_ASSIGN(AudioLogImpl);
128};
129
130AudioLogImpl::AudioLogImpl(int owner_id,
131 media::AudioLogFactory::AudioComponent component,
132 content::MediaInternals* media_internals)
133 : owner_id_(owner_id),
134 component_(component),
135 media_internals_(media_internals) {}
136
137AudioLogImpl::~AudioLogImpl() {}
138
139void AudioLogImpl::OnCreated(int component_id,
140 const media::AudioParameters& params,
[email protected]25d7f892014-02-13 15:22:45141 const std::string& device_id) {
[email protected]69946cf2013-11-27 00:11:42142 base::DictionaryValue dict;
143 StoreComponentMetadata(component_id, &dict);
144
145 dict.SetString(kAudioLogStatusKey, "created");
[email protected]25d7f892014-02-13 15:22:45146 dict.SetString("device_id", device_id);
dalecurtisb9a6b7872015-02-06 22:41:55147 dict.SetString("device_type", FormatToString(params.format()));
[email protected]69946cf2013-11-27 00:11:42148 dict.SetInteger("frames_per_buffer", params.frames_per_buffer());
149 dict.SetInteger("sample_rate", params.sample_rate());
[email protected]ad82f412013-11-27 04:20:41150 dict.SetInteger("channels", params.channels());
151 dict.SetString("channel_layout",
[email protected]69946cf2013-11-27 00:11:42152 ChannelLayoutToString(params.channel_layout()));
[email protected]9367e032014-03-07 19:42:37153 dict.SetString("effects", EffectsToString(params.effects()));
[email protected]69946cf2013-11-27 00:11:42154
dalecurtise6aa75f2015-03-31 02:39:38155 media_internals_->SendAudioLogUpdate(MediaInternals::CREATE,
156 FormatCacheKey(component_id),
157 kAudioLogUpdateFunction, &dict);
[email protected]69946cf2013-11-27 00:11:42158}
159
160void AudioLogImpl::OnStarted(int component_id) {
161 SendSingleStringUpdate(component_id, kAudioLogStatusKey, "started");
162}
163
164void AudioLogImpl::OnStopped(int component_id) {
165 SendSingleStringUpdate(component_id, kAudioLogStatusKey, "stopped");
166}
167
168void AudioLogImpl::OnClosed(int component_id) {
169 base::DictionaryValue dict;
170 StoreComponentMetadata(component_id, &dict);
171 dict.SetString(kAudioLogStatusKey, "closed");
dalecurtise6aa75f2015-03-31 02:39:38172 media_internals_->SendAudioLogUpdate(MediaInternals::UPDATE_AND_DELETE,
173 FormatCacheKey(component_id),
174 kAudioLogUpdateFunction, &dict);
[email protected]69946cf2013-11-27 00:11:42175}
176
177void AudioLogImpl::OnError(int component_id) {
178 SendSingleStringUpdate(component_id, "error_occurred", "true");
179}
180
181void AudioLogImpl::OnSetVolume(int component_id, double volume) {
182 base::DictionaryValue dict;
183 StoreComponentMetadata(component_id, &dict);
184 dict.SetDouble("volume", volume);
dalecurtise6aa75f2015-03-31 02:39:38185 media_internals_->SendAudioLogUpdate(MediaInternals::UPDATE_IF_EXISTS,
186 FormatCacheKey(component_id),
187 kAudioLogUpdateFunction, &dict);
188}
189
190void AudioLogImpl::SendWebContentsTitle(int component_id,
191 int render_process_id,
192 int render_frame_id) {
193 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
194 StoreComponentMetadata(component_id, dict.get());
195 SendWebContentsTitleHelper(FormatCacheKey(component_id), dict.Pass(),
196 render_process_id, render_frame_id);
[email protected]69946cf2013-11-27 00:11:42197}
198
199std::string AudioLogImpl::FormatCacheKey(int component_id) {
200 return base::StringPrintf("%d:%d:%d", owner_id_, component_, component_id);
201}
202
dalecurtise6aa75f2015-03-31 02:39:38203// static
204void AudioLogImpl::SendWebContentsTitleHelper(
205 const std::string& cache_key,
206 scoped_ptr<base::DictionaryValue> dict,
207 int render_process_id,
208 int render_frame_id) {
209 // Page title information can only be retrieved from the UI thread.
210 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
211 BrowserThread::PostTask(
212 BrowserThread::UI, FROM_HERE,
213 base::Bind(&SendWebContentsTitleHelper, cache_key, base::Passed(&dict),
214 render_process_id, render_frame_id));
215 return;
216 }
217
218 const WebContents* web_contents = WebContents::FromRenderFrameHost(
219 RenderFrameHost::FromID(render_process_id, render_frame_id));
220 if (!web_contents)
221 return;
222
223 // Note: by this point the given audio log entry could have been destroyed, so
224 // we use UPDATE_IF_EXISTS to discard such instances.
225 dict->SetInteger("render_process_id", render_process_id);
226 dict->SetString("web_contents_title", web_contents->GetTitle());
227 MediaInternals::GetInstance()->SendAudioLogUpdate(
228 MediaInternals::UPDATE_IF_EXISTS, cache_key, kAudioLogUpdateFunction,
229 dict.get());
230}
231
[email protected]69946cf2013-11-27 00:11:42232void AudioLogImpl::SendSingleStringUpdate(int component_id,
233 const std::string& key,
234 const std::string& value) {
235 base::DictionaryValue dict;
236 StoreComponentMetadata(component_id, &dict);
237 dict.SetString(key, value);
dalecurtise6aa75f2015-03-31 02:39:38238 media_internals_->SendAudioLogUpdate(MediaInternals::UPDATE_IF_EXISTS,
239 FormatCacheKey(component_id),
240 kAudioLogUpdateFunction, &dict);
[email protected]69946cf2013-11-27 00:11:42241}
242
243void AudioLogImpl::StoreComponentMetadata(int component_id,
244 base::DictionaryValue* dict) {
245 dict->SetInteger("owner_id", owner_id_);
246 dict->SetInteger("component_id", component_id);
247 dict->SetInteger("component_type", component_);
248}
249
xhwang0fdb8312015-06-10 23:15:38250// This class lives on the browser UI thread.
xhwange63e59b8d2015-06-10 00:45:35251class MediaInternals::MediaInternalsUMAHandler {
prabhur53bb9182014-11-13 03:25:17252 public:
253 MediaInternalsUMAHandler();
254
xhwange63e59b8d2015-06-10 00:45:35255 // Called when a render process is terminated. Reports the pipeline status to
256 // UMA for every player associated with the renderer process and then deletes
257 // the player state.
258 void OnProcessTerminated(int render_process_id);
prabhur53bb9182014-11-13 03:25:17259
260 // Helper function to save the event payload to RendererPlayerMap.
xhwange63e59b8d2015-06-10 00:45:35261 void SavePlayerState(int render_process_id,
262 const media::MediaLogEvent& event);
prabhur53bb9182014-11-13 03:25:17263
264 private:
265 struct PipelineInfo {
266 media::PipelineStatus last_pipeline_status;
267 bool has_audio;
268 bool has_video;
prabhurc4812392014-12-05 19:59:42269 bool video_dds;
watkc85d60e72015-01-14 19:08:28270 bool video_decoder_changed;
prabhur53bb9182014-11-13 03:25:17271 std::string audio_codec_name;
272 std::string video_codec_name;
273 std::string video_decoder;
274 PipelineInfo()
275 : last_pipeline_status(media::PIPELINE_OK),
276 has_audio(false),
prabhurc4812392014-12-05 19:59:42277 has_video(false),
watkc85d60e72015-01-14 19:08:28278 video_dds(false),
279 video_decoder_changed(false) {}
prabhur53bb9182014-11-13 03:25:17280 };
281
282 // Helper function to report PipelineStatus associated with a player to UMA.
283 void ReportUMAForPipelineStatus(const PipelineInfo& player_info);
284
prabhurc4812392014-12-05 19:59:42285 // Helper to generate PipelineStatus UMA name for AudioVideo streams.
286 std::string GetUMANameForAVStream(const PipelineInfo& player_info);
287
xhwang0fdb8312015-06-10 23:15:38288 // Key is player id.
prabhur53bb9182014-11-13 03:25:17289 typedef std::map<int, PipelineInfo> PlayerInfoMap;
290
xhwang0fdb8312015-06-10 23:15:38291 // Key is renderer id.
prabhur53bb9182014-11-13 03:25:17292 typedef std::map<int, PlayerInfoMap> RendererPlayerMap;
293
xhwang0fdb8312015-06-10 23:15:38294 // Stores player information per renderer.
prabhur53bb9182014-11-13 03:25:17295 RendererPlayerMap renderer_info_;
296
prabhur53bb9182014-11-13 03:25:17297 DISALLOW_COPY_AND_ASSIGN(MediaInternalsUMAHandler);
298};
299
300MediaInternals::MediaInternalsUMAHandler::MediaInternalsUMAHandler() {
prabhur53bb9182014-11-13 03:25:17301}
302
303void MediaInternals::MediaInternalsUMAHandler::SavePlayerState(
xhwange63e59b8d2015-06-10 00:45:35304 int render_process_id,
305 const media::MediaLogEvent& event) {
xhwang0fdb8312015-06-10 23:15:38306 DCHECK_CURRENTLY_ON(BrowserThread::UI);
prabhur53bb9182014-11-13 03:25:17307 PlayerInfoMap& player_info = renderer_info_[render_process_id];
308 switch (event.type) {
prabhur53bb9182014-11-13 03:25:17309 case media::MediaLogEvent::PIPELINE_ERROR: {
310 int status;
311 event.params.GetInteger("pipeline_error", &status);
312 player_info[event.id].last_pipeline_status =
313 static_cast<media::PipelineStatus>(status);
314 break;
315 }
316 case media::MediaLogEvent::PROPERTY_CHANGE:
317 if (event.params.HasKey("found_audio_stream")) {
318 event.params.GetBoolean("found_audio_stream",
319 &player_info[event.id].has_audio);
320 }
321 if (event.params.HasKey("found_video_stream")) {
322 event.params.GetBoolean("found_video_stream",
323 &player_info[event.id].has_video);
324 }
325 if (event.params.HasKey("audio_codec_name")) {
326 event.params.GetString("audio_codec_name",
327 &player_info[event.id].audio_codec_name);
328 }
329 if (event.params.HasKey("video_codec_name")) {
330 event.params.GetString("video_codec_name",
331 &player_info[event.id].video_codec_name);
332 }
333 if (event.params.HasKey("video_decoder")) {
watkc85d60e72015-01-14 19:08:28334 std::string previous_video_decoder(player_info[event.id].video_decoder);
prabhur53bb9182014-11-13 03:25:17335 event.params.GetString("video_decoder",
336 &player_info[event.id].video_decoder);
watkc85d60e72015-01-14 19:08:28337 if (!previous_video_decoder.empty() &&
338 previous_video_decoder != player_info[event.id].video_decoder) {
339 player_info[event.id].video_decoder_changed = true;
340 }
prabhur53bb9182014-11-13 03:25:17341 }
prabhurc4812392014-12-05 19:59:42342 if (event.params.HasKey("video_dds")) {
343 event.params.GetBoolean("video_dds", &player_info[event.id].video_dds);
344 }
prabhur53bb9182014-11-13 03:25:17345 break;
346 default:
347 break;
348 }
349 return;
350}
351
prabhurc4812392014-12-05 19:59:42352std::string MediaInternals::MediaInternalsUMAHandler::GetUMANameForAVStream(
353 const PipelineInfo& player_info) {
xhwang0fdb8312015-06-10 23:15:38354 DCHECK_CURRENTLY_ON(BrowserThread::UI);
prabhurc4812392014-12-05 19:59:42355 static const char kPipelineUmaPrefix[] = "Media.PipelineStatus.AudioVideo.";
356 std::string uma_name = kPipelineUmaPrefix;
357 if (player_info.video_codec_name == "vp8") {
358 uma_name += "VP8.";
359 } else if (player_info.video_codec_name == "vp9") {
360 uma_name += "VP9.";
361 } else if (player_info.video_codec_name == "h264") {
362 uma_name += "H264.";
363 } else {
364 return uma_name + "Other";
365 }
366
367 if (player_info.video_decoder ==
368 media::DecryptingVideoDecoder::kDecoderName) {
369 return uma_name + "DVD";
370 }
371
372 if (player_info.video_dds) {
373 uma_name += "DDS.";
374 }
375
376 if (player_info.video_decoder == media::GpuVideoDecoder::kDecoderName) {
377 uma_name += "HW";
378 } else {
379 uma_name += "SW";
380 }
381 return uma_name;
382}
383
prabhur53bb9182014-11-13 03:25:17384void MediaInternals::MediaInternalsUMAHandler::ReportUMAForPipelineStatus(
385 const PipelineInfo& player_info) {
xhwang0fdb8312015-06-10 23:15:38386 DCHECK_CURRENTLY_ON(BrowserThread::UI);
prabhur53bb9182014-11-13 03:25:17387 if (player_info.has_video && player_info.has_audio) {
prabhurc4812392014-12-05 19:59:42388 base::LinearHistogram::FactoryGet(
389 GetUMANameForAVStream(player_info), 1, media::PIPELINE_STATUS_MAX,
390 media::PIPELINE_STATUS_MAX + 1,
391 base::HistogramBase::kUmaTargetedHistogramFlag)
392 ->Add(player_info.last_pipeline_status);
prabhur53bb9182014-11-13 03:25:17393 } else if (player_info.has_audio) {
prabhurdc15c402014-11-21 22:53:01394 UMA_HISTOGRAM_ENUMERATION("Media.PipelineStatus.AudioOnly",
prabhur53bb9182014-11-13 03:25:17395 player_info.last_pipeline_status,
396 media::PIPELINE_STATUS_MAX + 1);
397 } else if (player_info.has_video) {
prabhurdc15c402014-11-21 22:53:01398 UMA_HISTOGRAM_ENUMERATION("Media.PipelineStatus.VideoOnly",
prabhur53bb9182014-11-13 03:25:17399 player_info.last_pipeline_status,
400 media::PIPELINE_STATUS_MAX + 1);
401 } else {
402 UMA_HISTOGRAM_ENUMERATION("Media.PipelineStatus.Unsupported",
403 player_info.last_pipeline_status,
404 media::PIPELINE_STATUS_MAX + 1);
405 }
watkc85d60e72015-01-14 19:08:28406 // Report whether video decoder fallback happened, but only if a video decoder
407 // was reported.
408 if (!player_info.video_decoder.empty()) {
409 UMA_HISTOGRAM_BOOLEAN("Media.VideoDecoderFallback",
410 player_info.video_decoder_changed);
411 }
prabhur53bb9182014-11-13 03:25:17412}
413
xhwange63e59b8d2015-06-10 00:45:35414void MediaInternals::MediaInternalsUMAHandler::OnProcessTerminated(
prabhur53bb9182014-11-13 03:25:17415 int render_process_id) {
xhwang0fdb8312015-06-10 23:15:38416 DCHECK_CURRENTLY_ON(BrowserThread::UI);
xhwange63e59b8d2015-06-10 00:45:35417
prabhur53bb9182014-11-13 03:25:17418 auto players_it = renderer_info_.find(render_process_id);
419 if (players_it == renderer_info_.end())
420 return;
421 auto it = players_it->second.begin();
422 while (it != players_it->second.end()) {
423 ReportUMAForPipelineStatus(it->second);
424 players_it->second.erase(it++);
425 }
xhwange63e59b8d2015-06-10 00:45:35426 renderer_info_.erase(players_it);
prabhur53bb9182014-11-13 03:25:17427}
428
[email protected]11158e2d2013-02-01 02:31:56429MediaInternals* MediaInternals::GetInstance() {
[email protected]69946cf2013-11-27 00:11:42430 return g_media_internals.Pointer();
[email protected]11158e2d2013-02-01 02:31:56431}
432
prabhur53bb9182014-11-13 03:25:17433MediaInternals::MediaInternals()
xhwangfe338e92015-06-08 17:36:09434 : can_update_(false),
435 owner_ids_(),
436 uma_handler_(new MediaInternalsUMAHandler()) {
xhwange63e59b8d2015-06-10 00:45:35437 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
438 NotificationService::AllBrowserContextsAndSources());
prabhur53bb9182014-11-13 03:25:17439}
440
[email protected]11158e2d2013-02-01 02:31:56441MediaInternals::~MediaInternals() {}
442
xhwange63e59b8d2015-06-10 00:45:35443void MediaInternals::Observe(int type,
444 const NotificationSource& source,
445 const NotificationDetails& details) {
446 DCHECK_CURRENTLY_ON(BrowserThread::UI);
447 DCHECK_EQ(type, NOTIFICATION_RENDERER_PROCESS_TERMINATED);
448 RenderProcessHost* process = Source<RenderProcessHost>(source).ptr();
449
450 uma_handler_->OnProcessTerminated(process->GetID());
xhwange63e59b8d2015-06-10 00:45:35451 pending_events_map_.erase(process->GetID());
452}
453
454// Converts the |event| to a |update|. Returns whether the conversion succeeded.
455static bool ConvertEventToUpdate(int render_process_id,
456 const media::MediaLogEvent& event,
457 base::string16* update) {
458 DCHECK(update);
459
460 base::DictionaryValue dict;
461 dict.SetInteger("renderer", render_process_id);
462 dict.SetInteger("player", event.id);
463 dict.SetString("type", media::MediaLog::EventTypeToString(event.type));
464
465 // TODO(dalecurtis): This is technically not correct. TimeTicks "can't" be
466 // converted to to a human readable time format. See base/time/time.h.
467 const double ticks = event.time.ToInternalValue();
468 const double ticks_millis = ticks / base::Time::kMicrosecondsPerMillisecond;
469 dict.SetDouble("ticksMillis", ticks_millis);
470
471 // Convert PipelineStatus to human readable string
472 if (event.type == media::MediaLogEvent::PIPELINE_ERROR) {
473 int status;
474 if (!event.params.GetInteger("pipeline_error", &status) ||
475 status < static_cast<int>(media::PIPELINE_OK) ||
476 status > static_cast<int>(media::PIPELINE_STATUS_MAX)) {
477 return false;
478 }
479 media::PipelineStatus error = static_cast<media::PipelineStatus>(status);
480 dict.SetString("params.pipeline_error",
481 media::MediaLog::PipelineStatusToString(error));
482 } else {
483 dict.Set("params", event.params.DeepCopy());
484 }
485
486 *update = SerializeUpdate("media.onMediaEvent", &dict);
487 return true;
488}
489
[email protected]0e7ee582013-05-04 14:06:59490void MediaInternals::OnMediaEvents(
491 int render_process_id, const std::vector<media::MediaLogEvent>& events) {
xhwang0fdb8312015-06-10 23:15:38492 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]69946cf2013-11-27 00:11:42493 // Notify observers that |event| has occurred.
xhwange63e59b8d2015-06-10 00:45:35494 for (const auto& event : events) {
495 if (CanUpdate()) {
496 base::string16 update;
497 if (ConvertEventToUpdate(render_process_id, event, &update))
498 SendUpdate(update);
prabhur53bb9182014-11-13 03:25:17499 }
500
xhwange63e59b8d2015-06-10 00:45:35501 SaveEvent(render_process_id, event);
502 uma_handler_->SavePlayerState(render_process_id, event);
[email protected]0e7ee582013-05-04 14:06:59503 }
[email protected]11158e2d2013-02-01 02:31:56504}
505
506void MediaInternals::AddUpdateCallback(const UpdateCallback& callback) {
xhwangfe338e92015-06-08 17:36:09507 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]11158e2d2013-02-01 02:31:56508 update_callbacks_.push_back(callback);
xhwangfe338e92015-06-08 17:36:09509
510 base::AutoLock auto_lock(lock_);
511 can_update_ = true;
[email protected]11158e2d2013-02-01 02:31:56512}
513
514void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) {
xhwangfe338e92015-06-08 17:36:09515 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]11158e2d2013-02-01 02:31:56516 for (size_t i = 0; i < update_callbacks_.size(); ++i) {
517 if (update_callbacks_[i].Equals(callback)) {
518 update_callbacks_.erase(update_callbacks_.begin() + i);
xhwangfe338e92015-06-08 17:36:09519 break;
[email protected]11158e2d2013-02-01 02:31:56520 }
521 }
xhwangfe338e92015-06-08 17:36:09522
523 base::AutoLock auto_lock(lock_);
524 can_update_ = !update_callbacks_.empty();
525}
526
527bool MediaInternals::CanUpdate() {
528 base::AutoLock auto_lock(lock_);
529 return can_update_;
[email protected]11158e2d2013-02-01 02:31:56530}
531
xhwange63e59b8d2015-06-10 00:45:35532void MediaInternals::SendHistoricalMediaEvents() {
533 DCHECK_CURRENTLY_ON(BrowserThread::UI);
xhwange63e59b8d2015-06-10 00:45:35534 for (const auto& pending_events : pending_events_map_) {
535 for (const auto& event : pending_events.second) {
536 base::string16 update;
537 if (ConvertEventToUpdate(pending_events.first, event, &update))
538 SendUpdate(update);
539 }
540 }
541 // Do not clear the map/list here so that refreshing the UI or opening a
542 // second UI still works nicely!
543}
544
mcasasfcb5c7de2014-10-11 20:22:17545void MediaInternals::SendAudioStreamData() {
546 base::string16 audio_stream_update;
[email protected]69946cf2013-11-27 00:11:42547 {
548 base::AutoLock auto_lock(lock_);
mcasasfcb5c7de2014-10-11 20:22:17549 audio_stream_update = SerializeUpdate(
550 "media.onReceiveAudioStreamData", &audio_streams_cached_data_);
[email protected]11158e2d2013-02-01 02:31:56551 }
mcasasfcb5c7de2014-10-11 20:22:17552 SendUpdate(audio_stream_update);
553}
554
burnik71963562014-10-17 14:57:14555void MediaInternals::SendVideoCaptureDeviceCapabilities() {
mostynb4c27d042015-03-18 21:47:47556 DCHECK_CURRENTLY_ON(BrowserThread::IO);
xhwangfe338e92015-06-08 17:36:09557
558 if (!CanUpdate())
559 return;
560
burnik71963562014-10-17 14:57:14561 SendUpdate(SerializeUpdate("media.onReceiveVideoCaptureCapabilities",
562 &video_capture_capabilities_cached_data_));
563}
564
mcasasfcb5c7de2014-10-11 20:22:17565void MediaInternals::UpdateVideoCaptureDeviceCapabilities(
566 const media::VideoCaptureDeviceInfos& video_capture_device_infos) {
mostynb4c27d042015-03-18 21:47:47567 DCHECK_CURRENTLY_ON(BrowserThread::IO);
burnik71963562014-10-17 14:57:14568 video_capture_capabilities_cached_data_.Clear();
mcasasfcb5c7de2014-10-11 20:22:17569
570 for (const auto& video_capture_device_info : video_capture_device_infos) {
burnik71963562014-10-17 14:57:14571 base::ListValue* format_list = new base::ListValue();
572 for (const auto& format : video_capture_device_info.supported_formats)
573 format_list->AppendString(format.ToString());
574
575 base::DictionaryValue* device_dict = new base::DictionaryValue();
576 device_dict->SetString("id", video_capture_device_info.name.id());
577 device_dict->SetString(
578 "name", video_capture_device_info.name.GetNameAndModel());
579 device_dict->Set("formats", format_list);
emircan86e26b252015-03-24 20:29:40580#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
581 defined(OS_ANDROID)
burnik4bf4ed12014-10-21 22:35:01582 device_dict->SetString(
emircan86e26b252015-03-24 20:29:40583 "captureApi", video_capture_device_info.name.GetCaptureApiTypeString());
mcasasfcb5c7de2014-10-11 20:22:17584#endif
burnik71963562014-10-17 14:57:14585 video_capture_capabilities_cached_data_.Append(device_dict);
mcasasfcb5c7de2014-10-11 20:22:17586 }
burnik71963562014-10-17 14:57:14587
xhwangfe338e92015-06-08 17:36:09588 SendVideoCaptureDeviceCapabilities();
mcasasfcb5c7de2014-10-11 20:22:17589}
590
591scoped_ptr<media::AudioLog> MediaInternals::CreateAudioLog(
592 AudioComponent component) {
593 base::AutoLock auto_lock(lock_);
594 return scoped_ptr<media::AudioLog>(new AudioLogImpl(
595 owner_ids_[component]++, component, this));
[email protected]e2fd1f72013-08-16 00:34:25596}
597
dalecurtise6aa75f2015-03-31 02:39:38598void MediaInternals::SetWebContentsTitleForAudioLogEntry(
599 int component_id,
600 int render_process_id,
601 int render_frame_id,
602 media::AudioLog* audio_log) {
603 static_cast<AudioLogImpl*>(audio_log)
604 ->SendWebContentsTitle(component_id, render_process_id, render_frame_id);
605}
606
[email protected]fcf75d42013-12-03 20:11:26607void MediaInternals::SendUpdate(const base::string16& update) {
xhwangfe338e92015-06-08 17:36:09608 // SendUpdate() may be called from any thread, but must run on the UI thread.
609 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
610 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
[email protected]69946cf2013-11-27 00:11:42611 &MediaInternals::SendUpdate, base::Unretained(this), update));
[email protected]11158e2d2013-02-01 02:31:56612 return;
[email protected]69946cf2013-11-27 00:11:42613 }
[email protected]11158e2d2013-02-01 02:31:56614
[email protected]11158e2d2013-02-01 02:31:56615 for (size_t i = 0; i < update_callbacks_.size(); i++)
616 update_callbacks_[i].Run(update);
617}
618
xhwange63e59b8d2015-06-10 00:45:35619void MediaInternals::SaveEvent(int process_id,
620 const media::MediaLogEvent& event) {
xhwang0fdb8312015-06-10 23:15:38621 DCHECK_CURRENTLY_ON(BrowserThread::UI);
xhwange63e59b8d2015-06-10 00:45:35622
623 // Max number of saved updates allowed for one process.
624 const size_t kMaxNumEvents = 128;
625
626 // Do not save instantaneous events that happen frequently and have little
627 // value in the future.
628 if (event.type == media::MediaLogEvent::NETWORK_ACTIVITY_SET ||
629 event.type == media::MediaLogEvent::BUFFERED_EXTENTS_CHANGED) {
630 return;
631 }
632
xhwange63e59b8d2015-06-10 00:45:35633 auto& pending_events = pending_events_map_[process_id];
634 // TODO(xhwang): Notify user that some old logs could have been truncated.
635 // See http://crbug.com/498520
636 if (pending_events.size() >= kMaxNumEvents)
637 pending_events.pop_front();
638 pending_events.push_back(event);
639}
640
dalecurtise6aa75f2015-03-31 02:39:38641void MediaInternals::SendAudioLogUpdate(AudioLogUpdateType type,
642 const std::string& cache_key,
643 const std::string& function,
644 const base::DictionaryValue* value) {
xhwangfe338e92015-06-08 17:36:09645 if (!CanUpdate())
646 return;
647
dalecurtise6aa75f2015-03-31 02:39:38648 {
649 base::AutoLock auto_lock(lock_);
650 const bool has_entry = audio_streams_cached_data_.HasKey(cache_key);
651 if ((type == UPDATE_IF_EXISTS || type == UPDATE_AND_DELETE) && !has_entry) {
652 return;
653 } else if (!has_entry) {
654 DCHECK_EQ(type, CREATE);
655 audio_streams_cached_data_.Set(cache_key, value->DeepCopy());
656 } else if (type == UPDATE_AND_DELETE) {
657 scoped_ptr<base::Value> out_value;
658 CHECK(audio_streams_cached_data_.Remove(cache_key, &out_value));
659 } else {
660 base::DictionaryValue* existing_dict = NULL;
661 CHECK(
662 audio_streams_cached_data_.GetDictionary(cache_key, &existing_dict));
663 existing_dict->MergeDictionary(value);
664 }
[email protected]69946cf2013-11-27 00:11:42665 }
666
[email protected]69946cf2013-11-27 00:11:42667 SendUpdate(SerializeUpdate(function, value));
[email protected]69946cf2013-11-27 00:11:42668}
669
[email protected]11158e2d2013-02-01 02:31:56670} // namespace content