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 "content/common/media/peer_connection_tracker_messages.h"
9 #include "content/renderer/media/rtc_media_constraints.h"
10 #include "content/renderer/media/rtc_peer_connection_handler.h"
11 #include "content/renderer/render_thread_impl.h"
12 #include "third_party/WebKit/public/platform/WebMediaConstraints.h"
13 #include "third_party/WebKit/public/platform/WebMediaStream.h"
14 #include "third_party/WebKit/public/platform/WebMediaStreamSource.h"
15 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
16 #include "third_party/WebKit/public/platform/WebRTCICECandidate.h"
17 #include "third_party/WebKit/public/platform/WebRTCPeerConnectionHandlerClient.h"
18 #include "third_party/WebKit/public/web/WebDocument.h"
19 #include "third_party/WebKit/public/web/WebFrame.h"
20 #include "third_party/WebKit/public/web/WebUserMediaRequest.h"
23 using webrtc::MediaConstraintsInterface
;
24 using blink::WebRTCPeerConnectionHandlerClient
;
28 static string
SerializeServers(
29 const std::vector
<webrtc::PeerConnectionInterface::IceServer
>& servers
) {
31 for (size_t i
= 0; i
< servers
.size(); ++i
) {
32 result
+= servers
[i
].uri
;
33 if (i
!= servers
.size() - 1)
40 static RTCMediaConstraints
GetNativeMediaConstraints(
41 const blink::WebMediaConstraints
& constraints
) {
42 RTCMediaConstraints native_constraints
;
44 if (constraints
.isNull())
45 return native_constraints
;
47 blink::WebVector
<blink::WebMediaConstraint
> mandatory
;
48 constraints
.getMandatoryConstraints(mandatory
);
49 for (size_t i
= 0; i
< mandatory
.size(); ++i
) {
50 native_constraints
.AddMandatory(
51 mandatory
[i
].m_name
.utf8(), mandatory
[i
].m_value
.utf8(), false);
54 blink::WebVector
<blink::WebMediaConstraint
> optional
;
55 constraints
.getOptionalConstraints(optional
);
56 for (size_t i
= 0; i
< optional
.size(); ++i
) {
57 native_constraints
.AddOptional(
58 optional
[i
].m_name
.utf8(), optional
[i
].m_value
.utf8(), false);
60 return native_constraints
;
63 static string
SerializeMediaConstraints(
64 const RTCMediaConstraints
& constraints
) {
66 MediaConstraintsInterface::Constraints mandatory
= constraints
.GetMandatory();
67 if (!mandatory
.empty()) {
68 result
+= "mandatory: {";
69 for (size_t i
= 0; i
< mandatory
.size(); ++i
) {
70 result
+= mandatory
[i
].key
+ ":" + mandatory
[i
].value
;
71 if (i
!= mandatory
.size() - 1)
76 MediaConstraintsInterface::Constraints optional
= constraints
.GetOptional();
77 if (!optional
.empty()) {
80 result
+= "optional: {";
81 for (size_t i
= 0; i
< optional
.size(); ++i
) {
82 result
+= optional
[i
].key
+ ":" + optional
[i
].value
;
83 if (i
!= optional
.size() - 1)
91 static string
SerializeMediaStreamComponent(
92 const blink::WebMediaStreamTrack component
) {
93 string id
= base::UTF16ToUTF8(component
.source().id());
97 static string
SerializeMediaDescriptor(
98 const blink::WebMediaStream
& stream
) {
99 string label
= base::UTF16ToUTF8(stream
.id());
100 string result
= "label: " + label
;
101 blink::WebVector
<blink::WebMediaStreamTrack
> tracks
;
102 stream
.audioTracks(tracks
);
103 if (!tracks
.isEmpty()) {
104 result
+= ", audio: [";
105 for (size_t i
= 0; i
< tracks
.size(); ++i
) {
106 result
+= SerializeMediaStreamComponent(tracks
[i
]);
107 if (i
!= tracks
.size() - 1)
112 stream
.videoTracks(tracks
);
113 if (!tracks
.isEmpty()) {
114 result
+= ", video: [";
115 for (size_t i
= 0; i
< tracks
.size(); ++i
) {
116 result
+= SerializeMediaStreamComponent(tracks
[i
]);
117 if (i
!= tracks
.size() - 1)
125 static std::string
SerializeIceTransportType(
126 webrtc::PeerConnectionInterface::IceTransportsType type
) {
127 string transport_type
;
129 case webrtc::PeerConnectionInterface::kNone
:
130 transport_type
= "none";
132 case webrtc::PeerConnectionInterface::kRelay
:
133 transport_type
= "relay";
135 case webrtc::PeerConnectionInterface::kAll
:
136 transport_type
= "all";
138 case webrtc::PeerConnectionInterface::kNoHost
:
139 transport_type
= "noHost";
144 return transport_type
;
147 #define GET_STRING_OF_STATE(state) \
148 case WebRTCPeerConnectionHandlerClient::state: \
152 static string
GetSignalingStateString(
153 WebRTCPeerConnectionHandlerClient::SignalingState state
) {
156 GET_STRING_OF_STATE(SignalingStateStable
)
157 GET_STRING_OF_STATE(SignalingStateHaveLocalOffer
)
158 GET_STRING_OF_STATE(SignalingStateHaveRemoteOffer
)
159 GET_STRING_OF_STATE(SignalingStateHaveLocalPrAnswer
)
160 GET_STRING_OF_STATE(SignalingStateHaveRemotePrAnswer
)
161 GET_STRING_OF_STATE(SignalingStateClosed
)
169 static string
GetIceConnectionStateString(
170 WebRTCPeerConnectionHandlerClient::ICEConnectionState state
) {
173 GET_STRING_OF_STATE(ICEConnectionStateStarting
)
174 GET_STRING_OF_STATE(ICEConnectionStateChecking
)
175 GET_STRING_OF_STATE(ICEConnectionStateConnected
)
176 GET_STRING_OF_STATE(ICEConnectionStateCompleted
)
177 GET_STRING_OF_STATE(ICEConnectionStateFailed
)
178 GET_STRING_OF_STATE(ICEConnectionStateDisconnected
)
179 GET_STRING_OF_STATE(ICEConnectionStateClosed
)
187 static string
GetIceGatheringStateString(
188 WebRTCPeerConnectionHandlerClient::ICEGatheringState state
) {
191 GET_STRING_OF_STATE(ICEGatheringStateNew
)
192 GET_STRING_OF_STATE(ICEGatheringStateGathering
)
193 GET_STRING_OF_STATE(ICEGatheringStateComplete
)
201 // Builds a DictionaryValue from the StatsReport.
202 // The caller takes the ownership of the returned value.
204 // The format must be consistent with what webrtc_internals.js expects.
205 // If you change it here, you must change webrtc_internals.js as well.
206 static base::DictionaryValue
* GetDictValueStats(
207 const webrtc::StatsReport
& report
) {
208 if (report
.values
.empty())
211 base::DictionaryValue
* dict
= new base::DictionaryValue();
212 dict
->SetDouble("timestamp", report
.timestamp
);
214 base::ListValue
* values
= new base::ListValue();
215 dict
->Set("values", values
);
217 for (size_t i
= 0; i
< report
.values
.size(); ++i
) {
218 values
->AppendString(report
.values
[i
].display_name());
219 values
->AppendString(report
.values
[i
].value
);
224 // Builds a DictionaryValue from the StatsReport.
225 // The caller takes the ownership of the returned value.
226 static base::DictionaryValue
* GetDictValue(const webrtc::StatsReport
& report
) {
227 scoped_ptr
<base::DictionaryValue
> stats
, result
;
229 stats
.reset(GetDictValueStats(report
));
233 result
.reset(new base::DictionaryValue());
235 // The format must be consistent with what webrtc_internals.js expects.
236 // If you change it here, you must change webrtc_internals.js as well.
237 result
->Set("stats", stats
.release());
238 result
->SetString("id", report
.id
);
239 result
->SetString("type", report
.type
);
241 return result
.release();
244 class InternalStatsObserver
: public webrtc::StatsObserver
{
246 InternalStatsObserver(int lid
)
249 virtual void OnComplete(
250 const std::vector
<webrtc::StatsReport
>& reports
) override
{
251 base::ListValue list
;
253 for (size_t i
= 0; i
< reports
.size(); ++i
) {
254 base::DictionaryValue
* report
= GetDictValue(reports
[i
]);
260 RenderThreadImpl::current()->Send(
261 new PeerConnectionTrackerHost_AddStats(lid_
, list
));
265 virtual ~InternalStatsObserver() {}
271 PeerConnectionTracker::PeerConnectionTracker() : next_lid_(1) {
274 PeerConnectionTracker::~PeerConnectionTracker() {
277 bool PeerConnectionTracker::OnControlMessageReceived(
278 const IPC::Message
& message
) {
280 IPC_BEGIN_MESSAGE_MAP(PeerConnectionTracker
, message
)
281 IPC_MESSAGE_HANDLER(PeerConnectionTracker_GetAllStats
, OnGetAllStats
)
282 IPC_MESSAGE_HANDLER(PeerConnectionTracker_OnSuspend
, OnSuspend
)
283 IPC_MESSAGE_UNHANDLED(handled
= false)
284 IPC_END_MESSAGE_MAP()
288 void PeerConnectionTracker::OnGetAllStats() {
289 for (PeerConnectionIdMap::iterator it
= peer_connection_id_map_
.begin();
290 it
!= peer_connection_id_map_
.end(); ++it
) {
292 rtc::scoped_refptr
<InternalStatsObserver
> observer(
293 new rtc::RefCountedObject
<InternalStatsObserver
>(it
->second
));
298 webrtc::PeerConnectionInterface::kStatsOutputLevelDebug
);
302 void PeerConnectionTracker::OnSuspend() {
303 for (PeerConnectionIdMap::iterator it
= peer_connection_id_map_
.begin();
304 it
!= peer_connection_id_map_
.end(); ++it
) {
305 it
->first
->CloseClientPeerConnection();
309 void PeerConnectionTracker::RegisterPeerConnection(
310 RTCPeerConnectionHandler
* pc_handler
,
311 const webrtc::PeerConnectionInterface::RTCConfiguration
& config
,
312 const RTCMediaConstraints
& constraints
,
313 const blink::WebFrame
* frame
) {
314 DVLOG(1) << "PeerConnectionTracker::RegisterPeerConnection()";
315 PeerConnectionInfo info
;
317 info
.lid
= GetNextLocalID();
318 info
.rtc_configuration
=
319 "{ servers: " + SerializeServers(config
.servers
) + ", " +
320 "iceTransportType: " + SerializeIceTransportType(config
.type
) + " }";
322 info
.constraints
= SerializeMediaConstraints(constraints
);
323 info
.url
= frame
->document().url().spec();
324 RenderThreadImpl::current()->Send(
325 new PeerConnectionTrackerHost_AddPeerConnection(info
));
327 DCHECK(peer_connection_id_map_
.find(pc_handler
) ==
328 peer_connection_id_map_
.end());
329 peer_connection_id_map_
[pc_handler
] = info
.lid
;
332 void PeerConnectionTracker::UnregisterPeerConnection(
333 RTCPeerConnectionHandler
* pc_handler
) {
334 DVLOG(1) << "PeerConnectionTracker::UnregisterPeerConnection()";
336 std::map
<RTCPeerConnectionHandler
*, int>::iterator it
=
337 peer_connection_id_map_
.find(pc_handler
);
339 if (it
== peer_connection_id_map_
.end()) {
340 // The PeerConnection might not have been registered if its initilization
345 RenderThreadImpl::current()->Send(
346 new PeerConnectionTrackerHost_RemovePeerConnection(it
->second
));
348 peer_connection_id_map_
.erase(it
);
351 void PeerConnectionTracker::TrackCreateOffer(
352 RTCPeerConnectionHandler
* pc_handler
,
353 const RTCMediaConstraints
& constraints
) {
354 SendPeerConnectionUpdate(
355 pc_handler
, "createOffer",
356 "constraints: {" + SerializeMediaConstraints(constraints
) + "}");
359 void PeerConnectionTracker::TrackCreateAnswer(
360 RTCPeerConnectionHandler
* pc_handler
,
361 const RTCMediaConstraints
& constraints
) {
362 SendPeerConnectionUpdate(
363 pc_handler
, "createAnswer",
364 "constraints: {" + SerializeMediaConstraints(constraints
) + "}");
367 void PeerConnectionTracker::TrackSetSessionDescription(
368 RTCPeerConnectionHandler
* pc_handler
,
369 const blink::WebRTCSessionDescription
& desc
,
371 string sdp
= base::UTF16ToUTF8(desc
.sdp());
372 string type
= base::UTF16ToUTF8(desc
.type());
374 string value
= "type: " + type
+ ", sdp: " + sdp
;
375 SendPeerConnectionUpdate(
377 source
== SOURCE_LOCAL
? "setLocalDescription" : "setRemoteDescription",
381 void PeerConnectionTracker::TrackUpdateIce(
382 RTCPeerConnectionHandler
* pc_handler
,
383 const webrtc::PeerConnectionInterface::RTCConfiguration
& config
,
384 const RTCMediaConstraints
& options
) {
385 string servers_string
= "servers: " + SerializeServers(config
.servers
);
387 string transport_type
=
388 "iceTransportType: " + SerializeIceTransportType(config
.type
);
391 "constraints: {" + SerializeMediaConstraints(options
) + "}";
393 SendPeerConnectionUpdate(
396 servers_string
+ ", " + transport_type
+ ", " + constraints
);
399 void PeerConnectionTracker::TrackAddIceCandidate(
400 RTCPeerConnectionHandler
* pc_handler
,
401 const blink::WebRTCICECandidate
& candidate
,
405 "sdpMid: " + base::UTF16ToUTF8(candidate
.sdpMid()) + ", " +
406 "sdpMLineIndex: " + base::IntToString(candidate
.sdpMLineIndex()) + ", " +
407 "candidate: " + base::UTF16ToUTF8(candidate
.candidate());
409 // OnIceCandidate always succeeds as it's a callback from the browser.
410 DCHECK(source
!= SOURCE_LOCAL
|| succeeded
);
413 (source
== SOURCE_LOCAL
) ? "onIceCandidate"
414 : (succeeded
? "addIceCandidate"
415 : "addIceCandidateFailed");
417 SendPeerConnectionUpdate(pc_handler
, event
, value
);
420 void PeerConnectionTracker::TrackAddStream(
421 RTCPeerConnectionHandler
* pc_handler
,
422 const blink::WebMediaStream
& stream
,
424 SendPeerConnectionUpdate(
425 pc_handler
, source
== SOURCE_LOCAL
? "addStream" : "onAddStream",
426 SerializeMediaDescriptor(stream
));
429 void PeerConnectionTracker::TrackRemoveStream(
430 RTCPeerConnectionHandler
* pc_handler
,
431 const blink::WebMediaStream
& stream
,
433 SendPeerConnectionUpdate(
434 pc_handler
, source
== SOURCE_LOCAL
? "removeStream" : "onRemoveStream",
435 SerializeMediaDescriptor(stream
));
438 void PeerConnectionTracker::TrackCreateDataChannel(
439 RTCPeerConnectionHandler
* pc_handler
,
440 const webrtc::DataChannelInterface
* data_channel
,
442 string value
= "label: " + data_channel
->label() +
443 ", reliable: " + (data_channel
->reliable() ? "true" : "false");
444 SendPeerConnectionUpdate(
446 source
== SOURCE_LOCAL
? "createLocalDataChannel" : "onRemoteDataChannel",
450 void PeerConnectionTracker::TrackStop(RTCPeerConnectionHandler
* pc_handler
) {
451 SendPeerConnectionUpdate(pc_handler
, "stop", std::string());
454 void PeerConnectionTracker::TrackSignalingStateChange(
455 RTCPeerConnectionHandler
* pc_handler
,
456 WebRTCPeerConnectionHandlerClient::SignalingState state
) {
457 SendPeerConnectionUpdate(
458 pc_handler
, "signalingStateChange", GetSignalingStateString(state
));
461 void PeerConnectionTracker::TrackIceConnectionStateChange(
462 RTCPeerConnectionHandler
* pc_handler
,
463 WebRTCPeerConnectionHandlerClient::ICEConnectionState state
) {
464 SendPeerConnectionUpdate(
465 pc_handler
, "iceConnectionStateChange",
466 GetIceConnectionStateString(state
));
469 void PeerConnectionTracker::TrackIceGatheringStateChange(
470 RTCPeerConnectionHandler
* pc_handler
,
471 WebRTCPeerConnectionHandlerClient::ICEGatheringState state
) {
472 SendPeerConnectionUpdate(
473 pc_handler
, "iceGatheringStateChange",
474 GetIceGatheringStateString(state
));
477 void PeerConnectionTracker::TrackSessionDescriptionCallback(
478 RTCPeerConnectionHandler
* pc_handler
, Action action
,
479 const string
& callback_type
, const string
& value
) {
482 case ACTION_SET_LOCAL_DESCRIPTION
:
483 update_type
= "setLocalDescription";
485 case ACTION_SET_REMOTE_DESCRIPTION
:
486 update_type
= "setRemoteDescription";
488 case ACTION_CREATE_OFFER
:
489 update_type
= "createOffer";
491 case ACTION_CREATE_ANSWER
:
492 update_type
= "createAnswer";
498 update_type
+= callback_type
;
500 SendPeerConnectionUpdate(pc_handler
, update_type
, value
);
503 void PeerConnectionTracker::TrackOnRenegotiationNeeded(
504 RTCPeerConnectionHandler
* pc_handler
) {
505 SendPeerConnectionUpdate(pc_handler
, "onRenegotiationNeeded", std::string());
508 void PeerConnectionTracker::TrackCreateDTMFSender(
509 RTCPeerConnectionHandler
* pc_handler
,
510 const blink::WebMediaStreamTrack
& track
) {
511 SendPeerConnectionUpdate(pc_handler
, "createDTMFSender",
512 base::UTF16ToUTF8(track
.id()));
515 void PeerConnectionTracker::TrackGetUserMedia(
516 const blink::WebUserMediaRequest
& user_media_request
) {
517 RTCMediaConstraints
audio_constraints(
518 GetNativeMediaConstraints(user_media_request
.audioConstraints()));
519 RTCMediaConstraints
video_constraints(
520 GetNativeMediaConstraints(user_media_request
.videoConstraints()));
522 RenderThreadImpl::current()->Send(new PeerConnectionTrackerHost_GetUserMedia(
523 user_media_request
.securityOrigin().toString().utf8(),
524 user_media_request
.audio(),
525 user_media_request
.video(),
526 SerializeMediaConstraints(audio_constraints
),
527 SerializeMediaConstraints(video_constraints
)));
530 int PeerConnectionTracker::GetNextLocalID() {
534 void PeerConnectionTracker::SendPeerConnectionUpdate(
535 RTCPeerConnectionHandler
* pc_handler
,
536 const std::string
& type
,
537 const std::string
& value
) {
538 if (peer_connection_id_map_
.find(pc_handler
) == peer_connection_id_map_
.end())
541 RenderThreadImpl::current()->Send(
542 new PeerConnectionTrackerHost_UpdatePeerConnection(
543 peer_connection_id_map_
[pc_handler
], type
, value
));
546 } // namespace content