| /* | 
 |  *  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 "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 <memory> | 
 |  | 
 | #include "api/jsep_ice_candidate.h" | 
 | #include "api/rtc_event_log_output_file.h" | 
 | #include "rtc_base/checks.h" | 
 | #include "rtc_base/numerics/safe_conversions.h" | 
 |  | 
 | NSString *const kRTCPeerConnectionErrorDomain = @"org.webrtc.RTC_OBJC_TYPE(RTCPeerConnection)"; | 
 | int const kRTCPeerConnnectionSessionDescriptionError = -1; | 
 |  | 
 | namespace webrtc { | 
 |  | 
 | class CreateSessionDescriptionObserverAdapter | 
 |     : public CreateSessionDescriptionObserver { | 
 |  public: | 
 |   CreateSessionDescriptionObserverAdapter(void (^completionHandler)( | 
 |       RTC_OBJC_TYPE(RTCSessionDescription) * sessionDescription, NSError *error)) { | 
 |     completion_handler_ = completionHandler; | 
 |   } | 
 |  | 
 |   ~CreateSessionDescriptionObserverAdapter() override { completion_handler_ = nil; } | 
 |  | 
 |   void OnSuccess(SessionDescriptionInterface *desc) override { | 
 |     RTC_DCHECK(completion_handler_); | 
 |     std::unique_ptr<webrtc::SessionDescriptionInterface> description = | 
 |         std::unique_ptr<webrtc::SessionDescriptionInterface>(desc); | 
 |     RTC_OBJC_TYPE(RTCSessionDescription) *session = | 
 |         [[RTC_OBJC_TYPE(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_)(RTC_OBJC_TYPE(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(RTC_OBJC_TYPE(RTCPeerConnection) * | 
 |                                                              peerConnection) { | 
 |   peer_connection_ = peerConnection; | 
 | } | 
 |  | 
 | PeerConnectionDelegateAdapter::~PeerConnectionDelegateAdapter() { | 
 |   peer_connection_ = nil; | 
 | } | 
 |  | 
 | void PeerConnectionDelegateAdapter::OnSignalingChange( | 
 |     PeerConnectionInterface::SignalingState new_state) { | 
 |   RTCSignalingState state = | 
 |       [[RTC_OBJC_TYPE(RTCPeerConnection) class] signalingStateForNativeState:new_state]; | 
 |   RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; | 
 |   [peer_connection.delegate peerConnection:peer_connection | 
 |                    didChangeSignalingState:state]; | 
 | } | 
 |  | 
 | void PeerConnectionDelegateAdapter::OnAddStream( | 
 |     rtc::scoped_refptr<MediaStreamInterface> stream) { | 
 |   RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; | 
 |   RTC_OBJC_TYPE(RTCMediaStream) *mediaStream = | 
 |       [[RTC_OBJC_TYPE(RTCMediaStream) alloc] initWithFactory:peer_connection.factory | 
 |                                            nativeMediaStream:stream]; | 
 |   [peer_connection.delegate peerConnection:peer_connection | 
 |                               didAddStream:mediaStream]; | 
 | } | 
 |  | 
 | void PeerConnectionDelegateAdapter::OnRemoveStream( | 
 |     rtc::scoped_refptr<MediaStreamInterface> stream) { | 
 |   RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; | 
 |   RTC_OBJC_TYPE(RTCMediaStream) *mediaStream = | 
 |       [[RTC_OBJC_TYPE(RTCMediaStream) alloc] initWithFactory:peer_connection.factory | 
 |                                            nativeMediaStream:stream]; | 
 |  | 
 |   [peer_connection.delegate peerConnection:peer_connection | 
 |                            didRemoveStream:mediaStream]; | 
 | } | 
 |  | 
 | void PeerConnectionDelegateAdapter::OnTrack( | 
 |     rtc::scoped_refptr<RtpTransceiverInterface> nativeTransceiver) { | 
 |   RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; | 
 |   RTC_OBJC_TYPE(RTCRtpTransceiver) *transceiver = | 
 |       [[RTC_OBJC_TYPE(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<DataChannelInterface> data_channel) { | 
 |   RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; | 
 |   RTC_OBJC_TYPE(RTCDataChannel) *dataChannel = | 
 |       [[RTC_OBJC_TYPE(RTCDataChannel) alloc] initWithFactory:peer_connection.factory | 
 |                                            nativeDataChannel:data_channel]; | 
 |   [peer_connection.delegate peerConnection:peer_connection | 
 |                         didOpenDataChannel:dataChannel]; | 
 | } | 
 |  | 
 | void PeerConnectionDelegateAdapter::OnRenegotiationNeeded() { | 
 |   RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; | 
 |   [peer_connection.delegate peerConnectionShouldNegotiate:peer_connection]; | 
 | } | 
 |  | 
 | void PeerConnectionDelegateAdapter::OnIceConnectionChange( | 
 |     PeerConnectionInterface::IceConnectionState new_state) { | 
 |   RTCIceConnectionState state = | 
 |       [RTC_OBJC_TYPE(RTCPeerConnection) iceConnectionStateForNativeState:new_state]; | 
 |   [peer_connection_.delegate peerConnection:peer_connection_ didChangeIceConnectionState:state]; | 
 | } | 
 |  | 
 | void PeerConnectionDelegateAdapter::OnStandardizedIceConnectionChange( | 
 |     PeerConnectionInterface::IceConnectionState new_state) { | 
 |   if ([peer_connection_.delegate | 
 |           respondsToSelector:@selector(peerConnection:didChangeStandardizedIceConnectionState:)]) { | 
 |     RTCIceConnectionState state = | 
 |         [RTC_OBJC_TYPE(RTCPeerConnection) iceConnectionStateForNativeState:new_state]; | 
 |     [peer_connection_.delegate peerConnection:peer_connection_ | 
 |         didChangeStandardizedIceConnectionState:state]; | 
 |   } | 
 | } | 
 |  | 
 | void PeerConnectionDelegateAdapter::OnConnectionChange( | 
 |     PeerConnectionInterface::PeerConnectionState new_state) { | 
 |   if ([peer_connection_.delegate | 
 |           respondsToSelector:@selector(peerConnection:didChangeConnectionState:)]) { | 
 |     RTCPeerConnectionState state = | 
 |         [RTC_OBJC_TYPE(RTCPeerConnection) connectionStateForNativeState:new_state]; | 
 |     [peer_connection_.delegate peerConnection:peer_connection_ didChangeConnectionState:state]; | 
 |   } | 
 | } | 
 |  | 
 | void PeerConnectionDelegateAdapter::OnIceGatheringChange( | 
 |     PeerConnectionInterface::IceGatheringState new_state) { | 
 |   RTCIceGatheringState state = | 
 |       [[RTC_OBJC_TYPE(RTCPeerConnection) class] iceGatheringStateForNativeState:new_state]; | 
 |   RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; | 
 |   [peer_connection.delegate peerConnection:peer_connection | 
 |                 didChangeIceGatheringState:state]; | 
 | } | 
 |  | 
 | void PeerConnectionDelegateAdapter::OnIceCandidate( | 
 |     const IceCandidateInterface *candidate) { | 
 |   RTC_OBJC_TYPE(RTCIceCandidate) *iceCandidate = | 
 |       [[RTC_OBJC_TYPE(RTCIceCandidate) alloc] initWithNativeCandidate:candidate]; | 
 |   RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; | 
 |   [peer_connection.delegate peerConnection:peer_connection | 
 |                    didGenerateIceCandidate:iceCandidate]; | 
 | } | 
 |  | 
 | void PeerConnectionDelegateAdapter::OnIceCandidatesRemoved( | 
 |     const std::vector<cricket::Candidate>& candidates) { | 
 |   NSMutableArray* ice_candidates = | 
 |       [NSMutableArray arrayWithCapacity:candidates.size()]; | 
 |   for (const auto& candidate : candidates) { | 
 |     std::unique_ptr<JsepIceCandidate> candidate_wrapper( | 
 |         new JsepIceCandidate(candidate.transport_name(), -1, candidate)); | 
 |     RTC_OBJC_TYPE(RTCIceCandidate) *ice_candidate = | 
 |         [[RTC_OBJC_TYPE(RTCIceCandidate) alloc] initWithNativeCandidate:candidate_wrapper.get()]; | 
 |     [ice_candidates addObject:ice_candidate]; | 
 |   } | 
 |   RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; | 
 |   [peer_connection.delegate peerConnection:peer_connection | 
 |                     didRemoveIceCandidates:ice_candidates]; | 
 | } | 
 |  | 
 | void PeerConnectionDelegateAdapter::OnIceSelectedCandidatePairChanged( | 
 |     const cricket::CandidatePairChangeEvent &event) { | 
 |   const auto &selected_pair = event.selected_candidate_pair; | 
 |   auto local_candidate_wrapper = std::make_unique<JsepIceCandidate>( | 
 |       selected_pair.local_candidate().transport_name(), -1, selected_pair.local_candidate()); | 
 |   RTC_OBJC_TYPE(RTCIceCandidate) *local_candidate = [[RTC_OBJC_TYPE(RTCIceCandidate) alloc] | 
 |       initWithNativeCandidate:local_candidate_wrapper.release()]; | 
 |   auto remote_candidate_wrapper = std::make_unique<JsepIceCandidate>( | 
 |       selected_pair.remote_candidate().transport_name(), -1, selected_pair.remote_candidate()); | 
 |   RTC_OBJC_TYPE(RTCIceCandidate) *remote_candidate = [[RTC_OBJC_TYPE(RTCIceCandidate) alloc] | 
 |       initWithNativeCandidate:remote_candidate_wrapper.release()]; | 
 |   RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; | 
 |   NSString *nsstr_reason = [NSString stringForStdString:event.reason]; | 
 |   if ([peer_connection.delegate | 
 |           respondsToSelector:@selector | 
 |           (peerConnection:didChangeLocalCandidate:remoteCandidate:lastReceivedMs:changeReason:)]) { | 
 |     [peer_connection.delegate peerConnection:peer_connection | 
 |                      didChangeLocalCandidate:local_candidate | 
 |                              remoteCandidate:remote_candidate | 
 |                               lastReceivedMs:event.last_data_received_ms | 
 |                                 changeReason:nsstr_reason]; | 
 |   } | 
 | } | 
 |  | 
 | void PeerConnectionDelegateAdapter::OnAddTrack( | 
 |     rtc::scoped_refptr<RtpReceiverInterface> receiver, | 
 |     const std::vector<rtc::scoped_refptr<MediaStreamInterface>> &streams) { | 
 |   RTC_OBJC_TYPE(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) { | 
 |       RTC_OBJC_TYPE(RTCMediaStream) *mediaStream = | 
 |           [[RTC_OBJC_TYPE(RTCMediaStream) alloc] initWithFactory:peer_connection.factory | 
 |                                                nativeMediaStream:nativeStream]; | 
 |       [mediaStreams addObject:mediaStream]; | 
 |     } | 
 |     RTC_OBJC_TYPE(RTCRtpReceiver) *rtpReceiver = | 
 |         [[RTC_OBJC_TYPE(RTCRtpReceiver) alloc] initWithFactory:peer_connection.factory | 
 |                                              nativeRtpReceiver:receiver]; | 
 |  | 
 |     [peer_connection.delegate peerConnection:peer_connection | 
 |                               didAddReceiver:rtpReceiver | 
 |                                      streams:mediaStreams]; | 
 |   } | 
 | } | 
 |  | 
 | void PeerConnectionDelegateAdapter::OnRemoveTrack( | 
 |     rtc::scoped_refptr<RtpReceiverInterface> receiver) { | 
 |   RTC_OBJC_TYPE(RTCPeerConnection) *peer_connection = peer_connection_; | 
 |   if ([peer_connection.delegate respondsToSelector:@selector(peerConnection:didRemoveReceiver:)]) { | 
 |     RTC_OBJC_TYPE(RTCRtpReceiver) *rtpReceiver = | 
 |         [[RTC_OBJC_TYPE(RTCRtpReceiver) alloc] initWithFactory:peer_connection.factory | 
 |                                              nativeRtpReceiver:receiver]; | 
 |     [peer_connection.delegate peerConnection:peer_connection didRemoveReceiver:rtpReceiver]; | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace webrtc | 
 |  | 
 | @implementation RTC_OBJC_TYPE (RTCPeerConnection) { | 
 |   RTC_OBJC_TYPE(RTCPeerConnectionFactory) * _factory; | 
 |   NSMutableArray<RTC_OBJC_TYPE(RTCMediaStream) *> *_localStreams; | 
 |   std::unique_ptr<webrtc::PeerConnectionDelegateAdapter> _observer; | 
 |   rtc::scoped_refptr<webrtc::PeerConnectionInterface> _peerConnection; | 
 |   std::unique_ptr<webrtc::MediaConstraints> _nativeConstraints; | 
 |   BOOL _hasStartedRtcEventLog; | 
 | } | 
 |  | 
 | @synthesize delegate = _delegate; | 
 | @synthesize factory = _factory; | 
 |  | 
 | - (instancetype)initWithFactory:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory | 
 |                   configuration:(RTC_OBJC_TYPE(RTCConfiguration) *)configuration | 
 |                     constraints:(RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints | 
 |                        delegate:(id<RTC_OBJC_TYPE(RTCPeerConnectionDelegate)>)delegate { | 
 |   NSParameterAssert(factory); | 
 |   std::unique_ptr<webrtc::PeerConnectionDependencies> dependencies = | 
 |       std::make_unique<webrtc::PeerConnectionDependencies>(nullptr); | 
 |   return [self initWithDependencies:factory | 
 |                       configuration:configuration | 
 |                         constraints:constraints | 
 |                        dependencies:std::move(dependencies) | 
 |                            delegate:delegate]; | 
 | } | 
 |  | 
 | - (instancetype)initWithDependencies:(RTC_OBJC_TYPE(RTCPeerConnectionFactory) *)factory | 
 |                        configuration:(RTC_OBJC_TYPE(RTCConfiguration) *)configuration | 
 |                          constraints:(RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints | 
 |                         dependencies: | 
 |                             (std::unique_ptr<webrtc::PeerConnectionDependencies>)dependencies | 
 |                             delegate:(id<RTC_OBJC_TYPE(RTCPeerConnectionDelegate)>)delegate { | 
 |   NSParameterAssert(factory); | 
 |   NSParameterAssert(dependencies.get()); | 
 |   std::unique_ptr<webrtc::PeerConnectionInterface::RTCConfiguration> 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()); | 
 |  | 
 |     webrtc::PeerConnectionDependencies deps = std::move(*dependencies.release()); | 
 |     deps.observer = _observer.get(); | 
 |     _peerConnection = factory.nativeFactory->CreatePeerConnection(*config, std::move(deps)); | 
 |  | 
 |     if (!_peerConnection) { | 
 |       return nil; | 
 |     } | 
 |     _factory = factory; | 
 |     _localStreams = [[NSMutableArray alloc] init]; | 
 |     _delegate = delegate; | 
 |   } | 
 |   return self; | 
 | } | 
 |  | 
 | - (NSArray<RTC_OBJC_TYPE(RTCMediaStream) *> *)localStreams { | 
 |   return [_localStreams copy]; | 
 | } | 
 |  | 
 | - (RTC_OBJC_TYPE(RTCSessionDescription) *)localDescription { | 
 |   // It's only safe to operate on SessionDescriptionInterface on the signaling thread. | 
 |   return _peerConnection->signaling_thread()->Invoke<RTC_OBJC_TYPE(RTCSessionDescription) *>( | 
 |       RTC_FROM_HERE, [self] { | 
 |         const webrtc::SessionDescriptionInterface *description = | 
 |             _peerConnection->local_description(); | 
 |         return description ? | 
 |             [[RTC_OBJC_TYPE(RTCSessionDescription) alloc] initWithNativeDescription:description] : | 
 |             nil; | 
 |       }); | 
 | } | 
 |  | 
 | - (RTC_OBJC_TYPE(RTCSessionDescription) *)remoteDescription { | 
 |   // It's only safe to operate on SessionDescriptionInterface on the signaling thread. | 
 |   return _peerConnection->signaling_thread()->Invoke<RTC_OBJC_TYPE(RTCSessionDescription) *>( | 
 |       RTC_FROM_HERE, [self] { | 
 |         const webrtc::SessionDescriptionInterface *description = | 
 |             _peerConnection->remote_description(); | 
 |         return description ? | 
 |             [[RTC_OBJC_TYPE(RTCSessionDescription) alloc] initWithNativeDescription:description] : | 
 |             nil; | 
 |       }); | 
 | } | 
 |  | 
 | - (RTCSignalingState)signalingState { | 
 |   return [[self class] | 
 |       signalingStateForNativeState:_peerConnection->signaling_state()]; | 
 | } | 
 |  | 
 | - (RTCIceConnectionState)iceConnectionState { | 
 |   return [[self class] iceConnectionStateForNativeState: | 
 |       _peerConnection->ice_connection_state()]; | 
 | } | 
 |  | 
 | - (RTCPeerConnectionState)connectionState { | 
 |   return [[self class] connectionStateForNativeState:_peerConnection->peer_connection_state()]; | 
 | } | 
 |  | 
 | - (RTCIceGatheringState)iceGatheringState { | 
 |   return [[self class] iceGatheringStateForNativeState: | 
 |       _peerConnection->ice_gathering_state()]; | 
 | } | 
 |  | 
 | - (BOOL)setConfiguration:(RTC_OBJC_TYPE(RTCConfiguration) *)configuration { | 
 |   std::unique_ptr<webrtc::PeerConnectionInterface::RTCConfiguration> config( | 
 |       [configuration createNativeConfiguration]); | 
 |   if (!config) { | 
 |     return NO; | 
 |   } | 
 |   CopyConstraintsIntoRtcConfiguration(_nativeConstraints.get(), | 
 |                                       config.get()); | 
 |   return _peerConnection->SetConfiguration(*config).ok(); | 
 | } | 
 |  | 
 | - (RTC_OBJC_TYPE(RTCConfiguration) *)configuration { | 
 |   webrtc::PeerConnectionInterface::RTCConfiguration config = | 
 |     _peerConnection->GetConfiguration(); | 
 |   return [[RTC_OBJC_TYPE(RTCConfiguration) alloc] initWithNativeConfiguration:config]; | 
 | } | 
 |  | 
 | - (void)close { | 
 |   _peerConnection->Close(); | 
 | } | 
 |  | 
 | - (void)addIceCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)candidate { | 
 |   std::unique_ptr<const webrtc::IceCandidateInterface> iceCandidate( | 
 |       candidate.nativeCandidate); | 
 |   _peerConnection->AddIceCandidate(iceCandidate.get()); | 
 | } | 
 |  | 
 | - (void)removeIceCandidates:(NSArray<RTC_OBJC_TYPE(RTCIceCandidate) *> *)iceCandidates { | 
 |   std::vector<cricket::Candidate> candidates; | 
 |   for (RTC_OBJC_TYPE(RTCIceCandidate) * iceCandidate in iceCandidates) { | 
 |     std::unique_ptr<const webrtc::IceCandidateInterface> 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:(RTC_OBJC_TYPE(RTCMediaStream) *)stream { | 
 |   if (!_peerConnection->AddStream(stream.nativeMediaStream)) { | 
 |     RTCLogError(@"Failed to add stream: %@", stream); | 
 |     return; | 
 |   } | 
 |   [_localStreams addObject:stream]; | 
 | } | 
 |  | 
 | - (void)removeStream:(RTC_OBJC_TYPE(RTCMediaStream) *)stream { | 
 |   _peerConnection->RemoveStream(stream.nativeMediaStream); | 
 |   [_localStreams removeObject:stream]; | 
 | } | 
 |  | 
 | - (RTC_OBJC_TYPE(RTCRtpSender) *)addTrack:(RTC_OBJC_TYPE(RTCMediaStreamTrack) *)track | 
 |                                 streamIds:(NSArray<NSString *> *)streamIds { | 
 |   std::vector<std::string> nativeStreamIds; | 
 |   for (NSString *streamId in streamIds) { | 
 |     nativeStreamIds.push_back([streamId UTF8String]); | 
 |   } | 
 |   webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpSenderInterface>> nativeSenderOrError = | 
 |       _peerConnection->AddTrack(track.nativeTrack, nativeStreamIds); | 
 |   if (!nativeSenderOrError.ok()) { | 
 |     RTCLogError(@"Failed to add track %@: %s", track, nativeSenderOrError.error().message()); | 
 |     return nil; | 
 |   } | 
 |   return [[RTC_OBJC_TYPE(RTCRtpSender) alloc] initWithFactory:self.factory | 
 |                                               nativeRtpSender:nativeSenderOrError.MoveValue()]; | 
 | } | 
 |  | 
 | - (BOOL)removeTrack:(RTC_OBJC_TYPE(RTCRtpSender) *)sender { | 
 |   bool result = _peerConnection->RemoveTrack(sender.nativeRtpSender); | 
 |   if (!result) { | 
 |     RTCLogError(@"Failed to remote track %@", sender); | 
 |   } | 
 |   return result; | 
 | } | 
 |  | 
 | - (RTC_OBJC_TYPE(RTCRtpTransceiver) *)addTransceiverWithTrack: | 
 |     (RTC_OBJC_TYPE(RTCMediaStreamTrack) *)track { | 
 |   return [self addTransceiverWithTrack:track | 
 |                                   init:[[RTC_OBJC_TYPE(RTCRtpTransceiverInit) alloc] init]]; | 
 | } | 
 |  | 
 | - (RTC_OBJC_TYPE(RTCRtpTransceiver) *) | 
 |     addTransceiverWithTrack:(RTC_OBJC_TYPE(RTCMediaStreamTrack) *)track | 
 |                        init:(RTC_OBJC_TYPE(RTCRtpTransceiverInit) *)init { | 
 |   webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>> nativeTransceiverOrError = | 
 |       _peerConnection->AddTransceiver(track.nativeTrack, init.nativeInit); | 
 |   if (!nativeTransceiverOrError.ok()) { | 
 |     RTCLogError( | 
 |         @"Failed to add transceiver %@: %s", track, nativeTransceiverOrError.error().message()); | 
 |     return nil; | 
 |   } | 
 |   return [[RTC_OBJC_TYPE(RTCRtpTransceiver) alloc] | 
 |            initWithFactory:self.factory | 
 |       nativeRtpTransceiver:nativeTransceiverOrError.MoveValue()]; | 
 | } | 
 |  | 
 | - (RTC_OBJC_TYPE(RTCRtpTransceiver) *)addTransceiverOfType:(RTCRtpMediaType)mediaType { | 
 |   return [self addTransceiverOfType:mediaType | 
 |                                init:[[RTC_OBJC_TYPE(RTCRtpTransceiverInit) alloc] init]]; | 
 | } | 
 |  | 
 | - (RTC_OBJC_TYPE(RTCRtpTransceiver) *)addTransceiverOfType:(RTCRtpMediaType)mediaType | 
 |                                                       init:(RTC_OBJC_TYPE(RTCRtpTransceiverInit) *) | 
 |                                                                init { | 
 |   webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>> nativeTransceiverOrError = | 
 |       _peerConnection->AddTransceiver( | 
 |           [RTC_OBJC_TYPE(RTCRtpReceiver) nativeMediaTypeForMediaType:mediaType], init.nativeInit); | 
 |   if (!nativeTransceiverOrError.ok()) { | 
 |     RTCLogError(@"Failed to add transceiver %@: %s", | 
 |                 [RTC_OBJC_TYPE(RTCRtpReceiver) stringForMediaType:mediaType], | 
 |                 nativeTransceiverOrError.error().message()); | 
 |     return nil; | 
 |   } | 
 |   return [[RTC_OBJC_TYPE(RTCRtpTransceiver) alloc] | 
 |            initWithFactory:self.factory | 
 |       nativeRtpTransceiver:nativeTransceiverOrError.MoveValue()]; | 
 | } | 
 |  | 
 | - (void)offerForConstraints:(RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints | 
 |           completionHandler:(void (^)(RTC_OBJC_TYPE(RTCSessionDescription) * sessionDescription, | 
 |                                       NSError *error))completionHandler { | 
 |   rtc::scoped_refptr<webrtc::CreateSessionDescriptionObserverAdapter> | 
 |       observer(new rtc::RefCountedObject | 
 |           <webrtc::CreateSessionDescriptionObserverAdapter>(completionHandler)); | 
 |   webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options; | 
 |   CopyConstraintsIntoOfferAnswerOptions(constraints.nativeConstraints.get(), &options); | 
 |  | 
 |   _peerConnection->CreateOffer(observer, options); | 
 | } | 
 |  | 
 | - (void)answerForConstraints:(RTC_OBJC_TYPE(RTCMediaConstraints) *)constraints | 
 |            completionHandler:(void (^)(RTC_OBJC_TYPE(RTCSessionDescription) * sessionDescription, | 
 |                                        NSError *error))completionHandler { | 
 |   rtc::scoped_refptr<webrtc::CreateSessionDescriptionObserverAdapter> | 
 |       observer(new rtc::RefCountedObject | 
 |           <webrtc::CreateSessionDescriptionObserverAdapter>(completionHandler)); | 
 |   webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options; | 
 |   CopyConstraintsIntoOfferAnswerOptions(constraints.nativeConstraints.get(), &options); | 
 |  | 
 |   _peerConnection->CreateAnswer(observer, options); | 
 | } | 
 |  | 
 | - (void)setLocalDescription:(RTC_OBJC_TYPE(RTCSessionDescription) *)sdp | 
 |           completionHandler:(void (^)(NSError *error))completionHandler { | 
 |   rtc::scoped_refptr<webrtc::SetSessionDescriptionObserverAdapter> observer( | 
 |       new rtc::RefCountedObject<webrtc::SetSessionDescriptionObserverAdapter>( | 
 |           completionHandler)); | 
 |   _peerConnection->SetLocalDescription(observer, sdp.nativeDescription); | 
 | } | 
 |  | 
 | - (void)setRemoteDescription:(RTC_OBJC_TYPE(RTCSessionDescription) *)sdp | 
 |            completionHandler:(void (^)(NSError *error))completionHandler { | 
 |   rtc::scoped_refptr<webrtc::SetSessionDescriptionObserverAdapter> observer( | 
 |       new rtc::RefCountedObject<webrtc::SetSessionDescriptionObserverAdapter>( | 
 |           completionHandler)); | 
 |   _peerConnection->SetRemoteDescription(observer, sdp.nativeDescription); | 
 | } | 
 |  | 
 | - (BOOL)setBweMinBitrateBps:(nullable NSNumber *)minBitrateBps | 
 |           currentBitrateBps:(nullable NSNumber *)currentBitrateBps | 
 |               maxBitrateBps:(nullable NSNumber *)maxBitrateBps { | 
 |   webrtc::BitrateSettings params; | 
 |   if (minBitrateBps != nil) { | 
 |     params.min_bitrate_bps = absl::optional<int>(minBitrateBps.intValue); | 
 |   } | 
 |   if (currentBitrateBps != nil) { | 
 |     params.start_bitrate_bps = absl::optional<int>(currentBitrateBps.intValue); | 
 |   } | 
 |   if (maxBitrateBps != nil) { | 
 |     params.max_bitrate_bps = absl::optional<int>(maxBitrateBps.intValue); | 
 |   } | 
 |   return _peerConnection->SetBitrate(params).ok(); | 
 | } | 
 |  | 
 | - (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; | 
 |   } | 
 |   FILE *f = fopen(filePath.UTF8String, "wb"); | 
 |   if (!f) { | 
 |     RTCLogError(@"Error opening file: %@. Error: %d", filePath, errno); | 
 |     return NO; | 
 |   } | 
 |   // TODO(eladalon): It would be better to not allow negative values into PC. | 
 |   const size_t max_size = (maxSizeInBytes < 0) ? webrtc::RtcEventLog::kUnlimitedOutput : | 
 |                                                  rtc::saturated_cast<size_t>(maxSizeInBytes); | 
 |  | 
 |   _hasStartedRtcEventLog = _peerConnection->StartRtcEventLog( | 
 |       std::make_unique<webrtc::RtcEventLogOutputFile>(f, max_size)); | 
 |   return _hasStartedRtcEventLog; | 
 | } | 
 |  | 
 | - (void)stopRtcEventLog { | 
 |   _peerConnection->StopRtcEventLog(); | 
 |   _hasStartedRtcEventLog = NO; | 
 | } | 
 |  | 
 | - (RTC_OBJC_TYPE(RTCRtpSender) *)senderWithKind:(NSString *)kind streamId:(NSString *)streamId { | 
 |   std::string nativeKind = [NSString stdStringForString:kind]; | 
 |   std::string nativeStreamId = [NSString stdStringForString:streamId]; | 
 |   rtc::scoped_refptr<webrtc::RtpSenderInterface> nativeSender( | 
 |       _peerConnection->CreateSender(nativeKind, nativeStreamId)); | 
 |   return nativeSender ? [[RTC_OBJC_TYPE(RTCRtpSender) alloc] initWithFactory:self.factory | 
 |                                                              nativeRtpSender:nativeSender] : | 
 |                         nil; | 
 | } | 
 |  | 
 | - (NSArray<RTC_OBJC_TYPE(RTCRtpSender) *> *)senders { | 
 |   std::vector<rtc::scoped_refptr<webrtc::RtpSenderInterface>> nativeSenders( | 
 |       _peerConnection->GetSenders()); | 
 |   NSMutableArray *senders = [[NSMutableArray alloc] init]; | 
 |   for (const auto &nativeSender : nativeSenders) { | 
 |     RTC_OBJC_TYPE(RTCRtpSender) *sender = | 
 |         [[RTC_OBJC_TYPE(RTCRtpSender) alloc] initWithFactory:self.factory | 
 |                                              nativeRtpSender:nativeSender]; | 
 |     [senders addObject:sender]; | 
 |   } | 
 |   return senders; | 
 | } | 
 |  | 
 | - (NSArray<RTC_OBJC_TYPE(RTCRtpReceiver) *> *)receivers { | 
 |   std::vector<rtc::scoped_refptr<webrtc::RtpReceiverInterface>> nativeReceivers( | 
 |       _peerConnection->GetReceivers()); | 
 |   NSMutableArray *receivers = [[NSMutableArray alloc] init]; | 
 |   for (const auto &nativeReceiver : nativeReceivers) { | 
 |     RTC_OBJC_TYPE(RTCRtpReceiver) *receiver = | 
 |         [[RTC_OBJC_TYPE(RTCRtpReceiver) alloc] initWithFactory:self.factory | 
 |                                              nativeRtpReceiver:nativeReceiver]; | 
 |     [receivers addObject:receiver]; | 
 |   } | 
 |   return receivers; | 
 | } | 
 |  | 
 | - (NSArray<RTC_OBJC_TYPE(RTCRtpTransceiver) *> *)transceivers { | 
 |   std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>> nativeTransceivers( | 
 |       _peerConnection->GetTransceivers()); | 
 |   NSMutableArray *transceivers = [[NSMutableArray alloc] init]; | 
 |   for (const auto &nativeTransceiver : nativeTransceivers) { | 
 |     RTC_OBJC_TYPE(RTCRtpTransceiver) *transceiver = | 
 |         [[RTC_OBJC_TYPE(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::PeerConnectionState)nativeConnectionStateForState: | 
 |         (RTCPeerConnectionState)state { | 
 |   switch (state) { | 
 |     case RTCPeerConnectionStateNew: | 
 |       return webrtc::PeerConnectionInterface::PeerConnectionState::kNew; | 
 |     case RTCPeerConnectionStateConnecting: | 
 |       return webrtc::PeerConnectionInterface::PeerConnectionState::kConnecting; | 
 |     case RTCPeerConnectionStateConnected: | 
 |       return webrtc::PeerConnectionInterface::PeerConnectionState::kConnected; | 
 |     case RTCPeerConnectionStateFailed: | 
 |       return webrtc::PeerConnectionInterface::PeerConnectionState::kFailed; | 
 |     case RTCPeerConnectionStateDisconnected: | 
 |       return webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected; | 
 |     case RTCPeerConnectionStateClosed: | 
 |       return webrtc::PeerConnectionInterface::PeerConnectionState::kClosed; | 
 |   } | 
 | } | 
 |  | 
 | + (RTCPeerConnectionState)connectionStateForNativeState: | 
 |         (webrtc::PeerConnectionInterface::PeerConnectionState)nativeState { | 
 |   switch (nativeState) { | 
 |     case webrtc::PeerConnectionInterface::PeerConnectionState::kNew: | 
 |       return RTCPeerConnectionStateNew; | 
 |     case webrtc::PeerConnectionInterface::PeerConnectionState::kConnecting: | 
 |       return RTCPeerConnectionStateConnecting; | 
 |     case webrtc::PeerConnectionInterface::PeerConnectionState::kConnected: | 
 |       return RTCPeerConnectionStateConnected; | 
 |     case webrtc::PeerConnectionInterface::PeerConnectionState::kFailed: | 
 |       return RTCPeerConnectionStateFailed; | 
 |     case webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected: | 
 |       return RTCPeerConnectionStateDisconnected; | 
 |     case webrtc::PeerConnectionInterface::PeerConnectionState::kClosed: | 
 |       return RTCPeerConnectionStateClosed; | 
 |   } | 
 | } | 
 |  | 
 | + (NSString *)stringForConnectionState:(RTCPeerConnectionState)state { | 
 |   switch (state) { | 
 |     case RTCPeerConnectionStateNew: | 
 |       return @"NEW"; | 
 |     case RTCPeerConnectionStateConnecting: | 
 |       return @"CONNECTING"; | 
 |     case RTCPeerConnectionStateConnected: | 
 |       return @"CONNECTED"; | 
 |     case RTCPeerConnectionStateFailed: | 
 |       return @"FAILED"; | 
 |     case RTCPeerConnectionStateDisconnected: | 
 |       return @"DISCONNECTED"; | 
 |     case RTCPeerConnectionStateClosed: | 
 |       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<webrtc::PeerConnectionInterface>)nativePeerConnection { | 
 |   return _peerConnection; | 
 | } | 
 |  | 
 | @end |