From a1cf366ea9ad5e47240c2a9ba7041c293fc6e84d Mon Sep 17 00:00:00 2001 From: hjon Date: Mon, 14 Mar 2016 20:55:22 -0700 Subject: [PATCH] Handle iOS devices with no rear-facing camera Devices exist (specifically the 16GB 5th generation iPod Touch) that do not have a rear-facing camera. This CL: - Adjusts RTCAVFoundationVideoCapturerInternal so initialization doesn't fail because a rear-facing camera doesn't exist, but still logs a warning - Provides a check for whether or not a rear-facing camera can be used (useful for toggling UI elements) - Turns an attempt to switch to the rear-facing camera into a no-op with a warning. BUG= Review URL: https://codereview.webrtc.org/1416303003 Cr-Commit-Position: refs/heads/master@{#11992} --- .../webrtc/objc/RTCAVFoundationVideoSource.mm | 4 ++ .../webrtc/objc/avfoundationvideocapturer.h | 4 ++ .../webrtc/objc/avfoundationvideocapturer.mm | 43 ++++++++++++++----- .../objc/public/RTCAVFoundationVideoSource.h | 2 + webrtc/api/objc/RTCAVFoundationVideoSource.h | 3 ++ webrtc/api/objc/RTCAVFoundationVideoSource.mm | 4 ++ webrtc/api/objc/avfoundationvideocapturer.h | 6 +++ webrtc/api/objc/avfoundationvideocapturer.mm | 43 ++++++++++++++----- 8 files changed, 89 insertions(+), 20 deletions(-) diff --git a/talk/app/webrtc/objc/RTCAVFoundationVideoSource.mm b/talk/app/webrtc/objc/RTCAVFoundationVideoSource.mm index 8b4e454a6e..660bb4d6cb 100644 --- a/talk/app/webrtc/objc/RTCAVFoundationVideoSource.mm +++ b/talk/app/webrtc/objc/RTCAVFoundationVideoSource.mm @@ -46,6 +46,10 @@ return [super initWithMediaSource:source]; } +- (BOOL)canUseBackCamera { + return self.capturer->CanUseBackCamera(); +} + - (BOOL)useBackCamera { return self.capturer->GetUseBackCamera(); } diff --git a/talk/app/webrtc/objc/avfoundationvideocapturer.h b/talk/app/webrtc/objc/avfoundationvideocapturer.h index adf7b2816c..1c910a84b8 100644 --- a/talk/app/webrtc/objc/avfoundationvideocapturer.h +++ b/talk/app/webrtc/objc/avfoundationvideocapturer.h @@ -56,6 +56,10 @@ class AVFoundationVideoCapturer : public cricket::VideoCapturer { // Returns the active capture session. AVCaptureSession* GetCaptureSession(); + // Returns whether the rear-facing camera can be used. + // e.g. It can't be used because it doesn't exist. + bool CanUseBackCamera() const; + // Switches the camera being used (either front or back). void SetUseBackCamera(bool useBackCamera); bool GetUseBackCamera() const; diff --git a/talk/app/webrtc/objc/avfoundationvideocapturer.mm b/talk/app/webrtc/objc/avfoundationvideocapturer.mm index 0f9dc6825e..f984590189 100644 --- a/talk/app/webrtc/objc/avfoundationvideocapturer.mm +++ b/talk/app/webrtc/objc/avfoundationvideocapturer.mm @@ -34,6 +34,7 @@ #import #import "webrtc/base/objc/RTCDispatcher.h" +#import "webrtc/base/objc/RTCLogging.h" // TODO(tkchin): support other formats. static NSString* const kDefaultPreset = AVCaptureSessionPreset640x480; @@ -52,6 +53,7 @@ static cricket::VideoFormat const kDefaultFormat = @property(nonatomic, readonly) AVCaptureSession* captureSession; @property(nonatomic, readonly) BOOL isRunning; +@property(nonatomic, readonly) BOOL canUseBackCamera; @property(nonatomic, assign) BOOL useBackCamera; // Defaults to NO. // We keep a pointer back to AVFoundationVideoCapturer to make callbacks on it @@ -105,10 +107,19 @@ static cricket::VideoFormat const kDefaultFormat = _capturer = nullptr; } +- (BOOL)canUseBackCamera { + return _backDeviceInput != nil; +} + - (void)setUseBackCamera:(BOOL)useBackCamera { if (_useBackCamera == useBackCamera) { return; } + if (!self.canUseBackCamera) { + RTCLog(@"No rear-facing camera exists or it cannot be used;" + "not switching."); + return; + } _useBackCamera = useBackCamera; [self updateSessionInput]; } @@ -203,10 +214,15 @@ static cricket::VideoFormat const kDefaultFormat = frontCaptureDevice = captureDevice; } } - if (!frontCaptureDevice || !backCaptureDevice) { - NSLog(@"Failed to get capture devices."); + if (!frontCaptureDevice) { + RTCLog(@"Failed to get front capture device."); return NO; } + if (!backCaptureDevice) { + RTCLog(@"Failed to get back capture device"); + // Don't return NO here because devices exist (16GB 5th generation iPod + // Touch) that don't have a rear-facing camera. + } // Set up the session inputs. NSError* error = nil; @@ -218,18 +234,21 @@ static cricket::VideoFormat const kDefaultFormat = error.localizedDescription); return NO; } - _backDeviceInput = - [AVCaptureDeviceInput deviceInputWithDevice:backCaptureDevice - error:&error]; - if (!_backDeviceInput) { - NSLog(@"Failed to get capture device input: %@", - error.localizedDescription); - return NO; + if (backCaptureDevice) { + error = nil; + _backDeviceInput = + [AVCaptureDeviceInput deviceInputWithDevice:backCaptureDevice + error:&error]; + if (error) { + RTCLog(@"Failed to get capture device input: %@", + error.localizedDescription); + _backDeviceInput = nil; + } } // Add the inputs. if (![_captureSession canAddInput:_frontDeviceInput] || - ![_captureSession canAddInput:_backDeviceInput]) { + (_backDeviceInput && ![_captureSession canAddInput:_backDeviceInput])) { NSLog(@"Session does not support capture inputs."); return NO; } @@ -353,6 +372,10 @@ AVCaptureSession* AVFoundationVideoCapturer::GetCaptureSession() { return _capturer.captureSession; } +bool AVFoundationVideoCapturer::CanUseBackCamera() const { + return _capturer.canUseBackCamera; +} + void AVFoundationVideoCapturer::SetUseBackCamera(bool useBackCamera) { _capturer.useBackCamera = useBackCamera; } diff --git a/talk/app/webrtc/objc/public/RTCAVFoundationVideoSource.h b/talk/app/webrtc/objc/public/RTCAVFoundationVideoSource.h index b6a686a7dc..fcc8f1f2c0 100644 --- a/talk/app/webrtc/objc/public/RTCAVFoundationVideoSource.h +++ b/talk/app/webrtc/objc/public/RTCAVFoundationVideoSource.h @@ -41,6 +41,8 @@ - (instancetype)initWithFactory:(RTCPeerConnectionFactory*)factory constraints:(RTCMediaConstraints*)constraints; +// Returns whether rear-facing camera is available for use. +@property(nonatomic, readonly) BOOL canUseBackCamera; // Switches the camera being used (either front or back). @property(nonatomic, assign) BOOL useBackCamera; // Returns the active capture session. diff --git a/webrtc/api/objc/RTCAVFoundationVideoSource.h b/webrtc/api/objc/RTCAVFoundationVideoSource.h index e3404441cc..1d1eac0fab 100644 --- a/webrtc/api/objc/RTCAVFoundationVideoSource.h +++ b/webrtc/api/objc/RTCAVFoundationVideoSource.h @@ -28,6 +28,9 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithFactory:(RTCPeerConnectionFactory *)factory constraints:(nullable RTCMediaConstraints *)constraints; +/** Returns whether rear-facing camera is available for use. */ +@property(nonatomic, readonly) BOOL canUseBackCamera; + /** Switches the camera being used (either front or back). */ @property(nonatomic, assign) BOOL useBackCamera; diff --git a/webrtc/api/objc/RTCAVFoundationVideoSource.mm b/webrtc/api/objc/RTCAVFoundationVideoSource.mm index 0cd015550c..1005c7dfa1 100644 --- a/webrtc/api/objc/RTCAVFoundationVideoSource.mm +++ b/webrtc/api/objc/RTCAVFoundationVideoSource.mm @@ -27,6 +27,10 @@ return [super initWithNativeVideoSource:source]; } +- (BOOL)canUseBackCamera { + return self.capturer->CanUseBackCamera(); +} + - (BOOL)useBackCamera { return self.capturer->GetUseBackCamera(); } diff --git a/webrtc/api/objc/avfoundationvideocapturer.h b/webrtc/api/objc/avfoundationvideocapturer.h index 2ee456d3f5..83ee70385e 100644 --- a/webrtc/api/objc/avfoundationvideocapturer.h +++ b/webrtc/api/objc/avfoundationvideocapturer.h @@ -40,6 +40,12 @@ class AVFoundationVideoCapturer : public cricket::VideoCapturer { /** Returns the active capture session. */ AVCaptureSession* GetCaptureSession(); + /** + * Returns whether the rear-facing camera can be used. + * e.g. It can't be used because it doesn't exist. + */ + bool CanUseBackCamera() const; + /** Switches the camera being used (either front or back). */ void SetUseBackCamera(bool useBackCamera); bool GetUseBackCamera() const; diff --git a/webrtc/api/objc/avfoundationvideocapturer.mm b/webrtc/api/objc/avfoundationvideocapturer.mm index a3f0f44160..c466512bd0 100644 --- a/webrtc/api/objc/avfoundationvideocapturer.mm +++ b/webrtc/api/objc/avfoundationvideocapturer.mm @@ -17,6 +17,7 @@ #import #import "webrtc/base/objc/RTCDispatcher.h" +#import "webrtc/base/objc/RTCLogging.h" // TODO(tkchin): support other formats. static NSString* const kDefaultPreset = AVCaptureSessionPreset640x480; @@ -35,6 +36,7 @@ static cricket::VideoFormat const kDefaultFormat = @property(nonatomic, readonly) AVCaptureSession *captureSession; @property(nonatomic, readonly) BOOL isRunning; +@property(nonatomic, readonly) BOOL canUseBackCamera; @property(nonatomic, assign) BOOL useBackCamera; // Defaults to NO. // We keep a pointer back to AVFoundationVideoCapturer to make callbacks on it @@ -88,10 +90,19 @@ static cricket::VideoFormat const kDefaultFormat = _capturer = nullptr; } +- (BOOL)canUseBackCamera { + return _backDeviceInput != nil; +} + - (void)setUseBackCamera:(BOOL)useBackCamera { if (_useBackCamera == useBackCamera) { return; } + if (!self.canUseBackCamera) { + RTCLog(@"No rear-facing camera exists or it cannot be used;" + "not switching."); + return; + } _useBackCamera = useBackCamera; [self updateSessionInput]; } @@ -186,10 +197,15 @@ static cricket::VideoFormat const kDefaultFormat = frontCaptureDevice = captureDevice; } } - if (!frontCaptureDevice || !backCaptureDevice) { - NSLog(@"Failed to get capture devices."); + if (!frontCaptureDevice) { + RTCLog(@"Failed to get front capture device."); return NO; } + if (!backCaptureDevice) { + RTCLog(@"Failed to get back capture device"); + // Don't return NO here because devices exist (16GB 5th generation iPod + // Touch) that don't have a rear-facing camera. + } // Set up the session inputs. NSError *error = nil; @@ -201,18 +217,21 @@ static cricket::VideoFormat const kDefaultFormat = error.localizedDescription); return NO; } - _backDeviceInput = - [AVCaptureDeviceInput deviceInputWithDevice:backCaptureDevice - error:&error]; - if (!_backDeviceInput) { - NSLog(@"Failed to get capture device input: %@", - error.localizedDescription); - return NO; + if (backCaptureDevice) { + error = nil; + _backDeviceInput = + [AVCaptureDeviceInput deviceInputWithDevice:backCaptureDevice + error:&error]; + if (error) { + RTCLog(@"Failed to get capture device input: %@", + error.localizedDescription); + _backDeviceInput = nil; + } } // Add the inputs. if (![_captureSession canAddInput:_frontDeviceInput] || - ![_captureSession canAddInput:_backDeviceInput]) { + (_backDeviceInput && ![_captureSession canAddInput:_backDeviceInput])) { NSLog(@"Session does not support capture inputs."); return NO; } @@ -336,6 +355,10 @@ AVCaptureSession* AVFoundationVideoCapturer::GetCaptureSession() { return _capturer.captureSession; } +bool AVFoundationVideoCapturer::CanUseBackCamera() const { + return _capturer.canUseBackCamera; +} + void AVFoundationVideoCapturer::SetUseBackCamera(bool useBackCamera) { _capturer.useBackCamera = useBackCamera; }