Skip to content

Commit

Permalink
Make things safer
Browse files Browse the repository at this point in the history
Fallback for requested camera, not crashing when taking pictures at the wrong time
  • Loading branch information
danielgindi authored May 1, 2017
1 parent a558930 commit 713538e
Showing 1 changed file with 76 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,14 @@ public class CameraSource {

private int mFacing = CAMERA_FACING_BACK;

private boolean mCameraFallbackAllowed = true;

/**
* Rotation of the device, and thus the associated preview images captured from the device.
* See {@link Frame.Metadata#getRotation()}.
*/
private int mRotation;
private int mRequestedCameraId;

private Size mPreviewSize;

Expand All @@ -143,7 +146,9 @@ public class CameraSource {
// These instances need to be held onto to avoid GC of their underlying resources. Even though
// these aren't used outside of the method that creates them, they still must have hard
// references maintained to them.
@SuppressWarnings("FieldCanBeLocal")
private SurfaceView mDummySurfaceView;
@SuppressWarnings("FieldCanBeLocal")
private SurfaceTexture mDummySurfaceTexture;

/**
Expand All @@ -153,6 +158,8 @@ public class CameraSource {
private Thread mProcessingThread;
private FrameProcessingRunnable mFrameProcessor;

private boolean mCanTakePicture = false;

/**
* Map to convert between a byte array, received from the camera, and its associated byte
* buffer. We use byte buffers internally because this is a more efficient way to call into
Expand Down Expand Up @@ -240,6 +247,16 @@ public Builder setFacing(int facing) {
return this;
}

/**
* Sets whether fallback from front to back or vice versa is allowed.
* Used in case the requested camera was not available.
* Default: true.
*/
public Builder setCameraFallbackAllowed(boolean allowed) {
mCameraSource.mCameraFallbackAllowed = allowed;
return this;
}

/**
* Creates an instance of the camera source.
*/
Expand Down Expand Up @@ -376,6 +393,8 @@ public CameraSource start(SurfaceHolder surfaceHolder) throws IOException {
mCamera = createCamera();
mCamera.setPreviewDisplay(surfaceHolder);
mCamera.startPreview();

mCanTakePicture = true;

mProcessingThread = new Thread(mFrameProcessor);
mFrameProcessor.setActive(true);
Expand Down Expand Up @@ -410,6 +429,8 @@ public void stop() {

// clear the buffer to prevent oom exceptions
mBytesToByteBuffer.clear();

mCanTakePicture = false;

if (mCamera != null) {
mCamera.stopPreview();
Expand Down Expand Up @@ -450,6 +471,22 @@ public int getCameraFacing() {
return mFacing;
}

/**
* Sets whether fallback from front to back or vice versa is allowed.
* Used in case the requested camera was not available.
*/
public boolean isCameraFallbackAllowed() {
return mCameraFallbackAllowed;
}

public boolean isCameraFacingBackAvailable() {
return getIdForRequestedCamera(CAMERA_FACING_BACK) != -1;
}

public boolean isCameraFacingFrontAvailable() {
return getIdForRequestedCamera(CAMERA_FACING_FRONT) != -1;
}

public int doZoom(float scale) {
synchronized (mCameraLock) {
if (mCamera == null) {
Expand Down Expand Up @@ -494,7 +531,10 @@ public int doZoom(float scale) {
*/
public void takePicture(ShutterCallback shutter, PictureCallback jpeg) {
synchronized (mCameraLock) {
if (mCamera != null) {
if (mCamera != null && mCanTakePicture) {

mCanTakePicture = false; // Preview is suspended until we're done

PictureStartCallback startCallback = new PictureStartCallback();
startCallback.mDelegate = shutter;
PictureDoneCallback doneCallback = new PictureDoneCallback();
Expand Down Expand Up @@ -535,7 +575,8 @@ public boolean setFocusMode(@FocusMode String mode) {
synchronized (mCameraLock) {
if (mCamera != null && mode != null) {
Camera.Parameters parameters = mCamera.getParameters();
if (parameters.getSupportedFocusModes().contains(mode)) {
final List<String> supportedFlashModes = parameters.getSupportedFlashModes();
if (supportedFlashModes != null && supportedFlashModes.contains(mode)) {
parameters.setFocusMode(mode);
mCamera.setParameters(parameters);
mFocusMode = mode;
Expand Down Expand Up @@ -575,7 +616,8 @@ public boolean setFlashMode(@FlashMode String mode) {
synchronized (mCameraLock) {
if (mCamera != null && mode != null) {
Camera.Parameters parameters = mCamera.getParameters();
if (parameters.getSupportedFlashModes().contains(mode)) {
final List<String> supportedFlashModes = parameters.getSupportedFlashModes();
if (supportedFlashModes != null && supportedFlashModes.contains(mode)) {
parameters.setFlashMode(mode);
mCamera.setParameters(parameters);
mFlashMode = mode;
Expand All @@ -600,7 +642,7 @@ public boolean setFlashMode(@FlashMode String mode) {
* <p/>
* <p>If the current flash mode is not
* {@link Camera.Parameters#FLASH_MODE_OFF}, flash may be
* fired during auto-focus, depending on the driver and camera hardware.<p>
* fired during auto-focus, depending on the driver and camera hardware.</p>
*
* @param cb the callback to run
* @see #cancelAutoFocus()
Expand Down Expand Up @@ -699,6 +741,7 @@ public void onPictureTaken(byte[] data, Camera camera) {
synchronized (mCameraLock) {
if (mCamera != null) {
mCamera.startPreview();
mCanTakePicture = true;
}
}
}
Expand Down Expand Up @@ -740,11 +783,23 @@ public void onAutoFocusMoving(boolean start, Camera camera) {
*/
@SuppressLint("InlinedApi")
private Camera createCamera() {
int requestedCameraId = getIdForRequestedCamera(mFacing);
if (requestedCameraId == -1) {
mRequestedCameraId = getIdForRequestedCamera(mFacing);

if (mRequestedCameraId == -1 && mCameraFallbackAllowed) {
if (mFacing == CAMERA_FACING_BACK) {
mFacing = CAMERA_FACING_FRONT;
} else {
mFacing = CAMERA_FACING_BACK;
}

mRequestedCameraId = getIdForRequestedCamera(mFacing);
}

if (mRequestedCameraId == -1) {
throw new RuntimeException("Could not find requested camera.");
}
Camera camera = Camera.open(requestedCameraId);

Camera camera = Camera.open(mRequestedCameraId);

SizePair sizePair = selectSizePair(camera, mRequestedPreviewWidth, mRequestedPreviewHeight);
if (sizePair == null) {
Expand All @@ -770,11 +825,11 @@ private Camera createCamera() {
previewFpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
parameters.setPreviewFormat(ImageFormat.NV21);

setRotation(camera, parameters, requestedCameraId);
setRotation(camera, parameters, mRequestedCameraId);

if (mFocusMode != null) {
if (parameters.getSupportedFocusModes().contains(
mFocusMode)) {
final List<String> supportedFlashModes = parameters.getSupportedFlashModes();
if (supportedFlashModes != null && supportedFlashModes.contains(mFocusMode)) {
parameters.setFocusMode(mFocusMode);
} else {
Log.i(TAG, "Camera focus mode: " + mFocusMode + " is not supported on this device.");
Expand All @@ -785,13 +840,11 @@ private Camera createCamera() {
mFocusMode = parameters.getFocusMode();

if (mFlashMode != null) {
if (parameters.getSupportedFlashModes() != null) {
if (parameters.getSupportedFlashModes().contains(
mFlashMode)) {
parameters.setFlashMode(mFlashMode);
} else {
Log.i(TAG, "Camera flash mode: " + mFlashMode + " is not supported on this device.");
}
final List<String> supportedFlashModes = parameters.getSupportedFlashModes();
if (supportedFlashModes != null && supportedFlashModes.contains(mFlashMode)) {
parameters.setFlashMode(mFlashMode);
} else {
Log.i(TAG, "Camera flash mode: " + mFlashMode + " is not supported on this device.");
}
}

Expand Down Expand Up @@ -972,6 +1025,12 @@ private int[] selectPreviewFpsRange(Camera camera, float desiredPreviewFps) {
return selectedFpsRange;
}

public void updateRotation() {
if (mCamera != null) {
setRotation(mCamera, mCamera.getParameters(), mRequestedCameraId);
}
}

/**
* Calculates the correct rotation for the given camera id and sets the rotation in the
* parameters. It also sets the camera's display orientation and rotation.
Expand Down

0 comments on commit 713538e

Please sign in to comment.