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