feat(android): Add exposure (brightness) support for iOS and standardize exposure value (#2419)

* Use standardized exposure value between 0 and 1

* Add iOS native exposure control

* Delete logging

* Add documentation + spell fixes

* Update RNCamera.m

* Remove reset of exposure after tap-to-focus
This commit is contained in:
Dominik Schwarz 2019-08-22 20:38:53 +02:00 committed by Sibelius Seraphini
parent 70c8cbdb12
commit ccd6f0b57c
9 changed files with 89 additions and 26 deletions

View File

@ -106,7 +106,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
private int mFlash;
private int mExposure;
private float mExposure;
private int mDisplayOrientation;
@ -358,12 +358,12 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
}
@Override
int getExposureCompensation() {
float getExposureCompensation() {
return mExposure;
}
@Override
void setExposureCompensation(int exposure) {
void setExposureCompensation(float exposure) {
if (exposure == mExposure) {
return;
@ -994,17 +994,19 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
}
}
private boolean setExposureInternal(int exposure) {
Log.e("CAMERA_1::", ""+isCameraOpened()+"; Exposure: "+exposure);
private boolean setExposureInternal(float exposure) {
mExposure = exposure;
if (isCameraOpened()){
int minExposure = mCameraParameters.getMinExposureCompensation();
int maxExposure = mCameraParameters.getMaxExposureCompensation();
Log.e("CAMERA_1::", ""+minExposure);
Log.e("CAMERA_1::", ""+maxExposure);
if (minExposure != maxExposure) {
mCameraParameters.setExposureCompensation(mExposure);
int scaledValue = 0;
if (mExposure >= 0 && mExposure <= 1) {
scaledValue = (int) (mExposure * (maxExposure - minExposure)) + minExposure;
}
mCameraParameters.setExposureCompensation(scaledValue);
return true;
}
}

View File

@ -238,7 +238,7 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
private int mFlash;
private int mExposure;
private float mExposure;
private int mCameraOrientation;
@ -477,12 +477,12 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
}
@Override
int getExposureCompensation() {
float getExposureCompensation() {
return mExposure;
}
@Override
void setExposureCompensation(int exposure) {
void setExposureCompensation(float exposure) {
Log.e("CAMERA_2:: ", "Adjusting exposure is not currently supported for Camera2");
}

View File

@ -478,11 +478,11 @@ public class CameraView extends FrameLayout {
return mImpl.getFlash();
}
public void setExposureCompensation(int exposure) {
public void setExposureCompensation(float exposure) {
mImpl.setExposureCompensation(exposure);
}
public int getExposureCompensation() {
public float getExposureCompensation() {
return mImpl.getExposureCompensation();
}
@ -653,7 +653,7 @@ public class CameraView extends FrameLayout {
@Flash
int flash;
int exposure;
float exposure;
float focusDepth;
@ -672,7 +672,7 @@ public class CameraView extends FrameLayout {
ratio = source.readParcelable(loader);
autoFocus = source.readByte() != 0;
flash = source.readInt();
exposure = source.readInt();
exposure = source.readFloat();
focusDepth = source.readFloat();
zoom = source.readFloat();
whiteBalance = source.readInt();
@ -691,7 +691,7 @@ public class CameraView extends FrameLayout {
out.writeParcelable(ratio, 0);
out.writeByte((byte) (autoFocus ? 1 : 0));
out.writeInt(flash);
out.writeInt(exposure);
out.writeFloat(exposure);
out.writeFloat(focusDepth);
out.writeFloat(zoom);
out.writeInt(whiteBalance);

View File

@ -76,9 +76,9 @@ abstract class CameraViewImpl {
abstract int getFlash();
abstract void setExposureCompensation(int exposure);
abstract void setExposureCompensation(float exposure);
abstract int getExposureCompensation();
abstract float getExposureCompensation();
abstract void takePicture(ReadableMap options);

View File

@ -84,7 +84,7 @@ public class CameraViewManager extends ViewGroupManager<RNCameraView> {
}
@ReactProp(name = "exposure")
public void setExposureCompensation(RNCameraView view, int exposure){
public void setExposureCompensation(RNCameraView view, float exposure){
view.setExposureCompensation(exposure);
}

View File

@ -32,6 +32,9 @@
@property(copy, nonatomic) NSDictionary *autoFocusPointOfInterest;
@property(assign, nonatomic) float focusDepth;
@property(assign, nonatomic) NSInteger whiteBalance;
@property(assign, nonatomic) float exposure;
@property(assign, nonatomic) float exposureIsoMin;
@property(assign, nonatomic) float exposureIsoMax;
@property(assign, nonatomic) AVCaptureSessionPreset pictureSize;
@property(nonatomic, assign) BOOL isReadingBarCodes;
@property(nonatomic, assign) BOOL isRecording;
@ -56,6 +59,7 @@
- (void)updateAutoFocusPointOfInterest;
- (void)updateZoom;
- (void)updateWhiteBalance;
- (void)updateExposure;
- (void)updatePictureSize;
// Face Detection props
- (void)updateTrackingEnabled:(id)requestedTracking;

View File

@ -84,7 +84,7 @@ static NSDictionary *defaultFaceDetectorOptions = nil;
name:AVAudioSessionInterruptionNotification
object:nil];
self.autoFocus = -1;
self.exposure = -1;
}
return self;
}
@ -223,28 +223,28 @@ static NSDictionary *defaultFaceDetectorOptions = nil;
{
AVCaptureDevice *device = [self.videoCaptureDeviceInput device];
NSError *error = nil;
if (![device lockForConfiguration:&error]) {
if (error) {
RCTLogError(@"%s: %@", __func__, error);
}
return;
}
if ([self.autoFocusPointOfInterest objectForKey:@"x"] && [self.autoFocusPointOfInterest objectForKey:@"y"]) {
float xValue = [self.autoFocusPointOfInterest[@"x"] floatValue];
float yValue = [self.autoFocusPointOfInterest[@"y"] floatValue];
if ([device isFocusPointOfInterestSupported] && [device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
CGPoint autofocusPoint = CGPointMake(xValue, yValue);
[device setFocusPointOfInterest:autofocusPoint];
[device setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
}
}
else {
RCTLogWarn(@"AutoFocusPointOfInterest not supported");
}
}
[device unlockForConfiguration];
}
@ -352,6 +352,57 @@ static NSDictionary *defaultFaceDetectorOptions = nil;
[device unlockForConfiguration];
}
/// Set the AVCaptureDevice's ISO values based on RNCamera's 'exposure' value,
/// which is a float between 0 and 1 if defined by the user or -1 to indicate that no
/// selection is active. 'exposure' gets mapped to a valid ISO value between the
/// device's min/max-range of ISO-values.
///
/// The exposure gets reset every time the user manually sets the autofocus-point in
/// 'updateAutoFocusPointOfInterest' automatically. Currently no explicit event is fired.
/// This leads to two 'exposure'-states: one here and one in the component, which is
/// fine. 'exposure' here gets only synced if 'exposure' on the js-side changes. You
/// can manually keep the state in sync by setting 'exposure' in your React-state
/// everytime the js-updateAutoFocusPointOfInterest-function gets called.
- (void)updateExposure
{
AVCaptureDevice *device = [self.videoCaptureDeviceInput device];
NSError *error = nil;
if (![device lockForConfiguration:&error]) {
if (error) {
RCTLogError(@"%s: %@", __func__, error);
}
return;
}
// Check that either no explicit exposure-val has been set yet
// or that it has been reset. Check for > 1 is only a guard.
if(self.exposure < 0 || self.exposure > 1){
[device setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
[device unlockForConfiguration];
return;
}
// Lazy init of range.
if(!self.exposureIsoMin){ self.exposureIsoMin = device.activeFormat.minISO; }
if(!self.exposureIsoMax){ self.exposureIsoMax = device.activeFormat.maxISO; }
// Get a valid ISO-value in range from min to max. After we mapped the exposure
// (a val between 0 - 1), the result gets corrected by the offset from 0, which
// is the min-ISO-value.
float appliedExposure = (self.exposureIsoMax - self.exposureIsoMin) * self.exposure + self.exposureIsoMin;
// Make sure we're in AVCaptureExposureModeCustom, else the ISO + duration time won't apply.
if(device.exposureMode != AVCaptureExposureModeCustom){
[device setExposureMode:AVCaptureExposureModeCustom];
}
// Only set the ISO for now, duration will be default as a change might affect frame rate.
[device setExposureModeCustomWithDuration:AVCaptureExposureDurationCurrent ISO:appliedExposure completionHandler:nil];
[device unlockForConfiguration];
}
- (void)updatePictureSize
{
[self updateSessionPreset:self.pictureSize];

View File

@ -206,6 +206,12 @@ RCT_CUSTOM_VIEW_PROPERTY(whiteBalance, NSInteger, RNCamera)
[view updateWhiteBalance];
}
RCT_CUSTOM_VIEW_PROPERTY(exposure, NSNumber, RNCamera)
{
[view setExposure:[RCTConvert float:json]];
[view updateExposure];
}
RCT_CUSTOM_VIEW_PROPERTY(pictureSize, NSString *, RNCamera)
{
[view setPictureSize:[[self class] pictureSizes][[RCTConvert NSString:json]]];

View File

@ -420,7 +420,7 @@ export default class Camera extends React.Component<PropsType, StateType> {
type: CameraManager.Type.back,
autoFocus: CameraManager.AutoFocus.on,
flashMode: CameraManager.FlashMode.off,
exposure: 0,
exposure: -1,
whiteBalance: CameraManager.WhiteBalance.auto,
faceDetectionMode: (CameraManager.FaceDetection || {}).fast,
barCodeTypes: Object.values(CameraManager.BarCodeType),