summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSol Boucher <solb@google.com>2014-08-08 00:20:53 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2014-08-07 15:00:45 +0000
commit399c82779ad59de44f46c25fd031f9909e0581e9 (patch)
tree6bb42e9422531b2754aaa9fd3540da2e5b2bb43d
parent91a9eebe63a2481ed6adbc59ecfbb22ce41fc65e (diff)
parentde48004068f8c16f9a56c60b0ed2485a67687b4b (diff)
downloadandroid_frameworks_ex-399c82779ad59de44f46c25fd031f9909e0581e9.tar.gz
android_frameworks_ex-399c82779ad59de44f46c25fd031f9909e0581e9.tar.bz2
android_frameworks_ex-399c82779ad59de44f46c25fd031f9909e0581e9.zip
Merge "camera2-portability: Support photo capture using camera2 API" into lmp-dev
-rw-r--r--camera2/portability/portability.mk1
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java299
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Capabilities.java391
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java455
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java12
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraSettings.java12
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java29
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilities.java3
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/CameraDeviceInfo.java100
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/CameraSettings.java40
-rw-r--r--camera2/portability/tests/Android.mk5
-rw-r--r--camera2/portability/tests/AndroidManifest.xml4
-rw-r--r--camera2/portability/tests/src/com/android/ex/camera2/portability/Camera2PortabilityTest.java183
-rw-r--r--camera2/utils/tests/src/com/android/ex/camera2/utils/Camera2DeviceTester.java93
-rw-r--r--camera2/utils/tests/src/com/android/ex/camera2/utils/Camera2UtilsTest.java70
15 files changed, 1137 insertions, 560 deletions
diff --git a/camera2/portability/portability.mk b/camera2/portability/portability.mk
index a86c511..2ecc1df 100644
--- a/camera2/portability/portability.mk
+++ b/camera2/portability/portability.mk
@@ -19,5 +19,6 @@ LOCAL_MODULE := android-ex-camera2-portability
LOCAL_MODULE_TAGS := optional
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := android-ex-camera2-utils
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java
index 74e76a3..65047f1 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java
@@ -18,6 +18,7 @@ package com.android.ex.camera2.portability;
import android.annotation.TargetApi;
import android.content.Context;
+import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
@@ -30,6 +31,8 @@ import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.MeteringRectangle;
+import android.media.Image;
+import android.media.ImageReader;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
@@ -38,7 +41,9 @@ import android.os.Message;
import android.view.Surface;
import com.android.ex.camera2.portability.debug.Log;
+import com.android.ex.camera2.utils.Camera2RequestSettingsSet;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -148,6 +153,10 @@ class AndroidCamera2AgentImpl extends CameraAgent {
return mDispatchThread;
}
+ private static abstract class CaptureAvailableListener
+ extends CameraCaptureSession.CaptureListener
+ implements ImageReader.OnImageAvailableListener {};
+
private class Camera2Handler extends HistoryHandler {
// Caller-provided when leaving CAMERA_UNOPENED state:
private CameraOpenCallback mOpenCallback;
@@ -157,8 +166,9 @@ class AndroidCamera2AgentImpl extends CameraAgent {
// Available in CAMERA_UNCONFIGURED state and above:
private CameraDevice mCamera;
private AndroidCamera2ProxyImpl mCameraProxy;
- private CaptureRequest.Builder mPersistentRequestBuilder;
+ private Camera2RequestSettingsSet mPersistentSettings;
private Rect mActiveArray;
+ private boolean mLegacyDevice;
// Available in CAMERA_CONFIGURED state and above:
private Size mPreviewSize;
@@ -168,6 +178,7 @@ class AndroidCamera2AgentImpl extends CameraAgent {
private SurfaceTexture mPreviewTexture;
private Surface mPreviewSurface;
private CameraCaptureSession mSession;
+ private ImageReader mCaptureReader;
// Available from the beginning of PREVIEW_ACTIVE until the first preview frame arrives:
private CameraStartPreviewCallback mOneshotPreviewingCallback;
@@ -175,6 +186,9 @@ class AndroidCamera2AgentImpl extends CameraAgent {
// Available in FOCUS_LOCKED between AF trigger receipt and whenever the lens stops moving:
private CameraAFCallback mOneshotAfCallback;
+ // Available when taking picture between AE trigger receipt and autoexposure convergence
+ private CaptureAvailableListener mOneshotCaptureCallback;
+
// Available whenever setAutoFocusMoveCallback() was last invoked with a non-null argument:
private CameraAFMoveCallback mPassiveAfCallback;
@@ -226,13 +240,17 @@ class AndroidCamera2AgentImpl extends CameraAgent {
mCamera = null;
}
mCameraProxy = null;
- mPersistentRequestBuilder = null;
+ mPersistentSettings = null;
mActiveArray = null;
if (mPreviewSurface != null) {
mPreviewSurface.release();
mPreviewSurface = null;
}
mPreviewTexture = null;
+ if (mCaptureReader != null) {
+ mCaptureReader.close();
+ mCaptureReader = null;
+ }
mPreviewSize = null;
mPhotoSize = null;
mCameraIndex = 0;
@@ -265,8 +283,10 @@ class AndroidCamera2AgentImpl extends CameraAgent {
mOneshotPreviewingCallback = (CameraStartPreviewCallback) msg.obj;
mCameraState.setState(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE);
try {
- mSession.setRepeatingRequest(mPersistentRequestBuilder.build(),
- /*listener*/mCameraFocusStateListener, /*handler*/this);
+ mSession.setRepeatingRequest(
+ mPersistentSettings.createRequest(mCamera,
+ CameraDevice.TEMPLATE_PREVIEW, mPreviewSurface),
+ /*listener*/mCameraFocusStateListener, /*handler*/this);
} catch(CameraAccessException ex) {
Log.w(TAG, "Unable to start preview", ex);
mCameraState.setState(AndroidCamera2StateHolder.CAMERA_PREVIEW_READY);
@@ -319,7 +339,7 @@ class AndroidCamera2AgentImpl extends CameraAgent {
}*/
case CameraActions.APPLY_SETTINGS: {
- CameraSettings settings = (CameraSettings) msg.obj;
+ AndroidCamera2Settings settings = (AndroidCamera2Settings) msg.obj;
applyToRequest(settings);
break;
}
@@ -365,17 +385,19 @@ class AndroidCamera2AgentImpl extends CameraAgent {
// Send a one-time capture to trigger the camera driver to lock focus.
mCameraState.setState(AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED);
- mPersistentRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
+ Camera2RequestSettingsSet trigger =
+ new Camera2RequestSettingsSet(mPersistentSettings);
+ trigger.set(CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_START);
try {
- mSession.capture(mPersistentRequestBuilder.build(),
+ mSession.capture(
+ trigger.createRequest(mCamera, CameraDevice.TEMPLATE_PREVIEW,
+ mPreviewSurface),
/*listener*/deferredCallbackSetter, /*handler*/ this);
} catch(CameraAccessException ex) {
Log.e(TAG, "Unable to lock autofocus", ex);
mCameraState.setState(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE);
}
- mPersistentRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
- CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
break;
}
@@ -389,18 +411,20 @@ class AndroidCamera2AgentImpl extends CameraAgent {
// Send a one-time capture to trigger the camera driver to resume scanning.
mCameraState.setState(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE);
- mPersistentRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
+ Camera2RequestSettingsSet cancel =
+ new Camera2RequestSettingsSet(mPersistentSettings);
+ cancel.set(CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
try {
- mSession.capture(mPersistentRequestBuilder.build(),
+ mSession.capture(
+ cancel.createRequest(mCamera, CameraDevice.TEMPLATE_PREVIEW,
+ mPreviewSurface),
/*listener*/null, /*handler*/this);
} catch(CameraAccessException ex) {
Log.e(TAG, "Unable to cancel autofocus", ex);
mCameraState.setState(
AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED);
}
- mPersistentRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
- CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
break;
}
@@ -431,15 +455,78 @@ class AndroidCamera2AgentImpl extends CameraAgent {
case CameraActions.ENABLE_SHUTTER_SOUND: {
break;
- }
+ }*/
case CameraActions.SET_DISPLAY_ORIENTATION: {
+ // TODO: Need to handle preview in addition to capture
+ // Only set the JPEG capture orientation if requested to do so; otherwise,
+ // capture in the sensor's physical orientation
+ mPersistentSettings.set(CaptureRequest.JPEG_ORIENTATION, msg.arg2 > 0 ?
+ mCameraProxy.getCharacteristics().getJpegOrientation(msg.arg1) : 0);
break;
}
case CameraActions.CAPTURE_PHOTO: {
+ if (mCameraState.getState() <
+ AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) {
+ Log.e(TAG, "Photos may only be taken when a preview is active");
+ break;
+ }
+ if (mCameraState.getState() !=
+ AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED) {
+ Log.w(TAG, "Taking a (likely blurry) photo without the lens locked");
+ }
+
+ final CaptureAvailableListener listener =
+ (CaptureAvailableListener) msg.obj;
+ if (mLegacyDevice) {
+ // Just snap the shot
+ mCaptureReader.setOnImageAvailableListener(listener, /*handler*/this);
+ try {
+ mSession.capture(
+ mPersistentSettings.createRequest(mCamera,
+ CameraDevice.TEMPLATE_STILL_CAPTURE,
+ mCaptureReader.getSurface()),
+ listener, /*handler*/this);
+ } catch (CameraAccessException ex) {
+ Log.e(TAG, "Unable to initiate legacy capture", ex);
+ }
+ } else {
+ // Not a legacy device, so we need to let AE converge before capturing
+ CameraCaptureSession.CaptureListener deferredCallbackSetter =
+ new CameraCaptureSession.CaptureListener() {
+ @Override
+ public void onCaptureCompleted(CameraCaptureSession session,
+ CaptureRequest request,
+ TotalCaptureResult result) {
+ mOneshotCaptureCallback = listener;
+ }
+
+ @Override
+ public void onCaptureFailed(CameraCaptureSession session,
+ CaptureRequest request,
+ CaptureFailure failure) {
+ Log.e(TAG, "Autoexposure and capture failed with reason " +
+ failure.getReason());
+ // TODO: Make an error callback?
+ }};
+
+ // Set a one-time capture to trigger the camera driver's autoexposure:
+ Camera2RequestSettingsSet expose =
+ new Camera2RequestSettingsSet(mPersistentSettings);
+ expose.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
+ CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
+ try {
+ mSession.capture(
+ expose.createRequest(mCamera, CameraDevice.TEMPLATE_PREVIEW,
+ mPreviewSurface),
+ /*listener*/deferredCallbackSetter, /*handler*/this);
+ } catch (CameraAccessException ex) {
+ Log.e(TAG, "Unable to run autoexposure and perform capture", ex);
+ }
+ }
break;
- }*/
+ }
default: {
// TODO: Rephrase once everything has been implemented
@@ -474,8 +561,13 @@ class AndroidCamera2AgentImpl extends CameraAgent {
}
public CameraSettings buildSettings(AndroidCamera2Capabilities caps) {
- return new AndroidCamera2Settings(caps, mPersistentRequestBuilder, mPreviewSize,
- mPhotoSize);
+ try {
+ return new AndroidCamera2Settings(mCamera, CameraDevice.TEMPLATE_PREVIEW,
+ mActiveArray, mPreviewSize, mPhotoSize);
+ } catch (CameraAccessException ex) {
+ Log.e(TAG, "Unable to query camera device to build settings representation");
+ return null;
+ }
}
/**
@@ -487,34 +579,19 @@ class AndroidCamera2AgentImpl extends CameraAgent {
*
* @param settings The new/updated settings
*/
- // TODO: Finish implementation to add support for all settings
- private void applyToRequest(CameraSettings settings) {
+ private void applyToRequest(AndroidCamera2Settings settings) {
// TODO: If invoked when in PREVIEW_READY state, a new preview size will not take effect
- AndroidCamera2Capabilities.IntegralStringifier intifier =
- mCameraProxy.getSpecializedCapabilities().getIntegralStringifier();
+
+ mPersistentSettings.union(settings.getRequestSettings());
mPreviewSize = settings.getCurrentPreviewSize();
mPhotoSize = settings.getCurrentPhotoSize();
- mPersistentRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
- intifier.intify(settings.getCurrentFocusMode()));
-
- mPersistentRequestBuilder.set(CaptureRequest.CONTROL_AF_REGIONS,
- legacyAreasToMeteringRectangles(settings.getFocusAreas()));
- mPersistentRequestBuilder.set(CaptureRequest.CONTROL_AE_REGIONS,
- legacyAreasToMeteringRectangles(settings.getMeteringAreas()));
-
- if (settings.getCurrentFlashMode() != CameraCapabilities.FlashMode.NO_FLASH) {
- mPersistentRequestBuilder.set(CaptureRequest.FLASH_MODE,
- intifier.intify(settings.getCurrentFlashMode()));
- }
- if (settings.getCurrentSceneMode() != CameraCapabilities.SceneMode.NO_SCENE_MODE) {
- mPersistentRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE,
- intifier.intify(settings.getCurrentSceneMode()));
- }
if (mCameraState.getState() >= AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) {
// If we're already previewing, reflect most settings immediately
try {
- mSession.setRepeatingRequest(mPersistentRequestBuilder.build(),
+ mSession.setRepeatingRequest(
+ mPersistentSettings.createRequest(mCamera,
+ CameraDevice.TEMPLATE_PREVIEW, mPreviewSurface),
/*listener*/mCameraFocusStateListener, /*handler*/this);
} catch (CameraAccessException ex) {
Log.e(TAG, "Failed to apply updated request settings", ex);
@@ -525,46 +602,6 @@ class AndroidCamera2AgentImpl extends CameraAgent {
}
}
- private MeteringRectangle[] legacyAreasToMeteringRectangles(
- List<android.hardware.Camera.Area> reference) {
- MeteringRectangle[] transformed = null;
- if (reference.size() > 0) {
-
- transformed = new MeteringRectangle[reference.size()];
- for (int index = 0; index < reference.size(); ++index) {
- android.hardware.Camera.Area source = reference.get(index);
- Rect rectangle = source.rect;
-
- // Old API coordinates were [-1000,1000]; new ones are [0,ACTIVE_ARRAY_SIZE).
- double oldLeft = (rectangle.left + 1000) / 2000.0;
- double oldTop = (rectangle.top + 1000) / 2000.0;
- double oldRight = (rectangle.right + 1000) / 2000.0;
- double oldBottom = (rectangle.bottom + 1000) / 2000.0;
- int left = toIntConstrained(
- mActiveArray.width() * oldLeft + mActiveArray.left,
- 0, mActiveArray.width() - 1);
- int top = toIntConstrained(
- mActiveArray.height() * oldTop + mActiveArray.top,
- 0, mActiveArray.height() - 1);
- int right = toIntConstrained(
- mActiveArray.width() * oldRight + mActiveArray.left,
- 0, mActiveArray.width() - 1);
- int bottom = toIntConstrained(
- mActiveArray.height() * oldBottom + mActiveArray.top,
- 0, mActiveArray.height() - 1);
- transformed[index] = new MeteringRectangle(left, top,
- right - left, bottom - top, source.weight);
- }
- }
- return transformed;
- }
-
- private int toIntConstrained(double original, int min, int max) {
- original = Math.max(original, min);
- original = Math.min(original, max);
- return (int) original;
- }
-
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!
@@ -589,14 +626,19 @@ class AndroidCamera2AgentImpl extends CameraAgent {
surfaceTexture.setDefaultBufferSize(mPreviewSize.width(), mPreviewSize.height());
if (mPreviewSurface != null) {
- mPersistentRequestBuilder.removeTarget(mPreviewSurface);
mPreviewSurface.release();
}
mPreviewSurface = new Surface(surfaceTexture);
- mPersistentRequestBuilder.addTarget(mPreviewSurface);
+
+ if (mCaptureReader != null) {
+ mCaptureReader.close();
+ }
+ mCaptureReader = ImageReader.newInstance(
+ mPhotoSize.width(), mPhotoSize.height(), ImageFormat.JPEG, 1);
try {
- mCamera.createCaptureSession(Arrays.asList(mPreviewSurface),
+ mCamera.createCaptureSession(
+ Arrays.asList(mPreviewSurface, mCaptureReader.getSurface()),
mCameraPreviewStateListener, this);
} catch (CameraAccessException ex) {
Log.e(TAG, "Failed to create camera capture session", ex);
@@ -625,10 +667,12 @@ class AndroidCamera2AgentImpl extends CameraAgent {
mCameraManager.getCameraCharacteristics(mCameraId);
mCameraProxy = new AndroidCamera2ProxyImpl(mCameraIndex, mCamera,
getCameraDeviceInfo().getCharacteristics(mCameraIndex), props);
- mPersistentRequestBuilder =
- camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ mPersistentSettings = new Camera2RequestSettingsSet();
mActiveArray =
props.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ mLegacyDevice =
+ props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) ==
+ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
mCameraState.setState(AndroidCamera2StateHolder.CAMERA_UNCONFIGURED);
mOpenCallback.onCameraOpened(mCameraProxy);
} catch (CameraAccessException ex) {
@@ -723,6 +767,37 @@ class AndroidCamera2AgentImpl extends CameraAgent {
}
}
}
+
+ Integer aeStateMaybe = result.get(CaptureResult.CONTROL_AE_STATE);
+ if (aeStateMaybe != null) {
+ int aeState = aeStateMaybe;
+
+ switch (aeState) {
+ case CaptureResult.CONTROL_AE_STATE_CONVERGED:
+ case CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED:
+ case CaptureResult.CONTROL_AE_STATE_LOCKED: {
+ if (mOneshotCaptureCallback != null) {
+ // A call to takePicture() was just made, and autoexposure converged
+ // so it's time to initiate the capture!
+ mCaptureReader.setOnImageAvailableListener(mOneshotCaptureCallback,
+ /*handler*/Camera2Handler.this);
+ try {
+ mSession.capture(
+ mPersistentSettings.createRequest(mCamera,
+ CameraDevice.TEMPLATE_STILL_CAPTURE,
+ mCaptureReader.getSurface()),
+ /*listener*/mOneshotCaptureCallback,
+ /*handler*/Camera2Handler.this);
+ } catch (CameraAccessException ex) {
+ Log.e(TAG, "Unable to initiate capture", ex);
+ } finally {
+ mOneshotCaptureCallback = null;
+ }
+ }
+ break;
+ }
+ }
+ }
}
@Override
@@ -836,15 +911,50 @@ class AndroidCamera2AgentImpl extends CameraAgent {
// TODO: Implement
@Override
- public void takePicture(Handler handler,
- CameraShutterCallback shutter,
+ public void takePicture(final Handler handler,
+ final CameraShutterCallback shutter,
CameraPictureCallback raw,
CameraPictureCallback postview,
- CameraPictureCallback jpeg) {}
+ final CameraPictureCallback jpeg) {
+ // TODO: We never call raw or postview
+ final CaptureAvailableListener picListener =
+ new CaptureAvailableListener() {
+ @Override
+ public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
+ long timestamp) {
+ if (shutter != null) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ shutter.onShutter(AndroidCamera2ProxyImpl.this);
+ }});
+ }
+ }
- // TODO: Remove this method override once we handle the message
- @Override
- public void setDisplayOrientation(int degrees) {}
+ @Override
+ public void onImageAvailable(ImageReader reader) {
+ try (Image image = reader.acquireNextImage()) {
+ if (jpeg != null) {
+ ByteBuffer buffer = image.getPlanes()[0].getBuffer();
+ final byte[] pixels = new byte[buffer.remaining()];
+ buffer.get(pixels);
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ jpeg.onPictureTaken(pixels, AndroidCamera2ProxyImpl.this);
+ }});
+ }
+ }
+ }};
+ mDispatchThread.runJob(new Runnable() {
+ @Override
+ public void run() {
+ mCameraState.waitForStates(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE |
+ AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED);
+ mCameraHandler.obtainMessage(CameraActions.CAPTURE_PHOTO, picListener)
+ .sendToTarget();
+ }});
+ }
// TODO: Implement
@Override
@@ -882,6 +992,15 @@ class AndroidCamera2AgentImpl extends CameraAgent {
@Override
public boolean applySettings(CameraSettings settings) {
+ if (settings == null) {
+ Log.w(TAG, "null parameters in applySettings()");
+ return false;
+ }
+ if (!(settings instanceof AndroidCamera2Settings)) {
+ Log.e(TAG, "Provided settings not compatible with the backing framework API");
+ return false;
+ }
+
return applySettingsHelper(settings, AndroidCamera2StateHolder.CAMERA_UNCONFIGURED |
AndroidCamera2StateHolder.CAMERA_CONFIGURED |
AndroidCamera2StateHolder.CAMERA_PREVIEW_READY);
@@ -995,7 +1114,7 @@ class AndroidCamera2AgentImpl extends CameraAgent {
return mFirstFrontCameraId;
}
- private static class AndroidCharacteristics2 implements Characteristics {
+ private static class AndroidCharacteristics2 extends Characteristics {
private CameraCharacteristics mCameraInfo;
AndroidCharacteristics2(CameraCharacteristics cameraInfo) {
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Capabilities.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Capabilities.java
index b81e098..4bdbe64 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Capabilities.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Capabilities.java
@@ -38,11 +38,8 @@ import java.util.Arrays;
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();
+ super(new Stringifier());
StreamConfigurationMap s = p.get(SCALER_STREAM_CONFIGURATION_MAP);
@@ -99,18 +96,18 @@ public class AndroidCamera2Capabilities extends CameraCapabilities {
if (mMaxNumOfMeteringArea > 0) {
mSupportedFeatures.add(Feature.METERING_AREA);
}
- }
- public IntegralStringifier getIntegralStringifier() {
- return mIntStringifier;
+ // TODO: Detect other features
}
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);
+ int[] scenes = p.get(CONTROL_AVAILABLE_SCENE_MODES);
+ if (scenes != null) {
+ for (int scene : scenes) {
+ SceneMode equiv = sceneModeFromInt(scene);
+ if (equiv != null) {
+ mSupportedSceneModes.add(equiv);
+ }
}
}
}
@@ -118,275 +115,145 @@ public class AndroidCamera2Capabilities extends CameraCapabilities {
private void buildFlashModes(CameraCharacteristics p) {
mSupportedFlashModes.add(FlashMode.OFF);
if (p.get(FLASH_INFO_AVAILABLE)) {
+ mSupportedFlashModes.add(FlashMode.AUTO);
mSupportedFlashModes.add(FlashMode.ON);
mSupportedFlashModes.add(FlashMode.TORCH);
- // TODO: New modes aren't represented here
+ for (int expose : p.get(CONTROL_AE_AVAILABLE_MODES)) {
+ if (expose == CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE) {
+ mSupportedFlashModes.add(FlashMode.RED_EYE);
+ }
+ }
}
}
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);
+ int[] focuses = p.get(CONTROL_AF_AVAILABLE_MODES);
+ if (focuses != null) {
+ for (int focus : focuses) {
+ FocusMode equiv = focusModeFromInt(focus);
+ if (equiv != null) {
+ 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);
+ int[] bals = p.get(CONTROL_AWB_AVAILABLE_MODES);
+ if (bals != null) {
+ for (int bal : bals) {
+ WhiteBalance equiv = whiteBalanceFromInt(bal);
+ if (equiv != null) {
+ 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 API-related integer representation of the focus mode to the
+ * abstract representation.
+ *
+ * @param fm The integral representation.
+ * @return The mode represented by the input integer, or {@code null} if it
+ * cannot be converted.
+ */
+ public static 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;
+ // TODO: We cannot support INFINITY
+ case CONTROL_AF_MODE_MACRO:
+ return FocusMode.MACRO;
}
+ Log.w(TAG, "Unable to convert from API 2 focus mode: " + fm);
+ return null;
+ }
- /**
- * 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 scene mode to the
+ * abstract representation.
+ *
+ * @param sm The integral representation.
+ * @return The mode represented by the input integer, or {@code null} if it
+ * cannot be converted.
+ */
+ public static 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;
+ // TODO: We cannot support HDR
+ case CONTROL_SCENE_MODE_LANDSCAPE:
+ return SceneMode.LANDSCAPE;
+ case CONTROL_SCENE_MODE_NIGHT:
+ return SceneMode.NIGHT;
+ // TODO: We cannot support NIGHT_PORTRAIT
+ 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: We cannot expose FACE_PRIORITY, or HIGH_SPEED_VIDEO
}
+ Log.w(TAG, "Unable to convert from API 2 scene mode: " + sm);
+ return null;
+ }
- /**
- * 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];
+ /**
+ * 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 {@code null} if
+ * it cannot be converted.
+ */
+ public static 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;
}
+ Log.w(TAG, "Unable to convert from API 2 white balance: " + wb);
+ return null;
}
}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java
index e21e177..288ded7 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java
@@ -16,32 +16,449 @@
package com.android.ex.camera2.portability;
-import android.hardware.camera2.CaptureRequest;
+import static android.hardware.camera2.CaptureRequest.*;
+
+import android.graphics.Rect;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.params.MeteringRectangle;
+import android.util.Range;
+
+import com.android.ex.camera2.portability.CameraCapabilities.FlashMode;
+import com.android.ex.camera2.portability.CameraCapabilities.FocusMode;
+import com.android.ex.camera2.portability.CameraCapabilities.SceneMode;
+import com.android.ex.camera2.portability.CameraCapabilities.WhiteBalance;
+import com.android.ex.camera2.portability.debug.Log;
+import com.android.ex.camera2.utils.Camera2RequestSettingsSet;
+
+import java.util.List;
+import java.util.Objects;
/**
* 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
+ private static final Log.Tag TAG = new Log.Tag("AndCam2Set");
+
+ private final Builder mTemplateSettings;
+ private final Rect mActiveArray;
+ private final Camera2RequestSettingsSet mRequestSettings;
+
+ /**
+ * Create a settings representation that answers queries of unspecified
+ * options in the same way as the provided template would.
+ *
+ * <p>The default settings provided by the given template are only ever used
+ * for reporting back to the client app (i.e. when it queries an option
+ * it didn't explicitly set first). {@link Camera2RequestSettingsSet}s
+ * generated by an instance of this class will have any settings not
+ * modified using one of that instance's mutators forced to default, so that
+ * their effective values when submitting a capture request will be those of
+ * the template that is provided to the camera framework at that time.</p>
+ *
+ * @param camera Device from which to draw default settings.
+ * @param template Specific template to use for the defaults.
+ * @param activeArray Boundary coordinates of the sensor's active array.
+ * @param preview Dimensions of preview streams.
+ * @param photo Dimensions of captured images.
+ *
+ * @throws CameraAccessException Upon internal framework/driver failure.
+ */
+ public AndroidCamera2Settings(CameraDevice camera, int template, Rect activeArray,
+ Size preview, Size photo) throws CameraAccessException {
+ mTemplateSettings = camera.createCaptureRequest(template);
+ mActiveArray = activeArray;
+ mRequestSettings = new Camera2RequestSettingsSet();
+
+ Range<Integer> previewFpsRange = mTemplateSettings.get(CONTROL_AE_TARGET_FPS_RANGE);
+ if (previewFpsRange != null) {
+ setPreviewFpsRange(previewFpsRange.getLower(), previewFpsRange.getUpper());
+ }
setPreviewSize(preview);
+ // TODO: mCurrentPreviewFormat
setPhotoSize(photo);
+ mJpegCompressQuality = queryTemplateDefaultOrMakeOneUp(JPEG_QUALITY, (byte) 0);
+ // TODO: mCurrentPhotoFormat
+ // TODO: mCurrentZoomRatio
+ mCurrentZoomRatio = 1.0f;
+ // TODO: mCurrentZoomIndex
+ mExposureCompensationIndex =
+ queryTemplateDefaultOrMakeOneUp(CONTROL_AE_EXPOSURE_COMPENSATION, 0);
+ mCurrentFlashMode = flashModeFromRequest();
+ mCurrentFocusMode = AndroidCamera2Capabilities.focusModeFromInt(
+ mTemplateSettings.get(CONTROL_AF_MODE));
+ mCurrentSceneMode = AndroidCamera2Capabilities.sceneModeFromInt(
+ mTemplateSettings.get(CONTROL_SCENE_MODE));
+ mWhiteBalance = AndroidCamera2Capabilities.whiteBalanceFromInt(
+ mTemplateSettings.get(CONTROL_AWB_MODE));
+ mVideoStabilizationEnabled = queryTemplateDefaultOrMakeOneUp(
+ CONTROL_VIDEO_STABILIZATION_MODE, CONTROL_VIDEO_STABILIZATION_MODE_OFF) ==
+ CONTROL_VIDEO_STABILIZATION_MODE_ON;
+ mAutoExposureLocked = queryTemplateDefaultOrMakeOneUp(CONTROL_AE_LOCK, false);
+ mAutoWhiteBalanceLocked = queryTemplateDefaultOrMakeOneUp(CONTROL_AWB_LOCK, false);
+ // TODO: mRecordingHintEnabled
+ // TODO: mGpsData
+ android.util.Size exifThumbnailSize = mTemplateSettings.get(JPEG_THUMBNAIL_SIZE);
+ if (exifThumbnailSize != null) {
+ mExifThumbnailSize =
+ new Size(exifThumbnailSize.getWidth(), exifThumbnailSize.getHeight());
+ }
+ }
+
+ public AndroidCamera2Settings(AndroidCamera2Settings other) {
+ super(other);
+ mTemplateSettings = other.mTemplateSettings;
+ mActiveArray = other.mActiveArray;
+ mRequestSettings = new Camera2RequestSettingsSet(other.mRequestSettings);
+ }
+
+ @Override
+ public CameraSettings copy() {
+ return new AndroidCamera2Settings(this);
+ }
+
+ private <T> T queryTemplateDefaultOrMakeOneUp(Key<T> key, T defaultDefault) {
+ T val = mTemplateSettings.get(key);
+ if (val != null) {
+ return val;
+ } else {
+ // Spoof the default so matchesTemplateDefault excludes this key from generated sets.
+ // This approach beats a simple sentinel because it provides basic boolean support.
+ mTemplateSettings.set(key, defaultDefault);
+ return defaultDefault;
+ }
+ }
+
+ private FlashMode flashModeFromRequest() {
+ Integer autoExposure = mTemplateSettings.get(CONTROL_AE_MODE);
+ if (autoExposure != null) {
+ switch (autoExposure) {
+ case CONTROL_AE_MODE_ON:
+ return FlashMode.OFF;
+ case CONTROL_AE_MODE_ON_AUTO_FLASH:
+ return FlashMode.AUTO;
+ case CONTROL_AE_MODE_ON_ALWAYS_FLASH: {
+ if (mTemplateSettings.get(FLASH_MODE) == FLASH_MODE_TORCH) {
+ return FlashMode.TORCH;
+ } else {
+ return FlashMode.ON;
+ }
+ }
+ case CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE:
+ return FlashMode.RED_EYE;
+ }
+ }
+ return null;
+ }
+
+ private boolean matchesTemplateDefault(Key<?> setting) {
+ if (setting == CONTROL_AE_REGIONS) {
+ return mMeteringAreas.size() == 0;
+ } else if (setting == CONTROL_AF_REGIONS) {
+ return mFocusAreas.size() == 0;
+ } else if (setting == CONTROL_AE_TARGET_FPS_RANGE) {
+ Range defaultFpsRange = mTemplateSettings.get(CONTROL_AE_TARGET_FPS_RANGE);
+ return (mPreviewFpsRangeMin == 0 && mPreviewFpsRangeMax == 0) ||
+ (defaultFpsRange != null && mPreviewFpsRangeMin == defaultFpsRange.getLower() &&
+ mPreviewFpsRangeMax == defaultFpsRange.getUpper());
+ } else if (setting == JPEG_QUALITY) {
+ return Objects.equals(mJpegCompressQuality,
+ mTemplateSettings.get(JPEG_QUALITY));
+ } else if (setting == CONTROL_AE_EXPOSURE_COMPENSATION) {
+ return Objects.equals(mExposureCompensationIndex,
+ mTemplateSettings.get(CONTROL_AE_EXPOSURE_COMPENSATION));
+ } else if (setting == CONTROL_VIDEO_STABILIZATION_MODE) {
+ Integer videoStabilization = mTemplateSettings.get(CONTROL_VIDEO_STABILIZATION_MODE);
+ return (videoStabilization != null &&
+ (mVideoStabilizationEnabled && videoStabilization ==
+ CONTROL_VIDEO_STABILIZATION_MODE_ON) ||
+ (!mVideoStabilizationEnabled && videoStabilization ==
+ CONTROL_VIDEO_STABILIZATION_MODE_OFF));
+ } else if (setting == CONTROL_AE_LOCK) {
+ return Objects.equals(mAutoExposureLocked, mTemplateSettings.get(CONTROL_AE_LOCK));
+ } else if (setting == CONTROL_AWB_LOCK) {
+ return Objects.equals(mAutoWhiteBalanceLocked, mTemplateSettings.get(CONTROL_AWB_LOCK));
+ } else if (setting == JPEG_THUMBNAIL_SIZE) {
+ android.util.Size defaultThumbnailSize = mTemplateSettings.get(JPEG_THUMBNAIL_SIZE);
+ return (mExifThumbnailSize.width() == 0 && mExifThumbnailSize.height() == 0) ||
+ (defaultThumbnailSize != null &&
+ mExifThumbnailSize.width() == defaultThumbnailSize.getWidth() &&
+ mExifThumbnailSize.height() == defaultThumbnailSize.getHeight());
+ }
+ Log.w(TAG, "Settings implementation checked default of unhandled option key");
+ // Since this class isn't equipped to handle it, claim it matches the default to prevent
+ // updateRequestSettingOrForceToDefault from going with the user-provided preference
+ return true;
+ }
+
+ private <T> void updateRequestSettingOrForceToDefault(Key<T> setting, T possibleChoice) {
+ mRequestSettings.set(setting, matchesTemplateDefault(setting) ? null : possibleChoice);
+ }
+
+ public Camera2RequestSettingsSet getRequestSettings() {
+ updateRequestSettingOrForceToDefault(CONTROL_AE_REGIONS,
+ legacyAreasToMeteringRectangles(mMeteringAreas));
+ updateRequestSettingOrForceToDefault(CONTROL_AF_REGIONS,
+ legacyAreasToMeteringRectangles(mFocusAreas));
+ updateRequestSettingOrForceToDefault(CONTROL_AE_TARGET_FPS_RANGE,
+ new Range(mPreviewFpsRangeMin, mPreviewFpsRangeMax));
+ // TODO: mCurrentPreviewFormat
+ updateRequestSettingOrForceToDefault(JPEG_QUALITY, mJpegCompressQuality);
+ // TODO: mCurrentPhotoFormat
+ // TODO: mCurrentZoomRatio
+ // TODO: mCurrentZoomIndex
+ updateRequestSettingOrForceToDefault(CONTROL_AE_EXPOSURE_COMPENSATION,
+ mExposureCompensationIndex);
+ updateRequestFlashMode();
+ updateRequestFocusMode();
+ updateRequestSceneMode();
+ updateRequestWhiteBalance();
+ updateRequestSettingOrForceToDefault(CONTROL_VIDEO_STABILIZATION_MODE,
+ mVideoStabilizationEnabled ?
+ CONTROL_VIDEO_STABILIZATION_MODE_ON : CONTROL_VIDEO_STABILIZATION_MODE_OFF);
+ // OIS shouldn't be on if software video stabilization is.
+ mRequestSettings.set(LENS_OPTICAL_STABILIZATION_MODE,
+ mVideoStabilizationEnabled ? LENS_OPTICAL_STABILIZATION_MODE_OFF :
+ null);
+ updateRequestSettingOrForceToDefault(CONTROL_AE_LOCK, mAutoExposureLocked);
+ updateRequestSettingOrForceToDefault(CONTROL_AWB_LOCK, mAutoWhiteBalanceLocked);
+ // TODO: mRecordingHintEnabled
+ // TODO: mGpsData
+ updateRequestSettingOrForceToDefault(JPEG_THUMBNAIL_SIZE,
+ new android.util.Size(
+ mExifThumbnailSize.width(), mExifThumbnailSize.height()));
+
+ return mRequestSettings;
+ }
+
+ private MeteringRectangle[] legacyAreasToMeteringRectangles(
+ List<android.hardware.Camera.Area> reference) {
+ MeteringRectangle[] transformed = null;
+ if (reference.size() > 0) {
+
+ transformed = new MeteringRectangle[reference.size()];
+ for (int index = 0; index < reference.size(); ++index) {
+ android.hardware.Camera.Area source = reference.get(index);
+ Rect rectangle = source.rect;
+
+ // Old API coordinates were [-1000,1000]; new ones are [0,ACTIVE_ARRAY_SIZE).
+ double oldLeft = (rectangle.left + 1000) / 2000.0;
+ double oldTop = (rectangle.top + 1000) / 2000.0;
+ double oldRight = (rectangle.right + 1000) / 2000.0;
+ double oldBottom = (rectangle.bottom + 1000) / 2000.0;
+ int left = toIntConstrained( mActiveArray.width() * oldLeft + mActiveArray.left,
+ 0, mActiveArray.width() - 1);
+ int top = toIntConstrained( mActiveArray.height() * oldTop + mActiveArray.top,
+ 0, mActiveArray.height() - 1);
+ int right = toIntConstrained( mActiveArray.width() * oldRight + mActiveArray.left,
+ 0, mActiveArray.width() - 1);
+ int bottom = toIntConstrained( mActiveArray.height() * oldBottom + mActiveArray.top,
+ 0, mActiveArray.height() - 1);
+ transformed[index] = new MeteringRectangle(left, top, right - left, bottom - top,
+ source.weight);
+ }
+ }
+ return transformed;
+ }
+
+ private int toIntConstrained(double original, int min, int max) {
+ original = Math.max(original, min);
+ original = Math.min(original, max);
+ return (int) original;
+ }
+
+ private void updateRequestFlashMode() {
+ Integer aeMode = null;
+ Integer flashMode = null;
+ if (mCurrentFlashMode != null) {
+ switch (mCurrentFlashMode) {
+ case AUTO: {
+ aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH;
+ break;
+ }
+ case OFF: {
+ aeMode = CONTROL_AE_MODE_ON;
+ flashMode = FLASH_MODE_OFF;
+ break;
+ }
+ case ON: {
+ aeMode = CONTROL_AE_MODE_ON_ALWAYS_FLASH;
+ flashMode = FLASH_MODE_SINGLE;
+ break;
+ }
+ case TORCH: {
+ flashMode = FLASH_MODE_TORCH;
+ break;
+ }
+ case RED_EYE: {
+ aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE;
+ break;
+ }
+ default: {
+ Log.w(TAG, "Unable to convert to API 2 flash mode: " + mCurrentFlashMode);
+ break;
+ }
+ }
+ }
+ mRequestSettings.set(CONTROL_AE_MODE, aeMode);
+ mRequestSettings.set(FLASH_MODE, flashMode);
+ }
+
+ private void updateRequestFocusMode() {
+ Integer mode = null;
+ if (mCurrentFocusMode != null) {
+ switch (mCurrentFocusMode) {
+ case AUTO: {
+ mode = CONTROL_AF_MODE_AUTO;
+ break;
+ }
+ case CONTINUOUS_PICTURE: {
+ mode = CONTROL_AF_MODE_CONTINUOUS_PICTURE;
+ break;
+ }
+ case CONTINUOUS_VIDEO: {
+ mode = CONTROL_AF_MODE_CONTINUOUS_VIDEO;
+ break;
+ }
+ case EXTENDED_DOF: {
+ mode = CONTROL_AF_MODE_EDOF;
+ break;
+ }
+ case FIXED: {
+ mode = CONTROL_AF_MODE_OFF;
+ break;
+ }
+ // TODO: We cannot support INFINITY
+ case MACRO: {
+ mode = CONTROL_AF_MODE_MACRO;
+ break;
+ }
+ default: {
+ Log.w(TAG, "Unable to convert to API 2 focus mode: " + mCurrentFocusMode);
+ break;
+ }
+ }
+ }
+ mRequestSettings.set(CONTROL_AF_MODE, mode);
+ }
+
+ private void updateRequestSceneMode() {
+ Integer mode = null;
+ if (mCurrentSceneMode != null) {
+ switch (mCurrentSceneMode) {
+ case AUTO: {
+ mode = CONTROL_SCENE_MODE_DISABLED;
+ break;
+ }
+ case ACTION: {
+ mode = CONTROL_SCENE_MODE_ACTION;
+ break;
+ }
+ case BARCODE: {
+ mode = CONTROL_SCENE_MODE_BARCODE;
+ break;
+ }
+ case BEACH: {
+ mode = CONTROL_SCENE_MODE_BEACH;
+ break;
+ }
+ case CANDLELIGHT: {
+ mode = CONTROL_SCENE_MODE_CANDLELIGHT;
+ break;
+ }
+ case FIREWORKS: {
+ mode = CONTROL_SCENE_MODE_FIREWORKS;
+ break;
+ }
+ // TODO: We cannot support HDR
+ case LANDSCAPE: {
+ mode = CONTROL_SCENE_MODE_LANDSCAPE;
+ break;
+ }
+ case NIGHT: {
+ mode = CONTROL_SCENE_MODE_NIGHT;
+ break;
+ }
+ // TODO: We cannot support NIGHT_PORTRAIT
+ case PARTY: {
+ mode = CONTROL_SCENE_MODE_PARTY;
+ break;
+ }
+ case PORTRAIT: {
+ mode = CONTROL_SCENE_MODE_PORTRAIT;
+ break;
+ }
+ case SNOW: {
+ mode = CONTROL_SCENE_MODE_SNOW;
+ break;
+ }
+ case SPORTS: {
+ mode = CONTROL_SCENE_MODE_SPORTS;
+ break;
+ }
+ case STEADYPHOTO: {
+ mode = CONTROL_SCENE_MODE_STEADYPHOTO;
+ break;
+ }
+ case SUNSET: {
+ mode = CONTROL_SCENE_MODE_SUNSET;
+ break;
+ }
+ case THEATRE: {
+ mode = CONTROL_SCENE_MODE_THEATRE;
+ break;
+ }
+ default: {
+ Log.w(TAG, "Unable to convert to API 2 scene mode: " + mCurrentSceneMode);
+ break;
+ }
+ }
+ }
+ mRequestSettings.set(CONTROL_SCENE_MODE, mode);
+ }
- // TODO: Initialize formats, too
+ private void updateRequestWhiteBalance() {
+ Integer mode = null;
+ if (mWhiteBalance != null) {
+ switch (mWhiteBalance) {
+ case AUTO: {
+ mode = CONTROL_AWB_MODE_AUTO;
+ break;
+ }
+ case CLOUDY_DAYLIGHT: {
+ mode = CONTROL_AWB_MODE_CLOUDY_DAYLIGHT;
+ break;
+ }
+ case DAYLIGHT: {
+ mode = CONTROL_AWB_MODE_DAYLIGHT;
+ break;
+ }
+ case FLUORESCENT: {
+ mode = CONTROL_AWB_MODE_FLUORESCENT;
+ break;
+ }
+ case INCANDESCENT: {
+ mode = CONTROL_AWB_MODE_INCANDESCENT;
+ break;
+ }
+ case SHADE: {
+ mode = CONTROL_AWB_MODE_SHADE;
+ break;
+ }
+ case TWILIGHT: {
+ mode = CONTROL_AWB_MODE_TWILIGHT;
+ break;
+ }
+ case WARM_FLUORESCENT: {
+ mode = CONTROL_AWB_MODE_WARM_FLUORESCENT;
+ break;
+ }
+ default: {
+ Log.w(TAG, "Unable to convert to API 2 white balance: " + mWhiteBalance);
+ break;
+ }
+ }
+ }
+ mRequestSettings.set(CONTROL_AWB_MODE, mode);
}
}
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 9bbbb7a..95f0320 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java
@@ -175,7 +175,7 @@ class AndroidCameraAgentImpl extends CameraAgent {
return mFirstFrontCameraId;
}
- private static class AndroidCharacteristics implements Characteristics {
+ private static class AndroidCharacteristics extends Characteristics {
private Camera.CameraInfo mCameraInfo;
AndroidCharacteristics(Camera.CameraInfo cameraInfo) {
@@ -437,7 +437,14 @@ class AndroidCameraAgentImpl extends CameraAgent {
}
case CameraActions.SET_DISPLAY_ORIENTATION: {
- mCamera.setDisplayOrientation(msg.arg1);
+ // Update preview orientation
+ mCamera.setDisplayOrientation(
+ mCharacteristics.getPreviewOrientation(msg.arg1));
+ // Only set the JPEG capture orientation if requested to do so; otherwise,
+ // capture in the sensor's physical orientation
+ mParamsToSet.setRotation(
+ msg.arg2 > 0 ? mCharacteristics.getJpegOrientation(msg.arg1) : 0);
+ mCamera.setParameters(mParamsToSet);
break;
}
@@ -566,7 +573,6 @@ class AndroidCameraAgentImpl extends CameraAgent {
// Should use settings.getCurrentZoomRatio() instead here.
mParamsToSet.setZoom(settings.getCurrentZoomIndex());
}
- mParamsToSet.setRotation((int) settings.getCurrentPhotoRotationDegrees());
mParamsToSet.setExposureCompensation(settings.getExposureCompensationIndex());
if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK)) {
mParamsToSet.setAutoExposureLock(settings.isAutoExposureLocked());
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraSettings.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraSettings.java
index a4039bd..ceab7fe 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraSettings.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraSettings.java
@@ -57,11 +57,19 @@ public class AndroidCameraSettings extends CameraSettings {
}
setRecordingHintEnabled(TRUE.equals(params.get(RECORDING_HINT)));
- // Output: Photo size, compression quality, rotation.
- setPhotoRotationDegrees(0f);
+ // Output: Photo size, compression quality
setPhotoJpegCompressionQuality(params.getJpegQuality());
Camera.Size paramPictureSize = params.getPictureSize();
setPhotoSize(new Size(paramPictureSize.width, paramPictureSize.height));
setPhotoFormat(params.getPictureFormat());
}
+
+ public AndroidCameraSettings(AndroidCameraSettings other) {
+ super(other);
+ }
+
+ @Override
+ public CameraSettings copy() {
+ return new AndroidCameraSettings(this);
+ }
}
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 d875cea..dd4f77c 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java
@@ -618,16 +618,32 @@ public abstract class CameraAgent {
CameraPictureCallback jpeg);
/**
- * Sets the display orientation for camera to adjust the preview orientation.
+ * Sets the display orientation for camera to adjust the preview and JPEG orientation.
*
- * @param degrees The rotation in degrees. Should be 0, 90, 180 or 270.
+ * @param degrees The counterclockwise rotation in degrees, relative to the device's natural
+ * orientation. Should be 0, 90, 180 or 270.
*/
public void setDisplayOrientation(final int degrees) {
+ setDisplayOrientation(degrees, true);
+ }
+
+ /**
+ * Sets the display orientation for camera to adjust the preview&mdash;and, optionally,
+ * JPEG&mdash;orientations.
+ * <p>If capture rotation is not requested, future captures will be returned in the sensor's
+ * physical rotation, which does not necessarily match the device's natural orientation.</p>
+ *
+ * @param degrees The counterclockwise rotation in degrees, relative to the device's natural
+ * orientation. Should be 0, 90, 180 or 270.
+ * @param capture Whether to adjust the JPEG capture orientation as well as the preview one.
+ */
+ public void setDisplayOrientation(final int degrees, final boolean capture) {
getDispatchThread().runJob(new Runnable() {
@Override
public void run() {
getCameraHandler()
- .obtainMessage(CameraActions.SET_DISPLAY_ORIENTATION, degrees, 0)
+ .obtainMessage(CameraActions.SET_DISPLAY_ORIENTATION, degrees,
+ capture ? 1 : 0)
.sendToTarget();
}});
}
@@ -713,7 +729,7 @@ public abstract class CameraAgent {
* @param statesToAwait Bitwise OR of the required camera states.
* @return Whether the settings can be applied.
*/
- protected boolean applySettingsHelper(final CameraSettings settings,
+ protected boolean applySettingsHelper(CameraSettings settings,
final int statesToAwait) {
if (settings == null) {
Log.v(TAG, "null parameters in applySettings()");
@@ -723,15 +739,14 @@ public abstract class CameraAgent {
return false;
}
- final CameraSettings copyOfSettings = new CameraSettings(settings);
+ final CameraSettings copyOfSettings = settings.copy();
getDispatchThread().runJob(new Runnable() {
@Override
public void run() {
getCameraState().waitForStates(statesToAwait);
getCameraHandler().obtainMessage(CameraActions.APPLY_SETTINGS, copyOfSettings)
.sendToTarget();
- }
- });
+ }});
return true;
}
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 9b7ca6e..16d70f9 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilities.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilities.java
@@ -97,6 +97,7 @@ public class CameraCapabilities {
* Focus is set at infinity.
* @see {@link android.hardware.Camera.Parameters#FOCUS_MODE_INFINITY}.
*/
+ // TODO: Unsupported on API 2
INFINITY,
/**
* Macro (close-up) focus mode.
@@ -182,6 +183,7 @@ public class CameraCapabilities {
* Capture a scene using high dynamic range imaging techniques.
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_HDR}.
*/
+ // TODO: Unsupported on API 2
HDR,
/**
* Take pictures on distant objects.
@@ -197,6 +199,7 @@ public class CameraCapabilities {
* Take people pictures at night.
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_NIGHT_PORTRAIT}.
*/
+ // TODO: Unsupported on API 2
NIGHT_PORTRAIT,
/**
* Take indoor low-light shot.
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraDeviceInfo.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraDeviceInfo.java
index ada1f29..a657170 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/CameraDeviceInfo.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraDeviceInfo.java
@@ -1,7 +1,25 @@
+/*
+ * 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;
+import com.android.ex.camera2.portability.debug.Log;
+
/**
* The device info for all attached cameras.
*/
@@ -35,27 +53,97 @@ public interface CameraDeviceInfo {
/**
* Device characteristics for a single camera.
*/
- public interface Characteristics {
+ public abstract class Characteristics {
+ private static final Log.Tag TAG = new Log.Tag("CamDvcInfChar");
+
/**
* @return Whether the camera faces the back of the device.
*/
- boolean isFacingBack();
+ public abstract boolean isFacingBack();
/**
* @return Whether the camera faces the device's screen.
*/
- boolean isFacingFront();
+ public abstract boolean isFacingFront();
/**
* @return The camera image orientation, or the clockwise rotation angle
* that must be applied to display it in its natural orientation
- * (in degrees, and always a multiple of 90).
+ * (in degrees, always a multiple of 90, and between [90,270]).
+ */
+ public abstract int getSensorOrientation();
+
+ /**
+ * @param currentDisplayOrientation
+ * The current display orientation, as measured clockwise from
+ * the device's natural orientation (in degrees, always a
+ * multiple of 90, and between 0 and 270, inclusive).
+ * @return
+ * The relative preview image orientation, or the clockwise
+ * rotation angle that must be applied to display preview
+ * frames in the matching orientation, accounting for implicit
+ * mirroring, if applicable (in degrees, always a multiple of
+ * 90, and between 0 and 270, inclusive).
*/
- int getSensorOrientation();
+ public int getPreviewOrientation(int currentDisplayOrientation) {
+ // Drivers tend to mirror the image during front camera preview.
+ return getRelativeImageOrientation(currentDisplayOrientation, true);
+ }
+
+ /**
+ * @param currentDisplayOrientation
+ * The current display orientation, as measured clockwise from
+ * the device's natural orientation (in degrees, always a
+ * multiple of 90, and between 0 and 270, inclusive).
+ * @return
+ * The relative capture image orientation, or the clockwise
+ * rotation angle that must be applied to display these frames
+ * in the matching orientation (in degrees, always a multiple
+ * of 90, and between 0 and 270, inclusive).
+ */
+ public int getJpegOrientation(int currentDisplayOrientation) {
+ // Don't mirror during capture!
+ return getRelativeImageOrientation(currentDisplayOrientation, false);
+ }
+
+ /**
+ * @param currentDisplayOrientaiton
+ * {@link #getPreviewOrientation}, {@link #getJpegOrientation}
+ * @param compensateForMirroring
+ * Whether to account for mirroring in the case of front-facing
+ * cameras, which is necessary iff the OS/driver is
+ * automatically reflecting the image.
+ * @return
+ * {@link #getPreviewOrientation}, {@link #getJpegOrientation}
+ *
+ * @see android.hardware.Camera.setDisplayOrientation
+ */
+ protected int getRelativeImageOrientation(int currentDisplayOrientation,
+ boolean compensateForMirroring) {
+ if (currentDisplayOrientation % 90 != 0) {
+ Log.e(TAG, "Provided display orientation is not divisible by 90");
+ }
+ if (currentDisplayOrientation < 0 || currentDisplayOrientation > 270) {
+ Log.e(TAG, "Provided display orientation is outside expected range");
+ }
+
+ int result = 0;
+ if (isFacingFront()) {
+ result = (getSensorOrientation() + currentDisplayOrientation) % 360;
+ if (compensateForMirroring) {
+ result = (360 - result) % 360;
+ }
+ } else if (isFacingBack()) {
+ result = (getSensorOrientation() - currentDisplayOrientation + 360) % 360;
+ } else {
+ Log.e(TAG, "Camera is facing unhandled direction");
+ }
+ return result;
+ }
/**
* @return Whether the shutter sound can be disabled.
*/
- boolean canDisableShutterSound();
+ public abstract boolean canDisableShutterSound();
}
}
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 3948b2e..26d0f85 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/CameraSettings.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraSettings.java
@@ -18,6 +18,8 @@ package com.android.ex.camera2.portability;
import android.hardware.Camera;
+import com.android.ex.camera2.portability.debug.Log;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -26,7 +28,12 @@ import java.util.TreeMap;
/**
* A class which stores the camera settings.
*/
-public class CameraSettings {
+public abstract class CameraSettings {
+ private static final Log.Tag TAG = new Log.Tag("CamSet");
+
+ // Attempts to provide a value outside this range will be ignored.
+ private static final int MIN_JPEG_COMPRESSION_QUALITY = 1;
+ private static final int MAX_JPEG_COMPRESSION_QUALITY = 100;
protected final Map<String, String> mGeneralSetting = new TreeMap<>();
protected final List<Camera.Area> mMeteringAreas = new ArrayList<>();
@@ -37,11 +44,10 @@ public class CameraSettings {
protected Size mCurrentPreviewSize;
private int mCurrentPreviewFormat;
protected Size mCurrentPhotoSize;
- protected int mJpegCompressQuality;
+ protected byte mJpegCompressQuality;
protected int mCurrentPhotoFormat;
protected float mCurrentZoomRatio;
protected int mCurrentZoomIndex;
- protected float mPhotoRotationDegrees;
protected int mExposureCompensationIndex;
protected CameraCapabilities.FlashMode mCurrentFlashMode;
protected CameraCapabilities.FocusMode mCurrentFocusMode;
@@ -96,7 +102,7 @@ public class CameraSettings {
* @param src The source settings.
* @return The copy of the source.
*/
- public CameraSettings(CameraSettings src) {
+ protected CameraSettings(CameraSettings src) {
mGeneralSetting.putAll(src.mGeneralSetting);
mMeteringAreas.addAll(src.mMeteringAreas);
mFocusAreas.addAll(src.mFocusAreas);
@@ -112,7 +118,6 @@ public class CameraSettings {
mCurrentPhotoFormat = src.mCurrentPhotoFormat;
mCurrentZoomRatio = src.mCurrentZoomRatio;
mCurrentZoomIndex = src.mCurrentZoomIndex;
- mPhotoRotationDegrees = src.mPhotoRotationDegrees;
mExposureCompensationIndex = src.mExposureCompensationIndex;
mCurrentFlashMode = src.mCurrentFlashMode;
mCurrentFocusMode = src.mCurrentFocusMode;
@@ -126,6 +131,11 @@ public class CameraSettings {
mExifThumbnailSize = src.mExifThumbnailSize;
}
+ /**
+ * @return A copy of this object, as an instance of the implementing class.
+ */
+ public abstract CameraSettings copy();
+
/** General setting **/
@Deprecated
public void setSetting(String key, String value) {
@@ -258,7 +268,12 @@ public class CameraSettings {
* @param quality The quality for JPEG.
*/
public void setPhotoJpegCompressionQuality(int quality) {
- mJpegCompressQuality = quality;
+ if (quality < MIN_JPEG_COMPRESSION_QUALITY || quality > MAX_JPEG_COMPRESSION_QUALITY) {
+ Log.w(TAG, "Ignoring JPEG quality that falls outside the expected range");
+ return;
+ }
+ // This is safe because the positive numbers go up to 127.
+ mJpegCompressQuality = (byte) quality;
}
public int getPhotoJpegCompressionQuality() {
@@ -296,22 +311,15 @@ public class CameraSettings {
mCurrentZoomIndex = index;
}
- /** Transformation **/
-
- public void setPhotoRotationDegrees(float photoRotationDegrees) {
- mPhotoRotationDegrees = photoRotationDegrees;
- }
-
- public float getCurrentPhotoRotationDegrees() {
- return mPhotoRotationDegrees;
- }
-
/** Exposure **/
public void setExposureCompensationIndex(int index) {
mExposureCompensationIndex = index;
}
+ /**
+ * @return The exposure compensation, with 0 meaning unadjusted.
+ */
public int getExposureCompensationIndex() {
return mExposureCompensationIndex;
}
diff --git a/camera2/portability/tests/Android.mk b/camera2/portability/tests/Android.mk
index 5bb09ef..f0b24e0 100644
--- a/camera2/portability/tests/Android.mk
+++ b/camera2/portability/tests/Android.mk
@@ -18,7 +18,8 @@ include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := android-ex-camera2-portability-tests
LOCAL_MODULE_TAGS := tests
LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-ex-camera2-portability android-support-test
+LOCAL_SRC_FILES := $(call all-java-files-under,src) $(call all-java-files-under,../../utils/tests)
+LOCAL_STATIC_JAVA_LIBRARIES := android-ex-camera2-portability android-ex-camera2-utils \
+ android-support-test mockito-target
include $(BUILD_PACKAGE)
diff --git a/camera2/portability/tests/AndroidManifest.xml b/camera2/portability/tests/AndroidManifest.xml
index 2e6a38b..65cf709 100644
--- a/camera2/portability/tests/AndroidManifest.xml
+++ b/camera2/portability/tests/AndroidManifest.xml
@@ -19,8 +19,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.ex.camera2.portability.tests">
<uses-permission android:name="android.permission.CAMERA" />
- <application android:label="CameraToo">
- <!--<uses-library android:name="android.test.runner" />-->
+ <application>
+ <uses-library android:name="android.test.runner" />
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.ex.camera2.portability.tests" />
diff --git a/camera2/portability/tests/src/com/android/ex/camera2/portability/Camera2PortabilityTest.java b/camera2/portability/tests/src/com/android/ex/camera2/portability/Camera2PortabilityTest.java
index b421b98..034fac7 100644
--- a/camera2/portability/tests/src/com/android/ex/camera2/portability/Camera2PortabilityTest.java
+++ b/camera2/portability/tests/src/com/android/ex/camera2/portability/Camera2PortabilityTest.java
@@ -16,45 +16,49 @@
package com.android.ex.camera2.portability;
-import static android.hardware.camera2.CameraCharacteristics.*;
+import static android.hardware.camera2.CaptureRequest.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
-import com.android.ex.camera2.portability.AndroidCamera2Capabilities.IntegralStringifier;
+import android.content.Context;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureRequest;
+import android.support.test.InjectContext;
+
import com.android.ex.camera2.portability.CameraCapabilities.FlashMode;
import com.android.ex.camera2.portability.CameraCapabilities.FocusMode;
import com.android.ex.camera2.portability.CameraCapabilities.SceneMode;
import com.android.ex.camera2.portability.CameraCapabilities.Stringifier;
import com.android.ex.camera2.portability.CameraCapabilities.WhiteBalance;
+import com.android.ex.camera2.utils.Camera2DeviceTester;
import org.junit.Test;
-import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
-public class Camera2PortabilityTest {
- private <E> void cameraCapabilitiesStringifierEach(Class<E> classy,
- Stringifier strfy,
- String call) throws Exception {
- for(E val : (E[]) classy.getMethod("values").invoke(null)) {
- String valString =
- (String) Stringifier.class.getMethod("stringify", classy).invoke(strfy, val);
- assertEquals(val,
- Stringifier.class.getMethod(call, String.class).invoke(strfy, valString));
- }
- }
-
+public class Camera2PortabilityTest extends Camera2DeviceTester {
@Test
- public void cameraCapabilitiesStringifier() throws Exception {
+ public void cameraCapabilitiesStringifier() {
Stringifier strfy = new Stringifier();
- cameraCapabilitiesStringifierEach(FocusMode.class, strfy, "focusModeFromString");
- cameraCapabilitiesStringifierEach(FlashMode.class, strfy, "flashModeFromString");
- cameraCapabilitiesStringifierEach(SceneMode.class, strfy, "sceneModeFromString");
- cameraCapabilitiesStringifierEach(WhiteBalance.class, strfy, "whiteBalanceFromString");
+ for(FocusMode val : FocusMode.values()) {
+ assertEquals(val, strfy.focusModeFromString(strfy.stringify(val)));
+ }
+ for(FlashMode val : FlashMode.values()) {
+ assertEquals(val, strfy.flashModeFromString(strfy.stringify(val)));
+ }
+ for(SceneMode val : SceneMode.values()) {
+ assertEquals(val, strfy.sceneModeFromString(strfy.stringify(val)));
+ }
+ for(WhiteBalance val : WhiteBalance.values()) {
+ assertEquals(val, strfy.whiteBalanceFromString(strfy.stringify(val)));
+ }
}
@Test
- public void cameraCapabilitiesStringifierNull() throws Exception {
+ public void cameraCapabilitiesStringifierNull() {
Stringifier strfy = new Stringifier();
assertEquals(strfy.focusModeFromString(null), FocusMode.AUTO);
assertEquals(strfy.flashModeFromString(null), FlashMode.NO_FLASH);
@@ -63,7 +67,7 @@ public class Camera2PortabilityTest {
}
@Test
- public void cameraCapabilitiesStringifierInvalid() throws Exception {
+ public void cameraCapabilitiesStringifierInvalid() {
Stringifier strfy = new Stringifier();
assertEquals(strfy.focusModeFromString("crap"), FocusMode.AUTO);
assertEquals(strfy.flashModeFromString("crap"), FlashMode.NO_FLASH);
@@ -71,72 +75,93 @@ public class Camera2PortabilityTest {
assertEquals(strfy.whiteBalanceFromString("crap"), WhiteBalance.AUTO);
}
- private void cameraCapabilitiesIntifierEach(int apiVal,
- IntegralStringifier intfy,
- String call) throws Exception {
- Method toCall = IntegralStringifier.class.getMethod(call, int.class);
- Class<?> returnType = toCall.getReturnType();
- Object returnVal = toCall.invoke(intfy, apiVal);
- assertEquals(apiVal,
- IntegralStringifier.class.getMethod("intify", returnType).invoke(intfy, returnVal));
+ private CameraCharacteristics buildFrameworkCharacteristics() throws CameraAccessException {
+ CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+ String id = manager.getCameraIdList()[0];
+ return manager.getCameraCharacteristics(id);
+ }
+
+ private void camera2SettingsCheckSingleOption(AndroidCamera2Settings setts,
+ Key<?> apiKey, int apiVal) {
+ assertEquals(apiVal, setts.getRequestSettings().get(apiKey));
}
@Test
- public void cameraCapabilitiesIntifier() throws Exception {
- IntegralStringifier intstr = new IntegralStringifier();
+ public void camera2SettingsSetOptionsAndGetRequestSettings() throws CameraAccessException {
+ AndroidCamera2Settings set = new AndroidCamera2Settings(
+ mCamera, CameraDevice.TEMPLATE_PREVIEW, null, null, null);
// Focus modes
- cameraCapabilitiesIntifierEach(CONTROL_AF_MODE_AUTO, intstr, "focusModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_AF_MODE_CONTINUOUS_PICTURE, intstr,
- "focusModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_AF_MODE_CONTINUOUS_VIDEO, intstr,
- "focusModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_AF_MODE_EDOF, intstr, "focusModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_AF_MODE_OFF, intstr, "focusModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_AF_MODE_MACRO, intstr, "focusModeFromInt");
-
- // Flash modes
- cameraCapabilitiesIntifierEach(FLASH_MODE_OFF, intstr, "flashModeFromInt");
- cameraCapabilitiesIntifierEach(FLASH_MODE_SINGLE, intstr, "flashModeFromInt");
- cameraCapabilitiesIntifierEach(FLASH_MODE_TORCH, intstr, "flashModeFromInt");
+ set.setFocusMode(FocusMode.AUTO);
+ camera2SettingsCheckSingleOption(set, CONTROL_AF_MODE, CONTROL_AF_MODE_AUTO);
+ set.setFocusMode(FocusMode.CONTINUOUS_PICTURE);
+ camera2SettingsCheckSingleOption(set, CONTROL_AF_MODE, CONTROL_AF_MODE_CONTINUOUS_PICTURE);
+ set.setFocusMode(FocusMode.CONTINUOUS_VIDEO);
+ camera2SettingsCheckSingleOption(set, CONTROL_AF_MODE, CONTROL_AF_MODE_CONTINUOUS_VIDEO);
+ set.setFocusMode(FocusMode.EXTENDED_DOF);
+ camera2SettingsCheckSingleOption(set, CONTROL_AF_MODE, CONTROL_AF_MODE_EDOF);
+ set.setFocusMode(FocusMode.FIXED);
+ camera2SettingsCheckSingleOption(set, CONTROL_AF_MODE, CONTROL_AF_MODE_OFF);
+ set.setFocusMode(FocusMode.MACRO);
+ camera2SettingsCheckSingleOption(set, CONTROL_AF_MODE, CONTROL_AF_MODE_MACRO);
// Scene modes
- cameraCapabilitiesIntifierEach(CONTROL_SCENE_MODE_DISABLED, intstr, "sceneModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_SCENE_MODE_ACTION, intstr, "sceneModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_SCENE_MODE_BARCODE, intstr, "sceneModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_SCENE_MODE_BEACH, intstr, "sceneModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_SCENE_MODE_CANDLELIGHT, intstr, "sceneModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_SCENE_MODE_FIREWORKS, intstr, "sceneModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_SCENE_MODE_LANDSCAPE, intstr, "sceneModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_SCENE_MODE_NIGHT, intstr, "sceneModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_SCENE_MODE_PARTY, intstr, "sceneModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_SCENE_MODE_PORTRAIT, intstr, "sceneModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_SCENE_MODE_SNOW, intstr, "sceneModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_SCENE_MODE_SPORTS, intstr, "sceneModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_SCENE_MODE_STEADYPHOTO, intstr, "sceneModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_SCENE_MODE_SUNSET, intstr, "sceneModeFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_SCENE_MODE_THEATRE, intstr, "sceneModeFromInt");
+ set.setSceneMode(SceneMode.AUTO);
+ camera2SettingsCheckSingleOption(set, CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_DISABLED);
+ set.setSceneMode(SceneMode.ACTION);
+ camera2SettingsCheckSingleOption(set, CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_ACTION);
+ set.setSceneMode(SceneMode.BARCODE);
+ camera2SettingsCheckSingleOption(set, CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_BARCODE);
+ set.setSceneMode(SceneMode.BEACH);
+ camera2SettingsCheckSingleOption(set, CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_BEACH);
+ set.setSceneMode(SceneMode.CANDLELIGHT);
+ camera2SettingsCheckSingleOption(set, CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_CANDLELIGHT);
+ set.setSceneMode(SceneMode.FIREWORKS);
+ camera2SettingsCheckSingleOption(set, CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_FIREWORKS);
+ set.setSceneMode(SceneMode.LANDSCAPE);
+ camera2SettingsCheckSingleOption(set, CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_LANDSCAPE);
+ set.setSceneMode(SceneMode.NIGHT);
+ camera2SettingsCheckSingleOption(set, CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_NIGHT);
+ set.setSceneMode(SceneMode.PARTY);
+ camera2SettingsCheckSingleOption(set, CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_PARTY);
+ set.setSceneMode(SceneMode.PORTRAIT);
+ camera2SettingsCheckSingleOption(set, CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_PORTRAIT);
+ set.setSceneMode(SceneMode.SNOW);
+ camera2SettingsCheckSingleOption(set, CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_SNOW);
+ set.setSceneMode(SceneMode.SPORTS);
+ camera2SettingsCheckSingleOption(set, CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_SPORTS);
+ set.setSceneMode(SceneMode.STEADYPHOTO);
+ camera2SettingsCheckSingleOption(set, CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_STEADYPHOTO);
+ set.setSceneMode(SceneMode.SUNSET);
+ camera2SettingsCheckSingleOption(set, CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_SUNSET);
+ set.setSceneMode(SceneMode.THEATRE);
+ camera2SettingsCheckSingleOption(set, CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_THEATRE);
// White balances
- cameraCapabilitiesIntifierEach(CONTROL_AWB_MODE_AUTO, intstr, "whiteBalanceFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_AWB_MODE_CLOUDY_DAYLIGHT, intstr,
- "whiteBalanceFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_AWB_MODE_DAYLIGHT, intstr, "whiteBalanceFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_AWB_MODE_FLUORESCENT, intstr,
- "whiteBalanceFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_AWB_MODE_INCANDESCENT, intstr,
- "whiteBalanceFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_AWB_MODE_SHADE, intstr, "whiteBalanceFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_AWB_MODE_TWILIGHT, intstr, "whiteBalanceFromInt");
- cameraCapabilitiesIntifierEach(CONTROL_AWB_MODE_WARM_FLUORESCENT, intstr,
- "whiteBalanceFromInt");
+ set.setWhiteBalance(WhiteBalance.AUTO);
+ camera2SettingsCheckSingleOption(set, CONTROL_AWB_MODE, CONTROL_AWB_MODE_AUTO);
+ set.setWhiteBalance(WhiteBalance.CLOUDY_DAYLIGHT);
+ camera2SettingsCheckSingleOption(set, CONTROL_AWB_MODE, CONTROL_AWB_MODE_CLOUDY_DAYLIGHT);
+ set.setWhiteBalance(WhiteBalance.DAYLIGHT);
+ camera2SettingsCheckSingleOption(set, CONTROL_AWB_MODE, CONTROL_AWB_MODE_DAYLIGHT);
+ set.setWhiteBalance(WhiteBalance.FLUORESCENT);
+ camera2SettingsCheckSingleOption(set, CONTROL_AWB_MODE, CONTROL_AWB_MODE_FLUORESCENT);
+ set.setWhiteBalance(WhiteBalance.INCANDESCENT);
+ camera2SettingsCheckSingleOption(set, CONTROL_AWB_MODE, CONTROL_AWB_MODE_INCANDESCENT);
+ set.setWhiteBalance(WhiteBalance.SHADE);
+ camera2SettingsCheckSingleOption(set, CONTROL_AWB_MODE, CONTROL_AWB_MODE_SHADE);
+ set.setWhiteBalance(WhiteBalance.TWILIGHT);
+ camera2SettingsCheckSingleOption(set, CONTROL_AWB_MODE, CONTROL_AWB_MODE_TWILIGHT);
+ set.setWhiteBalance(WhiteBalance.WARM_FLUORESCENT);
+ camera2SettingsCheckSingleOption(set, CONTROL_AWB_MODE, CONTROL_AWB_MODE_WARM_FLUORESCENT);
}
- // TODO: Add a test checking whether stringification matches API representation
+ // TODO: Add a test checking whether stringification matches API 1 representation
@Test
- public void cameraCapabilitiesIntsMatchApi2Representations() throws Exception {
- IntegralStringifier intstr = new IntegralStringifier();
+ public void camera2CapabilitiesFocusModeFromInt() throws CameraAccessException {
+ CameraCharacteristics chars = buildFrameworkCharacteristics();
+ AndroidCamera2Capabilities intstr = new AndroidCamera2Capabilities(chars);
// Focus modes
assertEquals(intstr.focusModeFromInt(CONTROL_AF_MODE_AUTO), FocusMode.AUTO);
@@ -148,18 +173,12 @@ public class Camera2PortabilityTest {
assertEquals(intstr.focusModeFromInt(CONTROL_AF_MODE_OFF), FocusMode.FIXED);
assertEquals(intstr.focusModeFromInt(CONTROL_AF_MODE_MACRO), FocusMode.MACRO);
- // Flash modes
- assertEquals(intstr.flashModeFromInt(FLASH_MODE_OFF), FlashMode.OFF);
- assertEquals(intstr.flashModeFromInt(FLASH_MODE_SINGLE), FlashMode.ON);
- assertEquals(intstr.flashModeFromInt(FLASH_MODE_TORCH), FlashMode.TORCH);
-
// Scene modes
assertEquals(intstr.sceneModeFromInt(CONTROL_SCENE_MODE_DISABLED), SceneMode.AUTO);
assertEquals(intstr.sceneModeFromInt(CONTROL_SCENE_MODE_ACTION), SceneMode.ACTION);
assertEquals(intstr.sceneModeFromInt(CONTROL_SCENE_MODE_BARCODE), SceneMode.BARCODE);
assertEquals(intstr.sceneModeFromInt(CONTROL_SCENE_MODE_BEACH), SceneMode.BEACH);
- assertEquals(intstr.sceneModeFromInt(CONTROL_SCENE_MODE_CANDLELIGHT),
- SceneMode.CANDLELIGHT);
+ assertEquals(intstr.sceneModeFromInt(CONTROL_SCENE_MODE_CANDLELIGHT), SceneMode.CANDLELIGHT);
assertEquals(intstr.sceneModeFromInt(CONTROL_SCENE_MODE_FIREWORKS), SceneMode.FIREWORKS);
assertEquals(intstr.sceneModeFromInt(CONTROL_SCENE_MODE_LANDSCAPE), SceneMode.LANDSCAPE);
assertEquals(intstr.sceneModeFromInt(CONTROL_SCENE_MODE_NIGHT), SceneMode.NIGHT);
diff --git a/camera2/utils/tests/src/com/android/ex/camera2/utils/Camera2DeviceTester.java b/camera2/utils/tests/src/com/android/ex/camera2/utils/Camera2DeviceTester.java
new file mode 100644
index 0000000..4db6dfb
--- /dev/null
+++ b/camera2/utils/tests/src/com/android/ex/camera2/utils/Camera2DeviceTester.java
@@ -0,0 +1,93 @@
+/*
+ * 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.utils;
+
+import android.content.Context;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.support.test.InjectContext;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+
+/**
+ * Subclasses of this have an {@code mCamera} instance variable representing the first camera.
+ */
+public class Camera2DeviceTester {
+ private static HandlerThread sThread;
+
+ private static Handler sHandler;
+
+ @BeforeClass
+ public static void setupBackgroundHandler() {
+ sThread = new HandlerThread("CameraFramework");
+ sThread.start();
+ sHandler = new Handler(sThread.getLooper());
+ }
+
+ @AfterClass
+ public static void teardownBackgroundHandler() throws Exception {
+ sThread.quitSafely();
+ sThread.join();
+ }
+
+ @InjectContext
+ public Context mContext;
+
+ private class DeviceCapturer extends CameraDevice.StateListener {
+ private CameraDevice mCamera;
+
+ public CameraDevice captureCameraDevice() throws Exception {
+ CameraManager manager =
+ (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+ String id = manager.getCameraIdList()[0];
+ synchronized (this) {
+ manager.openCamera(id, this, sHandler);
+ wait();
+ }
+ return mCamera;
+ }
+
+ @Override
+ public synchronized void onOpened(CameraDevice camera) {
+ mCamera = camera;
+ notify();
+ }
+
+ @Override
+ public void onDisconnected(CameraDevice camera) {}
+
+ @Override
+ public void onError(CameraDevice camera, int error) {}
+ }
+
+ protected CameraDevice mCamera;
+
+ @Before
+ public void obtainCameraCaptureRequestBuilderFactory() throws Exception {
+ mCamera = new DeviceCapturer().captureCameraDevice();
+ }
+
+ @After
+ public void releaseCameraCaptureRequestBuilderFactory() {
+ mCamera.close();
+ }
+}
diff --git a/camera2/utils/tests/src/com/android/ex/camera2/utils/Camera2UtilsTest.java b/camera2/utils/tests/src/com/android/ex/camera2/utils/Camera2UtilsTest.java
index dd9566b..bb23e37 100644
--- a/camera2/utils/tests/src/com/android/ex/camera2/utils/Camera2UtilsTest.java
+++ b/camera2/utils/tests/src/com/android/ex/camera2/utils/Camera2UtilsTest.java
@@ -23,24 +23,15 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import android.content.Context;
import android.hardware.camera2.CameraCaptureSession.CaptureListener;
import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureRequest.Key;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.support.test.InjectContext;
import android.view.Surface;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
-public class Camera2UtilsTest {
+public class Camera2UtilsTest extends Camera2DeviceTester {
private void captureListenerSplitterAllCallbacksReceived(CaptureListener splitter,
CaptureListener... terminals) {
splitter.onCaptureCompleted(null, null, null);
@@ -152,65 +143,6 @@ public class Camera2UtilsTest {
assertFalse(setUp.contains(CaptureRequest.CONTROL_AE_LOCK));
}
- private static HandlerThread sThread;
-
- private static Handler sHandler;
-
- @BeforeClass
- public static void setupBackgroundHandler() {
- sThread = new HandlerThread("CameraFramework");
- sThread.start();
- sHandler = new Handler(sThread.getLooper());
- }
-
- @AfterClass
- public static void teardownBackgroundHandler() throws Exception {
- sThread.quitSafely();
- sThread.join();
- }
-
- @InjectContext
- public Context mContext;
-
- public class DeviceCapturer extends CameraDevice.StateListener {
- private CameraDevice mCamera;
-
- public CameraDevice captureCameraDevice() throws Exception {
- CameraManager manager =
- (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
- String id = manager.getCameraIdList()[0];
- synchronized (this) {
- manager.openCamera(id, this, sHandler);
- wait();
- }
- return mCamera;
- }
-
- @Override
- public synchronized void onOpened(CameraDevice camera) {
- mCamera = camera;
- notify();
- }
-
- @Override
- public void onDisconnected(CameraDevice camera) {}
-
- @Override
- public void onError(CameraDevice camera, int error) {}
- }
-
- private CameraDevice mCamera;
-
- @Before
- public void obtainCameraCaptureRequestBuilderFactory() throws Exception {
- mCamera = new DeviceCapturer().captureCameraDevice();
- }
-
- @After
- public void releaseCameraCaptureRequestBuilderFactory() {
- mCamera.close();
- }
-
@Test
public void requestSettingsSetStartsWithoutChanges() {
Camera2RequestSettingsSet setUp = new Camera2RequestSettingsSet();