• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <WebRTC/RTCAudioSession.h>
14#import <WebRTC/RTCCameraVideoCapturer.h>
15#import <WebRTC/RTCDispatcher.h>
16#import <WebRTC/RTCLogging.h>
17#import <WebRTC/RTCMediaConstraints.h>
18
19#import "ARDAppClient.h"
20#import "ARDCaptureController.h"
21#import "ARDFileCaptureController.h"
22#import "ARDSettingsModel.h"
23#import "ARDVideoCallView.h"
24
25@interface ARDVideoCallViewController () <ARDAppClientDelegate,
26                                          ARDVideoCallViewDelegate,
27                                          RTC_OBJC_TYPE (RTCAudioSessionDelegate)>
28@property(nonatomic, strong) RTC_OBJC_TYPE(RTCVideoTrack) * remoteVideoTrack;
29@property(nonatomic, readonly) ARDVideoCallView *videoCallView;
30@property(nonatomic, assign) AVAudioSessionPortOverride portOverride;
31@end
32
33@implementation ARDVideoCallViewController {
34  ARDAppClient *_client;
35  RTC_OBJC_TYPE(RTCVideoTrack) * _remoteVideoTrack;
36  ARDCaptureController *_captureController;
37  ARDFileCaptureController *_fileCaptureController NS_AVAILABLE_IOS(10);
38}
39
40@synthesize videoCallView = _videoCallView;
41@synthesize remoteVideoTrack = _remoteVideoTrack;
42@synthesize delegate = _delegate;
43@synthesize portOverride = _portOverride;
44
45- (instancetype)initForRoom:(NSString *)room
46                 isLoopback:(BOOL)isLoopback
47                   delegate:(id<ARDVideoCallViewControllerDelegate>)delegate {
48  if (self = [super init]) {
49    ARDSettingsModel *settingsModel = [[ARDSettingsModel alloc] init];
50    _delegate = delegate;
51
52    _client = [[ARDAppClient alloc] initWithDelegate:self];
53    [_client connectToRoomWithId:room settings:settingsModel isLoopback:isLoopback];
54  }
55  return self;
56}
57
58- (void)loadView {
59  _videoCallView = [[ARDVideoCallView alloc] initWithFrame:CGRectZero];
60  _videoCallView.delegate = self;
61  _videoCallView.statusLabel.text =
62      [self statusTextForState:RTCIceConnectionStateNew];
63  self.view = _videoCallView;
64
65  RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance];
66  [session addDelegate:self];
67}
68
69- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
70  return UIInterfaceOrientationMaskAll;
71}
72
73#pragma mark - ARDAppClientDelegate
74
75- (void)appClient:(ARDAppClient *)client
76    didChangeState:(ARDAppClientState)state {
77  switch (state) {
78    case kARDAppClientStateConnected:
79      RTCLog(@"Client connected.");
80      break;
81    case kARDAppClientStateConnecting:
82      RTCLog(@"Client connecting.");
83      break;
84    case kARDAppClientStateDisconnected:
85      RTCLog(@"Client disconnected.");
86      [self hangup];
87      break;
88  }
89}
90
91- (void)appClient:(ARDAppClient *)client
92    didChangeConnectionState:(RTCIceConnectionState)state {
93  RTCLog(@"ICE state changed: %ld", (long)state);
94  __weak ARDVideoCallViewController *weakSelf = self;
95  dispatch_async(dispatch_get_main_queue(), ^{
96    ARDVideoCallViewController *strongSelf = weakSelf;
97    strongSelf.videoCallView.statusLabel.text =
98        [strongSelf statusTextForState:state];
99  });
100}
101
102- (void)appClient:(ARDAppClient *)client
103    didCreateLocalCapturer:(RTC_OBJC_TYPE(RTCCameraVideoCapturer) *)localCapturer {
104  _videoCallView.localVideoView.captureSession = localCapturer.captureSession;
105  ARDSettingsModel *settingsModel = [[ARDSettingsModel alloc] init];
106  _captureController =
107      [[ARDCaptureController alloc] initWithCapturer:localCapturer settings:settingsModel];
108  [_captureController startCapture];
109}
110
111- (void)appClient:(ARDAppClient *)client
112    didCreateLocalFileCapturer:(RTC_OBJC_TYPE(RTCFileVideoCapturer) *)fileCapturer {
113#if defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0)
114  if (@available(iOS 10, *)) {
115    _fileCaptureController = [[ARDFileCaptureController alloc] initWithCapturer:fileCapturer];
116    [_fileCaptureController startCapture];
117  }
118#endif
119}
120
121- (void)appClient:(ARDAppClient *)client
122    didReceiveLocalVideoTrack:(RTC_OBJC_TYPE(RTCVideoTrack) *)localVideoTrack {
123}
124
125- (void)appClient:(ARDAppClient *)client
126    didReceiveRemoteVideoTrack:(RTC_OBJC_TYPE(RTCVideoTrack) *)remoteVideoTrack {
127  self.remoteVideoTrack = remoteVideoTrack;
128  __weak ARDVideoCallViewController *weakSelf = self;
129  dispatch_async(dispatch_get_main_queue(), ^{
130    ARDVideoCallViewController *strongSelf = weakSelf;
131    strongSelf.videoCallView.statusLabel.hidden = YES;
132  });
133}
134
135- (void)appClient:(ARDAppClient *)client
136      didGetStats:(NSArray *)stats {
137  _videoCallView.statsView.stats = stats;
138  [_videoCallView setNeedsLayout];
139}
140
141- (void)appClient:(ARDAppClient *)client
142         didError:(NSError *)error {
143  NSString *message =
144      [NSString stringWithFormat:@"%@", error.localizedDescription];
145  [self hangup];
146  [self showAlertWithMessage:message];
147}
148
149#pragma mark - ARDVideoCallViewDelegate
150
151- (void)videoCallViewDidHangup:(ARDVideoCallView *)view {
152  [self hangup];
153}
154
155- (void)videoCallViewDidSwitchCamera:(ARDVideoCallView *)view {
156  // TODO(tkchin): Rate limit this so you can't tap continously on it.
157  // Probably through an animation.
158  [_captureController switchCamera];
159}
160
161- (void)videoCallViewDidChangeRoute:(ARDVideoCallView *)view {
162  AVAudioSessionPortOverride override = AVAudioSessionPortOverrideNone;
163  if (_portOverride == AVAudioSessionPortOverrideNone) {
164    override = AVAudioSessionPortOverrideSpeaker;
165  }
166  [RTC_OBJC_TYPE(RTCDispatcher) dispatchAsyncOnType:RTCDispatcherTypeAudioSession
167                                              block:^{
168                                                RTC_OBJC_TYPE(RTCAudioSession) *session =
169                                                    [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance];
170                                                [session lockForConfiguration];
171                                                NSError *error = nil;
172                                                if ([session overrideOutputAudioPort:override
173                                                                               error:&error]) {
174                                                  self.portOverride = override;
175                                                } else {
176                                                  RTCLogError(@"Error overriding output port: %@",
177                                                              error.localizedDescription);
178                                                }
179                                                [session unlockForConfiguration];
180                                              }];
181}
182
183- (void)videoCallViewDidEnableStats:(ARDVideoCallView *)view {
184  _client.shouldGetStats = YES;
185  _videoCallView.statsView.hidden = NO;
186}
187
188#pragma mark - RTC_OBJC_TYPE(RTCAudioSessionDelegate)
189
190- (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)audioSession
191    didDetectPlayoutGlitch:(int64_t)totalNumberOfGlitches {
192  RTCLog(@"Audio session detected glitch, total: %lld", totalNumberOfGlitches);
193}
194
195#pragma mark - Private
196
197- (void)setRemoteVideoTrack:(RTC_OBJC_TYPE(RTCVideoTrack) *)remoteVideoTrack {
198  if (_remoteVideoTrack == remoteVideoTrack) {
199    return;
200  }
201  [_remoteVideoTrack removeRenderer:_videoCallView.remoteVideoView];
202  _remoteVideoTrack = nil;
203  [_videoCallView.remoteVideoView renderFrame:nil];
204  _remoteVideoTrack = remoteVideoTrack;
205  [_remoteVideoTrack addRenderer:_videoCallView.remoteVideoView];
206}
207
208- (void)hangup {
209  self.remoteVideoTrack = nil;
210  _videoCallView.localVideoView.captureSession = nil;
211  [_captureController stopCapture];
212  _captureController = nil;
213  [_fileCaptureController stopCapture];
214  _fileCaptureController = nil;
215  [_client disconnect];
216  [_delegate viewControllerDidFinish:self];
217}
218
219- (NSString *)statusTextForState:(RTCIceConnectionState)state {
220  switch (state) {
221    case RTCIceConnectionStateNew:
222    case RTCIceConnectionStateChecking:
223      return @"Connecting...";
224    case RTCIceConnectionStateConnected:
225    case RTCIceConnectionStateCompleted:
226    case RTCIceConnectionStateFailed:
227    case RTCIceConnectionStateDisconnected:
228    case RTCIceConnectionStateClosed:
229    case RTCIceConnectionStateCount:
230      return nil;
231  }
232}
233
234- (void)showAlertWithMessage:(NSString*)message {
235  UIAlertController *alert =
236      [UIAlertController alertControllerWithTitle:nil
237                                          message:message
238                                   preferredStyle:UIAlertControllerStyleAlert];
239
240  UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"OK"
241                                                          style:UIAlertActionStyleDefault
242                                                        handler:^(UIAlertAction *action){
243                                                        }];
244
245  [alert addAction:defaultAction];
246  [self presentViewController:alert animated:YES completion:nil];
247}
248
249@end
250