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 "ARDVideoCallView.h" 12*d9f75844SAndroid Build Coastguard Worker 13*d9f75844SAndroid Build Coastguard Worker#import <AVFoundation/AVFoundation.h> 14*d9f75844SAndroid Build Coastguard Worker 15*d9f75844SAndroid Build Coastguard Worker#import "sdk/objc/components/renderer/metal/RTCMTLVideoView.h" 16*d9f75844SAndroid Build Coastguard Worker 17*d9f75844SAndroid Build Coastguard Worker#import "UIImage+ARDUtilities.h" 18*d9f75844SAndroid Build Coastguard Worker 19*d9f75844SAndroid Build Coastguard Workerstatic CGFloat const kButtonPadding = 16; 20*d9f75844SAndroid Build Coastguard Workerstatic CGFloat const kButtonSize = 48; 21*d9f75844SAndroid Build Coastguard Workerstatic CGFloat const kLocalVideoViewSize = 120; 22*d9f75844SAndroid Build Coastguard Workerstatic CGFloat const kLocalVideoViewPadding = 8; 23*d9f75844SAndroid Build Coastguard Workerstatic CGFloat const kStatusBarHeight = 20; 24*d9f75844SAndroid Build Coastguard Worker 25*d9f75844SAndroid Build Coastguard Worker@interface ARDVideoCallView () <RTC_OBJC_TYPE (RTCVideoViewDelegate)> 26*d9f75844SAndroid Build Coastguard Worker@end 27*d9f75844SAndroid Build Coastguard Worker 28*d9f75844SAndroid Build Coastguard Worker@implementation ARDVideoCallView { 29*d9f75844SAndroid Build Coastguard Worker UIButton *_routeChangeButton; 30*d9f75844SAndroid Build Coastguard Worker UIButton *_cameraSwitchButton; 31*d9f75844SAndroid Build Coastguard Worker UIButton *_hangupButton; 32*d9f75844SAndroid Build Coastguard Worker CGSize _remoteVideoSize; 33*d9f75844SAndroid Build Coastguard Worker} 34*d9f75844SAndroid Build Coastguard Worker 35*d9f75844SAndroid Build Coastguard Worker@synthesize statusLabel = _statusLabel; 36*d9f75844SAndroid Build Coastguard Worker@synthesize localVideoView = _localVideoView; 37*d9f75844SAndroid Build Coastguard Worker@synthesize remoteVideoView = _remoteVideoView; 38*d9f75844SAndroid Build Coastguard Worker@synthesize statsView = _statsView; 39*d9f75844SAndroid Build Coastguard Worker@synthesize delegate = _delegate; 40*d9f75844SAndroid Build Coastguard Worker 41*d9f75844SAndroid Build Coastguard Worker- (instancetype)initWithFrame:(CGRect)frame { 42*d9f75844SAndroid Build Coastguard Worker if (self = [super initWithFrame:frame]) { 43*d9f75844SAndroid Build Coastguard Worker 44*d9f75844SAndroid Build Coastguard Worker _remoteVideoView = [[RTC_OBJC_TYPE(RTCMTLVideoView) alloc] initWithFrame:CGRectZero]; 45*d9f75844SAndroid Build Coastguard Worker 46*d9f75844SAndroid Build Coastguard Worker [self addSubview:_remoteVideoView]; 47*d9f75844SAndroid Build Coastguard Worker 48*d9f75844SAndroid Build Coastguard Worker _localVideoView = [[RTC_OBJC_TYPE(RTCCameraPreviewView) alloc] initWithFrame:CGRectZero]; 49*d9f75844SAndroid Build Coastguard Worker [self addSubview:_localVideoView]; 50*d9f75844SAndroid Build Coastguard Worker 51*d9f75844SAndroid Build Coastguard Worker _statsView = [[ARDStatsView alloc] initWithFrame:CGRectZero]; 52*d9f75844SAndroid Build Coastguard Worker _statsView.hidden = YES; 53*d9f75844SAndroid Build Coastguard Worker [self addSubview:_statsView]; 54*d9f75844SAndroid Build Coastguard Worker 55*d9f75844SAndroid Build Coastguard Worker _routeChangeButton = [UIButton buttonWithType:UIButtonTypeCustom]; 56*d9f75844SAndroid Build Coastguard Worker _routeChangeButton.backgroundColor = [UIColor grayColor]; 57*d9f75844SAndroid Build Coastguard Worker _routeChangeButton.layer.cornerRadius = kButtonSize / 2; 58*d9f75844SAndroid Build Coastguard Worker _routeChangeButton.layer.masksToBounds = YES; 59*d9f75844SAndroid Build Coastguard Worker UIImage *image = [UIImage imageForName:@"ic_surround_sound_black_24dp.png" 60*d9f75844SAndroid Build Coastguard Worker color:[UIColor whiteColor]]; 61*d9f75844SAndroid Build Coastguard Worker [_routeChangeButton setImage:image forState:UIControlStateNormal]; 62*d9f75844SAndroid Build Coastguard Worker [_routeChangeButton addTarget:self 63*d9f75844SAndroid Build Coastguard Worker action:@selector(onRouteChange:) 64*d9f75844SAndroid Build Coastguard Worker forControlEvents:UIControlEventTouchUpInside]; 65*d9f75844SAndroid Build Coastguard Worker [self addSubview:_routeChangeButton]; 66*d9f75844SAndroid Build Coastguard Worker 67*d9f75844SAndroid Build Coastguard Worker // TODO(tkchin): don't display this if we can't actually do camera switch. 68*d9f75844SAndroid Build Coastguard Worker _cameraSwitchButton = [UIButton buttonWithType:UIButtonTypeCustom]; 69*d9f75844SAndroid Build Coastguard Worker _cameraSwitchButton.backgroundColor = [UIColor grayColor]; 70*d9f75844SAndroid Build Coastguard Worker _cameraSwitchButton.layer.cornerRadius = kButtonSize / 2; 71*d9f75844SAndroid Build Coastguard Worker _cameraSwitchButton.layer.masksToBounds = YES; 72*d9f75844SAndroid Build Coastguard Worker image = [UIImage imageForName:@"ic_switch_video_black_24dp.png" color:[UIColor whiteColor]]; 73*d9f75844SAndroid Build Coastguard Worker [_cameraSwitchButton setImage:image forState:UIControlStateNormal]; 74*d9f75844SAndroid Build Coastguard Worker [_cameraSwitchButton addTarget:self 75*d9f75844SAndroid Build Coastguard Worker action:@selector(onCameraSwitch:) 76*d9f75844SAndroid Build Coastguard Worker forControlEvents:UIControlEventTouchUpInside]; 77*d9f75844SAndroid Build Coastguard Worker [self addSubview:_cameraSwitchButton]; 78*d9f75844SAndroid Build Coastguard Worker 79*d9f75844SAndroid Build Coastguard Worker _hangupButton = [UIButton buttonWithType:UIButtonTypeCustom]; 80*d9f75844SAndroid Build Coastguard Worker _hangupButton.backgroundColor = [UIColor redColor]; 81*d9f75844SAndroid Build Coastguard Worker _hangupButton.layer.cornerRadius = kButtonSize / 2; 82*d9f75844SAndroid Build Coastguard Worker _hangupButton.layer.masksToBounds = YES; 83*d9f75844SAndroid Build Coastguard Worker image = [UIImage imageForName:@"ic_call_end_black_24dp.png" 84*d9f75844SAndroid Build Coastguard Worker color:[UIColor whiteColor]]; 85*d9f75844SAndroid Build Coastguard Worker [_hangupButton setImage:image forState:UIControlStateNormal]; 86*d9f75844SAndroid Build Coastguard Worker [_hangupButton addTarget:self 87*d9f75844SAndroid Build Coastguard Worker action:@selector(onHangup:) 88*d9f75844SAndroid Build Coastguard Worker forControlEvents:UIControlEventTouchUpInside]; 89*d9f75844SAndroid Build Coastguard Worker [self addSubview:_hangupButton]; 90*d9f75844SAndroid Build Coastguard Worker 91*d9f75844SAndroid Build Coastguard Worker _statusLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 92*d9f75844SAndroid Build Coastguard Worker _statusLabel.font = [UIFont fontWithName:@"Roboto" size:16]; 93*d9f75844SAndroid Build Coastguard Worker _statusLabel.textColor = [UIColor whiteColor]; 94*d9f75844SAndroid Build Coastguard Worker [self addSubview:_statusLabel]; 95*d9f75844SAndroid Build Coastguard Worker 96*d9f75844SAndroid Build Coastguard Worker UITapGestureRecognizer *tapRecognizer = 97*d9f75844SAndroid Build Coastguard Worker [[UITapGestureRecognizer alloc] 98*d9f75844SAndroid Build Coastguard Worker initWithTarget:self 99*d9f75844SAndroid Build Coastguard Worker action:@selector(didTripleTap:)]; 100*d9f75844SAndroid Build Coastguard Worker tapRecognizer.numberOfTapsRequired = 3; 101*d9f75844SAndroid Build Coastguard Worker [self addGestureRecognizer:tapRecognizer]; 102*d9f75844SAndroid Build Coastguard Worker } 103*d9f75844SAndroid Build Coastguard Worker return self; 104*d9f75844SAndroid Build Coastguard Worker} 105*d9f75844SAndroid Build Coastguard Worker 106*d9f75844SAndroid Build Coastguard Worker- (void)layoutSubviews { 107*d9f75844SAndroid Build Coastguard Worker CGRect bounds = self.bounds; 108*d9f75844SAndroid Build Coastguard Worker if (_remoteVideoSize.width > 0 && _remoteVideoSize.height > 0) { 109*d9f75844SAndroid Build Coastguard Worker // Aspect fill remote video into bounds. 110*d9f75844SAndroid Build Coastguard Worker CGRect remoteVideoFrame = 111*d9f75844SAndroid Build Coastguard Worker AVMakeRectWithAspectRatioInsideRect(_remoteVideoSize, bounds); 112*d9f75844SAndroid Build Coastguard Worker CGFloat scale = 1; 113*d9f75844SAndroid Build Coastguard Worker if (remoteVideoFrame.size.width > remoteVideoFrame.size.height) { 114*d9f75844SAndroid Build Coastguard Worker // Scale by height. 115*d9f75844SAndroid Build Coastguard Worker scale = bounds.size.height / remoteVideoFrame.size.height; 116*d9f75844SAndroid Build Coastguard Worker } else { 117*d9f75844SAndroid Build Coastguard Worker // Scale by width. 118*d9f75844SAndroid Build Coastguard Worker scale = bounds.size.width / remoteVideoFrame.size.width; 119*d9f75844SAndroid Build Coastguard Worker } 120*d9f75844SAndroid Build Coastguard Worker remoteVideoFrame.size.height *= scale; 121*d9f75844SAndroid Build Coastguard Worker remoteVideoFrame.size.width *= scale; 122*d9f75844SAndroid Build Coastguard Worker _remoteVideoView.frame = remoteVideoFrame; 123*d9f75844SAndroid Build Coastguard Worker _remoteVideoView.center = 124*d9f75844SAndroid Build Coastguard Worker CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); 125*d9f75844SAndroid Build Coastguard Worker } else { 126*d9f75844SAndroid Build Coastguard Worker _remoteVideoView.frame = bounds; 127*d9f75844SAndroid Build Coastguard Worker } 128*d9f75844SAndroid Build Coastguard Worker 129*d9f75844SAndroid Build Coastguard Worker // Aspect fit local video view into a square box. 130*d9f75844SAndroid Build Coastguard Worker CGRect localVideoFrame = 131*d9f75844SAndroid Build Coastguard Worker CGRectMake(0, 0, kLocalVideoViewSize, kLocalVideoViewSize); 132*d9f75844SAndroid Build Coastguard Worker // Place the view in the bottom right. 133*d9f75844SAndroid Build Coastguard Worker localVideoFrame.origin.x = CGRectGetMaxX(bounds) 134*d9f75844SAndroid Build Coastguard Worker - localVideoFrame.size.width - kLocalVideoViewPadding; 135*d9f75844SAndroid Build Coastguard Worker localVideoFrame.origin.y = CGRectGetMaxY(bounds) 136*d9f75844SAndroid Build Coastguard Worker - localVideoFrame.size.height - kLocalVideoViewPadding; 137*d9f75844SAndroid Build Coastguard Worker _localVideoView.frame = localVideoFrame; 138*d9f75844SAndroid Build Coastguard Worker 139*d9f75844SAndroid Build Coastguard Worker // Place stats at the top. 140*d9f75844SAndroid Build Coastguard Worker CGSize statsSize = [_statsView sizeThatFits:bounds.size]; 141*d9f75844SAndroid Build Coastguard Worker _statsView.frame = CGRectMake(CGRectGetMinX(bounds), 142*d9f75844SAndroid Build Coastguard Worker CGRectGetMinY(bounds) + kStatusBarHeight, 143*d9f75844SAndroid Build Coastguard Worker statsSize.width, statsSize.height); 144*d9f75844SAndroid Build Coastguard Worker 145*d9f75844SAndroid Build Coastguard Worker // Place hangup button in the bottom left. 146*d9f75844SAndroid Build Coastguard Worker _hangupButton.frame = 147*d9f75844SAndroid Build Coastguard Worker CGRectMake(CGRectGetMinX(bounds) + kButtonPadding, 148*d9f75844SAndroid Build Coastguard Worker CGRectGetMaxY(bounds) - kButtonPadding - 149*d9f75844SAndroid Build Coastguard Worker kButtonSize, 150*d9f75844SAndroid Build Coastguard Worker kButtonSize, 151*d9f75844SAndroid Build Coastguard Worker kButtonSize); 152*d9f75844SAndroid Build Coastguard Worker 153*d9f75844SAndroid Build Coastguard Worker // Place button to the right of hangup button. 154*d9f75844SAndroid Build Coastguard Worker CGRect cameraSwitchFrame = _hangupButton.frame; 155*d9f75844SAndroid Build Coastguard Worker cameraSwitchFrame.origin.x = 156*d9f75844SAndroid Build Coastguard Worker CGRectGetMaxX(cameraSwitchFrame) + kButtonPadding; 157*d9f75844SAndroid Build Coastguard Worker _cameraSwitchButton.frame = cameraSwitchFrame; 158*d9f75844SAndroid Build Coastguard Worker 159*d9f75844SAndroid Build Coastguard Worker // Place route button to the right of camera button. 160*d9f75844SAndroid Build Coastguard Worker CGRect routeChangeFrame = _cameraSwitchButton.frame; 161*d9f75844SAndroid Build Coastguard Worker routeChangeFrame.origin.x = 162*d9f75844SAndroid Build Coastguard Worker CGRectGetMaxX(routeChangeFrame) + kButtonPadding; 163*d9f75844SAndroid Build Coastguard Worker _routeChangeButton.frame = routeChangeFrame; 164*d9f75844SAndroid Build Coastguard Worker 165*d9f75844SAndroid Build Coastguard Worker [_statusLabel sizeToFit]; 166*d9f75844SAndroid Build Coastguard Worker _statusLabel.center = 167*d9f75844SAndroid Build Coastguard Worker CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); 168*d9f75844SAndroid Build Coastguard Worker} 169*d9f75844SAndroid Build Coastguard Worker 170*d9f75844SAndroid Build Coastguard Worker#pragma mark - RTC_OBJC_TYPE(RTCVideoViewDelegate) 171*d9f75844SAndroid Build Coastguard Worker 172*d9f75844SAndroid Build Coastguard Worker- (void)videoView:(id<RTC_OBJC_TYPE(RTCVideoRenderer)>)videoView didChangeVideoSize:(CGSize)size { 173*d9f75844SAndroid Build Coastguard Worker if (videoView == _remoteVideoView) { 174*d9f75844SAndroid Build Coastguard Worker _remoteVideoSize = size; 175*d9f75844SAndroid Build Coastguard Worker } 176*d9f75844SAndroid Build Coastguard Worker [self setNeedsLayout]; 177*d9f75844SAndroid Build Coastguard Worker} 178*d9f75844SAndroid Build Coastguard Worker 179*d9f75844SAndroid Build Coastguard Worker#pragma mark - Private 180*d9f75844SAndroid Build Coastguard Worker 181*d9f75844SAndroid Build Coastguard Worker- (void)onCameraSwitch:(UIButton *)sender { 182*d9f75844SAndroid Build Coastguard Worker sender.enabled = false; 183*d9f75844SAndroid Build Coastguard Worker [_delegate videoCallView:self 184*d9f75844SAndroid Build Coastguard Worker shouldSwitchCameraWithCompletion:^(NSError *error) { 185*d9f75844SAndroid Build Coastguard Worker dispatch_async(dispatch_get_main_queue(), ^(void) { 186*d9f75844SAndroid Build Coastguard Worker sender.enabled = true; 187*d9f75844SAndroid Build Coastguard Worker }); 188*d9f75844SAndroid Build Coastguard Worker }]; 189*d9f75844SAndroid Build Coastguard Worker} 190*d9f75844SAndroid Build Coastguard Worker 191*d9f75844SAndroid Build Coastguard Worker- (void)onRouteChange:(UIButton *)sender { 192*d9f75844SAndroid Build Coastguard Worker sender.enabled = false; 193*d9f75844SAndroid Build Coastguard Worker __weak ARDVideoCallView *weakSelf = self; 194*d9f75844SAndroid Build Coastguard Worker [_delegate videoCallView:self 195*d9f75844SAndroid Build Coastguard Worker shouldChangeRouteWithCompletion:^(void) { 196*d9f75844SAndroid Build Coastguard Worker ARDVideoCallView *strongSelf = weakSelf; 197*d9f75844SAndroid Build Coastguard Worker if (strongSelf) { 198*d9f75844SAndroid Build Coastguard Worker dispatch_async(dispatch_get_main_queue(), ^(void) { 199*d9f75844SAndroid Build Coastguard Worker sender.enabled = true; 200*d9f75844SAndroid Build Coastguard Worker }); 201*d9f75844SAndroid Build Coastguard Worker } 202*d9f75844SAndroid Build Coastguard Worker }]; 203*d9f75844SAndroid Build Coastguard Worker} 204*d9f75844SAndroid Build Coastguard Worker 205*d9f75844SAndroid Build Coastguard Worker- (void)onHangup:(id)sender { 206*d9f75844SAndroid Build Coastguard Worker [_delegate videoCallViewDidHangup:self]; 207*d9f75844SAndroid Build Coastguard Worker} 208*d9f75844SAndroid Build Coastguard Worker 209*d9f75844SAndroid Build Coastguard Worker- (void)didTripleTap:(UITapGestureRecognizer *)recognizer { 210*d9f75844SAndroid Build Coastguard Worker [_delegate videoCallViewDidEnableStats:self]; 211*d9f75844SAndroid Build Coastguard Worker} 212*d9f75844SAndroid Build Coastguard Worker 213*d9f75844SAndroid Build Coastguard Worker@end 214