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 #include "content/renderer/media/peer_connection_tracker.h" 5 6 #include "base/strings/utf_string_conversions.h" 7 #include "content/common/media/peer_connection_tracker_messages.h" 8 #include "content/renderer/media/rtc_media_constraints.h" 9 #include "content/renderer/media/rtc_peer_connection_handler.h" 10 #include "content/renderer/render_thread_impl.h" 11 #include "third_party/WebKit/public/platform/WebMediaConstraints.h" 12 #include "third_party/WebKit/public/platform/WebMediaStream.h" 13 #include "third_party/WebKit/public/platform/WebMediaStreamSource.h" 14 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" 15 #include "third_party/WebKit/public/platform/WebRTCICECandidate.h" 16 #include "third_party/WebKit/public/platform/WebRTCPeerConnectionHandlerClient.h" 17 #include "third_party/WebKit/public/web/WebDocument.h" 18 #include "third_party/WebKit/public/web/WebFrame.h" 19 #include "third_party/WebKit/public/web/WebUserMediaRequest.h" 20 21 using std::string; 22 using webrtc::MediaConstraintsInterface; 23 using blink::WebRTCPeerConnectionHandlerClient; 24 25 namespace content { 26 27 static string SerializeServers( 28 const std::vector<webrtc::PeerConnectionInterface::IceServer>& servers) { 29 string result = "["; 30 for (size_t i = 0; i < servers.size(); ++i) { 31 result += servers[i].uri; 32 if (i != servers.size() - 1) 33 result += ", "; 34 } 35 result += "]"; 36 return result; 37 } 38 39 static string SerializeMediaConstraints( 40 const RTCMediaConstraints& constraints) { 41 string result; 42 MediaConstraintsInterface::Constraints mandatory = constraints.GetMandatory(); 43 if (!mandatory.empty()) { 44 result += "mandatory: {"; 45 for (size_t i = 0; i < mandatory.size(); ++i) { 46 result += mandatory[i].key + ":" + mandatory[i].value; 47 if (i != mandatory.size() - 1) 48 result += ", "; 49 } 50 result += "}"; 51 } 52 MediaConstraintsInterface::Constraints optional = constraints.GetOptional(); 53 if (!optional.empty()) { 54 if (!result.empty()) 55 result += ", "; 56 result += "optional: {"; 57 for (size_t i = 0; i < optional.size(); ++i) { 58 result += optional[i].key + ":" + optional[i].value; 59 if (i != optional.size() - 1) 60 result += ", "; 61 } 62 result += "}"; 63 } 64 return result; 65 } 66 67 static string SerializeMediaStreamComponent( 68 const blink::WebMediaStreamTrack component) { 69 string id = base::UTF16ToUTF8(component.source().id()); 70 return id; 71 } 72 73 static string SerializeMediaDescriptor( 74 const blink::WebMediaStream& stream) { 75 string label = base::UTF16ToUTF8(stream.id()); 76 string result = "label: " + label; 77 blink::WebVector<blink::WebMediaStreamTrack> tracks; 78 stream.audioTracks(tracks); 79 if (!tracks.isEmpty()) { 80 result += ", audio: ["; 81 for (size_t i = 0; i < tracks.size(); ++i) { 82 result += SerializeMediaStreamComponent(tracks[i]); 83 if (i != tracks.size() - 1) 84 result += ", "; 85 } 86 result += "]"; 87 } 88 stream.videoTracks(tracks); 89 if (!tracks.isEmpty()) { 90 result += ", video: ["; 91 for (size_t i = 0; i < tracks.size(); ++i) { 92 result += SerializeMediaStreamComponent(tracks[i]); 93 if (i != tracks.size() - 1) 94 result += ", "; 95 } 96 result += "]"; 97 } 98 return result; 99 } 100 101 #define GET_STRING_OF_STATE(state) \ 102 case WebRTCPeerConnectionHandlerClient::state: \ 103 result = #state; \ 104 break; 105 106 static string GetSignalingStateString( 107 WebRTCPeerConnectionHandlerClient::SignalingState state) { 108 string result; 109 switch (state) { 110 GET_STRING_OF_STATE(SignalingStateStable) 111 GET_STRING_OF_STATE(SignalingStateHaveLocalOffer) 112 GET_STRING_OF_STATE(SignalingStateHaveRemoteOffer) 113 GET_STRING_OF_STATE(SignalingStateHaveLocalPrAnswer) 114 GET_STRING_OF_STATE(SignalingStateHaveRemotePrAnswer) 115 GET_STRING_OF_STATE(SignalingStateClosed) 116 default: 117 NOTREACHED(); 118 break; 119 } 120 return result; 121 } 122 123 static string GetIceConnectionStateString( 124 WebRTCPeerConnectionHandlerClient::ICEConnectionState state) { 125 string result; 126 switch (state) { 127 GET_STRING_OF_STATE(ICEConnectionStateStarting) 128 GET_STRING_OF_STATE(ICEConnectionStateChecking) 129 GET_STRING_OF_STATE(ICEConnectionStateConnected) 130 GET_STRING_OF_STATE(ICEConnectionStateCompleted) 131 GET_STRING_OF_STATE(ICEConnectionStateFailed) 132 GET_STRING_OF_STATE(ICEConnectionStateDisconnected) 133 GET_STRING_OF_STATE(ICEConnectionStateClosed) 134 default: 135 NOTREACHED(); 136 break; 137 } 138 return result; 139 } 140 141 static string GetIceGatheringStateString( 142 WebRTCPeerConnectionHandlerClient::ICEGatheringState state) { 143 string result; 144 switch (state) { 145 GET_STRING_OF_STATE(ICEGatheringStateNew) 146 GET_STRING_OF_STATE(ICEGatheringStateGathering) 147 GET_STRING_OF_STATE(ICEGatheringStateComplete) 148 default: 149 NOTREACHED(); 150 break; 151 } 152 return result; 153 } 154 155 // Builds a DictionaryValue from the StatsReport. 156 // The caller takes the ownership of the returned value. 157 // Note: 158 // The format must be consistent with what webrtc_internals.js expects. 159 // If you change it here, you must change webrtc_internals.js as well. 160 static base::DictionaryValue* GetDictValueStats( 161 const webrtc::StatsReport& report) { 162 if (report.values.empty()) 163 return NULL; 164 165 base::DictionaryValue* dict = new base::DictionaryValue(); 166 dict->SetDouble("timestamp", report.timestamp); 167 168 base::ListValue* values = new base::ListValue(); 169 dict->Set("values", values); 170 171 for (size_t i = 0; i < report.values.size(); ++i) { 172 values->AppendString(report.values[i].name); 173 values->AppendString(report.values[i].value); 174 } 175 return dict; 176 } 177 178 // Builds a DictionaryValue from the StatsReport. 179 // The caller takes the ownership of the returned value. 180 static base::DictionaryValue* GetDictValue(const webrtc::StatsReport& report) { 181 scoped_ptr<base::DictionaryValue> stats, result; 182 183 stats.reset(GetDictValueStats(report)); 184 if (!stats) 185 return NULL; 186 187 result.reset(new base::DictionaryValue()); 188 // Note: 189 // The format must be consistent with what webrtc_internals.js expects. 190 // If you change it here, you must change webrtc_internals.js as well. 191 result->Set("stats", stats.release()); 192 result->SetString("id", report.id); 193 result->SetString("type", report.type); 194 195 return result.release(); 196 } 197 198 class InternalStatsObserver : public webrtc::StatsObserver { 199 public: 200 InternalStatsObserver(int lid) 201 : lid_(lid){} 202 203 virtual void OnComplete( 204 const std::vector<webrtc::StatsReport>& reports) OVERRIDE { 205 base::ListValue list; 206 207 for (size_t i = 0; i < reports.size(); ++i) { 208 base::DictionaryValue* report = GetDictValue(reports[i]); 209 if (report) 210 list.Append(report); 211 } 212 213 if (!list.empty()) 214 RenderThreadImpl::current()->Send( 215 new PeerConnectionTrackerHost_AddStats(lid_, list)); 216 } 217 218 protected: 219 virtual ~InternalStatsObserver() {} 220 221 private: 222 int lid_; 223 }; 224 225 PeerConnectionTracker::PeerConnectionTracker() : next_lid_(1) { 226 } 227 228 PeerConnectionTracker::~PeerConnectionTracker() { 229 } 230 231 bool PeerConnectionTracker::OnControlMessageReceived( 232 const IPC::Message& message) { 233 bool handled = true; 234 IPC_BEGIN_MESSAGE_MAP(PeerConnectionTracker, message) 235 IPC_MESSAGE_HANDLER(PeerConnectionTracker_GetAllStats, OnGetAllStats) 236 IPC_MESSAGE_UNHANDLED(handled = false) 237 IPC_END_MESSAGE_MAP() 238 return handled; 239 } 240 241 void PeerConnectionTracker::OnGetAllStats() { 242 for (PeerConnectionIdMap::iterator it = peer_connection_id_map_.begin(); 243 it != peer_connection_id_map_.end(); ++it) { 244 245 talk_base::scoped_refptr<InternalStatsObserver> observer( 246 new talk_base::RefCountedObject<InternalStatsObserver>(it->second)); 247 248 it->first->GetStats( 249 observer, 250 NULL, 251 webrtc::PeerConnectionInterface::kStatsOutputLevelDebug); 252 } 253 } 254 255 void PeerConnectionTracker::RegisterPeerConnection( 256 RTCPeerConnectionHandler* pc_handler, 257 const std::vector<webrtc::PeerConnectionInterface::IceServer>& servers, 258 const RTCMediaConstraints& constraints, 259 const blink::WebFrame* frame) { 260 DVLOG(1) << "PeerConnectionTracker::RegisterPeerConnection()"; 261 PeerConnectionInfo info; 262 263 info.lid = GetNextLocalID(); 264 info.servers = SerializeServers(servers); 265 info.constraints = SerializeMediaConstraints(constraints); 266 info.url = frame->document().url().spec(); 267 RenderThreadImpl::current()->Send( 268 new PeerConnectionTrackerHost_AddPeerConnection(info)); 269 270 DCHECK(peer_connection_id_map_.find(pc_handler) == 271 peer_connection_id_map_.end()); 272 peer_connection_id_map_[pc_handler] = info.lid; 273 } 274 275 void PeerConnectionTracker::UnregisterPeerConnection( 276 RTCPeerConnectionHandler* pc_handler) { 277 DVLOG(1) << "PeerConnectionTracker::UnregisterPeerConnection()"; 278 279 std::map<RTCPeerConnectionHandler*, int>::iterator it = 280 peer_connection_id_map_.find(pc_handler); 281 282 if (it == peer_connection_id_map_.end()) { 283 // The PeerConnection might not have been registered if its initilization 284 // failed. 285 return; 286 } 287 288 RenderThreadImpl::current()->Send( 289 new PeerConnectionTrackerHost_RemovePeerConnection(it->second)); 290 291 peer_connection_id_map_.erase(it); 292 } 293 294 void PeerConnectionTracker::TrackCreateOffer( 295 RTCPeerConnectionHandler* pc_handler, 296 const RTCMediaConstraints& constraints) { 297 SendPeerConnectionUpdate( 298 pc_handler, "createOffer", 299 "constraints: {" + SerializeMediaConstraints(constraints) + "}"); 300 } 301 302 void PeerConnectionTracker::TrackCreateAnswer( 303 RTCPeerConnectionHandler* pc_handler, 304 const RTCMediaConstraints& constraints) { 305 SendPeerConnectionUpdate( 306 pc_handler, "createAnswer", 307 "constraints: {" + SerializeMediaConstraints(constraints) + "}"); 308 } 309 310 void PeerConnectionTracker::TrackSetSessionDescription( 311 RTCPeerConnectionHandler* pc_handler, 312 const blink::WebRTCSessionDescription& desc, 313 Source source) { 314 string sdp = base::UTF16ToUTF8(desc.sdp()); 315 string type = base::UTF16ToUTF8(desc.type()); 316 317 string value = "type: " + type + ", sdp: " + sdp; 318 SendPeerConnectionUpdate( 319 pc_handler, 320 source == SOURCE_LOCAL ? "setLocalDescription" : "setRemoteDescription", 321 value); 322 } 323 324 void PeerConnectionTracker::TrackUpdateIce( 325 RTCPeerConnectionHandler* pc_handler, 326 const std::vector<webrtc::PeerConnectionInterface::IceServer>& servers, 327 const RTCMediaConstraints& options) { 328 string servers_string = "servers: " + SerializeServers(servers); 329 string constraints = 330 "constraints: {" + SerializeMediaConstraints(options) + "}"; 331 332 SendPeerConnectionUpdate( 333 pc_handler, "updateIce", servers_string + ", " + constraints); 334 } 335 336 void PeerConnectionTracker::TrackAddIceCandidate( 337 RTCPeerConnectionHandler* pc_handler, 338 const blink::WebRTCICECandidate& candidate, 339 Source source) { 340 string value = "mid: " + base::UTF16ToUTF8(candidate.sdpMid()) + ", " + 341 "candidate: " + base::UTF16ToUTF8(candidate.candidate()); 342 SendPeerConnectionUpdate( 343 pc_handler, 344 source == SOURCE_LOCAL ? "onIceCandidate" : "addIceCandidate", value); 345 } 346 347 void PeerConnectionTracker::TrackAddStream( 348 RTCPeerConnectionHandler* pc_handler, 349 const blink::WebMediaStream& stream, 350 Source source){ 351 SendPeerConnectionUpdate( 352 pc_handler, source == SOURCE_LOCAL ? "addStream" : "onAddStream", 353 SerializeMediaDescriptor(stream)); 354 } 355 356 void PeerConnectionTracker::TrackRemoveStream( 357 RTCPeerConnectionHandler* pc_handler, 358 const blink::WebMediaStream& stream, 359 Source source){ 360 SendPeerConnectionUpdate( 361 pc_handler, source == SOURCE_LOCAL ? "removeStream" : "onRemoveStream", 362 SerializeMediaDescriptor(stream)); 363 } 364 365 void PeerConnectionTracker::TrackCreateDataChannel( 366 RTCPeerConnectionHandler* pc_handler, 367 const webrtc::DataChannelInterface* data_channel, 368 Source source) { 369 string value = "label: " + data_channel->label() + 370 ", reliable: " + (data_channel->reliable() ? "true" : "false"); 371 SendPeerConnectionUpdate( 372 pc_handler, 373 source == SOURCE_LOCAL ? "createLocalDataChannel" : "onRemoteDataChannel", 374 value); 375 } 376 377 void PeerConnectionTracker::TrackStop(RTCPeerConnectionHandler* pc_handler) { 378 SendPeerConnectionUpdate(pc_handler, "stop", std::string()); 379 } 380 381 void PeerConnectionTracker::TrackSignalingStateChange( 382 RTCPeerConnectionHandler* pc_handler, 383 WebRTCPeerConnectionHandlerClient::SignalingState state) { 384 SendPeerConnectionUpdate( 385 pc_handler, "signalingStateChange", GetSignalingStateString(state)); 386 } 387 388 void PeerConnectionTracker::TrackIceConnectionStateChange( 389 RTCPeerConnectionHandler* pc_handler, 390 WebRTCPeerConnectionHandlerClient::ICEConnectionState state) { 391 SendPeerConnectionUpdate( 392 pc_handler, "iceConnectionStateChange", 393 GetIceConnectionStateString(state)); 394 } 395 396 void PeerConnectionTracker::TrackIceGatheringStateChange( 397 RTCPeerConnectionHandler* pc_handler, 398 WebRTCPeerConnectionHandlerClient::ICEGatheringState state) { 399 SendPeerConnectionUpdate( 400 pc_handler, "iceGatheringStateChange", 401 GetIceGatheringStateString(state)); 402 } 403 404 void PeerConnectionTracker::TrackSessionDescriptionCallback( 405 RTCPeerConnectionHandler* pc_handler, Action action, 406 const string& callback_type, const string& value) { 407 string update_type; 408 switch (action) { 409 case ACTION_SET_LOCAL_DESCRIPTION: 410 update_type = "setLocalDescription"; 411 break; 412 case ACTION_SET_REMOTE_DESCRIPTION: 413 update_type = "setRemoteDescription"; 414 break; 415 case ACTION_CREATE_OFFER: 416 update_type = "createOffer"; 417 break; 418 case ACTION_CREATE_ANSWER: 419 update_type = "createAnswer"; 420 break; 421 default: 422 NOTREACHED(); 423 break; 424 } 425 update_type += callback_type; 426 427 SendPeerConnectionUpdate(pc_handler, update_type, value); 428 } 429 430 void PeerConnectionTracker::TrackOnRenegotiationNeeded( 431 RTCPeerConnectionHandler* pc_handler) { 432 SendPeerConnectionUpdate(pc_handler, "onRenegotiationNeeded", std::string()); 433 } 434 435 void PeerConnectionTracker::TrackCreateDTMFSender( 436 RTCPeerConnectionHandler* pc_handler, 437 const blink::WebMediaStreamTrack& track) { 438 SendPeerConnectionUpdate(pc_handler, "createDTMFSender", 439 base::UTF16ToUTF8(track.id())); 440 } 441 442 void PeerConnectionTracker::TrackGetUserMedia( 443 const blink::WebUserMediaRequest& user_media_request) { 444 RTCMediaConstraints audio_constraints(user_media_request.audioConstraints()); 445 RTCMediaConstraints video_constraints(user_media_request.videoConstraints()); 446 447 RenderThreadImpl::current()->Send(new PeerConnectionTrackerHost_GetUserMedia( 448 user_media_request.securityOrigin().toString().utf8(), 449 user_media_request.audio(), 450 user_media_request.video(), 451 SerializeMediaConstraints(audio_constraints), 452 SerializeMediaConstraints(video_constraints))); 453 } 454 455 int PeerConnectionTracker::GetNextLocalID() { 456 return next_lid_++; 457 } 458 459 void PeerConnectionTracker::SendPeerConnectionUpdate( 460 RTCPeerConnectionHandler* pc_handler, 461 const std::string& type, 462 const std::string& value) { 463 if (peer_connection_id_map_.find(pc_handler) == peer_connection_id_map_.end()) 464 return; 465 466 RenderThreadImpl::current()->Send( 467 new PeerConnectionTrackerHost_UpdatePeerConnection( 468 peer_connection_id_map_[pc_handler], type, value)); 469 } 470 471 } // namespace content 472