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