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}
This commit is contained in:
@@ -27,7 +27,15 @@
|
||||
|
||||
#import "ARDAppClient+Internal.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#if defined(WEBRTC_IOS)
|
||||
#import "RTCAVFoundationVideoSource.h"
|
||||
#endif
|
||||
#import "RTCICEServer.h"
|
||||
#import "RTCMediaConstraints.h"
|
||||
#import "RTCMediaStream.h"
|
||||
#import "RTCPair.h"
|
||||
#import "RTCVideoCapturer.h"
|
||||
#import "RTCAVFoundationVideoSource.h"
|
||||
|
||||
#import "ARDAppEngineClient.h"
|
||||
#import "ARDCEODTURNClient.h"
|
||||
@@ -37,13 +45,8 @@
|
||||
#import "ARDUtilities.h"
|
||||
#import "ARDWebSocketChannel.h"
|
||||
#import "RTCICECandidate+JSON.h"
|
||||
#import "RTCICEServer.h"
|
||||
#import "RTCMediaConstraints.h"
|
||||
#import "RTCMediaStream.h"
|
||||
#import "RTCPair.h"
|
||||
#import "RTCSessionDescription+JSON.h"
|
||||
#import "RTCVideoCapturer.h"
|
||||
#import "RTCVideoTrack.h"
|
||||
|
||||
|
||||
static NSString * const kARDDefaultSTUNServerUrl =
|
||||
@"stun:stun.l.google.com:19302";
|
||||
@@ -484,39 +487,33 @@ static NSInteger const kARDAppClientErrorInvalidRoom = -6;
|
||||
|
||||
- (RTCMediaStream *)createLocalMediaStream {
|
||||
RTCMediaStream* localStream = [_factory mediaStreamWithLabel:@"ARDAMS"];
|
||||
RTCVideoTrack* localVideoTrack = nil;
|
||||
RTCVideoTrack* localVideoTrack = [self createLocalVideoTrack];
|
||||
if (localVideoTrack) {
|
||||
[localStream addVideoTrack:localVideoTrack];
|
||||
[_delegate appClient:self didReceiveLocalVideoTrack:localVideoTrack];
|
||||
}
|
||||
[localStream addAudioTrack:[_factory audioTrackWithID:@"ARDAMSa0"]];
|
||||
return localStream;
|
||||
}
|
||||
|
||||
- (RTCVideoTrack *)createLocalVideoTrack {
|
||||
RTCVideoTrack* localVideoTrack = nil;
|
||||
// The iOS simulator doesn't provide any sort of camera capture
|
||||
// support or emulation (http://goo.gl/rHAnC1) so don't bother
|
||||
// trying to open a local stream.
|
||||
// TODO(tkchin): local video capture for OSX. See
|
||||
// https://code.google.com/p/webrtc/issues/detail?id=3417.
|
||||
#if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE
|
||||
NSString *cameraID = nil;
|
||||
for (AVCaptureDevice *captureDevice in
|
||||
[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
|
||||
if (captureDevice.position == AVCaptureDevicePositionFront) {
|
||||
cameraID = [captureDevice localizedName];
|
||||
break;
|
||||
}
|
||||
}
|
||||
NSAssert(cameraID, @"Unable to get the front camera id");
|
||||
|
||||
RTCVideoCapturer *capturer =
|
||||
[RTCVideoCapturer capturerWithDeviceName:cameraID];
|
||||
RTCMediaConstraints *mediaConstraints = [self defaultMediaStreamConstraints];
|
||||
RTCVideoSource *videoSource =
|
||||
[_factory videoSourceWithCapturer:capturer
|
||||
constraints:mediaConstraints];
|
||||
RTCAVFoundationVideoSource *source =
|
||||
[[RTCAVFoundationVideoSource alloc] initWithFactory:_factory
|
||||
constraints:mediaConstraints];
|
||||
localVideoTrack =
|
||||
[_factory videoTrackWithID:@"ARDAMSv0" source:videoSource];
|
||||
if (localVideoTrack) {
|
||||
[localStream addVideoTrack:localVideoTrack];
|
||||
}
|
||||
[_delegate appClient:self didReceiveLocalVideoTrack:localVideoTrack];
|
||||
[[RTCVideoTrack alloc] initWithFactory:_factory
|
||||
source:source
|
||||
trackId:@"ARDAMSv0"];
|
||||
#endif
|
||||
[localStream addAudioTrack:[_factory audioTrackWithID:@"ARDAMSa0"]];
|
||||
return localStream;
|
||||
return localVideoTrack;
|
||||
}
|
||||
|
||||
#pragma mark - Collider methods
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 @@ static CGFloat const kLocalVideoViewPadding = 8;
|
||||
_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 @@ static CGFloat const kLocalVideoViewPadding = 8;
|
||||
_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 @@ static CGFloat const kLocalVideoViewPadding = 8;
|
||||
- (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 @@ static CGFloat const kLocalVideoViewPadding = 8;
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)onCameraSwitch:(id)sender {
|
||||
[_delegate videoCallViewDidSwitchCamera:self];
|
||||
}
|
||||
|
||||
- (void)onHangup:(id)sender {
|
||||
[_delegate videoCallViewDidHangup:self];
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 242 B |
Binary file not shown.
|
After Width: | Height: | Size: 311 B |
Reference in New Issue
Block a user