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