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"
6 #include "base/strings/string_number_conversions.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "base/thread_task_runner_handle.h"
9 #include "content/common/media/peer_connection_tracker_messages.h"
10 #include "content/renderer/media/rtc_media_constraints.h"
11 #include "content/renderer/media/rtc_peer_connection_handler.h"
12 #include "content/renderer/render_thread_impl.h"
13 #include "third_party/WebKit/public/platform/WebMediaConstraints.h"
14 #include "third_party/WebKit/public/platform/WebMediaStream.h"
15 #include "third_party/WebKit/public/platform/WebMediaStreamSource.h"
16 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
17 #include "third_party/WebKit/public/platform/WebRTCICECandidate.h"
18 #include "third_party/WebKit/public/platform/WebRTCPeerConnectionHandlerClient.h"
19 #include "third_party/WebKit/public/web/WebDocument.h"
20 #include "third_party/WebKit/public/web/WebFrame.h"
21 #include "third_party/WebKit/public/web/WebUserMediaRequest.h"
24 using webrtc::MediaConstraintsInterface;
25 using blink::WebRTCPeerConnectionHandlerClient;
29 static string SerializeServers(
30 const std::vector<webrtc::PeerConnectionInterface::IceServer>& servers) {
32 for (size_t i = 0; i < servers.size(); ++i) {
33 result += servers[i].uri;
34 if (i != servers.size() - 1)
41 static RTCMediaConstraints GetNativeMediaConstraints(
42 const blink::WebMediaConstraints& constraints) {
43 RTCMediaConstraints native_constraints;
45 if (constraints.isNull())
46 return native_constraints;
48 blink::WebVector<blink::WebMediaConstraint> mandatory;
49 constraints.getMandatoryConstraints(mandatory);
50 for (size_t i = 0; i < mandatory.size(); ++i) {
51 native_constraints.AddMandatory(
52 mandatory[i].m_name.utf8(), mandatory[i].m_value.utf8(), false);
55 blink::WebVector<blink::WebMediaConstraint> optional;
56 constraints.getOptionalConstraints(optional);
57 for (size_t i = 0; i < optional.size(); ++i) {
58 native_constraints.AddOptional(
59 optional[i].m_name.utf8(), optional[i].m_value.utf8(), false);
61 return native_constraints;
64 static string SerializeMediaConstraints(
65 const RTCMediaConstraints& constraints) {
67 MediaConstraintsInterface::Constraints mandatory = constraints.GetMandatory();
68 if (!mandatory.empty()) {
69 result += "mandatory: {";
70 for (size_t i = 0; i < mandatory.size(); ++i) {
71 result += mandatory[i].key + ":" + mandatory[i].value;
72 if (i != mandatory.size() - 1)
77 MediaConstraintsInterface::Constraints optional = constraints.GetOptional();
78 if (!optional.empty()) {
81 result += "optional: {";
82 for (size_t i = 0; i < optional.size(); ++i) {
83 result += optional[i].key + ":" + optional[i].value;
84 if (i != optional.size() - 1)
92 static string SerializeMediaStreamComponent(
93 const blink::WebMediaStreamTrack component) {
94 string id = base::UTF16ToUTF8(component.source().id());
98 static string SerializeMediaDescriptor(
99 const blink::WebMediaStream& stream) {
100 string label = base::UTF16ToUTF8(stream.id());
101 string result = "label: " + label;
102 blink::WebVector<blink::WebMediaStreamTrack> tracks;
103 stream.audioTracks(tracks);
104 if (!tracks.isEmpty()) {
105 result += ", audio: [";
106 for (size_t i = 0; i < tracks.size(); ++i) {
107 result += SerializeMediaStreamComponent(tracks[i]);
108 if (i != tracks.size() - 1)
113 stream.videoTracks(tracks);
114 if (!tracks.isEmpty()) {
115 result += ", video: [";
116 for (size_t i = 0; i < tracks.size(); ++i) {
117 result += SerializeMediaStreamComponent(tracks[i]);
118 if (i != tracks.size() - 1)
126 static std::string SerializeIceTransportType(
127 webrtc::PeerConnectionInterface::IceTransportsType type) {
128 string transport_type;
130 case webrtc::PeerConnectionInterface::kNone:
131 transport_type = "none";
133 case webrtc::PeerConnectionInterface::kRelay:
134 transport_type = "relay";
136 case webrtc::PeerConnectionInterface::kAll:
137 transport_type = "all";
139 case webrtc::PeerConnectionInterface::kNoHost:
140 transport_type = "noHost";
145 return transport_type;
148 #define GET_STRING_OF_STATE(state) \
149 case WebRTCPeerConnectionHandlerClient::state: \
153 static string GetSignalingStateString(
154 WebRTCPeerConnectionHandlerClient::SignalingState state) {
157 GET_STRING_OF_STATE(SignalingStateStable)
158 GET_STRING_OF_STATE(SignalingStateHaveLocalOffer)
159 GET_STRING_OF_STATE(SignalingStateHaveRemoteOffer)
160 GET_STRING_OF_STATE(SignalingStateHaveLocalPrAnswer)
161 GET_STRING_OF_STATE(SignalingStateHaveRemotePrAnswer)
162 GET_STRING_OF_STATE(SignalingStateClosed)
170 static string GetIceConnectionStateString(
171 WebRTCPeerConnectionHandlerClient::ICEConnectionState state) {
174 GET_STRING_OF_STATE(ICEConnectionStateStarting)
175 GET_STRING_OF_STATE(ICEConnectionStateChecking)
176 GET_STRING_OF_STATE(ICEConnectionStateConnected)
177 GET_STRING_OF_STATE(ICEConnectionStateCompleted)
178 GET_STRING_OF_STATE(ICEConnectionStateFailed)
179 GET_STRING_OF_STATE(ICEConnectionStateDisconnected)
180 GET_STRING_OF_STATE(ICEConnectionStateClosed)
188 static string GetIceGatheringStateString(
189 WebRTCPeerConnectionHandlerClient::ICEGatheringState state) {
192 GET_STRING_OF_STATE(ICEGatheringStateNew)
193 GET_STRING_OF_STATE(ICEGatheringStateGathering)
194 GET_STRING_OF_STATE(ICEGatheringStateComplete)
202 // Builds a DictionaryValue from the StatsReport.
203 // The caller takes the ownership of the returned value.
205 // The format must be consistent with what webrtc_internals.js expects.
206 // If you change it here, you must change webrtc_internals.js as well.
207 static base::DictionaryValue* GetDictValueStats(
208 const webrtc::StatsReport& report) {
209 if (report.values.empty())
212 base::DictionaryValue* dict = new base::DictionaryValue();
213 dict->SetDouble("timestamp", report.timestamp);
215 base::ListValue* values = new base::ListValue();
216 dict->Set("values", values);
218 for (size_t i = 0; i < report.values.size(); ++i) {
219 values->AppendString(report.values[i].display_name());
220 values->AppendString(report.values[i].value);
225 // Builds a DictionaryValue from the StatsReport.
226 // The caller takes the ownership of the returned value.
227 static base::DictionaryValue* GetDictValue(const webrtc::StatsReport& report) {
228 scoped_ptr<base::DictionaryValue> stats, result;
230 stats.reset(GetDictValueStats(report));
234 result.reset(new base::DictionaryValue());
236 // The format must be consistent with what webrtc_internals.js expects.
237 // If you change it here, you must change webrtc_internals.js as well.
238 result->Set("stats", stats.release());
239 result->SetString("id", report.id);
240 result->SetString("type", report.type);
242 return result.release();
245 class InternalStatsObserver : public webrtc::StatsObserver {
247 InternalStatsObserver(int lid)
248 : lid_(lid), main_thread_(base::ThreadTaskRunnerHandle::Get()) {}
250 void OnComplete(const std::vector<webrtc::StatsReport>& reports) override {
251 scoped_ptr<base::ListValue> list(new base::ListValue());
253 for (size_t i = 0; i < reports.size(); ++i) {
254 base::DictionaryValue* report = GetDictValue(reports[i]);
256 list->Append(report);
259 if (!list->empty()) {
260 main_thread_->PostTask(FROM_HERE,
261 base::Bind(&InternalStatsObserver::OnCompleteImpl,
262 base::Passed(&list), lid_));
267 ~InternalStatsObserver() override {
268 // Will be destructed on libjingle's signaling thread.
269 // The signaling thread is where libjingle's objects live and from where
270 // libjingle makes callbacks. This may or may not be the same thread as
275 // Static since |this| will most likely have been deleted by the time we
277 static void OnCompleteImpl(scoped_ptr<base::ListValue> list, int lid) {
278 DCHECK(!list->empty());
279 RenderThreadImpl::current()->Send(
280 new PeerConnectionTrackerHost_AddStats(lid, *list.get()));
284 const scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
287 PeerConnectionTracker::PeerConnectionTracker() : next_lid_(1) {
290 PeerConnectionTracker::~PeerConnectionTracker() {
293 bool PeerConnectionTracker::OnControlMessageReceived(
294 const IPC::Message& message) {
296 IPC_BEGIN_MESSAGE_MAP(PeerConnectionTracker, message)
297 IPC_MESSAGE_HANDLER(PeerConnectionTracker_GetAllStats, OnGetAllStats)
298 IPC_MESSAGE_HANDLER(PeerConnectionTracker_OnSuspend, OnSuspend)
299 IPC_MESSAGE_UNHANDLED(handled = false)
300 IPC_END_MESSAGE_MAP()
304 void PeerConnectionTracker::OnGetAllStats() {
305 DCHECK(main_thread_.CalledOnValidThread());
307 const std::string empty_track_id;
308 for (PeerConnectionIdMap::iterator it = peer_connection_id_map_.begin();
309 it != peer_connection_id_map_.end(); ++it) {
310 rtc::scoped_refptr<InternalStatsObserver> observer(
311 new rtc::RefCountedObject<InternalStatsObserver>(it->second));
313 // The last type parameter is ignored when the track id is empty.
316 webrtc::PeerConnectionInterface::kStatsOutputLevelDebug,
317 empty_track_id, blink::WebMediaStreamSource::TypeAudio);
321 void PeerConnectionTracker::OnSuspend() {
322 DCHECK(main_thread_.CalledOnValidThread());
323 for (PeerConnectionIdMap::iterator it = peer_connection_id_map_.begin();
324 it != peer_connection_id_map_.end(); ++it) {
325 it->first->CloseClientPeerConnection();
329 void PeerConnectionTracker::RegisterPeerConnection(
330 RTCPeerConnectionHandler* pc_handler,
331 const webrtc::PeerConnectionInterface::RTCConfiguration& config,
332 const RTCMediaConstraints& constraints,
333 const blink::WebFrame* frame) {
334 DCHECK(main_thread_.CalledOnValidThread());
335 DVLOG(1) << "PeerConnectionTracker::RegisterPeerConnection()";
336 PeerConnectionInfo info;
338 info.lid = GetNextLocalID();
339 info.rtc_configuration =
340 "{ servers: " + SerializeServers(config.servers) + ", " +
341 "iceTransportType: " + SerializeIceTransportType(config.type) + " }";
343 info.constraints = SerializeMediaConstraints(constraints);
344 info.url = frame->document().url().spec();
345 RenderThreadImpl::current()->Send(
346 new PeerConnectionTrackerHost_AddPeerConnection(info));
348 DCHECK(peer_connection_id_map_.find(pc_handler) ==
349 peer_connection_id_map_.end());
350 peer_connection_id_map_[pc_handler] = info.lid;
353 void PeerConnectionTracker::UnregisterPeerConnection(
354 RTCPeerConnectionHandler* pc_handler) {
355 DCHECK(main_thread_.CalledOnValidThread());
356 DVLOG(1) << "PeerConnectionTracker::UnregisterPeerConnection()";
358 std::map<RTCPeerConnectionHandler*, int>::iterator it =
359 peer_connection_id_map_.find(pc_handler);
361 if (it == peer_connection_id_map_.end()) {
362 // The PeerConnection might not have been registered if its initilization
367 RenderThreadImpl::current()->Send(
368 new PeerConnectionTrackerHost_RemovePeerConnection(it->second));
370 peer_connection_id_map_.erase(it);
373 void PeerConnectionTracker::TrackCreateOffer(
374 RTCPeerConnectionHandler* pc_handler,
375 const RTCMediaConstraints& constraints) {
376 DCHECK(main_thread_.CalledOnValidThread());
377 SendPeerConnectionUpdate(
378 pc_handler, "createOffer",
379 "constraints: {" + SerializeMediaConstraints(constraints) + "}");
382 void PeerConnectionTracker::TrackCreateAnswer(
383 RTCPeerConnectionHandler* pc_handler,
384 const RTCMediaConstraints& constraints) {
385 DCHECK(main_thread_.CalledOnValidThread());
386 SendPeerConnectionUpdate(
387 pc_handler, "createAnswer",
388 "constraints: {" + SerializeMediaConstraints(constraints) + "}");
391 void PeerConnectionTracker::TrackSetSessionDescription(
392 RTCPeerConnectionHandler* pc_handler,
393 const std::string& sdp, const std::string& type, Source source) {
394 DCHECK(main_thread_.CalledOnValidThread());
395 string value = "type: " + type + ", sdp: " + sdp;
396 SendPeerConnectionUpdate(
398 source == SOURCE_LOCAL ? "setLocalDescription" : "setRemoteDescription",
402 void PeerConnectionTracker::TrackUpdateIce(
403 RTCPeerConnectionHandler* pc_handler,
404 const webrtc::PeerConnectionInterface::RTCConfiguration& config,
405 const RTCMediaConstraints& options) {
406 DCHECK(main_thread_.CalledOnValidThread());
407 string servers_string = "servers: " + SerializeServers(config.servers);
409 string transport_type =
410 "iceTransportType: " + SerializeIceTransportType(config.type);
413 "constraints: {" + SerializeMediaConstraints(options) + "}";
415 SendPeerConnectionUpdate(
418 servers_string + ", " + transport_type + ", " + constraints);
421 void PeerConnectionTracker::TrackAddIceCandidate(
422 RTCPeerConnectionHandler* pc_handler,
423 const blink::WebRTCICECandidate& candidate,
426 DCHECK(main_thread_.CalledOnValidThread());
428 "sdpMid: " + base::UTF16ToUTF8(candidate.sdpMid()) + ", " +
429 "sdpMLineIndex: " + base::IntToString(candidate.sdpMLineIndex()) + ", " +
430 "candidate: " + base::UTF16ToUTF8(candidate.candidate());
432 // OnIceCandidate always succeeds as it's a callback from the browser.
433 DCHECK(source != SOURCE_LOCAL || succeeded);
436 (source == SOURCE_LOCAL) ? "onIceCandidate"
437 : (succeeded ? "addIceCandidate"
438 : "addIceCandidateFailed");
440 SendPeerConnectionUpdate(pc_handler, event, value);
443 void PeerConnectionTracker::TrackAddStream(
444 RTCPeerConnectionHandler* pc_handler,
445 const blink::WebMediaStream& stream,
447 DCHECK(main_thread_.CalledOnValidThread());
448 SendPeerConnectionUpdate(
449 pc_handler, source == SOURCE_LOCAL ? "addStream" : "onAddStream",
450 SerializeMediaDescriptor(stream));
453 void PeerConnectionTracker::TrackRemoveStream(
454 RTCPeerConnectionHandler* pc_handler,
455 const blink::WebMediaStream& stream,
457 DCHECK(main_thread_.CalledOnValidThread());
458 SendPeerConnectionUpdate(
459 pc_handler, source == SOURCE_LOCAL ? "removeStream" : "onRemoveStream",
460 SerializeMediaDescriptor(stream));
463 void PeerConnectionTracker::TrackCreateDataChannel(
464 RTCPeerConnectionHandler* pc_handler,
465 const webrtc::DataChannelInterface* data_channel,
467 DCHECK(main_thread_.CalledOnValidThread());
468 string value = "label: " + data_channel->label() +
469 ", reliable: " + (data_channel->reliable() ? "true" : "false");
470 SendPeerConnectionUpdate(
472 source == SOURCE_LOCAL ? "createLocalDataChannel" : "onRemoteDataChannel",
476 void PeerConnectionTracker::TrackStop(RTCPeerConnectionHandler* pc_handler) {
477 DCHECK(main_thread_.CalledOnValidThread());
478 SendPeerConnectionUpdate(pc_handler, "stop", std::string());
481 void PeerConnectionTracker::TrackSignalingStateChange(
482 RTCPeerConnectionHandler* pc_handler,
483 WebRTCPeerConnectionHandlerClient::SignalingState state) {
484 DCHECK(main_thread_.CalledOnValidThread());
485 SendPeerConnectionUpdate(
486 pc_handler, "signalingStateChange", GetSignalingStateString(state));
489 void PeerConnectionTracker::TrackIceConnectionStateChange(
490 RTCPeerConnectionHandler* pc_handler,
491 WebRTCPeerConnectionHandlerClient::ICEConnectionState state) {
492 DCHECK(main_thread_.CalledOnValidThread());
493 SendPeerConnectionUpdate(
494 pc_handler, "iceConnectionStateChange",
495 GetIceConnectionStateString(state));
498 void PeerConnectionTracker::TrackIceGatheringStateChange(
499 RTCPeerConnectionHandler* pc_handler,
500 WebRTCPeerConnectionHandlerClient::ICEGatheringState state) {
501 DCHECK(main_thread_.CalledOnValidThread());
502 SendPeerConnectionUpdate(
503 pc_handler, "iceGatheringStateChange",
504 GetIceGatheringStateString(state));
507 void PeerConnectionTracker::TrackSessionDescriptionCallback(
508 RTCPeerConnectionHandler* pc_handler, Action action,
509 const string& callback_type, const string& value) {
510 DCHECK(main_thread_.CalledOnValidThread());
513 case ACTION_SET_LOCAL_DESCRIPTION:
514 update_type = "setLocalDescription";
516 case ACTION_SET_REMOTE_DESCRIPTION:
517 update_type = "setRemoteDescription";
519 case ACTION_CREATE_OFFER:
520 update_type = "createOffer";
522 case ACTION_CREATE_ANSWER:
523 update_type = "createAnswer";
529 update_type += callback_type;
531 SendPeerConnectionUpdate(pc_handler, update_type, value);
534 void PeerConnectionTracker::TrackOnRenegotiationNeeded(
535 RTCPeerConnectionHandler* pc_handler) {
536 DCHECK(main_thread_.CalledOnValidThread());
537 SendPeerConnectionUpdate(pc_handler, "onRenegotiationNeeded", std::string());
540 void PeerConnectionTracker::TrackCreateDTMFSender(
541 RTCPeerConnectionHandler* pc_handler,
542 const blink::WebMediaStreamTrack& track) {
543 DCHECK(main_thread_.CalledOnValidThread());
544 SendPeerConnectionUpdate(pc_handler, "createDTMFSender",
545 base::UTF16ToUTF8(track.id()));
548 void PeerConnectionTracker::TrackGetUserMedia(
549 const blink::WebUserMediaRequest& user_media_request) {
550 DCHECK(main_thread_.CalledOnValidThread());
551 RTCMediaConstraints audio_constraints(
552 GetNativeMediaConstraints(user_media_request.audioConstraints()));
553 RTCMediaConstraints video_constraints(
554 GetNativeMediaConstraints(user_media_request.videoConstraints()));
556 RenderThreadImpl::current()->Send(new PeerConnectionTrackerHost_GetUserMedia(
557 user_media_request.securityOrigin().toString().utf8(),
558 user_media_request.audio(),
559 user_media_request.video(),
560 SerializeMediaConstraints(audio_constraints),
561 SerializeMediaConstraints(video_constraints)));
564 int PeerConnectionTracker::GetNextLocalID() {
565 DCHECK(main_thread_.CalledOnValidThread());
569 void PeerConnectionTracker::SendPeerConnectionUpdate(
570 RTCPeerConnectionHandler* pc_handler,
571 const std::string& type,
572 const std::string& value) {
573 DCHECK(main_thread_.CalledOnValidThread());
574 if (peer_connection_id_map_.find(pc_handler) == peer_connection_id_map_.end())
577 RenderThreadImpl::current()->Send(
578 new PeerConnectionTrackerHost_UpdatePeerConnection(
579 peer_connection_id_map_[pc_handler], type, value));
582 } // namespace content