1/* 2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#import "RTCPeerConnection+Private.h" 12 13#import "NSString+StdString.h" 14#import "RTCConfiguration+Private.h" 15#import "RTCDataChannel+Private.h" 16#import "RTCIceCandidate+Private.h" 17#import "RTCLegacyStatsReport+Private.h" 18#import "RTCMediaConstraints+Private.h" 19#import "RTCMediaStream+Private.h" 20#import "RTCPeerConnectionFactory+Private.h" 21#import "RTCRtpReceiver+Private.h" 22#import "RTCRtpSender+Private.h" 23#import "RTCSessionDescription+Private.h" 24#import "WebRTC/RTCLogging.h" 25 26#include <memory> 27 28#include "webrtc/api/jsepicecandidate.h" 29#include "webrtc/base/checks.h" 30 31NSString * const kRTCPeerConnectionErrorDomain = 32 @"org.webrtc.RTCPeerConnection"; 33int const kRTCPeerConnnectionSessionDescriptionError = -1; 34 35namespace webrtc { 36 37class CreateSessionDescriptionObserverAdapter 38 : public CreateSessionDescriptionObserver { 39 public: 40 CreateSessionDescriptionObserverAdapter( 41 void (^completionHandler)(RTCSessionDescription *sessionDescription, 42 NSError *error)) { 43 completion_handler_ = completionHandler; 44 } 45 46 ~CreateSessionDescriptionObserverAdapter() { 47 completion_handler_ = nil; 48 } 49 50 void OnSuccess(SessionDescriptionInterface *desc) override { 51 RTC_DCHECK(completion_handler_); 52 std::unique_ptr<webrtc::SessionDescriptionInterface> description = 53 std::unique_ptr<webrtc::SessionDescriptionInterface>(desc); 54 RTCSessionDescription* session = 55 [[RTCSessionDescription alloc] initWithNativeDescription: 56 description.get()]; 57 completion_handler_(session, nil); 58 completion_handler_ = nil; 59 } 60 61 void OnFailure(const std::string& error) override { 62 RTC_DCHECK(completion_handler_); 63 NSString* str = [NSString stringForStdString:error]; 64 NSError* err = 65 [NSError errorWithDomain:kRTCPeerConnectionErrorDomain 66 code:kRTCPeerConnnectionSessionDescriptionError 67 userInfo:@{ NSLocalizedDescriptionKey : str }]; 68 completion_handler_(nil, err); 69 completion_handler_ = nil; 70 } 71 72 private: 73 void (^completion_handler_) 74 (RTCSessionDescription *sessionDescription, NSError *error); 75}; 76 77class SetSessionDescriptionObserverAdapter : 78 public SetSessionDescriptionObserver { 79 public: 80 SetSessionDescriptionObserverAdapter(void (^completionHandler) 81 (NSError *error)) { 82 completion_handler_ = completionHandler; 83 } 84 85 ~SetSessionDescriptionObserverAdapter() { 86 completion_handler_ = nil; 87 } 88 89 void OnSuccess() override { 90 RTC_DCHECK(completion_handler_); 91 completion_handler_(nil); 92 completion_handler_ = nil; 93 } 94 95 void OnFailure(const std::string& error) override { 96 RTC_DCHECK(completion_handler_); 97 NSString* str = [NSString stringForStdString:error]; 98 NSError* err = 99 [NSError errorWithDomain:kRTCPeerConnectionErrorDomain 100 code:kRTCPeerConnnectionSessionDescriptionError 101 userInfo:@{ NSLocalizedDescriptionKey : str }]; 102 completion_handler_(err); 103 completion_handler_ = nil; 104 } 105 106 private: 107 void (^completion_handler_)(NSError *error); 108}; 109 110PeerConnectionDelegateAdapter::PeerConnectionDelegateAdapter( 111 RTCPeerConnection *peerConnection) { 112 peer_connection_ = peerConnection; 113} 114 115PeerConnectionDelegateAdapter::~PeerConnectionDelegateAdapter() { 116 peer_connection_ = nil; 117} 118 119void PeerConnectionDelegateAdapter::OnSignalingChange( 120 PeerConnectionInterface::SignalingState new_state) { 121 RTCSignalingState state = 122 [[RTCPeerConnection class] signalingStateForNativeState:new_state]; 123 RTCPeerConnection *peer_connection = peer_connection_; 124 [peer_connection.delegate peerConnection:peer_connection 125 didChangeSignalingState:state]; 126} 127 128void PeerConnectionDelegateAdapter::OnAddStream( 129 rtc::scoped_refptr<MediaStreamInterface> stream) { 130 RTCMediaStream *mediaStream = 131 [[RTCMediaStream alloc] initWithNativeMediaStream:stream]; 132 RTCPeerConnection *peer_connection = peer_connection_; 133 [peer_connection.delegate peerConnection:peer_connection 134 didAddStream:mediaStream]; 135} 136 137void PeerConnectionDelegateAdapter::OnRemoveStream( 138 rtc::scoped_refptr<MediaStreamInterface> stream) { 139 RTCMediaStream *mediaStream = 140 [[RTCMediaStream alloc] initWithNativeMediaStream:stream]; 141 RTCPeerConnection *peer_connection = peer_connection_; 142 [peer_connection.delegate peerConnection:peer_connection 143 didRemoveStream:mediaStream]; 144} 145 146void PeerConnectionDelegateAdapter::OnDataChannel( 147 rtc::scoped_refptr<DataChannelInterface> data_channel) { 148 RTCDataChannel *dataChannel = 149 [[RTCDataChannel alloc] initWithNativeDataChannel:data_channel]; 150 RTCPeerConnection *peer_connection = peer_connection_; 151 [peer_connection.delegate peerConnection:peer_connection 152 didOpenDataChannel:dataChannel]; 153} 154 155void PeerConnectionDelegateAdapter::OnRenegotiationNeeded() { 156 RTCPeerConnection *peer_connection = peer_connection_; 157 [peer_connection.delegate peerConnectionShouldNegotiate:peer_connection]; 158} 159 160void PeerConnectionDelegateAdapter::OnIceConnectionChange( 161 PeerConnectionInterface::IceConnectionState new_state) { 162 RTCIceConnectionState state = 163 [[RTCPeerConnection class] iceConnectionStateForNativeState:new_state]; 164 RTCPeerConnection *peer_connection = peer_connection_; 165 [peer_connection.delegate peerConnection:peer_connection 166 didChangeIceConnectionState:state]; 167} 168 169void PeerConnectionDelegateAdapter::OnIceGatheringChange( 170 PeerConnectionInterface::IceGatheringState new_state) { 171 RTCIceGatheringState state = 172 [[RTCPeerConnection class] iceGatheringStateForNativeState:new_state]; 173 RTCPeerConnection *peer_connection = peer_connection_; 174 [peer_connection.delegate peerConnection:peer_connection 175 didChangeIceGatheringState:state]; 176} 177 178void PeerConnectionDelegateAdapter::OnIceCandidate( 179 const IceCandidateInterface *candidate) { 180 RTCIceCandidate *iceCandidate = 181 [[RTCIceCandidate alloc] initWithNativeCandidate:candidate]; 182 RTCPeerConnection *peer_connection = peer_connection_; 183 [peer_connection.delegate peerConnection:peer_connection 184 didGenerateIceCandidate:iceCandidate]; 185} 186 187void PeerConnectionDelegateAdapter::OnIceCandidatesRemoved( 188 const std::vector<cricket::Candidate>& candidates) { 189 NSMutableArray* ice_candidates = 190 [NSMutableArray arrayWithCapacity:candidates.size()]; 191 for (const auto& candidate : candidates) { 192 std::unique_ptr<JsepIceCandidate> candidate_wrapper( 193 new JsepIceCandidate(candidate.transport_name(), -1, candidate)); 194 RTCIceCandidate* ice_candidate = [[RTCIceCandidate alloc] 195 initWithNativeCandidate:candidate_wrapper.get()]; 196 [ice_candidates addObject:ice_candidate]; 197 } 198 RTCPeerConnection* peer_connection = peer_connection_; 199 [peer_connection.delegate peerConnection:peer_connection 200 didRemoveIceCandidates:ice_candidates]; 201} 202 203} // namespace webrtc 204 205 206@implementation RTCPeerConnection { 207 NSMutableArray<RTCMediaStream *> *_localStreams; 208 std::unique_ptr<webrtc::PeerConnectionDelegateAdapter> _observer; 209 rtc::scoped_refptr<webrtc::PeerConnectionInterface> _peerConnection; 210 std::unique_ptr<webrtc::MediaConstraints> _nativeConstraints; 211 BOOL _hasStartedRtcEventLog; 212} 213 214@synthesize delegate = _delegate; 215 216- (instancetype)initWithFactory:(RTCPeerConnectionFactory *)factory 217 configuration:(RTCConfiguration *)configuration 218 constraints:(RTCMediaConstraints *)constraints 219 delegate:(id<RTCPeerConnectionDelegate>)delegate { 220 NSParameterAssert(factory); 221 std::unique_ptr<webrtc::PeerConnectionInterface::RTCConfiguration> config( 222 [configuration createNativeConfiguration]); 223 if (!config) { 224 return nil; 225 } 226 if (self = [super init]) { 227 _observer.reset(new webrtc::PeerConnectionDelegateAdapter(self)); 228 _nativeConstraints = constraints.nativeConstraints; 229 CopyConstraintsIntoRtcConfiguration(_nativeConstraints.get(), 230 config.get()); 231 _peerConnection = 232 factory.nativeFactory->CreatePeerConnection(*config, 233 nullptr, 234 nullptr, 235 _observer.get()); 236 if (!_peerConnection) { 237 return nil; 238 } 239 _localStreams = [[NSMutableArray alloc] init]; 240 _delegate = delegate; 241 } 242 return self; 243} 244 245- (NSArray<RTCMediaStream *> *)localStreams { 246 return [_localStreams copy]; 247} 248 249- (RTCSessionDescription *)localDescription { 250 const webrtc::SessionDescriptionInterface *description = 251 _peerConnection->local_description(); 252 return description ? 253 [[RTCSessionDescription alloc] initWithNativeDescription:description] 254 : nil; 255} 256 257- (RTCSessionDescription *)remoteDescription { 258 const webrtc::SessionDescriptionInterface *description = 259 _peerConnection->remote_description(); 260 return description ? 261 [[RTCSessionDescription alloc] initWithNativeDescription:description] 262 : nil; 263} 264 265- (RTCSignalingState)signalingState { 266 return [[self class] 267 signalingStateForNativeState:_peerConnection->signaling_state()]; 268} 269 270- (RTCIceConnectionState)iceConnectionState { 271 return [[self class] iceConnectionStateForNativeState: 272 _peerConnection->ice_connection_state()]; 273} 274 275- (RTCIceGatheringState)iceGatheringState { 276 return [[self class] iceGatheringStateForNativeState: 277 _peerConnection->ice_gathering_state()]; 278} 279 280- (BOOL)setConfiguration:(RTCConfiguration *)configuration { 281 std::unique_ptr<webrtc::PeerConnectionInterface::RTCConfiguration> config( 282 [configuration createNativeConfiguration]); 283 if (!config) { 284 return NO; 285 } 286 CopyConstraintsIntoRtcConfiguration(_nativeConstraints.get(), 287 config.get()); 288 return _peerConnection->SetConfiguration(*config); 289} 290 291- (void)close { 292 _peerConnection->Close(); 293} 294 295- (void)addIceCandidate:(RTCIceCandidate *)candidate { 296 std::unique_ptr<const webrtc::IceCandidateInterface> iceCandidate( 297 candidate.nativeCandidate); 298 _peerConnection->AddIceCandidate(iceCandidate.get()); 299} 300 301- (void)removeIceCandidates:(NSArray<RTCIceCandidate *> *)iceCandidates { 302 std::vector<cricket::Candidate> candidates; 303 for (RTCIceCandidate *iceCandidate in iceCandidates) { 304 std::unique_ptr<const webrtc::IceCandidateInterface> candidate( 305 iceCandidate.nativeCandidate); 306 if (candidate) { 307 candidates.push_back(candidate->candidate()); 308 // Need to fill the transport name from the sdp_mid. 309 candidates.back().set_transport_name(candidate->sdp_mid()); 310 } 311 } 312 if (!candidates.empty()) { 313 _peerConnection->RemoveIceCandidates(candidates); 314 } 315} 316 317- (void)addStream:(RTCMediaStream *)stream { 318 if (!_peerConnection->AddStream(stream.nativeMediaStream)) { 319 RTCLogError(@"Failed to add stream: %@", stream); 320 return; 321 } 322 [_localStreams addObject:stream]; 323} 324 325- (void)removeStream:(RTCMediaStream *)stream { 326 _peerConnection->RemoveStream(stream.nativeMediaStream); 327 [_localStreams removeObject:stream]; 328} 329 330- (void)offerForConstraints:(RTCMediaConstraints *)constraints 331 completionHandler: 332 (void (^)(RTCSessionDescription *sessionDescription, 333 NSError *error))completionHandler { 334 rtc::scoped_refptr<webrtc::CreateSessionDescriptionObserverAdapter> 335 observer(new rtc::RefCountedObject 336 <webrtc::CreateSessionDescriptionObserverAdapter>(completionHandler)); 337 _peerConnection->CreateOffer(observer, constraints.nativeConstraints.get()); 338} 339 340- (void)answerForConstraints:(RTCMediaConstraints *)constraints 341 completionHandler: 342 (void (^)(RTCSessionDescription *sessionDescription, 343 NSError *error))completionHandler { 344 rtc::scoped_refptr<webrtc::CreateSessionDescriptionObserverAdapter> 345 observer(new rtc::RefCountedObject 346 <webrtc::CreateSessionDescriptionObserverAdapter>(completionHandler)); 347 _peerConnection->CreateAnswer(observer, constraints.nativeConstraints.get()); 348} 349 350- (void)setLocalDescription:(RTCSessionDescription *)sdp 351 completionHandler:(void (^)(NSError *error))completionHandler { 352 rtc::scoped_refptr<webrtc::SetSessionDescriptionObserverAdapter> observer( 353 new rtc::RefCountedObject<webrtc::SetSessionDescriptionObserverAdapter>( 354 completionHandler)); 355 _peerConnection->SetLocalDescription(observer, sdp.nativeDescription); 356} 357 358- (void)setRemoteDescription:(RTCSessionDescription *)sdp 359 completionHandler:(void (^)(NSError *error))completionHandler { 360 rtc::scoped_refptr<webrtc::SetSessionDescriptionObserverAdapter> observer( 361 new rtc::RefCountedObject<webrtc::SetSessionDescriptionObserverAdapter>( 362 completionHandler)); 363 _peerConnection->SetRemoteDescription(observer, sdp.nativeDescription); 364} 365 366- (BOOL)startRtcEventLogWithFilePath:(NSString *)filePath 367 maxSizeInBytes:(int64_t)maxSizeInBytes { 368 RTC_DCHECK(filePath.length); 369 RTC_DCHECK_GT(maxSizeInBytes, 0); 370 RTC_DCHECK(!_hasStartedRtcEventLog); 371 if (_hasStartedRtcEventLog) { 372 RTCLogError(@"Event logging already started."); 373 return NO; 374 } 375 int fd = open(filePath.UTF8String, O_WRONLY | O_CREAT | O_TRUNC, 376 S_IRUSR | S_IWUSR); 377 if (fd < 0) { 378 RTCLogError(@"Error opening file: %@. Error: %d", filePath, errno); 379 return NO; 380 } 381 _hasStartedRtcEventLog = 382 _peerConnection->StartRtcEventLog(fd, maxSizeInBytes); 383 return _hasStartedRtcEventLog; 384} 385 386- (void)stopRtcEventLog { 387 _peerConnection->StopRtcEventLog(); 388 _hasStartedRtcEventLog = NO; 389} 390 391- (RTCRtpSender *)senderWithKind:(NSString *)kind 392 streamId:(NSString *)streamId { 393 std::string nativeKind = [NSString stdStringForString:kind]; 394 std::string nativeStreamId = [NSString stdStringForString:streamId]; 395 rtc::scoped_refptr<webrtc::RtpSenderInterface> nativeSender( 396 _peerConnection->CreateSender(nativeKind, nativeStreamId)); 397 return nativeSender ? 398 [[RTCRtpSender alloc] initWithNativeRtpSender:nativeSender] 399 : nil; 400} 401 402- (NSArray<RTCRtpSender *> *)senders { 403 std::vector<rtc::scoped_refptr<webrtc::RtpSenderInterface>> nativeSenders( 404 _peerConnection->GetSenders()); 405 NSMutableArray *senders = [[NSMutableArray alloc] init]; 406 for (const auto &nativeSender : nativeSenders) { 407 RTCRtpSender *sender = 408 [[RTCRtpSender alloc] initWithNativeRtpSender:nativeSender]; 409 [senders addObject:sender]; 410 } 411 return senders; 412} 413 414- (NSArray<RTCRtpReceiver *> *)receivers { 415 std::vector<rtc::scoped_refptr<webrtc::RtpReceiverInterface>> nativeReceivers( 416 _peerConnection->GetReceivers()); 417 NSMutableArray *receivers = [[NSMutableArray alloc] init]; 418 for (const auto &nativeReceiver : nativeReceivers) { 419 RTCRtpReceiver *receiver = 420 [[RTCRtpReceiver alloc] initWithNativeRtpReceiver:nativeReceiver]; 421 [receivers addObject:receiver]; 422 } 423 return receivers; 424} 425 426#pragma mark - Private 427 428+ (webrtc::PeerConnectionInterface::SignalingState)nativeSignalingStateForState: 429 (RTCSignalingState)state { 430 switch (state) { 431 case RTCSignalingStateStable: 432 return webrtc::PeerConnectionInterface::kStable; 433 case RTCSignalingStateHaveLocalOffer: 434 return webrtc::PeerConnectionInterface::kHaveLocalOffer; 435 case RTCSignalingStateHaveLocalPrAnswer: 436 return webrtc::PeerConnectionInterface::kHaveLocalPrAnswer; 437 case RTCSignalingStateHaveRemoteOffer: 438 return webrtc::PeerConnectionInterface::kHaveRemoteOffer; 439 case RTCSignalingStateHaveRemotePrAnswer: 440 return webrtc::PeerConnectionInterface::kHaveRemotePrAnswer; 441 case RTCSignalingStateClosed: 442 return webrtc::PeerConnectionInterface::kClosed; 443 } 444} 445 446+ (RTCSignalingState)signalingStateForNativeState: 447 (webrtc::PeerConnectionInterface::SignalingState)nativeState { 448 switch (nativeState) { 449 case webrtc::PeerConnectionInterface::kStable: 450 return RTCSignalingStateStable; 451 case webrtc::PeerConnectionInterface::kHaveLocalOffer: 452 return RTCSignalingStateHaveLocalOffer; 453 case webrtc::PeerConnectionInterface::kHaveLocalPrAnswer: 454 return RTCSignalingStateHaveLocalPrAnswer; 455 case webrtc::PeerConnectionInterface::kHaveRemoteOffer: 456 return RTCSignalingStateHaveRemoteOffer; 457 case webrtc::PeerConnectionInterface::kHaveRemotePrAnswer: 458 return RTCSignalingStateHaveRemotePrAnswer; 459 case webrtc::PeerConnectionInterface::kClosed: 460 return RTCSignalingStateClosed; 461 } 462} 463 464+ (NSString *)stringForSignalingState:(RTCSignalingState)state { 465 switch (state) { 466 case RTCSignalingStateStable: 467 return @"STABLE"; 468 case RTCSignalingStateHaveLocalOffer: 469 return @"HAVE_LOCAL_OFFER"; 470 case RTCSignalingStateHaveLocalPrAnswer: 471 return @"HAVE_LOCAL_PRANSWER"; 472 case RTCSignalingStateHaveRemoteOffer: 473 return @"HAVE_REMOTE_OFFER"; 474 case RTCSignalingStateHaveRemotePrAnswer: 475 return @"HAVE_REMOTE_PRANSWER"; 476 case RTCSignalingStateClosed: 477 return @"CLOSED"; 478 } 479} 480 481+ (webrtc::PeerConnectionInterface::IceConnectionState) 482 nativeIceConnectionStateForState:(RTCIceConnectionState)state { 483 switch (state) { 484 case RTCIceConnectionStateNew: 485 return webrtc::PeerConnectionInterface::kIceConnectionNew; 486 case RTCIceConnectionStateChecking: 487 return webrtc::PeerConnectionInterface::kIceConnectionChecking; 488 case RTCIceConnectionStateConnected: 489 return webrtc::PeerConnectionInterface::kIceConnectionConnected; 490 case RTCIceConnectionStateCompleted: 491 return webrtc::PeerConnectionInterface::kIceConnectionCompleted; 492 case RTCIceConnectionStateFailed: 493 return webrtc::PeerConnectionInterface::kIceConnectionFailed; 494 case RTCIceConnectionStateDisconnected: 495 return webrtc::PeerConnectionInterface::kIceConnectionDisconnected; 496 case RTCIceConnectionStateClosed: 497 return webrtc::PeerConnectionInterface::kIceConnectionClosed; 498 case RTCIceConnectionStateCount: 499 return webrtc::PeerConnectionInterface::kIceConnectionMax; 500 } 501} 502 503+ (RTCIceConnectionState)iceConnectionStateForNativeState: 504 (webrtc::PeerConnectionInterface::IceConnectionState)nativeState { 505 switch (nativeState) { 506 case webrtc::PeerConnectionInterface::kIceConnectionNew: 507 return RTCIceConnectionStateNew; 508 case webrtc::PeerConnectionInterface::kIceConnectionChecking: 509 return RTCIceConnectionStateChecking; 510 case webrtc::PeerConnectionInterface::kIceConnectionConnected: 511 return RTCIceConnectionStateConnected; 512 case webrtc::PeerConnectionInterface::kIceConnectionCompleted: 513 return RTCIceConnectionStateCompleted; 514 case webrtc::PeerConnectionInterface::kIceConnectionFailed: 515 return RTCIceConnectionStateFailed; 516 case webrtc::PeerConnectionInterface::kIceConnectionDisconnected: 517 return RTCIceConnectionStateDisconnected; 518 case webrtc::PeerConnectionInterface::kIceConnectionClosed: 519 return RTCIceConnectionStateClosed; 520 case webrtc::PeerConnectionInterface::kIceConnectionMax: 521 return RTCIceConnectionStateCount; 522 } 523} 524 525+ (NSString *)stringForIceConnectionState:(RTCIceConnectionState)state { 526 switch (state) { 527 case RTCIceConnectionStateNew: 528 return @"NEW"; 529 case RTCIceConnectionStateChecking: 530 return @"CHECKING"; 531 case RTCIceConnectionStateConnected: 532 return @"CONNECTED"; 533 case RTCIceConnectionStateCompleted: 534 return @"COMPLETED"; 535 case RTCIceConnectionStateFailed: 536 return @"FAILED"; 537 case RTCIceConnectionStateDisconnected: 538 return @"DISCONNECTED"; 539 case RTCIceConnectionStateClosed: 540 return @"CLOSED"; 541 case RTCIceConnectionStateCount: 542 return @"COUNT"; 543 } 544} 545 546+ (webrtc::PeerConnectionInterface::IceGatheringState) 547 nativeIceGatheringStateForState:(RTCIceGatheringState)state { 548 switch (state) { 549 case RTCIceGatheringStateNew: 550 return webrtc::PeerConnectionInterface::kIceGatheringNew; 551 case RTCIceGatheringStateGathering: 552 return webrtc::PeerConnectionInterface::kIceGatheringGathering; 553 case RTCIceGatheringStateComplete: 554 return webrtc::PeerConnectionInterface::kIceGatheringComplete; 555 } 556} 557 558+ (RTCIceGatheringState)iceGatheringStateForNativeState: 559 (webrtc::PeerConnectionInterface::IceGatheringState)nativeState { 560 switch (nativeState) { 561 case webrtc::PeerConnectionInterface::kIceGatheringNew: 562 return RTCIceGatheringStateNew; 563 case webrtc::PeerConnectionInterface::kIceGatheringGathering: 564 return RTCIceGatheringStateGathering; 565 case webrtc::PeerConnectionInterface::kIceGatheringComplete: 566 return RTCIceGatheringStateComplete; 567 } 568} 569 570+ (NSString *)stringForIceGatheringState:(RTCIceGatheringState)state { 571 switch (state) { 572 case RTCIceGatheringStateNew: 573 return @"NEW"; 574 case RTCIceGatheringStateGathering: 575 return @"GATHERING"; 576 case RTCIceGatheringStateComplete: 577 return @"COMPLETE"; 578 } 579} 580 581+ (webrtc::PeerConnectionInterface::StatsOutputLevel) 582 nativeStatsOutputLevelForLevel:(RTCStatsOutputLevel)level { 583 switch (level) { 584 case RTCStatsOutputLevelStandard: 585 return webrtc::PeerConnectionInterface::kStatsOutputLevelStandard; 586 case RTCStatsOutputLevelDebug: 587 return webrtc::PeerConnectionInterface::kStatsOutputLevelDebug; 588 } 589} 590 591- (rtc::scoped_refptr<webrtc::PeerConnectionInterface>)nativePeerConnection { 592 return _peerConnection; 593} 594 595@end 596