blob: 3813858b07f926e4f096c229638264cdc628c85a [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
avi7f277562015-12-25 02:41:267#include <stddef.h>
dcheng36b6aec92015-12-26 06:16:368#include <utility>
avi7f277562015-12-25 02:41:269
10#include "base/macros.h"
prabhur53bb9182014-11-13 03:25:1711#include "base/metrics/histogram.h"
[email protected]348fbaac2013-06-11 06:31:5112#include "base/strings/string16.h"
[email protected]9367e032014-03-07 19:42:3713#include "base/strings/string_number_conversions.h"
[email protected]348fbaac2013-06-11 06:31:5114#include "base/strings/stringprintf.h"
avi7f277562015-12-25 02:41:2615#include "build/build_config.h"
[email protected]11158e2d2013-02-01 02:31:5616#include "content/public/browser/browser_thread.h"
prabhur53bb9182014-11-13 03:25:1717#include "content/public/browser/notification_service.h"
18#include "content/public/browser/notification_types.h"
dalecurtise6aa75f2015-03-31 02:39:3819#include "content/public/browser/render_frame_host.h"
prabhur53bb9182014-11-13 03:25:1720#include "content/public/browser/render_process_host.h"
dalecurtise6aa75f2015-03-31 02:39:3821#include "content/public/browser/web_contents.h"
[email protected]11158e2d2013-02-01 02:31:5622#include "content/public/browser/web_ui.h"
jrummell37a54c02016-04-22 19:54:2723#include "media/base/audio_parameters.h"
[email protected]11158e2d2013-02-01 02:31:5624#include "media/base/media_log_event.h"
prabhur957d46c72014-11-19 03:01:1625#include "media/filters/gpu_video_decoder.h"
[email protected]11158e2d2013-02-01 02:31:5626
xhwangf2189392015-10-19 22:21:5127#if !defined(OS_ANDROID)
28#include "media/filters/decrypting_video_decoder.h"
29#endif
30
[email protected]69946cf2013-11-27 00:11:4231namespace {
32
33static base::LazyInstance<content::MediaInternals>::Leaky g_media_internals =
34 LAZY_INSTANCE_INITIALIZER;
35
[email protected]fcf75d42013-12-03 20:11:2636base::string16 SerializeUpdate(const std::string& function,
37 const base::Value* value) {
[email protected]69946cf2013-11-27 00:11:4238 return content::WebUI::GetJavascriptCall(
39 function, std::vector<const base::Value*>(1, value));
40}
41
[email protected]9367e032014-03-07 19:42:3742std::string EffectsToString(int effects) {
43 if (effects == media::AudioParameters::NO_EFFECTS)
44 return "NO_EFFECTS";
45
46 struct {
47 int flag;
48 const char* name;
49 } flags[] = {
50 { media::AudioParameters::ECHO_CANCELLER, "ECHO_CANCELLER" },
51 { media::AudioParameters::DUCKING, "DUCKING" },
[email protected]81495eb2014-03-19 06:08:1852 { media::AudioParameters::KEYBOARD_MIC, "KEYBOARD_MIC" },
dalecurtisb9a6b7872015-02-06 22:41:5553 { media::AudioParameters::HOTWORD, "HOTWORD" },
[email protected]9367e032014-03-07 19:42:3754 };
55
56 std::string ret;
viettrungluu2dfaba72014-10-16 05:30:2557 for (size_t i = 0; i < arraysize(flags); ++i) {
[email protected]9367e032014-03-07 19:42:3758 if (effects & flags[i].flag) {
59 if (!ret.empty())
60 ret += " | ";
61 ret += flags[i].name;
62 effects &= ~flags[i].flag;
63 }
64 }
65
66 if (effects) {
67 if (!ret.empty())
68 ret += " | ";
69 ret += base::IntToString(effects);
70 }
71
72 return ret;
73}
74
dalecurtisb9a6b7872015-02-06 22:41:5575std::string FormatToString(media::AudioParameters::Format format) {
76 switch (format) {
77 case media::AudioParameters::AUDIO_PCM_LINEAR:
78 return "pcm_linear";
79 case media::AudioParameters::AUDIO_PCM_LOW_LATENCY:
80 return "pcm_low_latency";
81 case media::AudioParameters::AUDIO_FAKE:
82 return "fake";
dalecurtisb9a6b7872015-02-06 22:41:5583 }
84
85 NOTREACHED();
86 return "unknown";
87}
88
[email protected]69946cf2013-11-27 00:11:4289const char kAudioLogStatusKey[] = "status";
90const char kAudioLogUpdateFunction[] = "media.updateAudioComponent";
91
92} // namespace
93
[email protected]11158e2d2013-02-01 02:31:5694namespace content {
95
[email protected]69946cf2013-11-27 00:11:4296class AudioLogImpl : public media::AudioLog {
97 public:
98 AudioLogImpl(int owner_id,
99 media::AudioLogFactory::AudioComponent component,
100 content::MediaInternals* media_internals);
dchengc2282aa2014-10-21 12:07:58101 ~AudioLogImpl() override;
[email protected]69946cf2013-11-27 00:11:42102
dchengc2282aa2014-10-21 12:07:58103 void OnCreated(int component_id,
104 const media::AudioParameters& params,
105 const std::string& device_id) override;
106 void OnStarted(int component_id) override;
107 void OnStopped(int component_id) override;
108 void OnClosed(int component_id) override;
109 void OnError(int component_id) override;
110 void OnSetVolume(int component_id, double volume) override;
guidou61e29df2015-06-11 16:13:56111 void OnSwitchOutputDevice(int component_id,
112 const std::string& device_id) override;
[email protected]69946cf2013-11-27 00:11:42113
dalecurtise6aa75f2015-03-31 02:39:38114 // Called by MediaInternals to update the WebContents title for a stream.
115 void SendWebContentsTitle(int component_id,
116 int render_process_id,
117 int render_frame_id);
118
[email protected]69946cf2013-11-27 00:11:42119 private:
120 void SendSingleStringUpdate(int component_id,
121 const std::string& key,
122 const std::string& value);
123 void StoreComponentMetadata(int component_id, base::DictionaryValue* dict);
124 std::string FormatCacheKey(int component_id);
125
dcheng3b4fe472016-04-08 23:45:13126 static void SendWebContentsTitleHelper(
127 const std::string& cache_key,
128 std::unique_ptr<base::DictionaryValue> dict,
129 int render_process_id,
130 int render_frame_id);
dalecurtise6aa75f2015-03-31 02:39:38131
[email protected]69946cf2013-11-27 00:11:42132 const int owner_id_;
133 const media::AudioLogFactory::AudioComponent component_;
134 content::MediaInternals* const media_internals_;
135
136 DISALLOW_COPY_AND_ASSIGN(AudioLogImpl);
137};
138
139AudioLogImpl::AudioLogImpl(int owner_id,
140 media::AudioLogFactory::AudioComponent component,
141 content::MediaInternals* media_internals)
142 : owner_id_(owner_id),
143 component_(component),
144 media_internals_(media_internals) {}
145
146AudioLogImpl::~AudioLogImpl() {}
147
148void AudioLogImpl::OnCreated(int component_id,
149 const media::AudioParameters& params,
[email protected]25d7f892014-02-13 15:22:45150 const std::string& device_id) {
[email protected]69946cf2013-11-27 00:11:42151 base::DictionaryValue dict;
152 StoreComponentMetadata(component_id, &dict);
153
154 dict.SetString(kAudioLogStatusKey, "created");
[email protected]25d7f892014-02-13 15:22:45155 dict.SetString("device_id", device_id);
dalecurtisb9a6b7872015-02-06 22:41:55156 dict.SetString("device_type", FormatToString(params.format()));
[email protected]69946cf2013-11-27 00:11:42157 dict.SetInteger("frames_per_buffer", params.frames_per_buffer());
158 dict.SetInteger("sample_rate", params.sample_rate());
[email protected]ad82f412013-11-27 04:20:41159 dict.SetInteger("channels", params.channels());
160 dict.SetString("channel_layout",
[email protected]69946cf2013-11-27 00:11:42161 ChannelLayoutToString(params.channel_layout()));
[email protected]9367e032014-03-07 19:42:37162 dict.SetString("effects", EffectsToString(params.effects()));
[email protected]69946cf2013-11-27 00:11:42163
xhwang002c154f2015-06-16 02:55:54164 media_internals_->UpdateAudioLog(MediaInternals::CREATE,
165 FormatCacheKey(component_id),
166 kAudioLogUpdateFunction, &dict);
[email protected]69946cf2013-11-27 00:11:42167}
168
169void AudioLogImpl::OnStarted(int component_id) {
170 SendSingleStringUpdate(component_id, kAudioLogStatusKey, "started");
171}
172
173void AudioLogImpl::OnStopped(int component_id) {
174 SendSingleStringUpdate(component_id, kAudioLogStatusKey, "stopped");
175}
176
177void AudioLogImpl::OnClosed(int component_id) {
178 base::DictionaryValue dict;
179 StoreComponentMetadata(component_id, &dict);
180 dict.SetString(kAudioLogStatusKey, "closed");
xhwang002c154f2015-06-16 02:55:54181 media_internals_->UpdateAudioLog(MediaInternals::UPDATE_AND_DELETE,
182 FormatCacheKey(component_id),
183 kAudioLogUpdateFunction, &dict);
[email protected]69946cf2013-11-27 00:11:42184}
185
186void AudioLogImpl::OnError(int component_id) {
187 SendSingleStringUpdate(component_id, "error_occurred", "true");
188}
189
190void AudioLogImpl::OnSetVolume(int component_id, double volume) {
191 base::DictionaryValue dict;
192 StoreComponentMetadata(component_id, &dict);
193 dict.SetDouble("volume", volume);
xhwang002c154f2015-06-16 02:55:54194 media_internals_->UpdateAudioLog(MediaInternals::UPDATE_IF_EXISTS,
195 FormatCacheKey(component_id),
196 kAudioLogUpdateFunction, &dict);
dalecurtise6aa75f2015-03-31 02:39:38197}
198
guidou61e29df2015-06-11 16:13:56199void AudioLogImpl::OnSwitchOutputDevice(int component_id,
200 const std::string& device_id) {
201 base::DictionaryValue dict;
202 StoreComponentMetadata(component_id, &dict);
203 dict.SetString("device_id", device_id);
xhwang002c154f2015-06-16 02:55:54204 media_internals_->UpdateAudioLog(MediaInternals::UPDATE_IF_EXISTS,
205 FormatCacheKey(component_id),
206 kAudioLogUpdateFunction, &dict);
guidou61e29df2015-06-11 16:13:56207}
208
dalecurtise6aa75f2015-03-31 02:39:38209void AudioLogImpl::SendWebContentsTitle(int component_id,
210 int render_process_id,
211 int render_frame_id) {
dcheng3b4fe472016-04-08 23:45:13212 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
dalecurtise6aa75f2015-03-31 02:39:38213 StoreComponentMetadata(component_id, dict.get());
dcheng36b6aec92015-12-26 06:16:36214 SendWebContentsTitleHelper(FormatCacheKey(component_id), std::move(dict),
dalecurtise6aa75f2015-03-31 02:39:38215 render_process_id, render_frame_id);
[email protected]69946cf2013-11-27 00:11:42216}
217
218std::string AudioLogImpl::FormatCacheKey(int component_id) {
219 return base::StringPrintf("%d:%d:%d", owner_id_, component_, component_id);
220}
221
dalecurtise6aa75f2015-03-31 02:39:38222// static
223void AudioLogImpl::SendWebContentsTitleHelper(
224 const std::string& cache_key,
dcheng3b4fe472016-04-08 23:45:13225 std::unique_ptr<base::DictionaryValue> dict,
dalecurtise6aa75f2015-03-31 02:39:38226 int render_process_id,
227 int render_frame_id) {
228 // Page title information can only be retrieved from the UI thread.
229 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
230 BrowserThread::PostTask(
231 BrowserThread::UI, FROM_HERE,
232 base::Bind(&SendWebContentsTitleHelper, cache_key, base::Passed(&dict),
233 render_process_id, render_frame_id));
234 return;
235 }
236
237 const WebContents* web_contents = WebContents::FromRenderFrameHost(
238 RenderFrameHost::FromID(render_process_id, render_frame_id));
239 if (!web_contents)
240 return;
241
242 // Note: by this point the given audio log entry could have been destroyed, so
243 // we use UPDATE_IF_EXISTS to discard such instances.
244 dict->SetInteger("render_process_id", render_process_id);
245 dict->SetString("web_contents_title", web_contents->GetTitle());
xhwang002c154f2015-06-16 02:55:54246 MediaInternals::GetInstance()->UpdateAudioLog(
dalecurtise6aa75f2015-03-31 02:39:38247 MediaInternals::UPDATE_IF_EXISTS, cache_key, kAudioLogUpdateFunction,
248 dict.get());
249}
250
[email protected]69946cf2013-11-27 00:11:42251void AudioLogImpl::SendSingleStringUpdate(int component_id,
252 const std::string& key,
253 const std::string& value) {
254 base::DictionaryValue dict;
255 StoreComponentMetadata(component_id, &dict);
256 dict.SetString(key, value);
xhwang002c154f2015-06-16 02:55:54257 media_internals_->UpdateAudioLog(MediaInternals::UPDATE_IF_EXISTS,
258 FormatCacheKey(component_id),
259 kAudioLogUpdateFunction, &dict);
[email protected]69946cf2013-11-27 00:11:42260}
261
262void AudioLogImpl::StoreComponentMetadata(int component_id,
263 base::DictionaryValue* dict) {
264 dict->SetInteger("owner_id", owner_id_);
265 dict->SetInteger("component_id", component_id);
266 dict->SetInteger("component_type", component_);
267}
268
xhwang0fdb8312015-06-10 23:15:38269// This class lives on the browser UI thread.
xhwange63e59b8d2015-06-10 00:45:35270class MediaInternals::MediaInternalsUMAHandler {
prabhur53bb9182014-11-13 03:25:17271 public:
272 MediaInternalsUMAHandler();
273
xhwange63e59b8d2015-06-10 00:45:35274 // Called when a render process is terminated. Reports the pipeline status to
275 // UMA for every player associated with the renderer process and then deletes
276 // the player state.
277 void OnProcessTerminated(int render_process_id);
prabhur53bb9182014-11-13 03:25:17278
279 // Helper function to save the event payload to RendererPlayerMap.
xhwange63e59b8d2015-06-10 00:45:35280 void SavePlayerState(int render_process_id,
281 const media::MediaLogEvent& event);
prabhur53bb9182014-11-13 03:25:17282
283 private:
284 struct PipelineInfo {
dalecurtisa14620dd2016-02-27 02:13:25285 bool has_pipeline = false;
286 media::PipelineStatus last_pipeline_status = media::PIPELINE_OK;
287 bool has_audio = false;
288 bool has_video = false;
289 bool video_dds = false;
290 bool video_decoder_changed = false;
prabhur53bb9182014-11-13 03:25:17291 std::string audio_codec_name;
292 std::string video_codec_name;
293 std::string video_decoder;
prabhur53bb9182014-11-13 03:25:17294 };
295
296 // Helper function to report PipelineStatus associated with a player to UMA.
297 void ReportUMAForPipelineStatus(const PipelineInfo& player_info);
298
prabhurc4812392014-12-05 19:59:42299 // Helper to generate PipelineStatus UMA name for AudioVideo streams.
300 std::string GetUMANameForAVStream(const PipelineInfo& player_info);
301
xhwang0fdb8312015-06-10 23:15:38302 // Key is player id.
prabhur53bb9182014-11-13 03:25:17303 typedef std::map<int, PipelineInfo> PlayerInfoMap;
304
xhwang0fdb8312015-06-10 23:15:38305 // Key is renderer id.
prabhur53bb9182014-11-13 03:25:17306 typedef std::map<int, PlayerInfoMap> RendererPlayerMap;
307
xhwang0fdb8312015-06-10 23:15:38308 // Stores player information per renderer.
prabhur53bb9182014-11-13 03:25:17309 RendererPlayerMap renderer_info_;
310
prabhur53bb9182014-11-13 03:25:17311 DISALLOW_COPY_AND_ASSIGN(MediaInternalsUMAHandler);
312};
313
314MediaInternals::MediaInternalsUMAHandler::MediaInternalsUMAHandler() {
prabhur53bb9182014-11-13 03:25:17315}
316
317void MediaInternals::MediaInternalsUMAHandler::SavePlayerState(
xhwange63e59b8d2015-06-10 00:45:35318 int render_process_id,
319 const media::MediaLogEvent& event) {
xhwang0fdb8312015-06-10 23:15:38320 DCHECK_CURRENTLY_ON(BrowserThread::UI);
prabhur53bb9182014-11-13 03:25:17321 PlayerInfoMap& player_info = renderer_info_[render_process_id];
322 switch (event.type) {
dalecurtisa14620dd2016-02-27 02:13:25323 case media::MediaLogEvent::PIPELINE_STATE_CHANGED: {
324 player_info[event.id].has_pipeline = true;
325 break;
326 }
prabhur53bb9182014-11-13 03:25:17327 case media::MediaLogEvent::PIPELINE_ERROR: {
328 int status;
329 event.params.GetInteger("pipeline_error", &status);
330 player_info[event.id].last_pipeline_status =
331 static_cast<media::PipelineStatus>(status);
332 break;
333 }
334 case media::MediaLogEvent::PROPERTY_CHANGE:
335 if (event.params.HasKey("found_audio_stream")) {
336 event.params.GetBoolean("found_audio_stream",
337 &player_info[event.id].has_audio);
338 }
339 if (event.params.HasKey("found_video_stream")) {
340 event.params.GetBoolean("found_video_stream",
341 &player_info[event.id].has_video);
342 }
343 if (event.params.HasKey("audio_codec_name")) {
344 event.params.GetString("audio_codec_name",
345 &player_info[event.id].audio_codec_name);
346 }
347 if (event.params.HasKey("video_codec_name")) {
348 event.params.GetString("video_codec_name",
349 &player_info[event.id].video_codec_name);
350 }
351 if (event.params.HasKey("video_decoder")) {
watkc85d60e72015-01-14 19:08:28352 std::string previous_video_decoder(player_info[event.id].video_decoder);
prabhur53bb9182014-11-13 03:25:17353 event.params.GetString("video_decoder",
354 &player_info[event.id].video_decoder);
watkc85d60e72015-01-14 19:08:28355 if (!previous_video_decoder.empty() &&
356 previous_video_decoder != player_info[event.id].video_decoder) {
357 player_info[event.id].video_decoder_changed = true;
358 }
prabhur53bb9182014-11-13 03:25:17359 }
prabhurc4812392014-12-05 19:59:42360 if (event.params.HasKey("video_dds")) {
361 event.params.GetBoolean("video_dds", &player_info[event.id].video_dds);
362 }
prabhur53bb9182014-11-13 03:25:17363 break;
364 default:
365 break;
366 }
367 return;
368}
369
prabhurc4812392014-12-05 19:59:42370std::string MediaInternals::MediaInternalsUMAHandler::GetUMANameForAVStream(
371 const PipelineInfo& player_info) {
xhwang0fdb8312015-06-10 23:15:38372 DCHECK_CURRENTLY_ON(BrowserThread::UI);
prabhurc4812392014-12-05 19:59:42373 static const char kPipelineUmaPrefix[] = "Media.PipelineStatus.AudioVideo.";
374 std::string uma_name = kPipelineUmaPrefix;
375 if (player_info.video_codec_name == "vp8") {
376 uma_name += "VP8.";
377 } else if (player_info.video_codec_name == "vp9") {
378 uma_name += "VP9.";
379 } else if (player_info.video_codec_name == "h264") {
380 uma_name += "H264.";
381 } else {
382 return uma_name + "Other";
383 }
384
xhwangf2189392015-10-19 22:21:51385#if !defined(OS_ANDROID)
prabhurc4812392014-12-05 19:59:42386 if (player_info.video_decoder ==
387 media::DecryptingVideoDecoder::kDecoderName) {
388 return uma_name + "DVD";
389 }
xhwangf2189392015-10-19 22:21:51390#endif
prabhurc4812392014-12-05 19:59:42391
392 if (player_info.video_dds) {
393 uma_name += "DDS.";
394 }
395
396 if (player_info.video_decoder == media::GpuVideoDecoder::kDecoderName) {
397 uma_name += "HW";
398 } else {
399 uma_name += "SW";
400 }
401 return uma_name;
402}
403
prabhur53bb9182014-11-13 03:25:17404void MediaInternals::MediaInternalsUMAHandler::ReportUMAForPipelineStatus(
405 const PipelineInfo& player_info) {
xhwang0fdb8312015-06-10 23:15:38406 DCHECK_CURRENTLY_ON(BrowserThread::UI);
dalecurtisa14620dd2016-02-27 02:13:25407
408 // Don't log pipeline status for players which don't actually have a pipeline;
409 // e.g., the Android MediaSourcePlayer implementation.
410 if (!player_info.has_pipeline)
411 return;
412
prabhur53bb9182014-11-13 03:25:17413 if (player_info.has_video && player_info.has_audio) {
prabhurc4812392014-12-05 19:59:42414 base::LinearHistogram::FactoryGet(
415 GetUMANameForAVStream(player_info), 1, media::PIPELINE_STATUS_MAX,
416 media::PIPELINE_STATUS_MAX + 1,
417 base::HistogramBase::kUmaTargetedHistogramFlag)
418 ->Add(player_info.last_pipeline_status);
prabhur53bb9182014-11-13 03:25:17419 } else if (player_info.has_audio) {
prabhurdc15c402014-11-21 22:53:01420 UMA_HISTOGRAM_ENUMERATION("Media.PipelineStatus.AudioOnly",
prabhur53bb9182014-11-13 03:25:17421 player_info.last_pipeline_status,
422 media::PIPELINE_STATUS_MAX + 1);
423 } else if (player_info.has_video) {
prabhurdc15c402014-11-21 22:53:01424 UMA_HISTOGRAM_ENUMERATION("Media.PipelineStatus.VideoOnly",
prabhur53bb9182014-11-13 03:25:17425 player_info.last_pipeline_status,
426 media::PIPELINE_STATUS_MAX + 1);
427 } else {
dalecurtis69da898e2016-03-18 21:26:37428 // Note: This metric can be recorded as a result of normal operation with
429 // Media Source Extensions. If a site creates a MediaSource object but never
430 // creates a source buffer or appends data, PIPELINE_OK will be recorded.
prabhur53bb9182014-11-13 03:25:17431 UMA_HISTOGRAM_ENUMERATION("Media.PipelineStatus.Unsupported",
432 player_info.last_pipeline_status,
433 media::PIPELINE_STATUS_MAX + 1);
434 }
watkc85d60e72015-01-14 19:08:28435 // Report whether video decoder fallback happened, but only if a video decoder
436 // was reported.
437 if (!player_info.video_decoder.empty()) {
438 UMA_HISTOGRAM_BOOLEAN("Media.VideoDecoderFallback",
439 player_info.video_decoder_changed);
440 }
prabhur53bb9182014-11-13 03:25:17441}
442
xhwange63e59b8d2015-06-10 00:45:35443void MediaInternals::MediaInternalsUMAHandler::OnProcessTerminated(
prabhur53bb9182014-11-13 03:25:17444 int render_process_id) {
xhwang0fdb8312015-06-10 23:15:38445 DCHECK_CURRENTLY_ON(BrowserThread::UI);
xhwange63e59b8d2015-06-10 00:45:35446
prabhur53bb9182014-11-13 03:25:17447 auto players_it = renderer_info_.find(render_process_id);
448 if (players_it == renderer_info_.end())
449 return;
450 auto it = players_it->second.begin();
451 while (it != players_it->second.end()) {
452 ReportUMAForPipelineStatus(it->second);
453 players_it->second.erase(it++);
454 }
xhwange63e59b8d2015-06-10 00:45:35455 renderer_info_.erase(players_it);
prabhur53bb9182014-11-13 03:25:17456}
457
[email protected]11158e2d2013-02-01 02:31:56458MediaInternals* MediaInternals::GetInstance() {
[email protected]69946cf2013-11-27 00:11:42459 return g_media_internals.Pointer();
[email protected]11158e2d2013-02-01 02:31:56460}
461
prabhur53bb9182014-11-13 03:25:17462MediaInternals::MediaInternals()
xhwangfe338e92015-06-08 17:36:09463 : can_update_(false),
464 owner_ids_(),
465 uma_handler_(new MediaInternalsUMAHandler()) {
xhwange63e59b8d2015-06-10 00:45:35466 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
467 NotificationService::AllBrowserContextsAndSources());
prabhur53bb9182014-11-13 03:25:17468}
469
[email protected]11158e2d2013-02-01 02:31:56470MediaInternals::~MediaInternals() {}
471
xhwange63e59b8d2015-06-10 00:45:35472void MediaInternals::Observe(int type,
473 const NotificationSource& source,
474 const NotificationDetails& details) {
475 DCHECK_CURRENTLY_ON(BrowserThread::UI);
476 DCHECK_EQ(type, NOTIFICATION_RENDERER_PROCESS_TERMINATED);
477 RenderProcessHost* process = Source<RenderProcessHost>(source).ptr();
478
479 uma_handler_->OnProcessTerminated(process->GetID());
xhwange63e59b8d2015-06-10 00:45:35480 pending_events_map_.erase(process->GetID());
481}
482
483// Converts the |event| to a |update|. Returns whether the conversion succeeded.
484static bool ConvertEventToUpdate(int render_process_id,
485 const media::MediaLogEvent& event,
486 base::string16* update) {
487 DCHECK(update);
488
489 base::DictionaryValue dict;
490 dict.SetInteger("renderer", render_process_id);
491 dict.SetInteger("player", event.id);
492 dict.SetString("type", media::MediaLog::EventTypeToString(event.type));
493
494 // TODO(dalecurtis): This is technically not correct. TimeTicks "can't" be
495 // converted to to a human readable time format. See base/time/time.h.
496 const double ticks = event.time.ToInternalValue();
497 const double ticks_millis = ticks / base::Time::kMicrosecondsPerMillisecond;
498 dict.SetDouble("ticksMillis", ticks_millis);
499
500 // Convert PipelineStatus to human readable string
501 if (event.type == media::MediaLogEvent::PIPELINE_ERROR) {
502 int status;
503 if (!event.params.GetInteger("pipeline_error", &status) ||
504 status < static_cast<int>(media::PIPELINE_OK) ||
505 status > static_cast<int>(media::PIPELINE_STATUS_MAX)) {
506 return false;
507 }
508 media::PipelineStatus error = static_cast<media::PipelineStatus>(status);
509 dict.SetString("params.pipeline_error",
510 media::MediaLog::PipelineStatusToString(error));
511 } else {
512 dict.Set("params", event.params.DeepCopy());
513 }
514
515 *update = SerializeUpdate("media.onMediaEvent", &dict);
516 return true;
517}
518
[email protected]0e7ee582013-05-04 14:06:59519void MediaInternals::OnMediaEvents(
520 int render_process_id, const std::vector<media::MediaLogEvent>& events) {
xhwang0fdb8312015-06-10 23:15:38521 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]69946cf2013-11-27 00:11:42522 // Notify observers that |event| has occurred.
xhwange63e59b8d2015-06-10 00:45:35523 for (const auto& event : events) {
524 if (CanUpdate()) {
525 base::string16 update;
526 if (ConvertEventToUpdate(render_process_id, event, &update))
527 SendUpdate(update);
prabhur53bb9182014-11-13 03:25:17528 }
529
xhwange63e59b8d2015-06-10 00:45:35530 SaveEvent(render_process_id, event);
531 uma_handler_->SavePlayerState(render_process_id, event);
[email protected]0e7ee582013-05-04 14:06:59532 }
[email protected]11158e2d2013-02-01 02:31:56533}
534
535void MediaInternals::AddUpdateCallback(const UpdateCallback& callback) {
xhwangfe338e92015-06-08 17:36:09536 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]11158e2d2013-02-01 02:31:56537 update_callbacks_.push_back(callback);
xhwangfe338e92015-06-08 17:36:09538
539 base::AutoLock auto_lock(lock_);
540 can_update_ = true;
[email protected]11158e2d2013-02-01 02:31:56541}
542
543void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) {
xhwangfe338e92015-06-08 17:36:09544 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]11158e2d2013-02-01 02:31:56545 for (size_t i = 0; i < update_callbacks_.size(); ++i) {
546 if (update_callbacks_[i].Equals(callback)) {
547 update_callbacks_.erase(update_callbacks_.begin() + i);
xhwangfe338e92015-06-08 17:36:09548 break;
[email protected]11158e2d2013-02-01 02:31:56549 }
550 }
xhwangfe338e92015-06-08 17:36:09551
552 base::AutoLock auto_lock(lock_);
553 can_update_ = !update_callbacks_.empty();
554}
555
556bool MediaInternals::CanUpdate() {
557 base::AutoLock auto_lock(lock_);
558 return can_update_;
[email protected]11158e2d2013-02-01 02:31:56559}
560
xhwange63e59b8d2015-06-10 00:45:35561void MediaInternals::SendHistoricalMediaEvents() {
562 DCHECK_CURRENTLY_ON(BrowserThread::UI);
xhwange63e59b8d2015-06-10 00:45:35563 for (const auto& pending_events : pending_events_map_) {
564 for (const auto& event : pending_events.second) {
565 base::string16 update;
566 if (ConvertEventToUpdate(pending_events.first, event, &update))
567 SendUpdate(update);
568 }
569 }
570 // Do not clear the map/list here so that refreshing the UI or opening a
571 // second UI still works nicely!
572}
573
mcasasfcb5c7de2014-10-11 20:22:17574void MediaInternals::SendAudioStreamData() {
575 base::string16 audio_stream_update;
[email protected]69946cf2013-11-27 00:11:42576 {
577 base::AutoLock auto_lock(lock_);
mcasasfcb5c7de2014-10-11 20:22:17578 audio_stream_update = SerializeUpdate(
579 "media.onReceiveAudioStreamData", &audio_streams_cached_data_);
[email protected]11158e2d2013-02-01 02:31:56580 }
mcasasfcb5c7de2014-10-11 20:22:17581 SendUpdate(audio_stream_update);
582}
583
burnik71963562014-10-17 14:57:14584void MediaInternals::SendVideoCaptureDeviceCapabilities() {
mostynb4c27d042015-03-18 21:47:47585 DCHECK_CURRENTLY_ON(BrowserThread::IO);
xhwangfe338e92015-06-08 17:36:09586
587 if (!CanUpdate())
588 return;
589
burnik71963562014-10-17 14:57:14590 SendUpdate(SerializeUpdate("media.onReceiveVideoCaptureCapabilities",
591 &video_capture_capabilities_cached_data_));
592}
593
mcasasfcb5c7de2014-10-11 20:22:17594void MediaInternals::UpdateVideoCaptureDeviceCapabilities(
595 const media::VideoCaptureDeviceInfos& video_capture_device_infos) {
mostynb4c27d042015-03-18 21:47:47596 DCHECK_CURRENTLY_ON(BrowserThread::IO);
burnik71963562014-10-17 14:57:14597 video_capture_capabilities_cached_data_.Clear();
mcasasfcb5c7de2014-10-11 20:22:17598
599 for (const auto& video_capture_device_info : video_capture_device_infos) {
burnik71963562014-10-17 14:57:14600 base::ListValue* format_list = new base::ListValue();
nisse7a153fb2015-12-07 09:30:59601 // TODO(nisse): Representing format information as a string, to be
602 // parsed by the javascript handler, is brittle. Consider passing
603 // a list of mappings instead.
604
burnik71963562014-10-17 14:57:14605 for (const auto& format : video_capture_device_info.supported_formats)
ajose5d00e98782015-07-01 21:09:48606 format_list->AppendString(media::VideoCaptureFormat::ToString(format));
burnik71963562014-10-17 14:57:14607
608 base::DictionaryValue* device_dict = new base::DictionaryValue();
609 device_dict->SetString("id", video_capture_device_info.name.id());
610 device_dict->SetString(
611 "name", video_capture_device_info.name.GetNameAndModel());
612 device_dict->Set("formats", format_list);
emircan86e26b252015-03-24 20:29:40613#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
614 defined(OS_ANDROID)
burnik4bf4ed12014-10-21 22:35:01615 device_dict->SetString(
emircan86e26b252015-03-24 20:29:40616 "captureApi", video_capture_device_info.name.GetCaptureApiTypeString());
mcasasfcb5c7de2014-10-11 20:22:17617#endif
burnik71963562014-10-17 14:57:14618 video_capture_capabilities_cached_data_.Append(device_dict);
mcasasfcb5c7de2014-10-11 20:22:17619 }
burnik71963562014-10-17 14:57:14620
xhwangfe338e92015-06-08 17:36:09621 SendVideoCaptureDeviceCapabilities();
mcasasfcb5c7de2014-10-11 20:22:17622}
623
dcheng3b4fe472016-04-08 23:45:13624std::unique_ptr<media::AudioLog> MediaInternals::CreateAudioLog(
mcasasfcb5c7de2014-10-11 20:22:17625 AudioComponent component) {
626 base::AutoLock auto_lock(lock_);
dcheng3b4fe472016-04-08 23:45:13627 return std::unique_ptr<media::AudioLog>(
628 new AudioLogImpl(owner_ids_[component]++, component, this));
[email protected]e2fd1f72013-08-16 00:34:25629}
630
dalecurtise6aa75f2015-03-31 02:39:38631void MediaInternals::SetWebContentsTitleForAudioLogEntry(
632 int component_id,
633 int render_process_id,
634 int render_frame_id,
635 media::AudioLog* audio_log) {
636 static_cast<AudioLogImpl*>(audio_log)
637 ->SendWebContentsTitle(component_id, render_process_id, render_frame_id);
638}
639
[email protected]fcf75d42013-12-03 20:11:26640void MediaInternals::SendUpdate(const base::string16& update) {
xhwangfe338e92015-06-08 17:36:09641 // SendUpdate() may be called from any thread, but must run on the UI thread.
642 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
643 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
[email protected]69946cf2013-11-27 00:11:42644 &MediaInternals::SendUpdate, base::Unretained(this), update));
[email protected]11158e2d2013-02-01 02:31:56645 return;
[email protected]69946cf2013-11-27 00:11:42646 }
[email protected]11158e2d2013-02-01 02:31:56647
[email protected]11158e2d2013-02-01 02:31:56648 for (size_t i = 0; i < update_callbacks_.size(); i++)
649 update_callbacks_[i].Run(update);
650}
651
xhwange63e59b8d2015-06-10 00:45:35652void MediaInternals::SaveEvent(int process_id,
653 const media::MediaLogEvent& event) {
xhwang0fdb8312015-06-10 23:15:38654 DCHECK_CURRENTLY_ON(BrowserThread::UI);
xhwange63e59b8d2015-06-10 00:45:35655
656 // Max number of saved updates allowed for one process.
657 const size_t kMaxNumEvents = 128;
658
659 // Do not save instantaneous events that happen frequently and have little
660 // value in the future.
661 if (event.type == media::MediaLogEvent::NETWORK_ACTIVITY_SET ||
662 event.type == media::MediaLogEvent::BUFFERED_EXTENTS_CHANGED) {
663 return;
664 }
665
xhwange63e59b8d2015-06-10 00:45:35666 auto& pending_events = pending_events_map_[process_id];
667 // TODO(xhwang): Notify user that some old logs could have been truncated.
668 // See http://crbug.com/498520
669 if (pending_events.size() >= kMaxNumEvents)
670 pending_events.pop_front();
671 pending_events.push_back(event);
672}
673
xhwang002c154f2015-06-16 02:55:54674void MediaInternals::UpdateAudioLog(AudioLogUpdateType type,
675 const std::string& cache_key,
676 const std::string& function,
677 const base::DictionaryValue* value) {
dalecurtise6aa75f2015-03-31 02:39:38678 {
679 base::AutoLock auto_lock(lock_);
680 const bool has_entry = audio_streams_cached_data_.HasKey(cache_key);
681 if ((type == UPDATE_IF_EXISTS || type == UPDATE_AND_DELETE) && !has_entry) {
682 return;
683 } else if (!has_entry) {
684 DCHECK_EQ(type, CREATE);
685 audio_streams_cached_data_.Set(cache_key, value->DeepCopy());
686 } else if (type == UPDATE_AND_DELETE) {
dcheng3b4fe472016-04-08 23:45:13687 std::unique_ptr<base::Value> out_value;
dalecurtise6aa75f2015-03-31 02:39:38688 CHECK(audio_streams_cached_data_.Remove(cache_key, &out_value));
689 } else {
690 base::DictionaryValue* existing_dict = NULL;
691 CHECK(
692 audio_streams_cached_data_.GetDictionary(cache_key, &existing_dict));
693 existing_dict->MergeDictionary(value);
694 }
[email protected]69946cf2013-11-27 00:11:42695 }
696
xhwang002c154f2015-06-16 02:55:54697 if (CanUpdate())
698 SendUpdate(SerializeUpdate(function, value));
[email protected]69946cf2013-11-27 00:11:42699}
700
[email protected]11158e2d2013-02-01 02:31:56701} // namespace content