mirror of
https://github.com/status-im/react-native-camera.git
synced 2025-02-22 16:58:05 +00:00
Added support for camera switching and basic image capturing!
This commit is contained in:
parent
0ca5719db2
commit
20be488fa8
@ -12,6 +12,7 @@ var merge = require('merge');
|
||||
var Camera = React.createClass({
|
||||
propTypes: {
|
||||
aspect: PropTypes.string,
|
||||
camera: PropTypes.string,
|
||||
orientation: PropTypes.string,
|
||||
},
|
||||
|
||||
@ -24,7 +25,10 @@ var Camera = React.createClass({
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
isAuthorized: false
|
||||
isAuthorized: false,
|
||||
aspect: this.props.aspect || 'Fill',
|
||||
camera: this.props.camera || 'Back',
|
||||
orientation: this.props.orientation || 'Portrait'
|
||||
};
|
||||
},
|
||||
|
||||
@ -37,13 +41,10 @@ var Camera = React.createClass({
|
||||
|
||||
render: function() {
|
||||
var style = flattenStyle([styles.base, this.props.style]);
|
||||
var aspect = this.props.aspect || 'Fill';
|
||||
var camera = this.props.camera || 'Back';
|
||||
var orientation = this.props.orientation || 'Portrait';
|
||||
|
||||
aspect = NativeModules.CameraManager.aspects[aspect];
|
||||
camera = NativeModules.CameraManager.cameras[camera];
|
||||
orientation = NativeModules.CameraManager.orientations[orientation];
|
||||
aspect = NativeModules.CameraManager.aspects[this.state.aspect];
|
||||
camera = NativeModules.CameraManager.cameras[this.state.camera];
|
||||
orientation = NativeModules.CameraManager.orientations[this.state.orientation];
|
||||
|
||||
var nativeProps = merge(this.props, {
|
||||
style,
|
||||
@ -54,6 +55,15 @@ var Camera = React.createClass({
|
||||
|
||||
return <RCTCamera {... nativeProps} />
|
||||
},
|
||||
|
||||
switch: function() {
|
||||
this.state.camera = this.state.camera == 'Back' ? 'Front' : 'Back';
|
||||
this.setState(this.state);
|
||||
},
|
||||
|
||||
takePicture: function(cb) {
|
||||
NativeModules.CameraManager.takePicture(cb);
|
||||
}
|
||||
});
|
||||
|
||||
var RCTCamera = createReactIOSNativeComponentClass({
|
||||
|
@ -2,12 +2,11 @@
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import "ViewfinderView.h"
|
||||
|
||||
@class AVCaptureSession;
|
||||
@class RCTCameraManager;
|
||||
|
||||
@interface RCTCamera : UIView
|
||||
|
||||
@property (nonatomic) AVCaptureSession *session;
|
||||
@property (nonatomic) RCTCameraManager *cameraManager;
|
||||
@property (nonatomic) ViewfinderView *viewfinder;
|
||||
@property (nonatomic) AVCaptureDeviceInput *captureDeviceInput;
|
||||
|
||||
@end
|
||||
|
76
RCTCamera.m
76
RCTCamera.m
@ -1,4 +1,6 @@
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTCamera.h"
|
||||
#import "RCTCameraManager.h"
|
||||
#import "RCTLog.h"
|
||||
#import "ViewfinderView.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
@ -7,92 +9,34 @@
|
||||
|
||||
- (void)setAspect:(NSString *)aspect
|
||||
{
|
||||
[(AVCaptureVideoPreviewLayer *)[[self viewfinder] layer] setVideoGravity:aspect];
|
||||
RCTCameraManager *cameraManager = [RCTCameraManager sharedManager];
|
||||
[cameraManager setAspect:aspect];
|
||||
}
|
||||
|
||||
- (void)setCamera:(NSInteger)camera
|
||||
{
|
||||
// AVCaptureDevice *currentVideoDevice = [_captureDeviceInput device];
|
||||
AVCaptureDevice *videoDevice = [self deviceWithMediaType:AVMediaTypeVideo preferringPosition:(AVCaptureDevicePosition)camera];
|
||||
AVCaptureDeviceInput *videoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:nil];
|
||||
|
||||
[[self session] beginConfiguration];
|
||||
|
||||
[[self session] removeInput:[self captureDeviceInput]];
|
||||
if ([[self session] canAddInput:videoDeviceInput])
|
||||
{
|
||||
// [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:currentVideoDevice];
|
||||
//
|
||||
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(subjectAreaDidChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:videoDevice];
|
||||
//
|
||||
[[self session] addInput:videoDeviceInput];
|
||||
[self setCaptureDeviceInput:videoDeviceInput];
|
||||
}
|
||||
else
|
||||
{
|
||||
[[self session] addInput:_captureDeviceInput];
|
||||
}
|
||||
|
||||
[[self session] commitConfiguration];
|
||||
RCTCameraManager *cameraManager = [RCTCameraManager sharedManager];
|
||||
[cameraManager setCamera:camera];
|
||||
}
|
||||
|
||||
- (void)setOrientation:(NSInteger)orientation
|
||||
{
|
||||
[[(AVCaptureVideoPreviewLayer *)[[self viewfinder] layer] connection] setVideoOrientation:orientation];
|
||||
RCTCameraManager *cameraManager = [RCTCameraManager sharedManager];
|
||||
[cameraManager setOrientation:orientation];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
|
||||
[self setCameraManager:[RCTCameraManager sharedManager]];
|
||||
_viewfinder = [[ViewfinderView alloc] init];
|
||||
AVCaptureSession *session = [[AVCaptureSession alloc] init];
|
||||
|
||||
[[self viewfinder] setSession:session];
|
||||
[[self viewfinder] setSession:_cameraManager.session];
|
||||
[self addSubview:_viewfinder];
|
||||
|
||||
NSError *error = nil;
|
||||
|
||||
AVCaptureDevice *captureDevice = [self deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionBack];
|
||||
|
||||
_captureDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
|
||||
|
||||
if (error)
|
||||
{
|
||||
NSLog(@"%@", error);
|
||||
}
|
||||
|
||||
if ([session canAddInput:_captureDeviceInput])
|
||||
{
|
||||
[session addInput:_captureDeviceInput];
|
||||
|
||||
[[(AVCaptureVideoPreviewLayer *)[[self viewfinder] layer] connection] setVideoOrientation:AVCaptureVideoOrientationPortrait];
|
||||
[(AVCaptureVideoPreviewLayer *)[[self viewfinder] layer] setVideoGravity:AVLayerVideoGravityResizeAspectFill];
|
||||
}
|
||||
|
||||
[session startRunning];
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (AVCaptureDevice *)deviceWithMediaType:(NSString *)mediaType preferringPosition:(AVCaptureDevicePosition)position
|
||||
{
|
||||
NSArray *devices = [AVCaptureDevice devicesWithMediaType:mediaType];
|
||||
AVCaptureDevice *captureDevice = [devices firstObject];
|
||||
|
||||
for (AVCaptureDevice *device in devices)
|
||||
{
|
||||
if ([device position] == position)
|
||||
{
|
||||
captureDevice = device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return captureDevice;
|
||||
}
|
||||
|
||||
- (NSArray *)reactSubviews
|
||||
{
|
||||
NSArray *subviews = @[_viewfinder];
|
||||
|
@ -1,4 +1,20 @@
|
||||
#import "RCTViewManager.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@class RCTCamera;
|
||||
|
||||
@interface RCTCameraManager : RCTViewManager
|
||||
@end
|
||||
|
||||
@property (nonatomic) AVCaptureSession *session;
|
||||
@property (nonatomic) AVCaptureDeviceInput *captureDeviceInput;
|
||||
@property (nonatomic) AVCaptureStillImageOutput *stillImageOutput;
|
||||
@property (nonatomic) RCTCamera *currentCamera;
|
||||
|
||||
+ (id)sharedManager;
|
||||
- (id)init;
|
||||
- (void)setAspect:(NSString *) aspect;
|
||||
- (void)setCamera:(NSInteger) camera;
|
||||
- (void)setOrientation:(NSInteger) orientation;
|
||||
- (AVCaptureDevice *)deviceWithMediaType:(NSString *)mediaType preferringPosition:(AVCaptureDevicePosition)position;
|
||||
|
||||
@end
|
||||
|
@ -1,15 +1,26 @@
|
||||
#import "RCTCameraManager.h"
|
||||
#import "RCTCamera.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTUtils.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@implementation RCTCameraManager
|
||||
|
||||
+ (id)sharedManager {
|
||||
static RCTCameraManager *sharedCameraManager = nil;
|
||||
@synchronized(self) {
|
||||
if (sharedCameraManager == nil)
|
||||
sharedCameraManager = [[self alloc] init];
|
||||
}
|
||||
return sharedCameraManager;
|
||||
}
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [[RCTCamera alloc] init];
|
||||
[self setCurrentCamera:[[RCTCamera alloc] init]];
|
||||
return _currentCamera;
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(aspect, NSString);
|
||||
@ -37,8 +48,45 @@ RCT_EXPORT_VIEW_PROPERTY(orientation, NSInteger);
|
||||
};
|
||||
}
|
||||
|
||||
- (void)checkDeviceAuthorizationStatus:(RCTResponseSenderBlock) callback
|
||||
{
|
||||
- (id)init {
|
||||
if ((self = [super init])) {
|
||||
[self setSession:[[AVCaptureSession alloc] init]];
|
||||
[_session setSessionPreset:AVCaptureSessionPresetHigh];
|
||||
|
||||
NSError *error = nil;
|
||||
|
||||
AVCaptureDevice *captureDevice = [self deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionBack];
|
||||
|
||||
_captureDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
|
||||
|
||||
|
||||
if (error)
|
||||
{
|
||||
NSLog(@"%@", error);
|
||||
}
|
||||
|
||||
if ([_session canAddInput:_captureDeviceInput])
|
||||
{
|
||||
[_session addInput:_captureDeviceInput];
|
||||
|
||||
[[(AVCaptureVideoPreviewLayer *)_currentCamera.viewfinder.layer connection] setVideoOrientation:AVCaptureVideoOrientationPortrait];
|
||||
[(AVCaptureVideoPreviewLayer *)_currentCamera.viewfinder.layer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
|
||||
}
|
||||
|
||||
AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
|
||||
if ([_session canAddOutput:stillImageOutput])
|
||||
{
|
||||
[_stillImageOutput setOutputSettings:@{AVVideoCodecKey : AVVideoCodecJPEG}];
|
||||
[_session addOutput:stillImageOutput];
|
||||
[self setStillImageOutput:stillImageOutput];
|
||||
}
|
||||
|
||||
[_session startRunning];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)checkDeviceAuthorizationStatus:(RCTResponseSenderBlock) callback {
|
||||
RCT_EXPORT();
|
||||
NSString *mediaType = AVMediaTypeVideo;
|
||||
|
||||
@ -47,4 +95,91 @@ RCT_EXPORT_VIEW_PROPERTY(orientation, NSInteger);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setAspect:(NSString *)aspect
|
||||
{
|
||||
[(AVCaptureVideoPreviewLayer *)_currentCamera.viewfinder.layer setVideoGravity:aspect];
|
||||
}
|
||||
|
||||
|
||||
- (void)setCamera:(NSInteger)camera
|
||||
{
|
||||
// AVCaptureDevice *currentVideoDevice = [_captureDeviceInput device];
|
||||
AVCaptureDevicePosition position = (AVCaptureDevicePosition)camera;
|
||||
AVCaptureDevice *videoDevice = [self deviceWithMediaType:AVMediaTypeVideo preferringPosition:(AVCaptureDevicePosition)position];
|
||||
|
||||
NSError *error = nil;
|
||||
AVCaptureDeviceInput *videoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];
|
||||
|
||||
if (error)
|
||||
{
|
||||
NSLog(@"%@", error);
|
||||
}
|
||||
|
||||
[[self session] beginConfiguration];
|
||||
|
||||
[[self session] removeInput:[self captureDeviceInput]];
|
||||
|
||||
|
||||
if ([[self session] canAddInput:videoDeviceInput])
|
||||
{
|
||||
// [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:currentVideoDevice];
|
||||
//
|
||||
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(subjectAreaDidChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:videoDevice];
|
||||
//
|
||||
[[self session] addInput:videoDeviceInput];
|
||||
[self setCaptureDeviceInput:videoDeviceInput];
|
||||
}
|
||||
else
|
||||
{
|
||||
[[self session] addInput:_captureDeviceInput];
|
||||
}
|
||||
|
||||
[[self session] commitConfiguration];
|
||||
}
|
||||
|
||||
- (void)setOrientation:(NSInteger)orientation
|
||||
{
|
||||
[[(AVCaptureVideoPreviewLayer *)_currentCamera.viewfinder.layer connection] setVideoOrientation:orientation];
|
||||
}
|
||||
|
||||
- (void)takePicture:(RCTResponseSenderBlock) callback {
|
||||
RCT_EXPORT();
|
||||
// Update the orientation on the still image output video connection before capturing.
|
||||
[[[self stillImageOutput] connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:[[(AVCaptureVideoPreviewLayer *)_currentCamera.viewfinder.layer connection] videoOrientation]];
|
||||
|
||||
// Flash set to Auto for Still Capture
|
||||
// [AVCamViewController setFlashMode:AVCaptureFlashModeAuto forDevice:[[self videoDeviceInput] device]];
|
||||
|
||||
// Capture a still image.
|
||||
[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:[[self stillImageOutput] connectionWithMediaType:AVMediaTypeVideo] completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
|
||||
|
||||
if (imageDataSampleBuffer)
|
||||
{
|
||||
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
|
||||
NSString *imageBase64 = [imageData base64EncodedStringWithOptions:0];
|
||||
callback(@[[NSNull null], imageBase64]);
|
||||
}
|
||||
else {
|
||||
callback(@[RCTMakeError(error.description, nil, nil)]);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (AVCaptureDevice *)deviceWithMediaType:(NSString *)mediaType preferringPosition:(AVCaptureDevicePosition)position
|
||||
{
|
||||
NSArray *devices = [AVCaptureDevice devicesWithMediaType:mediaType];
|
||||
AVCaptureDevice *captureDevice = [devices firstObject];
|
||||
|
||||
for (AVCaptureDevice *device in devices)
|
||||
{
|
||||
if ([device position] == position)
|
||||
{
|
||||
captureDevice = device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return captureDevice;
|
||||
}
|
||||
|
||||
@end
|
||||
|
39
README.md
39
README.md
@ -30,13 +30,21 @@ var cameraApp = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Camera
|
||||
aspect="Stretch"
|
||||
orientation="PortraitUpsideDown"
|
||||
style={{height: 200, width: 200}}
|
||||
/>
|
||||
<TouchableHighlight onPress={this._switchCamera}>
|
||||
<View>
|
||||
<Camera
|
||||
ref="cam"
|
||||
aspect="Stretch"
|
||||
orientation="PortraitUpsideDown"
|
||||
style={{height: 200, width: 200}}
|
||||
/>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
_switchCamera: function() {
|
||||
this.refs.cam.switch();
|
||||
}
|
||||
});
|
||||
|
||||
@ -51,6 +59,13 @@ Values: `Fit`, `Fill` (default), `Stretch`
|
||||
|
||||
The `aspect` prop allows you to define how your viewfinder renders the camera's view. For instance, if you have a square viewfinder and you want to fill the it entirely, you have two options: `Fill`, where the aspect ratio of the camera's view is preserved by cropping the view or `Stretch`, where the aspect ratio is skewed in order to fit the entire image inside the viewfinder. The other option is `Fit`, which ensures the camera's entire view fits inside your viewfinder without altering the aspect ratio.
|
||||
|
||||
#### `camera`
|
||||
|
||||
Values: `Front`, `Back` (default)
|
||||
|
||||
Use the `camera` prop to specify which camera to use.
|
||||
|
||||
|
||||
#### `orientation`
|
||||
|
||||
Values: `LandscapeLeft`, `LandscapeRight`, `Portrait` (default), `PortraitUpsideDown`
|
||||
@ -59,6 +74,20 @@ The `orientation` prop allows you to specify the current orientation of the phon
|
||||
|
||||
TODO: Add support for an `Auto` value to automatically adjust for orientation changes.
|
||||
|
||||
|
||||
### Component methods
|
||||
|
||||
You can access component methods by adding a `ref` (ie. `ref="camera"`) prop to your `<Camera>` element, then you can use `this.refs.camera.switch()`, etc. inside your component.
|
||||
|
||||
#### `switch()`
|
||||
|
||||
The `switch()` method toggles between the `Front` and `Back` cameras.
|
||||
|
||||
|
||||
#### `takePicture(callback)`
|
||||
|
||||
Basic implementation of image capture. This method is subject to change, but currently works by accepting a callback like `function(err, base64EncodedJpeg) { ... }`.
|
||||
|
||||
------------
|
||||
|
||||
Thanks to Brent Vatne (@brentvatne) for the `react-native-video` module which provided me with a great example of how to set up this module.
|
||||
|
@ -1,7 +1,11 @@
|
||||
{
|
||||
"name": "react-native-camera",
|
||||
"version": "0.0.2",
|
||||
"description": "A <Camera> element for React Native",
|
||||
"repository": {
|
||||
"type" : "git",
|
||||
"url" : "http://github.com/lwansbrough/react-native-camera.git"
|
||||
},
|
||||
"version": "0.0.3",
|
||||
"description": "A Camera element for React Native",
|
||||
"main": "Camera.ios.js",
|
||||
"author": "Lochlan Wansbrough <lochie@live.com> (http://lwansbrough.com)",
|
||||
"dependencies": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user