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/path_service.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "content/browser/media/webrtc_internals_ui_observer.h"
10 #include "content/browser/web_contents/web_contents_view.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/content_browser_client.h"
13 #include "content/public/browser/notification_service.h"
14 #include "content/public/browser/notification_types.h"
15 #include "content/public/browser/render_process_host.h"
16 #include "content/public/browser/web_contents.h"
18 using base::ProcessId
;
24 // Makes sure that |dict| has a ListValue under path "log".
25 static base::ListValue
* EnsureLogList(base::DictionaryValue
* dict
) {
26 base::ListValue
* log
= NULL
;
27 if (!dict
->GetList("log", &log
)) {
28 log
= new base::ListValue();
30 dict
->Set("log", log
);
37 WebRTCInternals::WebRTCInternals()
38 : aec_dump_enabled_(false) {
39 registrar_
.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED
,
40 NotificationService::AllBrowserContextsAndSources());
41 // TODO(grunell): Shouldn't all the webrtc_internals* files be excluded from the
42 // build if WebRTC is disabled?
43 #if defined(ENABLE_WEBRTC)
45 GetContentClient()->browser()->GetDefaultDownloadDirectory();
46 if (aec_dump_file_path_
.empty()) {
47 // In this case the default path (|aec_dump_file_path_|) will be empty and
48 // the platform default path will be used in the file dialog (with no
49 // default file name). See SelectFileDialog::SelectFile. On Android where
50 // there's no dialog we'll fail to open the file.
51 VLOG(1) << "Could not get the download directory.";
54 aec_dump_file_path_
.Append(FILE_PATH_LITERAL("audio.aecdump"));
56 #endif // defined(ENABLE_WEBRTC)
59 WebRTCInternals::~WebRTCInternals() {
62 WebRTCInternals
* WebRTCInternals::GetInstance() {
63 return Singleton
<WebRTCInternals
>::get();
66 void WebRTCInternals::OnAddPeerConnection(int render_process_id
,
70 const string
& servers
,
71 const string
& constraints
) {
72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
74 base::DictionaryValue
* dict
= new base::DictionaryValue();
78 dict
->SetInteger("rid", render_process_id
);
79 dict
->SetInteger("pid", static_cast<int>(pid
));
80 dict
->SetInteger("lid", lid
);
81 dict
->SetString("servers", servers
);
82 dict
->SetString("constraints", constraints
);
83 dict
->SetString("url", url
);
84 peer_connection_data_
.Append(dict
);
86 if (observers_
.might_have_observers())
87 SendUpdate("addPeerConnection", dict
);
90 void WebRTCInternals::OnRemovePeerConnection(ProcessId pid
, int lid
) {
91 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
92 for (size_t i
= 0; i
< peer_connection_data_
.GetSize(); ++i
) {
93 base::DictionaryValue
* dict
= NULL
;
94 peer_connection_data_
.GetDictionary(i
, &dict
);
98 dict
->GetInteger("pid", &this_pid
);
99 dict
->GetInteger("lid", &this_lid
);
101 if (this_pid
!= static_cast<int>(pid
) || this_lid
!= lid
)
104 peer_connection_data_
.Remove(i
, NULL
);
106 if (observers_
.might_have_observers()) {
107 base::DictionaryValue id
;
108 id
.SetInteger("pid", static_cast<int>(pid
));
109 id
.SetInteger("lid", lid
);
110 SendUpdate("removePeerConnection", &id
);
116 void WebRTCInternals::OnUpdatePeerConnection(
117 ProcessId pid
, int lid
, const string
& type
, const string
& value
) {
118 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
120 for (size_t i
= 0; i
< peer_connection_data_
.GetSize(); ++i
) {
121 base::DictionaryValue
* record
= NULL
;
122 peer_connection_data_
.GetDictionary(i
, &record
);
124 int this_pid
= 0, this_lid
= 0;
125 record
->GetInteger("pid", &this_pid
);
126 record
->GetInteger("lid", &this_lid
);
128 if (this_pid
!= static_cast<int>(pid
) || this_lid
!= lid
)
131 // Append the update to the end of the log.
132 base::ListValue
* log
= EnsureLogList(record
);
136 base::DictionaryValue
* log_entry
= new base::DictionaryValue();
140 int64 milliseconds
= (base::Time::Now() - base::Time()).InMilliseconds();
141 string time
= base::Int64ToString(milliseconds
);
142 log_entry
->SetString("time", time
);
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
.MergeDictionary(log_entry
);
153 SendUpdate("updatePeerConnection", &update
);
159 void WebRTCInternals::OnAddStats(base::ProcessId pid
, int lid
,
160 const base::ListValue
& value
) {
161 if (!observers_
.might_have_observers())
164 base::DictionaryValue dict
;
165 dict
.SetInteger("pid", static_cast<int>(pid
));
166 dict
.SetInteger("lid", lid
);
168 base::ListValue
* list
= value
.DeepCopy();
172 dict
.Set("reports", list
);
174 SendUpdate("addStats", &dict
);
177 void WebRTCInternals::OnGetUserMedia(int rid
,
179 const std::string
& origin
,
182 const std::string
& audio_constraints
,
183 const std::string
& video_constraints
) {
184 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
186 base::DictionaryValue
* dict
= new base::DictionaryValue();
187 dict
->SetInteger("rid", rid
);
188 dict
->SetInteger("pid", static_cast<int>(pid
));
189 dict
->SetString("origin", origin
);
191 dict
->SetString("audio", audio_constraints
);
193 dict
->SetString("video", video_constraints
);
195 get_user_media_requests_
.Append(dict
);
197 if (observers_
.might_have_observers())
198 SendUpdate("addGetUserMedia", dict
);
201 void WebRTCInternals::AddObserver(WebRTCInternalsUIObserver
*observer
) {
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
203 observers_
.AddObserver(observer
);
206 void WebRTCInternals::RemoveObserver(WebRTCInternalsUIObserver
*observer
) {
207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
208 observers_
.RemoveObserver(observer
);
210 // Disables the AEC recording if it is enabled and the last webrtc-internals
211 // page is going away.
212 if (aec_dump_enabled_
&& !observers_
.might_have_observers())
216 void WebRTCInternals::UpdateObserver(WebRTCInternalsUIObserver
* observer
) {
217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
218 if (peer_connection_data_
.GetSize() > 0)
219 observer
->OnUpdate("updateAllPeerConnections", &peer_connection_data_
);
221 for (base::ListValue::iterator it
= get_user_media_requests_
.begin();
222 it
!= get_user_media_requests_
.end();
224 observer
->OnUpdate("addGetUserMedia", *it
);
228 void WebRTCInternals::EnableAecDump(content::WebContents
* web_contents
) {
229 #if defined(ENABLE_WEBRTC)
230 #if defined(OS_ANDROID)
231 EnableAecDumpOnAllRenderProcessHosts();
233 select_file_dialog_
= ui::SelectFileDialog::Create(this, NULL
);
234 select_file_dialog_
->SelectFile(
235 ui::SelectFileDialog::SELECT_SAVEAS_FILE
,
240 FILE_PATH_LITERAL(""),
241 web_contents
->GetTopLevelNativeWindow(),
247 void WebRTCInternals::DisableAecDump() {
248 #if defined(ENABLE_WEBRTC)
249 aec_dump_enabled_
= false;
251 // Tear down the dialog since the user has unchecked the AEC dump box.
252 select_file_dialog_
= NULL
;
254 for (RenderProcessHost::iterator
i(
255 content::RenderProcessHost::AllHostsIterator());
256 !i
.IsAtEnd(); i
.Advance()) {
257 i
.GetCurrentValue()->DisableAecDump();
262 void WebRTCInternals::ResetForTesting() {
263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
265 peer_connection_data_
.Clear();
266 get_user_media_requests_
.Clear();
267 aec_dump_enabled_
= false;
270 void WebRTCInternals::SendUpdate(const string
& command
, base::Value
* value
) {
271 DCHECK(observers_
.might_have_observers());
273 FOR_EACH_OBSERVER(WebRTCInternalsUIObserver
,
275 OnUpdate(command
, value
));
278 void WebRTCInternals::Observe(int type
,
279 const NotificationSource
& source
,
280 const NotificationDetails
& details
) {
281 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
282 DCHECK_EQ(type
, NOTIFICATION_RENDERER_PROCESS_TERMINATED
);
283 OnRendererExit(Source
<RenderProcessHost
>(source
)->GetID());
286 void WebRTCInternals::FileSelected(const base::FilePath
& path
,
287 int /* unused_index */,
288 void* /*unused_params */) {
289 #if defined(ENABLE_WEBRTC)
290 aec_dump_file_path_
= path
;
291 EnableAecDumpOnAllRenderProcessHosts();
295 void WebRTCInternals::FileSelectionCanceled(void* params
) {
296 #if defined(ENABLE_WEBRTC)
297 SendUpdate("aecRecordingFileSelectionCancelled", NULL
);
301 void WebRTCInternals::OnRendererExit(int render_process_id
) {
302 // Iterates from the end of the list to remove the PeerConnections created
303 // by the exitting renderer.
304 for (int i
= peer_connection_data_
.GetSize() - 1; i
>= 0; --i
) {
305 base::DictionaryValue
* record
= NULL
;
306 peer_connection_data_
.GetDictionary(i
, &record
);
309 record
->GetInteger("rid", &this_rid
);
311 if (this_rid
== render_process_id
) {
312 if (observers_
.might_have_observers()) {
313 int lid
= 0, pid
= 0;
314 record
->GetInteger("lid", &lid
);
315 record
->GetInteger("pid", &pid
);
317 base::DictionaryValue update
;
318 update
.SetInteger("lid", lid
);
319 update
.SetInteger("pid", pid
);
320 SendUpdate("removePeerConnection", &update
);
322 peer_connection_data_
.Remove(i
, NULL
);
326 bool found_any
= false;
327 // Iterates from the end of the list to remove the getUserMedia requests
328 // created by the exiting renderer.
329 for (int i
= get_user_media_requests_
.GetSize() - 1; i
>= 0; --i
) {
330 base::DictionaryValue
* record
= NULL
;
331 get_user_media_requests_
.GetDictionary(i
, &record
);
334 record
->GetInteger("rid", &this_rid
);
336 if (this_rid
== render_process_id
) {
337 get_user_media_requests_
.Remove(i
, NULL
);
342 if (found_any
&& observers_
.might_have_observers()) {
343 base::DictionaryValue update
;
344 update
.SetInteger("rid", render_process_id
);
345 SendUpdate("removeGetUserMediaForRenderer", &update
);
349 #if defined(ENABLE_WEBRTC)
350 void WebRTCInternals::EnableAecDumpOnAllRenderProcessHosts() {
351 aec_dump_enabled_
= true;
352 for (RenderProcessHost::iterator
i(
353 content::RenderProcessHost::AllHostsIterator());
354 !i
.IsAtEnd(); i
.Advance()) {
355 i
.GetCurrentValue()->EnableAecDump(aec_dump_file_path_
);
360 } // namespace content