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. 4 5 #include "content/browser/media/webrtc_internals.h" 6 7 #include "content/browser/media/webrtc_internals_ui_observer.h" 8 #include "content/public/browser/browser_thread.h" 9 #include "content/public/browser/child_process_data.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 14 using base::ProcessId; 15 using std::string; 16 17 namespace content { 18 19 namespace { 20 // Makes sure that |dict| has a ListValue under path "log". 21 static base::ListValue* EnsureLogList(base::DictionaryValue* dict) { 22 base::ListValue* log = NULL; 23 if (!dict->GetList("log", &log)) { 24 log = new base::ListValue(); 25 if (log) 26 dict->Set("log", log); 27 } 28 return log; 29 } 30 31 } // namespace 32 33 WebRTCInternals::WebRTCInternals() : is_recording_rtp_(false) { 34 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED, 35 NotificationService::AllBrowserContextsAndSources()); 36 37 BrowserChildProcessObserver::Add(this); 38 } 39 40 WebRTCInternals::~WebRTCInternals() { 41 BrowserChildProcessObserver::Remove(this); 42 } 43 44 WebRTCInternals* WebRTCInternals::GetInstance() { 45 return Singleton<WebRTCInternals>::get(); 46 } 47 48 void WebRTCInternals::OnAddPeerConnection(int render_process_id, 49 ProcessId pid, 50 int lid, 51 const string& url, 52 const string& servers, 53 const string& constraints) { 54 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 55 56 base::DictionaryValue* dict = new base::DictionaryValue(); 57 if (!dict) 58 return; 59 60 dict->SetInteger("rid", render_process_id); 61 dict->SetInteger("pid", static_cast<int>(pid)); 62 dict->SetInteger("lid", lid); 63 dict->SetString("servers", servers); 64 dict->SetString("constraints", constraints); 65 dict->SetString("url", url); 66 peer_connection_data_.Append(dict); 67 68 if (observers_.size() > 0) 69 SendUpdate("addPeerConnection", dict); 70 } 71 72 void WebRTCInternals::OnRemovePeerConnection(ProcessId pid, int lid) { 73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 74 for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) { 75 base::DictionaryValue* dict = NULL; 76 peer_connection_data_.GetDictionary(i, &dict); 77 78 int this_pid = 0; 79 int this_lid = 0; 80 dict->GetInteger("pid", &this_pid); 81 dict->GetInteger("lid", &this_lid); 82 83 if (this_pid != static_cast<int>(pid) || this_lid != lid) 84 continue; 85 86 peer_connection_data_.Remove(i, NULL); 87 88 if (observers_.size() > 0) { 89 base::DictionaryValue id; 90 id.SetInteger("pid", static_cast<int>(pid)); 91 id.SetInteger("lid", lid); 92 SendUpdate("removePeerConnection", &id); 93 } 94 break; 95 } 96 } 97 98 void WebRTCInternals::OnUpdatePeerConnection( 99 ProcessId pid, int lid, const string& type, const string& value) { 100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 101 102 for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) { 103 base::DictionaryValue* record = NULL; 104 peer_connection_data_.GetDictionary(i, &record); 105 106 int this_pid = 0, this_lid = 0; 107 record->GetInteger("pid", &this_pid); 108 record->GetInteger("lid", &this_lid); 109 110 if (this_pid != static_cast<int>(pid) || this_lid != lid) 111 continue; 112 113 // Append the update to the end of the log. 114 base::ListValue* log = EnsureLogList(record); 115 if (!log) 116 return; 117 118 base::DictionaryValue* log_entry = new base::DictionaryValue(); 119 if (!log_entry) 120 return; 121 122 log_entry->SetString("type", type); 123 log_entry->SetString("value", value); 124 log->Append(log_entry); 125 126 if (observers_.size() > 0) { 127 base::DictionaryValue update; 128 update.SetInteger("pid", static_cast<int>(pid)); 129 update.SetInteger("lid", lid); 130 update.SetString("type", type); 131 update.SetString("value", value); 132 133 SendUpdate("updatePeerConnection", &update); 134 } 135 return; 136 } 137 } 138 139 void WebRTCInternals::OnAddStats(base::ProcessId pid, int lid, 140 const base::ListValue& value) { 141 if (observers_.size() == 0) 142 return; 143 144 base::DictionaryValue dict; 145 dict.SetInteger("pid", static_cast<int>(pid)); 146 dict.SetInteger("lid", lid); 147 148 base::ListValue* list = value.DeepCopy(); 149 if (!list) 150 return; 151 152 dict.Set("reports", list); 153 154 SendUpdate("addStats", &dict); 155 } 156 157 void WebRTCInternals::AddObserver(WebRTCInternalsUIObserver *observer) { 158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 159 observers_.AddObserver(observer); 160 } 161 162 void WebRTCInternals::RemoveObserver(WebRTCInternalsUIObserver *observer) { 163 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 164 observers_.RemoveObserver(observer); 165 } 166 167 void WebRTCInternals::SendAllUpdates() { 168 if (observers_.size() > 0) 169 SendUpdate("updateAllPeerConnections", &peer_connection_data_); 170 } 171 172 void WebRTCInternals::StartRtpRecording() { 173 if (!is_recording_rtp_) { 174 is_recording_rtp_ = true; 175 // TODO(justinlin): start RTP recording. 176 } 177 } 178 179 void WebRTCInternals::StopRtpRecording() { 180 if (is_recording_rtp_) { 181 is_recording_rtp_ = false; 182 // TODO(justinlin): stop RTP recording. 183 } 184 } 185 186 void WebRTCInternals::SendUpdate(const string& command, base::Value* value) { 187 DCHECK_GT(observers_.size(), (size_t)0); 188 189 FOR_EACH_OBSERVER(WebRTCInternalsUIObserver, 190 observers_, 191 OnUpdate(command, value)); 192 } 193 194 void WebRTCInternals::BrowserChildProcessCrashed( 195 const ChildProcessData& data) { 196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 197 OnRendererExit(data.id); 198 } 199 200 void WebRTCInternals::Observe(int type, 201 const NotificationSource& source, 202 const NotificationDetails& details) { 203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 204 DCHECK_EQ(type, NOTIFICATION_RENDERER_PROCESS_TERMINATED); 205 OnRendererExit(Source<RenderProcessHost>(source)->GetID()); 206 } 207 208 void WebRTCInternals::OnRendererExit(int render_process_id) { 209 // Iterates from the end of the list to remove the PeerConnections created 210 // by the exitting renderer. 211 for (int i = peer_connection_data_.GetSize() - 1; i >= 0; --i) { 212 base::DictionaryValue* record = NULL; 213 peer_connection_data_.GetDictionary(i, &record); 214 215 int this_rid = 0; 216 record->GetInteger("rid", &this_rid); 217 218 if (this_rid == render_process_id) { 219 if (observers_.size() > 0) { 220 int lid = 0, pid = 0; 221 record->GetInteger("lid", &lid); 222 record->GetInteger("pid", &pid); 223 224 base::DictionaryValue update; 225 update.SetInteger("lid", lid); 226 update.SetInteger("pid", pid); 227 SendUpdate("removePeerConnection", &update); 228 } 229 peer_connection_data_.Remove(i, NULL); 230 } 231 } 232 } 233 234 // TODO(justlin): Calls this method as necessary to update the recording status 235 // UI. 236 void WebRTCInternals::SendRtpRecordingUpdate() { 237 DCHECK(is_recording_rtp_); 238 base::DictionaryValue update; 239 // TODO(justinlin): Fill in |update| with values as appropriate. 240 SendUpdate("updateDumpStatus", &update); 241 } 242 243 } // namespace content 244