1 // Copyright (c) 2012 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/renderer/media/rtc_peer_connection_handler.h"
6
7 #include <string>
8 #include <utility>
9 #include <vector>
10
11 #include "base/command_line.h"
12 #include "base/debug/trace_event.h"
13 #include "base/lazy_instance.h"
14 #include "base/logging.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/metrics/histogram.h"
17 #include "base/stl_util.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "content/public/common/content_switches.h"
20 #include "content/renderer/media/media_stream_track.h"
21 #include "content/renderer/media/peer_connection_tracker.h"
22 #include "content/renderer/media/remote_media_stream_impl.h"
23 #include "content/renderer/media/rtc_data_channel_handler.h"
24 #include "content/renderer/media/rtc_dtmf_sender_handler.h"
25 #include "content/renderer/media/rtc_media_constraints.h"
26 #include "content/renderer/media/webrtc/peer_connection_dependency_factory.h"
27 #include "content/renderer/media/webrtc/webrtc_media_stream_adapter.h"
28 #include "content/renderer/media/webrtc_audio_capturer.h"
29 #include "content/renderer/media/webrtc_audio_device_impl.h"
30 #include "content/renderer/media/webrtc_uma_histograms.h"
31 #include "content/renderer/render_thread_impl.h"
32 #include "third_party/WebKit/public/platform/WebMediaConstraints.h"
33 #include "third_party/WebKit/public/platform/WebMediaStreamSource.h"
34 #include "third_party/WebKit/public/platform/WebRTCConfiguration.h"
35 #include "third_party/WebKit/public/platform/WebRTCDataChannelInit.h"
36 #include "third_party/WebKit/public/platform/WebRTCICECandidate.h"
37 #include "third_party/WebKit/public/platform/WebRTCOfferOptions.h"
38 #include "third_party/WebKit/public/platform/WebRTCSessionDescription.h"
39 #include "third_party/WebKit/public/platform/WebRTCSessionDescriptionRequest.h"
40 #include "third_party/WebKit/public/platform/WebRTCVoidRequest.h"
41 #include "third_party/WebKit/public/platform/WebURL.h"
42
43 using webrtc::StatsReport;
44 using webrtc::StatsReports;
45
46 namespace content {
47
48 // Converter functions from libjingle types to WebKit types.
49 blink::WebRTCPeerConnectionHandlerClient::ICEGatheringState
GetWebKitIceGatheringState(webrtc::PeerConnectionInterface::IceGatheringState state)50 GetWebKitIceGatheringState(
51 webrtc::PeerConnectionInterface::IceGatheringState state) {
52 using blink::WebRTCPeerConnectionHandlerClient;
53 switch (state) {
54 case webrtc::PeerConnectionInterface::kIceGatheringNew:
55 return WebRTCPeerConnectionHandlerClient::ICEGatheringStateNew;
56 case webrtc::PeerConnectionInterface::kIceGatheringGathering:
57 return WebRTCPeerConnectionHandlerClient::ICEGatheringStateGathering;
58 case webrtc::PeerConnectionInterface::kIceGatheringComplete:
59 return WebRTCPeerConnectionHandlerClient::ICEGatheringStateComplete;
60 default:
61 NOTREACHED();
62 return WebRTCPeerConnectionHandlerClient::ICEGatheringStateNew;
63 }
64 }
65
66 static blink::WebRTCPeerConnectionHandlerClient::ICEConnectionState
GetWebKitIceConnectionState(webrtc::PeerConnectionInterface::IceConnectionState ice_state)67 GetWebKitIceConnectionState(
68 webrtc::PeerConnectionInterface::IceConnectionState ice_state) {
69 using blink::WebRTCPeerConnectionHandlerClient;
70 switch (ice_state) {
71 case webrtc::PeerConnectionInterface::kIceConnectionNew:
72 return WebRTCPeerConnectionHandlerClient::ICEConnectionStateStarting;
73 case webrtc::PeerConnectionInterface::kIceConnectionChecking:
74 return WebRTCPeerConnectionHandlerClient::ICEConnectionStateChecking;
75 case webrtc::PeerConnectionInterface::kIceConnectionConnected:
76 return WebRTCPeerConnectionHandlerClient::ICEConnectionStateConnected;
77 case webrtc::PeerConnectionInterface::kIceConnectionCompleted:
78 return WebRTCPeerConnectionHandlerClient::ICEConnectionStateCompleted;
79 case webrtc::PeerConnectionInterface::kIceConnectionFailed:
80 return WebRTCPeerConnectionHandlerClient::ICEConnectionStateFailed;
81 case webrtc::PeerConnectionInterface::kIceConnectionDisconnected:
82 return WebRTCPeerConnectionHandlerClient::ICEConnectionStateDisconnected;
83 case webrtc::PeerConnectionInterface::kIceConnectionClosed:
84 return WebRTCPeerConnectionHandlerClient::ICEConnectionStateClosed;
85 default:
86 NOTREACHED();
87 return WebRTCPeerConnectionHandlerClient::ICEConnectionStateClosed;
88 }
89 }
90
91 static blink::WebRTCPeerConnectionHandlerClient::SignalingState
GetWebKitSignalingState(webrtc::PeerConnectionInterface::SignalingState state)92 GetWebKitSignalingState(webrtc::PeerConnectionInterface::SignalingState state) {
93 using blink::WebRTCPeerConnectionHandlerClient;
94 switch (state) {
95 case webrtc::PeerConnectionInterface::kStable:
96 return WebRTCPeerConnectionHandlerClient::SignalingStateStable;
97 case webrtc::PeerConnectionInterface::kHaveLocalOffer:
98 return WebRTCPeerConnectionHandlerClient::SignalingStateHaveLocalOffer;
99 case webrtc::PeerConnectionInterface::kHaveLocalPrAnswer:
100 return WebRTCPeerConnectionHandlerClient::SignalingStateHaveLocalPrAnswer;
101 case webrtc::PeerConnectionInterface::kHaveRemoteOffer:
102 return WebRTCPeerConnectionHandlerClient::SignalingStateHaveRemoteOffer;
103 case webrtc::PeerConnectionInterface::kHaveRemotePrAnswer:
104 return
105 WebRTCPeerConnectionHandlerClient::SignalingStateHaveRemotePrAnswer;
106 case webrtc::PeerConnectionInterface::kClosed:
107 return WebRTCPeerConnectionHandlerClient::SignalingStateClosed;
108 default:
109 NOTREACHED();
110 return WebRTCPeerConnectionHandlerClient::SignalingStateClosed;
111 }
112 }
113
114 static blink::WebRTCSessionDescription
CreateWebKitSessionDescription(const webrtc::SessionDescriptionInterface * native_desc)115 CreateWebKitSessionDescription(
116 const webrtc::SessionDescriptionInterface* native_desc) {
117 blink::WebRTCSessionDescription description;
118 if (!native_desc) {
119 LOG(ERROR) << "Native session description is null.";
120 return description;
121 }
122
123 std::string sdp;
124 if (!native_desc->ToString(&sdp)) {
125 LOG(ERROR) << "Failed to get SDP string of native session description.";
126 return description;
127 }
128
129 description.initialize(base::UTF8ToUTF16(native_desc->type()),
130 base::UTF8ToUTF16(sdp));
131 return description;
132 }
133
134 // Converter functions from WebKit types to libjingle types.
135
GetNativeRtcConfiguration(const blink::WebRTCConfiguration & server_configuration,webrtc::PeerConnectionInterface::RTCConfiguration * config)136 static void GetNativeRtcConfiguration(
137 const blink::WebRTCConfiguration& server_configuration,
138 webrtc::PeerConnectionInterface::RTCConfiguration* config) {
139 if (server_configuration.isNull() || !config)
140 return;
141 for (size_t i = 0; i < server_configuration.numberOfServers(); ++i) {
142 webrtc::PeerConnectionInterface::IceServer server;
143 const blink::WebRTCICEServer& webkit_server =
144 server_configuration.server(i);
145 server.username = base::UTF16ToUTF8(webkit_server.username());
146 server.password = base::UTF16ToUTF8(webkit_server.credential());
147 server.uri = webkit_server.uri().spec();
148 config->servers.push_back(server);
149 }
150
151 switch (server_configuration.iceTransports()) {
152 case blink::WebRTCIceTransportsNone:
153 config->type = webrtc::PeerConnectionInterface::kNone;
154 break;
155 case blink::WebRTCIceTransportsRelay:
156 config->type = webrtc::PeerConnectionInterface::kRelay;
157 break;
158 case blink::WebRTCIceTransportsAll:
159 config->type = webrtc::PeerConnectionInterface::kAll;
160 break;
161 default:
162 NOTREACHED();
163 }
164 }
165
166 class SessionDescriptionRequestTracker {
167 public:
SessionDescriptionRequestTracker(RTCPeerConnectionHandler * handler,PeerConnectionTracker::Action action)168 SessionDescriptionRequestTracker(RTCPeerConnectionHandler* handler,
169 PeerConnectionTracker::Action action)
170 : handler_(handler), action_(action) {}
171
TrackOnSuccess(const webrtc::SessionDescriptionInterface * desc)172 void TrackOnSuccess(const webrtc::SessionDescriptionInterface* desc) {
173 std::string value;
174 if (desc) {
175 desc->ToString(&value);
176 value = "type: " + desc->type() + ", sdp: " + value;
177 }
178 if (handler_->peer_connection_tracker())
179 handler_->peer_connection_tracker()->TrackSessionDescriptionCallback(
180 handler_, action_, "OnSuccess", value);
181 }
182
TrackOnFailure(const std::string & error)183 void TrackOnFailure(const std::string& error) {
184 if (handler_->peer_connection_tracker())
185 handler_->peer_connection_tracker()->TrackSessionDescriptionCallback(
186 handler_, action_, "OnFailure", error);
187 }
188
189 private:
190 RTCPeerConnectionHandler* handler_;
191 PeerConnectionTracker::Action action_;
192 };
193
194 // Class mapping responses from calls to libjingle CreateOffer/Answer and
195 // the blink::WebRTCSessionDescriptionRequest.
196 class CreateSessionDescriptionRequest
197 : public webrtc::CreateSessionDescriptionObserver {
198 public:
CreateSessionDescriptionRequest(const blink::WebRTCSessionDescriptionRequest & request,RTCPeerConnectionHandler * handler,PeerConnectionTracker::Action action)199 explicit CreateSessionDescriptionRequest(
200 const blink::WebRTCSessionDescriptionRequest& request,
201 RTCPeerConnectionHandler* handler,
202 PeerConnectionTracker::Action action)
203 : webkit_request_(request), tracker_(handler, action) {}
204
OnSuccess(webrtc::SessionDescriptionInterface * desc)205 virtual void OnSuccess(webrtc::SessionDescriptionInterface* desc) OVERRIDE {
206 tracker_.TrackOnSuccess(desc);
207 webkit_request_.requestSucceeded(CreateWebKitSessionDescription(desc));
208 delete desc;
209 }
OnFailure(const std::string & error)210 virtual void OnFailure(const std::string& error) OVERRIDE {
211 tracker_.TrackOnFailure(error);
212 webkit_request_.requestFailed(base::UTF8ToUTF16(error));
213 }
214
215 protected:
~CreateSessionDescriptionRequest()216 virtual ~CreateSessionDescriptionRequest() {}
217
218 private:
219 blink::WebRTCSessionDescriptionRequest webkit_request_;
220 SessionDescriptionRequestTracker tracker_;
221 };
222
223 // Class mapping responses from calls to libjingle
224 // SetLocalDescription/SetRemoteDescription and a blink::WebRTCVoidRequest.
225 class SetSessionDescriptionRequest
226 : public webrtc::SetSessionDescriptionObserver {
227 public:
SetSessionDescriptionRequest(const blink::WebRTCVoidRequest & request,RTCPeerConnectionHandler * handler,PeerConnectionTracker::Action action)228 explicit SetSessionDescriptionRequest(
229 const blink::WebRTCVoidRequest& request,
230 RTCPeerConnectionHandler* handler,
231 PeerConnectionTracker::Action action)
232 : webkit_request_(request), tracker_(handler, action) {}
233
OnSuccess()234 virtual void OnSuccess() OVERRIDE {
235 tracker_.TrackOnSuccess(NULL);
236 webkit_request_.requestSucceeded();
237 }
OnFailure(const std::string & error)238 virtual void OnFailure(const std::string& error) OVERRIDE {
239 tracker_.TrackOnFailure(error);
240 webkit_request_.requestFailed(base::UTF8ToUTF16(error));
241 }
242
243 protected:
~SetSessionDescriptionRequest()244 virtual ~SetSessionDescriptionRequest() {}
245
246 private:
247 blink::WebRTCVoidRequest webkit_request_;
248 SessionDescriptionRequestTracker tracker_;
249 };
250
251 // Class mapping responses from calls to libjingle
252 // GetStats into a blink::WebRTCStatsCallback.
253 class StatsResponse : public webrtc::StatsObserver {
254 public:
StatsResponse(const scoped_refptr<LocalRTCStatsRequest> & request)255 explicit StatsResponse(const scoped_refptr<LocalRTCStatsRequest>& request)
256 : request_(request.get()), response_(request_->createResponse().get()) {
257 // Measure the overall time it takes to satisfy a getStats request.
258 TRACE_EVENT_ASYNC_BEGIN0("webrtc", "getStats_Native", this);
259 }
260
OnComplete(const StatsReports & reports)261 virtual void OnComplete(const StatsReports& reports) OVERRIDE {
262 TRACE_EVENT0("webrtc", "StatsResponse::OnComplete")
263 for (StatsReports::const_iterator it = reports.begin();
264 it != reports.end(); ++it) {
265 if ((*it)->values.size() > 0) {
266 AddReport(*(*it));
267 }
268 }
269
270 // Record the getSync operation as done before calling into Blink so that
271 // we don't skew the perf measurements of the native code with whatever the
272 // callback might be doing.
273 TRACE_EVENT_ASYNC_END0("webrtc", "getStats_Native", this);
274
275 request_->requestSucceeded(response_);
276 }
277
278 private:
AddReport(const StatsReport & report)279 void AddReport(const StatsReport& report) {
280 int idx = response_->addReport(blink::WebString::fromUTF8(report.id),
281 blink::WebString::fromUTF8(report.type),
282 report.timestamp);
283 for (StatsReport::Values::const_iterator value_it = report.values.begin();
284 value_it != report.values.end(); ++value_it) {
285 AddStatistic(idx, value_it->display_name(), value_it->value);
286 }
287 }
288
AddStatistic(int idx,const char * name,const std::string & value)289 void AddStatistic(int idx, const char* name, const std::string& value) {
290 response_->addStatistic(idx,
291 blink::WebString::fromUTF8(name),
292 blink::WebString::fromUTF8(value));
293 }
294
295 rtc::scoped_refptr<LocalRTCStatsRequest> request_;
296 rtc::scoped_refptr<LocalRTCStatsResponse> response_;
297 };
298
299 // Implementation of LocalRTCStatsRequest.
LocalRTCStatsRequest(blink::WebRTCStatsRequest impl)300 LocalRTCStatsRequest::LocalRTCStatsRequest(blink::WebRTCStatsRequest impl)
301 : impl_(impl),
302 response_(NULL) {
303 }
304
LocalRTCStatsRequest()305 LocalRTCStatsRequest::LocalRTCStatsRequest() {}
~LocalRTCStatsRequest()306 LocalRTCStatsRequest::~LocalRTCStatsRequest() {}
307
hasSelector() const308 bool LocalRTCStatsRequest::hasSelector() const {
309 return impl_.hasSelector();
310 }
311
component() const312 blink::WebMediaStreamTrack LocalRTCStatsRequest::component() const {
313 return impl_.component();
314 }
315
createResponse()316 scoped_refptr<LocalRTCStatsResponse> LocalRTCStatsRequest::createResponse() {
317 DCHECK(!response_);
318 response_ = new rtc::RefCountedObject<LocalRTCStatsResponse>(
319 impl_.createResponse());
320 return response_.get();
321 }
322
requestSucceeded(const LocalRTCStatsResponse * response)323 void LocalRTCStatsRequest::requestSucceeded(
324 const LocalRTCStatsResponse* response) {
325 impl_.requestSucceeded(response->webKitStatsResponse());
326 }
327
328 // Implementation of LocalRTCStatsResponse.
webKitStatsResponse() const329 blink::WebRTCStatsResponse LocalRTCStatsResponse::webKitStatsResponse() const {
330 return impl_;
331 }
332
addReport(blink::WebString type,blink::WebString id,double timestamp)333 size_t LocalRTCStatsResponse::addReport(blink::WebString type,
334 blink::WebString id,
335 double timestamp) {
336 return impl_.addReport(type, id, timestamp);
337 }
338
addStatistic(size_t report,blink::WebString name,blink::WebString value)339 void LocalRTCStatsResponse::addStatistic(size_t report,
340 blink::WebString name,
341 blink::WebString value) {
342 impl_.addStatistic(report, name, value);
343 }
344
345 namespace {
346
347 class PeerConnectionUMAObserver : public webrtc::UMAObserver {
348 public:
PeerConnectionUMAObserver()349 PeerConnectionUMAObserver() {}
~PeerConnectionUMAObserver()350 virtual ~PeerConnectionUMAObserver() {}
351
IncrementCounter(webrtc::PeerConnectionUMAMetricsCounter counter)352 virtual void IncrementCounter(
353 webrtc::PeerConnectionUMAMetricsCounter counter) OVERRIDE {
354 UMA_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.IPMetrics",
355 counter,
356 webrtc::kBoundary);
357 }
358
AddHistogramSample(webrtc::PeerConnectionUMAMetricsName type,int value)359 virtual void AddHistogramSample(
360 webrtc::PeerConnectionUMAMetricsName type, int value) OVERRIDE {
361 switch (type) {
362 case webrtc::kTimeToConnect:
363 UMA_HISTOGRAM_MEDIUM_TIMES(
364 "WebRTC.PeerConnection.TimeToConnect",
365 base::TimeDelta::FromMilliseconds(value));
366 break;
367 case webrtc::kNetworkInterfaces_IPv4:
368 UMA_HISTOGRAM_COUNTS_100("WebRTC.PeerConnection.IPv4Interfaces",
369 value);
370 break;
371 case webrtc::kNetworkInterfaces_IPv6:
372 UMA_HISTOGRAM_COUNTS_100("WebRTC.PeerConnection.IPv6Interfaces",
373 value);
374 break;
375 default:
376 NOTREACHED();
377 }
378 }
379 };
380
381 base::LazyInstance<std::set<RTCPeerConnectionHandler*> >::Leaky
382 g_peer_connection_handlers = LAZY_INSTANCE_INITIALIZER;
383
384 } // namespace
385
RTCPeerConnectionHandler(blink::WebRTCPeerConnectionHandlerClient * client,PeerConnectionDependencyFactory * dependency_factory)386 RTCPeerConnectionHandler::RTCPeerConnectionHandler(
387 blink::WebRTCPeerConnectionHandlerClient* client,
388 PeerConnectionDependencyFactory* dependency_factory)
389 : client_(client),
390 dependency_factory_(dependency_factory),
391 frame_(NULL),
392 peer_connection_tracker_(NULL),
393 num_data_channels_created_(0),
394 num_local_candidates_ipv4_(0),
395 num_local_candidates_ipv6_(0) {
396 g_peer_connection_handlers.Get().insert(this);
397 }
398
~RTCPeerConnectionHandler()399 RTCPeerConnectionHandler::~RTCPeerConnectionHandler() {
400 g_peer_connection_handlers.Get().erase(this);
401 if (peer_connection_tracker_)
402 peer_connection_tracker_->UnregisterPeerConnection(this);
403 STLDeleteValues(&remote_streams_);
404
405 UMA_HISTOGRAM_COUNTS_10000(
406 "WebRTC.NumDataChannelsPerPeerConnection", num_data_channels_created_);
407 }
408
409 // static
DestructAllHandlers()410 void RTCPeerConnectionHandler::DestructAllHandlers() {
411 std::set<RTCPeerConnectionHandler*> handlers(
412 g_peer_connection_handlers.Get().begin(),
413 g_peer_connection_handlers.Get().end());
414 for (std::set<RTCPeerConnectionHandler*>::iterator handler = handlers.begin();
415 handler != handlers.end();
416 ++handler) {
417 (*handler)->client_->releasePeerConnectionHandler();
418 }
419 }
420
ConvertOfferOptionsToConstraints(const blink::WebRTCOfferOptions & options,RTCMediaConstraints * output)421 void RTCPeerConnectionHandler::ConvertOfferOptionsToConstraints(
422 const blink::WebRTCOfferOptions& options,
423 RTCMediaConstraints* output) {
424 output->AddMandatory(
425 webrtc::MediaConstraintsInterface::kOfferToReceiveAudio,
426 options.offerToReceiveAudio() > 0 ? "true" : "false",
427 true);
428
429 output->AddMandatory(
430 webrtc::MediaConstraintsInterface::kOfferToReceiveVideo,
431 options.offerToReceiveVideo() > 0 ? "true" : "false",
432 true);
433
434 if (!options.voiceActivityDetection()) {
435 output->AddMandatory(
436 webrtc::MediaConstraintsInterface::kVoiceActivityDetection,
437 "false",
438 true);
439 }
440
441 if (options.iceRestart()) {
442 output->AddMandatory(
443 webrtc::MediaConstraintsInterface::kIceRestart, "true", true);
444 }
445 }
446
associateWithFrame(blink::WebFrame * frame)447 void RTCPeerConnectionHandler::associateWithFrame(blink::WebFrame* frame) {
448 DCHECK(frame);
449 frame_ = frame;
450 }
451
initialize(const blink::WebRTCConfiguration & server_configuration,const blink::WebMediaConstraints & options)452 bool RTCPeerConnectionHandler::initialize(
453 const blink::WebRTCConfiguration& server_configuration,
454 const blink::WebMediaConstraints& options) {
455 DCHECK(frame_);
456
457 peer_connection_tracker_ =
458 RenderThreadImpl::current()->peer_connection_tracker();
459
460 webrtc::PeerConnectionInterface::RTCConfiguration config;
461 GetNativeRtcConfiguration(server_configuration, &config);
462
463 RTCMediaConstraints constraints(options);
464
465 native_peer_connection_ =
466 dependency_factory_->CreatePeerConnection(
467 config, &constraints, frame_, this);
468
469 if (!native_peer_connection_.get()) {
470 LOG(ERROR) << "Failed to initialize native PeerConnection.";
471 return false;
472 }
473 if (peer_connection_tracker_)
474 peer_connection_tracker_->RegisterPeerConnection(
475 this, config, constraints, frame_);
476
477 uma_observer_ = new rtc::RefCountedObject<PeerConnectionUMAObserver>();
478 native_peer_connection_->RegisterUMAObserver(uma_observer_.get());
479 return true;
480 }
481
InitializeForTest(const blink::WebRTCConfiguration & server_configuration,const blink::WebMediaConstraints & options,PeerConnectionTracker * peer_connection_tracker)482 bool RTCPeerConnectionHandler::InitializeForTest(
483 const blink::WebRTCConfiguration& server_configuration,
484 const blink::WebMediaConstraints& options,
485 PeerConnectionTracker* peer_connection_tracker) {
486 webrtc::PeerConnectionInterface::RTCConfiguration config;
487 GetNativeRtcConfiguration(server_configuration, &config);
488
489 RTCMediaConstraints constraints(options);
490 native_peer_connection_ =
491 dependency_factory_->CreatePeerConnection(
492 config, &constraints, NULL, this);
493 if (!native_peer_connection_.get()) {
494 LOG(ERROR) << "Failed to initialize native PeerConnection.";
495 return false;
496 }
497 peer_connection_tracker_ = peer_connection_tracker;
498 return true;
499 }
500
createOffer(const blink::WebRTCSessionDescriptionRequest & request,const blink::WebMediaConstraints & options)501 void RTCPeerConnectionHandler::createOffer(
502 const blink::WebRTCSessionDescriptionRequest& request,
503 const blink::WebMediaConstraints& options) {
504 scoped_refptr<CreateSessionDescriptionRequest> description_request(
505 new rtc::RefCountedObject<CreateSessionDescriptionRequest>(
506 request, this, PeerConnectionTracker::ACTION_CREATE_OFFER));
507 RTCMediaConstraints constraints(options);
508 native_peer_connection_->CreateOffer(description_request.get(), &constraints);
509
510 if (peer_connection_tracker_)
511 peer_connection_tracker_->TrackCreateOffer(this, constraints);
512 }
513
createOffer(const blink::WebRTCSessionDescriptionRequest & request,const blink::WebRTCOfferOptions & options)514 void RTCPeerConnectionHandler::createOffer(
515 const blink::WebRTCSessionDescriptionRequest& request,
516 const blink::WebRTCOfferOptions& options) {
517 scoped_refptr<CreateSessionDescriptionRequest> description_request(
518 new rtc::RefCountedObject<CreateSessionDescriptionRequest>(
519 request, this, PeerConnectionTracker::ACTION_CREATE_OFFER));
520
521 RTCMediaConstraints constraints;
522 ConvertOfferOptionsToConstraints(options, &constraints);
523 native_peer_connection_->CreateOffer(description_request.get(), &constraints);
524
525 if (peer_connection_tracker_)
526 peer_connection_tracker_->TrackCreateOffer(this, constraints);
527 }
528
createAnswer(const blink::WebRTCSessionDescriptionRequest & request,const blink::WebMediaConstraints & options)529 void RTCPeerConnectionHandler::createAnswer(
530 const blink::WebRTCSessionDescriptionRequest& request,
531 const blink::WebMediaConstraints& options) {
532 scoped_refptr<CreateSessionDescriptionRequest> description_request(
533 new rtc::RefCountedObject<CreateSessionDescriptionRequest>(
534 request, this, PeerConnectionTracker::ACTION_CREATE_ANSWER));
535 RTCMediaConstraints constraints(options);
536 native_peer_connection_->CreateAnswer(description_request.get(),
537 &constraints);
538
539 if (peer_connection_tracker_)
540 peer_connection_tracker_->TrackCreateAnswer(this, constraints);
541 }
542
setLocalDescription(const blink::WebRTCVoidRequest & request,const blink::WebRTCSessionDescription & description)543 void RTCPeerConnectionHandler::setLocalDescription(
544 const blink::WebRTCVoidRequest& request,
545 const blink::WebRTCSessionDescription& description) {
546 webrtc::SdpParseError error;
547 webrtc::SessionDescriptionInterface* native_desc =
548 CreateNativeSessionDescription(description, &error);
549 if (!native_desc) {
550 std::string reason_str = "Failed to parse SessionDescription. ";
551 reason_str.append(error.line);
552 reason_str.append(" ");
553 reason_str.append(error.description);
554 LOG(ERROR) << reason_str;
555 request.requestFailed(blink::WebString::fromUTF8(reason_str));
556 return;
557 }
558 if (peer_connection_tracker_)
559 peer_connection_tracker_->TrackSetSessionDescription(
560 this, description, PeerConnectionTracker::SOURCE_LOCAL);
561
562 scoped_refptr<SetSessionDescriptionRequest> set_request(
563 new rtc::RefCountedObject<SetSessionDescriptionRequest>(
564 request, this, PeerConnectionTracker::ACTION_SET_LOCAL_DESCRIPTION));
565 native_peer_connection_->SetLocalDescription(set_request.get(), native_desc);
566 }
567
setRemoteDescription(const blink::WebRTCVoidRequest & request,const blink::WebRTCSessionDescription & description)568 void RTCPeerConnectionHandler::setRemoteDescription(
569 const blink::WebRTCVoidRequest& request,
570 const blink::WebRTCSessionDescription& description) {
571 webrtc::SdpParseError error;
572 webrtc::SessionDescriptionInterface* native_desc =
573 CreateNativeSessionDescription(description, &error);
574 if (!native_desc) {
575 std::string reason_str = "Failed to parse SessionDescription. ";
576 reason_str.append(error.line);
577 reason_str.append(" ");
578 reason_str.append(error.description);
579 LOG(ERROR) << reason_str;
580 request.requestFailed(blink::WebString::fromUTF8(reason_str));
581 return;
582 }
583 if (peer_connection_tracker_)
584 peer_connection_tracker_->TrackSetSessionDescription(
585 this, description, PeerConnectionTracker::SOURCE_REMOTE);
586
587 scoped_refptr<SetSessionDescriptionRequest> set_request(
588 new rtc::RefCountedObject<SetSessionDescriptionRequest>(
589 request, this, PeerConnectionTracker::ACTION_SET_REMOTE_DESCRIPTION));
590 native_peer_connection_->SetRemoteDescription(set_request.get(), native_desc);
591 }
592
593 blink::WebRTCSessionDescription
localDescription()594 RTCPeerConnectionHandler::localDescription() {
595 const webrtc::SessionDescriptionInterface* native_desc =
596 native_peer_connection_->local_description();
597 blink::WebRTCSessionDescription description =
598 CreateWebKitSessionDescription(native_desc);
599 return description;
600 }
601
602 blink::WebRTCSessionDescription
remoteDescription()603 RTCPeerConnectionHandler::remoteDescription() {
604 const webrtc::SessionDescriptionInterface* native_desc =
605 native_peer_connection_->remote_description();
606 blink::WebRTCSessionDescription description =
607 CreateWebKitSessionDescription(native_desc);
608 return description;
609 }
610
updateICE(const blink::WebRTCConfiguration & server_configuration,const blink::WebMediaConstraints & options)611 bool RTCPeerConnectionHandler::updateICE(
612 const blink::WebRTCConfiguration& server_configuration,
613 const blink::WebMediaConstraints& options) {
614 webrtc::PeerConnectionInterface::RTCConfiguration config;
615 GetNativeRtcConfiguration(server_configuration, &config);
616 RTCMediaConstraints constraints(options);
617
618 if (peer_connection_tracker_)
619 peer_connection_tracker_->TrackUpdateIce(this, config, constraints);
620
621 return native_peer_connection_->UpdateIce(config.servers,
622 &constraints);
623 }
624
addICECandidate(const blink::WebRTCVoidRequest & request,const blink::WebRTCICECandidate & candidate)625 bool RTCPeerConnectionHandler::addICECandidate(
626 const blink::WebRTCVoidRequest& request,
627 const blink::WebRTCICECandidate& candidate) {
628 // Libjingle currently does not accept callbacks for addICECandidate.
629 // For that reason we are going to call callbacks from here.
630 bool result = addICECandidate(candidate);
631 base::MessageLoop::current()->PostTask(
632 FROM_HERE,
633 base::Bind(&RTCPeerConnectionHandler::OnaddICECandidateResult,
634 base::Unretained(this), request, result));
635 // On failure callback will be triggered.
636 return true;
637 }
638
addICECandidate(const blink::WebRTCICECandidate & candidate)639 bool RTCPeerConnectionHandler::addICECandidate(
640 const blink::WebRTCICECandidate& candidate) {
641 scoped_ptr<webrtc::IceCandidateInterface> native_candidate(
642 dependency_factory_->CreateIceCandidate(
643 base::UTF16ToUTF8(candidate.sdpMid()),
644 candidate.sdpMLineIndex(),
645 base::UTF16ToUTF8(candidate.candidate())));
646 bool return_value = false;
647
648 if (native_candidate) {
649 return_value =
650 native_peer_connection_->AddIceCandidate(native_candidate.get());
651 LOG_IF(ERROR, !return_value) << "Error processing ICE candidate.";
652 } else {
653 LOG(ERROR) << "Could not create native ICE candidate.";
654 }
655
656 if (peer_connection_tracker_) {
657 peer_connection_tracker_->TrackAddIceCandidate(
658 this, candidate, PeerConnectionTracker::SOURCE_REMOTE, return_value);
659 }
660 return return_value;
661 }
662
OnaddICECandidateResult(const blink::WebRTCVoidRequest & webkit_request,bool result)663 void RTCPeerConnectionHandler::OnaddICECandidateResult(
664 const blink::WebRTCVoidRequest& webkit_request, bool result) {
665 if (!result) {
666 // We don't have the actual error code from the libjingle, so for now
667 // using a generic error string.
668 return webkit_request.requestFailed(
669 base::UTF8ToUTF16("Error processing ICE candidate"));
670 }
671
672 return webkit_request.requestSucceeded();
673 }
674
addStream(const blink::WebMediaStream & stream,const blink::WebMediaConstraints & options)675 bool RTCPeerConnectionHandler::addStream(
676 const blink::WebMediaStream& stream,
677 const blink::WebMediaConstraints& options) {
678
679 for (ScopedVector<WebRtcMediaStreamAdapter>::iterator adapter_it =
680 local_streams_.begin(); adapter_it != local_streams_.end();
681 ++adapter_it) {
682 if ((*adapter_it)->IsEqual(stream)) {
683 DVLOG(1) << "RTCPeerConnectionHandler::addStream called with the same "
684 << "stream twice. id=" << stream.id().utf8();
685 return false;
686 }
687 }
688
689 if (peer_connection_tracker_)
690 peer_connection_tracker_->TrackAddStream(
691 this, stream, PeerConnectionTracker::SOURCE_LOCAL);
692
693 PerSessionWebRTCAPIMetrics::GetInstance()->IncrementStreamCounter();
694
695 WebRtcMediaStreamAdapter* adapter =
696 new WebRtcMediaStreamAdapter(stream, dependency_factory_);
697 local_streams_.push_back(adapter);
698
699 webrtc::MediaStreamInterface* webrtc_stream = adapter->webrtc_media_stream();
700 track_metrics_.AddStream(MediaStreamTrackMetrics::SENT_STREAM,
701 webrtc_stream);
702
703 RTCMediaConstraints constraints(options);
704 return native_peer_connection_->AddStream(webrtc_stream, &constraints);
705 }
706
removeStream(const blink::WebMediaStream & stream)707 void RTCPeerConnectionHandler::removeStream(
708 const blink::WebMediaStream& stream) {
709 // Find the webrtc stream.
710 scoped_refptr<webrtc::MediaStreamInterface> webrtc_stream;
711 for (ScopedVector<WebRtcMediaStreamAdapter>::iterator adapter_it =
712 local_streams_.begin(); adapter_it != local_streams_.end();
713 ++adapter_it) {
714 if ((*adapter_it)->IsEqual(stream)) {
715 webrtc_stream = (*adapter_it)->webrtc_media_stream();
716 local_streams_.erase(adapter_it);
717 break;
718 }
719 }
720 DCHECK(webrtc_stream.get());
721 native_peer_connection_->RemoveStream(webrtc_stream.get());
722
723 if (peer_connection_tracker_)
724 peer_connection_tracker_->TrackRemoveStream(
725 this, stream, PeerConnectionTracker::SOURCE_LOCAL);
726 PerSessionWebRTCAPIMetrics::GetInstance()->DecrementStreamCounter();
727 track_metrics_.RemoveStream(MediaStreamTrackMetrics::SENT_STREAM,
728 webrtc_stream.get());
729 }
730
getStats(const blink::WebRTCStatsRequest & request)731 void RTCPeerConnectionHandler::getStats(
732 const blink::WebRTCStatsRequest& request) {
733 scoped_refptr<LocalRTCStatsRequest> inner_request(
734 new rtc::RefCountedObject<LocalRTCStatsRequest>(request));
735 getStats(inner_request.get());
736 }
737
getStats(LocalRTCStatsRequest * request)738 void RTCPeerConnectionHandler::getStats(LocalRTCStatsRequest* request) {
739 rtc::scoped_refptr<webrtc::StatsObserver> observer(
740 new rtc::RefCountedObject<StatsResponse>(request));
741 webrtc::MediaStreamTrackInterface* track = NULL;
742 if (request->hasSelector()) {
743 blink::WebMediaStreamSource::Type type =
744 request->component().source().type();
745 std::string track_id = request->component().id().utf8();
746 if (type == blink::WebMediaStreamSource::TypeAudio) {
747 track =
748 native_peer_connection_->local_streams()->FindAudioTrack(track_id);
749 if (!track) {
750 track =
751 native_peer_connection_->remote_streams()->FindAudioTrack(track_id);
752 }
753 } else {
754 DCHECK_EQ(blink::WebMediaStreamSource::TypeVideo, type);
755 track =
756 native_peer_connection_->local_streams()->FindVideoTrack(track_id);
757 if (!track) {
758 track =
759 native_peer_connection_->remote_streams()->FindVideoTrack(track_id);
760 }
761 }
762 if (!track) {
763 DVLOG(1) << "GetStats: Track not found.";
764 // TODO(hta): Consider how to get an error back.
765 observer->OnComplete(StatsReports());
766 return;
767 }
768 }
769 GetStats(observer,
770 track,
771 webrtc::PeerConnectionInterface::kStatsOutputLevelStandard);
772 }
773
GetStats(webrtc::StatsObserver * observer,webrtc::MediaStreamTrackInterface * track,webrtc::PeerConnectionInterface::StatsOutputLevel level)774 void RTCPeerConnectionHandler::GetStats(
775 webrtc::StatsObserver* observer,
776 webrtc::MediaStreamTrackInterface* track,
777 webrtc::PeerConnectionInterface::StatsOutputLevel level) {
778 TRACE_EVENT0("webrtc", "RTCPeerConnectionHandler::GetStats");
779 if (!native_peer_connection_->GetStats(observer, track, level)) {
780 DVLOG(1) << "GetStats failed.";
781 // TODO(hta): Consider how to get an error back.
782 observer->OnComplete(StatsReports());
783 return;
784 }
785 }
786
CloseClientPeerConnection()787 void RTCPeerConnectionHandler::CloseClientPeerConnection() {
788 client_->closePeerConnection();
789 }
790
createDataChannel(const blink::WebString & label,const blink::WebRTCDataChannelInit & init)791 blink::WebRTCDataChannelHandler* RTCPeerConnectionHandler::createDataChannel(
792 const blink::WebString& label, const blink::WebRTCDataChannelInit& init) {
793 DVLOG(1) << "createDataChannel label " << base::UTF16ToUTF8(label);
794
795 webrtc::DataChannelInit config;
796 // TODO(jiayl): remove the deprecated reliable field once Libjingle is updated
797 // to handle that.
798 config.reliable = false;
799 config.id = init.id;
800 config.ordered = init.ordered;
801 config.negotiated = init.negotiated;
802 config.maxRetransmits = init.maxRetransmits;
803 config.maxRetransmitTime = init.maxRetransmitTime;
804 config.protocol = base::UTF16ToUTF8(init.protocol);
805
806 rtc::scoped_refptr<webrtc::DataChannelInterface> webrtc_channel(
807 native_peer_connection_->CreateDataChannel(base::UTF16ToUTF8(label),
808 &config));
809 if (!webrtc_channel) {
810 DLOG(ERROR) << "Could not create native data channel.";
811 return NULL;
812 }
813 if (peer_connection_tracker_)
814 peer_connection_tracker_->TrackCreateDataChannel(
815 this, webrtc_channel.get(), PeerConnectionTracker::SOURCE_LOCAL);
816
817 ++num_data_channels_created_;
818
819 return new RtcDataChannelHandler(webrtc_channel);
820 }
821
createDTMFSender(const blink::WebMediaStreamTrack & track)822 blink::WebRTCDTMFSenderHandler* RTCPeerConnectionHandler::createDTMFSender(
823 const blink::WebMediaStreamTrack& track) {
824 DVLOG(1) << "createDTMFSender.";
825
826 MediaStreamTrack* native_track = MediaStreamTrack::GetTrack(track);
827 if (!native_track ||
828 track.source().type() != blink::WebMediaStreamSource::TypeAudio) {
829 DLOG(ERROR) << "Could not create DTMF sender from a non-audio track.";
830 return NULL;
831 }
832
833 webrtc::AudioTrackInterface* audio_track = native_track->GetAudioAdapter();
834 rtc::scoped_refptr<webrtc::DtmfSenderInterface> sender(
835 native_peer_connection_->CreateDtmfSender(audio_track));
836 if (!sender) {
837 DLOG(ERROR) << "Could not create native DTMF sender.";
838 return NULL;
839 }
840 if (peer_connection_tracker_)
841 peer_connection_tracker_->TrackCreateDTMFSender(this, track);
842
843 return new RtcDtmfSenderHandler(sender);
844 }
845
stop()846 void RTCPeerConnectionHandler::stop() {
847 DVLOG(1) << "RTCPeerConnectionHandler::stop";
848
849 if (peer_connection_tracker_)
850 peer_connection_tracker_->TrackStop(this);
851 native_peer_connection_->Close();
852 }
853
OnError()854 void RTCPeerConnectionHandler::OnError() {
855 // TODO(perkj): Implement.
856 NOTIMPLEMENTED();
857 }
858
OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state)859 void RTCPeerConnectionHandler::OnSignalingChange(
860 webrtc::PeerConnectionInterface::SignalingState new_state) {
861 blink::WebRTCPeerConnectionHandlerClient::SignalingState state =
862 GetWebKitSignalingState(new_state);
863 if (peer_connection_tracker_)
864 peer_connection_tracker_->TrackSignalingStateChange(this, state);
865 client_->didChangeSignalingState(state);
866 }
867
868 // Called any time the IceConnectionState changes
OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState new_state)869 void RTCPeerConnectionHandler::OnIceConnectionChange(
870 webrtc::PeerConnectionInterface::IceConnectionState new_state) {
871 if (new_state == webrtc::PeerConnectionInterface::kIceConnectionChecking) {
872 ice_connection_checking_start_ = base::TimeTicks::Now();
873 } else if (new_state ==
874 webrtc::PeerConnectionInterface::kIceConnectionConnected) {
875 // If the state becomes connected, send the time needed for PC to become
876 // connected from checking to UMA. UMA data will help to know how much
877 // time needed for PC to connect with remote peer.
878 UMA_HISTOGRAM_MEDIUM_TIMES(
879 "WebRTC.PeerConnection.TimeToConnect",
880 base::TimeTicks::Now() - ice_connection_checking_start_);
881 }
882
883 track_metrics_.IceConnectionChange(new_state);
884 blink::WebRTCPeerConnectionHandlerClient::ICEConnectionState state =
885 GetWebKitIceConnectionState(new_state);
886 if (peer_connection_tracker_)
887 peer_connection_tracker_->TrackIceConnectionStateChange(this, state);
888 client_->didChangeICEConnectionState(state);
889 }
890
891 // Called any time the IceGatheringState changes
OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state)892 void RTCPeerConnectionHandler::OnIceGatheringChange(
893 webrtc::PeerConnectionInterface::IceGatheringState new_state) {
894 if (new_state == webrtc::PeerConnectionInterface::kIceGatheringComplete) {
895 // If ICE gathering is completed, generate a NULL ICE candidate,
896 // to signal end of candidates.
897 blink::WebRTCICECandidate null_candidate;
898 client_->didGenerateICECandidate(null_candidate);
899
900 UMA_HISTOGRAM_COUNTS_100("WebRTC.PeerConnection.IPv4LocalCandidates",
901 num_local_candidates_ipv4_);
902
903 UMA_HISTOGRAM_COUNTS_100("WebRTC.PeerConnection.IPv6LocalCandidates",
904 num_local_candidates_ipv6_);
905 } else if (new_state ==
906 webrtc::PeerConnectionInterface::kIceGatheringGathering) {
907 // ICE restarts will change gathering state back to "gathering",
908 // reset the counter.
909 num_local_candidates_ipv6_ = 0;
910 num_local_candidates_ipv4_ = 0;
911 }
912
913 blink::WebRTCPeerConnectionHandlerClient::ICEGatheringState state =
914 GetWebKitIceGatheringState(new_state);
915 if (peer_connection_tracker_)
916 peer_connection_tracker_->TrackIceGatheringStateChange(this, state);
917 client_->didChangeICEGatheringState(state);
918 }
919
OnAddStream(webrtc::MediaStreamInterface * stream_interface)920 void RTCPeerConnectionHandler::OnAddStream(
921 webrtc::MediaStreamInterface* stream_interface) {
922 DCHECK(stream_interface);
923 DCHECK(remote_streams_.find(stream_interface) == remote_streams_.end());
924
925 RemoteMediaStreamImpl* remote_stream =
926 new RemoteMediaStreamImpl(stream_interface);
927 remote_streams_.insert(
928 std::pair<webrtc::MediaStreamInterface*, RemoteMediaStreamImpl*> (
929 stream_interface, remote_stream));
930
931 if (peer_connection_tracker_)
932 peer_connection_tracker_->TrackAddStream(
933 this, remote_stream->webkit_stream(),
934 PeerConnectionTracker::SOURCE_REMOTE);
935
936 PerSessionWebRTCAPIMetrics::GetInstance()->IncrementStreamCounter();
937
938 track_metrics_.AddStream(MediaStreamTrackMetrics::RECEIVED_STREAM,
939 stream_interface);
940
941 client_->didAddRemoteStream(remote_stream->webkit_stream());
942 }
943
OnRemoveStream(webrtc::MediaStreamInterface * stream_interface)944 void RTCPeerConnectionHandler::OnRemoveStream(
945 webrtc::MediaStreamInterface* stream_interface) {
946 DCHECK(stream_interface);
947 RemoteStreamMap::iterator it = remote_streams_.find(stream_interface);
948 if (it == remote_streams_.end()) {
949 NOTREACHED() << "Stream not found";
950 return;
951 }
952
953 track_metrics_.RemoveStream(MediaStreamTrackMetrics::RECEIVED_STREAM,
954 stream_interface);
955 PerSessionWebRTCAPIMetrics::GetInstance()->DecrementStreamCounter();
956
957 scoped_ptr<RemoteMediaStreamImpl> remote_stream(it->second);
958 const blink::WebMediaStream& webkit_stream = remote_stream->webkit_stream();
959 DCHECK(!webkit_stream.isNull());
960 remote_streams_.erase(it);
961
962 if (peer_connection_tracker_)
963 peer_connection_tracker_->TrackRemoveStream(
964 this, webkit_stream, PeerConnectionTracker::SOURCE_REMOTE);
965
966 client_->didRemoveRemoteStream(webkit_stream);
967 }
968
OnIceCandidate(const webrtc::IceCandidateInterface * candidate)969 void RTCPeerConnectionHandler::OnIceCandidate(
970 const webrtc::IceCandidateInterface* candidate) {
971 DCHECK(candidate);
972 std::string sdp;
973 if (!candidate->ToString(&sdp)) {
974 NOTREACHED() << "OnIceCandidate: Could not get SDP string.";
975 return;
976 }
977 blink::WebRTCICECandidate web_candidate;
978 web_candidate.initialize(base::UTF8ToUTF16(sdp),
979 base::UTF8ToUTF16(candidate->sdp_mid()),
980 candidate->sdp_mline_index());
981 if (peer_connection_tracker_)
982 peer_connection_tracker_->TrackAddIceCandidate(
983 this, web_candidate, PeerConnectionTracker::SOURCE_LOCAL, true);
984
985 // Only the first m line's first component is tracked to avoid
986 // miscounting when doing BUNDLE or rtcp mux.
987 if (candidate->sdp_mline_index() == 0 &&
988 candidate->candidate().component() == 1) {
989 if (candidate->candidate().address().family() == AF_INET) {
990 num_local_candidates_ipv4_++;
991 } else if (candidate->candidate().address().family() == AF_INET6) {
992 num_local_candidates_ipv6_++;
993 } else {
994 NOTREACHED();
995 }
996 }
997 client_->didGenerateICECandidate(web_candidate);
998 }
999
OnDataChannel(webrtc::DataChannelInterface * data_channel)1000 void RTCPeerConnectionHandler::OnDataChannel(
1001 webrtc::DataChannelInterface* data_channel) {
1002 if (peer_connection_tracker_)
1003 peer_connection_tracker_->TrackCreateDataChannel(
1004 this, data_channel, PeerConnectionTracker::SOURCE_REMOTE);
1005
1006 DVLOG(1) << "RTCPeerConnectionHandler::OnDataChannel "
1007 << data_channel->label();
1008 client_->didAddRemoteDataChannel(new RtcDataChannelHandler(data_channel));
1009 }
1010
OnRenegotiationNeeded()1011 void RTCPeerConnectionHandler::OnRenegotiationNeeded() {
1012 if (peer_connection_tracker_)
1013 peer_connection_tracker_->TrackOnRenegotiationNeeded(this);
1014 client_->negotiationNeeded();
1015 }
1016
peer_connection_tracker()1017 PeerConnectionTracker* RTCPeerConnectionHandler::peer_connection_tracker() {
1018 return peer_connection_tracker_;
1019 }
1020
1021 webrtc::SessionDescriptionInterface*
CreateNativeSessionDescription(const blink::WebRTCSessionDescription & description,webrtc::SdpParseError * error)1022 RTCPeerConnectionHandler::CreateNativeSessionDescription(
1023 const blink::WebRTCSessionDescription& description,
1024 webrtc::SdpParseError* error) {
1025 std::string sdp = base::UTF16ToUTF8(description.sdp());
1026 std::string type = base::UTF16ToUTF8(description.type());
1027 webrtc::SessionDescriptionInterface* native_desc =
1028 dependency_factory_->CreateSessionDescription(type, sdp, error);
1029
1030 LOG_IF(ERROR, !native_desc) << "Failed to create native session description."
1031 << " Type: " << type << " SDP: " << sdp;
1032
1033 return native_desc;
1034 }
1035
1036 } // namespace content
1037