xref: /aosp_15_r20/external/webrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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 "sdk/objc/api/peerconnection/RTCMediaConstraints.h"
14#import "sdk/objc/base/RTCLogging.h"
15#import "sdk/objc/components/audio/RTCAudioSession.h"
16#import "sdk/objc/components/capturer/RTCCameraVideoCapturer.h"
17#import "sdk/objc/helpers/RTCDispatcher.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 didGetStats:(RTC_OBJC_TYPE(RTCStatisticsReport) *)stats {
136  _videoCallView.statsView.stats = stats;
137  [_videoCallView setNeedsLayout];
138}
139
140- (void)appClient:(ARDAppClient *)client
141         didError:(NSError *)error {
142  NSString *message =
143      [NSString stringWithFormat:@"%@", error.localizedDescription];
144  [self hangup];
145  [self showAlertWithMessage:message];
146}
147
148#pragma mark - ARDVideoCallViewDelegate
149
150- (void)videoCallViewDidHangup:(ARDVideoCallView *)view {
151  [self hangup];
152}
153
154- (void)videoCallView:(ARDVideoCallView *)view
155    shouldSwitchCameraWithCompletion:(void (^)(NSError *))completion {
156  [_captureController switchCamera:completion];
157}
158
159- (void)videoCallView:(ARDVideoCallView *)view
160    shouldChangeRouteWithCompletion:(void (^)(void))completion {
161  NSParameterAssert(completion);
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                                                completion();
181                                              }];
182}
183
184- (void)videoCallViewDidEnableStats:(ARDVideoCallView *)view {
185  _client.shouldGetStats = YES;
186  _videoCallView.statsView.hidden = NO;
187}
188
189#pragma mark - RTC_OBJC_TYPE(RTCAudioSessionDelegate)
190
191- (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)audioSession
192    didDetectPlayoutGlitch:(int64_t)totalNumberOfGlitches {
193  RTCLog(@"Audio session detected glitch, total: %lld", totalNumberOfGlitches);
194}
195
196#pragma mark - Private
197
198- (void)setRemoteVideoTrack:(RTC_OBJC_TYPE(RTCVideoTrack) *)remoteVideoTrack {
199  if (_remoteVideoTrack == remoteVideoTrack) {
200    return;
201  }
202  [_remoteVideoTrack removeRenderer:_videoCallView.remoteVideoView];
203  _remoteVideoTrack = nil;
204  [_videoCallView.remoteVideoView renderFrame:nil];
205  _remoteVideoTrack = remoteVideoTrack;
206  [_remoteVideoTrack addRenderer:_videoCallView.remoteVideoView];
207}
208
209- (void)hangup {
210  self.remoteVideoTrack = nil;
211  _videoCallView.localVideoView.captureSession = nil;
212  [_captureController stopCapture];
213  _captureController = nil;
214  [_fileCaptureController stopCapture];
215  _fileCaptureController = nil;
216  [_client disconnect];
217  [_delegate viewControllerDidFinish:self];
218}
219
220- (NSString *)statusTextForState:(RTCIceConnectionState)state {
221  switch (state) {
222    case RTCIceConnectionStateNew:
223    case RTCIceConnectionStateChecking:
224      return @"Connecting...";
225    case RTCIceConnectionStateConnected:
226    case RTCIceConnectionStateCompleted:
227    case RTCIceConnectionStateFailed:
228    case RTCIceConnectionStateDisconnected:
229    case RTCIceConnectionStateClosed:
230    case RTCIceConnectionStateCount:
231      return nil;
232  }
233}
234
235- (void)showAlertWithMessage:(NSString*)message {
236  UIAlertController *alert =
237      [UIAlertController alertControllerWithTitle:nil
238                                          message:message
239                                   preferredStyle:UIAlertControllerStyleAlert];
240
241  UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"OK"
242                                                          style:UIAlertActionStyleDefault
243                                                        handler:^(UIAlertAction *action){
244                                                        }];
245
246  [alert addAction:defaultAction];
247  [self presentViewController:alert animated:YES completion:nil];
248}
249
250@end
251