blob: f626c6033935376a1bf63512e40a2e84c252df10 [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>
dcheng98e96a72016-06-11 03:41:488
xhwang29c5ad202017-04-14 07:02:199#include <tuple>
dcheng36b6aec92015-12-26 06:16:3610#include <utility>
avi7f277562015-12-25 02:41:2611
Becca Hughesa0754d72018-10-09 00:33:4012#include "base/containers/adapters.h"
Henrik Grunelle7e52912018-10-18 07:39:5313#include "base/feature_list.h"
avi7f277562015-12-25 02:41:2614#include "base/macros.h"
Dale Curtis4a6f54c02017-06-06 23:32:2015#include "base/metrics/histogram_functions.h"
asvitkine8d51e9d2016-09-02 23:55:4316#include "base/metrics/histogram_macros.h"
Yeonwoo Jo095a2732018-10-03 17:10:4017#include "base/stl_util.h"
[email protected]348fbaac2013-06-11 06:31:5118#include "base/strings/string16.h"
[email protected]9367e032014-03-07 19:42:3719#include "base/strings/string_number_conversions.h"
[email protected]348fbaac2013-06-11 06:31:5120#include "base/strings/stringprintf.h"
Eric Seckler8652dcd52018-09-20 10:42:2821#include "base/task/post_task.h"
avi7f277562015-12-25 02:41:2622#include "build/build_config.h"
Becca Hughesd34443942018-08-17 22:15:0823#include "content/browser/media/session/media_session_impl.h"
guidou50db1a62016-06-01 17:12:1724#include "content/browser/renderer_host/media/media_stream_manager.h"
xhwang29c5ad202017-04-14 07:02:1925#include "content/public/browser/browser_context.h"
Eric Seckler8652dcd52018-09-20 10:42:2826#include "content/public/browser/browser_task_traits.h"
[email protected]11158e2d2013-02-01 02:31:5627#include "content/public/browser/browser_thread.h"
prabhur53bb9182014-11-13 03:25:1728#include "content/public/browser/notification_service.h"
29#include "content/public/browser/notification_types.h"
dalecurtise6aa75f2015-03-31 02:39:3830#include "content/public/browser/render_frame_host.h"
prabhur53bb9182014-11-13 03:25:1731#include "content/public/browser/render_process_host.h"
dalecurtise6aa75f2015-03-31 02:39:3832#include "content/public/browser/web_contents.h"
[email protected]11158e2d2013-02-01 02:31:5633#include "content/public/browser/web_ui.h"
Henrik Grunelle7e52912018-10-18 07:39:5334#include "content/public/common/content_features.h"
Becca Hughes2f7072f2018-10-04 00:58:4035#include "content/public/common/service_manager_connection.h"
jrummell37a54c02016-04-22 19:54:2736#include "media/base/audio_parameters.h"
[email protected]11158e2d2013-02-01 02:31:5637#include "media/base/media_log_event.h"
prabhur957d46c72014-11-19 03:01:1638#include "media/filters/gpu_video_decoder.h"
Henrik Grunelle7e52912018-10-18 07:39:5339#include "media/webrtc/webrtc_switches.h"
Guido Urdaneta35a86982018-02-20 13:34:3840#include "mojo/public/cpp/bindings/strong_binding.h"
Henrik Grunelle7e52912018-10-18 07:39:5341#include "services/service_manager/sandbox/features.h"
[email protected]11158e2d2013-02-01 02:31:5642
kraush42521052017-05-04 19:14:3443#if !defined(OS_ANDROID)
xhwangf2189392015-10-19 22:21:5144#include "media/filters/decrypting_video_decoder.h"
45#endif
46
[email protected]69946cf2013-11-27 00:11:4247namespace {
48
[email protected]fcf75d42013-12-03 20:11:2649base::string16 SerializeUpdate(const std::string& function,
50 const base::Value* value) {
[email protected]69946cf2013-11-27 00:11:4251 return content::WebUI::GetJavascriptCall(
52 function, std::vector<const base::Value*>(1, value));
53}
54
[email protected]9367e032014-03-07 19:42:3755std::string EffectsToString(int effects) {
56 if (effects == media::AudioParameters::NO_EFFECTS)
57 return "NO_EFFECTS";
58
59 struct {
60 int flag;
61 const char* name;
62 } flags[] = {
Guido Urdaneta35a86982018-02-20 13:34:3863 {media::AudioParameters::ECHO_CANCELLER, "ECHO_CANCELLER"},
64 {media::AudioParameters::DUCKING, "DUCKING"},
65 {media::AudioParameters::KEYBOARD_MIC, "KEYBOARD_MIC"},
66 {media::AudioParameters::HOTWORD, "HOTWORD"},
[email protected]9367e032014-03-07 19:42:3767 };
68
69 std::string ret;
viettrungluu2dfaba72014-10-16 05:30:2570 for (size_t i = 0; i < arraysize(flags); ++i) {
[email protected]9367e032014-03-07 19:42:3771 if (effects & flags[i].flag) {
72 if (!ret.empty())
73 ret += " | ";
74 ret += flags[i].name;
75 effects &= ~flags[i].flag;
76 }
77 }
78
79 if (effects) {
80 if (!ret.empty())
81 ret += " | ";
82 ret += base::IntToString(effects);
83 }
84
85 return ret;
86}
87
dalecurtisb9a6b7872015-02-06 22:41:5588std::string FormatToString(media::AudioParameters::Format format) {
89 switch (format) {
90 case media::AudioParameters::AUDIO_PCM_LINEAR:
91 return "pcm_linear";
92 case media::AudioParameters::AUDIO_PCM_LOW_LATENCY:
93 return "pcm_low_latency";
tsunghung59327d62016-11-19 16:09:4894 case media::AudioParameters::AUDIO_BITSTREAM_AC3:
95 return "ac3";
96 case media::AudioParameters::AUDIO_BITSTREAM_EAC3:
97 return "eac3";
dalecurtisb9a6b7872015-02-06 22:41:5598 case media::AudioParameters::AUDIO_FAKE:
99 return "fake";
dalecurtisb9a6b7872015-02-06 22:41:55100 }
101
102 NOTREACHED();
103 return "unknown";
104}
105
xhwang29c5ad202017-04-14 07:02:19106// Whether the player is in incognito mode or ChromeOS guest mode.
107bool IsIncognito(int render_process_id) {
108 content::RenderProcessHost* render_process_host =
109 content::RenderProcessHost::FromID(render_process_id);
110 if (!render_process_host) {
111 // This could happen in tests.
112 LOG(ERROR) << "Cannot get RenderProcessHost";
113 return false;
114 }
115
116 content::BrowserContext* browser_context =
117 render_process_host->GetBrowserContext();
118 DCHECK(browser_context);
119
120 return browser_context->IsOffTheRecord();
121}
122
[email protected]69946cf2013-11-27 00:11:42123const char kAudioLogStatusKey[] = "status";
124const char kAudioLogUpdateFunction[] = "media.updateAudioComponent";
125
[email protected]69946cf2013-11-27 00:11:42126} // namespace
127
[email protected]11158e2d2013-02-01 02:31:56128namespace content {
129
Guido Urdanetaee6cfd02018-07-12 09:19:34130class MediaInternals::AudioLogImpl : public media::mojom::AudioLog,
131 public media::AudioLog {
[email protected]69946cf2013-11-27 00:11:42132 public:
133 AudioLogImpl(int owner_id,
134 media::AudioLogFactory::AudioComponent component,
Guido Urdaneta35a86982018-02-20 13:34:38135 content::MediaInternals* media_internals,
136 int component_id,
137 int render_process_id,
138 int render_frame_id);
dchengc2282aa2014-10-21 12:07:58139 ~AudioLogImpl() override;
[email protected]69946cf2013-11-27 00:11:42140
Guido Urdaneta35a86982018-02-20 13:34:38141 void OnCreated(const media::AudioParameters& params,
dchengc2282aa2014-10-21 12:07:58142 const std::string& device_id) override;
Guido Urdaneta35a86982018-02-20 13:34:38143 void OnStarted() override;
144 void OnStopped() override;
145 void OnClosed() override;
146 void OnError() override;
147 void OnSetVolume(double volume) override;
148 void OnLogMessage(const std::string& message) override;
Max Morin86d4f132018-08-30 21:51:13149 void OnProcessingStateChanged(const std::string& message) override;
dalecurtise6aa75f2015-03-31 02:39:38150
[email protected]69946cf2013-11-27 00:11:42151 private:
Guido Urdaneta35a86982018-02-20 13:34:38152 // If possible, i.e. a WebContents exists for the given RenderFrameHostID,
153 // tells an existing AudioLogEntry the WebContents title for easier
154 // differentiation on the UI. Note that the log entry must be created (by
155 // calling OnCreated() before calling this method.
156 void SetWebContentsTitle();
157
158 void SendSingleStringUpdate(const std::string& key, const std::string& value);
159 void StoreComponentMetadata(base::DictionaryValue* dict);
160 std::string FormatCacheKey();
[email protected]69946cf2013-11-27 00:11:42161
dcheng3b4fe472016-04-08 23:45:13162 static void SendWebContentsTitleHelper(
163 const std::string& cache_key,
164 std::unique_ptr<base::DictionaryValue> dict,
165 int render_process_id,
166 int render_frame_id);
dalecurtise6aa75f2015-03-31 02:39:38167
[email protected]69946cf2013-11-27 00:11:42168 const int owner_id_;
169 const media::AudioLogFactory::AudioComponent component_;
170 content::MediaInternals* const media_internals_;
Guido Urdaneta35a86982018-02-20 13:34:38171 const int component_id_;
172 const int render_process_id_;
173 const int render_frame_id_;
[email protected]69946cf2013-11-27 00:11:42174
175 DISALLOW_COPY_AND_ASSIGN(AudioLogImpl);
176};
177
Guido Urdanetaee6cfd02018-07-12 09:19:34178MediaInternals::AudioLogImpl::AudioLogImpl(
179 int owner_id,
180 media::AudioLogFactory::AudioComponent component,
181 content::MediaInternals* media_internals,
182 int component_id,
183 int render_process_id,
184 int render_frame_id)
[email protected]69946cf2013-11-27 00:11:42185 : owner_id_(owner_id),
186 component_(component),
Guido Urdaneta35a86982018-02-20 13:34:38187 media_internals_(media_internals),
188 component_id_(component_id),
189 render_process_id_(render_process_id),
190 render_frame_id_(render_frame_id) {}
[email protected]69946cf2013-11-27 00:11:42191
Guido Urdanetaee6cfd02018-07-12 09:19:34192MediaInternals::AudioLogImpl::~AudioLogImpl() {
193 // Ensure log is always closed. This covers the case of crashes in the audio
194 // service utility process, in which case the log may not be closed
195 // explicitly.
196 OnClosed();
197}
[email protected]69946cf2013-11-27 00:11:42198
Guido Urdanetaee6cfd02018-07-12 09:19:34199void MediaInternals::AudioLogImpl::OnCreated(
200 const media::AudioParameters& params,
201 const std::string& device_id) {
[email protected]69946cf2013-11-27 00:11:42202 base::DictionaryValue dict;
Guido Urdaneta35a86982018-02-20 13:34:38203 StoreComponentMetadata(&dict);
[email protected]69946cf2013-11-27 00:11:42204
205 dict.SetString(kAudioLogStatusKey, "created");
[email protected]25d7f892014-02-13 15:22:45206 dict.SetString("device_id", device_id);
dalecurtisb9a6b7872015-02-06 22:41:55207 dict.SetString("device_type", FormatToString(params.format()));
[email protected]69946cf2013-11-27 00:11:42208 dict.SetInteger("frames_per_buffer", params.frames_per_buffer());
209 dict.SetInteger("sample_rate", params.sample_rate());
[email protected]ad82f412013-11-27 04:20:41210 dict.SetInteger("channels", params.channels());
211 dict.SetString("channel_layout",
[email protected]69946cf2013-11-27 00:11:42212 ChannelLayoutToString(params.channel_layout()));
[email protected]9367e032014-03-07 19:42:37213 dict.SetString("effects", EffectsToString(params.effects()));
[email protected]69946cf2013-11-27 00:11:42214
Guido Urdaneta35a86982018-02-20 13:34:38215 media_internals_->UpdateAudioLog(MediaInternals::CREATE, FormatCacheKey(),
xhwang002c154f2015-06-16 02:55:54216 kAudioLogUpdateFunction, &dict);
Guido Urdaneta35a86982018-02-20 13:34:38217 SetWebContentsTitle();
[email protected]69946cf2013-11-27 00:11:42218}
219
Guido Urdanetaee6cfd02018-07-12 09:19:34220void MediaInternals::AudioLogImpl::OnStarted() {
Guido Urdaneta35a86982018-02-20 13:34:38221 SendSingleStringUpdate(kAudioLogStatusKey, "started");
[email protected]69946cf2013-11-27 00:11:42222}
223
Guido Urdanetaee6cfd02018-07-12 09:19:34224void MediaInternals::AudioLogImpl::OnStopped() {
Guido Urdaneta35a86982018-02-20 13:34:38225 SendSingleStringUpdate(kAudioLogStatusKey, "stopped");
[email protected]69946cf2013-11-27 00:11:42226}
227
Guido Urdanetaee6cfd02018-07-12 09:19:34228void MediaInternals::AudioLogImpl::OnClosed() {
[email protected]69946cf2013-11-27 00:11:42229 base::DictionaryValue dict;
Guido Urdaneta35a86982018-02-20 13:34:38230 StoreComponentMetadata(&dict);
[email protected]69946cf2013-11-27 00:11:42231 dict.SetString(kAudioLogStatusKey, "closed");
xhwang002c154f2015-06-16 02:55:54232 media_internals_->UpdateAudioLog(MediaInternals::UPDATE_AND_DELETE,
Guido Urdaneta35a86982018-02-20 13:34:38233 FormatCacheKey(), kAudioLogUpdateFunction,
234 &dict);
[email protected]69946cf2013-11-27 00:11:42235}
236
Guido Urdanetaee6cfd02018-07-12 09:19:34237void MediaInternals::AudioLogImpl::OnError() {
Guido Urdaneta35a86982018-02-20 13:34:38238 SendSingleStringUpdate("error_occurred", "true");
[email protected]69946cf2013-11-27 00:11:42239}
240
Guido Urdanetaee6cfd02018-07-12 09:19:34241void MediaInternals::AudioLogImpl::OnSetVolume(double volume) {
[email protected]69946cf2013-11-27 00:11:42242 base::DictionaryValue dict;
Guido Urdaneta35a86982018-02-20 13:34:38243 StoreComponentMetadata(&dict);
[email protected]69946cf2013-11-27 00:11:42244 dict.SetDouble("volume", volume);
xhwang002c154f2015-06-16 02:55:54245 media_internals_->UpdateAudioLog(MediaInternals::UPDATE_IF_EXISTS,
Guido Urdaneta35a86982018-02-20 13:34:38246 FormatCacheKey(), kAudioLogUpdateFunction,
247 &dict);
dalecurtise6aa75f2015-03-31 02:39:38248}
249
Max Morin86d4f132018-08-30 21:51:13250void MediaInternals::AudioLogImpl::OnProcessingStateChanged(
251 const std::string& message) {
252 SendSingleStringUpdate("processing state", message);
253}
254
Guido Urdanetaee6cfd02018-07-12 09:19:34255void MediaInternals::AudioLogImpl::OnLogMessage(const std::string& message) {
guidou50db1a62016-06-01 17:12:17256 MediaStreamManager::SendMessageToNativeLog(message);
257}
258
Guido Urdanetaee6cfd02018-07-12 09:19:34259void MediaInternals::AudioLogImpl::SetWebContentsTitle() {
Guido Urdaneta35a86982018-02-20 13:34:38260 if (render_process_id_ < 0 || render_frame_id_ < 0)
261 return;
dcheng3b4fe472016-04-08 23:45:13262 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
Guido Urdaneta35a86982018-02-20 13:34:38263 StoreComponentMetadata(dict.get());
264 SendWebContentsTitleHelper(FormatCacheKey(), std::move(dict),
265 render_process_id_, render_frame_id_);
[email protected]69946cf2013-11-27 00:11:42266}
267
Guido Urdanetaee6cfd02018-07-12 09:19:34268std::string MediaInternals::AudioLogImpl::FormatCacheKey() {
Guido Urdaneta35a86982018-02-20 13:34:38269 return base::StringPrintf("%d:%d:%d", owner_id_, component_, component_id_);
[email protected]69946cf2013-11-27 00:11:42270}
271
dalecurtise6aa75f2015-03-31 02:39:38272// static
Guido Urdanetaee6cfd02018-07-12 09:19:34273void MediaInternals::AudioLogImpl::SendWebContentsTitleHelper(
dalecurtise6aa75f2015-03-31 02:39:38274 const std::string& cache_key,
dcheng3b4fe472016-04-08 23:45:13275 std::unique_ptr<base::DictionaryValue> dict,
dalecurtise6aa75f2015-03-31 02:39:38276 int render_process_id,
277 int render_frame_id) {
278 // Page title information can only be retrieved from the UI thread.
279 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
Eric Seckler8652dcd52018-09-20 10:42:28280 base::PostTaskWithTraits(
281 FROM_HERE, {BrowserThread::UI},
tzikccf160c2018-02-20 12:43:13282 base::BindOnce(&SendWebContentsTitleHelper, cache_key, std::move(dict),
283 render_process_id, render_frame_id));
dalecurtise6aa75f2015-03-31 02:39:38284 return;
285 }
286
Lucas Furukawa Gadanie1c5dfda2018-11-29 17:57:41287 WebContents* web_contents = WebContents::FromRenderFrameHost(
dalecurtise6aa75f2015-03-31 02:39:38288 RenderFrameHost::FromID(render_process_id, render_frame_id));
289 if (!web_contents)
290 return;
291
292 // Note: by this point the given audio log entry could have been destroyed, so
293 // we use UPDATE_IF_EXISTS to discard such instances.
294 dict->SetInteger("render_process_id", render_process_id);
295 dict->SetString("web_contents_title", web_contents->GetTitle());
xhwang002c154f2015-06-16 02:55:54296 MediaInternals::GetInstance()->UpdateAudioLog(
dalecurtise6aa75f2015-03-31 02:39:38297 MediaInternals::UPDATE_IF_EXISTS, cache_key, kAudioLogUpdateFunction,
298 dict.get());
299}
300
Guido Urdanetaee6cfd02018-07-12 09:19:34301void MediaInternals::AudioLogImpl::SendSingleStringUpdate(
302 const std::string& key,
303 const std::string& value) {
[email protected]69946cf2013-11-27 00:11:42304 base::DictionaryValue dict;
Guido Urdaneta35a86982018-02-20 13:34:38305 StoreComponentMetadata(&dict);
[email protected]69946cf2013-11-27 00:11:42306 dict.SetString(key, value);
xhwang002c154f2015-06-16 02:55:54307 media_internals_->UpdateAudioLog(MediaInternals::UPDATE_IF_EXISTS,
Guido Urdaneta35a86982018-02-20 13:34:38308 FormatCacheKey(), kAudioLogUpdateFunction,
309 &dict);
[email protected]69946cf2013-11-27 00:11:42310}
311
Guido Urdanetaee6cfd02018-07-12 09:19:34312void MediaInternals::AudioLogImpl::StoreComponentMetadata(
313 base::DictionaryValue* dict) {
[email protected]69946cf2013-11-27 00:11:42314 dict->SetInteger("owner_id", owner_id_);
Guido Urdaneta35a86982018-02-20 13:34:38315 dict->SetInteger("component_id", component_id_);
[email protected]69946cf2013-11-27 00:11:42316 dict->SetInteger("component_type", component_);
317}
318
xhwang0fdb8312015-06-10 23:15:38319// This class lives on the browser UI thread.
xhwange63e59b8d2015-06-10 00:45:35320class MediaInternals::MediaInternalsUMAHandler {
prabhur53bb9182014-11-13 03:25:17321 public:
Dale Curtis1adbe6a2017-08-02 02:09:13322 MediaInternalsUMAHandler();
prabhur53bb9182014-11-13 03:25:17323
xhwange63e59b8d2015-06-10 00:45:35324 // Called when a render process is terminated. Reports the pipeline status to
325 // UMA for every player associated with the renderer process and then deletes
326 // the player state.
327 void OnProcessTerminated(int render_process_id);
prabhur53bb9182014-11-13 03:25:17328
329 // Helper function to save the event payload to RendererPlayerMap.
xhwange63e59b8d2015-06-10 00:45:35330 void SavePlayerState(int render_process_id,
331 const media::MediaLogEvent& event);
prabhur53bb9182014-11-13 03:25:17332
333 private:
334 struct PipelineInfo {
xhwang29c5ad202017-04-14 07:02:19335 explicit PipelineInfo(bool is_incognito) : is_incognito(is_incognito) {}
336
dalecurtisa14620dd2016-02-27 02:13:25337 bool has_pipeline = false;
dalecurtisc58ff8e2017-02-22 18:20:45338 bool has_ever_played = false;
339 bool has_reached_have_enough = false;
dalecurtisa14620dd2016-02-27 02:13:25340 media::PipelineStatus last_pipeline_status = media::PIPELINE_OK;
341 bool has_audio = false;
342 bool has_video = false;
343 bool video_dds = false;
344 bool video_decoder_changed = false;
xhwang29c5ad202017-04-14 07:02:19345 bool has_cdm = false;
346 bool is_incognito = false;
prabhur53bb9182014-11-13 03:25:17347 std::string audio_codec_name;
348 std::string video_codec_name;
349 std::string video_decoder;
Dan Sandersfbf10062018-07-17 19:56:48350 bool is_platform_video_decoder = false;
dalecurtis2cff7f372017-05-24 08:30:08351 GURL origin_url;
prabhur53bb9182014-11-13 03:25:17352 };
353
354 // Helper function to report PipelineStatus associated with a player to UMA.
355 void ReportUMAForPipelineStatus(const PipelineInfo& player_info);
356
prabhurc4812392014-12-05 19:59:42357 // Helper to generate PipelineStatus UMA name for AudioVideo streams.
358 std::string GetUMANameForAVStream(const PipelineInfo& player_info);
359
xhwang0fdb8312015-06-10 23:15:38360 // Key is player id.
prabhur53bb9182014-11-13 03:25:17361 typedef std::map<int, PipelineInfo> PlayerInfoMap;
362
xhwang0fdb8312015-06-10 23:15:38363 // Key is renderer id.
prabhur53bb9182014-11-13 03:25:17364 typedef std::map<int, PlayerInfoMap> RendererPlayerMap;
365
xhwang0fdb8312015-06-10 23:15:38366 // Stores player information per renderer.
prabhur53bb9182014-11-13 03:25:17367 RendererPlayerMap renderer_info_;
368
prabhur53bb9182014-11-13 03:25:17369 DISALLOW_COPY_AND_ASSIGN(MediaInternalsUMAHandler);
370};
371
Dale Curtis1adbe6a2017-08-02 02:09:13372MediaInternals::MediaInternalsUMAHandler::MediaInternalsUMAHandler() {}
prabhur53bb9182014-11-13 03:25:17373
374void MediaInternals::MediaInternalsUMAHandler::SavePlayerState(
xhwange63e59b8d2015-06-10 00:45:35375 int render_process_id,
376 const media::MediaLogEvent& event) {
xhwang0fdb8312015-06-10 23:15:38377 DCHECK_CURRENTLY_ON(BrowserThread::UI);
xhwang29c5ad202017-04-14 07:02:19378
379 PlayerInfoMap& player_info_map = renderer_info_[render_process_id];
380
381 auto it = player_info_map.find(event.id);
382 if (it == player_info_map.end()) {
383 bool success = false;
384 std::tie(it, success) = player_info_map.emplace(
385 std::make_pair(event.id, PipelineInfo(IsIncognito(render_process_id))));
386 if (!success) {
387 LOG(ERROR) << "Failed to insert a new PipelineInfo.";
388 return;
389 }
390 }
391
392 PipelineInfo& player_info = it->second;
393
prabhur53bb9182014-11-13 03:25:17394 switch (event.type) {
dalecurtis2cff7f372017-05-24 08:30:08395 case media::MediaLogEvent::Type::WEBMEDIAPLAYER_CREATED: {
396 std::string origin_url;
397 event.params.GetString("origin_url", &origin_url);
398 player_info.origin_url = GURL(origin_url);
399 break;
400 }
dalecurtisc58ff8e2017-02-22 18:20:45401 case media::MediaLogEvent::PLAY: {
xhwang29c5ad202017-04-14 07:02:19402 player_info.has_ever_played = true;
dalecurtisc58ff8e2017-02-22 18:20:45403 break;
404 }
dalecurtisa14620dd2016-02-27 02:13:25405 case media::MediaLogEvent::PIPELINE_STATE_CHANGED: {
xhwang29c5ad202017-04-14 07:02:19406 player_info.has_pipeline = true;
dalecurtisa14620dd2016-02-27 02:13:25407 break;
408 }
prabhur53bb9182014-11-13 03:25:17409 case media::MediaLogEvent::PIPELINE_ERROR: {
Xiaohan Wang007b1b422017-11-01 18:55:18410 int status = static_cast<media::PipelineStatus>(media::PIPELINE_OK);
prabhur53bb9182014-11-13 03:25:17411 event.params.GetInteger("pipeline_error", &status);
xhwang29c5ad202017-04-14 07:02:19412 player_info.last_pipeline_status =
prabhur53bb9182014-11-13 03:25:17413 static_cast<media::PipelineStatus>(status);
414 break;
415 }
416 case media::MediaLogEvent::PROPERTY_CHANGE:
417 if (event.params.HasKey("found_audio_stream")) {
xhwang29c5ad202017-04-14 07:02:19418 event.params.GetBoolean("found_audio_stream", &player_info.has_audio);
prabhur53bb9182014-11-13 03:25:17419 }
420 if (event.params.HasKey("found_video_stream")) {
xhwang29c5ad202017-04-14 07:02:19421 event.params.GetBoolean("found_video_stream", &player_info.has_video);
prabhur53bb9182014-11-13 03:25:17422 }
423 if (event.params.HasKey("audio_codec_name")) {
424 event.params.GetString("audio_codec_name",
xhwang29c5ad202017-04-14 07:02:19425 &player_info.audio_codec_name);
prabhur53bb9182014-11-13 03:25:17426 }
427 if (event.params.HasKey("video_codec_name")) {
428 event.params.GetString("video_codec_name",
xhwang29c5ad202017-04-14 07:02:19429 &player_info.video_codec_name);
prabhur53bb9182014-11-13 03:25:17430 }
431 if (event.params.HasKey("video_decoder")) {
xhwang29c5ad202017-04-14 07:02:19432 std::string previous_video_decoder(player_info.video_decoder);
433 event.params.GetString("video_decoder", &player_info.video_decoder);
watkc85d60e72015-01-14 19:08:28434 if (!previous_video_decoder.empty() &&
xhwang29c5ad202017-04-14 07:02:19435 previous_video_decoder != player_info.video_decoder) {
436 player_info.video_decoder_changed = true;
watkc85d60e72015-01-14 19:08:28437 }
prabhur53bb9182014-11-13 03:25:17438 }
Dan Sandersfbf10062018-07-17 19:56:48439 if (event.params.HasKey("is_platform_video_decoder")) {
440 event.params.GetBoolean("is_platform_video_decoder",
441 &player_info.is_platform_video_decoder);
442 }
prabhurc4812392014-12-05 19:59:42443 if (event.params.HasKey("video_dds")) {
xhwang29c5ad202017-04-14 07:02:19444 event.params.GetBoolean("video_dds", &player_info.video_dds);
445 }
446 if (event.params.HasKey("has_cdm")) {
447 event.params.GetBoolean("has_cdm", &player_info.has_cdm);
prabhurc4812392014-12-05 19:59:42448 }
dalecurtisc58ff8e2017-02-22 18:20:45449 if (event.params.HasKey("pipeline_buffering_state")) {
450 std::string buffering_state;
451 event.params.GetString("pipeline_buffering_state", &buffering_state);
Dale Curtisff576552018-03-30 02:32:44452
453 bool for_suspended_start;
454 event.params.GetBoolean("for_suspended_start", &for_suspended_start);
455
456 // Ignore the BUFFERING_HAVE_ENOUGH event if it was for a suspended
457 // start. Otherwise we won't reflect reductions to the HasEverPlayed
458 // statistic.
459 if (buffering_state == "BUFFERING_HAVE_ENOUGH" && !for_suspended_start)
xhwang29c5ad202017-04-14 07:02:19460 player_info.has_reached_have_enough = true;
dalecurtisc58ff8e2017-02-22 18:20:45461 }
prabhur53bb9182014-11-13 03:25:17462 break;
dalecurtis04bdb582016-08-17 22:15:23463 case media::MediaLogEvent::Type::WEBMEDIAPLAYER_DESTROYED: {
464 // Upon player destruction report UMA data; if the player is not torn down
465 // before process exit, it will be logged during OnProcessTerminated().
Dave Tapuska4b7980102018-01-05 20:05:45466 ReportUMAForPipelineStatus(player_info);
xhwang29c5ad202017-04-14 07:02:19467 player_info_map.erase(it);
Nico Weber147d1ff2018-01-29 19:29:37468 break;
dalecurtis04bdb582016-08-17 22:15:23469 }
prabhur53bb9182014-11-13 03:25:17470 default:
471 break;
472 }
473 return;
474}
475
prabhurc4812392014-12-05 19:59:42476std::string MediaInternals::MediaInternalsUMAHandler::GetUMANameForAVStream(
477 const PipelineInfo& player_info) {
xhwang0fdb8312015-06-10 23:15:38478 DCHECK_CURRENTLY_ON(BrowserThread::UI);
prabhurc4812392014-12-05 19:59:42479 static const char kPipelineUmaPrefix[] = "Media.PipelineStatus.AudioVideo.";
480 std::string uma_name = kPipelineUmaPrefix;
481 if (player_info.video_codec_name == "vp8") {
482 uma_name += "VP8.";
483 } else if (player_info.video_codec_name == "vp9") {
484 uma_name += "VP9.";
485 } else if (player_info.video_codec_name == "h264") {
486 uma_name += "H264.";
487 } else {
488 return uma_name + "Other";
489 }
490
kraush42521052017-05-04 19:14:34491#if !defined(OS_ANDROID)
prabhurc4812392014-12-05 19:59:42492 if (player_info.video_decoder ==
493 media::DecryptingVideoDecoder::kDecoderName) {
494 return uma_name + "DVD";
495 }
xhwangf2189392015-10-19 22:21:51496#endif
prabhurc4812392014-12-05 19:59:42497
498 if (player_info.video_dds) {
499 uma_name += "DDS.";
500 }
501
Dan Sandersfbf10062018-07-17 19:56:48502 // Note that HW essentailly means 'platform' anyway. MediaCodec has been
503 // reported as HW forever, regardless of the underlying platform
504 // implementation.
505 if (player_info.is_platform_video_decoder) {
prabhurc4812392014-12-05 19:59:42506 uma_name += "HW";
507 } else {
508 uma_name += "SW";
509 }
510 return uma_name;
511}
512
xhwang29c5ad202017-04-14 07:02:19513// TODO(xhwang): This function reports more metrics than just pipeline status
514// and should be renamed. Similarly, PipelineInfo should be PlayerInfo.
prabhur53bb9182014-11-13 03:25:17515void MediaInternals::MediaInternalsUMAHandler::ReportUMAForPipelineStatus(
516 const PipelineInfo& player_info) {
xhwang0fdb8312015-06-10 23:15:38517 DCHECK_CURRENTLY_ON(BrowserThread::UI);
dalecurtisa14620dd2016-02-27 02:13:25518
519 // Don't log pipeline status for players which don't actually have a pipeline;
520 // e.g., the Android MediaSourcePlayer implementation.
521 if (!player_info.has_pipeline)
522 return;
523
prabhur53bb9182014-11-13 03:25:17524 if (player_info.has_video && player_info.has_audio) {
Dale Curtis4a6f54c02017-06-06 23:32:20525 base::UmaHistogramExactLinear(GetUMANameForAVStream(player_info),
526 player_info.last_pipeline_status,
527 media::PIPELINE_STATUS_MAX);
prabhur53bb9182014-11-13 03:25:17528 } else if (player_info.has_audio) {
prabhurdc15c402014-11-21 22:53:01529 UMA_HISTOGRAM_ENUMERATION("Media.PipelineStatus.AudioOnly",
prabhur53bb9182014-11-13 03:25:17530 player_info.last_pipeline_status,
531 media::PIPELINE_STATUS_MAX + 1);
532 } else if (player_info.has_video) {
prabhurdc15c402014-11-21 22:53:01533 UMA_HISTOGRAM_ENUMERATION("Media.PipelineStatus.VideoOnly",
prabhur53bb9182014-11-13 03:25:17534 player_info.last_pipeline_status,
535 media::PIPELINE_STATUS_MAX + 1);
536 } else {
dalecurtis69da898e2016-03-18 21:26:37537 // Note: This metric can be recorded as a result of normal operation with
538 // Media Source Extensions. If a site creates a MediaSource object but never
539 // creates a source buffer or appends data, PIPELINE_OK will be recorded.
prabhur53bb9182014-11-13 03:25:17540 UMA_HISTOGRAM_ENUMERATION("Media.PipelineStatus.Unsupported",
541 player_info.last_pipeline_status,
542 media::PIPELINE_STATUS_MAX + 1);
543 }
watkc85d60e72015-01-14 19:08:28544 // Report whether video decoder fallback happened, but only if a video decoder
545 // was reported.
546 if (!player_info.video_decoder.empty()) {
547 UMA_HISTOGRAM_BOOLEAN("Media.VideoDecoderFallback",
548 player_info.video_decoder_changed);
549 }
dalecurtisc58ff8e2017-02-22 18:20:45550
551 // Report whether this player ever saw a playback event. Used to measure the
552 // effectiveness of efforts to reduce loaded-but-never-used players.
553 if (player_info.has_reached_have_enough)
554 UMA_HISTOGRAM_BOOLEAN("Media.HasEverPlayed", player_info.has_ever_played);
xhwang29c5ad202017-04-14 07:02:19555
556 // Report whether an encrypted playback is in incognito window, excluding
557 // never-used players.
558 if (player_info.has_cdm && player_info.has_ever_played)
559 UMA_HISTOGRAM_BOOLEAN("Media.EME.IsIncognito", player_info.is_incognito);
prabhur53bb9182014-11-13 03:25:17560}
561
xhwange63e59b8d2015-06-10 00:45:35562void MediaInternals::MediaInternalsUMAHandler::OnProcessTerminated(
prabhur53bb9182014-11-13 03:25:17563 int render_process_id) {
xhwang0fdb8312015-06-10 23:15:38564 DCHECK_CURRENTLY_ON(BrowserThread::UI);
xhwange63e59b8d2015-06-10 00:45:35565
prabhur53bb9182014-11-13 03:25:17566 auto players_it = renderer_info_.find(render_process_id);
567 if (players_it == renderer_info_.end())
568 return;
569 auto it = players_it->second.begin();
570 while (it != players_it->second.end()) {
571 ReportUMAForPipelineStatus(it->second);
572 players_it->second.erase(it++);
573 }
xhwange63e59b8d2015-06-10 00:45:35574 renderer_info_.erase(players_it);
prabhur53bb9182014-11-13 03:25:17575}
576
[email protected]11158e2d2013-02-01 02:31:56577MediaInternals* MediaInternals::GetInstance() {
dalecurtisc3af5092017-02-11 02:08:18578 static content::MediaInternals* internals = new content::MediaInternals();
579 return internals;
[email protected]11158e2d2013-02-01 02:31:56580}
581
prabhur53bb9182014-11-13 03:25:17582MediaInternals::MediaInternals()
xhwangfe338e92015-06-08 17:36:09583 : can_update_(false),
584 owner_ids_(),
Dale Curtis1adbe6a2017-08-02 02:09:13585 uma_handler_(new MediaInternalsUMAHandler()) {
Dan Sandersb26585d2017-07-12 23:51:30586 // TODO(sandersd): Is there ever a relevant case where TERMINATED is sent
587 // without CLOSED also being sent?
588 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_CLOSED,
589 NotificationService::AllBrowserContextsAndSources());
xhwange63e59b8d2015-06-10 00:45:35590 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
591 NotificationService::AllBrowserContextsAndSources());
prabhur53bb9182014-11-13 03:25:17592}
593
[email protected]11158e2d2013-02-01 02:31:56594MediaInternals::~MediaInternals() {}
595
xhwange63e59b8d2015-06-10 00:45:35596void MediaInternals::Observe(int type,
597 const NotificationSource& source,
598 const NotificationDetails& details) {
599 DCHECK_CURRENTLY_ON(BrowserThread::UI);
xhwange63e59b8d2015-06-10 00:45:35600 RenderProcessHost* process = Source<RenderProcessHost>(source).ptr();
xhwange63e59b8d2015-06-10 00:45:35601 uma_handler_->OnProcessTerminated(process->GetID());
Dan Sandersb26585d2017-07-12 23:51:30602 // TODO(sandersd): Send a termination event before clearing the log.
watkb5983f92016-08-09 20:47:51603 saved_events_by_process_.erase(process->GetID());
xhwange63e59b8d2015-06-10 00:45:35604}
605
606// Converts the |event| to a |update|. Returns whether the conversion succeeded.
607static bool ConvertEventToUpdate(int render_process_id,
608 const media::MediaLogEvent& event,
609 base::string16* update) {
610 DCHECK(update);
611
612 base::DictionaryValue dict;
613 dict.SetInteger("renderer", render_process_id);
614 dict.SetInteger("player", event.id);
615 dict.SetString("type", media::MediaLog::EventTypeToString(event.type));
616
617 // TODO(dalecurtis): This is technically not correct. TimeTicks "can't" be
618 // converted to to a human readable time format. See base/time/time.h.
619 const double ticks = event.time.ToInternalValue();
620 const double ticks_millis = ticks / base::Time::kMicrosecondsPerMillisecond;
621 dict.SetDouble("ticksMillis", ticks_millis);
622
623 // Convert PipelineStatus to human readable string
624 if (event.type == media::MediaLogEvent::PIPELINE_ERROR) {
625 int status;
626 if (!event.params.GetInteger("pipeline_error", &status) ||
627 status < static_cast<int>(media::PIPELINE_OK) ||
628 status > static_cast<int>(media::PIPELINE_STATUS_MAX)) {
629 return false;
630 }
631 media::PipelineStatus error = static_cast<media::PipelineStatus>(status);
632 dict.SetString("params.pipeline_error",
633 media::MediaLog::PipelineStatusToString(error));
634 } else {
jdoerriecc9f5732017-08-23 14:12:30635 dict.SetKey("params", event.params.Clone());
xhwange63e59b8d2015-06-10 00:45:35636 }
637
638 *update = SerializeUpdate("media.onMediaEvent", &dict);
639 return true;
640}
641
[email protected]0e7ee582013-05-04 14:06:59642void MediaInternals::OnMediaEvents(
Dale Curtis1adbe6a2017-08-02 02:09:13643 int render_process_id,
644 const std::vector<media::MediaLogEvent>& events) {
xhwang0fdb8312015-06-10 23:15:38645 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]69946cf2013-11-27 00:11:42646 // Notify observers that |event| has occurred.
xhwange63e59b8d2015-06-10 00:45:35647 for (const auto& event : events) {
Dale Curtis1adbe6a2017-08-02 02:09:13648 if (CanUpdate()) {
649 base::string16 update;
650 if (ConvertEventToUpdate(render_process_id, event, &update))
651 SendUpdate(update);
prabhur53bb9182014-11-13 03:25:17652 }
Dale Curtis1adbe6a2017-08-02 02:09:13653 SaveEvent(render_process_id, event);
xhwange63e59b8d2015-06-10 00:45:35654 uma_handler_->SavePlayerState(render_process_id, event);
[email protected]0e7ee582013-05-04 14:06:59655 }
[email protected]11158e2d2013-02-01 02:31:56656}
657
658void MediaInternals::AddUpdateCallback(const UpdateCallback& callback) {
xhwangfe338e92015-06-08 17:36:09659 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]11158e2d2013-02-01 02:31:56660 update_callbacks_.push_back(callback);
xhwangfe338e92015-06-08 17:36:09661
662 base::AutoLock auto_lock(lock_);
663 can_update_ = true;
Becca Hughes1aa4e672018-10-23 22:09:08664 audio_focus_helper_.SetEnabled(true);
[email protected]11158e2d2013-02-01 02:31:56665}
666
667void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) {
xhwangfe338e92015-06-08 17:36:09668 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]11158e2d2013-02-01 02:31:56669 for (size_t i = 0; i < update_callbacks_.size(); ++i) {
670 if (update_callbacks_[i].Equals(callback)) {
671 update_callbacks_.erase(update_callbacks_.begin() + i);
xhwangfe338e92015-06-08 17:36:09672 break;
[email protected]11158e2d2013-02-01 02:31:56673 }
674 }
xhwangfe338e92015-06-08 17:36:09675
676 base::AutoLock auto_lock(lock_);
677 can_update_ = !update_callbacks_.empty();
Becca Hughes1aa4e672018-10-23 22:09:08678 audio_focus_helper_.SetEnabled(can_update_);
xhwangfe338e92015-06-08 17:36:09679}
680
681bool MediaInternals::CanUpdate() {
682 base::AutoLock auto_lock(lock_);
683 return can_update_;
[email protected]11158e2d2013-02-01 02:31:56684}
685
xhwange63e59b8d2015-06-10 00:45:35686void MediaInternals::SendHistoricalMediaEvents() {
687 DCHECK_CURRENTLY_ON(BrowserThread::UI);
watkb5983f92016-08-09 20:47:51688 for (const auto& saved_events : saved_events_by_process_) {
689 for (const auto& event : saved_events.second) {
xhwange63e59b8d2015-06-10 00:45:35690 base::string16 update;
watkb5983f92016-08-09 20:47:51691 if (ConvertEventToUpdate(saved_events.first, event, &update))
xhwange63e59b8d2015-06-10 00:45:35692 SendUpdate(update);
693 }
694 }
695 // Do not clear the map/list here so that refreshing the UI or opening a
696 // second UI still works nicely!
697}
698
Henrik Grunelle7e52912018-10-18 07:39:53699void MediaInternals::SendGeneralAudioInformation() {
700 base::DictionaryValue audio_info_data;
701
702 // Audio feature information.
703 auto set_feature_data = [&](auto& feature) {
704 audio_info_data.SetKey(
705 feature.name,
706 base::Value(base::FeatureList::IsEnabled(feature) ? "Enabled"
707 : "Disabled"));
708 };
709 set_feature_data(features::kAudioServiceAudioStreams);
710 set_feature_data(features::kAudioServiceOutOfProcess);
711 set_feature_data(features::kAudioServiceLaunchOnStartup);
712 set_feature_data(service_manager::features::kAudioServiceSandbox);
713 set_feature_data(features::kWebRtcApmInAudioService);
714
715 base::string16 audio_info_update =
716 SerializeUpdate("media.updateGeneralAudioInformation", &audio_info_data);
717 SendUpdate(audio_info_update);
718}
719
mcasasfcb5c7de2014-10-11 20:22:17720void MediaInternals::SendAudioStreamData() {
721 base::string16 audio_stream_update;
[email protected]69946cf2013-11-27 00:11:42722 {
723 base::AutoLock auto_lock(lock_);
Guido Urdaneta35a86982018-02-20 13:34:38724 audio_stream_update = SerializeUpdate("media.onReceiveAudioStreamData",
725 &audio_streams_cached_data_);
[email protected]11158e2d2013-02-01 02:31:56726 }
mcasasfcb5c7de2014-10-11 20:22:17727 SendUpdate(audio_stream_update);
728}
729
burnik71963562014-10-17 14:57:14730void MediaInternals::SendVideoCaptureDeviceCapabilities() {
mostynb4c27d042015-03-18 21:47:47731 DCHECK_CURRENTLY_ON(BrowserThread::IO);
xhwangfe338e92015-06-08 17:36:09732
733 if (!CanUpdate())
734 return;
735
burnik71963562014-10-17 14:57:14736 SendUpdate(SerializeUpdate("media.onReceiveVideoCaptureCapabilities",
737 &video_capture_capabilities_cached_data_));
738}
739
Becca Hughesd34443942018-08-17 22:15:08740void MediaInternals::SendAudioFocusState() {
Becca Hughes1aa4e672018-10-23 22:09:08741 audio_focus_helper_.SendAudioFocusState();
Becca Hughes8da7a66a2018-08-24 21:58:42742}
Becca Hughesd34443942018-08-17 22:15:08743
mcasasfcb5c7de2014-10-11 20:22:17744void MediaInternals::UpdateVideoCaptureDeviceCapabilities(
chfremer1c38eb42016-08-02 15:54:31745 const std::vector<std::tuple<media::VideoCaptureDeviceDescriptor,
746 media::VideoCaptureFormats>>&
747 descriptors_and_formats) {
mostynb4c27d042015-03-18 21:47:47748 DCHECK_CURRENTLY_ON(BrowserThread::IO);
burnik71963562014-10-17 14:57:14749 video_capture_capabilities_cached_data_.Clear();
mcasasfcb5c7de2014-10-11 20:22:17750
chfremer1c38eb42016-08-02 15:54:31751 for (const auto& device_format_pair : descriptors_and_formats) {
Jeremy Roman04f27c372017-10-27 15:20:55752 auto format_list = std::make_unique<base::ListValue>();
nisse7a153fb2015-12-07 09:30:59753 // TODO(nisse): Representing format information as a string, to be
754 // parsed by the javascript handler, is brittle. Consider passing
755 // a list of mappings instead.
756
chfremer1c38eb42016-08-02 15:54:31757 const media::VideoCaptureDeviceDescriptor& descriptor =
758 std::get<0>(device_format_pair);
759 const media::VideoCaptureFormats& supported_formats =
760 std::get<1>(device_format_pair);
761 for (const auto& format : supported_formats)
ajose5d00e98782015-07-01 21:09:48762 format_list->AppendString(media::VideoCaptureFormat::ToString(format));
burnik71963562014-10-17 14:57:14763
dcheng98e96a72016-06-11 03:41:48764 std::unique_ptr<base::DictionaryValue> device_dict(
765 new base::DictionaryValue());
chfremer1c38eb42016-08-02 15:54:31766 device_dict->SetString("id", descriptor.device_id);
767 device_dict->SetString("name", descriptor.GetNameAndModel());
jdoerrie664305c2017-06-07 08:34:34768 device_dict->Set("formats", std::move(format_list));
emircan86e26b252015-03-24 20:29:40769#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
770 defined(OS_ANDROID)
chfremer1c38eb42016-08-02 15:54:31771 device_dict->SetString("captureApi", descriptor.GetCaptureApiTypeString());
mcasasfcb5c7de2014-10-11 20:22:17772#endif
dcheng98e96a72016-06-11 03:41:48773 video_capture_capabilities_cached_data_.Append(std::move(device_dict));
mcasasfcb5c7de2014-10-11 20:22:17774 }
burnik71963562014-10-17 14:57:14775
xhwangfe338e92015-06-08 17:36:09776 SendVideoCaptureDeviceCapabilities();
mcasasfcb5c7de2014-10-11 20:22:17777}
778
dcheng3b4fe472016-04-08 23:45:13779std::unique_ptr<media::AudioLog> MediaInternals::CreateAudioLog(
Guido Urdaneta35a86982018-02-20 13:34:38780 AudioComponent component,
781 int component_id) {
Guido Urdanetaee6cfd02018-07-12 09:19:34782 return CreateAudioLogImpl(component, component_id, -1, MSG_ROUTING_NONE);
[email protected]e2fd1f72013-08-16 00:34:25783}
784
Guido Urdaneta35a86982018-02-20 13:34:38785media::mojom::AudioLogPtr MediaInternals::CreateMojoAudioLog(
786 media::AudioLogFactory::AudioComponent component,
dalecurtise6aa75f2015-03-31 02:39:38787 int component_id,
788 int render_process_id,
Guido Urdaneta35a86982018-02-20 13:34:38789 int render_frame_id) {
Guido Urdaneta35a86982018-02-20 13:34:38790 media::mojom::AudioLogPtr audio_log_ptr;
Guido Urdanetaee6cfd02018-07-12 09:19:34791 CreateMojoAudioLog(component, component_id, mojo::MakeRequest(&audio_log_ptr),
792 render_process_id, render_frame_id);
Guido Urdaneta35a86982018-02-20 13:34:38793 return audio_log_ptr;
dalecurtise6aa75f2015-03-31 02:39:38794}
795
Guido Urdanetaee6cfd02018-07-12 09:19:34796void MediaInternals::CreateMojoAudioLog(
797 media::AudioLogFactory::AudioComponent component,
798 int component_id,
799 media::mojom::AudioLogRequest request,
800 int render_process_id,
801 int render_frame_id) {
802 mojo::MakeStrongBinding(
803 CreateAudioLogImpl(component, component_id, render_process_id,
804 render_frame_id),
805 std::move(request));
806}
807
808std::unique_ptr<MediaInternals::AudioLogImpl>
809MediaInternals::CreateAudioLogImpl(
810 media::AudioLogFactory::AudioComponent component,
811 int component_id,
812 int render_process_id,
813 int render_frame_id) {
814 base::AutoLock auto_lock(lock_);
815 return std::make_unique<AudioLogImpl>(owner_ids_[component]++, component,
816 this, component_id, render_process_id,
817 render_frame_id);
818}
819
dalecurtisc45b1c4e2017-04-01 07:14:27820void MediaInternals::OnProcessTerminatedForTesting(int process_id) {
821 uma_handler_->OnProcessTerminated(process_id);
822}
823
[email protected]fcf75d42013-12-03 20:11:26824void MediaInternals::SendUpdate(const base::string16& update) {
xhwangfe338e92015-06-08 17:36:09825 // SendUpdate() may be called from any thread, but must run on the UI thread.
826 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
Eric Seckler8652dcd52018-09-20 10:42:28827 base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
828 base::BindOnce(&MediaInternals::SendUpdate,
829 base::Unretained(this), update));
[email protected]11158e2d2013-02-01 02:31:56830 return;
[email protected]69946cf2013-11-27 00:11:42831 }
[email protected]11158e2d2013-02-01 02:31:56832
[email protected]11158e2d2013-02-01 02:31:56833 for (size_t i = 0; i < update_callbacks_.size(); i++)
834 update_callbacks_[i].Run(update);
835}
836
xhwange63e59b8d2015-06-10 00:45:35837void MediaInternals::SaveEvent(int process_id,
838 const media::MediaLogEvent& event) {
xhwang0fdb8312015-06-10 23:15:38839 DCHECK_CURRENTLY_ON(BrowserThread::UI);
xhwange63e59b8d2015-06-10 00:45:35840
Dale Curtis1ff5a375a2017-06-16 21:38:31841// Save the event and limit the total number per renderer. At the time of
842// writing, 512 events of the kind: { "property": value } together consume
843// ~88kb of memory on linux.
844#if defined(OS_ANDROID)
845 const size_t kEventLimit = 128;
846#else
847 const size_t kEventLimit = 512;
848#endif
xhwange63e59b8d2015-06-10 00:45:35849
Dale Curtis1ff5a375a2017-06-16 21:38:31850 auto& saved_events = saved_events_by_process_[process_id];
watkb5983f92016-08-09 20:47:51851 saved_events.push_back(event);
Dale Curtis1ff5a375a2017-06-16 21:38:31852 if (saved_events.size() > kEventLimit) {
watkb5983f92016-08-09 20:47:51853 // Remove all events for a given player as soon as we have to remove a
854 // single event for that player to avoid showing incomplete players.
Dale Curtis1ff5a375a2017-06-16 21:38:31855 const int id_to_remove = saved_events.front().id;
Yeonwoo Jo095a2732018-10-03 17:10:40856 base::EraseIf(saved_events, [&](const media::MediaLogEvent& event) {
857 return event.id == id_to_remove;
858 });
watkb5983f92016-08-09 20:47:51859 }
xhwange63e59b8d2015-06-10 00:45:35860}
861
xhwang002c154f2015-06-16 02:55:54862void MediaInternals::UpdateAudioLog(AudioLogUpdateType type,
863 const std::string& cache_key,
864 const std::string& function,
865 const base::DictionaryValue* value) {
dalecurtise6aa75f2015-03-31 02:39:38866 {
867 base::AutoLock auto_lock(lock_);
868 const bool has_entry = audio_streams_cached_data_.HasKey(cache_key);
869 if ((type == UPDATE_IF_EXISTS || type == UPDATE_AND_DELETE) && !has_entry) {
870 return;
871 } else if (!has_entry) {
872 DCHECK_EQ(type, CREATE);
jdoerriecc9f5732017-08-23 14:12:30873 audio_streams_cached_data_.Set(
Jeremy Roman04f27c372017-10-27 15:20:55874 cache_key, std::make_unique<base::Value>(value->Clone()));
dalecurtise6aa75f2015-03-31 02:39:38875 } else if (type == UPDATE_AND_DELETE) {
dcheng3b4fe472016-04-08 23:45:13876 std::unique_ptr<base::Value> out_value;
dalecurtise6aa75f2015-03-31 02:39:38877 CHECK(audio_streams_cached_data_.Remove(cache_key, &out_value));
878 } else {
Ivan Kotenkov2c0d2bb32017-11-01 15:41:28879 base::DictionaryValue* existing_dict = nullptr;
dalecurtise6aa75f2015-03-31 02:39:38880 CHECK(
881 audio_streams_cached_data_.GetDictionary(cache_key, &existing_dict));
882 existing_dict->MergeDictionary(value);
883 }
[email protected]69946cf2013-11-27 00:11:42884 }
885
xhwang002c154f2015-06-16 02:55:54886 if (CanUpdate())
887 SendUpdate(SerializeUpdate(function, value));
[email protected]69946cf2013-11-27 00:11:42888}
889
[email protected]11158e2d2013-02-01 02:31:56890} // namespace content