/* * Copyright 2015 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #import "RTCPeerConnection+Private.h" #import "RTCConfiguration+Private.h" #import "RTCDataChannel+Private.h" #import "RTCIceCandidate+Private.h" #import "RTCLegacyStatsReport+Private.h" #import "RTCMediaConstraints+Private.h" #import "RTCMediaStream+Private.h" #import "RTCMediaStreamTrack+Private.h" #import "RTCPeerConnection+Native.h" #import "RTCPeerConnectionFactory+Private.h" #import "RTCRtpReceiver+Private.h" #import "RTCRtpSender+Private.h" #import "RTCRtpTransceiver+Private.h" #import "RTCSessionDescription+Private.h" #import "base/RTCLogging.h" #import "helpers/NSString+StdString.h" #include #include "api/jsepicecandidate.h" #include "rtc_base/checks.h" NSString * const kRTCPeerConnectionErrorDomain = @"org.webrtc.RTCPeerConnection"; int const kRTCPeerConnnectionSessionDescriptionError = -1; namespace webrtc { class CreateSessionDescriptionObserverAdapter : public CreateSessionDescriptionObserver { public: CreateSessionDescriptionObserverAdapter( void (^completionHandler)(RTCSessionDescription *sessionDescription, NSError *error)) { completion_handler_ = completionHandler; } ~CreateSessionDescriptionObserverAdapter() override { completion_handler_ = nil; } void OnSuccess(SessionDescriptionInterface *desc) override { RTC_DCHECK(completion_handler_); std::unique_ptr description = std::unique_ptr(desc); RTCSessionDescription* session = [[RTCSessionDescription alloc] initWithNativeDescription: description.get()]; completion_handler_(session, nil); completion_handler_ = nil; } void OnFailure(RTCError error) override { RTC_DCHECK(completion_handler_); // TODO(hta): Add handling of error.type() NSString *str = [NSString stringForStdString:error.message()]; NSError* err = [NSError errorWithDomain:kRTCPeerConnectionErrorDomain code:kRTCPeerConnnectionSessionDescriptionError userInfo:@{ NSLocalizedDescriptionKey : str }]; completion_handler_(nil, err); completion_handler_ = nil; } private: void (^completion_handler_) (RTCSessionDescription *sessionDescription, NSError *error); }; class SetSessionDescriptionObserverAdapter : public SetSessionDescriptionObserver { public: SetSessionDescriptionObserverAdapter(void (^completionHandler) (NSError *error)) { completion_handler_ = completionHandler; } ~SetSessionDescriptionObserverAdapter() override { completion_handler_ = nil; } void OnSuccess() override { RTC_DCHECK(completion_handler_); completion_handler_(nil); completion_handler_ = nil; } void OnFailure(RTCError error) override { RTC_DCHECK(completion_handler_); // TODO(hta): Add handling of error.type() NSString *str = [NSString stringForStdString:error.message()]; NSError* err = [NSError errorWithDomain:kRTCPeerConnectionErrorDomain code:kRTCPeerConnnectionSessionDescriptionError userInfo:@{ NSLocalizedDescriptionKey : str }]; completion_handler_(err); completion_handler_ = nil; } private: void (^completion_handler_)(NSError *error); }; PeerConnectionDelegateAdapter::PeerConnectionDelegateAdapter( RTCPeerConnection *peerConnection) { peer_connection_ = peerConnection; } PeerConnectionDelegateAdapter::~PeerConnectionDelegateAdapter() { peer_connection_ = nil; } void PeerConnectionDelegateAdapter::OnSignalingChange( PeerConnectionInterface::SignalingState new_state) { RTCSignalingState state = [[RTCPeerConnection class] signalingStateForNativeState:new_state]; RTCPeerConnection *peer_connection = peer_connection_; [peer_connection.delegate peerConnection:peer_connection didChangeSignalingState:state]; } void PeerConnectionDelegateAdapter::OnAddStream( rtc::scoped_refptr stream) { RTCPeerConnection *peer_connection = peer_connection_; RTCMediaStream *mediaStream = [[RTCMediaStream alloc] initWithFactory:peer_connection.factory nativeMediaStream:stream]; [peer_connection.delegate peerConnection:peer_connection didAddStream:mediaStream]; } void PeerConnectionDelegateAdapter::OnRemoveStream( rtc::scoped_refptr stream) { RTCPeerConnection *peer_connection = peer_connection_; RTCMediaStream *mediaStream = [[RTCMediaStream alloc] initWithFactory:peer_connection.factory nativeMediaStream:stream]; [peer_connection.delegate peerConnection:peer_connection didRemoveStream:mediaStream]; } void PeerConnectionDelegateAdapter::OnTrack( rtc::scoped_refptr nativeTransceiver) { RTCPeerConnection *peer_connection = peer_connection_; RTCRtpTransceiver *transceiver = [[RTCRtpTransceiver alloc] initWithFactory:peer_connection.factory nativeRtpTransceiver:nativeTransceiver]; if ([peer_connection.delegate respondsToSelector:@selector(peerConnection:didStartReceivingOnTransceiver:)]) { [peer_connection.delegate peerConnection:peer_connection didStartReceivingOnTransceiver:transceiver]; } } void PeerConnectionDelegateAdapter::OnDataChannel( rtc::scoped_refptr data_channel) { RTCPeerConnection *peer_connection = peer_connection_; RTCDataChannel *dataChannel = [[RTCDataChannel alloc] initWithFactory:peer_connection.factory nativeDataChannel:data_channel]; [peer_connection.delegate peerConnection:peer_connection didOpenDataChannel:dataChannel]; } void PeerConnectionDelegateAdapter::OnRenegotiationNeeded() { RTCPeerConnection *peer_connection = peer_connection_; [peer_connection.delegate peerConnectionShouldNegotiate:peer_connection]; } void PeerConnectionDelegateAdapter::OnIceConnectionChange( PeerConnectionInterface::IceConnectionState new_state) { RTCIceConnectionState state = [[RTCPeerConnection class] iceConnectionStateForNativeState:new_state]; RTCPeerConnection *peer_connection = peer_connection_; [peer_connection.delegate peerConnection:peer_connection didChangeIceConnectionState:state]; } void PeerConnectionDelegateAdapter::OnIceGatheringChange( PeerConnectionInterface::IceGatheringState new_state) { RTCIceGatheringState state = [[RTCPeerConnection class] iceGatheringStateForNativeState:new_state]; RTCPeerConnection *peer_connection = peer_connection_; [peer_connection.delegate peerConnection:peer_connection didChangeIceGatheringState:state]; } void PeerConnectionDelegateAdapter::OnIceCandidate( const IceCandidateInterface *candidate) { RTCIceCandidate *iceCandidate = [[RTCIceCandidate alloc] initWithNativeCandidate:candidate]; RTCPeerConnection *peer_connection = peer_connection_; [peer_connection.delegate peerConnection:peer_connection didGenerateIceCandidate:iceCandidate]; } void PeerConnectionDelegateAdapter::OnIceCandidatesRemoved( const std::vector& candidates) { NSMutableArray* ice_candidates = [NSMutableArray arrayWithCapacity:candidates.size()]; for (const auto& candidate : candidates) { std::unique_ptr candidate_wrapper( new JsepIceCandidate(candidate.transport_name(), -1, candidate)); RTCIceCandidate* ice_candidate = [[RTCIceCandidate alloc] initWithNativeCandidate:candidate_wrapper.get()]; [ice_candidates addObject:ice_candidate]; } RTCPeerConnection* peer_connection = peer_connection_; [peer_connection.delegate peerConnection:peer_connection didRemoveIceCandidates:ice_candidates]; } void PeerConnectionDelegateAdapter::OnAddTrack( rtc::scoped_refptr receiver, const std::vector>& streams) { RTCPeerConnection *peer_connection = peer_connection_; if ([peer_connection.delegate respondsToSelector:@selector(peerConnection:didAddReceiver:streams:)]) { NSMutableArray *mediaStreams = [NSMutableArray arrayWithCapacity:streams.size()]; for (const auto& nativeStream : streams) { RTCMediaStream *mediaStream = [[RTCMediaStream alloc] initWithFactory:peer_connection.factory nativeMediaStream:nativeStream]; [mediaStreams addObject:mediaStream]; } RTCRtpReceiver *rtpReceiver = [[RTCRtpReceiver alloc] initWithFactory:peer_connection.factory nativeRtpReceiver:receiver]; [peer_connection.delegate peerConnection:peer_connection didAddReceiver:rtpReceiver streams:mediaStreams]; } } void PeerConnectionDelegateAdapter::OnRemoveTrack( rtc::scoped_refptr receiver) { RTCPeerConnection *peer_connection = peer_connection_; if ([peer_connection.delegate respondsToSelector:@selector(peerConnection:didRemoveReceiver:)]) { RTCRtpReceiver *rtpReceiver = [[RTCRtpReceiver alloc] initWithFactory:peer_connection.factory nativeRtpReceiver:receiver]; [peer_connection.delegate peerConnection:peer_connection didRemoveReceiver:rtpReceiver]; } } } // namespace webrtc @implementation RTCPeerConnection { RTCPeerConnectionFactory *_factory; NSMutableArray *_localStreams; std::unique_ptr _observer; rtc::scoped_refptr _peerConnection; std::unique_ptr _nativeConstraints; BOOL _hasStartedRtcEventLog; } @synthesize delegate = _delegate; @synthesize factory = _factory; - (instancetype)initWithFactory:(RTCPeerConnectionFactory *)factory configuration:(RTCConfiguration *)configuration constraints:(RTCMediaConstraints *)constraints delegate:(id)delegate { NSParameterAssert(factory); std::unique_ptr config( [configuration createNativeConfiguration]); if (!config) { return nil; } if (self = [super init]) { _observer.reset(new webrtc::PeerConnectionDelegateAdapter(self)); _nativeConstraints = constraints.nativeConstraints; CopyConstraintsIntoRtcConfiguration(_nativeConstraints.get(), config.get()); _peerConnection = factory.nativeFactory->CreatePeerConnection(*config, nullptr, nullptr, _observer.get()); if (!_peerConnection) { return nil; } _factory = factory; _localStreams = [[NSMutableArray alloc] init]; _delegate = delegate; } return self; } - (NSArray *)localStreams { return [_localStreams copy]; } - (RTCSessionDescription *)localDescription { const webrtc::SessionDescriptionInterface *description = _peerConnection->local_description(); return description ? [[RTCSessionDescription alloc] initWithNativeDescription:description] : nil; } - (RTCSessionDescription *)remoteDescription { const webrtc::SessionDescriptionInterface *description = _peerConnection->remote_description(); return description ? [[RTCSessionDescription alloc] initWithNativeDescription:description] : nil; } - (RTCSignalingState)signalingState { return [[self class] signalingStateForNativeState:_peerConnection->signaling_state()]; } - (RTCIceConnectionState)iceConnectionState { return [[self class] iceConnectionStateForNativeState: _peerConnection->ice_connection_state()]; } - (RTCIceGatheringState)iceGatheringState { return [[self class] iceGatheringStateForNativeState: _peerConnection->ice_gathering_state()]; } - (BOOL)setConfiguration:(RTCConfiguration *)configuration { std::unique_ptr config( [configuration createNativeConfiguration]); if (!config) { return NO; } CopyConstraintsIntoRtcConfiguration(_nativeConstraints.get(), config.get()); return _peerConnection->SetConfiguration(*config); } - (RTCConfiguration *)configuration { webrtc::PeerConnectionInterface::RTCConfiguration config = _peerConnection->GetConfiguration(); return [[RTCConfiguration alloc] initWithNativeConfiguration:config]; } - (void)close { _peerConnection->Close(); } - (void)addIceCandidate:(RTCIceCandidate *)candidate { std::unique_ptr iceCandidate( candidate.nativeCandidate); _peerConnection->AddIceCandidate(iceCandidate.get()); } - (void)removeIceCandidates:(NSArray *)iceCandidates { std::vector candidates; for (RTCIceCandidate *iceCandidate in iceCandidates) { std::unique_ptr candidate( iceCandidate.nativeCandidate); if (candidate) { candidates.push_back(candidate->candidate()); // Need to fill the transport name from the sdp_mid. candidates.back().set_transport_name(candidate->sdp_mid()); } } if (!candidates.empty()) { _peerConnection->RemoveIceCandidates(candidates); } } - (void)addStream:(RTCMediaStream *)stream { if (!_peerConnection->AddStream(stream.nativeMediaStream)) { RTCLogError(@"Failed to add stream: %@", stream); return; } [_localStreams addObject:stream]; } - (void)removeStream:(RTCMediaStream *)stream { _peerConnection->RemoveStream(stream.nativeMediaStream); [_localStreams removeObject:stream]; } - (RTCRtpSender *)addTrack:(RTCMediaStreamTrack *)track streamIds:(NSArray *)streamIds { std::vector nativeStreamIds; for (NSString *streamId in streamIds) { nativeStreamIds.push_back([streamId UTF8String]); } webrtc::RTCErrorOr> nativeSenderOrError = _peerConnection->AddTrack(track.nativeTrack, nativeStreamIds); if (!nativeSenderOrError.ok()) { RTCLogError(@"Failed to add track %@: %s", track, nativeSenderOrError.error().message()); return nil; } return [[RTCRtpSender alloc] initWithFactory:self.factory nativeRtpSender:nativeSenderOrError.MoveValue()]; } - (BOOL)removeTrack:(RTCRtpSender *)sender { bool result = _peerConnection->RemoveTrack(sender.nativeRtpSender); if (!result) { RTCLogError(@"Failed to remote track %@", sender); } return result; } - (RTCRtpTransceiver *)addTransceiverWithTrack:(RTCMediaStreamTrack *)track { return [self addTransceiverWithTrack:track init:[[RTCRtpTransceiverInit alloc] init]]; } - (RTCRtpTransceiver *)addTransceiverWithTrack:(RTCMediaStreamTrack *)track init:(RTCRtpTransceiverInit *)init { webrtc::RTCErrorOr> nativeTransceiverOrError = _peerConnection->AddTransceiver(track.nativeTrack, init.nativeInit); if (!nativeTransceiverOrError.ok()) { RTCLogError( @"Failed to add transceiver %@: %s", track, nativeTransceiverOrError.error().message()); return nil; } return [[RTCRtpTransceiver alloc] initWithFactory:self.factory nativeRtpTransceiver:nativeTransceiverOrError.MoveValue()]; } - (RTCRtpTransceiver *)addTransceiverOfType:(RTCRtpMediaType)mediaType { return [self addTransceiverOfType:mediaType init:[[RTCRtpTransceiverInit alloc] init]]; } - (RTCRtpTransceiver *)addTransceiverOfType:(RTCRtpMediaType)mediaType init:(RTCRtpTransceiverInit *)init { webrtc::RTCErrorOr> nativeTransceiverOrError = _peerConnection->AddTransceiver([RTCRtpReceiver nativeMediaTypeForMediaType:mediaType], init.nativeInit); if (!nativeTransceiverOrError.ok()) { RTCLogError(@"Failed to add transceiver %@: %s", [RTCRtpReceiver stringForMediaType:mediaType], nativeTransceiverOrError.error().message()); return nil; } return [[RTCRtpTransceiver alloc] initWithFactory:self.factory nativeRtpTransceiver:nativeTransceiverOrError.MoveValue()]; } - (void)offerForConstraints:(RTCMediaConstraints *)constraints completionHandler: (void (^)(RTCSessionDescription *sessionDescription, NSError *error))completionHandler { rtc::scoped_refptr observer(new rtc::RefCountedObject (completionHandler)); webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options; CopyConstraintsIntoOfferAnswerOptions(constraints.nativeConstraints.get(), &options); _peerConnection->CreateOffer(observer, options); } - (void)answerForConstraints:(RTCMediaConstraints *)constraints completionHandler: (void (^)(RTCSessionDescription *sessionDescription, NSError *error))completionHandler { rtc::scoped_refptr observer(new rtc::RefCountedObject (completionHandler)); webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options; CopyConstraintsIntoOfferAnswerOptions(constraints.nativeConstraints.get(), &options); _peerConnection->CreateAnswer(observer, options); } - (void)setLocalDescription:(RTCSessionDescription *)sdp completionHandler:(void (^)(NSError *error))completionHandler { rtc::scoped_refptr observer( new rtc::RefCountedObject( completionHandler)); _peerConnection->SetLocalDescription(observer, sdp.nativeDescription); } - (void)setRemoteDescription:(RTCSessionDescription *)sdp completionHandler:(void (^)(NSError *error))completionHandler { rtc::scoped_refptr observer( new rtc::RefCountedObject( completionHandler)); _peerConnection->SetRemoteDescription(observer, sdp.nativeDescription); } - (BOOL)setBweMinBitrateBps:(nullable NSNumber *)minBitrateBps currentBitrateBps:(nullable NSNumber *)currentBitrateBps maxBitrateBps:(nullable NSNumber *)maxBitrateBps { webrtc::PeerConnectionInterface::BitrateParameters params; if (minBitrateBps != nil) { params.min_bitrate_bps = absl::optional(minBitrateBps.intValue); } if (currentBitrateBps != nil) { params.current_bitrate_bps = absl::optional(currentBitrateBps.intValue); } if (maxBitrateBps != nil) { params.max_bitrate_bps = absl::optional(maxBitrateBps.intValue); } return _peerConnection->SetBitrate(params).ok(); } - (void)setBitrateAllocationStrategy: (std::unique_ptr)bitrateAllocationStrategy { _peerConnection->SetBitrateAllocationStrategy(std::move(bitrateAllocationStrategy)); } - (BOOL)startRtcEventLogWithFilePath:(NSString *)filePath maxSizeInBytes:(int64_t)maxSizeInBytes { RTC_DCHECK(filePath.length); RTC_DCHECK_GT(maxSizeInBytes, 0); RTC_DCHECK(!_hasStartedRtcEventLog); if (_hasStartedRtcEventLog) { RTCLogError(@"Event logging already started."); return NO; } int fd = open(filePath.UTF8String, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (fd < 0) { RTCLogError(@"Error opening file: %@. Error: %d", filePath, errno); return NO; } _hasStartedRtcEventLog = _peerConnection->StartRtcEventLog(fd, maxSizeInBytes); return _hasStartedRtcEventLog; } - (void)stopRtcEventLog { _peerConnection->StopRtcEventLog(); _hasStartedRtcEventLog = NO; } - (RTCRtpSender *)senderWithKind:(NSString *)kind streamId:(NSString *)streamId { std::string nativeKind = [NSString stdStringForString:kind]; std::string nativeStreamId = [NSString stdStringForString:streamId]; rtc::scoped_refptr nativeSender( _peerConnection->CreateSender(nativeKind, nativeStreamId)); return nativeSender ? [[RTCRtpSender alloc] initWithFactory:self.factory nativeRtpSender:nativeSender] : nil; } - (NSArray *)senders { std::vector> nativeSenders( _peerConnection->GetSenders()); NSMutableArray *senders = [[NSMutableArray alloc] init]; for (const auto &nativeSender : nativeSenders) { RTCRtpSender *sender = [[RTCRtpSender alloc] initWithFactory:self.factory nativeRtpSender:nativeSender]; [senders addObject:sender]; } return senders; } - (NSArray *)receivers { std::vector> nativeReceivers( _peerConnection->GetReceivers()); NSMutableArray *receivers = [[NSMutableArray alloc] init]; for (const auto &nativeReceiver : nativeReceivers) { RTCRtpReceiver *receiver = [[RTCRtpReceiver alloc] initWithFactory:self.factory nativeRtpReceiver:nativeReceiver]; [receivers addObject:receiver]; } return receivers; } - (NSArray *)transceivers { std::vector> nativeTransceivers( _peerConnection->GetTransceivers()); NSMutableArray *transceivers = [[NSMutableArray alloc] init]; for (auto nativeTransceiver : nativeTransceivers) { RTCRtpTransceiver *transceiver = [[RTCRtpTransceiver alloc] initWithFactory:self.factory nativeRtpTransceiver:nativeTransceiver]; [transceivers addObject:transceiver]; } return transceivers; } #pragma mark - Private + (webrtc::PeerConnectionInterface::SignalingState)nativeSignalingStateForState: (RTCSignalingState)state { switch (state) { case RTCSignalingStateStable: return webrtc::PeerConnectionInterface::kStable; case RTCSignalingStateHaveLocalOffer: return webrtc::PeerConnectionInterface::kHaveLocalOffer; case RTCSignalingStateHaveLocalPrAnswer: return webrtc::PeerConnectionInterface::kHaveLocalPrAnswer; case RTCSignalingStateHaveRemoteOffer: return webrtc::PeerConnectionInterface::kHaveRemoteOffer; case RTCSignalingStateHaveRemotePrAnswer: return webrtc::PeerConnectionInterface::kHaveRemotePrAnswer; case RTCSignalingStateClosed: return webrtc::PeerConnectionInterface::kClosed; } } + (RTCSignalingState)signalingStateForNativeState: (webrtc::PeerConnectionInterface::SignalingState)nativeState { switch (nativeState) { case webrtc::PeerConnectionInterface::kStable: return RTCSignalingStateStable; case webrtc::PeerConnectionInterface::kHaveLocalOffer: return RTCSignalingStateHaveLocalOffer; case webrtc::PeerConnectionInterface::kHaveLocalPrAnswer: return RTCSignalingStateHaveLocalPrAnswer; case webrtc::PeerConnectionInterface::kHaveRemoteOffer: return RTCSignalingStateHaveRemoteOffer; case webrtc::PeerConnectionInterface::kHaveRemotePrAnswer: return RTCSignalingStateHaveRemotePrAnswer; case webrtc::PeerConnectionInterface::kClosed: return RTCSignalingStateClosed; } } + (NSString *)stringForSignalingState:(RTCSignalingState)state { switch (state) { case RTCSignalingStateStable: return @"STABLE"; case RTCSignalingStateHaveLocalOffer: return @"HAVE_LOCAL_OFFER"; case RTCSignalingStateHaveLocalPrAnswer: return @"HAVE_LOCAL_PRANSWER"; case RTCSignalingStateHaveRemoteOffer: return @"HAVE_REMOTE_OFFER"; case RTCSignalingStateHaveRemotePrAnswer: return @"HAVE_REMOTE_PRANSWER"; case RTCSignalingStateClosed: return @"CLOSED"; } } + (webrtc::PeerConnectionInterface::IceConnectionState) nativeIceConnectionStateForState:(RTCIceConnectionState)state { switch (state) { case RTCIceConnectionStateNew: return webrtc::PeerConnectionInterface::kIceConnectionNew; case RTCIceConnectionStateChecking: return webrtc::PeerConnectionInterface::kIceConnectionChecking; case RTCIceConnectionStateConnected: return webrtc::PeerConnectionInterface::kIceConnectionConnected; case RTCIceConnectionStateCompleted: return webrtc::PeerConnectionInterface::kIceConnectionCompleted; case RTCIceConnectionStateFailed: return webrtc::PeerConnectionInterface::kIceConnectionFailed; case RTCIceConnectionStateDisconnected: return webrtc::PeerConnectionInterface::kIceConnectionDisconnected; case RTCIceConnectionStateClosed: return webrtc::PeerConnectionInterface::kIceConnectionClosed; case RTCIceConnectionStateCount: return webrtc::PeerConnectionInterface::kIceConnectionMax; } } + (RTCIceConnectionState)iceConnectionStateForNativeState: (webrtc::PeerConnectionInterface::IceConnectionState)nativeState { switch (nativeState) { case webrtc::PeerConnectionInterface::kIceConnectionNew: return RTCIceConnectionStateNew; case webrtc::PeerConnectionInterface::kIceConnectionChecking: return RTCIceConnectionStateChecking; case webrtc::PeerConnectionInterface::kIceConnectionConnected: return RTCIceConnectionStateConnected; case webrtc::PeerConnectionInterface::kIceConnectionCompleted: return RTCIceConnectionStateCompleted; case webrtc::PeerConnectionInterface::kIceConnectionFailed: return RTCIceConnectionStateFailed; case webrtc::PeerConnectionInterface::kIceConnectionDisconnected: return RTCIceConnectionStateDisconnected; case webrtc::PeerConnectionInterface::kIceConnectionClosed: return RTCIceConnectionStateClosed; case webrtc::PeerConnectionInterface::kIceConnectionMax: return RTCIceConnectionStateCount; } } + (NSString *)stringForIceConnectionState:(RTCIceConnectionState)state { switch (state) { case RTCIceConnectionStateNew: return @"NEW"; case RTCIceConnectionStateChecking: return @"CHECKING"; case RTCIceConnectionStateConnected: return @"CONNECTED"; case RTCIceConnectionStateCompleted: return @"COMPLETED"; case RTCIceConnectionStateFailed: return @"FAILED"; case RTCIceConnectionStateDisconnected: return @"DISCONNECTED"; case RTCIceConnectionStateClosed: return @"CLOSED"; case RTCIceConnectionStateCount: return @"COUNT"; } } + (webrtc::PeerConnectionInterface::IceGatheringState) nativeIceGatheringStateForState:(RTCIceGatheringState)state { switch (state) { case RTCIceGatheringStateNew: return webrtc::PeerConnectionInterface::kIceGatheringNew; case RTCIceGatheringStateGathering: return webrtc::PeerConnectionInterface::kIceGatheringGathering; case RTCIceGatheringStateComplete: return webrtc::PeerConnectionInterface::kIceGatheringComplete; } } + (RTCIceGatheringState)iceGatheringStateForNativeState: (webrtc::PeerConnectionInterface::IceGatheringState)nativeState { switch (nativeState) { case webrtc::PeerConnectionInterface::kIceGatheringNew: return RTCIceGatheringStateNew; case webrtc::PeerConnectionInterface::kIceGatheringGathering: return RTCIceGatheringStateGathering; case webrtc::PeerConnectionInterface::kIceGatheringComplete: return RTCIceGatheringStateComplete; } } + (NSString *)stringForIceGatheringState:(RTCIceGatheringState)state { switch (state) { case RTCIceGatheringStateNew: return @"NEW"; case RTCIceGatheringStateGathering: return @"GATHERING"; case RTCIceGatheringStateComplete: return @"COMPLETE"; } } + (webrtc::PeerConnectionInterface::StatsOutputLevel) nativeStatsOutputLevelForLevel:(RTCStatsOutputLevel)level { switch (level) { case RTCStatsOutputLevelStandard: return webrtc::PeerConnectionInterface::kStatsOutputLevelStandard; case RTCStatsOutputLevelDebug: return webrtc::PeerConnectionInterface::kStatsOutputLevelDebug; } } - (rtc::scoped_refptr)nativePeerConnection { return _peerConnection; } @end