Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / content / browser / media / webrtc_internals.cc
blob03f6cd04899a3d925a8f4cb49d0419b36d17d7c4
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/strings/string_number_conversions.h"
8 #include "content/browser/media/webrtc_internals_ui_observer.h"
9 #include "content/browser/web_contents/web_contents_view.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "content/public/browser/content_browser_client.h"
12 #include "content/public/browser/power_save_blocker.h"
13 #include "content/public/browser/render_process_host.h"
14 #include "content/public/browser/web_contents.h"
16 using base::ProcessId;
17 using std::string;
19 namespace content {
21 namespace {
23 static base::LazyInstance<WebRTCInternals>::Leaky g_webrtc_internals =
24 LAZY_INSTANCE_INITIALIZER;
26 // Makes sure that |dict| has a ListValue under path "log".
27 static base::ListValue* EnsureLogList(base::DictionaryValue* dict) {
28 base::ListValue* log = NULL;
29 if (!dict->GetList("log", &log)) {
30 log = new base::ListValue();
31 if (log)
32 dict->Set("log", log);
34 return log;
37 } // namespace
39 WebRTCInternals::WebRTCInternals()
40 : audio_debug_recordings_(false) {
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)
44 audio_debug_recordings_file_path_ =
45 GetContentClient()->browser()->GetDefaultDownloadDirectory();
46 if (audio_debug_recordings_file_path_.empty()) {
47 // In this case the default path (|audio_debug_recordings_file_path_|) will
48 // be empty and the platform default path will be used in the file dialog
49 // (with no default file name). See SelectFileDialog::SelectFile. On Android
50 // where there's no dialog we'll fail to open the file.
51 VLOG(1) << "Could not get the download directory.";
52 } else {
53 audio_debug_recordings_file_path_ =
54 audio_debug_recordings_file_path_.Append(
55 FILE_PATH_LITERAL("audio_debug"));
57 #endif // defined(ENABLE_WEBRTC)
60 WebRTCInternals::~WebRTCInternals() {
63 WebRTCInternals* WebRTCInternals::GetInstance() {
64 return g_webrtc_internals.Pointer();
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_CURRENTLY_ON(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);
91 if (render_process_id_set_.insert(render_process_id).second) {
92 RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
93 if (host)
94 host->AddObserver(this);
98 void WebRTCInternals::OnRemovePeerConnection(ProcessId pid, int lid) {
99 DCHECK_CURRENTLY_ON(BrowserThread::UI);
100 for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) {
101 base::DictionaryValue* dict = NULL;
102 peer_connection_data_.GetDictionary(i, &dict);
104 int this_pid = 0;
105 int this_lid = 0;
106 dict->GetInteger("pid", &this_pid);
107 dict->GetInteger("lid", &this_lid);
109 if (this_pid != static_cast<int>(pid) || this_lid != lid)
110 continue;
112 peer_connection_data_.Remove(i, NULL);
113 CreateOrReleasePowerSaveBlocker();
115 if (observers_.might_have_observers()) {
116 base::DictionaryValue id;
117 id.SetInteger("pid", static_cast<int>(pid));
118 id.SetInteger("lid", lid);
119 SendUpdate("removePeerConnection", &id);
121 break;
125 void WebRTCInternals::OnUpdatePeerConnection(
126 ProcessId pid, int lid, const string& type, const string& value) {
127 DCHECK_CURRENTLY_ON(BrowserThread::UI);
129 for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) {
130 base::DictionaryValue* record = NULL;
131 peer_connection_data_.GetDictionary(i, &record);
133 int this_pid = 0, this_lid = 0;
134 record->GetInteger("pid", &this_pid);
135 record->GetInteger("lid", &this_lid);
137 if (this_pid != static_cast<int>(pid) || this_lid != lid)
138 continue;
140 // Append the update to the end of the log.
141 base::ListValue* log = EnsureLogList(record);
142 if (!log)
143 return;
145 base::DictionaryValue* log_entry = new base::DictionaryValue();
146 if (!log_entry)
147 return;
149 double epoch_time = base::Time::Now().ToJsTime();
150 string time = base::DoubleToString(epoch_time);
151 log_entry->SetString("time", time);
152 log_entry->SetString("type", type);
153 log_entry->SetString("value", value);
154 log->Append(log_entry);
156 if (observers_.might_have_observers()) {
157 base::DictionaryValue update;
158 update.SetInteger("pid", static_cast<int>(pid));
159 update.SetInteger("lid", lid);
160 update.MergeDictionary(log_entry);
162 SendUpdate("updatePeerConnection", &update);
164 return;
168 void WebRTCInternals::OnAddStats(base::ProcessId pid, int lid,
169 const base::ListValue& value) {
170 if (!observers_.might_have_observers())
171 return;
173 base::DictionaryValue dict;
174 dict.SetInteger("pid", static_cast<int>(pid));
175 dict.SetInteger("lid", lid);
177 base::ListValue* list = value.DeepCopy();
178 if (!list)
179 return;
181 dict.Set("reports", list);
183 SendUpdate("addStats", &dict);
186 void WebRTCInternals::OnGetUserMedia(int rid,
187 base::ProcessId pid,
188 const std::string& origin,
189 bool audio,
190 bool video,
191 const std::string& audio_constraints,
192 const std::string& video_constraints) {
193 DCHECK_CURRENTLY_ON(BrowserThread::UI);
195 base::DictionaryValue* dict = new base::DictionaryValue();
196 dict->SetInteger("rid", rid);
197 dict->SetInteger("pid", static_cast<int>(pid));
198 dict->SetString("origin", origin);
199 if (audio)
200 dict->SetString("audio", audio_constraints);
201 if (video)
202 dict->SetString("video", video_constraints);
204 get_user_media_requests_.Append(dict);
206 if (observers_.might_have_observers())
207 SendUpdate("addGetUserMedia", dict);
209 if (render_process_id_set_.insert(rid).second) {
210 RenderProcessHost* host = RenderProcessHost::FromID(rid);
211 if (host)
212 host->AddObserver(this);
216 void WebRTCInternals::AddObserver(WebRTCInternalsUIObserver *observer) {
217 DCHECK_CURRENTLY_ON(BrowserThread::UI);
218 observers_.AddObserver(observer);
221 void WebRTCInternals::RemoveObserver(WebRTCInternalsUIObserver *observer) {
222 DCHECK_CURRENTLY_ON(BrowserThread::UI);
223 observers_.RemoveObserver(observer);
225 // Disables audio debug recordings if it is enabled and the last
226 // webrtc-internals page is going away.
227 if (audio_debug_recordings_ && !observers_.might_have_observers())
228 DisableAudioDebugRecordings();
231 void WebRTCInternals::UpdateObserver(WebRTCInternalsUIObserver* observer) {
232 DCHECK_CURRENTLY_ON(BrowserThread::UI);
233 if (peer_connection_data_.GetSize() > 0)
234 observer->OnUpdate("updateAllPeerConnections", &peer_connection_data_);
236 for (base::ListValue::iterator it = get_user_media_requests_.begin();
237 it != get_user_media_requests_.end();
238 ++it) {
239 observer->OnUpdate("addGetUserMedia", *it);
243 void WebRTCInternals::EnableAudioDebugRecordings(
244 content::WebContents* web_contents) {
245 #if defined(ENABLE_WEBRTC)
246 DCHECK_CURRENTLY_ON(BrowserThread::UI);
247 #if defined(OS_ANDROID)
248 EnableAudioDebugRecordingsOnAllRenderProcessHosts();
249 #else
250 select_file_dialog_ = ui::SelectFileDialog::Create(this, NULL);
251 select_file_dialog_->SelectFile(
252 ui::SelectFileDialog::SELECT_SAVEAS_FILE,
253 base::string16(),
254 audio_debug_recordings_file_path_,
255 NULL,
257 FILE_PATH_LITERAL(""),
258 web_contents->GetTopLevelNativeWindow(),
259 NULL);
260 #endif
261 #endif
264 void WebRTCInternals::DisableAudioDebugRecordings() {
265 #if defined(ENABLE_WEBRTC)
266 DCHECK_CURRENTLY_ON(BrowserThread::UI);
267 audio_debug_recordings_ = false;
269 // Tear down the dialog since the user has unchecked the audio debug
270 // recordings box.
271 select_file_dialog_ = NULL;
273 for (RenderProcessHost::iterator i(
274 content::RenderProcessHost::AllHostsIterator());
275 !i.IsAtEnd(); i.Advance()) {
276 i.GetCurrentValue()->DisableAudioDebugRecordings();
278 #endif
281 bool WebRTCInternals::IsAudioDebugRecordingsEnabled() const {
282 DCHECK_CURRENTLY_ON(BrowserThread::UI);
283 return audio_debug_recordings_;
286 const base::FilePath& WebRTCInternals::GetAudioDebugRecordingsFilePath() const {
287 DCHECK_CURRENTLY_ON(BrowserThread::UI);
288 return audio_debug_recordings_file_path_;
291 void WebRTCInternals::ResetForTesting() {
292 DCHECK_CURRENTLY_ON(BrowserThread::UI);
293 observers_.Clear();
294 peer_connection_data_.Clear();
295 CreateOrReleasePowerSaveBlocker();
296 get_user_media_requests_.Clear();
297 audio_debug_recordings_ = false;
300 void WebRTCInternals::SendUpdate(const string& command, base::Value* value) {
301 DCHECK(observers_.might_have_observers());
303 FOR_EACH_OBSERVER(WebRTCInternalsUIObserver,
304 observers_,
305 OnUpdate(command, value));
308 void WebRTCInternals::RenderProcessHostDestroyed(RenderProcessHost* host) {
309 DCHECK_CURRENTLY_ON(BrowserThread::UI);
310 OnRendererExit(host->GetID());
312 render_process_id_set_.erase(host->GetID());
313 host->RemoveObserver(this);
316 void WebRTCInternals::FileSelected(const base::FilePath& path,
317 int /* unused_index */,
318 void* /*unused_params */) {
319 #if defined(ENABLE_WEBRTC)
320 DCHECK_CURRENTLY_ON(BrowserThread::UI);
321 audio_debug_recordings_file_path_ = path;
322 EnableAudioDebugRecordingsOnAllRenderProcessHosts();
323 #endif
326 void WebRTCInternals::FileSelectionCanceled(void* params) {
327 #if defined(ENABLE_WEBRTC)
328 DCHECK_CURRENTLY_ON(BrowserThread::UI);
329 SendUpdate("audioDebugRecordingsFileSelectionCancelled", NULL);
330 #endif
333 void WebRTCInternals::OnRendererExit(int render_process_id) {
334 DCHECK_CURRENTLY_ON(BrowserThread::UI);
336 // Iterates from the end of the list to remove the PeerConnections created
337 // by the exitting renderer.
338 for (int i = peer_connection_data_.GetSize() - 1; i >= 0; --i) {
339 base::DictionaryValue* record = NULL;
340 peer_connection_data_.GetDictionary(i, &record);
342 int this_rid = 0;
343 record->GetInteger("rid", &this_rid);
345 if (this_rid == render_process_id) {
346 if (observers_.might_have_observers()) {
347 int lid = 0, pid = 0;
348 record->GetInteger("lid", &lid);
349 record->GetInteger("pid", &pid);
351 base::DictionaryValue update;
352 update.SetInteger("lid", lid);
353 update.SetInteger("pid", pid);
354 SendUpdate("removePeerConnection", &update);
356 peer_connection_data_.Remove(i, NULL);
359 CreateOrReleasePowerSaveBlocker();
361 bool found_any = false;
362 // Iterates from the end of the list to remove the getUserMedia requests
363 // created by the exiting renderer.
364 for (int i = get_user_media_requests_.GetSize() - 1; i >= 0; --i) {
365 base::DictionaryValue* record = NULL;
366 get_user_media_requests_.GetDictionary(i, &record);
368 int this_rid = 0;
369 record->GetInteger("rid", &this_rid);
371 if (this_rid == render_process_id) {
372 get_user_media_requests_.Remove(i, NULL);
373 found_any = true;
377 if (found_any && observers_.might_have_observers()) {
378 base::DictionaryValue update;
379 update.SetInteger("rid", render_process_id);
380 SendUpdate("removeGetUserMediaForRenderer", &update);
384 #if defined(ENABLE_WEBRTC)
385 void WebRTCInternals::EnableAudioDebugRecordingsOnAllRenderProcessHosts() {
386 DCHECK_CURRENTLY_ON(BrowserThread::UI);
388 audio_debug_recordings_ = true;
389 for (RenderProcessHost::iterator i(
390 content::RenderProcessHost::AllHostsIterator());
391 !i.IsAtEnd(); i.Advance()) {
392 i.GetCurrentValue()->EnableAudioDebugRecordings(
393 audio_debug_recordings_file_path_);
396 #endif
398 void WebRTCInternals::CreateOrReleasePowerSaveBlocker() {
399 DCHECK_CURRENTLY_ON(BrowserThread::UI);
401 if (peer_connection_data_.empty() && power_save_blocker_) {
402 DVLOG(1) << ("Releasing the block on application suspension since no "
403 "PeerConnections are active anymore.");
404 power_save_blocker_.reset();
405 } else if (!peer_connection_data_.empty() && !power_save_blocker_) {
406 DVLOG(1) << ("Preventing the application from being suspended while one or "
407 "more PeerConnections are active.");
408 power_save_blocker_ =
409 content::PowerSaveBlocker::Create(
410 PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension,
411 PowerSaveBlocker::kReasonOther,
412 "WebRTC has active PeerConnections").Pass();
416 } // namespace content