diff options
author | Sol Boucher <solb@google.com> | 2014-06-09 12:05:13 -0700 |
---|---|---|
committer | Sol Boucher <solb@google.com> | 2014-07-08 15:17:16 -0700 |
commit | a0842b40441db5332a5290f941021636b1182761 (patch) | |
tree | f87677f9a18112b5b8e4bcff2a502c8a416fa70d /camera2/portability/src/com | |
parent | 858edec400cf5c7cf3171f877f81bbc891fc4bcc (diff) | |
download | android_frameworks_ex-a0842b40441db5332a5290f941021636b1182761.tar.gz android_frameworks_ex-a0842b40441db5332a5290f941021636b1182761.tar.bz2 android_frameworks_ex-a0842b40441db5332a5290f941021636b1182761.zip |
camera2-portability: Add support for previews using the camera2 API
At the moment, only SurfaceTextures (and not SurfaceViews/SurfaceHolders) are
supported. The tests still only cover Stringifier and IntegralStringifier.
Change-Id: Ie643c58f8383cd3b9f59c16e0b79239df0ca068d
Diffstat (limited to 'camera2/portability/src/com')
13 files changed, 1723 insertions, 602 deletions
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java new file mode 100644 index 0000000..ec1f794 --- /dev/null +++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java @@ -0,0 +1,784 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ex.camera2.portability; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.SurfaceTexture; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CaptureRequest; +import android.os.Build; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.view.Surface; + +import com.android.ex.camera2.portability.debug.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * A class to implement {@link CameraAgent} of the Android camera2 framework. + */ +class AndroidCamera2AgentImpl extends CameraAgent { + private static final Log.Tag TAG = new Log.Tag("AndCam2AgntImp"); + + private final Camera2Handler mCameraHandler; + private final HandlerThread mCameraHandlerThread; + private final CameraStateHolder mCameraState; + private final DispatchThread mDispatchThread; + private final CameraManager mCameraManager; + + /** + * Number of camera devices. The length of {@code mCameraDevices} does not reveal this + * information because that list may contain since-invalidated indices. + */ + private int mNumCameraDevices; + + /** + * Transformation between integral camera indices and the {@link java.lang.String} indices used + * by the underlying API. Note that devices may disappear because they've been disconnected or + * have otherwise gone offline. Because we need to keep the meanings of whatever indices we + * expose stable, we cannot simply remove them in such a case; instead, we insert {@code null}s + * to invalidate any such indices. Whenever new devices appear, they are appended to the end of + * the list, and thereby assigned the lowest index that has never yet been used. + */ + private final List<String> mCameraDevices; + + AndroidCamera2AgentImpl(Context context) { + mCameraHandlerThread = new HandlerThread("Camera2 Handler Thread"); + mCameraHandlerThread.start(); + mCameraHandler = new Camera2Handler(mCameraHandlerThread.getLooper()); + mCameraState = new AndroidCamera2StateHolder(); + mDispatchThread = new DispatchThread(mCameraHandler, mCameraHandlerThread); + mDispatchThread.start(); + mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); + + mNumCameraDevices = 0; + mCameraDevices = new ArrayList<String>(); + updateCameraDevices(); + } + + /** + * Updates the camera device index assignments stored in {@link mCameraDevices}, without + * reappropriating any currently-assigned index. + * @return Whether the operation was successful + */ + private boolean updateCameraDevices() { + try { + String[] currentCameraDevices = mCameraManager.getCameraIdList(); + Set<String> currentSet = new HashSet<String>(Arrays.asList(currentCameraDevices)); + + // Invalidate the indices assigned to any camera devices that are no longer present + for (int index = 0; index < mCameraDevices.size(); ++index) { + if (!currentSet.contains(mCameraDevices.get(index))) { + mCameraDevices.set(index, null); + --mNumCameraDevices; + } + } + + // Assign fresh indices to any new camera devices + currentSet.removeAll(mCameraDevices); // The devices we didn't know about + for (String device : currentCameraDevices) { + if (currentSet.contains(device)) { + mCameraDevices.add(device); + ++mNumCameraDevices; + } + } + + return true; + } catch (CameraAccessException ex) { + Log.e(TAG, "Could not get device listing from camera subsystem", ex); + return false; + } + } + + // TODO: Implement + @Override + public void setCameraDefaultExceptionCallback(CameraExceptionCallback callback, + Handler handler) {} + + // TODO: Implement + @Override + public void recycle() {} + + // TODO: Some indices may now be invalid; ensure everyone can handle that and update the docs + @Override + public CameraDeviceInfo getCameraDeviceInfo() { + updateCameraDevices(); + return new AndroidCamera2DeviceInfo(mCameraManager, mCameraDevices.toArray(new String[0]), + mNumCameraDevices); + } + + @Override + protected Handler getCameraHandler() { + return mCameraHandler; + } + + @Override + protected DispatchThread getDispatchThread() { + return mDispatchThread; + } + + private class Camera2Handler extends HistoryHandler { + private CameraOpenCallback mOpenCallback; + private int mCameraIndex; + private String mCameraId; + private CameraDevice mCamera; + private AndroidCamera2ProxyImpl mCameraProxy; + private CaptureRequest.Builder mPreviewRequestBuilder; + private Size mPreviewSize; + private Size mPhotoSize; + private SurfaceTexture mPreviewTexture; + private Surface mPreviewSurface; + private CameraCaptureSession mPreviewSession; + + Camera2Handler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(final Message msg) { + super.handleMessage(msg); + try { + switch(msg.what) { + case CameraActions.OPEN_CAMERA: + case CameraActions.RECONNECT: { + CameraOpenCallback openCallback = (CameraOpenCallback) msg.obj; + int cameraIndex = msg.arg1; + + if (mCameraState.getState() != AndroidCamera2StateHolder.CAMERA_UNOPENED) { + openCallback.onDeviceOpenedAlready(cameraIndex, + generateHistoryString(cameraIndex)); + break; + } + + mOpenCallback = openCallback; + mCameraIndex = cameraIndex; + mCameraId = mCameraDevices.get(mCameraIndex); + + if (mCameraId == null) { + mOpenCallback.onCameraDisabled(msg.arg1); + break; + } + mCameraManager.openCamera(mCameraId, mCameraDeviceStateListener, this); + + break; + } + + case CameraActions.RELEASE: { + if (mCameraState.getState() == AndroidCamera2StateHolder.CAMERA_UNOPENED) { + Log.e(TAG, "Ignoring release at inappropriate time"); + } + + if (mCameraState.getState() == + AndroidCamera2StateHolder.CAMERA_PREVIEW_READY) { + closePreviewSession(); + } + if (mCamera != null) { + mCamera.close(); + mCamera = null; + } + mCameraProxy = null; + mPreviewRequestBuilder = null; + if (mPreviewSurface != null) { + mPreviewSurface.release(); + mPreviewSurface = null; + } + mPreviewTexture = null; + mPreviewSize = null; + mPhotoSize = null; + mCameraIndex = 0; + mCameraId = null; + mCameraState.setState(AndroidCamera2StateHolder.CAMERA_UNOPENED); + break; + } + + /*case CameraActions.UNLOCK: { + break; + } + + case CameraActions.LOCK: { + break; + }*/ + + case CameraActions.SET_PREVIEW_TEXTURE_ASYNC: { + setPreviewTexture((SurfaceTexture) msg.obj); + break; + } + + case CameraActions.START_PREVIEW_ASYNC: { + if (mCameraState.getState() != + AndroidCamera2StateHolder.CAMERA_PREVIEW_READY || + mPreviewSession == null) { + // TODO: Provide better feedback here? + Log.e(TAG, "Refusing to start preview at inappropriate time"); + break; + } + + final CameraStartPreviewCallbackForward cbForward = + (CameraStartPreviewCallbackForward) msg.obj; + CameraCaptureSession.CaptureListener cbDispatcher = null; + if (cbForward != null) { + cbDispatcher = new CameraCaptureSession.CaptureListener() { + @Override + public void onCaptureStarted(CameraCaptureSession session, + CaptureRequest request, + long timestamp) { + cbForward.onPreviewStarted(); + }}; + } + mPreviewSession.setRepeatingRequest(mPreviewRequestBuilder.build(), + cbDispatcher/*listener*/, this/*handler*/); + mCameraState.setState(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE); + break; + } + + case CameraActions.STOP_PREVIEW: { + if (mCameraState.getState() != + AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE || + mPreviewSession == null) { + Log.e(TAG, "Refusing to stop preview at inappropriate time"); + } + + mPreviewSession.stopRepeating(); + mCameraState.setState(AndroidCamera2StateHolder.CAMERA_PREVIEW_READY); + break; + } + + /*case CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER: { + break; + } + + case CameraActions.ADD_CALLBACK_BUFFER: { + break; + } + + case CameraActions.SET_PREVIEW_DISPLAY_ASYNC: { + break; + } + + case CameraActions.SET_PREVIEW_CALLBACK: { + break; + } + + case CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK: { + break; + } + + case CameraActions.SET_PARAMETERS: { + break; + } + + case CameraActions.GET_PARAMETERS: { + break; + } + + case CameraActions.REFRESH_PARAMETERS: { + break; + }*/ + + case CameraActions.APPLY_SETTINGS: { + CameraSettings settings = (CameraSettings) msg.obj; + applyToRequest(settings); + break; + } + + /*case CameraActions.AUTO_FOCUS: { + break; + } + + case CameraActions.CANCEL_AUTO_FOCUS: { + break; + } + + case CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK: { + break; + } + + case CameraActions.SET_ZOOM_CHANGE_LISTENER: { + break; + } + + case CameraActions.SET_FACE_DETECTION_LISTENER: { + break; + } + + case CameraActions.START_FACE_DETECTION: { + break; + } + + case CameraActions.STOP_FACE_DETECTION: { + break; + } + + case CameraActions.SET_ERROR_CALLBACK: { + break; + } + + case CameraActions.ENABLE_SHUTTER_SOUND: { + break; + } + + case CameraActions.SET_DISPLAY_ORIENTATION: { + break; + } + + case CameraActions.CAPTURE_PHOTO: { + break; + }*/ + + default: { + // TODO: Rephrase once everything has been implemented + throw new RuntimeException("Unimplemented CameraProxy message=" + msg.what); + } + } + } catch (final Exception ex) { + if (msg.what != CameraActions.RELEASE && mCamera != null) { + mCamera.close(); + mCamera = null; + } else if (mCamera == null) { + if (msg.what == CameraActions.OPEN_CAMERA) { + if (mOpenCallback != null) { + mOpenCallback.onDeviceOpenFailure(mCameraIndex, + generateHistoryString(mCameraIndex)); + } + } else { + Log.w(TAG, "Cannot handle message " + msg.what + ", mCamera is null"); + } + return; + } + + if (ex instanceof RuntimeException) { + post(new Runnable() { + @Override + public void run() { + sCameraExceptionCallback.onCameraException((RuntimeException) ex); + }}); + } + } + } + + public CameraSettings buildSettings(AndroidCamera2Capabilities caps) { + return new AndroidCamera2Settings(caps, mPreviewRequestBuilder, mPreviewSize, + mPhotoSize); + } + + // TODO: Finish implementation to add support for all settings + private void applyToRequest(CameraSettings settings) { + // TODO: If invoked when in PREVIEW_READY state, a new preview size will not take effect + AndroidCamera2Capabilities.IntegralStringifier intifier = + mCameraProxy.getSpecializedCapabilities().getIntegralStringifier(); + mPreviewSize = settings.getCurrentPreviewSize(); + mPhotoSize = settings.getCurrentPhotoSize(); + mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, + intifier.intify(settings.getCurrentFocusMode())); + if (settings.getCurrentFlashMode() != CameraCapabilities.FlashMode.NO_FLASH) { + mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE, + intifier.intify(settings.getCurrentFlashMode())); + } + if (settings.getCurrentSceneMode() != CameraCapabilities.SceneMode.NO_SCENE_MODE) { + mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, + intifier.intify(settings.getCurrentSceneMode())); + } + + // If we're already ready to preview, this doesn't regress our state + if (mCameraState.getState() != AndroidCamera2StateHolder.CAMERA_PREVIEW_READY) { + mCameraState.setState(AndroidCamera2StateHolder.CAMERA_CONFIGURED); + } + } + + private void setPreviewTexture(SurfaceTexture surfaceTexture) { + // TODO: Must be called after providing a .*Settings populated with sizes + // TODO: We don't technically offer a selection of sizes tailored to SurfaceTextures! + + // TODO: Handle this error condition with a callback or exception + if (mCameraState.getState() != AndroidCamera2StateHolder.CAMERA_CONFIGURED) { + Log.e(TAG, "Ignoring texture setting at inappropriate time"); + return; + } + + // Avoid initializing another capture session unless we absolutely have to + if (surfaceTexture == mPreviewTexture) { + Log.i(TAG, "Optimizing out redundant preview texture setting"); + return; + } + + if (mPreviewSession != null) { + closePreviewSession(); + } + + mPreviewTexture = surfaceTexture; + surfaceTexture.setDefaultBufferSize(mPreviewSize.width(), mPreviewSize.height()); + + if (mPreviewSurface != null) { + mPreviewRequestBuilder.removeTarget(mPreviewSurface); + mPreviewSurface.release(); + } + mPreviewSurface = new Surface(surfaceTexture); + mPreviewRequestBuilder.addTarget(mPreviewSurface); + + try { + mCamera.createCaptureSession(Arrays.asList(mPreviewSurface), + mCameraPreviewStateListener, this); + } catch (CameraAccessException ex) { + Log.e(TAG, "Failed to create camera capture session", ex); + } + } + + private void closePreviewSession() { + try { + mPreviewSession.abortCaptures(); + mPreviewSession = null; + } catch (CameraAccessException ex) { + Log.e(TAG, "Failed to close existing camera capture session", ex); + } + mCameraState.setState(AndroidCamera2StateHolder.CAMERA_CONFIGURED); + } + + private CameraDevice.StateListener mCameraDeviceStateListener = + new CameraDevice.StateListener() { + @Override + public void onOpened(CameraDevice camera) { + mCamera = camera; + if (mOpenCallback != null) { + try { + CameraCharacteristics props = + mCameraManager.getCameraCharacteristics(mCameraId); + mCameraProxy = new AndroidCamera2ProxyImpl(mCameraIndex, mCamera, + getCameraDeviceInfo().getCharacteristics(mCameraIndex), props); + mPreviewRequestBuilder = + camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); + mCameraState.setState(AndroidCamera2StateHolder.CAMERA_UNCONFIGURED); + mOpenCallback.onCameraOpened(mCameraProxy); + } catch (CameraAccessException ex) { + mOpenCallback.onDeviceOpenFailure(mCameraIndex, + generateHistoryString(mCameraIndex)); + } + } + } + + @Override + public void onDisconnected(CameraDevice camera) { + Log.w(TAG, "Camera device '" + mCameraIndex + "' was disconnected"); + } + + @Override + public void onError(CameraDevice camera, int error) { + Log.e(TAG, "Camera device '" + mCameraIndex + "' encountered error code '" + + error + '\''); + if (mOpenCallback != null) { + mOpenCallback.onDeviceOpenFailure(mCameraIndex, + generateHistoryString(mCameraIndex)); + } + }}; + + private CameraCaptureSession.StateListener mCameraPreviewStateListener = + new CameraCaptureSession.StateListener() { + @Override + public void onConfigured(CameraCaptureSession session) { + mPreviewSession = session; + mCameraState.setState(AndroidCamera2StateHolder.CAMERA_PREVIEW_READY); + } + + @Override + public void onConfigureFailed(CameraCaptureSession session) { + // TODO: Invoke a callback + Log.e(TAG, "Failed to configure the camera for capture"); + }}; + } + + private class AndroidCamera2ProxyImpl extends CameraAgent.CameraProxy { + private final int mCameraIndex; + private final CameraDevice mCamera; + private final CameraDeviceInfo.Characteristics mCharacteristics; + private final AndroidCamera2Capabilities mCapabilities; + + public AndroidCamera2ProxyImpl(int cameraIndex, CameraDevice camera, + CameraDeviceInfo.Characteristics characteristics, + CameraCharacteristics properties) { + mCameraIndex = cameraIndex; + mCamera = camera; + mCharacteristics = characteristics; + mCapabilities = new AndroidCamera2Capabilities(properties); + } + + // TODO: Implement + @Override + public android.hardware.Camera getCamera() { return null; } + + @Override + public int getCameraId() { + return mCameraIndex; + } + + @Override + public CameraDeviceInfo.Characteristics getCharacteristics() { + return mCharacteristics; + } + + @Override + public CameraCapabilities getCapabilities() { + return mCapabilities; + } + + private AndroidCamera2Capabilities getSpecializedCapabilities() { + return mCapabilities; + } + + // TODO: Implement + @Override + public void setPreviewDataCallback(Handler handler, CameraPreviewDataCallback cb) {} + + // TODO: Implement + @Override + public void setOneShotPreviewCallback(Handler handler, CameraPreviewDataCallback cb) {} + + // TODO: Implement + @Override + public void setPreviewDataCallbackWithBuffer(Handler handler, CameraPreviewDataCallback cb) + {} + + // TODO: Implement + @Override + public void autoFocus(Handler handler, CameraAFCallback cb) {} + + // TODO: Remove this method override once we handle this message + @Override + public void cancelAutoFocus() {} + + // TODO: Implement + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + @Override + public void setAutoFocusMoveCallback(Handler handler, CameraAFMoveCallback cb) {} + + // TODO: Implement + @Override + public void takePicture(Handler handler, + CameraShutterCallback shutter, + CameraPictureCallback raw, + CameraPictureCallback postview, + CameraPictureCallback jpeg) {} + + // TODO: Remove this method override once we handle the message + @Override + public void setDisplayOrientation(int degrees) {} + + // TODO: Implement + @Override + public void setZoomChangeListener(android.hardware.Camera.OnZoomChangeListener listener) {} + + // TODO: Implement + @Override + public void setFaceDetectionCallback(Handler handler, CameraFaceDetectionCallback callback) + {} + + // TODO: Remove this method override once we handle this message + @Override + public void startFaceDetection() {} + + // TODO: Implement + @Override + public void setErrorCallback(Handler handler, CameraErrorCallback cb) {} + + // TODO: Implement + @Override + public void setParameters(android.hardware.Camera.Parameters params) {} + + // TODO: Implement + @Override + public android.hardware.Camera.Parameters getParameters() { return null; } + + @Override + public CameraSettings getSettings() { + return mCameraHandler.buildSettings(mCapabilities); + } + + @Override + public boolean applySettings(CameraSettings settings) { + return applySettingsHelper(settings, AndroidCamera2StateHolder.CAMERA_UNCONFIGURED | + AndroidCamera2StateHolder.CAMERA_CONFIGURED | + AndroidCamera2StateHolder.CAMERA_PREVIEW_READY); + } + + // TODO: Implement + @Override + public String dumpDeviceSettings() { return null; } + + @Override + public Handler getCameraHandler() { + return AndroidCamera2AgentImpl.this.getCameraHandler(); + } + + @Override + public DispatchThread getDispatchThread() { + return AndroidCamera2AgentImpl.this.getDispatchThread(); + } + + @Override + public CameraStateHolder getCameraState() { + return mCameraState; + } + } + + private static class AndroidCamera2StateHolder extends CameraStateHolder { + // Usage flow: openCamera() -> applySettings() -> setPreviewTexture() -> startPreview() + /* Camera states */ + /** No camera device is opened. */ + public static final int CAMERA_UNOPENED = 1; + /** A camera is opened, but no settings have been provided. */ + public static final int CAMERA_UNCONFIGURED = 2; + /** The open camera has been configured by providing it with settings. */ + public static final int CAMERA_CONFIGURED = 3; + /** A capture session is ready to stream a preview, but still has no repeating request. */ + public static final int CAMERA_PREVIEW_READY = 4; + /** A preview is currently being streamed. */ + public static final int CAMERA_PREVIEW_ACTIVE = 5; + + private int mState; + + public AndroidCamera2StateHolder() { + this(CAMERA_UNOPENED); + } + + public AndroidCamera2StateHolder(int state) { + setState(state); + } + + @Override + public synchronized void setState(int state) { + mState = state; + this.notifyAll(); + } + + @Override + public synchronized int getState() { + return mState; + } + } + + private static class AndroidCamera2DeviceInfo implements CameraDeviceInfo { + private final CameraManager mCameraManager; + private final String[] mCameraIds; + private final int mNumberOfCameras; + private final int mFirstBackCameraId; + private final int mFirstFrontCameraId; + + public AndroidCamera2DeviceInfo(CameraManager cameraManager, + String[] cameraIds, int numberOfCameras) { + mCameraManager = cameraManager; + mCameraIds = cameraIds; + mNumberOfCameras = numberOfCameras; + + int firstBackId = NO_DEVICE; + int firstFrontId = NO_DEVICE; + for (int id = 0; id < cameraIds.length; ++id) { + try { + int lensDirection = cameraManager.getCameraCharacteristics(cameraIds[id]) + .get(CameraCharacteristics.LENS_FACING); + if (firstBackId == NO_DEVICE && + lensDirection == CameraCharacteristics.LENS_FACING_BACK) { + firstBackId = id; + } + if (firstFrontId == NO_DEVICE && + lensDirection == CameraCharacteristics.LENS_FACING_FRONT) { + firstFrontId = id; + } + } catch (CameraAccessException ex) { + Log.w(TAG, "Couldn't get characteristics of camera '" + id + "'", ex); + } + } + mFirstBackCameraId = firstBackId; + mFirstFrontCameraId = firstFrontId; + } + + @Override + public Characteristics getCharacteristics(int cameraId) { + String actualId = mCameraIds[cameraId]; + try { + CameraCharacteristics info = mCameraManager.getCameraCharacteristics(actualId); + return new AndroidCharacteristics2(info); + } catch (CameraAccessException ex) { + return null; + } + } + + @Override + public int getNumberOfCameras() { + return mNumberOfCameras; + } + + @Override + public int getFirstBackCameraId() { + return mFirstBackCameraId; + } + + @Override + public int getFirstFrontCameraId() { + return mFirstFrontCameraId; + } + + private static class AndroidCharacteristics2 implements Characteristics { + private CameraCharacteristics mCameraInfo; + + AndroidCharacteristics2(CameraCharacteristics cameraInfo) { + mCameraInfo = cameraInfo; + } + + @Override + public boolean isFacingBack() { + return mCameraInfo.get(CameraCharacteristics.LENS_FACING) + .equals(CameraCharacteristics.LENS_FACING_BACK); + } + + @Override + public boolean isFacingFront() { + return mCameraInfo.get(CameraCharacteristics.LENS_FACING) + .equals(CameraCharacteristics.LENS_FACING_FRONT); + } + + @Override + public int getSensorOrientation() { + return mCameraInfo.get(CameraCharacteristics.SENSOR_ORIENTATION); + } + + @Override + public boolean canDisableShutterSound() { + // The new API doesn't support this operation, so don't encourage people to try it. + // TODO: What kind of assumptions have callers made about this result's meaning? + return false; + } + } + } + + private static final CameraExceptionCallback sCameraExceptionCallback = + new CameraExceptionCallback() { + @Override + public synchronized void onCameraException(RuntimeException e) { + throw e; + } + }; +} diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Capabilities.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Capabilities.java new file mode 100644 index 0000000..03c9ba6 --- /dev/null +++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Capabilities.java @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ex.camera2.portability; + +import static android.hardware.camera2.CameraCharacteristics.*; + +import android.graphics.Point; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.ImageReader; +import android.media.MediaRecorder; +import android.util.Range; +import android.util.Rational; +import android.view.SurfaceHolder; + +import com.android.ex.camera2.portability.debug.Log; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * The subclass of {@link CameraCapabilities} for Android Camera 2 API. + */ +public class AndroidCamera2Capabilities extends CameraCapabilities { + private static Log.Tag TAG = new Log.Tag("AndCam2Capabs"); + + private IntegralStringifier mIntStringifier; + + AndroidCamera2Capabilities(CameraCharacteristics p) { + super(new IntegralStringifier()); + mIntStringifier = (IntegralStringifier) getStringifier(); + + StreamConfigurationMap s = p.get(SCALER_STREAM_CONFIGURATION_MAP); + + for (Range<Integer> fpsRange : p.get(CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES)) { + mSupportedPreviewFpsRange.add(new int[] { fpsRange.getLower(), fpsRange.getUpper() }); + } + + // TODO: We only support SurfaceView preview rendering + mSupportedPreviewSizes.addAll(Size.buildListFromAndroidSizes(Arrays.asList( + s.getOutputSizes(SurfaceHolder.class)))); + for (int format : s.getOutputFormats()) { + mSupportedPreviewFormats.add(format); + } + + // TODO: We only support MediaRecorder videos capture + mSupportedVideoSizes.addAll(Size.buildListFromAndroidSizes(Arrays.asList( + s.getOutputSizes(MediaRecorder.class)))); + + // TODO: We only support ImageReader image capture + mSupportedPhotoSizes.addAll(Size.buildListFromAndroidSizes(Arrays.asList( + s.getOutputSizes(ImageReader.class)))); + mSupportedPhotoFormats.addAll(mSupportedPreviewFormats); + + buildSceneModes(p); + buildFlashModes(p); + buildFocusModes(p); + buildWhiteBalances(p); + // TODO: Populate mSupportedFeatures + + // TODO: Populate mPreferredPreviewSizeForVideo + + Range<Integer> ecRange = p.get(CONTROL_AE_COMPENSATION_RANGE); + mMinExposureCompensation = ecRange.getLower(); + mMaxExposureCompensation = ecRange.getUpper(); + + Rational ecStep = p.get(CONTROL_AE_COMPENSATION_STEP); + mExposureCompensationStep = (float) ecStep.getNumerator() / ecStep.getDenominator(); + + mMaxNumOfFacesSupported = p.get(STATISTICS_INFO_MAX_FACE_COUNT); + mMaxNumOfFocusAreas = p.get(CONTROL_MAX_REGIONS_AF); + mMaxNumOfMeteringArea = p.get(CONTROL_MAX_REGIONS_AE); + + // TODO: Populate mMaxZoomRatio + // TODO: Populate mHorizontalViewAngle + // TODO: Populate mVerticalViewAngle + // TODO: Populate mZoomRatioList + // TODO: Populate mMaxZoomIndex + } + + public IntegralStringifier getIntegralStringifier() { + return mIntStringifier; + } + + private void buildSceneModes(CameraCharacteristics p) { + for (int scene : p.get(CONTROL_AVAILABLE_SCENE_MODES)) { + SceneMode equiv = mIntStringifier.sceneModeFromInt(scene); + if (equiv != SceneMode.NO_SCENE_MODE) { + // equiv isn't a default generated because we couldn't handle this mode, so add it + mSupportedSceneModes.add(equiv); + } + } + } + + private void buildFlashModes(CameraCharacteristics p) { + mSupportedFlashModes.add(FlashMode.OFF); + if (p.get(FLASH_INFO_AVAILABLE)) { + mSupportedFlashModes.add(FlashMode.ON); + mSupportedFlashModes.add(FlashMode.TORCH); + // TODO: New modes aren't represented here + } + } + + private void buildFocusModes(CameraCharacteristics p) { + for (int focus : p.get(CONTROL_AF_AVAILABLE_MODES)) { + FocusMode equiv = mIntStringifier.focusModeFromInt(focus); + if (equiv != FocusMode.AUTO || focus == CONTROL_AF_MODE_AUTO) { + // equiv isn't a default generated because we couldn't handle this mode, so add it + mSupportedFocusModes.add(equiv); + } + } + } + + private void buildWhiteBalances(CameraCharacteristics p) { + for (int bal : p.get(CONTROL_AWB_AVAILABLE_MODES)) { + WhiteBalance equiv = mIntStringifier.whiteBalanceFromInt(bal); + if (equiv != WhiteBalance.AUTO || bal == CONTROL_AWB_MODE_AUTO) { + // equiv isn't a default generated because we couldn't handle this mode, so add it + mSupportedWhiteBalances.add(equiv); + } + } + } + + public static class IntegralStringifier extends Stringifier { + /** + * Converts the focus mode to API-related integer representation. + * + * @param fm The focus mode to convert. + * @return The corresponding {@code int} used by the camera framework + * API, or {@link CONTROL_AF_MODE_AUTO} if that fails. + */ + public int intify(FocusMode fm) { + switch (fm) { + case AUTO: + return CONTROL_AF_MODE_AUTO; + case CONTINUOUS_PICTURE: + return CONTROL_AF_MODE_CONTINUOUS_PICTURE; + case CONTINUOUS_VIDEO: + return CONTROL_AF_MODE_CONTINUOUS_VIDEO; + case EXTENDED_DOF: + return CONTROL_AF_MODE_EDOF; + case FIXED: + return CONTROL_AF_MODE_OFF; + case MACRO: + return CONTROL_AF_MODE_MACRO; + // TODO: New modes aren't represented here + } + return CONTROL_AF_MODE_AUTO; + } + + /** + * Converts the API-related integer representation of the focus mode to + * the abstract representation. + * + * @param val The integral representation. + * @return The mode represented by the input integer, or the focus mode + * with the lowest ordinal if it cannot be converted. + */ + public FocusMode focusModeFromInt(int fm) { + switch (fm) { + case CONTROL_AF_MODE_AUTO: + return FocusMode.AUTO; + case CONTROL_AF_MODE_CONTINUOUS_PICTURE: + return FocusMode.CONTINUOUS_PICTURE; + case CONTROL_AF_MODE_CONTINUOUS_VIDEO: + return FocusMode.CONTINUOUS_VIDEO; + case CONTROL_AF_MODE_EDOF: + return FocusMode.EXTENDED_DOF; + case CONTROL_AF_MODE_OFF: + return FocusMode.FIXED; + case CONTROL_AF_MODE_MACRO: + return FocusMode.MACRO; + // TODO: New modes aren't represented here + } + return FocusMode.values()[0]; + } + + /** + * Converts the flash mode to API-related integer representation. + * + * @param fm The flash mode to convert. + * @return The corresponding {@code int} used by the camera framework + * API, or {@link CONTROL_AF_MODE_AUTO} if that fails. + */ + public int intify(FlashMode flm) { + switch (flm) { + case OFF: + return FLASH_MODE_OFF; + case ON: + return FLASH_MODE_SINGLE; + case TORCH: + return FLASH_MODE_TORCH; + // TODO: New modes aren't represented here + } + return FLASH_MODE_OFF; + } + + /** + * Converts the API-related integer representation of the flash mode to + * the abstract representation. + * + * @param flm The integral representation. + * @return The mode represented by the input integer, or the flash mode + * with the lowest ordinal if it cannot be converted. + */ + public FlashMode flashModeFromInt(int flm) { + switch (flm) { + case FLASH_MODE_OFF: + return FlashMode.OFF; + case FLASH_MODE_SINGLE: + return FlashMode.ON; + case FLASH_MODE_TORCH: + return FlashMode.TORCH; + // TODO: New modes aren't represented here + } + return FlashMode.values()[0]; + } + + /** + * Converts the scene mode to API-related integer representation. + * + * @param fm The scene mode to convert. + * @return The corresponding {@code int} used by the camera framework + * API, or {@link CONTROL_SCENE_MODE_DISABLED} if that fails. + */ + public int intify(SceneMode sm) { + switch (sm) { + case AUTO: + return CONTROL_SCENE_MODE_DISABLED; + case ACTION: + return CONTROL_SCENE_MODE_ACTION; + case BARCODE: + return CONTROL_SCENE_MODE_BARCODE; + case BEACH: + return CONTROL_SCENE_MODE_BEACH; + case CANDLELIGHT: + return CONTROL_SCENE_MODE_CANDLELIGHT; + case FIREWORKS: + return CONTROL_SCENE_MODE_FIREWORKS; + case LANDSCAPE: + return CONTROL_SCENE_MODE_LANDSCAPE; + case NIGHT: + return CONTROL_SCENE_MODE_NIGHT; + case PARTY: + return CONTROL_SCENE_MODE_PARTY; + case PORTRAIT: + return CONTROL_SCENE_MODE_PORTRAIT; + case SNOW: + return CONTROL_SCENE_MODE_SNOW; + case SPORTS: + return CONTROL_SCENE_MODE_SPORTS; + case STEADYPHOTO: + return CONTROL_SCENE_MODE_STEADYPHOTO; + case SUNSET: + return CONTROL_SCENE_MODE_SUNSET; + case THEATRE: + return CONTROL_SCENE_MODE_THEATRE; + // TODO: New modes aren't represented here + } + return CONTROL_SCENE_MODE_DISABLED; + } + + /** + * Converts the API-related integer representation of the scene mode to + * the abstract representation. + * + * @param sm The integral representation. + * @return The mode represented by the input integer, or the scene mode + * with the lowest ordinal if it cannot be converted. + */ + public SceneMode sceneModeFromInt(int sm) { + switch (sm) { + case CONTROL_SCENE_MODE_DISABLED: + return SceneMode.AUTO; + case CONTROL_SCENE_MODE_ACTION: + return SceneMode.ACTION; + case CONTROL_SCENE_MODE_BARCODE: + return SceneMode.BARCODE; + case CONTROL_SCENE_MODE_BEACH: + return SceneMode.BEACH; + case CONTROL_SCENE_MODE_CANDLELIGHT: + return SceneMode.CANDLELIGHT; + case CONTROL_SCENE_MODE_FIREWORKS: + return SceneMode.FIREWORKS; + case CONTROL_SCENE_MODE_LANDSCAPE: + return SceneMode.LANDSCAPE; + case CONTROL_SCENE_MODE_NIGHT: + return SceneMode.NIGHT; + case CONTROL_SCENE_MODE_PARTY: + return SceneMode.PARTY; + case CONTROL_SCENE_MODE_PORTRAIT: + return SceneMode.PORTRAIT; + case CONTROL_SCENE_MODE_SNOW: + return SceneMode.SNOW; + case CONTROL_SCENE_MODE_SPORTS: + return SceneMode.SPORTS; + case CONTROL_SCENE_MODE_STEADYPHOTO: + return SceneMode.STEADYPHOTO; + case CONTROL_SCENE_MODE_SUNSET: + return SceneMode.SUNSET; + case CONTROL_SCENE_MODE_THEATRE: + return SceneMode.THEATRE; + // TODO: New modes aren't represented here + } + return SceneMode.values()[0]; + } + + /** + * Converts the white balance to API-related integer representation. + * + * @param fm The white balance to convert. + * @return The corresponding {@code int} used by the camera framework + * API, or {@link CONTROL_SCENE_MODE_DISABLED} if that fails. + */ + public int intify(WhiteBalance wb) { + switch (wb) { + case AUTO: + return CONTROL_AWB_MODE_AUTO; + case CLOUDY_DAYLIGHT: + return CONTROL_AWB_MODE_CLOUDY_DAYLIGHT; + case DAYLIGHT: + return CONTROL_AWB_MODE_DAYLIGHT; + case FLUORESCENT: + return CONTROL_AWB_MODE_FLUORESCENT; + case INCANDESCENT: + return CONTROL_AWB_MODE_INCANDESCENT; + case SHADE: + return CONTROL_AWB_MODE_SHADE; + case TWILIGHT: + return CONTROL_AWB_MODE_TWILIGHT; + case WARM_FLUORESCENT: + return CONTROL_AWB_MODE_WARM_FLUORESCENT; + // TODO: New modes aren't represented here + } + return CONTROL_AWB_MODE_AUTO; + } + + /** + * Converts the API-related integer representation of the white balance + * to the abstract representation. + * + * @param wb The integral representation. + * @return The balance represented by the input integer, or the white + * balance with the lowest ordinal if it cannot be converted. + */ + public WhiteBalance whiteBalanceFromInt(int wb) { + switch (wb) { + case CONTROL_AWB_MODE_AUTO: + return WhiteBalance.AUTO; + case CONTROL_AWB_MODE_CLOUDY_DAYLIGHT: + return WhiteBalance.CLOUDY_DAYLIGHT; + case CONTROL_AWB_MODE_DAYLIGHT: + return WhiteBalance.DAYLIGHT; + case CONTROL_AWB_MODE_FLUORESCENT: + return WhiteBalance.FLUORESCENT; + case CONTROL_AWB_MODE_INCANDESCENT: + return WhiteBalance.INCANDESCENT; + case CONTROL_AWB_MODE_SHADE: + return WhiteBalance.SHADE; + case CONTROL_AWB_MODE_TWILIGHT: + return WhiteBalance.TWILIGHT; + case CONTROL_AWB_MODE_WARM_FLUORESCENT: + return WhiteBalance.WARM_FLUORESCENT; + // TODO: New modes aren't represented here + } + return WhiteBalance.values()[0]; + } + } +} diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java new file mode 100644 index 0000000..e21e177 --- /dev/null +++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ex.camera2.portability; + +import android.hardware.camera2.CaptureRequest; + +/** + * The subclass of {@link CameraSettings} for Android Camera 2 API. + */ +public class AndroidCamera2Settings extends CameraSettings { + // TODO: Implement more completely + public AndroidCamera2Settings(AndroidCamera2Capabilities capabilities, + CaptureRequest.Builder request, + Size preview, Size photo) { + // TODO: Support zoom + setZoomRatio(1.0f); + setZoomIndex(0); + + // TODO: Set exposure compensation + + AndroidCamera2Capabilities.IntegralStringifier stringifier = + capabilities.getIntegralStringifier(); + setFocusMode(stringifier.focusModeFromInt(request.get(CaptureRequest.CONTROL_AF_MODE))); + setFlashMode(stringifier.flashModeFromInt(request.get(CaptureRequest.FLASH_MODE))); + setSceneMode(stringifier.sceneModeFromInt(request.get(CaptureRequest.CONTROL_SCENE_MODE))); + + // This is mutability-safe because those setters copy the Size objects + setPreviewSize(preview); + setPhotoSize(photo); + + // TODO: Initialize formats, too + } +} diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java index 7388519..328c8f4 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java @@ -43,8 +43,8 @@ import java.util.StringTokenizer; /** * A class to implement {@link CameraAgent} of the Android camera framework. */ -class AndroidCameraAgentImpl implements CameraAgent { - private static final Log.Tag TAG = new Log.Tag("AndroidCamMgrImpl"); +class AndroidCameraAgentImpl extends CameraAgent { + private static final Log.Tag TAG = new Log.Tag("AndCamAgntImp"); private Parameters mParameters; private boolean mParametersIsDirty; @@ -70,7 +70,7 @@ class AndroidCameraAgentImpl implements CameraAgent { mCameraHandlerThread.start(); mCameraHandler = new CameraHandler(mCameraHandlerThread.getLooper()); mCameraExceptionCallbackHandler = mCameraHandler; - mCameraState = new CameraStateHolder(); + mCameraState = new AndroidCameraStateHolder(); mDispatchThread = new DispatchThread(mCameraHandler, mCameraHandlerThread); mDispatchThread.start(); } @@ -95,6 +95,16 @@ class AndroidCameraAgentImpl implements CameraAgent { return AndroidCameraDeviceInfo.create(); } + @Override + protected Handler getCameraHandler() { + return mCameraHandler; + } + + @Override + protected DispatchThread getDispatchThread() { + return mDispatchThread; + } + private static class AndroidCameraDeviceInfo implements CameraDeviceInfo { private final Camera.CameraInfo[] mCameraInfos; private final int mNumberOfCameras; @@ -289,7 +299,7 @@ class AndroidCameraAgentImpl implements CameraAgent { case CameraActions.OPEN_CAMERA: { final CameraOpenCallback openCallback = (CameraOpenCallback) msg.obj; final int cameraId = msg.arg1; - if (mCameraState.getState() != CameraStateHolder.CAMERA_UNOPENED) { + if (mCameraState.getState() != AndroidCameraStateHolder.CAMERA_UNOPENED) { openCallback.onDeviceOpenedAlready(cameraId, generateHistoryString(cameraId)); break; } @@ -305,7 +315,7 @@ class AndroidCameraAgentImpl implements CameraAgent { AndroidCameraDeviceInfo.create().getCharacteristics(cameraId); mCapabilities = new AndroidCameraCapabilities(mParamsToSet); - mCameraState.setState(CameraStateHolder.CAMERA_IDLE); + mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE); if (openCallback != null) { openCallback.onCameraOpened( new AndroidCameraProxyImpl(cameraId, mCamera, @@ -322,7 +332,7 @@ class AndroidCameraAgentImpl implements CameraAgent { case CameraActions.RELEASE: { if (mCamera != null) { mCamera.release(); - mCameraState.setState(CameraStateHolder.CAMERA_UNOPENED); + mCameraState.setState(AndroidCameraStateHolder.CAMERA_UNOPENED); mCamera = null; } else { Log.w(TAG, "Releasing camera without any camera opened."); @@ -344,7 +354,7 @@ class AndroidCameraAgentImpl implements CameraAgent { break; } - mCameraState.setState(CameraStateHolder.CAMERA_IDLE); + mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE); if (cbForward != null) { cbForward.onCameraOpened( new AndroidCameraProxyImpl(cameraId, mCamera, mCharacteristics, @@ -355,13 +365,13 @@ class AndroidCameraAgentImpl implements CameraAgent { case CameraActions.UNLOCK: { mCamera.unlock(); - mCameraState.setState(CameraStateHolder.CAMERA_UNLOCKED); + mCameraState.setState(AndroidCameraStateHolder.CAMERA_UNLOCKED); break; } case CameraActions.LOCK: { mCamera.lock(); - mCameraState.setState(CameraStateHolder.CAMERA_IDLE); + mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE); break; } @@ -410,14 +420,14 @@ class AndroidCameraAgentImpl implements CameraAgent { } case CameraActions.AUTO_FOCUS: { - mCameraState.setState(CameraStateHolder.CAMERA_FOCUSING); + mCameraState.setState(AndroidCameraStateHolder.CAMERA_FOCUSING); mCamera.autoFocus((AutoFocusCallback) msg.obj); break; } case CameraActions.CANCEL_AUTO_FOCUS: { mCamera.cancelAutoFocus(); - mCameraState.setState(CameraStateHolder.CAMERA_IDLE); + mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE); break; } @@ -495,7 +505,7 @@ class AndroidCameraAgentImpl implements CameraAgent { } case CameraActions.CAPTURE_PHOTO: { - mCameraState.setState(CameraStateHolder.CAMERA_CAPTURING); + mCameraState.setState(AndroidCameraStateHolder.CAMERA_CAPTURING); capture((CaptureCallbacks) msg.obj); break; } @@ -508,7 +518,7 @@ class AndroidCameraAgentImpl implements CameraAgent { if (msg.what != CameraActions.RELEASE && mCamera != null) { try { mCamera.release(); - mCameraState.setState(CameraStateHolder.CAMERA_UNOPENED); + mCameraState.setState(AndroidCameraStateHolder.CAMERA_UNOPENED); } catch (Exception ex) { Log.e(TAG, "Fail to release the camera."); } @@ -608,46 +618,11 @@ class AndroidCameraAgentImpl implements CameraAgent { } } - @Override - public void openCamera(final Handler handler, final int cameraId, - final CameraOpenCallback callback) { - mDispatchThread.runJob(new Runnable() { - @Override - public void run() { - mCameraHandler.obtainMessage(CameraActions.OPEN_CAMERA, cameraId, 0, - CameraOpenCallbackForward.getNewInstance(handler, callback)).sendToTarget(); - } - }); - } - - @Override - public void closeCamera(CameraProxy camera, boolean synced) { - if (synced) { - final WaitDoneBundle bundle = new WaitDoneBundle(); - - mDispatchThread.runJobSync(new Runnable() { - @Override - public void run() { - mCameraHandler.obtainMessage(CameraActions.RELEASE).sendToTarget(); - mCameraHandler.post(bundle.mUnlockRunnable); - } - }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera release"); - } else { - mDispatchThread.runJob(new Runnable() { - @Override - public void run() { - mCameraHandler.removeCallbacksAndMessages(null); - mCameraHandler.obtainMessage(CameraActions.RELEASE).sendToTarget(); - } - }); - } - } - /** * A class which implements {@link CameraAgent.CameraProxy} and * camera handler thread. */ - private class AndroidCameraProxyImpl implements CameraAgent.CameraProxy { + private class AndroidCameraProxyImpl extends CameraAgent.CameraProxy { private final int mCameraId; /* TODO: remove this Camera instance. */ private final Camera mCamera; @@ -684,112 +659,6 @@ class AndroidCameraAgentImpl implements CameraAgent { } @Override - public void reconnect(final Handler handler, final CameraOpenCallback cb) { - mDispatchThread.runJob(new Runnable() { - @Override - public void run() { - mCameraHandler.obtainMessage(CameraActions.RECONNECT, mCameraId, 0, - CameraOpenCallbackForward.getNewInstance(handler, cb)).sendToTarget(); - } - }); - } - - @Override - public void unlock() { - final WaitDoneBundle bundle = new WaitDoneBundle(); - mDispatchThread.runJobSync(new Runnable() { - @Override - public void run() { - mCameraHandler.sendEmptyMessage(CameraActions.UNLOCK); - mCameraHandler.post(bundle.mUnlockRunnable); - } - }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera unlock"); - } - - @Override - public void lock() { - mDispatchThread.runJob(new Runnable() { - @Override - public void run() { - mCameraHandler.sendEmptyMessage(CameraActions.LOCK); - } - }); - } - - @Override - public void setPreviewTexture(final SurfaceTexture surfaceTexture) { - mDispatchThread.runJob(new Runnable() { - @Override - public void run() { - mCameraHandler - .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture) - .sendToTarget(); - } - }); - } - - @Override - public void setPreviewTextureSync(final SurfaceTexture surfaceTexture) { - final WaitDoneBundle bundle = new WaitDoneBundle(); - mDispatchThread.runJobSync(new Runnable() { - @Override - public void run() { - mCameraHandler - .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture) - .sendToTarget(); - mCameraHandler.post(bundle.mUnlockRunnable); - } - }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "set preview texture"); - } - - @Override - public void setPreviewDisplay(final SurfaceHolder surfaceHolder) { - mDispatchThread.runJob(new Runnable() { - @Override - public void run() { - mCameraHandler - .obtainMessage(CameraActions.SET_PREVIEW_DISPLAY_ASYNC, surfaceHolder) - .sendToTarget(); - } - }); - } - - @Override - public void startPreview() { - mDispatchThread.runJob(new Runnable() { - @Override - public void run() { - mCameraHandler - .obtainMessage(CameraActions.START_PREVIEW_ASYNC, null).sendToTarget(); - } - }); - } - - @Override - public void startPreviewWithCallback(final Handler handler, - final CameraStartPreviewCallback cb) { - mDispatchThread.runJob(new Runnable() { - @Override - public void run() { - mCameraHandler.obtainMessage(CameraActions.START_PREVIEW_ASYNC, - CameraStartPreviewCallbackForward.getNewInstance(handler, cb)).sendToTarget(); - } - }); - } - - @Override - public void stopPreview() { - final WaitDoneBundle bundle = new WaitDoneBundle(); - mDispatchThread.runJobSync(new Runnable() { - @Override - public void run() { - mCameraHandler.sendEmptyMessage(CameraActions.STOP_PREVIEW); - mCameraHandler.post(bundle.mUnlockRunnable); - } - }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "stop preview"); - } - - @Override public void setPreviewDataCallback( final Handler handler, final CameraPreviewDataCallback cb) { mDispatchThread.runJob(new Runnable() { @@ -802,6 +671,7 @@ class AndroidCameraAgentImpl implements CameraAgent { } }); } + @Override public void setOneShotPreviewCallback(final Handler handler, final CameraPreviewDataCallback cb) { @@ -831,25 +701,14 @@ class AndroidCameraAgentImpl implements CameraAgent { } @Override - public void addCallbackBuffer(final byte[] callbackBuffer) { - mDispatchThread.runJob(new Runnable() { - @Override - public void run() { - mCameraHandler.obtainMessage(CameraActions.ADD_CALLBACK_BUFFER, callbackBuffer) - .sendToTarget(); - } - }); - } - - @Override public void autoFocus(final Handler handler, final CameraAFCallback cb) { final AutoFocusCallback afCallback = new AutoFocusCallback() { @Override public void onAutoFocus(final boolean b, Camera camera) { - if (mCameraState.getState() != CameraStateHolder.CAMERA_FOCUSING) { + if (mCameraState.getState() != AndroidCameraStateHolder.CAMERA_FOCUSING) { Log.w(TAG, "onAutoFocus callback returning when not focusing"); } else { - mCameraState.setState(CameraStateHolder.CAMERA_IDLE); + mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE); } handler.post(new Runnable() { @Override @@ -862,24 +721,13 @@ class AndroidCameraAgentImpl implements CameraAgent { mDispatchThread.runJob(new Runnable() { @Override public void run() { - mCameraState.waitForStates(CameraStateHolder.CAMERA_IDLE); + mCameraState.waitForStates(AndroidCameraStateHolder.CAMERA_IDLE); mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, afCallback) .sendToTarget(); } }); } - @Override - public void cancelAutoFocus() { - mDispatchThread.runJob(new Runnable() { - @Override - public void run() { - mCameraHandler.removeMessages(CameraActions.AUTO_FOCUS); - mCameraHandler.sendEmptyMessage(CameraActions.CANCEL_AUTO_FOCUS); - } - }); - } - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) @Override public void setAutoFocusMoveCallback( @@ -903,10 +751,10 @@ class AndroidCameraAgentImpl implements CameraAgent { final PictureCallback jpegCallback = new PictureCallback() { @Override public void onPictureTaken(final byte[] data, Camera camera) { - if (mCameraState.getState() != CameraStateHolder.CAMERA_CAPTURING) { + if (mCameraState.getState() != AndroidCameraStateHolder.CAMERA_CAPTURING) { Log.w(TAG, "picture callback returning when not capturing"); } else { - mCameraState.setState(CameraStateHolder.CAMERA_IDLE); + mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE); } handler.post(new Runnable() { @Override @@ -920,8 +768,8 @@ class AndroidCameraAgentImpl implements CameraAgent { mDispatchThread.runJob(new Runnable() { @Override public void run() { - mCameraState.waitForStates( - CameraStateHolder.CAMERA_IDLE | CameraStateHolder.CAMERA_UNLOCKED); + mCameraState.waitForStates(CameraStateHolder.CAMERA_IDLE | + AndroidCameraStateHolder.CAMERA_UNLOCKED); mCameraHandler.requestTakePicture(ShutterCallbackForward .getNewInstance(handler, AndroidCameraProxyImpl.this, shutter), PictureCallbackForward @@ -935,17 +783,6 @@ class AndroidCameraAgentImpl implements CameraAgent { } @Override - public void setDisplayOrientation(final int degrees) { - mDispatchThread.runJob(new Runnable() { - @Override - public void run() { - mCameraHandler.obtainMessage(CameraActions.SET_DISPLAY_ORIENTATION, degrees, 0) - .sendToTarget(); - } - }); - } - - @Override public void setZoomChangeListener(final OnZoomChangeListener listener) { mDispatchThread.runJob(new Runnable() { @Override @@ -971,26 +808,6 @@ class AndroidCameraAgentImpl implements CameraAgent { } @Override - public void startFaceDetection() { - mDispatchThread.runJob(new Runnable() { - @Override - public void run() { - mCameraHandler.sendEmptyMessage(CameraActions.START_FACE_DETECTION); - } - }); - } - - @Override - public void stopFaceDetection() { - mDispatchThread.runJob(new Runnable() { - @Override - public void run() { - mCameraHandler.sendEmptyMessage(CameraActions.STOP_FACE_DETECTION); - } - }); - } - - @Override public void setErrorCallback(final Handler handler, final CameraErrorCallback cb) { mDispatchThread.runJob(new Runnable() { @Override @@ -1013,8 +830,8 @@ class AndroidCameraAgentImpl implements CameraAgent { mDispatchThread.runJob(new Runnable() { @Override public void run() { - mCameraState.waitForStates( - CameraStateHolder.CAMERA_IDLE | CameraStateHolder.CAMERA_UNLOCKED); + mCameraState.waitForStates(AndroidCameraStateHolder.CAMERA_IDLE | + AndroidCameraStateHolder.CAMERA_UNLOCKED); mCameraHandler.obtainMessage(CameraActions.SET_PARAMETERS, flattenedParameters) .sendToTarget(); } @@ -1040,48 +857,9 @@ class AndroidCameraAgentImpl implements CameraAgent { } @Override - public boolean applySettings(final CameraSettings settings) { - if (settings == null) { - Log.v(TAG, "null parameters in applySettings()"); - return false; - } - if (!mCapabilities.supports(settings)) { - return false; - } - - final CameraSettings copyOfSettings = new CameraSettings(settings); - mDispatchThread.runJob(new Runnable() { - @Override - public void run() { - mCameraState.waitForStates( - CameraStateHolder.CAMERA_IDLE | CameraStateHolder.CAMERA_UNLOCKED); - mCameraHandler.obtainMessage(CameraActions.APPLY_SETTINGS, copyOfSettings) - .sendToTarget(); - } - }); - return true; - } - - @Override - public void refreshSettings() { - mDispatchThread.runJob(new Runnable() { - @Override - public void run() { - mCameraHandler.sendEmptyMessage(CameraActions.REFRESH_PARAMETERS); - } - }); - } - - @Override - public void enableShutterSound(final boolean enable) { - mDispatchThread.runJob(new Runnable() { - @Override - public void run() { - mCameraHandler - .obtainMessage(CameraActions.ENABLE_SHUTTER_SOUND, (enable ? 1 : 0), 0) - .sendToTarget(); - } - }); + public boolean applySettings(CameraSettings settings) { + return applySettingsHelper(settings, AndroidCameraStateHolder.CAMERA_IDLE | + AndroidCameraStateHolder.CAMERA_UNLOCKED); } @Override @@ -1095,22 +873,52 @@ class AndroidCameraAgentImpl implements CameraAgent { return dumpedSettings; } + + @Override + public Handler getCameraHandler() { + return AndroidCameraAgentImpl.this.getCameraHandler(); + } + + @Override + public DispatchThread getDispatchThread() { + return AndroidCameraAgentImpl.this.getDispatchThread(); + } + + @Override + public CameraStateHolder getCameraState() { + return mCameraState; + } } - private static class WaitDoneBundle { - public Runnable mUnlockRunnable; - private final Object mWaitLock; + private static class AndroidCameraStateHolder extends CameraStateHolder { + /* Camera states */ + // These states are defined bitwise so we can easily to specify a set of + // states together. + public static final int CAMERA_UNOPENED = 1; + public static final int CAMERA_IDLE = 1 << 1; + public static final int CAMERA_UNLOCKED = 1 << 2; + public static final int CAMERA_CAPTURING = 1 << 3; + public static final int CAMERA_FOCUSING = 1 << 4; - WaitDoneBundle() { - mWaitLock = new Object(); - mUnlockRunnable = new Runnable() { - @Override - public void run() { - synchronized (mWaitLock) { - mWaitLock.notifyAll(); - } - } - }; + private int mState; + + public AndroidCameraStateHolder() { + this(CAMERA_UNOPENED); + } + + public AndroidCameraStateHolder(int state) { + setState(state); + } + + @Override + public synchronized void setState(int state) { + mState = state; + this.notifyAll(); + } + + @Override + public synchronized int getState() { + return mState; } } diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraCapabilities.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraCapabilities.java index 28d1fd1..14fcaaa 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraCapabilities.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraCapabilities.java @@ -29,13 +29,13 @@ import java.util.List; */ class AndroidCameraCapabilities extends CameraCapabilities { - private static Log.Tag TAG = new Log.Tag("AndroidCameraCapabilities"); + private static Log.Tag TAG = new Log.Tag("AndCamCapabs"); private FpsComparator mFpsComparator = new FpsComparator(); private SizeComparator mSizeComparator = new SizeComparator(); AndroidCameraCapabilities(Camera.Parameters p) { - super(new AndroidCameraCapabilityStringifier()); + super(new Stringifier()); mMaxExposureCompensation = p.getMaxExposureCompensation(); mMinExposureCompensation = p.getMinExposureCompensation(); mExposureCompensationStep = p.getExposureCompensationStep(); @@ -250,241 +250,4 @@ class AndroidCameraCapabilities extends CameraCapabilities { size1.width() - size2.width()); } } - - private static class AndroidCameraCapabilityStringifier implements Stringifier { - - @Override - public String stringify(FocusMode focus) { - if (focus == null) { - return null; - } - - switch (focus) { - case AUTO: - return Camera.Parameters.FOCUS_MODE_AUTO; - case CONTINUOUS_PICTURE: - return Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE; - case CONTINUOUS_VIDEO: - return Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO; - case EXTENDED_DOF: - return Camera.Parameters.FOCUS_MODE_EDOF; - case FIXED: - return Camera.Parameters.FOCUS_MODE_FIXED; - case INFINITY: - return Camera.Parameters.FOCUS_MODE_INFINITY; - case MACRO: - return Camera.Parameters.FOCUS_MODE_MACRO; - } - return null; - } - - @Override - public FocusMode focusModeFromString(String val) { - if (val == null) { - return null; - } - - if (Camera.Parameters.FOCUS_MODE_AUTO.equals(val)) { - return FocusMode.AUTO; - } else if (Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(val)) { - return FocusMode.CONTINUOUS_PICTURE; - } else if (Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO.equals(val)) { - return FocusMode.CONTINUOUS_VIDEO; - } else if (Camera.Parameters.FOCUS_MODE_EDOF.equals(val)) { - return FocusMode.EXTENDED_DOF; - } else if (Camera.Parameters.FOCUS_MODE_FIXED.equals(val)) { - return FocusMode.FIXED; - } else if (Camera.Parameters.FOCUS_MODE_INFINITY.equals(val)) { - return FocusMode.INFINITY; - } else if (Camera.Parameters.FOCUS_MODE_MACRO.equals(val)) { - return FocusMode.MACRO; - } else { - return null; - } - } - - @Override - public String stringify(FlashMode flash) { - if (flash == null) { - return null; - } - - switch (flash) { - case NO_FLASH: - return null; - case AUTO: - return Camera.Parameters.FLASH_MODE_AUTO; - case OFF: - return Camera.Parameters.FLASH_MODE_OFF; - case ON: - return Camera.Parameters.FLASH_MODE_ON; - case TORCH: - return Camera.Parameters.FLASH_MODE_TORCH; - case RED_EYE: - return Camera.Parameters.FLASH_MODE_RED_EYE; - } - return null; - } - - @Override - public FlashMode flashModeFromString(String val) { - if (val == null) { - return FlashMode.NO_FLASH; - } else if (Camera.Parameters.FLASH_MODE_AUTO.equals(val)) { - return FlashMode.AUTO; - } else if (Camera.Parameters.FLASH_MODE_OFF.equals(val)) { - return FlashMode.OFF; - } else if (Camera.Parameters.FLASH_MODE_ON.equals(val)) { - return FlashMode.ON; - } else if (Camera.Parameters.FLASH_MODE_TORCH.equals(val)) { - return FlashMode.TORCH; - } else if (Camera.Parameters.FLASH_MODE_RED_EYE.equals(val)) { - return FlashMode.RED_EYE; - } else { - return null; - } - } - - @Override - public String stringify(SceneMode scene) { - if (scene == null) { - return null; - } - - switch (scene) { - case AUTO: - return Camera.Parameters.SCENE_MODE_AUTO; - case ACTION: - return Camera.Parameters.SCENE_MODE_ACTION; - case BARCODE: - return Camera.Parameters.SCENE_MODE_BARCODE; - case BEACH: - return Camera.Parameters.SCENE_MODE_BEACH; - case CANDLELIGHT: - return Camera.Parameters.SCENE_MODE_CANDLELIGHT; - case FIREWORKS: - return Camera.Parameters.SCENE_MODE_FIREWORKS; - case HDR: - return Camera.Parameters.SCENE_MODE_HDR; - case LANDSCAPE: - return Camera.Parameters.SCENE_MODE_LANDSCAPE; - case NIGHT: - return Camera.Parameters.SCENE_MODE_NIGHT; - case NIGHT_PORTRAIT: - return Camera.Parameters.SCENE_MODE_NIGHT_PORTRAIT; - case PARTY: - return Camera.Parameters.SCENE_MODE_PARTY; - case PORTRAIT: - return Camera.Parameters.SCENE_MODE_PORTRAIT; - case SNOW: - return Camera.Parameters.SCENE_MODE_SNOW; - case SPORTS: - return Camera.Parameters.SCENE_MODE_SPORTS; - case STEADYPHOTO: - return Camera.Parameters.SCENE_MODE_STEADYPHOTO; - case SUNSET: - return Camera.Parameters.SCENE_MODE_SUNSET; - case THEATRE: - return Camera.Parameters.SCENE_MODE_THEATRE; - } - return null; - } - - @Override - public SceneMode sceneModeFromString(String val) { - if (val == null) { - return SceneMode.NO_SCENE_MODE; - } else if (Camera.Parameters.SCENE_MODE_AUTO.equals(val)) { - return SceneMode.AUTO; - } else if (Camera.Parameters.SCENE_MODE_ACTION.equals(val)) { - return SceneMode.ACTION; - } else if (Camera.Parameters.SCENE_MODE_BARCODE.equals(val)) { - return SceneMode.BARCODE; - } else if (Camera.Parameters.SCENE_MODE_BEACH.equals(val)) { - return SceneMode.BEACH; - } else if (Camera.Parameters.SCENE_MODE_CANDLELIGHT.equals(val)) { - return SceneMode.CANDLELIGHT; - } else if (Camera.Parameters.SCENE_MODE_FIREWORKS.equals(val)) { - return SceneMode.FIREWORKS; - } else if (Camera.Parameters.SCENE_MODE_HDR.equals(val)) { - return SceneMode.HDR; - } else if (Camera.Parameters.SCENE_MODE_LANDSCAPE.equals(val)) { - return SceneMode.LANDSCAPE; - } else if (Camera.Parameters.SCENE_MODE_NIGHT.equals(val)) { - return SceneMode.NIGHT; - } else if (Camera.Parameters.SCENE_MODE_NIGHT_PORTRAIT.equals(val)) { - return SceneMode.NIGHT_PORTRAIT; - } else if (Camera.Parameters.SCENE_MODE_PARTY.equals(val)) { - return SceneMode.PARTY; - } else if (Camera.Parameters.SCENE_MODE_PORTRAIT.equals(val)) { - return SceneMode.PORTRAIT; - } else if (Camera.Parameters.SCENE_MODE_SNOW.equals(val)) { - return SceneMode.SNOW; - } else if (Camera.Parameters.SCENE_MODE_SPORTS.equals(val)) { - return SceneMode.SPORTS; - } else if (Camera.Parameters.SCENE_MODE_STEADYPHOTO.equals(val)) { - return SceneMode.STEADYPHOTO; - } else if (Camera.Parameters.SCENE_MODE_SUNSET.equals(val)) { - return SceneMode.SUNSET; - } else if (Camera.Parameters.SCENE_MODE_THEATRE.equals(val)) { - return SceneMode.THEATRE; - } else { - return null; - } - } - - @Override - public String stringify(WhiteBalance wb) { - if (wb == null) { - return null; - } - - switch (wb) { - case AUTO: - return Camera.Parameters.WHITE_BALANCE_AUTO; - case CLOUDY_DAYLIGHT: - return Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT; - case DAYLIGHT: - return Camera.Parameters.WHITE_BALANCE_DAYLIGHT; - case FLUORESCENT: - return Camera.Parameters.WHITE_BALANCE_FLUORESCENT; - case INCANDESCENT: - return Camera.Parameters.WHITE_BALANCE_INCANDESCENT; - case SHADE: - return Camera.Parameters.WHITE_BALANCE_SHADE; - case TWILIGHT: - return Camera.Parameters.WHITE_BALANCE_TWILIGHT; - case WARM_FLUORESCENT: - return Camera.Parameters.WHITE_BALANCE_WARM_FLUORESCENT; - } - return null; - } - - @Override - public WhiteBalance whiteBalanceFromString(String val) { - if (val == null) { - return null; - } - - if (Camera.Parameters.WHITE_BALANCE_AUTO.equals(val)) { - return WhiteBalance.AUTO; - } else if (Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT.equals(val)) { - return WhiteBalance.CLOUDY_DAYLIGHT; - } else if (Camera.Parameters.WHITE_BALANCE_DAYLIGHT.equals(val)) { - return WhiteBalance.DAYLIGHT; - } else if (Camera.Parameters.WHITE_BALANCE_FLUORESCENT.equals(val)) { - return WhiteBalance.FLUORESCENT; - } else if (Camera.Parameters.WHITE_BALANCE_INCANDESCENT.equals(val)) { - return WhiteBalance.INCANDESCENT; - } else if (Camera.Parameters.WHITE_BALANCE_SHADE.equals(val)) { - return WhiteBalance.SHADE; - } else if (Camera.Parameters.WHITE_BALANCE_TWILIGHT.equals(val)) { - return WhiteBalance.TWILIGHT; - } else if (Camera.Parameters.WHITE_BALANCE_WARM_FLUORESCENT.equals(val)) { - return WhiteBalance.WARM_FLUORESCENT; - } else { - return null; - } - } - } } diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java index 9c3c400..d875cea 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java @@ -25,6 +25,8 @@ import android.os.Handler; import android.os.Looper; import android.view.SurfaceHolder; +import com.android.ex.camera2.portability.debug.Log; + /** * An interface which provides possible camera device operations. * @@ -39,9 +41,11 @@ import android.view.SurfaceHolder; * {@code android.hardware.Camera.ErrorCallback}, * {@code android.hardware.Camera.OnZoomChangeListener}, and */ -public interface CameraAgent { +public abstract class CameraAgent { public static final long CAMERA_OPERATION_TIMEOUT_MS = 2500; + private static final Log.Tag TAG = new Log.Tag("CamAgnt"); + public static class CameraStartPreviewCallbackForward implements CameraStartPreviewCallback { private final Handler mHandler; @@ -67,8 +71,7 @@ public interface CameraAgent { @Override public void run() { mCallback.onPreviewStarted(); - } - }); + }}); } } @@ -110,8 +113,7 @@ public interface CameraAgent { @Override public void run() { mCallback.onCameraOpened(camera); - } - }); + }}); } @Override @@ -120,8 +122,7 @@ public interface CameraAgent { @Override public void run() { mCallback.onCameraDisabled(cameraId); - } - }); + }}); } @Override @@ -130,8 +131,7 @@ public interface CameraAgent { @Override public void run() { mCallback.onDeviceOpenFailure(cameraId, info); - } - }); + }}); } @Override @@ -140,8 +140,7 @@ public interface CameraAgent { @Override public void run() { mCallback.onDeviceOpenedAlready(cameraId, info); - } - }); + }}); } @Override @@ -150,8 +149,7 @@ public interface CameraAgent { @Override public void run() { mCallback.onReconnectionFailure(mgr, info); - } - }); + }}); } } @@ -159,7 +157,7 @@ public interface CameraAgent { * A handler for all camera api runtime exceptions. * The default behavior is to throw the runtime exception. */ - public interface CameraExceptionCallback { + public static interface CameraExceptionCallback { public void onCameraException(RuntimeException e); } @@ -167,7 +165,7 @@ public interface CameraAgent { * An interface which wraps * {@link android.hardware.Camera.ErrorCallback} */ - public interface CameraErrorCallback { + public static interface CameraErrorCallback { public void onError(int error, CameraProxy camera); } @@ -175,7 +173,7 @@ public interface CameraAgent { * An interface which wraps * {@link android.hardware.Camera.AutoFocusCallback}. */ - public interface CameraAFCallback { + public static interface CameraAFCallback { public void onAutoFocus(boolean focused, CameraProxy camera); } @@ -183,7 +181,7 @@ public interface CameraAgent { * An interface which wraps * {@link android.hardware.Camera.AutoFocusMoveCallback}. */ - public interface CameraAFMoveCallback { + public static interface CameraAFMoveCallback { public void onAutoFocusMoving(boolean moving, CameraProxy camera); } @@ -191,7 +189,7 @@ public interface CameraAgent { * An interface which wraps * {@link android.hardware.Camera.ShutterCallback}. */ - public interface CameraShutterCallback { + public static interface CameraShutterCallback { public void onShutter(CameraProxy camera); } @@ -199,7 +197,7 @@ public interface CameraAgent { * An interface which wraps * {@link android.hardware.Camera.PictureCallback}. */ - public interface CameraPictureCallback { + public static interface CameraPictureCallback { public void onPictureTaken(byte[] data, CameraProxy camera); } @@ -207,7 +205,7 @@ public interface CameraAgent { * An interface which wraps * {@link android.hardware.Camera.PreviewCallback}. */ - public interface CameraPreviewDataCallback { + public static interface CameraPreviewDataCallback { public void onPreviewFrame(byte[] data, CameraProxy camera); } @@ -215,7 +213,7 @@ public interface CameraAgent { * An interface which wraps * {@link android.hardware.Camera.FaceDetectionListener}. */ - public interface CameraFaceDetectionCallback { + public static interface CameraFaceDetectionCallback { /** * Callback for face detection. * @@ -228,7 +226,7 @@ public interface CameraAgent { /** * An interface to be called when the camera preview has started. */ - public interface CameraStartPreviewCallback { + public static interface CameraStartPreviewCallback { /** * Callback when the preview starts. */ @@ -241,7 +239,7 @@ public interface CameraAgent { * in the framework, {@link android.hardware.Camera.ErrorCallback}, which * is used after the camera is opened. */ - public interface CameraOpenCallback { + public static interface CameraOpenCallback { /** * Callback when camera open succeeds. */ @@ -292,7 +290,15 @@ public interface CameraAgent { * @param callback The callback for the result. * @param cameraId The camera ID to open. */ - public void openCamera(Handler handler, int cameraId, CameraOpenCallback callback); + public void openCamera(final Handler handler, final int cameraId, + final CameraOpenCallback callback) { + getDispatchThread().runJob(new Runnable() { + @Override + public void run() { + getCameraHandler().obtainMessage(CameraActions.OPEN_CAMERA, cameraId, 0, + CameraOpenCallbackForward.getNewInstance(handler, callback)).sendToTarget(); + }}); + } /** * Closes the camera device. @@ -300,32 +306,60 @@ public interface CameraAgent { * @param camera The camera to close. {@code null} means all. * @param synced Whether this call should be synchronous. */ - public void closeCamera(CameraProxy camera, boolean synced); + public void closeCamera(CameraProxy camera, boolean synced) { + if (synced) { + final WaitDoneBundle bundle = new WaitDoneBundle(); + + getDispatchThread().runJobSync(new Runnable() { + @Override + public void run() { + getCameraHandler().obtainMessage(CameraActions.RELEASE).sendToTarget(); + getCameraHandler().post(bundle.mUnlockRunnable); + }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera release"); + } else { + getDispatchThread().runJob(new Runnable() { + @Override + public void run() { + getCameraHandler().removeCallbacksAndMessages(null); + getCameraHandler().obtainMessage(CameraActions.RELEASE).sendToTarget(); + }}); + } + } /** * Sets a callback for handling camera api runtime exceptions on * a handler. */ - public void setCameraDefaultExceptionCallback(CameraExceptionCallback callback, + public abstract void setCameraDefaultExceptionCallback(CameraExceptionCallback callback, Handler handler); /** * Recycles the resources used by this instance. CameraAgent will be in * an unusable state after calling this. */ - public void recycle(); + public abstract void recycle(); /** * @return The camera devices info. */ - public CameraDeviceInfo getCameraDeviceInfo(); + public abstract CameraDeviceInfo getCameraDeviceInfo(); + + /** + * @return The handler to which camera tasks should be posted. + */ + protected abstract Handler getCameraHandler(); + + /** + * @return The thread used on which client callbacks are served. + */ + protected abstract DispatchThread getDispatchThread(); /** * An interface that takes camera operation requests and post messages to the * camera handler thread. All camera operations made through this interface is * asynchronous by default except those mentioned specifically. */ - public interface CameraProxy { + public static abstract class CameraProxy { /** * Returns the underlying {@link android.hardware.Camera} object used @@ -334,23 +368,23 @@ public interface CameraAgent { * recording. */ @Deprecated - public android.hardware.Camera getCamera(); + public abstract android.hardware.Camera getCamera(); /** * @return The camera ID associated to by this * {@link CameraAgent.CameraProxy}. */ - public int getCameraId(); + public abstract int getCameraId(); /** * @return The camera characteristics. */ - public CameraDeviceInfo.Characteristics getCharacteristics(); + public abstract CameraDeviceInfo.Characteristics getCharacteristics(); /** * @return The camera capabilities. */ - public CameraCapabilities getCapabilities(); + public abstract CameraCapabilities getCapabilities(); /** * Reconnects to the camera device. On success, the camera device will @@ -363,27 +397,56 @@ public interface CameraAgent { * was handled. * @param cb The callback when any error happens. */ - public void reconnect(Handler handler, CameraOpenCallback cb); + public void reconnect(final Handler handler, final CameraOpenCallback cb) { + getDispatchThread().runJob(new Runnable() { + @Override + public void run() { + getCameraHandler().obtainMessage(CameraActions.RECONNECT, getCameraId(), 0, + CameraOpenCallbackForward.getNewInstance(handler, cb)).sendToTarget(); + }}); + } /** * Unlocks the camera device. * * @see android.hardware.Camera#unlock() */ - public void unlock(); + public void unlock() { + final WaitDoneBundle bundle = new WaitDoneBundle(); + getDispatchThread().runJobSync(new Runnable() { + @Override + public void run() { + getCameraHandler().sendEmptyMessage(CameraActions.UNLOCK); + getCameraHandler().post(bundle.mUnlockRunnable); + }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera unlock"); + } /** * Locks the camera device. * @see android.hardware.Camera#lock() */ - public void lock(); + public void lock() { + getDispatchThread().runJob(new Runnable() { + @Override + public void run() { + getCameraHandler().sendEmptyMessage(CameraActions.LOCK); + }}); + } /** * Sets the {@link android.graphics.SurfaceTexture} for preview. * * @param surfaceTexture The {@link SurfaceTexture} for preview. */ - public void setPreviewTexture(final SurfaceTexture surfaceTexture); + public void setPreviewTexture(final SurfaceTexture surfaceTexture) { + getDispatchThread().runJob(new Runnable() { + @Override + public void run() { + getCameraHandler() + .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture) + .sendToTarget(); + }}); + } /** * Blocks until a {@link android.graphics.SurfaceTexture} has been set @@ -391,32 +454,73 @@ public interface CameraAgent { * * @param surfaceTexture The {@link SurfaceTexture} for preview. */ - public void setPreviewTextureSync(final SurfaceTexture surfaceTexture); + public void setPreviewTextureSync(final SurfaceTexture surfaceTexture) { + final WaitDoneBundle bundle = new WaitDoneBundle(); + getDispatchThread().runJobSync(new Runnable() { + @Override + public void run() { + getCameraHandler() + .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture) + .sendToTarget(); + getCameraHandler().post(bundle.mUnlockRunnable); + }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "set preview texture"); + } /** * Sets the {@link android.view.SurfaceHolder} for preview. * * @param surfaceHolder The {@link SurfaceHolder} for preview. */ - public void setPreviewDisplay(final SurfaceHolder surfaceHolder); + public void setPreviewDisplay(final SurfaceHolder surfaceHolder) { + getDispatchThread().runJob(new Runnable() { + @Override + public void run() { + getCameraHandler() + .obtainMessage(CameraActions.SET_PREVIEW_DISPLAY_ASYNC, surfaceHolder) + .sendToTarget(); + }}); + } /** * Starts the camera preview. */ - public void startPreview(); + public void startPreview() { + getDispatchThread().runJob(new Runnable() { + @Override + public void run() { + getCameraHandler() + .obtainMessage(CameraActions.START_PREVIEW_ASYNC, null).sendToTarget(); + }}); + } /** * Starts the camera preview and executes a callback on a handler once * the preview starts. */ - public void startPreviewWithCallback(Handler h, CameraStartPreviewCallback cb); + public void startPreviewWithCallback(final Handler h, final CameraStartPreviewCallback cb) { + getDispatchThread().runJob(new Runnable() { + @Override + public void run() { + getCameraHandler().obtainMessage(CameraActions.START_PREVIEW_ASYNC, + CameraStartPreviewCallbackForward.getNewInstance(h, cb)) + .sendToTarget(); + }}); + } /** * Stops the camera preview synchronously. * {@code stopPreview()} must be synchronous to ensure that the caller can * continues to release resources related to camera preview. */ - public void stopPreview(); + public void stopPreview() { + final WaitDoneBundle bundle = new WaitDoneBundle(); + getDispatchThread().runJobSync(new Runnable() { + @Override + public void run() { + getCameraHandler().sendEmptyMessage(CameraActions.STOP_PREVIEW); + getCameraHandler().post(bundle.mUnlockRunnable); + }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "stop preview"); + } /** * Sets the callback for preview data. @@ -425,7 +529,7 @@ public interface CameraAgent { * @param cb The callback to be invoked when the preview data is available. * @see android.hardware.Camera#setPreviewCallback(android.hardware.Camera.PreviewCallback) */ - public void setPreviewDataCallback(Handler handler, CameraPreviewDataCallback cb); + public abstract void setPreviewDataCallback(Handler handler, CameraPreviewDataCallback cb); /** * Sets the one-time callback for preview data. @@ -435,7 +539,8 @@ public interface CameraAgent { * next frame is available. * @see android.hardware.Camera#setPreviewCallback(android.hardware.Camera.PreviewCallback) */ - public void setOneShotPreviewCallback(Handler handler, CameraPreviewDataCallback cb); + public abstract void setOneShotPreviewCallback(Handler handler, + CameraPreviewDataCallback cb); /** * Sets the callback for preview data. @@ -444,14 +549,24 @@ public interface CameraAgent { * @param cb The callback to be invoked when the preview data is available. * @see android.hardware.Camera#setPreviewCallbackWithBuffer(android.hardware.Camera.PreviewCallback) */ - public void setPreviewDataCallbackWithBuffer(Handler handler, CameraPreviewDataCallback cb); + public abstract void setPreviewDataCallbackWithBuffer(Handler handler, + CameraPreviewDataCallback cb); /** * Adds buffer for the preview callback. * * @param callbackBuffer The buffer allocated for the preview data. */ - public void addCallbackBuffer(byte[] callbackBuffer); + public void addCallbackBuffer(final byte[] callbackBuffer) { + getDispatchThread().runJob(new Runnable() { + @Override + public void run() { + getCameraHandler() + .obtainMessage(CameraActions.ADD_CALLBACK_BUFFER, callbackBuffer) + .sendToTarget(); + } + }); + } /** * Starts the auto-focus process. The result will be returned through the callback. @@ -459,12 +574,19 @@ public interface CameraAgent { * @param handler The handler in which the callback will be invoked. * @param cb The auto-focus callback. */ - public void autoFocus(Handler handler, CameraAFCallback cb); + public abstract void autoFocus(Handler handler, CameraAFCallback cb); /** * Cancels the auto-focus process. */ - public void cancelAutoFocus(); + public void cancelAutoFocus() { + getDispatchThread().runJob(new Runnable() { + @Override + public void run() { + getCameraHandler().removeMessages(CameraActions.AUTO_FOCUS); + getCameraHandler().sendEmptyMessage(CameraActions.CANCEL_AUTO_FOCUS); + }}); + } /** * Sets the auto-focus callback @@ -473,7 +595,7 @@ public interface CameraAgent { * @param cb The callback to be invoked when the preview data is available. */ @TargetApi(Build.VERSION_CODES.JELLY_BEAN) - public void setAutoFocusMoveCallback(Handler handler, CameraAFMoveCallback cb); + public abstract void setAutoFocusMoveCallback(Handler handler, CameraAFMoveCallback cb); /** * Instrument the camera to take a picture. @@ -488,7 +610,7 @@ public interface CameraAgent { * android.hardware.Camera.PictureCallback, * android.hardware.Camera.PictureCallback) */ - public void takePicture( + public abstract void takePicture( Handler handler, CameraShutterCallback shutter, CameraPictureCallback raw, @@ -500,14 +622,22 @@ public interface CameraAgent { * * @param degrees The rotation in degrees. Should be 0, 90, 180 or 270. */ - public void setDisplayOrientation(int degrees); + public void setDisplayOrientation(final int degrees) { + getDispatchThread().runJob(new Runnable() { + @Override + public void run() { + getCameraHandler() + .obtainMessage(CameraActions.SET_DISPLAY_ORIENTATION, degrees, 0) + .sendToTarget(); + }}); + } /** * Sets the listener for zoom change. * * @param listener The listener. */ - public void setZoomChangeListener(OnZoomChangeListener listener); + public abstract void setZoomChangeListener(OnZoomChangeListener listener); /** * Sets the face detection listener. @@ -515,17 +645,30 @@ public interface CameraAgent { * @param handler The handler in which the callback will be invoked. * @param callback The callback for face detection results. */ - public void setFaceDetectionCallback(Handler handler, CameraFaceDetectionCallback callback); + public abstract void setFaceDetectionCallback(Handler handler, + CameraFaceDetectionCallback callback); /** * Starts the face detection. */ - public void startFaceDetection(); + public void startFaceDetection() { + getDispatchThread().runJob(new Runnable() { + @Override + public void run() { + getCameraHandler().sendEmptyMessage(CameraActions.START_FACE_DETECTION); + }}); + } /** * Stops the face detection. */ - public void stopFaceDetection(); + public void stopFaceDetection() { + getDispatchThread().runJob(new Runnable() { + @Override + public void run() { + getCameraHandler().sendEmptyMessage(CameraActions.STOP_FACE_DETECTION); + }}); + } /** * Registers an error callback. @@ -534,7 +677,7 @@ public interface CameraAgent { * @param cb The error callback. * @see android.hardware.Camera#setErrorCallback(android.hardware.Camera.ErrorCallback) */ - public void setErrorCallback(Handler handler, CameraErrorCallback cb); + public abstract void setErrorCallback(Handler handler, CameraErrorCallback cb); /** * Sets the camera parameters. @@ -542,7 +685,7 @@ public interface CameraAgent { * @param params The camera parameters to use. */ @Deprecated - public void setParameters(Camera.Parameters params); + public abstract void setParameters(Camera.Parameters params); /** * Gets the current camera parameters synchronously. This method is @@ -551,7 +694,7 @@ public interface CameraAgent { * immediately. */ @Deprecated - public Camera.Parameters getParameters(); + public abstract Camera.Parameters getParameters(); /** * Gets the current camera settings synchronously. @@ -559,7 +702,38 @@ public interface CameraAgent { * camera to return the parameters. If the parameters are already * cached, it returns immediately.</p> */ - public CameraSettings getSettings(); + public abstract CameraSettings getSettings(); + + /** + * Default implementation of {@link #applySettings(CameraSettings)} + * that is only missing the set of states it needs to wait for + * before applying the settings. + * + * @param settings The settings to use on the device. + * @param statesToAwait Bitwise OR of the required camera states. + * @return Whether the settings can be applied. + */ + protected boolean applySettingsHelper(final CameraSettings settings, + final int statesToAwait) { + if (settings == null) { + Log.v(TAG, "null parameters in applySettings()"); + return false; + } + if (!getCapabilities().supports(settings)) { + return false; + } + + final CameraSettings copyOfSettings = new CameraSettings(settings); + getDispatchThread().runJob(new Runnable() { + @Override + public void run() { + getCameraState().waitForStates(statesToAwait); + getCameraHandler().obtainMessage(CameraActions.APPLY_SETTINGS, copyOfSettings) + .sendToTarget(); + } + }); + return true; + } /** * Applies the settings to the camera device. @@ -567,13 +741,19 @@ public interface CameraAgent { * @param settings The settings to use on the device. * @return Whether the settings can be applied. */ - public boolean applySettings(CameraSettings settings); + public abstract boolean applySettings(CameraSettings settings); /** * Forces {@code CameraProxy} to update the cached version of the camera * settings regardless of the dirty bit. */ - public void refreshSettings(); + public void refreshSettings() { + getDispatchThread().runJob(new Runnable() { + @Override + public void run() { + getCameraHandler().sendEmptyMessage(CameraActions.REFRESH_PARAMETERS); + }}); + } /** * Enables/Disables the camera shutter sound. @@ -581,7 +761,15 @@ public interface CameraAgent { * @param enable {@code true} to enable the shutter sound, * {@code false} to disable it. */ - public void enableShutterSound(boolean enable); + public void enableShutterSound(final boolean enable) { + getDispatchThread().runJob(new Runnable() { + @Override + public void run() { + getCameraHandler() + .obtainMessage(CameraActions.ENABLE_SHUTTER_SOUND, (enable ? 1 : 0), 0) + .sendToTarget(); + }}); + } /** * Dumps the current settings of the camera device. @@ -591,6 +779,37 @@ public interface CameraAgent { * * @return The content of the device settings represented by a string. */ - public String dumpDeviceSettings(); + public abstract String dumpDeviceSettings(); + + /** + * @return The handler to which camera tasks should be posted. + */ + public abstract Handler getCameraHandler(); + + /** + * @return The thread used on which client callbacks are served. + */ + public abstract DispatchThread getDispatchThread(); + + /** + * @return The state machine tracking the camera API's current mode. + */ + public abstract CameraStateHolder getCameraState(); + } + + public static class WaitDoneBundle { + public final Runnable mUnlockRunnable; + public final Object mWaitLock; + + WaitDoneBundle() { + mWaitLock = new Object(); + mUnlockRunnable = new Runnable() { + @Override + public void run() { + synchronized (mWaitLock) { + mWaitLock.notifyAll(); + } + }}; + } } } diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraAgentFactory.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraAgentFactory.java index ce8379d..00dc280 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/CameraAgentFactory.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraAgentFactory.java @@ -16,12 +16,14 @@ package com.android.ex.camera2.portability; +import android.content.Context; + /** * A factory class for {@link CameraAgent}. */ public class CameraAgentFactory { - private static AndroidCameraAgentImpl sAndroidCameraAgent; + private static CameraAgent sAndroidCameraAgent; private static int sAndroidCameraAgentClientCount; /** @@ -29,9 +31,13 @@ public class CameraAgentFactory { * * @return The {@link CameraAgent} to control the camera device. */ - public static synchronized CameraAgent getAndroidCameraAgent() { + public static synchronized CameraAgent getAndroidCameraAgent(Context context) { if (sAndroidCameraAgent == null) { - sAndroidCameraAgent = new AndroidCameraAgentImpl(); + if (false) { + sAndroidCameraAgent = new AndroidCamera2AgentImpl(context); + } else { + sAndroidCameraAgent = new AndroidCameraAgentImpl(); + } sAndroidCameraAgentClientCount = 1; } else { ++sAndroidCameraAgentClientCount; diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilities.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilities.java index f08301c..9b7ca6e 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilities.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilities.java @@ -34,7 +34,7 @@ import java.util.TreeSet; */ public class CameraCapabilities { - private static Log.Tag TAG = new Log.Tag("CamCapabilities"); + private static Log.Tag TAG = new Log.Tag("CamCapabs"); /* All internal states are declared final and should be thread-safe. */ @@ -311,61 +311,116 @@ public class CameraCapabilities { * A interface stringifier to convert abstract representations to API * related string representation. */ - public interface Stringifier { + public static class Stringifier { + /** + * Converts the string to hyphen-delimited lowercase for compatibility with multiple APIs. + * + * @param enumCase The name of an enum constant. + * @return The converted string. + */ + private static String toApiCase(String enumCase) { + return enumCase.toLowerCase().replaceAll("_", "-"); + } + + /** + * Conerts the string to underscore-delimited uppercase to match the enum constant names. + * + * @param apiCase An API-related string representation. + * @return The converted string. + */ + private static String toEnumCase(String apiCase) { + return apiCase.toUpperCase().replaceAll("-", "_"); + } + /** * Converts the focus mode to API-related string representation. * * @param focus The focus mode to convert. * @return The string used by the camera framework API to represent the - * focus mode. + * focus mode. */ - String stringify(FocusMode focus); + public String stringify(FocusMode focus) { + return toApiCase(focus.name()); + } /** * Converts the API-related string representation of the focus mode to the * abstract representation. * * @param val The string representation. - * @return The focus mode represented by the input string. + * @return The focus mode represented by the input string, or the focus + * mode with the lowest ordinal if it cannot be converted. */ - FocusMode focusModeFromString(String val); + public FocusMode focusModeFromString(String val) { + if (val == null) { + return FocusMode.values()[0]; + } + try { + return FocusMode.valueOf(toEnumCase(val)); + } catch (IllegalArgumentException ex) { + return FocusMode.values()[0]; + } + } /** * Converts the flash mode to API-related string representation. * * @param flash The focus mode to convert. * @return The string used by the camera framework API to represent the - * flash mode. + * flash mode. */ - String stringify(FlashMode flash); + public String stringify(FlashMode flash) { + return toApiCase(flash.name()); + } /** * Converts the API-related string representation of the flash mode to the * abstract representation. * * @param val The string representation. - * @return The flash mode represented by the input string. Can be - * {@code null}. + * @return The flash mode represented by the input string, or the flash + * mode with the lowest ordinal if it cannot be converted. */ - FlashMode flashModeFromString(String val); + public FlashMode flashModeFromString(String val) { + if (val == null) { + return FlashMode.values()[0]; + } + try { + return FlashMode.valueOf(toEnumCase(val)); + } catch (IllegalArgumentException ex) { + return FlashMode.values()[0]; + } + } /** * Converts the scene mode to API-related string representation. * * @param scene The focus mode to convert. * @return The string used by the camera framework API to represent the - * scene mode. + * scene mode. */ - String stringify(SceneMode scene); + public String stringify(SceneMode scene) { + return toApiCase(scene.name()); + } /** * Converts the API-related string representation of the scene mode to the * abstract representation. * * @param val The string representation. - * @return The scene mode represented by the input string. + * @return The scene mode represented by the input string, or the scene + * mode with the lowest ordinal if it cannot be converted. */ - SceneMode sceneModeFromString(String val); + public SceneMode sceneModeFromString(String val) { + if (val == null) { + return SceneMode.values()[0]; + } + try { + return SceneMode.valueOf(toEnumCase(val)); + } catch (IllegalArgumentException ex) { + return SceneMode.values()[0]; + } + } /** * Converts the white balance to API-related string representation. @@ -374,16 +429,29 @@ public class CameraCapabilities { * @return The string used by the camera framework API to represent the * white balance. */ - String stringify(WhiteBalance wb); + public String stringify(WhiteBalance wb) { + return toApiCase(wb.name()); + } /** * Converts the API-related string representation of the white balance to * the abstract representation. * * @param val The string representation. - * @return The white balance represented by the input string. + * @return The white balance represented by the input string, or the + * white balance with the lowest ordinal if it cannot be + * converted. */ - WhiteBalance whiteBalanceFromString(String val); + public WhiteBalance whiteBalanceFromString(String val) { + if (val == null) { + return WhiteBalance.values()[0]; + } + try { + return WhiteBalance.valueOf(toEnumCase(val)); + } catch (IllegalArgumentException ex) { + return WhiteBalance.values()[0]; + } + } } /** diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilitiesFactory.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilitiesFactory.java index 80765d3..6c2bc54 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilitiesFactory.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilitiesFactory.java @@ -22,7 +22,7 @@ import com.android.ex.camera2.portability.debug.Log; public class CameraCapabilitiesFactory { - private static Log.Tag TAG = new Log.Tag("CapabilitiesFactory"); + private static Log.Tag TAG = new Log.Tag("CamCapabsFact"); public static CameraCapabilities createFrom(Camera.Parameters p) { if (p == null) { diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraSettings.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraSettings.java index 33ceb5c..3948b2e 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/CameraSettings.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraSettings.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.ex.camera2.portability; import android.hardware.Camera; diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraStateHolder.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraStateHolder.java index 02d6c48..5df541a 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/CameraStateHolder.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraStateHolder.java @@ -20,8 +20,8 @@ import android.os.SystemClock; import com.android.ex.camera2.portability.debug.Log; -class CameraStateHolder { - private static final Log.Tag TAG = new Log.Tag("CameraStateHolder"); +public abstract class CameraStateHolder { + private static final Log.Tag TAG = new Log.Tag("CamStateHolder"); /** Camera states **/ // These states are defined bitwise so we can easily to specify a set of @@ -51,7 +51,7 @@ class CameraStateHolder { return mState; } - private interface ConditionChecker { + private static interface ConditionChecker { /** * @return Whether the condition holds. */ @@ -99,7 +99,7 @@ class CameraStateHolder { return waitForCondition(new ConditionChecker() { @Override public boolean success() { - return (states | mState) == states; + return (states | getState()) == states; } }, CameraAgent.CAMERA_OPERATION_TIMEOUT_MS); } @@ -116,7 +116,7 @@ class CameraStateHolder { return waitForCondition(new ConditionChecker() { @Override public boolean success() { - return (states & mState) == 0; + return (states & getState()) == 0; } }, CameraAgent.CAMERA_OPERATION_TIMEOUT_MS); } diff --git a/camera2/portability/src/com/android/ex/camera2/portability/DispatchThread.java b/camera2/portability/src/com/android/ex/camera2/portability/DispatchThread.java index 67713c9..bc77259 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/DispatchThread.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/DispatchThread.java @@ -25,7 +25,7 @@ import com.android.ex.camera2.portability.debug.Log; import java.util.LinkedList; import java.util.Queue; -class DispatchThread extends Thread { +public class DispatchThread extends Thread { private static final Log.Tag TAG = new Log.Tag("DispatchThread"); private static final long MAX_MESSAGE_QUEUE_LENGTH = 256; diff --git a/camera2/portability/src/com/android/ex/camera2/portability/Size.java b/camera2/portability/src/com/android/ex/camera2/portability/Size.java index 9ae21aa..042c443 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/Size.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/Size.java @@ -45,6 +45,20 @@ public class Size { } /** + * A helper method to build a list of this class from a list of {@link android.util.Size}. + * + * @param cameraSizes Source. + * @return The built list. + */ + public static List<Size> buildListFromAndroidSizes(List<android.util.Size> androidSizes) { + ArrayList<Size> list = new ArrayList<Size>(androidSizes.size()); + for (android.util.Size androidSize : androidSizes) { + list.add(new Size(androidSize)); + } + return list; + } + + /** * Encode List of this class as comma-separated list of integers. * * @param sizes List of this class to encode. @@ -110,6 +124,19 @@ public class Size { } /** + * Constructor from a source {@link android.util.Size}. + * + * @param other The source size. + */ + public Size(android.util.Size other) { + if (other == null) { + val = new Point(0, 0); + } else { + val = new Point(other.getWidth(), other.getHeight()); + } + } + + /** * Constructor from a source {@link android.graphics.Point}. * * @param p The source size. |