feat(android): Android ui thread (#2560)
* Move heavy work to a dedicated background thread. Improves camera initial loading and resumes from background.
Details:
- Use a HandlerThread to delegate heavy tasks to background. The thread is managed by the view, and passed down to the implementation in case it also needs to use it. The view will fire start calls and other possibly heavy operations in this thread to avoid ANRs. Some code sent to this thread:
- start calls: start is extremely heavy and will cause ANRs on some devices, especially when coming back from background
- Camera1: some preset changes fire a stop/start sequence. These will now happen in the background thread
- take picture and start recording (from view class) will also start in this thread
- Add some extra null checks
- View was not properly cleaning up itself on destroy (host destroy event was never fired)
* catch possible errors when starting camera preview. This might still randomly fail on some devices for some reason.
* delay capture in progress until we have resumed/paused preview.
* do not crash the app if set texture setup failed
* more synchronized checks to prevent crashes due to concurrent camera updates
* remove unused imports
This commit is contained in:
parent
0158ebfd39
commit
87774dd370
@ -94,6 +94,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
private final SizeMap mPreviewSizes = new SizeMap();
|
||||
|
||||
private boolean mIsPreviewActive = false;
|
||||
private boolean mShowingPreview = true; // preview enabled by default
|
||||
|
||||
private final SizeMap mPictureSizes = new SizeMap();
|
||||
|
||||
@ -101,8 +102,6 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
|
||||
private AspectRatio mAspectRatio;
|
||||
|
||||
private boolean mShowingPreview;
|
||||
|
||||
private boolean mAutoFocus;
|
||||
|
||||
private int mFacing;
|
||||
@ -127,8 +126,9 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
|
||||
private SurfaceTexture mPreviewTexture;
|
||||
|
||||
Camera1(Callback callback, PreviewImpl preview) {
|
||||
super(callback, preview);
|
||||
Camera1(Callback callback, PreviewImpl preview, Handler bgHandler) {
|
||||
super(callback, preview, bgHandler);
|
||||
|
||||
preview.setCallback(new PreviewImpl.Callback() {
|
||||
@Override
|
||||
public void onSurfaceChanged() {
|
||||
@ -137,7 +137,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
|
||||
@Override
|
||||
public void onSurfaceDestroyed() {
|
||||
stop();
|
||||
stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -150,12 +150,24 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
// pause preview calls
|
||||
// capture callbacks will handle it if needed afterwards.
|
||||
if(!isPictureCaptureInProgress.get() && !mIsRecording.get()){
|
||||
synchronized (this) {
|
||||
mustUpdateSurface = false;
|
||||
setUpPreview();
|
||||
mIsPreviewActive = false;
|
||||
adjustCameraParameters();
|
||||
}
|
||||
mBgHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized(Camera1.this){
|
||||
// check for camera null again since it might have changed
|
||||
if(mCamera != null){
|
||||
mustUpdateSurface = false;
|
||||
setUpPreview();
|
||||
adjustCameraParameters();
|
||||
|
||||
// only start preview if we are showing it
|
||||
if(mShowingPreview){
|
||||
startCameraPreview();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else{
|
||||
mustUpdateSurface = true;
|
||||
@ -165,30 +177,37 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
|
||||
@Override
|
||||
boolean start() {
|
||||
synchronized (this) {
|
||||
|
||||
synchronized(this){
|
||||
chooseCamera();
|
||||
if (!openCamera()) {
|
||||
mCallback.onMountError();
|
||||
// returning false will result in invoking this method again
|
||||
return true;
|
||||
}
|
||||
|
||||
// if our preview layer is not ready
|
||||
// do not set it up. Surface handler will do it for us
|
||||
// once ready.
|
||||
// This prevents some redundant camera work
|
||||
if (mPreview.isReady()) {
|
||||
setUpPreview();
|
||||
if(mShowingPreview){
|
||||
startCameraPreview();
|
||||
}
|
||||
}
|
||||
mShowingPreview = true;
|
||||
startCameraPreview();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void stop() {
|
||||
|
||||
// make sure no other threads are trying to do this at the same time
|
||||
// such as another call to stop() from surface destroyed
|
||||
// such as another call to stop from surface destroyed
|
||||
// or host destroyed. Should avoid crashes with concurrent calls
|
||||
|
||||
synchronized (this) {
|
||||
synchronized(this){
|
||||
if (mMediaRecorder != null) {
|
||||
try{
|
||||
mMediaRecorder.stop();
|
||||
@ -214,10 +233,10 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
}
|
||||
|
||||
if (mCamera != null) {
|
||||
mIsPreviewActive = false;
|
||||
mCamera.stopPreview();
|
||||
mCamera.setPreviewCallback(null);
|
||||
}
|
||||
mShowingPreview = false;
|
||||
|
||||
releaseCamera();
|
||||
}
|
||||
@ -227,43 +246,68 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
@SuppressLint("NewApi")
|
||||
void setUpPreview() {
|
||||
try {
|
||||
if (mPreviewTexture != null) {
|
||||
mCamera.setPreviewTexture(mPreviewTexture);
|
||||
} else if (mPreview.getOutputClass() == SurfaceHolder.class) {
|
||||
final boolean needsToStopPreview = mShowingPreview && Build.VERSION.SDK_INT < 14;
|
||||
if (needsToStopPreview) {
|
||||
mCamera.stopPreview();
|
||||
mIsPreviewActive = false;
|
||||
if(mCamera != null){
|
||||
if (mPreviewTexture != null) {
|
||||
mCamera.setPreviewTexture(mPreviewTexture);
|
||||
} else if (mPreview.getOutputClass() == SurfaceHolder.class) {
|
||||
final boolean needsToStopPreview = mIsPreviewActive && Build.VERSION.SDK_INT < 14;
|
||||
if (needsToStopPreview) {
|
||||
mCamera.stopPreview();
|
||||
mIsPreviewActive = false;
|
||||
}
|
||||
mCamera.setPreviewDisplay(mPreview.getSurfaceHolder());
|
||||
if (needsToStopPreview) {
|
||||
startCameraPreview();
|
||||
}
|
||||
} else {
|
||||
mCamera.setPreviewTexture((SurfaceTexture) mPreview.getSurfaceTexture());
|
||||
}
|
||||
mCamera.setPreviewDisplay(mPreview.getSurfaceHolder());
|
||||
if (needsToStopPreview) {
|
||||
startCameraPreview();
|
||||
}
|
||||
} else {
|
||||
mCamera.setPreviewTexture((SurfaceTexture) mPreview.getSurfaceTexture());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (Exception e) {
|
||||
Log.e("CAMERA_1::", "setUpPreview failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void startCameraPreview() {
|
||||
mCamera.startPreview();
|
||||
mIsPreviewActive = true;
|
||||
if (mIsScanning) {
|
||||
mCamera.setPreviewCallback(this);
|
||||
// only start the preview if we didn't yet.
|
||||
if(!mIsPreviewActive && mCamera != null){
|
||||
try{
|
||||
mIsPreviewActive = true;
|
||||
mCamera.startPreview();
|
||||
if (mIsScanning) {
|
||||
mCamera.setPreviewCallback(this);
|
||||
}
|
||||
}
|
||||
catch(Exception e){
|
||||
mIsPreviewActive = false;
|
||||
Log.e("CAMERA_1::", "startCameraPreview failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumePreview() {
|
||||
startCameraPreview();
|
||||
mBgHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized(this){
|
||||
mShowingPreview = true;
|
||||
startCameraPreview();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pausePreview() {
|
||||
mCamera.stopPreview();
|
||||
mIsPreviewActive = false;
|
||||
synchronized(this){
|
||||
mIsPreviewActive = false;
|
||||
mShowingPreview = false;
|
||||
|
||||
if(mCamera != null){
|
||||
mCamera.stopPreview();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -277,10 +321,17 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
return;
|
||||
}
|
||||
mFacing = facing;
|
||||
if (isCameraOpened()) {
|
||||
stop();
|
||||
start();
|
||||
}
|
||||
|
||||
mBgHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isCameraOpened()) {
|
||||
stop();
|
||||
start();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -299,10 +350,15 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
// Passing null will always yield true
|
||||
if(!Objects.equals(_mCameraId, String.valueOf(mCameraId))){
|
||||
// this will call chooseCamera
|
||||
if (isCameraOpened()) {
|
||||
stop();
|
||||
start();
|
||||
}
|
||||
mBgHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isCameraOpened()) {
|
||||
stop();
|
||||
start();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -360,13 +416,16 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
} else {
|
||||
mPictureSize = size;
|
||||
}
|
||||
if (mCameraParameters != null && mCamera != null) {
|
||||
mCameraParameters.setPictureSize(mPictureSize.getWidth(), mPictureSize.getHeight());
|
||||
try{
|
||||
mCamera.setParameters(mCameraParameters);
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "setParameters failed", e);
|
||||
synchronized(this){
|
||||
if (mCameraParameters != null && mCamera != null) {
|
||||
mCameraParameters.setPictureSize(mPictureSize.getWidth(), mPictureSize.getHeight());
|
||||
try{
|
||||
mCamera.setParameters(mCameraParameters);
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "setParameters failed", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -377,7 +436,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean setAspectRatio(AspectRatio ratio) {
|
||||
boolean setAspectRatio(final AspectRatio ratio) {
|
||||
if (mAspectRatio == null || !isCameraOpened()) {
|
||||
// Handle this later when camera is opened
|
||||
mAspectRatio = ratio;
|
||||
@ -388,7 +447,16 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
// do nothing, ratio remains unchanged. Consistent with Camera2 and initial mount behaviour
|
||||
} else {
|
||||
mAspectRatio = ratio;
|
||||
adjustCameraParameters();
|
||||
mBgHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized(Camera1.this){
|
||||
if(mCamera != null){
|
||||
adjustCameraParameters();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -405,13 +473,17 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
if (mAutoFocus == autoFocus) {
|
||||
return;
|
||||
}
|
||||
if (setAutoFocusInternal(autoFocus)) {
|
||||
try{
|
||||
mCamera.setParameters(mCameraParameters);
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "setParameters failed", e);
|
||||
}
|
||||
synchronized(this){
|
||||
if (setAutoFocusInternal(autoFocus)) {
|
||||
try{
|
||||
if(mCamera != null){
|
||||
mCamera.setParameters(mCameraParameters);
|
||||
}
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "setParameters failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -430,12 +502,14 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
return;
|
||||
}
|
||||
if (setFlashInternal(flash)) {
|
||||
try{
|
||||
mCamera.setParameters(mCameraParameters);
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "setParameters failed", e);
|
||||
}
|
||||
try{
|
||||
if(mCamera != null){
|
||||
mCamera.setParameters(mCameraParameters);
|
||||
}
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "setParameters failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -456,13 +530,16 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
return;
|
||||
}
|
||||
if (setExposureInternal(exposure)) {
|
||||
try{
|
||||
mCamera.setParameters(mCameraParameters);
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "setParameters failed", e);
|
||||
}
|
||||
try{
|
||||
if(mCamera != null){
|
||||
mCamera.setParameters(mCameraParameters);
|
||||
}
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "setParameters failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -481,12 +558,14 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
return;
|
||||
}
|
||||
if (setZoomInternal(zoom)) {
|
||||
try{
|
||||
mCamera.setParameters(mCameraParameters);
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "setParameters failed", e);
|
||||
}
|
||||
try{
|
||||
if(mCamera != null){
|
||||
mCamera.setParameters(mCameraParameters);
|
||||
}
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "setParameters failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -502,12 +581,14 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
return;
|
||||
}
|
||||
if (setWhiteBalanceInternal(whiteBalance)) {
|
||||
try{
|
||||
mCamera.setParameters(mCameraParameters);
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "setParameters failed", e);
|
||||
}
|
||||
try{
|
||||
if(mCamera != null){
|
||||
mCamera.setParameters(mCameraParameters);
|
||||
}
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "setParameters failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -597,7 +678,6 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
mCamera.takePicture(null, null, null, new Camera.PictureCallback() {
|
||||
@Override
|
||||
public void onPictureTaken(byte[] data, Camera camera) {
|
||||
isPictureCaptureInProgress.set(false);
|
||||
|
||||
// this shouldn't be needed and messes up autoFocusPointOfInterest
|
||||
// camera.cancelAutoFocus();
|
||||
@ -614,6 +694,8 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
camera.setPreviewCallback(null);
|
||||
}
|
||||
|
||||
isPictureCaptureInProgress.set(false);
|
||||
|
||||
mOrientation = Constants.ORIENTATION_AUTO;
|
||||
mCallback.onPictureTaken(data, displayOrientationToOrientationEnum(mDeviceOrientation));
|
||||
|
||||
@ -687,14 +769,14 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
}
|
||||
|
||||
@Override
|
||||
void setDisplayOrientation(int displayOrientation) {
|
||||
synchronized (this) {
|
||||
void setDisplayOrientation(final int displayOrientation) {
|
||||
synchronized(this){
|
||||
if (mDisplayOrientation == displayOrientation) {
|
||||
return;
|
||||
}
|
||||
mDisplayOrientation = displayOrientation;
|
||||
if (isCameraOpened()) {
|
||||
final boolean needsToStopPreview = mShowingPreview && Build.VERSION.SDK_INT < 14;
|
||||
boolean needsToStopPreview = mIsPreviewActive && Build.VERSION.SDK_INT < 14;
|
||||
if (needsToStopPreview) {
|
||||
mCamera.stopPreview();
|
||||
mIsPreviewActive = false;
|
||||
@ -714,8 +796,8 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
}
|
||||
|
||||
@Override
|
||||
void setDeviceOrientation(int deviceOrientation) {
|
||||
synchronized (this) {
|
||||
void setDeviceOrientation(final int deviceOrientation) {
|
||||
synchronized(this){
|
||||
if (mDeviceOrientation == deviceOrientation) {
|
||||
return;
|
||||
}
|
||||
@ -733,27 +815,33 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPreviewTexture(SurfaceTexture surfaceTexture) {
|
||||
try {
|
||||
if (mCamera == null) {
|
||||
mPreviewTexture = surfaceTexture;
|
||||
return;
|
||||
public void setPreviewTexture(final SurfaceTexture surfaceTexture) {
|
||||
|
||||
mBgHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try{
|
||||
if (mCamera == null) {
|
||||
mPreviewTexture = surfaceTexture;
|
||||
return;
|
||||
}
|
||||
|
||||
mCamera.stopPreview();
|
||||
mIsPreviewActive = false;
|
||||
|
||||
if (surfaceTexture == null) {
|
||||
mCamera.setPreviewTexture((SurfaceTexture) mPreview.getSurfaceTexture());
|
||||
} else {
|
||||
mCamera.setPreviewTexture(surfaceTexture);
|
||||
}
|
||||
|
||||
mPreviewTexture = surfaceTexture;
|
||||
startCameraPreview();
|
||||
} catch (IOException e) {
|
||||
Log.e("CAMERA_1::", "setPreviewTexture failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
mCamera.stopPreview();
|
||||
mIsPreviewActive = false;
|
||||
|
||||
if (surfaceTexture == null) {
|
||||
mCamera.setPreviewTexture((SurfaceTexture) mPreview.getSurfaceTexture());
|
||||
} else {
|
||||
mCamera.setPreviewTexture(surfaceTexture);
|
||||
}
|
||||
|
||||
mPreviewTexture = surfaceTexture;
|
||||
startCameraPreview();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -845,7 +933,8 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
|
||||
// Always re-apply camera parameters
|
||||
mPictureSize = mPictureSizes.sizes(mAspectRatio).last();
|
||||
if (mShowingPreview) {
|
||||
boolean needsToStopPreview = mIsPreviewActive;
|
||||
if (needsToStopPreview) {
|
||||
mCamera.stopPreview();
|
||||
mIsPreviewActive = false;
|
||||
}
|
||||
@ -870,7 +959,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "setParameters failed", e);
|
||||
}
|
||||
if (mShowingPreview) {
|
||||
if (needsToStopPreview) {
|
||||
startCameraPreview();
|
||||
}
|
||||
}
|
||||
@ -916,89 +1005,96 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
}
|
||||
|
||||
// Most credit: https://github.com/CameraKit/camerakit-android/blob/master/camerakit-core/src/main/api16/com/wonderkiln/camerakit/Camera1.java
|
||||
void setFocusArea(float x, float y) {
|
||||
if (mCamera != null) {
|
||||
Camera.Parameters parameters = mCamera.getParameters();
|
||||
if (parameters == null) return;
|
||||
void setFocusArea(final float x, final float y) {
|
||||
mBgHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized(Camera1.this){
|
||||
if (mCamera != null) {
|
||||
Camera.Parameters parameters = mCamera.getParameters();
|
||||
if (parameters == null) return;
|
||||
|
||||
String focusMode = parameters.getFocusMode();
|
||||
Rect rect = calculateFocusArea(x, y);
|
||||
String focusMode = parameters.getFocusMode();
|
||||
Rect rect = calculateFocusArea(x, y);
|
||||
|
||||
List<Camera.Area> meteringAreas = new ArrayList<>();
|
||||
meteringAreas.add(new Camera.Area(rect, FOCUS_METERING_AREA_WEIGHT_DEFAULT));
|
||||
List<Camera.Area> meteringAreas = new ArrayList<>();
|
||||
meteringAreas.add(new Camera.Area(rect, FOCUS_METERING_AREA_WEIGHT_DEFAULT));
|
||||
|
||||
if (parameters.getMaxNumFocusAreas() != 0 && focusMode != null &&
|
||||
(focusMode.equals(Camera.Parameters.FOCUS_MODE_AUTO) ||
|
||||
focusMode.equals(Camera.Parameters.FOCUS_MODE_MACRO) ||
|
||||
focusMode.equals(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) ||
|
||||
focusMode.equals(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO))
|
||||
) {
|
||||
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
|
||||
parameters.setFocusAreas(meteringAreas);
|
||||
if (parameters.getMaxNumMeteringAreas() > 0) {
|
||||
parameters.setMeteringAreas(meteringAreas);
|
||||
}
|
||||
if (!parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
|
||||
return; //cannot autoFocus
|
||||
}
|
||||
try{
|
||||
mCamera.setParameters(parameters);
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "setParameters failed", e);
|
||||
}
|
||||
if (parameters.getMaxNumFocusAreas() != 0 && focusMode != null &&
|
||||
(focusMode.equals(Camera.Parameters.FOCUS_MODE_AUTO) ||
|
||||
focusMode.equals(Camera.Parameters.FOCUS_MODE_MACRO) ||
|
||||
focusMode.equals(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) ||
|
||||
focusMode.equals(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO))
|
||||
) {
|
||||
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
|
||||
parameters.setFocusAreas(meteringAreas);
|
||||
if (parameters.getMaxNumMeteringAreas() > 0) {
|
||||
parameters.setMeteringAreas(meteringAreas);
|
||||
}
|
||||
if (!parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
|
||||
return; //cannot autoFocus
|
||||
}
|
||||
try{
|
||||
mCamera.setParameters(parameters);
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "setParameters failed", e);
|
||||
}
|
||||
|
||||
try{
|
||||
mCamera.autoFocus(new Camera.AutoFocusCallback() {
|
||||
@Override
|
||||
public void onAutoFocus(boolean success, Camera camera) {
|
||||
//resetFocus(success, camera);
|
||||
try{
|
||||
mCamera.autoFocus(new Camera.AutoFocusCallback() {
|
||||
@Override
|
||||
public void onAutoFocus(boolean success, Camera camera) {
|
||||
//resetFocus(success, camera);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "autoFocus failed", e);
|
||||
}
|
||||
} else if (parameters.getMaxNumMeteringAreas() > 0) {
|
||||
if (!parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
|
||||
return; //cannot autoFocus
|
||||
}
|
||||
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
|
||||
parameters.setFocusAreas(meteringAreas);
|
||||
parameters.setMeteringAreas(meteringAreas);
|
||||
|
||||
try{
|
||||
mCamera.setParameters(parameters);
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "setParameters failed", e);
|
||||
}
|
||||
|
||||
try{
|
||||
mCamera.autoFocus(new Camera.AutoFocusCallback() {
|
||||
@Override
|
||||
public void onAutoFocus(boolean success, Camera camera) {
|
||||
//resetFocus(success, camera);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "autoFocus failed", e);
|
||||
}
|
||||
} else {
|
||||
try{
|
||||
mCamera.autoFocus(new Camera.AutoFocusCallback() {
|
||||
@Override
|
||||
public void onAutoFocus(boolean success, Camera camera) {
|
||||
//mCamera.cancelAutoFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "autoFocus failed", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "autoFocus failed", e);
|
||||
}
|
||||
} else if (parameters.getMaxNumMeteringAreas() > 0) {
|
||||
if (!parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
|
||||
return; //cannot autoFocus
|
||||
}
|
||||
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
|
||||
parameters.setFocusAreas(meteringAreas);
|
||||
parameters.setMeteringAreas(meteringAreas);
|
||||
|
||||
try{
|
||||
mCamera.setParameters(parameters);
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "setParameters failed", e);
|
||||
}
|
||||
|
||||
try{
|
||||
mCamera.autoFocus(new Camera.AutoFocusCallback() {
|
||||
@Override
|
||||
public void onAutoFocus(boolean success, Camera camera) {
|
||||
//resetFocus(success, camera);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "autoFocus failed", e);
|
||||
}
|
||||
} else {
|
||||
try{
|
||||
mCamera.autoFocus(new Camera.AutoFocusCallback() {
|
||||
@Override
|
||||
public void onAutoFocus(boolean success, Camera camera) {
|
||||
//mCamera.cancelAutoFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
catch(RuntimeException e ) {
|
||||
Log.e("CAMERA_1::", "autoFocus failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void resetFocus(final boolean success, final Camera camera) {
|
||||
|
||||
@ -263,8 +263,8 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
|
||||
private Rect mInitialCropRegion;
|
||||
|
||||
Camera2(Callback callback, PreviewImpl preview, Context context) {
|
||||
super(callback, preview);
|
||||
Camera2(Callback callback, PreviewImpl preview, Context context, Handler bgHandler) {
|
||||
super(callback, preview, bgHandler);
|
||||
mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
|
||||
mCameraManager.registerAvailabilityCallback(new CameraManager.AvailabilityCallback() {
|
||||
@Override
|
||||
|
||||
@ -20,13 +20,14 @@ import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.ImageFormat;
|
||||
import android.hardware.camera2.params.StreamConfigurationMap;
|
||||
import android.os.Handler;
|
||||
|
||||
|
||||
@TargetApi(23)
|
||||
class Camera2Api23 extends Camera2 {
|
||||
|
||||
Camera2Api23(Callback callback, PreviewImpl preview, Context context) {
|
||||
super(callback, preview, context);
|
||||
Camera2Api23(Callback callback, PreviewImpl preview, Context context, Handler bgHandler) {
|
||||
super(callback, preview, context, bgHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -22,6 +22,8 @@ import android.graphics.Rect;
|
||||
import android.hardware.Camera;
|
||||
import android.media.CamcorderProfile;
|
||||
import android.os.Build;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Handler;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import androidx.annotation.IntDef;
|
||||
@ -91,6 +93,10 @@ public class CameraView extends FrameLayout {
|
||||
|
||||
private final DisplayOrientationDetector mDisplayOrientationDetector;
|
||||
|
||||
protected HandlerThread mBgThread;
|
||||
protected Handler mBgHandler;
|
||||
|
||||
|
||||
public CameraView(Context context, boolean fallbackToOldApi) {
|
||||
this(context, null, fallbackToOldApi);
|
||||
}
|
||||
@ -102,6 +108,13 @@ public class CameraView extends FrameLayout {
|
||||
@SuppressWarnings("WrongConstant")
|
||||
public CameraView(Context context, AttributeSet attrs, int defStyleAttr, boolean fallbackToOldApi) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
// bg hanadler for non UI heavy work
|
||||
mBgThread = new HandlerThread("RNCamera-Handler-Thread");
|
||||
mBgThread.start();
|
||||
mBgHandler = new Handler(mBgThread.getLooper());
|
||||
|
||||
|
||||
if (isInEditMode()){
|
||||
mCallbacks = null;
|
||||
mDisplayOrientationDetector = null;
|
||||
@ -114,11 +127,11 @@ public class CameraView extends FrameLayout {
|
||||
final PreviewImpl preview = createPreviewImpl(context);
|
||||
mCallbacks = new CallbackBridge();
|
||||
if (fallbackToOldApi || Build.VERSION.SDK_INT < 21) {
|
||||
mImpl = new Camera1(mCallbacks, preview);
|
||||
mImpl = new Camera1(mCallbacks, preview, mBgHandler);
|
||||
} else if (Build.VERSION.SDK_INT < 23) {
|
||||
mImpl = new Camera2(mCallbacks, preview, context);
|
||||
mImpl = new Camera2(mCallbacks, preview, context, mBgHandler);
|
||||
} else {
|
||||
mImpl = new Camera2Api23(mCallbacks, preview, context);
|
||||
mImpl = new Camera2Api23(mCallbacks, preview, context, mBgHandler);
|
||||
}
|
||||
|
||||
// Display orientation detector
|
||||
@ -131,6 +144,13 @@ public class CameraView extends FrameLayout {
|
||||
};
|
||||
}
|
||||
|
||||
public void cleanup(){
|
||||
if(mBgThread != null){
|
||||
mBgThread.quitSafely();
|
||||
mBgThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private PreviewImpl createPreviewImpl(Context context) {
|
||||
PreviewImpl preview;
|
||||
@ -269,9 +289,9 @@ public class CameraView extends FrameLayout {
|
||||
stop();
|
||||
}
|
||||
if (Build.VERSION.SDK_INT < 23) {
|
||||
mImpl = new Camera2(mCallbacks, mImpl.mPreview, mContext);
|
||||
mImpl = new Camera2(mCallbacks, mImpl.mPreview, mContext, mBgHandler);
|
||||
} else {
|
||||
mImpl = new Camera2Api23(mCallbacks, mImpl.mPreview, mContext);
|
||||
mImpl = new Camera2Api23(mCallbacks, mImpl.mPreview, mContext, mBgHandler);
|
||||
}
|
||||
} else {
|
||||
if (mImpl instanceof Camera1) {
|
||||
@ -281,7 +301,7 @@ public class CameraView extends FrameLayout {
|
||||
if (wasOpened) {
|
||||
stop();
|
||||
}
|
||||
mImpl = new Camera1(mCallbacks, mImpl.mPreview);
|
||||
mImpl = new Camera1(mCallbacks, mImpl.mPreview, mBgHandler);
|
||||
}
|
||||
start();
|
||||
}
|
||||
@ -298,7 +318,7 @@ public class CameraView extends FrameLayout {
|
||||
//store the state and restore this state after fall back to Camera1
|
||||
Parcelable state=onSaveInstanceState();
|
||||
// Camera2 uses legacy hardware layer; fall back to Camera1
|
||||
mImpl = new Camera1(mCallbacks, createPreviewImpl(getContext()));
|
||||
mImpl = new Camera1(mCallbacks, createPreviewImpl(getContext()), mBgHandler);
|
||||
onRestoreInstanceState(state);
|
||||
mImpl.start();
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ package com.google.android.cameraview;
|
||||
import android.media.CamcorderProfile;
|
||||
import android.view.View;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.os.Handler;
|
||||
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
@ -31,12 +32,19 @@ import java.util.SortedSet;
|
||||
abstract class CameraViewImpl {
|
||||
|
||||
protected final Callback mCallback;
|
||||
|
||||
protected final PreviewImpl mPreview;
|
||||
|
||||
CameraViewImpl(Callback callback, PreviewImpl preview) {
|
||||
// Background handler that the implementation an use to run heavy tasks in background
|
||||
// in a thread/looper provided by the view.
|
||||
// Most calls should not require this since the view will already schedule it
|
||||
// on the bg thread. However, the implementation might need to do some heavy work
|
||||
// by itself.
|
||||
protected final Handler mBgHandler;
|
||||
|
||||
CameraViewImpl(Callback callback, PreviewImpl preview, Handler bgHandler) {
|
||||
mCallback = callback;
|
||||
mPreview = preview;
|
||||
mBgHandler = bgHandler;
|
||||
}
|
||||
|
||||
View getView() {
|
||||
|
||||
@ -270,11 +270,8 @@ public class CameraModule extends ReactContextBaseJavaModule {
|
||||
promise.reject("E_CAMERA_UNAVAILABLE", "Camera is not running");
|
||||
}
|
||||
}
|
||||
catch(IllegalStateException e){
|
||||
promise.reject("E_CAMERA_UNAVAILABLE", e.getMessage());
|
||||
}
|
||||
catch (Exception e) {
|
||||
promise.reject("E_CAMERA_BAD_VIEWTAG", e.getMessage());
|
||||
promise.reject("E_TAKE_PICTURE_FAILED", e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -43,7 +43,7 @@ public class CameraViewManager extends ViewGroupManager<RNCameraView> {
|
||||
|
||||
@Override
|
||||
public void onDropViewInstance(RNCameraView view) {
|
||||
view.stop();
|
||||
view.onHostDestroy();
|
||||
super.onDropViewInstance(view);
|
||||
}
|
||||
|
||||
|
||||
@ -226,22 +226,28 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
mPlaySoundOnCapture = playSoundOnCapture;
|
||||
}
|
||||
|
||||
public void takePicture(ReadableMap options, final Promise promise, File cacheDirectory) {
|
||||
mPictureTakenPromises.add(promise);
|
||||
mPictureTakenOptions.put(promise, options);
|
||||
mPictureTakenDirectories.put(promise, cacheDirectory);
|
||||
if (mPlaySoundOnCapture) {
|
||||
MediaActionSound sound = new MediaActionSound();
|
||||
sound.play(MediaActionSound.SHUTTER_CLICK);
|
||||
}
|
||||
try {
|
||||
super.takePicture(options);
|
||||
} catch (Exception e) {
|
||||
mPictureTakenPromises.remove(promise);
|
||||
mPictureTakenOptions.remove(promise);
|
||||
mPictureTakenDirectories.remove(promise);
|
||||
throw e;
|
||||
}
|
||||
public void takePicture(final ReadableMap options, final Promise promise, final File cacheDirectory) {
|
||||
mBgHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mPictureTakenPromises.add(promise);
|
||||
mPictureTakenOptions.put(promise, options);
|
||||
mPictureTakenDirectories.put(promise, cacheDirectory);
|
||||
if (mPlaySoundOnCapture) {
|
||||
MediaActionSound sound = new MediaActionSound();
|
||||
sound.play(MediaActionSound.SHUTTER_CLICK);
|
||||
}
|
||||
try {
|
||||
RNCameraView.super.takePicture(options);
|
||||
} catch (Exception e) {
|
||||
mPictureTakenPromises.remove(promise);
|
||||
mPictureTakenOptions.remove(promise);
|
||||
mPictureTakenDirectories.remove(promise);
|
||||
|
||||
promise.reject("E_TAKE_PICTURE_FAILED", e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -249,39 +255,44 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
RNCameraViewHelper.emitPictureSavedEvent(this, response);
|
||||
}
|
||||
|
||||
public void record(ReadableMap options, final Promise promise, File cacheDirectory) {
|
||||
try {
|
||||
String path = options.hasKey("path") ? options.getString("path") : RNFileUtils.getOutputFilePath(cacheDirectory, ".mp4");
|
||||
int maxDuration = options.hasKey("maxDuration") ? options.getInt("maxDuration") : -1;
|
||||
int maxFileSize = options.hasKey("maxFileSize") ? options.getInt("maxFileSize") : -1;
|
||||
public void record(final ReadableMap options, final Promise promise, final File cacheDirectory) {
|
||||
mBgHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
String path = options.hasKey("path") ? options.getString("path") : RNFileUtils.getOutputFilePath(cacheDirectory, ".mp4");
|
||||
int maxDuration = options.hasKey("maxDuration") ? options.getInt("maxDuration") : -1;
|
||||
int maxFileSize = options.hasKey("maxFileSize") ? options.getInt("maxFileSize") : -1;
|
||||
|
||||
CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
|
||||
if (options.hasKey("quality")) {
|
||||
profile = RNCameraViewHelper.getCamcorderProfile(options.getInt("quality"));
|
||||
}
|
||||
if (options.hasKey("videoBitrate")) {
|
||||
profile.videoBitRate = options.getInt("videoBitrate");
|
||||
}
|
||||
CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
|
||||
if (options.hasKey("quality")) {
|
||||
profile = RNCameraViewHelper.getCamcorderProfile(options.getInt("quality"));
|
||||
}
|
||||
if (options.hasKey("videoBitrate")) {
|
||||
profile.videoBitRate = options.getInt("videoBitrate");
|
||||
}
|
||||
|
||||
boolean recordAudio = true;
|
||||
if (options.hasKey("mute")) {
|
||||
recordAudio = !options.getBoolean("mute");
|
||||
}
|
||||
boolean recordAudio = true;
|
||||
if (options.hasKey("mute")) {
|
||||
recordAudio = !options.getBoolean("mute");
|
||||
}
|
||||
|
||||
int orientation = Constants.ORIENTATION_AUTO;
|
||||
if (options.hasKey("orientation")) {
|
||||
orientation = options.getInt("orientation");
|
||||
}
|
||||
int orientation = Constants.ORIENTATION_AUTO;
|
||||
if (options.hasKey("orientation")) {
|
||||
orientation = options.getInt("orientation");
|
||||
}
|
||||
|
||||
if (super.record(path, maxDuration * 1000, maxFileSize, recordAudio, profile, orientation)) {
|
||||
mIsRecording = true;
|
||||
mVideoRecordedPromise = promise;
|
||||
} else {
|
||||
promise.reject("E_RECORDING_FAILED", "Starting video recording failed. Another recording might be in progress.");
|
||||
if (RNCameraView.super.record(path, maxDuration * 1000, maxFileSize, recordAudio, profile, orientation)) {
|
||||
mIsRecording = true;
|
||||
mVideoRecordedPromise = promise;
|
||||
} else {
|
||||
promise.reject("E_RECORDING_FAILED", "Starting video recording failed. Another recording might be in progress.");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
promise.reject("E_RECORDING_FAILED", "Starting video recording failed - could not create video file.");
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
promise.reject("E_RECORDING_FAILED", "Starting video recording failed - could not create video file.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -476,11 +487,16 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
@Override
|
||||
public void onHostResume() {
|
||||
if (hasCameraPermissions()) {
|
||||
if ((mIsPaused && !isCameraOpened()) || mIsNew) {
|
||||
mIsPaused = false;
|
||||
mIsNew = false;
|
||||
start();
|
||||
}
|
||||
mBgHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if ((mIsPaused && !isCameraOpened()) || mIsNew) {
|
||||
mIsPaused = false;
|
||||
mIsNew = false;
|
||||
start();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
RNCameraViewHelper.emitMountErrorEvent(this, "Camera permissions not granted - component could not be rendered.");
|
||||
}
|
||||
@ -508,6 +524,8 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
mMultiFormatReader = null;
|
||||
stop();
|
||||
mThemedReactContext.removeLifecycleEventListener(this);
|
||||
|
||||
this.cleanup();
|
||||
}
|
||||
|
||||
private boolean hasCameraPermissions() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user