Skip to content

Commit

Permalink
Make things safer
Browse files Browse the repository at this point in the history
1. Fallback for requested camera
2. Not crashing when taking pictures at the wrong time
3. Callback for state changes - to be able to take actions only when actually ready
4. Query for modes, like is flash available?
  • Loading branch information
danielgindi committed May 1, 2017
1 parent a558930 commit c118ad0
Showing 1 changed file with 115 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ public class CameraSource {
*/
private static final float ASPECT_RATIO_TOLERANCE = 0.01f;

public CameraSourceStateListener getStateListener() {
return mStateListener;
}

public void setStateListener(CameraSourceStateListener stateListener) {
this.mStateListener = stateListener;
}

@StringDef({
Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO,
Expand Down Expand Up @@ -122,11 +130,16 @@ public class CameraSource {

private int mFacing = CAMERA_FACING_BACK;

private boolean mCameraFallbackAllowed = true;

private CameraSourceStateListener mStateListener = null;

/**
* 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 +156,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 +168,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 +257,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 @@ -356,6 +383,11 @@ public CameraSource start() throws IOException {
mFrameProcessor.setActive(true);
mProcessingThread.start();
}

if (mStateListener != null) {
mStateListener.onCameraSourceStarted();
}

return this;
}

Expand All @@ -377,10 +409,17 @@ public CameraSource start(SurfaceHolder surfaceHolder) throws IOException {
mCamera.setPreviewDisplay(surfaceHolder);
mCamera.startPreview();

mCanTakePicture = true;

mProcessingThread = new Thread(mFrameProcessor);
mFrameProcessor.setActive(true);
mProcessingThread.start();
}

if (mStateListener != null) {
mStateListener.onCameraSourceStarted();
}

return this;
}

Expand Down Expand Up @@ -411,6 +450,8 @@ public void stop() {
// clear the buffer to prevent oom exceptions
mBytesToByteBuffer.clear();

mCanTakePicture = false;

if (mCamera != null) {
mCamera.stopPreview();
mCamera.setPreviewCallbackWithBuffer(null);
Expand All @@ -433,6 +474,10 @@ public void stop() {
mCamera = null;
}
}

if (mStateListener != null) {
mStateListener.onCameraSourceStopped();
}
}

/**
Expand All @@ -450,6 +495,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 +555,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 +599,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> supportedFocusModes = parameters.getSupportedFlashModes();
if (supportedFocusModes != null && supportedFocusModes.contains(mode)) {
parameters.setFocusMode(mode);
mCamera.setParameters(parameters);
mFocusMode = mode;
Expand Down Expand Up @@ -575,7 +640,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 @@ -587,6 +653,15 @@ public boolean setFlashMode(@FlashMode String mode) {
}
}

public boolean isModeSupported(String mode) {
if (mCamera != null) {
Camera.Parameters parameters = mCamera.getParameters();
final List<String> supportedModes = parameters.getSupportedFlashModes();
return supportedModes != null && supportedModes.contains(mode);
}
return false;
}

/**
* Starts camera auto-focus and registers a callback function to run when
* the camera is focused. This method is only valid when preview is active
Expand All @@ -600,7 +675,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 +774,7 @@ public void onPictureTaken(byte[] data, Camera camera) {
synchronized (mCameraLock) {
if (mCamera != null) {
mCamera.startPreview();
mCanTakePicture = true;
}
}
}
Expand Down Expand Up @@ -740,11 +816,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 +858,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> supportedFocusModes = parameters.getSupportedFlashModes();
if (supportedFocusModes != null && supportedFocusModes.contains(mFocusMode)) {
parameters.setFocusMode(mFocusMode);
} else {
Log.i(TAG, "Camera focus mode: " + mFocusMode + " is not supported on this device.");
Expand All @@ -785,13 +873,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 +1058,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 Expand Up @@ -1211,4 +1303,10 @@ public void run() {
}
}
}

public interface CameraSourceStateListener
{
void onCameraSourceStarted();
void onCameraSourceStopped();
}
}

0 comments on commit c118ad0

Please sign in to comment.