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 webrtc::StatsReport
;
26 using webrtc::StatsReports
;
27 using blink::WebRTCPeerConnectionHandlerClient
;
31 static string
SerializeServers(
32 const std::vector
<webrtc::PeerConnectionInterface::IceServer
>& servers
) {
34 for (size_t i
= 0; i
< servers
.size(); ++i
) {
35 result
+= servers
[i
].uri
;
36 if (i
!= servers
.size() - 1)
43 static RTCMediaConstraints
GetNativeMediaConstraints(
44 const blink::WebMediaConstraints
& constraints
) {
45 RTCMediaConstraints native_constraints
;
47 if (constraints
.isNull())
48 return native_constraints
;
50 blink::WebVector
<blink::WebMediaConstraint
> mandatory
;
51 constraints
.getMandatoryConstraints(mandatory
);
52 for (size_t i
= 0; i
< mandatory
.size(); ++i
) {
53 native_constraints
.AddMandatory(
54 mandatory
[i
].m_name
.utf8(), mandatory
[i
].m_value
.utf8(), false);
57 blink::WebVector
<blink::WebMediaConstraint
> optional
;
58 constraints
.getOptionalConstraints(optional
);
59 for (size_t i
= 0; i
< optional
.size(); ++i
) {
60 native_constraints
.AddOptional(
61 optional
[i
].m_name
.utf8(), optional
[i
].m_value
.utf8(), false);
63 return native_constraints
;
66 static string
SerializeMediaConstraints(
67 const RTCMediaConstraints
& constraints
) {
69 MediaConstraintsInterface::Constraints mandatory
= constraints
.GetMandatory();
70 if (!mandatory
.empty()) {
71 result
+= "mandatory: {";
72 for (size_t i
= 0; i
< mandatory
.size(); ++i
) {
73 result
+= mandatory
[i
].key
+ ":" + mandatory
[i
].value
;
74 if (i
!= mandatory
.size() - 1)
79 MediaConstraintsInterface::Constraints optional
= constraints
.GetOptional();
80 if (!optional
.empty()) {
83 result
+= "optional: {";
84 for (size_t i
= 0; i
< optional
.size(); ++i
) {
85 result
+= optional
[i
].key
+ ":" + optional
[i
].value
;
86 if (i
!= optional
.size() - 1)
94 static string
SerializeMediaStreamComponent(
95 const blink::WebMediaStreamTrack component
) {
96 string id
= base::UTF16ToUTF8(base::StringPiece16(component
.source().id()));
100 static string
SerializeMediaDescriptor(
101 const blink::WebMediaStream
& stream
) {
102 string label
= base::UTF16ToUTF8(base::StringPiece16(stream
.id()));
103 string result
= "label: " + label
;
104 blink::WebVector
<blink::WebMediaStreamTrack
> tracks
;
105 stream
.audioTracks(tracks
);
106 if (!tracks
.isEmpty()) {
107 result
+= ", audio: [";
108 for (size_t i
= 0; i
< tracks
.size(); ++i
) {
109 result
+= SerializeMediaStreamComponent(tracks
[i
]);
110 if (i
!= tracks
.size() - 1)
115 stream
.videoTracks(tracks
);
116 if (!tracks
.isEmpty()) {
117 result
+= ", video: [";
118 for (size_t i
= 0; i
< tracks
.size(); ++i
) {
119 result
+= SerializeMediaStreamComponent(tracks
[i
]);
120 if (i
!= tracks
.size() - 1)
128 static std::string
SerializeIceTransportType(
129 webrtc::PeerConnectionInterface::IceTransportsType type
) {
130 string transport_type
;
132 case webrtc::PeerConnectionInterface::kNone
:
133 transport_type
= "none";
135 case webrtc::PeerConnectionInterface::kRelay
:
136 transport_type
= "relay";
138 case webrtc::PeerConnectionInterface::kAll
:
139 transport_type
= "all";
141 case webrtc::PeerConnectionInterface::kNoHost
:
142 transport_type
= "noHost";
147 return transport_type
;
150 static std::string
SerializeBundlePolicy(
151 webrtc::PeerConnectionInterface::BundlePolicy policy
) {
154 case webrtc::PeerConnectionInterface::kBundlePolicyBalanced
:
155 policy_str
= "balanced";
157 case webrtc::PeerConnectionInterface::kBundlePolicyMaxBundle
:
158 policy_str
= "max-bundle";
160 case webrtc::PeerConnectionInterface::kBundlePolicyMaxCompat
:
161 policy_str
= "max-compat";
169 static std::string
SerializeRtcpMuxPolicy(
170 webrtc::PeerConnectionInterface::RtcpMuxPolicy policy
) {
173 case webrtc::PeerConnectionInterface::kRtcpMuxPolicyNegotiate
:
174 policy_str
= "negotiate";
176 case webrtc::PeerConnectionInterface::kRtcpMuxPolicyRequire
:
177 policy_str
= "require";
185 #define GET_STRING_OF_STATE(state) \
186 case WebRTCPeerConnectionHandlerClient::state: \
190 static string
GetSignalingStateString(
191 WebRTCPeerConnectionHandlerClient::SignalingState state
) {
194 GET_STRING_OF_STATE(SignalingStateStable
)
195 GET_STRING_OF_STATE(SignalingStateHaveLocalOffer
)
196 GET_STRING_OF_STATE(SignalingStateHaveRemoteOffer
)
197 GET_STRING_OF_STATE(SignalingStateHaveLocalPrAnswer
)
198 GET_STRING_OF_STATE(SignalingStateHaveRemotePrAnswer
)
199 GET_STRING_OF_STATE(SignalingStateClosed
)
207 static string
GetIceConnectionStateString(
208 WebRTCPeerConnectionHandlerClient::ICEConnectionState state
) {
211 GET_STRING_OF_STATE(ICEConnectionStateStarting
)
212 GET_STRING_OF_STATE(ICEConnectionStateChecking
)
213 GET_STRING_OF_STATE(ICEConnectionStateConnected
)
214 GET_STRING_OF_STATE(ICEConnectionStateCompleted
)
215 GET_STRING_OF_STATE(ICEConnectionStateFailed
)
216 GET_STRING_OF_STATE(ICEConnectionStateDisconnected
)
217 GET_STRING_OF_STATE(ICEConnectionStateClosed
)
225 static string
GetIceGatheringStateString(
226 WebRTCPeerConnectionHandlerClient::ICEGatheringState state
) {
229 GET_STRING_OF_STATE(ICEGatheringStateNew
)
230 GET_STRING_OF_STATE(ICEGatheringStateGathering
)
231 GET_STRING_OF_STATE(ICEGatheringStateComplete
)
239 // Builds a DictionaryValue from the StatsReport.
240 // The caller takes the ownership of the returned value.
242 // The format must be consistent with what webrtc_internals.js expects.
243 // If you change it here, you must change webrtc_internals.js as well.
244 static base::DictionaryValue
* GetDictValueStats(const StatsReport
& report
) {
245 if (report
.values().empty())
248 base::DictionaryValue
* dict
= new base::DictionaryValue();
249 dict
->SetDouble("timestamp", report
.timestamp());
251 base::ListValue
* values
= new base::ListValue();
252 dict
->Set("values", values
);
254 for (const auto& v
: report
.values()) {
255 const StatsReport::ValuePtr
& value
= v
.second
;
256 values
->AppendString(value
->display_name());
257 switch (value
->type()) {
258 case StatsReport::Value::kInt
:
259 values
->AppendInteger(value
->int_val());
261 case StatsReport::Value::kFloat
:
262 values
->AppendDouble(value
->float_val());
264 case StatsReport::Value::kString
:
265 values
->AppendString(value
->string_val());
267 case StatsReport::Value::kStaticString
:
268 values
->AppendString(value
->static_string_val());
270 case StatsReport::Value::kBool
:
271 values
->AppendBoolean(value
->bool_val());
273 case StatsReport::Value::kInt64
: // int64 isn't supported, so use string.
274 case StatsReport::Value::kId
:
276 values
->AppendString(value
->ToString());
284 // Builds a DictionaryValue from the StatsReport.
285 // The caller takes the ownership of the returned value.
286 static base::DictionaryValue
* GetDictValue(const StatsReport
& report
) {
287 scoped_ptr
<base::DictionaryValue
> stats
, result
;
289 stats
.reset(GetDictValueStats(report
));
293 result
.reset(new base::DictionaryValue());
295 // The format must be consistent with what webrtc_internals.js expects.
296 // If you change it here, you must change webrtc_internals.js as well.
297 result
->Set("stats", stats
.release());
298 result
->SetString("id", report
.id()->ToString());
299 result
->SetString("type", report
.TypeToString());
301 return result
.release();
304 class InternalStatsObserver
: public webrtc::StatsObserver
{
306 InternalStatsObserver(int lid
)
307 : lid_(lid
), main_thread_(base::ThreadTaskRunnerHandle::Get()) {}
309 void OnComplete(const StatsReports
& reports
) override
{
310 scoped_ptr
<base::ListValue
> list(new base::ListValue());
312 for (const auto* r
: reports
) {
313 base::DictionaryValue
* report
= GetDictValue(*r
);
315 list
->Append(report
);
318 if (!list
->empty()) {
319 main_thread_
->PostTask(FROM_HERE
,
320 base::Bind(&InternalStatsObserver::OnCompleteImpl
,
321 base::Passed(&list
), lid_
));
326 ~InternalStatsObserver() override
{
327 // Will be destructed on libjingle's signaling thread.
328 // The signaling thread is where libjingle's objects live and from where
329 // libjingle makes callbacks. This may or may not be the same thread as
334 // Static since |this| will most likely have been deleted by the time we
336 static void OnCompleteImpl(scoped_ptr
<base::ListValue
> list
, int lid
) {
337 DCHECK(!list
->empty());
338 RenderThreadImpl::current()->Send(
339 new PeerConnectionTrackerHost_AddStats(lid
, *list
.get()));
343 const scoped_refptr
<base::SingleThreadTaskRunner
> main_thread_
;
346 PeerConnectionTracker::PeerConnectionTracker() : next_lid_(1) {
349 PeerConnectionTracker::~PeerConnectionTracker() {
352 bool PeerConnectionTracker::OnControlMessageReceived(
353 const IPC::Message
& message
) {
355 IPC_BEGIN_MESSAGE_MAP(PeerConnectionTracker
, message
)
356 IPC_MESSAGE_HANDLER(PeerConnectionTracker_GetAllStats
, OnGetAllStats
)
357 IPC_MESSAGE_HANDLER(PeerConnectionTracker_OnSuspend
, OnSuspend
)
358 IPC_MESSAGE_UNHANDLED(handled
= false)
359 IPC_END_MESSAGE_MAP()
363 void PeerConnectionTracker::OnGetAllStats() {
364 DCHECK(main_thread_
.CalledOnValidThread());
366 const std::string empty_track_id
;
367 for (PeerConnectionIdMap::iterator it
= peer_connection_id_map_
.begin();
368 it
!= peer_connection_id_map_
.end(); ++it
) {
369 rtc::scoped_refptr
<InternalStatsObserver
> observer(
370 new rtc::RefCountedObject
<InternalStatsObserver
>(it
->second
));
372 // The last type parameter is ignored when the track id is empty.
375 webrtc::PeerConnectionInterface::kStatsOutputLevelDebug
,
376 empty_track_id
, blink::WebMediaStreamSource::TypeAudio
);
380 void PeerConnectionTracker::OnSuspend() {
381 DCHECK(main_thread_
.CalledOnValidThread());
382 for (PeerConnectionIdMap::iterator it
= peer_connection_id_map_
.begin();
383 it
!= peer_connection_id_map_
.end(); ++it
) {
384 it
->first
->CloseClientPeerConnection();
388 void PeerConnectionTracker::RegisterPeerConnection(
389 RTCPeerConnectionHandler
* pc_handler
,
390 const webrtc::PeerConnectionInterface::RTCConfiguration
& config
,
391 const RTCMediaConstraints
& constraints
,
392 const blink::WebFrame
* frame
) {
393 DCHECK(main_thread_
.CalledOnValidThread());
394 DVLOG(1) << "PeerConnectionTracker::RegisterPeerConnection()";
395 PeerConnectionInfo info
;
397 info
.lid
= GetNextLocalID();
398 info
.rtc_configuration
=
399 "{ servers: " + SerializeServers(config
.servers
) + ", " +
400 "iceTransportType: " + SerializeIceTransportType(config
.type
) + ", " +
401 "bundlePolicy: " + SerializeBundlePolicy(config
.bundle_policy
) + ", " +
402 "rtcpMuxPolicy: " + SerializeRtcpMuxPolicy(config
.rtcp_mux_policy
) + " }";
404 info
.constraints
= SerializeMediaConstraints(constraints
);
405 info
.url
= frame
->document().url().spec();
406 RenderThreadImpl::current()->Send(
407 new PeerConnectionTrackerHost_AddPeerConnection(info
));
409 DCHECK(peer_connection_id_map_
.find(pc_handler
) ==
410 peer_connection_id_map_
.end());
411 peer_connection_id_map_
[pc_handler
] = info
.lid
;
414 void PeerConnectionTracker::UnregisterPeerConnection(
415 RTCPeerConnectionHandler
* pc_handler
) {
416 DCHECK(main_thread_
.CalledOnValidThread());
417 DVLOG(1) << "PeerConnectionTracker::UnregisterPeerConnection()";
419 std::map
<RTCPeerConnectionHandler
*, int>::iterator it
=
420 peer_connection_id_map_
.find(pc_handler
);
422 if (it
== peer_connection_id_map_
.end()) {
423 // The PeerConnection might not have been registered if its initilization
428 RenderThreadImpl::current()->Send(
429 new PeerConnectionTrackerHost_RemovePeerConnection(it
->second
));
431 peer_connection_id_map_
.erase(it
);
434 void PeerConnectionTracker::TrackCreateOffer(
435 RTCPeerConnectionHandler
* pc_handler
,
436 const RTCMediaConstraints
& constraints
) {
437 DCHECK(main_thread_
.CalledOnValidThread());
438 SendPeerConnectionUpdate(
439 pc_handler
, "createOffer",
440 "constraints: {" + SerializeMediaConstraints(constraints
) + "}");
443 void PeerConnectionTracker::TrackCreateAnswer(
444 RTCPeerConnectionHandler
* pc_handler
,
445 const RTCMediaConstraints
& constraints
) {
446 DCHECK(main_thread_
.CalledOnValidThread());
447 SendPeerConnectionUpdate(
448 pc_handler
, "createAnswer",
449 "constraints: {" + SerializeMediaConstraints(constraints
) + "}");
452 void PeerConnectionTracker::TrackSetSessionDescription(
453 RTCPeerConnectionHandler
* pc_handler
,
454 const std::string
& sdp
, const std::string
& type
, Source source
) {
455 DCHECK(main_thread_
.CalledOnValidThread());
456 string value
= "type: " + type
+ ", sdp: " + sdp
;
457 SendPeerConnectionUpdate(
459 source
== SOURCE_LOCAL
? "setLocalDescription" : "setRemoteDescription",
463 void PeerConnectionTracker::TrackUpdateIce(
464 RTCPeerConnectionHandler
* pc_handler
,
465 const webrtc::PeerConnectionInterface::RTCConfiguration
& config
,
466 const RTCMediaConstraints
& options
) {
467 DCHECK(main_thread_
.CalledOnValidThread());
468 string servers_string
= "servers: " + SerializeServers(config
.servers
);
470 string transport_type
=
471 "iceTransportType: " + SerializeIceTransportType(config
.type
);
473 string bundle_policy
=
474 "bundlePolicy: " + SerializeBundlePolicy(config
.bundle_policy
);
476 string rtcp_mux_policy
=
477 "rtcpMuxPolicy: " + SerializeRtcpMuxPolicy(config
.rtcp_mux_policy
);
480 "constraints: {" + SerializeMediaConstraints(options
) + "}";
482 SendPeerConnectionUpdate(
485 servers_string
+ ", " + transport_type
+ ", " +
486 bundle_policy
+ ", " + rtcp_mux_policy
+ ", " +
490 void PeerConnectionTracker::TrackAddIceCandidate(
491 RTCPeerConnectionHandler
* pc_handler
,
492 const blink::WebRTCICECandidate
& candidate
,
495 DCHECK(main_thread_
.CalledOnValidThread());
498 base::UTF16ToUTF8(base::StringPiece16(candidate
.sdpMid())) + ", " +
499 "sdpMLineIndex: " + base::IntToString(candidate
.sdpMLineIndex()) +
500 ", " + "candidate: " +
501 base::UTF16ToUTF8(base::StringPiece16(candidate
.candidate()));
503 // OnIceCandidate always succeeds as it's a callback from the browser.
504 DCHECK(source
!= SOURCE_LOCAL
|| succeeded
);
507 (source
== SOURCE_LOCAL
) ? "onIceCandidate"
508 : (succeeded
? "addIceCandidate"
509 : "addIceCandidateFailed");
511 SendPeerConnectionUpdate(pc_handler
, event
, value
);
514 void PeerConnectionTracker::TrackAddStream(
515 RTCPeerConnectionHandler
* pc_handler
,
516 const blink::WebMediaStream
& stream
,
518 DCHECK(main_thread_
.CalledOnValidThread());
519 SendPeerConnectionUpdate(
520 pc_handler
, source
== SOURCE_LOCAL
? "addStream" : "onAddStream",
521 SerializeMediaDescriptor(stream
));
524 void PeerConnectionTracker::TrackRemoveStream(
525 RTCPeerConnectionHandler
* pc_handler
,
526 const blink::WebMediaStream
& stream
,
528 DCHECK(main_thread_
.CalledOnValidThread());
529 SendPeerConnectionUpdate(
530 pc_handler
, source
== SOURCE_LOCAL
? "removeStream" : "onRemoveStream",
531 SerializeMediaDescriptor(stream
));
534 void PeerConnectionTracker::TrackCreateDataChannel(
535 RTCPeerConnectionHandler
* pc_handler
,
536 const webrtc::DataChannelInterface
* data_channel
,
538 DCHECK(main_thread_
.CalledOnValidThread());
539 string value
= "label: " + data_channel
->label() +
540 ", reliable: " + (data_channel
->reliable() ? "true" : "false");
541 SendPeerConnectionUpdate(
543 source
== SOURCE_LOCAL
? "createLocalDataChannel" : "onRemoteDataChannel",
547 void PeerConnectionTracker::TrackStop(RTCPeerConnectionHandler
* pc_handler
) {
548 DCHECK(main_thread_
.CalledOnValidThread());
549 SendPeerConnectionUpdate(pc_handler
, "stop", std::string());
552 void PeerConnectionTracker::TrackSignalingStateChange(
553 RTCPeerConnectionHandler
* pc_handler
,
554 WebRTCPeerConnectionHandlerClient::SignalingState state
) {
555 DCHECK(main_thread_
.CalledOnValidThread());
556 SendPeerConnectionUpdate(
557 pc_handler
, "signalingStateChange", GetSignalingStateString(state
));
560 void PeerConnectionTracker::TrackIceConnectionStateChange(
561 RTCPeerConnectionHandler
* pc_handler
,
562 WebRTCPeerConnectionHandlerClient::ICEConnectionState state
) {
563 DCHECK(main_thread_
.CalledOnValidThread());
564 SendPeerConnectionUpdate(
565 pc_handler
, "iceConnectionStateChange",
566 GetIceConnectionStateString(state
));
569 void PeerConnectionTracker::TrackIceGatheringStateChange(
570 RTCPeerConnectionHandler
* pc_handler
,
571 WebRTCPeerConnectionHandlerClient::ICEGatheringState state
) {
572 DCHECK(main_thread_
.CalledOnValidThread());
573 SendPeerConnectionUpdate(
574 pc_handler
, "iceGatheringStateChange",
575 GetIceGatheringStateString(state
));
578 void PeerConnectionTracker::TrackSessionDescriptionCallback(
579 RTCPeerConnectionHandler
* pc_handler
, Action action
,
580 const string
& callback_type
, const string
& value
) {
581 DCHECK(main_thread_
.CalledOnValidThread());
584 case ACTION_SET_LOCAL_DESCRIPTION
:
585 update_type
= "setLocalDescription";
587 case ACTION_SET_REMOTE_DESCRIPTION
:
588 update_type
= "setRemoteDescription";
590 case ACTION_CREATE_OFFER
:
591 update_type
= "createOffer";
593 case ACTION_CREATE_ANSWER
:
594 update_type
= "createAnswer";
600 update_type
+= callback_type
;
602 SendPeerConnectionUpdate(pc_handler
, update_type
, value
);
605 void PeerConnectionTracker::TrackOnRenegotiationNeeded(
606 RTCPeerConnectionHandler
* pc_handler
) {
607 DCHECK(main_thread_
.CalledOnValidThread());
608 SendPeerConnectionUpdate(pc_handler
, "onRenegotiationNeeded", std::string());
611 void PeerConnectionTracker::TrackCreateDTMFSender(
612 RTCPeerConnectionHandler
* pc_handler
,
613 const blink::WebMediaStreamTrack
& track
) {
614 DCHECK(main_thread_
.CalledOnValidThread());
615 SendPeerConnectionUpdate(pc_handler
, "createDTMFSender",
616 base::UTF16ToUTF8(base::StringPiece16(track
.id())));
619 void PeerConnectionTracker::TrackGetUserMedia(
620 const blink::WebUserMediaRequest
& user_media_request
) {
621 DCHECK(main_thread_
.CalledOnValidThread());
622 RTCMediaConstraints
audio_constraints(
623 GetNativeMediaConstraints(user_media_request
.audioConstraints()));
624 RTCMediaConstraints
video_constraints(
625 GetNativeMediaConstraints(user_media_request
.videoConstraints()));
627 RenderThreadImpl::current()->Send(new PeerConnectionTrackerHost_GetUserMedia(
628 user_media_request
.securityOrigin().toString().utf8(),
629 user_media_request
.audio(),
630 user_media_request
.video(),
631 SerializeMediaConstraints(audio_constraints
),
632 SerializeMediaConstraints(video_constraints
)));
635 int PeerConnectionTracker::GetNextLocalID() {
636 DCHECK(main_thread_
.CalledOnValidThread());
640 void PeerConnectionTracker::SendPeerConnectionUpdate(
641 RTCPeerConnectionHandler
* pc_handler
,
642 const std::string
& type
,
643 const std::string
& value
) {
644 DCHECK(main_thread_
.CalledOnValidThread());
645 if (peer_connection_id_map_
.find(pc_handler
) == peer_connection_id_map_
.end())
648 RenderThreadImpl::current()->Send(
649 new PeerConnectionTrackerHost_UpdatePeerConnection(
650 peer_connection_id_map_
[pc_handler
], type
, value
));
653 } // namespace content