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 "ARDVideoCallViewController.h" 12 13#import "RTCAVFoundationVideoSource.h" 14#import "RTCLogging.h" 15 16#import "ARDAppClient.h" 17#import "ARDVideoCallView.h" 18 19@interface ARDVideoCallViewController () <ARDAppClientDelegate, 20 ARDVideoCallViewDelegate> 21@property(nonatomic, strong) RTCVideoTrack *localVideoTrack; 22@property(nonatomic, strong) RTCVideoTrack *remoteVideoTrack; 23@property(nonatomic, readonly) ARDVideoCallView *videoCallView; 24@end 25 26@implementation ARDVideoCallViewController { 27 ARDAppClient *_client; 28 RTCVideoTrack *_remoteVideoTrack; 29 RTCVideoTrack *_localVideoTrack; 30} 31 32@synthesize videoCallView = _videoCallView; 33 34- (instancetype)initForRoom:(NSString *)room 35 isLoopback:(BOOL)isLoopback 36 isAudioOnly:(BOOL)isAudioOnly { 37 if (self = [super init]) { 38 _client = [[ARDAppClient alloc] initWithDelegate:self]; 39 [_client connectToRoomWithId:room 40 isLoopback:isLoopback 41 isAudioOnly:isAudioOnly]; 42 } 43 return self; 44} 45 46- (void)loadView { 47 _videoCallView = [[ARDVideoCallView alloc] initWithFrame:CGRectZero]; 48 _videoCallView.delegate = self; 49 _videoCallView.statusLabel.text = 50 [self statusTextForState:RTCICEConnectionNew]; 51 self.view = _videoCallView; 52} 53 54#pragma mark - ARDAppClientDelegate 55 56- (void)appClient:(ARDAppClient *)client 57 didChangeState:(ARDAppClientState)state { 58 switch (state) { 59 case kARDAppClientStateConnected: 60 RTCLog(@"Client connected."); 61 break; 62 case kARDAppClientStateConnecting: 63 RTCLog(@"Client connecting."); 64 break; 65 case kARDAppClientStateDisconnected: 66 RTCLog(@"Client disconnected."); 67 [self hangup]; 68 break; 69 } 70} 71 72- (void)appClient:(ARDAppClient *)client 73 didChangeConnectionState:(RTCICEConnectionState)state { 74 RTCLog(@"ICE state changed: %d", state); 75 __weak ARDVideoCallViewController *weakSelf = self; 76 dispatch_async(dispatch_get_main_queue(), ^{ 77 ARDVideoCallViewController *strongSelf = weakSelf; 78 strongSelf.videoCallView.statusLabel.text = 79 [strongSelf statusTextForState:state]; 80 }); 81} 82 83- (void)appClient:(ARDAppClient *)client 84 didReceiveLocalVideoTrack:(RTCVideoTrack *)localVideoTrack { 85 self.localVideoTrack = localVideoTrack; 86} 87 88- (void)appClient:(ARDAppClient *)client 89 didReceiveRemoteVideoTrack:(RTCVideoTrack *)remoteVideoTrack { 90 self.remoteVideoTrack = remoteVideoTrack; 91 _videoCallView.statusLabel.hidden = YES; 92} 93 94- (void)appClient:(ARDAppClient *)client 95 didGetStats:(NSArray *)stats { 96 _videoCallView.statsView.stats = stats; 97 [_videoCallView setNeedsLayout]; 98} 99 100- (void)appClient:(ARDAppClient *)client 101 didError:(NSError *)error { 102 NSString *message = 103 [NSString stringWithFormat:@"%@", error.localizedDescription]; 104 [self showAlertWithMessage:message]; 105 [self hangup]; 106} 107 108#pragma mark - ARDVideoCallViewDelegate 109 110- (void)videoCallViewDidHangup:(ARDVideoCallView *)view { 111 [self hangup]; 112} 113 114- (void)videoCallViewDidSwitchCamera:(ARDVideoCallView *)view { 115 // TODO(tkchin): Rate limit this so you can't tap continously on it. 116 // Probably through an animation. 117 [self switchCamera]; 118} 119 120- (void)videoCallViewDidEnableStats:(ARDVideoCallView *)view { 121 _client.shouldGetStats = YES; 122 _videoCallView.statsView.hidden = NO; 123} 124 125#pragma mark - Private 126 127- (void)setLocalVideoTrack:(RTCVideoTrack *)localVideoTrack { 128 if (_localVideoTrack == localVideoTrack) { 129 return; 130 } 131 _localVideoTrack = nil; 132 _localVideoTrack = localVideoTrack; 133 RTCAVFoundationVideoSource *source = nil; 134 if ([localVideoTrack.source 135 isKindOfClass:[RTCAVFoundationVideoSource class]]) { 136 source = (RTCAVFoundationVideoSource*)localVideoTrack.source; 137 } 138 _videoCallView.localVideoView.captureSession = source.captureSession; 139} 140 141- (void)setRemoteVideoTrack:(RTCVideoTrack *)remoteVideoTrack { 142 if (_remoteVideoTrack == remoteVideoTrack) { 143 return; 144 } 145 [_remoteVideoTrack removeRenderer:_videoCallView.remoteVideoView]; 146 _remoteVideoTrack = nil; 147 [_videoCallView.remoteVideoView renderFrame:nil]; 148 _remoteVideoTrack = remoteVideoTrack; 149 [_remoteVideoTrack addRenderer:_videoCallView.remoteVideoView]; 150} 151 152- (void)hangup { 153 self.remoteVideoTrack = nil; 154 self.localVideoTrack = nil; 155 [_client disconnect]; 156 if (![self isBeingDismissed]) { 157 [self.presentingViewController dismissViewControllerAnimated:YES 158 completion:nil]; 159 } 160} 161 162- (void)switchCamera { 163 RTCVideoSource* source = self.localVideoTrack.source; 164 if ([source isKindOfClass:[RTCAVFoundationVideoSource class]]) { 165 RTCAVFoundationVideoSource* avSource = (RTCAVFoundationVideoSource*)source; 166 avSource.useBackCamera = !avSource.useBackCamera; 167 _videoCallView.localVideoView.transform = avSource.useBackCamera ? 168 CGAffineTransformIdentity : CGAffineTransformMakeScale(-1, 1); 169 } 170} 171 172- (NSString *)statusTextForState:(RTCICEConnectionState)state { 173 switch (state) { 174 case RTCICEConnectionNew: 175 case RTCICEConnectionChecking: 176 return @"Connecting..."; 177 case RTCICEConnectionConnected: 178 case RTCICEConnectionCompleted: 179 case RTCICEConnectionFailed: 180 case RTCICEConnectionDisconnected: 181 case RTCICEConnectionClosed: 182 case RTCICEConnectionMax: 183 return nil; 184 } 185} 186 187- (void)showAlertWithMessage:(NSString*)message { 188 UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:nil 189 message:message 190 delegate:nil 191 cancelButtonTitle:@"OK" 192 otherButtonTitles:nil]; 193 [alertView show]; 194} 195 196@end 197