iOS camera switching video capturer.
Introduces a new capture class derived from cricket::VideoCapturer that
provides the ability to switch cameras and updates AppRTCDemo to use it.
Some future work pending to clean up AppRTCDemo UI.
BUG=4070
R=magjed@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/48279005
Cr-Commit-Position: refs/heads/master@{#9137}
diff --git a/talk/examples/objc/AppRTCDemo/ios/ARDVideoCallView.h b/talk/examples/objc/AppRTCDemo/ios/ARDVideoCallView.h
index 1bb5b5c..7c1decb 100644
--- a/talk/examples/objc/AppRTCDemo/ios/ARDVideoCallView.h
+++ b/talk/examples/objc/AppRTCDemo/ios/ARDVideoCallView.h
@@ -32,6 +32,9 @@
@class ARDVideoCallView;
@protocol ARDVideoCallViewDelegate <NSObject>
+// Called when the camera switch button is pressed.
+- (void)videoCallViewDidSwitchCamera:(ARDVideoCallView *)view;
+
// Called when the hangup button is pressed.
- (void)videoCallViewDidHangup:(ARDVideoCallView *)view;
diff --git a/talk/examples/objc/AppRTCDemo/ios/ARDVideoCallView.m b/talk/examples/objc/AppRTCDemo/ios/ARDVideoCallView.m
index 852951e..47bfe89 100644
--- a/talk/examples/objc/AppRTCDemo/ios/ARDVideoCallView.m
+++ b/talk/examples/objc/AppRTCDemo/ios/ARDVideoCallView.m
@@ -30,19 +30,20 @@
#import <AVFoundation/AVFoundation.h>
#import "UIImage+ARDUtilities.h"
-static CGFloat const kHangupButtonPadding = 16;
-static CGFloat const kHangupButtonSize = 48;
-static CGFloat const kLocalVideoViewWidth = 90;
-static CGFloat const kLocalVideoViewHeight = 120;
+static CGFloat const kButtonPadding = 16;
+static CGFloat const kButtonSize = 48;
+static CGFloat const kLocalVideoViewSize = 120;
static CGFloat const kLocalVideoViewPadding = 8;
@interface ARDVideoCallView () <RTCEAGLVideoViewDelegate>
@end
@implementation ARDVideoCallView {
+ UIButton *_cameraSwitchButton;
UIButton *_hangupButton;
CGSize _localVideoSize;
CGSize _remoteVideoSize;
+ BOOL _useRearCamera;
}
@synthesize statusLabel = _statusLabel;
@@ -56,17 +57,30 @@
_remoteVideoView.delegate = self;
[self addSubview:_remoteVideoView];
+ // TODO(tkchin): replace this with a view that renders layer from
+ // AVCaptureSession.
_localVideoView = [[RTCEAGLVideoView alloc] initWithFrame:CGRectZero];
- _localVideoView.transform = CGAffineTransformMakeScale(-1, 1);
_localVideoView.delegate = self;
[self addSubview:_localVideoView];
+ // TODO(tkchin): don't display this if we can't actually do camera switch.
+ _cameraSwitchButton = [UIButton buttonWithType:UIButtonTypeCustom];
+ _cameraSwitchButton.backgroundColor = [UIColor whiteColor];
+ _cameraSwitchButton.layer.cornerRadius = kButtonSize / 2;
+ _cameraSwitchButton.layer.masksToBounds = YES;
+ UIImage *image = [UIImage imageNamed:@"ic_switch_video_black_24dp.png"];
+ [_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 = kHangupButtonSize / 2;
+ _hangupButton.layer.cornerRadius = kButtonSize / 2;
_hangupButton.layer.masksToBounds = YES;
- UIImage *image = [UIImage imageForName:@"ic_call_end_black_24dp.png"
- color:[UIColor whiteColor]];
+ image = [UIImage imageForName:@"ic_call_end_black_24dp.png"
+ color:[UIColor whiteColor]];
[_hangupButton setImage:image forState:UIControlStateNormal];
[_hangupButton addTarget:self
action:@selector(onHangup:)
@@ -104,21 +118,36 @@
_remoteVideoView.frame = bounds;
}
- CGRect localVideoFrame = CGRectZero;
- localVideoFrame.origin.x =
- CGRectGetMaxX(bounds) - kLocalVideoViewWidth - kLocalVideoViewPadding;
- localVideoFrame.origin.y =
- CGRectGetMaxY(bounds) - kLocalVideoViewHeight - kLocalVideoViewPadding;
- localVideoFrame.size.width = kLocalVideoViewWidth;
- localVideoFrame.size.height = kLocalVideoViewHeight;
- _localVideoView.frame = localVideoFrame;
+ if (_localVideoSize.width && _localVideoSize.height > 0) {
+ // Aspect fit local video view into a square box.
+ CGRect localVideoFrame =
+ CGRectMake(0, 0, kLocalVideoViewSize, kLocalVideoViewSize);
+ localVideoFrame =
+ AVMakeRectWithAspectRatioInsideRect(_localVideoSize, localVideoFrame);
+ // 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;
+ } else {
+ _localVideoView.frame = bounds;
+ }
+
+ // Place hangup button in the bottom left.
_hangupButton.frame =
- CGRectMake(CGRectGetMinX(bounds) + kHangupButtonPadding,
- CGRectGetMaxY(bounds) - kHangupButtonPadding -
- kHangupButtonSize,
- kHangupButtonSize,
- kHangupButtonSize);
+ 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;
[_statusLabel sizeToFit];
_statusLabel.center =
@@ -130,6 +159,7 @@
- (void)videoView:(RTCEAGLVideoView*)videoView didChangeVideoSize:(CGSize)size {
if (videoView == _localVideoView) {
_localVideoSize = size;
+ _localVideoView.hidden = CGSizeEqualToSize(CGSizeZero, _localVideoSize);
} else if (videoView == _remoteVideoView) {
_remoteVideoSize = size;
}
@@ -138,6 +168,10 @@
#pragma mark - Private
+- (void)onCameraSwitch:(id)sender {
+ [_delegate videoCallViewDidSwitchCamera:self];
+}
+
- (void)onHangup:(id)sender {
[_delegate videoCallViewDidHangup:self];
}
diff --git a/talk/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.m b/talk/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.m
index af4aaff..b12a61a 100644
--- a/talk/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.m
+++ b/talk/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.m
@@ -27,11 +27,15 @@
#import "ARDVideoCallViewController.h"
+#import "RTCAVFoundationVideoSource.h"
+
#import "ARDAppClient.h"
#import "ARDVideoCallView.h"
@interface ARDVideoCallViewController () <ARDAppClientDelegate,
ARDVideoCallViewDelegate>
+@property(nonatomic, strong) RTCVideoTrack *localVideoTrack;
+@property(nonatomic, strong) RTCVideoTrack *remoteVideoTrack;
@property(nonatomic, readonly) ARDVideoCallView *videoCallView;
@end
@@ -90,19 +94,13 @@
- (void)appClient:(ARDAppClient *)client
didReceiveLocalVideoTrack:(RTCVideoTrack *)localVideoTrack {
- if (!_localVideoTrack) {
- _localVideoTrack = localVideoTrack;
- [_localVideoTrack addRenderer:_videoCallView.localVideoView];
- }
+ self.localVideoTrack = localVideoTrack;
}
- (void)appClient:(ARDAppClient *)client
didReceiveRemoteVideoTrack:(RTCVideoTrack *)remoteVideoTrack {
- if (!_remoteVideoTrack) {
- _remoteVideoTrack = remoteVideoTrack;
- [_remoteVideoTrack addRenderer:_videoCallView.remoteVideoView];
- _videoCallView.statusLabel.hidden = YES;
- }
+ self.remoteVideoTrack = remoteVideoTrack;
+ _videoCallView.statusLabel.hidden = YES;
}
- (void)appClient:(ARDAppClient *)client
@@ -119,24 +117,54 @@
[self hangup];
}
+- (void)videoCallViewDidSwitchCamera:(ARDVideoCallView *)view {
+ // TODO(tkchin): Rate limit this so you can't tap continously on it.
+ // Probably through an animation.
+ [self switchCamera];
+}
+
#pragma mark - Private
+- (void)setLocalVideoTrack:(RTCVideoTrack *)localVideoTrack {
+ if (_localVideoTrack == localVideoTrack) {
+ return;
+ }
+ [_localVideoTrack removeRenderer:_videoCallView.localVideoView];
+ _localVideoTrack = nil;
+ [_videoCallView.localVideoView renderFrame:nil];
+ _localVideoTrack = localVideoTrack;
+ [_localVideoTrack addRenderer:_videoCallView.localVideoView];
+}
+
+- (void)setRemoteVideoTrack:(RTCVideoTrack *)remoteVideoTrack {
+ if (_remoteVideoTrack == remoteVideoTrack) {
+ return;
+ }
+ [_remoteVideoTrack removeRenderer:_videoCallView.localVideoView];
+ _remoteVideoTrack = nil;
+ [_videoCallView.remoteVideoView renderFrame:nil];
+ _remoteVideoTrack = remoteVideoTrack;
+ [_remoteVideoTrack addRenderer:_videoCallView.remoteVideoView];
+}
+
- (void)hangup {
- if (_remoteVideoTrack) {
- [_remoteVideoTrack removeRenderer:_videoCallView.remoteVideoView];
- _remoteVideoTrack = nil;
- [_videoCallView.remoteVideoView renderFrame:nil];
- }
- if (_localVideoTrack) {
- [_localVideoTrack removeRenderer:_videoCallView.localVideoView];
- _localVideoTrack = nil;
- [_videoCallView.localVideoView renderFrame:nil];
- }
+ self.remoteVideoTrack = nil;
+ self.localVideoTrack = nil;
[_client disconnect];
[self.presentingViewController dismissViewControllerAnimated:YES
completion:nil];
}
+- (void)switchCamera {
+ RTCVideoSource* source = self.localVideoTrack.source;
+ if ([source isKindOfClass:[RTCAVFoundationVideoSource class]]) {
+ RTCAVFoundationVideoSource* avSource = (RTCAVFoundationVideoSource*)source;
+ avSource.useBackCamera = !avSource.useBackCamera;
+ _videoCallView.localVideoView.transform = avSource.useBackCamera ?
+ CGAffineTransformIdentity : CGAffineTransformMakeScale(-1, 1);
+ }
+}
+
- (NSString *)statusTextForState:(RTCICEConnectionState)state {
switch (state) {
case RTCICEConnectionNew:
diff --git a/talk/examples/objc/AppRTCDemo/ios/resources/ic_switch_video_black_24dp.png b/talk/examples/objc/AppRTCDemo/ios/resources/ic_switch_video_black_24dp.png
new file mode 100644
index 0000000..85271c8
--- /dev/null
+++ b/talk/examples/objc/AppRTCDemo/ios/resources/ic_switch_video_black_24dp.png
Binary files differ
diff --git a/talk/examples/objc/AppRTCDemo/ios/resources/ic_switch_video_black_24dp@2x.png b/talk/examples/objc/AppRTCDemo/ios/resources/ic_switch_video_black_24dp@2x.png
new file mode 100644
index 0000000..62b13a6
--- /dev/null
+++ b/talk/examples/objc/AppRTCDemo/ios/resources/ic_switch_video_black_24dp@2x.png
Binary files differ