Source code

Revision control

Copy as Markdown

Other Tools

/*
* Copyright 2015 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#import "ARDVideoCallView.h"
#import <AVFoundation/AVFoundation.h>
#import "sdk/objc/components/renderer/metal/RTCMTLVideoView.h"
#import "UIImage+ARDUtilities.h"
static CGFloat const kButtonPadding = 16;
static CGFloat const kButtonSize = 48;
static CGFloat const kLocalVideoViewSize = 120;
static CGFloat const kLocalVideoViewPadding = 8;
static CGFloat const kStatusBarHeight = 20;
@interface ARDVideoCallView () <RTC_OBJC_TYPE (RTCVideoViewDelegate)>
@end
@implementation ARDVideoCallView {
UIButton *_routeChangeButton;
UIButton *_cameraSwitchButton;
UIButton *_hangupButton;
CGSize _remoteVideoSize;
}
@synthesize statusLabel = _statusLabel;
@synthesize localVideoView = _localVideoView;
@synthesize remoteVideoView = _remoteVideoView;
@synthesize statsView = _statsView;
@synthesize delegate = _delegate;
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_remoteVideoView = [[RTC_OBJC_TYPE(RTCMTLVideoView) alloc] initWithFrame:CGRectZero];
[self addSubview:_remoteVideoView];
_localVideoView = [[RTC_OBJC_TYPE(RTCCameraPreviewView) alloc] initWithFrame:CGRectZero];
[self addSubview:_localVideoView];
_statsView = [[ARDStatsView alloc] initWithFrame:CGRectZero];
_statsView.hidden = YES;
[self addSubview:_statsView];
_routeChangeButton = [UIButton buttonWithType:UIButtonTypeCustom];
_routeChangeButton.backgroundColor = [UIColor grayColor];
_routeChangeButton.layer.cornerRadius = kButtonSize / 2;
_routeChangeButton.layer.masksToBounds = YES;
UIImage *image = [UIImage imageForName:@"ic_surround_sound_black_24dp.png"
color:[UIColor whiteColor]];
[_routeChangeButton setImage:image forState:UIControlStateNormal];
[_routeChangeButton addTarget:self
action:@selector(onRouteChange:)
forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_routeChangeButton];
// TODO(tkchin): don't display this if we can't actually do camera switch.
_cameraSwitchButton = [UIButton buttonWithType:UIButtonTypeCustom];
_cameraSwitchButton.backgroundColor = [UIColor grayColor];
_cameraSwitchButton.layer.cornerRadius = kButtonSize / 2;
_cameraSwitchButton.layer.masksToBounds = YES;
image = [UIImage imageForName:@"ic_switch_video_black_24dp.png" color:[UIColor whiteColor]];
[_cameraSwitchButton setImage:image forState:UIControlStateNormal];
[_cameraSwitchButton addTarget:self
action:@selector(onCameraSwitch:)
forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_cameraSwitchButton];
_hangupButton = [UIButton buttonWithType:UIButtonTypeCustom];
_hangupButton.backgroundColor = [UIColor redColor];
_hangupButton.layer.cornerRadius = kButtonSize / 2;
_hangupButton.layer.masksToBounds = YES;
image = [UIImage imageForName:@"ic_call_end_black_24dp.png"
color:[UIColor whiteColor]];
[_hangupButton setImage:image forState:UIControlStateNormal];
[_hangupButton addTarget:self
action:@selector(onHangup:)
forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_hangupButton];
_statusLabel = [[UILabel alloc] initWithFrame:CGRectZero];
_statusLabel.font = [UIFont fontWithName:@"Roboto" size:16];
_statusLabel.textColor = [UIColor whiteColor];
[self addSubview:_statusLabel];
UITapGestureRecognizer *tapRecognizer =
[[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(didTripleTap:)];
tapRecognizer.numberOfTapsRequired = 3;
[self addGestureRecognizer:tapRecognizer];
}
return self;
}
- (void)layoutSubviews {
CGRect bounds = self.bounds;
if (_remoteVideoSize.width > 0 && _remoteVideoSize.height > 0) {
// Aspect fill remote video into bounds.
CGRect remoteVideoFrame =
AVMakeRectWithAspectRatioInsideRect(_remoteVideoSize, bounds);
CGFloat scale = 1;
if (remoteVideoFrame.size.width > remoteVideoFrame.size.height) {
// Scale by height.
scale = bounds.size.height / remoteVideoFrame.size.height;
} else {
// Scale by width.
scale = bounds.size.width / remoteVideoFrame.size.width;
}
remoteVideoFrame.size.height *= scale;
remoteVideoFrame.size.width *= scale;
_remoteVideoView.frame = remoteVideoFrame;
_remoteVideoView.center =
CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
} else {
_remoteVideoView.frame = bounds;
}
// Aspect fit local video view into a square box.
CGRect localVideoFrame =
CGRectMake(0, 0, kLocalVideoViewSize, kLocalVideoViewSize);
// Place the view in the bottom right.
localVideoFrame.origin.x = CGRectGetMaxX(bounds)
- localVideoFrame.size.width - kLocalVideoViewPadding;
localVideoFrame.origin.y = CGRectGetMaxY(bounds)
- localVideoFrame.size.height - kLocalVideoViewPadding;
_localVideoView.frame = localVideoFrame;
// Place stats at the top.
CGSize statsSize = [_statsView sizeThatFits:bounds.size];
_statsView.frame = CGRectMake(CGRectGetMinX(bounds),
CGRectGetMinY(bounds) + kStatusBarHeight,
statsSize.width, statsSize.height);
// Place hangup button in the bottom left.
_hangupButton.frame =
CGRectMake(CGRectGetMinX(bounds) + kButtonPadding,
CGRectGetMaxY(bounds) - kButtonPadding -
kButtonSize,
kButtonSize,
kButtonSize);
// Place button to the right of hangup button.
CGRect cameraSwitchFrame = _hangupButton.frame;
cameraSwitchFrame.origin.x =
CGRectGetMaxX(cameraSwitchFrame) + kButtonPadding;
_cameraSwitchButton.frame = cameraSwitchFrame;
// Place route button to the right of camera button.
CGRect routeChangeFrame = _cameraSwitchButton.frame;
routeChangeFrame.origin.x =
CGRectGetMaxX(routeChangeFrame) + kButtonPadding;
_routeChangeButton.frame = routeChangeFrame;
[_statusLabel sizeToFit];
_statusLabel.center =
CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
}
#pragma mark - RTC_OBJC_TYPE(RTCVideoViewDelegate)
- (void)videoView:(id<RTC_OBJC_TYPE(RTCVideoRenderer)>)videoView didChangeVideoSize:(CGSize)size {
if (videoView == _remoteVideoView) {
_remoteVideoSize = size;
}
[self setNeedsLayout];
}
#pragma mark - Private
- (void)onCameraSwitch:(UIButton *)sender {
sender.enabled = false;
[_delegate videoCallView:self
shouldSwitchCameraWithCompletion:^(NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^(void) {
sender.enabled = true;
});
}];
}
- (void)onRouteChange:(UIButton *)sender {
sender.enabled = false;
__weak ARDVideoCallView *weakSelf = self;
[_delegate videoCallView:self
shouldChangeRouteWithCompletion:^(void) {
ARDVideoCallView *strongSelf = weakSelf;
if (strongSelf) {
dispatch_async(dispatch_get_main_queue(), ^(void) {
sender.enabled = true;
});
}
}];
}
- (void)onHangup:(id)sender {
[_delegate videoCallViewDidHangup:self];
}
- (void)didTripleTap:(UITapGestureRecognizer *)recognizer {
[_delegate videoCallViewDidEnableStats:self];
}
@end