Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / content / browser / media / webrtc_internals.cc
blob92ca83cc2f4b393dc81e0123d6a648cb10ddae7a
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/power_save_blocker.h"
16 #include "content/public/browser/render_process_host.h"
17 #include "content/public/browser/web_contents.h"
19 using base::ProcessId;
20 using std::string;
22 namespace content {
24 namespace {
25 // Makes sure that |dict| has a ListValue under path "log".
26 static base::ListValue* EnsureLogList(base::DictionaryValue* dict) {
27 base::ListValue* log = NULL;
28 if (!dict->GetList("log", &log)) {
29 log = new base::ListValue();
30 if (log)
31 dict->Set("log", log);
33 return log;
36 } // namespace
38 WebRTCInternals::WebRTCInternals()
39 : aec_dump_enabled_(false) {
40 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
41 NotificationService::AllBrowserContextsAndSources());
42 // TODO(grunell): Shouldn't all the webrtc_internals* files be excluded from the
43 // build if WebRTC is disabled?
44 #if defined(ENABLE_WEBRTC)
45 aec_dump_file_path_ =
46 GetContentClient()->browser()->GetDefaultDownloadDirectory();
47 if (aec_dump_file_path_.empty()) {
48 // In this case the default path (|aec_dump_file_path_|) will be empty and
49 // the platform default path will be used in the file dialog (with no
50 // default file name). See SelectFileDialog::SelectFile. On Android where
51 // there's no dialog we'll fail to open the file.
52 VLOG(1) << "Could not get the download directory.";
53 } else {
54 aec_dump_file_path_ =
55 aec_dump_file_path_.Append(FILE_PATH_LITERAL("audio.aecdump"));
57 #endif // defined(ENABLE_WEBRTC)
60 WebRTCInternals::~WebRTCInternals() {
63 WebRTCInternals* WebRTCInternals::GetInstance() {
64 return Singleton<WebRTCInternals>::get();
67 void WebRTCInternals::OnAddPeerConnection(int render_process_id,
68 ProcessId pid,
69 int lid,
70 const string& url,
71 const string& rtc_configuration,
72 const string& constraints) {
73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
75 base::DictionaryValue* dict = new base::DictionaryValue();
76 if (!dict)
77 return;
79 dict->SetInteger("rid", render_process_id);
80 dict->SetInteger("pid", static_cast<int>(pid));
81 dict->SetInteger("lid", lid);
82 dict->SetString("rtcConfiguration", rtc_configuration);
83 dict->SetString("constraints", constraints);
84 dict->SetString("url", url);
85 peer_connection_data_.Append(dict);
86 CreateOrReleasePowerSaveBlocker();
88 if (observers_.might_have_observers())
89 SendUpdate("addPeerConnection", dict);
92 void WebRTCInternals::OnRemovePeerConnection(ProcessId pid, int lid) {
93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
94 for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) {
95 base::DictionaryValue* dict = NULL;
96 peer_connection_data_.GetDictionary(i, &dict);
98 int this_pid = 0;
99 int this_lid = 0;
100 dict->GetInteger("pid", &this_pid);
101 dict->GetInteger("lid", &this_lid);
103 if (this_pid != static_cast<int>(pid) || this_lid != lid)
104 continue;
106 peer_connection_data_.Remove(i, NULL);
107 CreateOrReleasePowerSaveBlocker();
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);
115 break;
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)
132 continue;
134 // Append the update to the end of the log.
135 base::ListValue* log = EnsureLogList(record);
136 if (!log)
137 return;
139 base::DictionaryValue* log_entry = new base::DictionaryValue();
140 if (!log_entry)
141 return;
143 double epoch_time = base::Time::Now().ToJsTime();
144 string time = base::DoubleToString(epoch_time);
145 log_entry->SetString("time", time);
146 log_entry->SetString("type", type);
147 log_entry->SetString("value", value);
148 log->Append(log_entry);
150 if (observers_.might_have_observers()) {
151 base::DictionaryValue update;
152 update.SetInteger("pid", static_cast<int>(pid));
153 update.SetInteger("lid", lid);
154 update.MergeDictionary(log_entry);
156 SendUpdate("updatePeerConnection", &update);
158 return;
162 void WebRTCInternals::OnAddStats(base::ProcessId pid, int lid,
163 const base::ListValue& value) {
164 if (!observers_.might_have_observers())
165 return;
167 base::DictionaryValue dict;
168 dict.SetInteger("pid", static_cast<int>(pid));
169 dict.SetInteger("lid", lid);
171 base::ListValue* list = value.DeepCopy();
172 if (!list)
173 return;
175 dict.Set("reports", list);
177 SendUpdate("addStats", &dict);
180 void WebRTCInternals::OnGetUserMedia(int rid,
181 base::ProcessId pid,
182 const std::string& origin,
183 bool audio,
184 bool video,
185 const std::string& audio_constraints,
186 const std::string& video_constraints) {
187 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
189 base::DictionaryValue* dict = new base::DictionaryValue();
190 dict->SetInteger("rid", rid);
191 dict->SetInteger("pid", static_cast<int>(pid));
192 dict->SetString("origin", origin);
193 if (audio)
194 dict->SetString("audio", audio_constraints);
195 if (video)
196 dict->SetString("video", video_constraints);
198 get_user_media_requests_.Append(dict);
200 if (observers_.might_have_observers())
201 SendUpdate("addGetUserMedia", dict);
204 void WebRTCInternals::AddObserver(WebRTCInternalsUIObserver *observer) {
205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
206 observers_.AddObserver(observer);
209 void WebRTCInternals::RemoveObserver(WebRTCInternalsUIObserver *observer) {
210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
211 observers_.RemoveObserver(observer);
213 // Disables the AEC recording if it is enabled and the last webrtc-internals
214 // page is going away.
215 if (aec_dump_enabled_ && !observers_.might_have_observers())
216 DisableAecDump();
219 void WebRTCInternals::UpdateObserver(WebRTCInternalsUIObserver* observer) {
220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
221 if (peer_connection_data_.GetSize() > 0)
222 observer->OnUpdate("updateAllPeerConnections", &peer_connection_data_);
224 for (base::ListValue::iterator it = get_user_media_requests_.begin();
225 it != get_user_media_requests_.end();
226 ++it) {
227 observer->OnUpdate("addGetUserMedia", *it);
231 void WebRTCInternals::EnableAecDump(content::WebContents* web_contents) {
232 #if defined(ENABLE_WEBRTC)
233 #if defined(OS_ANDROID)
234 EnableAecDumpOnAllRenderProcessHosts();
235 #else
236 select_file_dialog_ = ui::SelectFileDialog::Create(this, NULL);
237 select_file_dialog_->SelectFile(
238 ui::SelectFileDialog::SELECT_SAVEAS_FILE,
239 base::string16(),
240 aec_dump_file_path_,
241 NULL,
243 FILE_PATH_LITERAL(""),
244 web_contents->GetTopLevelNativeWindow(),
245 NULL);
246 #endif
247 #endif
250 void WebRTCInternals::DisableAecDump() {
251 #if defined(ENABLE_WEBRTC)
252 aec_dump_enabled_ = false;
254 // Tear down the dialog since the user has unchecked the AEC dump box.
255 select_file_dialog_ = NULL;
257 for (RenderProcessHost::iterator i(
258 content::RenderProcessHost::AllHostsIterator());
259 !i.IsAtEnd(); i.Advance()) {
260 i.GetCurrentValue()->DisableAecDump();
262 #endif
265 void WebRTCInternals::ResetForTesting() {
266 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
267 observers_.Clear();
268 peer_connection_data_.Clear();
269 CreateOrReleasePowerSaveBlocker();
270 get_user_media_requests_.Clear();
271 aec_dump_enabled_ = false;
274 void WebRTCInternals::SendUpdate(const string& command, base::Value* value) {
275 DCHECK(observers_.might_have_observers());
277 FOR_EACH_OBSERVER(WebRTCInternalsUIObserver,
278 observers_,
279 OnUpdate(command, value));
282 void WebRTCInternals::Observe(int type,
283 const NotificationSource& source,
284 const NotificationDetails& details) {
285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
286 DCHECK_EQ(type, NOTIFICATION_RENDERER_PROCESS_TERMINATED);
287 OnRendererExit(Source<RenderProcessHost>(source)->GetID());
290 void WebRTCInternals::FileSelected(const base::FilePath& path,
291 int /* unused_index */,
292 void* /*unused_params */) {
293 #if defined(ENABLE_WEBRTC)
294 aec_dump_file_path_ = path;
295 EnableAecDumpOnAllRenderProcessHosts();
296 #endif
299 void WebRTCInternals::FileSelectionCanceled(void* params) {
300 #if defined(ENABLE_WEBRTC)
301 SendUpdate("aecRecordingFileSelectionCancelled", NULL);
302 #endif
305 void WebRTCInternals::OnRendererExit(int render_process_id) {
306 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
308 // Iterates from the end of the list to remove the PeerConnections created
309 // by the exitting renderer.
310 for (int i = peer_connection_data_.GetSize() - 1; i >= 0; --i) {
311 base::DictionaryValue* record = NULL;
312 peer_connection_data_.GetDictionary(i, &record);
314 int this_rid = 0;
315 record->GetInteger("rid", &this_rid);
317 if (this_rid == render_process_id) {
318 if (observers_.might_have_observers()) {
319 int lid = 0, pid = 0;
320 record->GetInteger("lid", &lid);
321 record->GetInteger("pid", &pid);
323 base::DictionaryValue update;
324 update.SetInteger("lid", lid);
325 update.SetInteger("pid", pid);
326 SendUpdate("removePeerConnection", &update);
328 peer_connection_data_.Remove(i, NULL);
331 CreateOrReleasePowerSaveBlocker();
333 bool found_any = false;
334 // Iterates from the end of the list to remove the getUserMedia requests
335 // created by the exiting renderer.
336 for (int i = get_user_media_requests_.GetSize() - 1; i >= 0; --i) {
337 base::DictionaryValue* record = NULL;
338 get_user_media_requests_.GetDictionary(i, &record);
340 int this_rid = 0;
341 record->GetInteger("rid", &this_rid);
343 if (this_rid == render_process_id) {
344 get_user_media_requests_.Remove(i, NULL);
345 found_any = true;
349 if (found_any && observers_.might_have_observers()) {
350 base::DictionaryValue update;
351 update.SetInteger("rid", render_process_id);
352 SendUpdate("removeGetUserMediaForRenderer", &update);
356 #if defined(ENABLE_WEBRTC)
357 void WebRTCInternals::EnableAecDumpOnAllRenderProcessHosts() {
358 aec_dump_enabled_ = true;
359 for (RenderProcessHost::iterator i(
360 content::RenderProcessHost::AllHostsIterator());
361 !i.IsAtEnd(); i.Advance()) {
362 i.GetCurrentValue()->EnableAecDump(aec_dump_file_path_);
365 #endif
367 void WebRTCInternals::CreateOrReleasePowerSaveBlocker() {
368 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
370 if (peer_connection_data_.empty() && power_save_blocker_) {
371 DVLOG(1) << ("Releasing the block on application suspension since no "
372 "PeerConnections are active anymore.");
373 power_save_blocker_.reset();
374 } else if (!peer_connection_data_.empty() && !power_save_blocker_) {
375 DVLOG(1) << ("Preventing the application from being suspended while one or "
376 "more PeerConnections are active.");
377 power_save_blocker_ = content::PowerSaveBlocker::Create(
378 content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension,
379 "WebRTC has active PeerConnections.").Pass();
383 } // namespace content