blob: 3c708c25b1522ae0c38846c3d0db1b946c7714d9 [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
Jan Wilken Dörriead587c32021-03-11 14:09:279#include <string>
xhwang29c5ad202017-04-14 07:02:1910#include <tuple>
dcheng36b6aec92015-12-26 06:16:3611#include <utility>
avi7f277562015-12-25 02:41:2612
Sebastien Marchandf8cbfab2019-01-25 16:02:3013#include "base/bind.h"
Becca Hughesa0754d72018-10-09 00:33:4014#include "base/containers/adapters.h"
Henrik Grunelle7e52912018-10-18 07:39:5315#include "base/feature_list.h"
Henrik Grunell5c1dc362019-05-20 18:06:1716#include "base/metrics/field_trial_params.h"
Dale Curtis4a6f54c02017-06-06 23:32:2017#include "base/metrics/histogram_functions.h"
asvitkine8d51e9d2016-09-02 23:55:4318#include "base/metrics/histogram_macros.h"
Yeonwoo Jo095a2732018-10-03 17:10:4019#include "base/stl_util.h"
Henrik Grunell5c1dc362019-05-20 18:06:1720#include "base/strings/strcat.h"
[email protected]9367e032014-03-07 19:42:3721#include "base/strings/string_number_conversions.h"
[email protected]348fbaac2013-06-11 06:31:5122#include "base/strings/stringprintf.h"
avi7f277562015-12-25 02:41:2623#include "build/build_config.h"
Becca Hughesd34443942018-08-17 22:15:0824#include "content/browser/media/session/media_session_impl.h"
guidou50db1a62016-06-01 17:12:1725#include "content/browser/renderer_host/media/media_stream_manager.h"
Guido Urdanetae02b5722020-07-22 05:58:2126#include "content/public/browser/audio_service.h"
xhwang29c5ad202017-04-14 07:02:1927#include "content/public/browser/browser_context.h"
Eric Seckler8652dcd52018-09-20 10:42:2828#include "content/public/browser/browser_task_traits.h"
[email protected]11158e2d2013-02-01 02:31:5629#include "content/public/browser/browser_thread.h"
Guido Urdanetae02b5722020-07-22 05:58:2130#include "content/public/browser/content_browser_client.h"
prabhur53bb9182014-11-13 03:25:1731#include "content/public/browser/notification_service.h"
32#include "content/public/browser/notification_types.h"
dalecurtise6aa75f2015-03-31 02:39:3833#include "content/public/browser/render_frame_host.h"
prabhur53bb9182014-11-13 03:25:1734#include "content/public/browser/render_process_host.h"
dalecurtise6aa75f2015-03-31 02:39:3835#include "content/public/browser/web_contents.h"
[email protected]11158e2d2013-02-01 02:31:5636#include "content/public/browser/web_ui.h"
Guido Urdanetae02b5722020-07-22 05:58:2137#include "content/public/common/content_client.h"
Henrik Grunelle7e52912018-10-18 07:39:5338#include "content/public/common/content_features.h"
Henrik Grunell5c1dc362019-05-20 18:06:1739#include "media/audio/audio_features.h"
jrummell37a54c02016-04-22 19:54:2740#include "media/base/audio_parameters.h"
Ted Meyer380ca982020-01-12 01:00:2941#include "media/base/media_log_record.h"
Henrik Grunelle7e52912018-10-18 07:39:5342#include "media/webrtc/webrtc_switches.h"
Miyoung Shin4e4b5032019-10-24 13:54:0643#include "mojo/public/cpp/bindings/self_owned_receiver.h"
Robert Sesek7d0b49b2020-07-08 18:31:2744#include "sandbox/policy/features.h"
45#include "sandbox/policy/sandbox_type.h"
[email protected]11158e2d2013-02-01 02:31:5646
kraush42521052017-05-04 19:14:3447#if !defined(OS_ANDROID)
xhwangf2189392015-10-19 22:21:5148#include "media/filters/decrypting_video_decoder.h"
49#endif
50
[email protected]69946cf2013-11-27 00:11:4251namespace {
52
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:5853std::u16string SerializeUpdate(const std::string& function,
[email protected]fcf75d42013-12-03 20:11:2654 const base::Value* value) {
[email protected]69946cf2013-11-27 00:11:4255 return content::WebUI::GetJavascriptCall(
56 function, std::vector<const base::Value*>(1, value));
57}
58
[email protected]9367e032014-03-07 19:42:3759std::string EffectsToString(int effects) {
60 if (effects == media::AudioParameters::NO_EFFECTS)
61 return "NO_EFFECTS";
62
63 struct {
64 int flag;
65 const char* name;
66 } flags[] = {
Guido Urdaneta35a86982018-02-20 13:34:3867 {media::AudioParameters::ECHO_CANCELLER, "ECHO_CANCELLER"},
68 {media::AudioParameters::DUCKING, "DUCKING"},
69 {media::AudioParameters::KEYBOARD_MIC, "KEYBOARD_MIC"},
70 {media::AudioParameters::HOTWORD, "HOTWORD"},
[email protected]9367e032014-03-07 19:42:3771 };
72
73 std::string ret;
Avi Drissman9d3ded92018-12-25 20:50:2174 for (size_t i = 0; i < base::size(flags); ++i) {
[email protected]9367e032014-03-07 19:42:3775 if (effects & flags[i].flag) {
76 if (!ret.empty())
77 ret += " | ";
78 ret += flags[i].name;
79 effects &= ~flags[i].flag;
80 }
81 }
82
83 if (effects) {
84 if (!ret.empty())
85 ret += " | ";
Raul Tambre6c0c3f5b2019-02-04 17:44:1786 ret += base::NumberToString(effects);
[email protected]9367e032014-03-07 19:42:3787 }
88
89 return ret;
90}
91
dalecurtisb9a6b7872015-02-06 22:41:5592std::string FormatToString(media::AudioParameters::Format format) {
93 switch (format) {
94 case media::AudioParameters::AUDIO_PCM_LINEAR:
95 return "pcm_linear";
96 case media::AudioParameters::AUDIO_PCM_LOW_LATENCY:
97 return "pcm_low_latency";
tsunghung59327d62016-11-19 16:09:4898 case media::AudioParameters::AUDIO_BITSTREAM_AC3:
99 return "ac3";
100 case media::AudioParameters::AUDIO_BITSTREAM_EAC3:
101 return "eac3";
dalecurtisb9a6b7872015-02-06 22:41:55102 case media::AudioParameters::AUDIO_FAKE:
103 return "fake";
dalecurtisb9a6b7872015-02-06 22:41:55104 }
105
106 NOTREACHED();
107 return "unknown";
108}
109
[email protected]69946cf2013-11-27 00:11:42110const char kAudioLogStatusKey[] = "status";
111const char kAudioLogUpdateFunction[] = "media.updateAudioComponent";
112
113} // namespace
114
[email protected]11158e2d2013-02-01 02:31:56115namespace content {
116
Abhijeet Kandalkaraad4e172020-02-19 19:31:29117// This class works as a receiver of logs of events occurring in the
118// media pipeline. Media logs send by the renderer process to the
119// browser process is handled by the below implementation in the
120// browser side.
121class MediaInternals::MediaInternalLogRecordsImpl
122 : public content::mojom::MediaInternalLogRecords {
123 public:
124 MediaInternalLogRecordsImpl(content::MediaInternals* media_internals,
125 int render_process_id);
126 ~MediaInternalLogRecordsImpl() override = default;
127 void Log(const std::vector<::media::MediaLogRecord>& arr) override;
128
129 private:
130 content::MediaInternals* const media_internals_;
131 const int render_process_id_;
132 DISALLOW_COPY_AND_ASSIGN(MediaInternalLogRecordsImpl);
133};
134
135MediaInternals::MediaInternalLogRecordsImpl::MediaInternalLogRecordsImpl(
136 content::MediaInternals* media_internals,
137 int render_process_id)
138 : media_internals_(media_internals),
139 render_process_id_(render_process_id) {}
140
141void MediaInternals::MediaInternalLogRecordsImpl::Log(
142 const std::vector<::media::MediaLogRecord>& events) {
143 media_internals_->OnMediaEvents(render_process_id_, events);
144}
145
Guido Urdanetaee6cfd02018-07-12 09:19:34146class MediaInternals::AudioLogImpl : public media::mojom::AudioLog,
147 public media::AudioLog {
[email protected]69946cf2013-11-27 00:11:42148 public:
149 AudioLogImpl(int owner_id,
150 media::AudioLogFactory::AudioComponent component,
Guido Urdaneta35a86982018-02-20 13:34:38151 content::MediaInternals* media_internals,
152 int component_id,
153 int render_process_id,
154 int render_frame_id);
dchengc2282aa2014-10-21 12:07:58155 ~AudioLogImpl() override;
[email protected]69946cf2013-11-27 00:11:42156
Guido Urdaneta35a86982018-02-20 13:34:38157 void OnCreated(const media::AudioParameters& params,
dchengc2282aa2014-10-21 12:07:58158 const std::string& device_id) override;
Guido Urdaneta35a86982018-02-20 13:34:38159 void OnStarted() override;
160 void OnStopped() override;
161 void OnClosed() override;
162 void OnError() override;
163 void OnSetVolume(double volume) override;
164 void OnLogMessage(const std::string& message) override;
Max Morin86d4f132018-08-30 21:51:13165 void OnProcessingStateChanged(const std::string& message) override;
dalecurtise6aa75f2015-03-31 02:39:38166
[email protected]69946cf2013-11-27 00:11:42167 private:
Guido Urdaneta35a86982018-02-20 13:34:38168 // If possible, i.e. a WebContents exists for the given RenderFrameHostID,
169 // tells an existing AudioLogEntry the WebContents title for easier
170 // differentiation on the UI. Note that the log entry must be created (by
171 // calling OnCreated() before calling this method.
172 void SetWebContentsTitle();
173
174 void SendSingleStringUpdate(const std::string& key, const std::string& value);
175 void StoreComponentMetadata(base::DictionaryValue* dict);
176 std::string FormatCacheKey();
[email protected]69946cf2013-11-27 00:11:42177
dcheng3b4fe472016-04-08 23:45:13178 static void SendWebContentsTitleHelper(
179 const std::string& cache_key,
180 std::unique_ptr<base::DictionaryValue> dict,
181 int render_process_id,
182 int render_frame_id);
dalecurtise6aa75f2015-03-31 02:39:38183
[email protected]69946cf2013-11-27 00:11:42184 const int owner_id_;
185 const media::AudioLogFactory::AudioComponent component_;
186 content::MediaInternals* const media_internals_;
Guido Urdaneta35a86982018-02-20 13:34:38187 const int component_id_;
188 const int render_process_id_;
189 const int render_frame_id_;
[email protected]69946cf2013-11-27 00:11:42190
191 DISALLOW_COPY_AND_ASSIGN(AudioLogImpl);
192};
193
Guido Urdanetaee6cfd02018-07-12 09:19:34194MediaInternals::AudioLogImpl::AudioLogImpl(
195 int owner_id,
196 media::AudioLogFactory::AudioComponent component,
197 content::MediaInternals* media_internals,
198 int component_id,
199 int render_process_id,
200 int render_frame_id)
[email protected]69946cf2013-11-27 00:11:42201 : owner_id_(owner_id),
202 component_(component),
Guido Urdaneta35a86982018-02-20 13:34:38203 media_internals_(media_internals),
204 component_id_(component_id),
205 render_process_id_(render_process_id),
206 render_frame_id_(render_frame_id) {}
[email protected]69946cf2013-11-27 00:11:42207
Guido Urdanetaee6cfd02018-07-12 09:19:34208MediaInternals::AudioLogImpl::~AudioLogImpl() {
209 // Ensure log is always closed. This covers the case of crashes in the audio
210 // service utility process, in which case the log may not be closed
211 // explicitly.
212 OnClosed();
213}
[email protected]69946cf2013-11-27 00:11:42214
Guido Urdanetaee6cfd02018-07-12 09:19:34215void MediaInternals::AudioLogImpl::OnCreated(
216 const media::AudioParameters& params,
217 const std::string& device_id) {
[email protected]69946cf2013-11-27 00:11:42218 base::DictionaryValue dict;
Guido Urdaneta35a86982018-02-20 13:34:38219 StoreComponentMetadata(&dict);
[email protected]69946cf2013-11-27 00:11:42220
221 dict.SetString(kAudioLogStatusKey, "created");
[email protected]25d7f892014-02-13 15:22:45222 dict.SetString("device_id", device_id);
dalecurtisb9a6b7872015-02-06 22:41:55223 dict.SetString("device_type", FormatToString(params.format()));
[email protected]69946cf2013-11-27 00:11:42224 dict.SetInteger("frames_per_buffer", params.frames_per_buffer());
225 dict.SetInteger("sample_rate", params.sample_rate());
[email protected]ad82f412013-11-27 04:20:41226 dict.SetInteger("channels", params.channels());
227 dict.SetString("channel_layout",
[email protected]69946cf2013-11-27 00:11:42228 ChannelLayoutToString(params.channel_layout()));
[email protected]9367e032014-03-07 19:42:37229 dict.SetString("effects", EffectsToString(params.effects()));
[email protected]69946cf2013-11-27 00:11:42230
Guido Urdaneta35a86982018-02-20 13:34:38231 media_internals_->UpdateAudioLog(MediaInternals::CREATE, FormatCacheKey(),
xhwang002c154f2015-06-16 02:55:54232 kAudioLogUpdateFunction, &dict);
Guido Urdaneta35a86982018-02-20 13:34:38233 SetWebContentsTitle();
[email protected]69946cf2013-11-27 00:11:42234}
235
Guido Urdanetaee6cfd02018-07-12 09:19:34236void MediaInternals::AudioLogImpl::OnStarted() {
Guido Urdaneta35a86982018-02-20 13:34:38237 SendSingleStringUpdate(kAudioLogStatusKey, "started");
[email protected]69946cf2013-11-27 00:11:42238}
239
Guido Urdanetaee6cfd02018-07-12 09:19:34240void MediaInternals::AudioLogImpl::OnStopped() {
Guido Urdaneta35a86982018-02-20 13:34:38241 SendSingleStringUpdate(kAudioLogStatusKey, "stopped");
[email protected]69946cf2013-11-27 00:11:42242}
243
Guido Urdanetaee6cfd02018-07-12 09:19:34244void MediaInternals::AudioLogImpl::OnClosed() {
[email protected]69946cf2013-11-27 00:11:42245 base::DictionaryValue dict;
Guido Urdaneta35a86982018-02-20 13:34:38246 StoreComponentMetadata(&dict);
[email protected]69946cf2013-11-27 00:11:42247 dict.SetString(kAudioLogStatusKey, "closed");
xhwang002c154f2015-06-16 02:55:54248 media_internals_->UpdateAudioLog(MediaInternals::UPDATE_AND_DELETE,
Guido Urdaneta35a86982018-02-20 13:34:38249 FormatCacheKey(), kAudioLogUpdateFunction,
250 &dict);
[email protected]69946cf2013-11-27 00:11:42251}
252
Guido Urdanetaee6cfd02018-07-12 09:19:34253void MediaInternals::AudioLogImpl::OnError() {
Guido Urdaneta35a86982018-02-20 13:34:38254 SendSingleStringUpdate("error_occurred", "true");
[email protected]69946cf2013-11-27 00:11:42255}
256
Guido Urdanetaee6cfd02018-07-12 09:19:34257void MediaInternals::AudioLogImpl::OnSetVolume(double volume) {
[email protected]69946cf2013-11-27 00:11:42258 base::DictionaryValue dict;
Guido Urdaneta35a86982018-02-20 13:34:38259 StoreComponentMetadata(&dict);
[email protected]69946cf2013-11-27 00:11:42260 dict.SetDouble("volume", volume);
xhwang002c154f2015-06-16 02:55:54261 media_internals_->UpdateAudioLog(MediaInternals::UPDATE_IF_EXISTS,
Guido Urdaneta35a86982018-02-20 13:34:38262 FormatCacheKey(), kAudioLogUpdateFunction,
263 &dict);
dalecurtise6aa75f2015-03-31 02:39:38264}
265
Max Morin86d4f132018-08-30 21:51:13266void MediaInternals::AudioLogImpl::OnProcessingStateChanged(
267 const std::string& message) {
268 SendSingleStringUpdate("processing state", message);
269}
270
Guido Urdanetaee6cfd02018-07-12 09:19:34271void MediaInternals::AudioLogImpl::OnLogMessage(const std::string& message) {
guidou50db1a62016-06-01 17:12:17272 MediaStreamManager::SendMessageToNativeLog(message);
273}
274
Guido Urdanetaee6cfd02018-07-12 09:19:34275void MediaInternals::AudioLogImpl::SetWebContentsTitle() {
Guido Urdaneta35a86982018-02-20 13:34:38276 if (render_process_id_ < 0 || render_frame_id_ < 0)
277 return;
dcheng3b4fe472016-04-08 23:45:13278 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
Guido Urdaneta35a86982018-02-20 13:34:38279 StoreComponentMetadata(dict.get());
280 SendWebContentsTitleHelper(FormatCacheKey(), std::move(dict),
281 render_process_id_, render_frame_id_);
[email protected]69946cf2013-11-27 00:11:42282}
283
Guido Urdanetaee6cfd02018-07-12 09:19:34284std::string MediaInternals::AudioLogImpl::FormatCacheKey() {
Guido Urdaneta35a86982018-02-20 13:34:38285 return base::StringPrintf("%d:%d:%d", owner_id_, component_, component_id_);
[email protected]69946cf2013-11-27 00:11:42286}
287
dalecurtise6aa75f2015-03-31 02:39:38288// static
Guido Urdanetaee6cfd02018-07-12 09:19:34289void MediaInternals::AudioLogImpl::SendWebContentsTitleHelper(
dalecurtise6aa75f2015-03-31 02:39:38290 const std::string& cache_key,
dcheng3b4fe472016-04-08 23:45:13291 std::unique_ptr<base::DictionaryValue> dict,
dalecurtise6aa75f2015-03-31 02:39:38292 int render_process_id,
293 int render_frame_id) {
294 // Page title information can only be retrieved from the UI thread.
295 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
Gabriel Charettee7cdc5cd2020-05-27 23:35:05296 GetUIThreadTaskRunner({})->PostTask(
297 FROM_HERE,
tzikccf160c2018-02-20 12:43:13298 base::BindOnce(&SendWebContentsTitleHelper, cache_key, std::move(dict),
299 render_process_id, render_frame_id));
dalecurtise6aa75f2015-03-31 02:39:38300 return;
301 }
302
Lucas Furukawa Gadanie1c5dfda2018-11-29 17:57:41303 WebContents* web_contents = WebContents::FromRenderFrameHost(
dalecurtise6aa75f2015-03-31 02:39:38304 RenderFrameHost::FromID(render_process_id, render_frame_id));
305 if (!web_contents)
306 return;
307
308 // Note: by this point the given audio log entry could have been destroyed, so
309 // we use UPDATE_IF_EXISTS to discard such instances.
310 dict->SetInteger("render_process_id", render_process_id);
311 dict->SetString("web_contents_title", web_contents->GetTitle());
xhwang002c154f2015-06-16 02:55:54312 MediaInternals::GetInstance()->UpdateAudioLog(
dalecurtise6aa75f2015-03-31 02:39:38313 MediaInternals::UPDATE_IF_EXISTS, cache_key, kAudioLogUpdateFunction,
314 dict.get());
315}
316
Guido Urdanetaee6cfd02018-07-12 09:19:34317void MediaInternals::AudioLogImpl::SendSingleStringUpdate(
318 const std::string& key,
319 const std::string& value) {
[email protected]69946cf2013-11-27 00:11:42320 base::DictionaryValue dict;
Guido Urdaneta35a86982018-02-20 13:34:38321 StoreComponentMetadata(&dict);
[email protected]69946cf2013-11-27 00:11:42322 dict.SetString(key, value);
xhwang002c154f2015-06-16 02:55:54323 media_internals_->UpdateAudioLog(MediaInternals::UPDATE_IF_EXISTS,
Guido Urdaneta35a86982018-02-20 13:34:38324 FormatCacheKey(), kAudioLogUpdateFunction,
325 &dict);
[email protected]69946cf2013-11-27 00:11:42326}
327
Guido Urdanetaee6cfd02018-07-12 09:19:34328void MediaInternals::AudioLogImpl::StoreComponentMetadata(
329 base::DictionaryValue* dict) {
[email protected]69946cf2013-11-27 00:11:42330 dict->SetInteger("owner_id", owner_id_);
Guido Urdaneta35a86982018-02-20 13:34:38331 dict->SetInteger("component_id", component_id_);
[email protected]69946cf2013-11-27 00:11:42332 dict->SetInteger("component_type", component_);
333}
334
[email protected]11158e2d2013-02-01 02:31:56335MediaInternals* MediaInternals::GetInstance() {
dalecurtisc3af5092017-02-11 02:08:18336 static content::MediaInternals* internals = new content::MediaInternals();
337 return internals;
[email protected]11158e2d2013-02-01 02:31:56338}
339
Ted Meyerd5885f82019-07-16 19:19:17340MediaInternals::MediaInternals() : can_update_(false), owner_ids_() {
Dan Sandersb26585d2017-07-12 23:51:30341 // TODO(sandersd): Is there ever a relevant case where TERMINATED is sent
342 // without CLOSED also being sent?
343 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_CLOSED,
344 NotificationService::AllBrowserContextsAndSources());
xhwange63e59b8d2015-06-10 00:45:35345 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
346 NotificationService::AllBrowserContextsAndSources());
prabhur53bb9182014-11-13 03:25:17347}
348
[email protected]11158e2d2013-02-01 02:31:56349MediaInternals::~MediaInternals() {}
350
xhwange63e59b8d2015-06-10 00:45:35351void MediaInternals::Observe(int type,
352 const NotificationSource& source,
353 const NotificationDetails& details) {
354 DCHECK_CURRENTLY_ON(BrowserThread::UI);
xhwange63e59b8d2015-06-10 00:45:35355 RenderProcessHost* process = Source<RenderProcessHost>(source).ptr();
Dan Sandersb26585d2017-07-12 23:51:30356 // TODO(sandersd): Send a termination event before clearing the log.
watkb5983f92016-08-09 20:47:51357 saved_events_by_process_.erase(process->GetID());
xhwange63e59b8d2015-06-10 00:45:35358}
359
360// Converts the |event| to a |update|. Returns whether the conversion succeeded.
361static bool ConvertEventToUpdate(int render_process_id,
Ted Meyer380ca982020-01-12 01:00:29362 const media::MediaLogRecord& event,
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:58363 std::u16string* update) {
xhwange63e59b8d2015-06-10 00:45:35364 DCHECK(update);
365
366 base::DictionaryValue dict;
367 dict.SetInteger("renderer", render_process_id);
368 dict.SetInteger("player", event.id);
xhwange63e59b8d2015-06-10 00:45:35369
370 // TODO(dalecurtis): This is technically not correct. TimeTicks "can't" be
371 // converted to to a human readable time format. See base/time/time.h.
372 const double ticks = event.time.ToInternalValue();
373 const double ticks_millis = ticks / base::Time::kMicrosecondsPerMillisecond;
374 dict.SetDouble("ticksMillis", ticks_millis);
375
Ted Meyer0134aed2020-01-23 23:56:40376 base::Value cloned_params = event.params.Clone();
377 switch (event.type) {
378 case media::MediaLogRecord::Type::kMessage:
379 dict.SetString("type", "MEDIA_LOG_ENTRY");
380 break;
381 case media::MediaLogRecord::Type::kMediaPropertyChange:
382 dict.SetString("type", "PROPERTY_CHANGE");
383 break;
384 case media::MediaLogRecord::Type::kMediaEventTriggered: {
385 // Delete the "event" param so that it won't spam the log.
386 base::Optional<base::Value> exists = cloned_params.ExtractPath("event");
387 DCHECK(exists.has_value());
388 dict.SetKey("type", std::move(exists.value()));
389 break;
390 }
Ted Meyer2a41e242020-03-20 08:01:27391 case media::MediaLogRecord::Type::kMediaStatus:
Ted Meyer0134aed2020-01-23 23:56:40392 dict.SetString("type", "PIPELINE_ERROR");
393 break;
394 }
395
xhwange63e59b8d2015-06-10 00:45:35396 // Convert PipelineStatus to human readable string
Ted Meyer2a41e242020-03-20 08:01:27397 if (event.type == media::MediaLogRecord::Type::kMediaStatus) {
xhwange63e59b8d2015-06-10 00:45:35398 int status;
399 if (!event.params.GetInteger("pipeline_error", &status) ||
400 status < static_cast<int>(media::PIPELINE_OK) ||
401 status > static_cast<int>(media::PIPELINE_STATUS_MAX)) {
402 return false;
403 }
404 media::PipelineStatus error = static_cast<media::PipelineStatus>(status);
405 dict.SetString("params.pipeline_error",
Xiaohan Wang866b0cd2019-10-31 16:57:51406 media::PipelineStatusToString(error));
xhwange63e59b8d2015-06-10 00:45:35407 } else {
Ted Meyer0134aed2020-01-23 23:56:40408 dict.SetKey("params", std::move(cloned_params));
xhwange63e59b8d2015-06-10 00:45:35409 }
410
411 *update = SerializeUpdate("media.onMediaEvent", &dict);
412 return true;
413}
414
[email protected]0e7ee582013-05-04 14:06:59415void MediaInternals::OnMediaEvents(
Dale Curtis1adbe6a2017-08-02 02:09:13416 int render_process_id,
Ted Meyer380ca982020-01-12 01:00:29417 const std::vector<media::MediaLogRecord>& events) {
xhwang0fdb8312015-06-10 23:15:38418 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]69946cf2013-11-27 00:11:42419 // Notify observers that |event| has occurred.
xhwange63e59b8d2015-06-10 00:45:35420 for (const auto& event : events) {
Dale Curtis1adbe6a2017-08-02 02:09:13421 if (CanUpdate()) {
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:58422 std::u16string update;
Dale Curtis1adbe6a2017-08-02 02:09:13423 if (ConvertEventToUpdate(render_process_id, event, &update))
424 SendUpdate(update);
prabhur53bb9182014-11-13 03:25:17425 }
Dale Curtis1adbe6a2017-08-02 02:09:13426 SaveEvent(render_process_id, event);
[email protected]0e7ee582013-05-04 14:06:59427 }
[email protected]11158e2d2013-02-01 02:31:56428}
429
danakjd15a2d82019-12-10 23:53:07430void MediaInternals::AddUpdateCallback(UpdateCallback callback) {
xhwangfe338e92015-06-08 17:36:09431 DCHECK_CURRENTLY_ON(BrowserThread::UI);
danakjd15a2d82019-12-10 23:53:07432 update_callbacks_.push_back(std::move(callback));
xhwangfe338e92015-06-08 17:36:09433
434 base::AutoLock auto_lock(lock_);
435 can_update_ = true;
Becca Hughes1aa4e672018-10-23 22:09:08436 audio_focus_helper_.SetEnabled(true);
[email protected]11158e2d2013-02-01 02:31:56437}
438
439void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) {
xhwangfe338e92015-06-08 17:36:09440 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]11158e2d2013-02-01 02:31:56441 for (size_t i = 0; i < update_callbacks_.size(); ++i) {
Robert Liao634e1972019-06-05 18:03:01442 if (update_callbacks_[i] == callback) {
[email protected]11158e2d2013-02-01 02:31:56443 update_callbacks_.erase(update_callbacks_.begin() + i);
xhwangfe338e92015-06-08 17:36:09444 break;
[email protected]11158e2d2013-02-01 02:31:56445 }
446 }
xhwangfe338e92015-06-08 17:36:09447
448 base::AutoLock auto_lock(lock_);
449 can_update_ = !update_callbacks_.empty();
Becca Hughes1aa4e672018-10-23 22:09:08450 audio_focus_helper_.SetEnabled(can_update_);
xhwangfe338e92015-06-08 17:36:09451}
452
453bool MediaInternals::CanUpdate() {
454 base::AutoLock auto_lock(lock_);
455 return can_update_;
[email protected]11158e2d2013-02-01 02:31:56456}
457
xhwange63e59b8d2015-06-10 00:45:35458void MediaInternals::SendHistoricalMediaEvents() {
459 DCHECK_CURRENTLY_ON(BrowserThread::UI);
watkb5983f92016-08-09 20:47:51460 for (const auto& saved_events : saved_events_by_process_) {
461 for (const auto& event : saved_events.second) {
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:58462 std::u16string update;
watkb5983f92016-08-09 20:47:51463 if (ConvertEventToUpdate(saved_events.first, event, &update))
xhwange63e59b8d2015-06-10 00:45:35464 SendUpdate(update);
465 }
466 }
467 // Do not clear the map/list here so that refreshing the UI or opening a
468 // second UI still works nicely!
469}
470
Henrik Grunelle7e52912018-10-18 07:39:53471void MediaInternals::SendGeneralAudioInformation() {
472 base::DictionaryValue audio_info_data;
473
474 // Audio feature information.
475 auto set_feature_data = [&](auto& feature) {
476 audio_info_data.SetKey(
477 feature.name,
478 base::Value(base::FeatureList::IsEnabled(feature) ? "Enabled"
479 : "Disabled"));
480 };
Henrik Grunell5c1dc362019-05-20 18:06:17481
Guido Urdanetad06ae842019-10-10 20:04:12482 auto set_explicit_feature_data = [&](auto& feature, bool feature_value) {
483 audio_info_data.SetKey(feature.name,
484 base::Value(feature_value ? "Enabled" : "Disabled"));
485 };
486
Henrik Grunelle7e52912018-10-18 07:39:53487 set_feature_data(features::kAudioServiceOutOfProcess);
Henrik Grunell5c1dc362019-05-20 18:06:17488
489 std::string feature_value_string;
490 if (base::FeatureList::IsEnabled(
491 features::kAudioServiceOutOfProcessKillAtHang)) {
492 std::string timeout_value = base::GetFieldTrialParamValueByFeature(
493 features::kAudioServiceOutOfProcessKillAtHang, "timeout_seconds");
494 if (timeout_value.empty())
495 timeout_value = "<undefined>";
496 feature_value_string =
497 base::StrCat({"Enabled, timeout = ", timeout_value, " seconds"});
498 } else {
499 feature_value_string = "Disabled";
500 }
501 audio_info_data.SetKey(features::kAudioServiceOutOfProcessKillAtHang.name,
502 base::Value(feature_value_string));
503
Henrik Grunelle7e52912018-10-18 07:39:53504 set_feature_data(features::kAudioServiceLaunchOnStartup);
Guido Urdanetae02b5722020-07-22 05:58:21505 set_explicit_feature_data(
506 features::kAudioServiceSandbox,
507 GetContentClient()->browser()->ShouldSandboxAudioService());
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:58508 std::u16string audio_info_update =
Henrik Grunelle7e52912018-10-18 07:39:53509 SerializeUpdate("media.updateGeneralAudioInformation", &audio_info_data);
510 SendUpdate(audio_info_update);
511}
512
mcasasfcb5c7de2014-10-11 20:22:17513void MediaInternals::SendAudioStreamData() {
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:58514 std::u16string audio_stream_update;
[email protected]69946cf2013-11-27 00:11:42515 {
516 base::AutoLock auto_lock(lock_);
Guido Urdaneta35a86982018-02-20 13:34:38517 audio_stream_update = SerializeUpdate("media.onReceiveAudioStreamData",
518 &audio_streams_cached_data_);
[email protected]11158e2d2013-02-01 02:31:56519 }
mcasasfcb5c7de2014-10-11 20:22:17520 SendUpdate(audio_stream_update);
521}
522
burnik71963562014-10-17 14:57:14523void MediaInternals::SendVideoCaptureDeviceCapabilities() {
mostynb4c27d042015-03-18 21:47:47524 DCHECK_CURRENTLY_ON(BrowserThread::IO);
xhwangfe338e92015-06-08 17:36:09525
526 if (!CanUpdate())
527 return;
528
burnik71963562014-10-17 14:57:14529 SendUpdate(SerializeUpdate("media.onReceiveVideoCaptureCapabilities",
530 &video_capture_capabilities_cached_data_));
531}
532
Becca Hughesd34443942018-08-17 22:15:08533void MediaInternals::SendAudioFocusState() {
Becca Hughes1aa4e672018-10-23 22:09:08534 audio_focus_helper_.SendAudioFocusState();
Becca Hughes8da7a66a2018-08-24 21:58:42535}
Becca Hughesd34443942018-08-17 22:15:08536
mcasasfcb5c7de2014-10-11 20:22:17537void MediaInternals::UpdateVideoCaptureDeviceCapabilities(
chfremer1c38eb42016-08-02 15:54:31538 const std::vector<std::tuple<media::VideoCaptureDeviceDescriptor,
539 media::VideoCaptureFormats>>&
540 descriptors_and_formats) {
mostynb4c27d042015-03-18 21:47:47541 DCHECK_CURRENTLY_ON(BrowserThread::IO);
burnik71963562014-10-17 14:57:14542 video_capture_capabilities_cached_data_.Clear();
mcasasfcb5c7de2014-10-11 20:22:17543
chfremer1c38eb42016-08-02 15:54:31544 for (const auto& device_format_pair : descriptors_and_formats) {
Eero Häkkinen072d9132020-09-30 22:57:35545 auto control_support = std::make_unique<base::ListValue>();
Jeremy Roman04f27c372017-10-27 15:20:55546 auto format_list = std::make_unique<base::ListValue>();
nisse7a153fb2015-12-07 09:30:59547 // TODO(nisse): Representing format information as a string, to be
548 // parsed by the javascript handler, is brittle. Consider passing
549 // a list of mappings instead.
550
chfremer1c38eb42016-08-02 15:54:31551 const media::VideoCaptureDeviceDescriptor& descriptor =
552 std::get<0>(device_format_pair);
553 const media::VideoCaptureFormats& supported_formats =
554 std::get<1>(device_format_pair);
Eero Häkkinen072d9132020-09-30 22:57:35555 if (descriptor.control_support().pan)
556 control_support->AppendString("pan");
557 if (descriptor.control_support().tilt)
558 control_support->AppendString("tilt");
559 if (descriptor.control_support().zoom)
560 control_support->AppendString("zoom");
chfremer1c38eb42016-08-02 15:54:31561 for (const auto& format : supported_formats)
ajose5d00e98782015-07-01 21:09:48562 format_list->AppendString(media::VideoCaptureFormat::ToString(format));
burnik71963562014-10-17 14:57:14563
dcheng98e96a72016-06-11 03:41:48564 std::unique_ptr<base::DictionaryValue> device_dict(
565 new base::DictionaryValue());
chfremer1c38eb42016-08-02 15:54:31566 device_dict->SetString("id", descriptor.device_id);
567 device_dict->SetString("name", descriptor.GetNameAndModel());
Eero Häkkinen072d9132020-09-30 22:57:35568 device_dict->Set("controlSupport", std::move(control_support));
jdoerrie664305c2017-06-07 08:34:34569 device_dict->Set("formats", std::move(format_list));
chfremer1c38eb42016-08-02 15:54:31570 device_dict->SetString("captureApi", descriptor.GetCaptureApiTypeString());
dcheng98e96a72016-06-11 03:41:48571 video_capture_capabilities_cached_data_.Append(std::move(device_dict));
mcasasfcb5c7de2014-10-11 20:22:17572 }
burnik71963562014-10-17 14:57:14573
xhwangfe338e92015-06-08 17:36:09574 SendVideoCaptureDeviceCapabilities();
mcasasfcb5c7de2014-10-11 20:22:17575}
576
dcheng3b4fe472016-04-08 23:45:13577std::unique_ptr<media::AudioLog> MediaInternals::CreateAudioLog(
Guido Urdaneta35a86982018-02-20 13:34:38578 AudioComponent component,
579 int component_id) {
Guido Urdanetaee6cfd02018-07-12 09:19:34580 return CreateAudioLogImpl(component, component_id, -1, MSG_ROUTING_NONE);
[email protected]e2fd1f72013-08-16 00:34:25581}
582
Miyoung Shin4e4b5032019-10-24 13:54:06583mojo::PendingRemote<media::mojom::AudioLog> MediaInternals::CreateMojoAudioLog(
Guido Urdaneta35a86982018-02-20 13:34:38584 media::AudioLogFactory::AudioComponent component,
dalecurtise6aa75f2015-03-31 02:39:38585 int component_id,
586 int render_process_id,
Guido Urdaneta35a86982018-02-20 13:34:38587 int render_frame_id) {
Miyoung Shin4e4b5032019-10-24 13:54:06588 mojo::PendingRemote<media::mojom::AudioLog> audio_log;
589 CreateMojoAudioLog(component, component_id,
590 audio_log.InitWithNewPipeAndPassReceiver(),
Guido Urdanetaee6cfd02018-07-12 09:19:34591 render_process_id, render_frame_id);
Miyoung Shin4e4b5032019-10-24 13:54:06592 return audio_log;
dalecurtise6aa75f2015-03-31 02:39:38593}
594
Guido Urdanetaee6cfd02018-07-12 09:19:34595void MediaInternals::CreateMojoAudioLog(
596 media::AudioLogFactory::AudioComponent component,
597 int component_id,
Miyoung Shin4e4b5032019-10-24 13:54:06598 mojo::PendingReceiver<media::mojom::AudioLog> receiver,
Guido Urdanetaee6cfd02018-07-12 09:19:34599 int render_process_id,
600 int render_frame_id) {
Miyoung Shin4e4b5032019-10-24 13:54:06601 mojo::MakeSelfOwnedReceiver(
Guido Urdanetaee6cfd02018-07-12 09:19:34602 CreateAudioLogImpl(component, component_id, render_process_id,
603 render_frame_id),
Miyoung Shin4e4b5032019-10-24 13:54:06604 std::move(receiver));
Guido Urdanetaee6cfd02018-07-12 09:19:34605}
606
Abhijeet Kandalkaraad4e172020-02-19 19:31:29607// static
608void MediaInternals::CreateMediaLogRecords(
609 int render_process_id,
610 mojo::PendingReceiver<content::mojom::MediaInternalLogRecords> receiver) {
611 mojo::MakeSelfOwnedReceiver(
612 std::make_unique<MediaInternalLogRecordsImpl>(
613 MediaInternals::GetInstance(), render_process_id),
614 std::move(receiver));
615}
616
Guido Urdanetaee6cfd02018-07-12 09:19:34617std::unique_ptr<MediaInternals::AudioLogImpl>
618MediaInternals::CreateAudioLogImpl(
619 media::AudioLogFactory::AudioComponent component,
620 int component_id,
621 int render_process_id,
622 int render_frame_id) {
623 base::AutoLock auto_lock(lock_);
624 return std::make_unique<AudioLogImpl>(owner_ids_[component]++, component,
625 this, component_id, render_process_id,
626 render_frame_id);
627}
628
Jan Wilken Dörrieaace0cfef2021-03-11 22:01:58629void MediaInternals::SendUpdate(const std::u16string& update) {
xhwangfe338e92015-06-08 17:36:09630 // SendUpdate() may be called from any thread, but must run on the UI thread.
631 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
Gabriel Charettee7cdc5cd2020-05-27 23:35:05632 GetUIThreadTaskRunner({})->PostTask(
633 FROM_HERE, base::BindOnce(&MediaInternals::SendUpdate,
Sami Kyostila8e4d5a92019-08-02 12:45:05634 base::Unretained(this), update));
[email protected]11158e2d2013-02-01 02:31:56635 return;
[email protected]69946cf2013-11-27 00:11:42636 }
[email protected]11158e2d2013-02-01 02:31:56637
[email protected]11158e2d2013-02-01 02:31:56638 for (size_t i = 0; i < update_callbacks_.size(); i++)
639 update_callbacks_[i].Run(update);
640}
641
xhwange63e59b8d2015-06-10 00:45:35642void MediaInternals::SaveEvent(int process_id,
Ted Meyer380ca982020-01-12 01:00:29643 const media::MediaLogRecord& event) {
xhwang0fdb8312015-06-10 23:15:38644 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Dale Curtis1ff5a375a2017-06-16 21:38:31645 auto& saved_events = saved_events_by_process_[process_id];
watkb5983f92016-08-09 20:47:51646 saved_events.push_back(event);
Dale Curtis904d79e82020-11-04 03:14:53647 if (saved_events.size() > media::MediaLog::kLogLimit) {
watkb5983f92016-08-09 20:47:51648 // Remove all events for a given player as soon as we have to remove a
649 // single event for that player to avoid showing incomplete players.
Dale Curtis1ff5a375a2017-06-16 21:38:31650 const int id_to_remove = saved_events.front().id;
Ted Meyer380ca982020-01-12 01:00:29651 base::EraseIf(saved_events, [&](const media::MediaLogRecord& event) {
Yeonwoo Jo095a2732018-10-03 17:10:40652 return event.id == id_to_remove;
653 });
watkb5983f92016-08-09 20:47:51654 }
xhwange63e59b8d2015-06-10 00:45:35655}
656
xhwang002c154f2015-06-16 02:55:54657void MediaInternals::UpdateAudioLog(AudioLogUpdateType type,
658 const std::string& cache_key,
659 const std::string& function,
660 const base::DictionaryValue* value) {
dalecurtise6aa75f2015-03-31 02:39:38661 {
662 base::AutoLock auto_lock(lock_);
663 const bool has_entry = audio_streams_cached_data_.HasKey(cache_key);
664 if ((type == UPDATE_IF_EXISTS || type == UPDATE_AND_DELETE) && !has_entry) {
665 return;
666 } else if (!has_entry) {
667 DCHECK_EQ(type, CREATE);
jdoerriecc9f5732017-08-23 14:12:30668 audio_streams_cached_data_.Set(
Jeremy Roman04f27c372017-10-27 15:20:55669 cache_key, std::make_unique<base::Value>(value->Clone()));
dalecurtise6aa75f2015-03-31 02:39:38670 } else if (type == UPDATE_AND_DELETE) {
dcheng3b4fe472016-04-08 23:45:13671 std::unique_ptr<base::Value> out_value;
dalecurtise6aa75f2015-03-31 02:39:38672 CHECK(audio_streams_cached_data_.Remove(cache_key, &out_value));
673 } else {
Ivan Kotenkov2c0d2bb32017-11-01 15:41:28674 base::DictionaryValue* existing_dict = nullptr;
dalecurtise6aa75f2015-03-31 02:39:38675 CHECK(
676 audio_streams_cached_data_.GetDictionary(cache_key, &existing_dict));
677 existing_dict->MergeDictionary(value);
678 }
[email protected]69946cf2013-11-27 00:11:42679 }
680
xhwang002c154f2015-06-16 02:55:54681 if (CanUpdate())
682 SendUpdate(SerializeUpdate(function, value));
[email protected]69946cf2013-11-27 00:11:42683}
684
[email protected]11158e2d2013-02-01 02:31:56685} // namespace content