tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ARDAppClient.m (34457B)


      1 /*
      2 *  Copyright 2014 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 "ARDAppClient+Internal.h"
     12 
     13 #import "sdk/objc/api/peerconnection/RTCAudioTrack.h"
     14 #import "sdk/objc/api/peerconnection/RTCConfiguration.h"
     15 #import "sdk/objc/api/peerconnection/RTCFileLogger.h"
     16 #import "sdk/objc/api/peerconnection/RTCIceCandidateErrorEvent.h"
     17 #import "sdk/objc/api/peerconnection/RTCIceServer.h"
     18 #import "sdk/objc/api/peerconnection/RTCMediaConstraints.h"
     19 #import "sdk/objc/api/peerconnection/RTCMediaStream.h"
     20 #import "sdk/objc/api/peerconnection/RTCPeerConnectionFactory.h"
     21 #import "sdk/objc/api/peerconnection/RTCRtpSender.h"
     22 #import "sdk/objc/api/peerconnection/RTCRtpTransceiver.h"
     23 #import "sdk/objc/api/peerconnection/RTCTracing.h"
     24 #import "sdk/objc/api/peerconnection/RTCVideoSource.h"
     25 #import "sdk/objc/api/peerconnection/RTCVideoTrack.h"
     26 #import "sdk/objc/base/RTCLogging.h"
     27 #import "sdk/objc/components/capturer/RTCCameraVideoCapturer.h"
     28 #import "sdk/objc/components/capturer/RTCFileVideoCapturer.h"
     29 #import "sdk/objc/components/video_codec/RTCDefaultVideoDecoderFactory.h"
     30 #import "sdk/objc/components/video_codec/RTCDefaultVideoEncoderFactory.h"
     31 
     32 #import "ARDAppEngineClient.h"
     33 #import "ARDExternalSampleCapturer.h"
     34 #import "ARDJoinResponse.h"
     35 #import "ARDMessageResponse.h"
     36 #import "ARDSettingsModel.h"
     37 #import "ARDSignalingMessage.h"
     38 #import "ARDTURNClient+Internal.h"
     39 #import "ARDUtilities.h"
     40 #import "ARDWebSocketChannel.h"
     41 #import "RTCIceCandidate+JSON.h"
     42 #import "RTCSessionDescription+JSON.h"
     43 
     44 static NSString *const kARDIceServerRequestUrl = @"https://appr.tc/params";
     45 
     46 static NSString *const kARDAppClientErrorDomain = @"ARDAppClient";
     47 static NSInteger const kARDAppClientErrorUnknown = -1;
     48 static NSInteger const kARDAppClientErrorRoomFull = -2;
     49 static NSInteger const kARDAppClientErrorCreateSDP = -3;
     50 static NSInteger const kARDAppClientErrorSetSDP = -4;
     51 static NSInteger const kARDAppClientErrorInvalidClient = -5;
     52 static NSInteger const kARDAppClientErrorInvalidRoom = -6;
     53 static NSString *const kARDMediaStreamId = @"ARDAMS";
     54 static NSString *const kARDAudioTrackId = @"ARDAMSa0";
     55 static NSString *const kARDVideoTrackId = @"ARDAMSv0";
     56 static NSString *const kARDVideoTrackKind = @"video";
     57 
     58 // TODO(tkchin): Add these as UI options.
     59 #if defined(WEBRTC_IOS)
     60 static BOOL const kARDAppClientEnableTracing = NO;
     61 static BOOL const kARDAppClientEnableRtcEventLog = YES;
     62 static int64_t const kARDAppClientAecDumpMaxSizeInBytes = 5e6;      // 5 MB.
     63 static int64_t const kARDAppClientRtcEventLogMaxSizeInBytes = 5e6;  // 5 MB.
     64 #endif
     65 static int const kKbpsMultiplier = 1000;
     66 
     67 // We need a proxy to NSTimer because it causes a strong retain cycle. When
     68 // using the proxy, `invalidate` must be called before it properly deallocs.
     69 @interface ARDTimerProxy : NSObject
     70 
     71 - (instancetype)initWithInterval:(NSTimeInterval)interval
     72                         repeats:(BOOL)repeats
     73                    timerHandler:(void (^)(void))timerHandler;
     74 - (void)invalidate;
     75 
     76 @end
     77 
     78 @implementation ARDTimerProxy {
     79  NSTimer *_timer;
     80  void (^_timerHandler)(void);
     81 }
     82 
     83 - (instancetype)initWithInterval:(NSTimeInterval)interval
     84                         repeats:(BOOL)repeats
     85                    timerHandler:(void (^)(void))timerHandler {
     86  NSParameterAssert(timerHandler);
     87  self = [super init];
     88  if (self) {
     89    _timerHandler = timerHandler;
     90    _timer = [NSTimer scheduledTimerWithTimeInterval:interval
     91                                              target:self
     92                                            selector:@selector(timerDidFire:)
     93                                            userInfo:nil
     94                                             repeats:repeats];
     95  }
     96  return self;
     97 }
     98 
     99 - (void)invalidate {
    100  [_timer invalidate];
    101 }
    102 
    103 - (void)timerDidFire:(NSTimer *)timer {
    104  _timerHandler();
    105 }
    106 
    107 @end
    108 
    109 @implementation ARDAppClient {
    110  RTC_OBJC_TYPE(RTCFileLogger) * _fileLogger;
    111  ARDTimerProxy *_statsTimer;
    112  ARDSettingsModel *_settings;
    113  RTC_OBJC_TYPE(RTCVideoTrack) * _localVideoTrack;
    114 }
    115 
    116 @synthesize shouldGetStats = _shouldGetStats;
    117 @synthesize state = _state;
    118 @synthesize delegate = _delegate;
    119 @synthesize roomServerClient = _roomServerClient;
    120 @synthesize channel = _channel;
    121 @synthesize loopbackChannel = _loopbackChannel;
    122 @synthesize turnClient = _turnClient;
    123 @synthesize peerConnection = _peerConnection;
    124 @synthesize factory = _factory;
    125 @synthesize messageQueue = _messageQueue;
    126 @synthesize isTurnComplete = _isTurnComplete;
    127 @synthesize hasReceivedSdp = _hasReceivedSdp;
    128 @synthesize roomId = _roomId;
    129 @synthesize clientId = _clientId;
    130 @synthesize isInitiator = _isInitiator;
    131 @synthesize iceServers = _iceServers;
    132 @synthesize webSocketURL = _websocketURL;
    133 @synthesize webSocketRestURL = _websocketRestURL;
    134 @synthesize defaultPeerConnectionConstraints =
    135    _defaultPeerConnectionConstraints;
    136 @synthesize isLoopback = _isLoopback;
    137 @synthesize broadcast = _broadcast;
    138 
    139 - (instancetype)init {
    140  return [self initWithDelegate:nil];
    141 }
    142 
    143 - (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
    144  self = [super init];
    145  if (self) {
    146    _roomServerClient = [[ARDAppEngineClient alloc] init];
    147    _delegate = delegate;
    148    NSURL *turnRequestURL = [NSURL URLWithString:kARDIceServerRequestUrl];
    149    _turnClient = [[ARDTURNClient alloc] initWithURL:turnRequestURL];
    150    [self configure];
    151  }
    152  return self;
    153 }
    154 
    155 // TODO(tkchin): Provide signaling channel factory interface so we can recreate
    156 // channel if we need to on network failure. Also, make this the default public
    157 // constructor.
    158 - (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient
    159                        signalingChannel:(id<ARDSignalingChannel>)channel
    160                              turnClient:(id<ARDTURNClient>)turnClient
    161                                delegate:(id<ARDAppClientDelegate>)delegate {
    162  NSParameterAssert(rsClient);
    163  NSParameterAssert(channel);
    164  NSParameterAssert(turnClient);
    165  self = [super init];
    166  if (self) {
    167    _roomServerClient = rsClient;
    168    _channel = channel;
    169    _turnClient = turnClient;
    170    _delegate = delegate;
    171    [self configure];
    172  }
    173  return self;
    174 }
    175 
    176 - (void)configure {
    177  _messageQueue = [NSMutableArray array];
    178  _iceServers = [NSMutableArray array];
    179  _fileLogger = [[RTC_OBJC_TYPE(RTCFileLogger) alloc] init];
    180  [_fileLogger start];
    181 }
    182 
    183 - (void)dealloc {
    184  self.shouldGetStats = NO;
    185  [self disconnect];
    186 }
    187 
    188 - (void)setShouldGetStats:(BOOL)shouldGetStats {
    189  if (_shouldGetStats == shouldGetStats) {
    190    return;
    191  }
    192  if (shouldGetStats) {
    193    __weak ARDAppClient *weakSelf = self;
    194    _statsTimer = [[ARDTimerProxy alloc]
    195        initWithInterval:1
    196                 repeats:YES
    197            timerHandler:^{
    198              ARDAppClient *strongSelf = weakSelf;
    199              [strongSelf.peerConnection
    200                  statisticsWithCompletionHandler:^(
    201                      RTC_OBJC_TYPE(RTCStatisticsReport) * stats) {
    202                    dispatch_async(dispatch_get_main_queue(), ^{
    203                      ARDAppClient *strongerSelf = weakSelf;
    204                      [strongerSelf.delegate appClient:strongerSelf
    205                                           didGetStats:stats];
    206                    });
    207                  }];
    208            }];
    209  } else {
    210    [_statsTimer invalidate];
    211    _statsTimer = nil;
    212  }
    213  _shouldGetStats = shouldGetStats;
    214 }
    215 
    216 - (void)setState:(ARDAppClientState)state {
    217  if (_state == state) {
    218    return;
    219  }
    220  _state = state;
    221  [_delegate appClient:self didChangeState:_state];
    222 }
    223 
    224 - (void)connectToRoomWithId:(NSString *)roomId
    225                   settings:(ARDSettingsModel *)settings
    226                 isLoopback:(BOOL)isLoopback {
    227  NSParameterAssert(roomId.length);
    228  NSParameterAssert(_state == kARDAppClientStateDisconnected);
    229  _settings = settings;
    230  _isLoopback = isLoopback;
    231  self.state = kARDAppClientStateConnecting;
    232 
    233  RTC_OBJC_TYPE(RTCDefaultVideoDecoderFactory) *decoderFactory =
    234      [[RTC_OBJC_TYPE(RTCDefaultVideoDecoderFactory) alloc] init];
    235  RTC_OBJC_TYPE(RTCDefaultVideoEncoderFactory) *encoderFactory =
    236      [[RTC_OBJC_TYPE(RTCDefaultVideoEncoderFactory) alloc] init];
    237  encoderFactory.preferredCodec = [settings currentVideoCodecSettingFromStore];
    238  _factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc]
    239      initWithEncoderFactory:encoderFactory
    240              decoderFactory:decoderFactory];
    241 
    242 #if defined(WEBRTC_IOS)
    243  if (kARDAppClientEnableTracing) {
    244    NSString *filePath =
    245        [self documentsFilePathForFileName:@"webrtc-trace.txt"];
    246    RTCStartInternalCapture(filePath);
    247  }
    248 #endif
    249 
    250  // Request TURN.
    251  __weak ARDAppClient *weakSelf = self;
    252  [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers,
    253                                                     NSError *error) {
    254    if (error) {
    255      RTCLogError(@"Error retrieving TURN servers: %@",
    256                  error.localizedDescription);
    257    }
    258    ARDAppClient *strongSelf = weakSelf;
    259    [strongSelf.iceServers addObjectsFromArray:turnServers];
    260    strongSelf.isTurnComplete = YES;
    261    [strongSelf startSignalingIfReady];
    262  }];
    263 
    264  // Join room on room server.
    265  [_roomServerClient
    266      joinRoomWithRoomId:roomId
    267              isLoopback:isLoopback
    268       completionHandler:^(ARDJoinResponse *response, NSError *error) {
    269         ARDAppClient *strongSelf = weakSelf;
    270         if (error) {
    271           [strongSelf.delegate appClient:strongSelf didError:error];
    272           return;
    273         }
    274         NSError *joinError =
    275             [[strongSelf class] errorForJoinResultType:response.result];
    276         if (joinError) {
    277           RTCLogError(@"Failed to join room:%@ on room server.", roomId);
    278           [strongSelf disconnect];
    279           [strongSelf.delegate appClient:strongSelf didError:joinError];
    280           return;
    281         }
    282         RTCLog(@"Joined room:%@ on room server.", roomId);
    283         strongSelf.roomId = response.roomId;
    284         strongSelf.clientId = response.clientId;
    285         strongSelf.isInitiator = response.isInitiator;
    286         for (ARDSignalingMessage *message in response.messages) {
    287           if (message.type == kARDSignalingMessageTypeOffer ||
    288               message.type == kARDSignalingMessageTypeAnswer) {
    289             strongSelf.hasReceivedSdp = YES;
    290             [strongSelf.messageQueue insertObject:message atIndex:0];
    291           } else {
    292             [strongSelf.messageQueue addObject:message];
    293           }
    294         }
    295         strongSelf.webSocketURL = response.webSocketURL;
    296         strongSelf.webSocketRestURL = response.webSocketRestURL;
    297         [strongSelf registerWithColliderIfReady];
    298         [strongSelf startSignalingIfReady];
    299       }];
    300 }
    301 
    302 - (void)disconnect {
    303  if (_state == kARDAppClientStateDisconnected) {
    304    return;
    305  }
    306  if (self.hasJoinedRoomServerRoom) {
    307    [_roomServerClient leaveRoomWithRoomId:_roomId
    308                                  clientId:_clientId
    309                         completionHandler:nil];
    310  }
    311  if (_channel) {
    312    if (_channel.state == kARDSignalingChannelStateRegistered) {
    313      // Tell the other client we're hanging up.
    314      ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
    315      [_channel sendMessage:byeMessage];
    316    }
    317    // Disconnect from collider.
    318    _channel = nil;
    319  }
    320  _clientId = nil;
    321  _roomId = nil;
    322  _isInitiator = NO;
    323  _hasReceivedSdp = NO;
    324  _messageQueue = [NSMutableArray array];
    325  _localVideoTrack = nil;
    326 #if defined(WEBRTC_IOS)
    327  [_factory stopAecDump];
    328  [_peerConnection stopRtcEventLog];
    329 #endif
    330  [_peerConnection close];
    331  _peerConnection = nil;
    332  self.state = kARDAppClientStateDisconnected;
    333 #if defined(WEBRTC_IOS)
    334  if (kARDAppClientEnableTracing) {
    335    RTCStopInternalCapture();
    336  }
    337 #endif
    338 }
    339 
    340 #pragma mark - ARDSignalingChannelDelegate
    341 
    342 - (void)channel:(id<ARDSignalingChannel>)channel
    343    didReceiveMessage:(ARDSignalingMessage *)message {
    344  switch (message.type) {
    345    case kARDSignalingMessageTypeOffer:
    346    case kARDSignalingMessageTypeAnswer:
    347      // Offers and answers must be processed before any other message, so we
    348      // place them at the front of the queue.
    349      _hasReceivedSdp = YES;
    350      [_messageQueue insertObject:message atIndex:0];
    351      break;
    352    case kARDSignalingMessageTypeCandidate:
    353    case kARDSignalingMessageTypeCandidateRemoval:
    354      [_messageQueue addObject:message];
    355      break;
    356    case kARDSignalingMessageTypeBye:
    357      // Disconnects can be processed immediately.
    358      [self processSignalingMessage:message];
    359      return;
    360  }
    361  [self drainMessageQueueIfReady];
    362 }
    363 
    364 - (void)channel:(id<ARDSignalingChannel>)channel
    365    didChangeState:(ARDSignalingChannelState)state {
    366  switch (state) {
    367    case kARDSignalingChannelStateOpen:
    368      break;
    369    case kARDSignalingChannelStateRegistered:
    370      break;
    371    case kARDSignalingChannelStateClosed:
    372    case kARDSignalingChannelStateError:
    373      // TODO(tkchin): reconnection scenarios. Right now we just disconnect
    374      // completely if the websocket connection fails.
    375      [self disconnect];
    376      break;
    377  }
    378 }
    379 
    380 #pragma mark - RTC_OBJC_TYPE(RTCPeerConnectionDelegate)
    381 // Callbacks for this delegate occur on non-main thread and need to be
    382 // dispatched back to main queue as needed.
    383 
    384 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
    385    didChangeSignalingState:(RTCSignalingState)stateChanged {
    386  RTCLog(@"Signaling state changed: %ld", (long)stateChanged);
    387 }
    388 
    389 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
    390          didAddStream:(RTC_OBJC_TYPE(RTCMediaStream) *)stream {
    391  RTCLog(@"Stream with %lu video tracks and %lu audio tracks was added.",
    392         (unsigned long)stream.videoTracks.count,
    393         (unsigned long)stream.audioTracks.count);
    394 }
    395 
    396 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
    397    didStartReceivingOnTransceiver:
    398        (RTC_OBJC_TYPE(RTCRtpTransceiver) *)transceiver {
    399  RTC_OBJC_TYPE(RTCMediaStreamTrack) *track = transceiver.receiver.track;
    400  RTCLog(@"Now receiving %@ on track %@.", track.kind, track.trackId);
    401 }
    402 
    403 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
    404       didRemoveStream:(RTC_OBJC_TYPE(RTCMediaStream) *)stream {
    405  RTCLog(@"Stream was removed.");
    406 }
    407 
    408 - (void)peerConnectionShouldNegotiate:
    409    (RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection {
    410  RTCLog(@"WARNING: Renegotiation needed but unimplemented.");
    411 }
    412 
    413 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
    414    didChangeIceConnectionState:(RTCIceConnectionState)newState {
    415  RTCLog(@"ICE state changed: %ld", (long)newState);
    416  dispatch_async(dispatch_get_main_queue(), ^{
    417    [self.delegate appClient:self didChangeConnectionState:newState];
    418  });
    419 }
    420 
    421 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
    422    didChangeConnectionState:(RTCPeerConnectionState)newState {
    423  RTCLog(@"ICE+DTLS state changed: %ld", (long)newState);
    424 }
    425 
    426 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
    427    didChangeIceGatheringState:(RTCIceGatheringState)newState {
    428  RTCLog(@"ICE gathering state changed: %ld", (long)newState);
    429 }
    430 
    431 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
    432    didGenerateIceCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)candidate {
    433  dispatch_async(dispatch_get_main_queue(), ^{
    434    ARDICECandidateMessage *message =
    435        [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
    436    [self sendSignalingMessage:message];
    437  });
    438 }
    439 
    440 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
    441    didFailToGatherIceCandidate:
    442        (RTC_OBJC_TYPE(RTCIceCandidateErrorEvent) *)event {
    443  RTCLog(@"Failed to gather ICE candidate. address: %@, port: %d, url: %@, "
    444         @"errorCode: %d, "
    445         @"errorText: %@",
    446         event.address,
    447         event.port,
    448         event.url,
    449         event.errorCode,
    450         event.errorText);
    451 }
    452 
    453 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
    454    didRemoveIceCandidates:
    455        (NSArray<RTC_OBJC_TYPE(RTCIceCandidate) *> *)candidates {
    456  dispatch_async(dispatch_get_main_queue(), ^{
    457    ARDICECandidateRemovalMessage *message =
    458        [[ARDICECandidateRemovalMessage alloc]
    459            initWithRemovedCandidates:candidates];
    460    [self sendSignalingMessage:message];
    461  });
    462 }
    463 
    464 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
    465     didChangeLocalCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)local
    466    didChangeRemoteCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)remote
    467              lastReceivedMs:(int)lastDataReceivedMs
    468               didHaveReason:(NSString *)reason {
    469  RTCLog(@"ICE candidate pair changed because: %@", reason);
    470 }
    471 
    472 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
    473    didOpenDataChannel:(RTC_OBJC_TYPE(RTCDataChannel) *)dataChannel {
    474 }
    475 
    476 #pragma mark - RTCSessionDescriptionDelegate
    477 // Callbacks for this delegate occur on non-main thread and need to be
    478 // dispatched back to main queue as needed.
    479 
    480 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
    481    didCreateSessionDescription:(RTC_OBJC_TYPE(RTCSessionDescription) *)sdp
    482                          error:(NSError *)error {
    483  dispatch_async(dispatch_get_main_queue(), ^{
    484    if (error) {
    485      RTCLogError(@"Failed to create session description. Error: %@", error);
    486      [self disconnect];
    487      NSDictionary *userInfo = @{
    488        NSLocalizedDescriptionKey : @"Failed to create session description.",
    489      };
    490      NSError *sdpError =
    491          [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
    492                                     code:kARDAppClientErrorCreateSDP
    493                                 userInfo:userInfo];
    494      [self.delegate appClient:self didError:sdpError];
    495      return;
    496    }
    497    __weak ARDAppClient *weakSelf = self;
    498    [self.peerConnection
    499        setLocalDescription:sdp
    500          completionHandler:^(NSError *sldError) {
    501            ARDAppClient *strongSelf = weakSelf;
    502            [strongSelf peerConnection:strongSelf.peerConnection
    503                didSetSessionDescriptionWithError:sldError];
    504          }];
    505    ARDSessionDescriptionMessage *message =
    506        [[ARDSessionDescriptionMessage alloc] initWithDescription:sdp];
    507    [self sendSignalingMessage:message];
    508    [self setMaxBitrateForPeerConnectionVideoSender];
    509  });
    510 }
    511 
    512 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
    513    didSetSessionDescriptionWithError:(NSError *)error {
    514  dispatch_async(dispatch_get_main_queue(), ^{
    515    if (error) {
    516      RTCLogError(@"Failed to set session description. Error: %@", error);
    517      [self disconnect];
    518      NSDictionary *userInfo = @{
    519        NSLocalizedDescriptionKey : @"Failed to set session description.",
    520      };
    521      NSError *sdpError =
    522          [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
    523                                     code:kARDAppClientErrorSetSDP
    524                                 userInfo:userInfo];
    525      [self.delegate appClient:self didError:sdpError];
    526      return;
    527    }
    528    // If we're answering and we've just set the remote offer we need to create
    529    // an answer and set the local description.
    530    if (!self.isInitiator && !self.peerConnection.localDescription) {
    531      RTC_OBJC_TYPE(RTCMediaConstraints) *constraints =
    532          [self defaultAnswerConstraints];
    533      __weak ARDAppClient *weakSelf = self;
    534      [self.peerConnection
    535          answerForConstraints:constraints
    536             completionHandler:^(RTC_OBJC_TYPE(RTCSessionDescription) * sdp,
    537                                 NSError * answerError) {
    538               ARDAppClient *strongSelf = weakSelf;
    539               [strongSelf peerConnection:strongSelf.peerConnection
    540                   didCreateSessionDescription:sdp
    541                                         error:answerError];
    542             }];
    543    }
    544  });
    545 }
    546 
    547 #pragma mark - Private
    548 
    549 #if defined(WEBRTC_IOS)
    550 
    551 - (NSString *)documentsFilePathForFileName:(NSString *)fileName {
    552  NSParameterAssert(fileName.length);
    553  NSArray *paths = NSSearchPathForDirectoriesInDomains(
    554      NSDocumentDirectory, NSUserDomainMask, YES);
    555  NSString *documentsDirPath = paths.firstObject;
    556  NSString *filePath =
    557      [documentsDirPath stringByAppendingPathComponent:fileName];
    558  return filePath;
    559 }
    560 
    561 #endif
    562 
    563 - (BOOL)hasJoinedRoomServerRoom {
    564  return _clientId.length;
    565 }
    566 
    567 // Begins the peer connection connection process if we have both joined a room
    568 // on the room server and tried to obtain a TURN server. Otherwise does nothing.
    569 // A peer connection object will be created with a stream that contains local
    570 // audio and video capture. If this client is the caller, an offer is created as
    571 // well, otherwise the client will wait for an offer to arrive.
    572 - (void)startSignalingIfReady {
    573  if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
    574    return;
    575  }
    576  self.state = kARDAppClientStateConnected;
    577 
    578  // Create peer connection.
    579  RTC_OBJC_TYPE(RTCMediaConstraints) *constraints =
    580      [self defaultPeerConnectionConstraints];
    581  RTC_OBJC_TYPE(RTCConfiguration) *config =
    582      [[RTC_OBJC_TYPE(RTCConfiguration) alloc] init];
    583  RTC_OBJC_TYPE(RTCCertificate) *pcert =
    584      [RTC_OBJC_TYPE(RTCCertificate) generateCertificateWithParams:@{
    585        @"expires" : @100000,
    586        @"name" : @"RSASSA-PKCS1-v1_5"
    587      }];
    588  config.iceServers = _iceServers;
    589  config.sdpSemantics = RTCSdpSemanticsUnifiedPlan;
    590  config.certificate = pcert;
    591 
    592  _peerConnection = [_factory peerConnectionWithConfiguration:config
    593                                                  constraints:constraints
    594                                                     delegate:self];
    595  // Create AV senders.
    596  [self createMediaSenders];
    597  if (_isInitiator) {
    598    // Send offer.
    599    __weak ARDAppClient *weakSelf = self;
    600    [_peerConnection
    601        offerForConstraints:[self defaultOfferConstraints]
    602          completionHandler:^(RTC_OBJC_TYPE(RTCSessionDescription) * sdp,
    603                              NSError * error) {
    604            ARDAppClient *strongSelf = weakSelf;
    605            [strongSelf peerConnection:strongSelf.peerConnection
    606                didCreateSessionDescription:sdp
    607                                      error:error];
    608          }];
    609  } else {
    610    // Check if we've received an offer.
    611    [self drainMessageQueueIfReady];
    612  }
    613 #if defined(WEBRTC_IOS)
    614  // Start event log.
    615  if (kARDAppClientEnableRtcEventLog) {
    616    NSString *filePath =
    617        [self documentsFilePathForFileName:@"webrtc-rtceventlog"];
    618    if (![_peerConnection
    619            startRtcEventLogWithFilePath:filePath
    620                          maxSizeInBytes:
    621                              kARDAppClientRtcEventLogMaxSizeInBytes]) {
    622      RTCLogError(@"Failed to start event logging.");
    623    }
    624  }
    625 
    626  // Start aecdump diagnostic recording.
    627  if ([_settings currentCreateAecDumpSettingFromStore]) {
    628    NSString *filePath =
    629        [self documentsFilePathForFileName:@"webrtc-audio.aecdump"];
    630    if (![_factory
    631            startAecDumpWithFilePath:filePath
    632                      maxSizeInBytes:kARDAppClientAecDumpMaxSizeInBytes]) {
    633      RTCLogError(@"Failed to start aec dump.");
    634    }
    635  }
    636 #endif
    637 }
    638 
    639 // Processes the messages that we've received from the room server and the
    640 // signaling channel. The offer or answer message must be processed before other
    641 // signaling messages, however they can arrive out of order. Hence, this method
    642 // only processes pending messages if there is a peer connection object and
    643 // if we have received either an offer or answer.
    644 - (void)drainMessageQueueIfReady {
    645  if (!_peerConnection || !_hasReceivedSdp) {
    646    return;
    647  }
    648  for (ARDSignalingMessage *message in _messageQueue) {
    649    [self processSignalingMessage:message];
    650  }
    651  [_messageQueue removeAllObjects];
    652 }
    653 
    654 // Processes the given signaling message based on its type.
    655 - (void)processSignalingMessage:(ARDSignalingMessage *)message {
    656  NSParameterAssert(_peerConnection ||
    657                    message.type == kARDSignalingMessageTypeBye);
    658  switch (message.type) {
    659    case kARDSignalingMessageTypeOffer:
    660    case kARDSignalingMessageTypeAnswer: {
    661      ARDSessionDescriptionMessage *sdpMessage =
    662          (ARDSessionDescriptionMessage *)message;
    663      RTC_OBJC_TYPE(RTCSessionDescription) *description =
    664          sdpMessage.sessionDescription;
    665      __weak ARDAppClient *weakSelf = self;
    666      [_peerConnection setRemoteDescription:description
    667                          completionHandler:^(NSError *error) {
    668                            ARDAppClient *strongSelf = weakSelf;
    669                            [strongSelf peerConnection:strongSelf.peerConnection
    670                                didSetSessionDescriptionWithError:error];
    671                          }];
    672      break;
    673    }
    674    case kARDSignalingMessageTypeCandidate: {
    675      ARDICECandidateMessage *candidateMessage =
    676          (ARDICECandidateMessage *)message;
    677      __weak ARDAppClient *weakSelf = self;
    678      [_peerConnection addIceCandidate:candidateMessage.candidate
    679                     completionHandler:^(NSError *error) {
    680                       ARDAppClient *strongSelf = weakSelf;
    681                       if (error) {
    682                         [strongSelf.delegate appClient:strongSelf
    683                                               didError:error];
    684                       }
    685                     }];
    686      break;
    687    }
    688    case kARDSignalingMessageTypeCandidateRemoval: {
    689      ARDICECandidateRemovalMessage *candidateMessage =
    690          (ARDICECandidateRemovalMessage *)message;
    691      [_peerConnection removeIceCandidates:candidateMessage.candidates];
    692      break;
    693    }
    694    case kARDSignalingMessageTypeBye:
    695      // Other client disconnected.
    696      // TODO(tkchin): support waiting in room for next client. For now just
    697      // disconnect.
    698      [self disconnect];
    699      break;
    700  }
    701 }
    702 
    703 // Sends a signaling message to the other client. The caller will send messages
    704 // through the room server, whereas the callee will send messages over the
    705 // signaling channel.
    706 - (void)sendSignalingMessage:(ARDSignalingMessage *)message {
    707  if (_isInitiator) {
    708    __weak ARDAppClient *weakSelf = self;
    709    [_roomServerClient
    710              sendMessage:message
    711                forRoomId:_roomId
    712                 clientId:_clientId
    713        completionHandler:^(ARDMessageResponse *response, NSError *error) {
    714          ARDAppClient *strongSelf = weakSelf;
    715          if (error) {
    716            [strongSelf.delegate appClient:strongSelf didError:error];
    717            return;
    718          }
    719          NSError *messageError =
    720              [[strongSelf class] errorForMessageResultType:response.result];
    721          if (messageError) {
    722            [strongSelf.delegate appClient:strongSelf didError:messageError];
    723            return;
    724          }
    725        }];
    726  } else {
    727    [_channel sendMessage:message];
    728  }
    729 }
    730 
    731 - (void)setMaxBitrateForPeerConnectionVideoSender {
    732  for (RTC_OBJC_TYPE(RTCRtpSender) * sender in _peerConnection.senders) {
    733    if (sender.track != nil) {
    734      if ([sender.track.kind isEqualToString:kARDVideoTrackKind]) {
    735        [self setMaxBitrate:[_settings currentMaxBitrateSettingFromStore]
    736             forVideoSender:sender];
    737      }
    738    }
    739  }
    740 }
    741 
    742 - (void)setMaxBitrate:(NSNumber *)maxBitrate
    743       forVideoSender:(RTC_OBJC_TYPE(RTCRtpSender) *)sender {
    744  if (maxBitrate.intValue <= 0) {
    745    return;
    746  }
    747 
    748  RTC_OBJC_TYPE(RTCRtpParameters) *parametersToModify = sender.parameters;
    749  for (RTC_OBJC_TYPE(RTCRtpEncodingParameters) *
    750       encoding in parametersToModify.encodings) {
    751    encoding.maxBitrateBps = @(maxBitrate.intValue * kKbpsMultiplier);
    752  }
    753  [sender setParameters:parametersToModify];
    754 }
    755 
    756 - (RTC_OBJC_TYPE(RTCRtpTransceiver) *)videoTransceiver {
    757  for (RTC_OBJC_TYPE(RTCRtpTransceiver) *
    758       transceiver in _peerConnection.transceivers) {
    759    if (transceiver.mediaType == RTCRtpMediaTypeVideo) {
    760      return transceiver;
    761    }
    762  }
    763  return nil;
    764 }
    765 
    766 - (void)createMediaSenders {
    767  RTC_OBJC_TYPE(RTCMediaConstraints) *constraints =
    768      [self defaultMediaAudioConstraints];
    769  RTC_OBJC_TYPE(RTCAudioSource) *source =
    770      [_factory audioSourceWithConstraints:constraints];
    771  RTC_OBJC_TYPE(RTCAudioTrack) *audioTrack =
    772      [_factory audioTrackWithSource:source trackId:kARDAudioTrackId];
    773  [_peerConnection addTrack:audioTrack streamIds:@[ kARDMediaStreamId ]];
    774  _localVideoTrack = [self createLocalVideoTrack];
    775  if (_localVideoTrack) {
    776    [_peerConnection addTrack:_localVideoTrack
    777                    streamIds:@[ kARDMediaStreamId ]];
    778    [_delegate appClient:self didReceiveLocalVideoTrack:_localVideoTrack];
    779    // We can set up rendering for the remote track right away since the
    780    // transceiver already has an RTC_OBJC_TYPE(RTCRtpReceiver) with a track.
    781    // The track will automatically get unmuted and produce frames once RTP is
    782    // received.
    783    RTC_OBJC_TYPE(RTCVideoTrack) *videoTrack = (RTC_OBJC_TYPE(RTCVideoTrack) *)(
    784        [self videoTransceiver].receiver.track);
    785    [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
    786  }
    787 }
    788 
    789 - (RTC_OBJC_TYPE(RTCVideoTrack) *)createLocalVideoTrack {
    790  if ([_settings currentAudioOnlySettingFromStore]) {
    791    return nil;
    792  }
    793 
    794  RTC_OBJC_TYPE(RTCVideoSource) *source = [_factory videoSource];
    795 
    796 #if !TARGET_IPHONE_SIMULATOR
    797  if (self.isBroadcast) {
    798    ARDExternalSampleCapturer *capturer =
    799        [[ARDExternalSampleCapturer alloc] initWithDelegate:source];
    800    [_delegate appClient:self didCreateLocalExternalSampleCapturer:capturer];
    801  } else {
    802    RTC_OBJC_TYPE(RTCCameraVideoCapturer) *capturer =
    803        [[RTC_OBJC_TYPE(RTCCameraVideoCapturer) alloc] initWithDelegate:source];
    804    [_delegate appClient:self didCreateLocalCapturer:capturer];
    805  }
    806 #else
    807 #if defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0)
    808  if (@available(iOS 10, *)) {
    809    RTC_OBJC_TYPE(RTCFileVideoCapturer) *fileCapturer =
    810        [[RTC_OBJC_TYPE(RTCFileVideoCapturer) alloc] initWithDelegate:source];
    811    [_delegate appClient:self didCreateLocalFileCapturer:fileCapturer];
    812  }
    813 #endif
    814 #endif
    815 
    816  return [_factory videoTrackWithSource:source trackId:kARDVideoTrackId];
    817 }
    818 
    819 #pragma mark - Collider methods
    820 
    821 - (void)registerWithColliderIfReady {
    822  if (!self.hasJoinedRoomServerRoom) {
    823    return;
    824  }
    825  // Open WebSocket connection.
    826  if (!_channel) {
    827    _channel = [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
    828                                                restURL:_websocketRestURL
    829                                               delegate:self];
    830    if (_isLoopback) {
    831      _loopbackChannel =
    832          [[ARDLoopbackWebSocketChannel alloc] initWithURL:_websocketURL
    833                                                   restURL:_websocketRestURL];
    834    }
    835  }
    836  [_channel registerForRoomId:_roomId clientId:_clientId];
    837  if (_isLoopback) {
    838    [_loopbackChannel registerForRoomId:_roomId clientId:@"LOOPBACK_CLIENT_ID"];
    839  }
    840 }
    841 
    842 #pragma mark - Defaults
    843 
    844 - (RTC_OBJC_TYPE(RTCMediaConstraints) *)defaultMediaAudioConstraints {
    845  NSDictionary *mandatoryConstraints = @{};
    846  RTC_OBJC_TYPE(RTCMediaConstraints) *constraints =
    847      [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc]
    848          initWithMandatoryConstraints:mandatoryConstraints
    849                   optionalConstraints:nil];
    850  return constraints;
    851 }
    852 
    853 - (RTC_OBJC_TYPE(RTCMediaConstraints) *)defaultAnswerConstraints {
    854  return [self defaultOfferConstraints];
    855 }
    856 
    857 - (RTC_OBJC_TYPE(RTCMediaConstraints) *)defaultOfferConstraints {
    858  NSDictionary *mandatoryConstraints =
    859      @{@"OfferToReceiveAudio" : @"true", @"OfferToReceiveVideo" : @"true"};
    860  RTC_OBJC_TYPE(RTCMediaConstraints) *constraints =
    861      [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc]
    862          initWithMandatoryConstraints:mandatoryConstraints
    863                   optionalConstraints:nil];
    864  return constraints;
    865 }
    866 
    867 - (RTC_OBJC_TYPE(RTCMediaConstraints) *)defaultPeerConnectionConstraints {
    868  if (_defaultPeerConnectionConstraints) {
    869    return _defaultPeerConnectionConstraints;
    870  }
    871  NSString *value = _isLoopback ? @"false" : @"true";
    872  NSDictionary *optionalConstraints = @{@"DtlsSrtpKeyAgreement" : value};
    873  RTC_OBJC_TYPE(RTCMediaConstraints) *constraints =
    874      [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc]
    875          initWithMandatoryConstraints:nil
    876                   optionalConstraints:optionalConstraints];
    877  return constraints;
    878 }
    879 
    880 #pragma mark - Errors
    881 
    882 + (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
    883  NSError *error = nil;
    884  switch (resultType) {
    885    case kARDJoinResultTypeSuccess:
    886      break;
    887    case kARDJoinResultTypeUnknown: {
    888      error = [[NSError alloc]
    889          initWithDomain:kARDAppClientErrorDomain
    890                    code:kARDAppClientErrorUnknown
    891                userInfo:@{
    892                  NSLocalizedDescriptionKey : @"Unknown error.",
    893                }];
    894      break;
    895    }
    896    case kARDJoinResultTypeFull: {
    897      error =
    898          [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
    899                                     code:kARDAppClientErrorRoomFull
    900                                 userInfo:@{
    901                                   NSLocalizedDescriptionKey : @"Room is full.",
    902                                 }];
    903      break;
    904    }
    905  }
    906  return error;
    907 }
    908 
    909 + (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
    910  NSError *error = nil;
    911  switch (resultType) {
    912    case kARDMessageResultTypeSuccess:
    913      break;
    914    case kARDMessageResultTypeUnknown:
    915      error = [[NSError alloc]
    916          initWithDomain:kARDAppClientErrorDomain
    917                    code:kARDAppClientErrorUnknown
    918                userInfo:@{
    919                  NSLocalizedDescriptionKey : @"Unknown error.",
    920                }];
    921      break;
    922    case kARDMessageResultTypeInvalidClient:
    923      error = [[NSError alloc]
    924          initWithDomain:kARDAppClientErrorDomain
    925                    code:kARDAppClientErrorInvalidClient
    926                userInfo:@{
    927                  NSLocalizedDescriptionKey : @"Invalid client.",
    928                }];
    929      break;
    930    case kARDMessageResultTypeInvalidRoom:
    931      error =
    932          [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
    933                                     code:kARDAppClientErrorInvalidRoom
    934                                 userInfo:@{
    935                                   NSLocalizedDescriptionKey : @"Invalid room.",
    936                                 }];
    937      break;
    938  }
    939  return error;
    940 }
    941 
    942 @end