1 // Copyright (c) 2013 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.
5 #include "content/browser/media/webrtc_internals.h"
7 #include "base/command_line.h"
8 #include "content/browser/media/webrtc_internals_ui_observer.h"
9 #include "content/public/browser/browser_thread.h"
10 #include "content/public/browser/notification_service.h"
11 #include "content/public/browser/notification_types.h"
12 #include "content/public/browser/render_process_host.h"
13 #include "content/public/browser/web_contents.h"
14 #include "content/public/browser/web_contents_view.h"
15 #include "content/public/common/content_switches.h"
17 using base::ProcessId
;
23 // Makes sure that |dict| has a ListValue under path "log".
24 static base::ListValue
* EnsureLogList(base::DictionaryValue
* dict
) {
25 base::ListValue
* log
= NULL
;
26 if (!dict
->GetList("log", &log
)) {
27 log
= new base::ListValue();
29 dict
->Set("log", log
);
36 WebRTCInternals::WebRTCInternals()
37 : aec_dump_enabled_(false) {
38 registrar_
.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED
,
39 NotificationService::AllBrowserContextsAndSources());
40 // TODO(grunell): Shouldn't all the webrtc_internals* files be excluded from the
41 // build if WebRTC is disabled?
42 #if defined(ENABLE_WEBRTC)
43 if (CommandLine::ForCurrentProcess()->HasSwitch(
44 switches::kEnableWebRtcAecRecordings
)) {
45 aec_dump_enabled_
= true;
46 aec_dump_file_path_
= CommandLine::ForCurrentProcess()->GetSwitchValuePath(
47 switches::kEnableWebRtcAecRecordings
);
49 #if defined(OS_CHROMEOS)
51 base::FilePath(FILE_PATH_LITERAL("/tmp/audio.aecdump"));
52 #elif defined(OS_ANDROID)
54 base::FilePath(FILE_PATH_LITERAL("/sdcard/audio.aecdump"));
56 aec_dump_file_path_
= base::FilePath(FILE_PATH_LITERAL("audio.aecdump"));
59 #endif // defined(ENABLE_WEBRTC)
62 WebRTCInternals::~WebRTCInternals() {
65 WebRTCInternals
* WebRTCInternals::GetInstance() {
66 return Singleton
<WebRTCInternals
>::get();
69 void WebRTCInternals::OnAddPeerConnection(int render_process_id
,
73 const string
& servers
,
74 const string
& constraints
) {
75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
77 base::DictionaryValue
* dict
= new base::DictionaryValue();
81 dict
->SetInteger("rid", render_process_id
);
82 dict
->SetInteger("pid", static_cast<int>(pid
));
83 dict
->SetInteger("lid", lid
);
84 dict
->SetString("servers", servers
);
85 dict
->SetString("constraints", constraints
);
86 dict
->SetString("url", url
);
87 peer_connection_data_
.Append(dict
);
89 if (observers_
.might_have_observers())
90 SendUpdate("addPeerConnection", dict
);
93 void WebRTCInternals::OnRemovePeerConnection(ProcessId pid
, int lid
) {
94 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
95 for (size_t i
= 0; i
< peer_connection_data_
.GetSize(); ++i
) {
96 base::DictionaryValue
* dict
= NULL
;
97 peer_connection_data_
.GetDictionary(i
, &dict
);
101 dict
->GetInteger("pid", &this_pid
);
102 dict
->GetInteger("lid", &this_lid
);
104 if (this_pid
!= static_cast<int>(pid
) || this_lid
!= lid
)
107 peer_connection_data_
.Remove(i
, NULL
);
109 if (observers_
.might_have_observers()) {
110 base::DictionaryValue id
;
111 id
.SetInteger("pid", static_cast<int>(pid
));
112 id
.SetInteger("lid", lid
);
113 SendUpdate("removePeerConnection", &id
);
119 void WebRTCInternals::OnUpdatePeerConnection(
120 ProcessId pid
, int lid
, const string
& type
, const string
& value
) {
121 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
123 for (size_t i
= 0; i
< peer_connection_data_
.GetSize(); ++i
) {
124 base::DictionaryValue
* record
= NULL
;
125 peer_connection_data_
.GetDictionary(i
, &record
);
127 int this_pid
= 0, this_lid
= 0;
128 record
->GetInteger("pid", &this_pid
);
129 record
->GetInteger("lid", &this_lid
);
131 if (this_pid
!= static_cast<int>(pid
) || this_lid
!= lid
)
134 // Append the update to the end of the log.
135 base::ListValue
* log
= EnsureLogList(record
);
139 base::DictionaryValue
* log_entry
= new base::DictionaryValue();
143 log_entry
->SetString("type", type
);
144 log_entry
->SetString("value", value
);
145 log
->Append(log_entry
);
147 if (observers_
.might_have_observers()) {
148 base::DictionaryValue update
;
149 update
.SetInteger("pid", static_cast<int>(pid
));
150 update
.SetInteger("lid", lid
);
151 update
.SetString("type", type
);
152 update
.SetString("value", value
);
154 SendUpdate("updatePeerConnection", &update
);
160 void WebRTCInternals::OnAddStats(base::ProcessId pid
, int lid
,
161 const base::ListValue
& value
) {
162 if (!observers_
.might_have_observers())
165 base::DictionaryValue dict
;
166 dict
.SetInteger("pid", static_cast<int>(pid
));
167 dict
.SetInteger("lid", lid
);
169 base::ListValue
* list
= value
.DeepCopy();
173 dict
.Set("reports", list
);
175 SendUpdate("addStats", &dict
);
178 void WebRTCInternals::OnGetUserMedia(int rid
,
180 const std::string
& origin
,
183 const std::string
& audio_constraints
,
184 const std::string
& video_constraints
) {
185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
187 base::DictionaryValue
* dict
= new base::DictionaryValue();
188 dict
->SetInteger("rid", rid
);
189 dict
->SetInteger("pid", static_cast<int>(pid
));
190 dict
->SetString("origin", origin
);
192 dict
->SetString("audio", audio_constraints
);
194 dict
->SetString("video", video_constraints
);
196 get_user_media_requests_
.Append(dict
);
198 if (observers_
.might_have_observers())
199 SendUpdate("addGetUserMedia", dict
);
202 void WebRTCInternals::AddObserver(WebRTCInternalsUIObserver
*observer
) {
203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
204 observers_
.AddObserver(observer
);
207 void WebRTCInternals::RemoveObserver(WebRTCInternalsUIObserver
*observer
) {
208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
209 observers_
.RemoveObserver(observer
);
211 // Disables the AEC recording if it is enabled and the last webrtc-internals
212 // page is going away.
213 if (aec_dump_enabled_
&& !observers_
.might_have_observers())
217 void WebRTCInternals::UpdateObserver(WebRTCInternalsUIObserver
* observer
) {
218 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
219 if (peer_connection_data_
.GetSize() > 0)
220 observer
->OnUpdate("updateAllPeerConnections", &peer_connection_data_
);
222 for (base::ListValue::iterator it
= get_user_media_requests_
.begin();
223 it
!= get_user_media_requests_
.end();
225 observer
->OnUpdate("addGetUserMedia", *it
);
229 void WebRTCInternals::EnableAecDump(content::WebContents
* web_contents
) {
230 #if defined(ENABLE_WEBRTC)
231 #if defined(OS_ANDROID)
232 EnableAecDumpOnAllRenderProcessHosts();
234 select_file_dialog_
= ui::SelectFileDialog::Create(this, NULL
);
235 select_file_dialog_
->SelectFile(
236 ui::SelectFileDialog::SELECT_SAVEAS_FILE
,
241 FILE_PATH_LITERAL(""),
242 web_contents
->GetView()->GetTopLevelNativeWindow(),
248 void WebRTCInternals::DisableAecDump() {
249 #if defined(ENABLE_WEBRTC)
250 aec_dump_enabled_
= false;
251 for (RenderProcessHost::iterator
i(
252 content::RenderProcessHost::AllHostsIterator());
253 !i
.IsAtEnd(); i
.Advance()) {
254 i
.GetCurrentValue()->DisableAecDump();
259 void WebRTCInternals::ResetForTesting() {
260 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
262 peer_connection_data_
.Clear();
263 get_user_media_requests_
.Clear();
264 aec_dump_enabled_
= false;
267 void WebRTCInternals::SendUpdate(const string
& command
, base::Value
* value
) {
268 DCHECK(observers_
.might_have_observers());
270 FOR_EACH_OBSERVER(WebRTCInternalsUIObserver
,
272 OnUpdate(command
, value
));
275 void WebRTCInternals::Observe(int type
,
276 const NotificationSource
& source
,
277 const NotificationDetails
& details
) {
278 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
279 DCHECK_EQ(type
, NOTIFICATION_RENDERER_PROCESS_TERMINATED
);
280 OnRendererExit(Source
<RenderProcessHost
>(source
)->GetID());
283 void WebRTCInternals::FileSelected(const base::FilePath
& path
,
284 int /* unused_index */,
285 void* /*unused_params */) {
286 #if defined(ENABLE_WEBRTC)
287 aec_dump_file_path_
= path
;
288 EnableAecDumpOnAllRenderProcessHosts();
292 void WebRTCInternals::FileSelectionCanceled(void* params
) {
293 #if defined(ENABLE_WEBRTC)
294 SendUpdate("aecRecordingFileSelectionCancelled", NULL
);
298 void WebRTCInternals::OnRendererExit(int render_process_id
) {
299 // Iterates from the end of the list to remove the PeerConnections created
300 // by the exitting renderer.
301 for (int i
= peer_connection_data_
.GetSize() - 1; i
>= 0; --i
) {
302 base::DictionaryValue
* record
= NULL
;
303 peer_connection_data_
.GetDictionary(i
, &record
);
306 record
->GetInteger("rid", &this_rid
);
308 if (this_rid
== render_process_id
) {
309 if (observers_
.might_have_observers()) {
310 int lid
= 0, pid
= 0;
311 record
->GetInteger("lid", &lid
);
312 record
->GetInteger("pid", &pid
);
314 base::DictionaryValue update
;
315 update
.SetInteger("lid", lid
);
316 update
.SetInteger("pid", pid
);
317 SendUpdate("removePeerConnection", &update
);
319 peer_connection_data_
.Remove(i
, NULL
);
323 bool found_any
= false;
324 // Iterates from the end of the list to remove the getUserMedia requests
325 // created by the exiting renderer.
326 for (int i
= get_user_media_requests_
.GetSize() - 1; i
>= 0; --i
) {
327 base::DictionaryValue
* record
= NULL
;
328 get_user_media_requests_
.GetDictionary(i
, &record
);
331 record
->GetInteger("rid", &this_rid
);
333 if (this_rid
== render_process_id
) {
334 get_user_media_requests_
.Remove(i
, NULL
);
339 if (found_any
&& observers_
.might_have_observers()) {
340 base::DictionaryValue update
;
341 update
.SetInteger("rid", render_process_id
);
342 SendUpdate("removeGetUserMediaForRenderer", &update
);
346 #if defined(ENABLE_WEBRTC)
347 void WebRTCInternals::EnableAecDumpOnAllRenderProcessHosts() {
348 aec_dump_enabled_
= true;
349 for (RenderProcessHost::iterator
i(
350 content::RenderProcessHost::AllHostsIterator());
351 !i
.IsAtEnd(); i
.Advance()) {
352 i
.GetCurrentValue()->EnableAecDump(aec_dump_file_path_
);
357 } // namespace content