summaryrefslogtreecommitdiffstats
path: root/camera2/portability/src/com/android/ex
diff options
context:
space:
mode:
Diffstat (limited to 'camera2/portability/src/com/android/ex')
-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.java405
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java463
-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.java5
-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
9 files changed, 953 insertions, 412 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
index 9e1fa51..fc19aed 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;
@@ -228,13 +242,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;
@@ -267,8 +285,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);
@@ -321,7 +341,7 @@ class AndroidCamera2AgentImpl extends CameraAgent {
}*/
case CameraActions.APPLY_SETTINGS: {
- CameraSettings settings = (CameraSettings) msg.obj;
+ AndroidCamera2Settings settings = (AndroidCamera2Settings) msg.obj;
applyToRequest(settings);
break;
}
@@ -367,17 +387,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;
}
@@ -391,18 +413,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;
}
@@ -433,15 +457,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
@@ -476,8 +563,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;
+ }
}
/**
@@ -489,34 +581,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);
@@ -527,46 +604,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!
@@ -591,14 +628,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);
@@ -627,10 +669,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) {
@@ -725,6 +769,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
@@ -841,15 +916,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
@@ -887,6 +997,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);
@@ -1000,7 +1119,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..51c1422 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Capabilities.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Capabilities.java
@@ -18,14 +18,14 @@ package com.android.ex.camera2.portability;
import static android.hardware.camera2.CameraCharacteristics.*;
+import android.graphics.ImageFormat;
import android.graphics.Point;
+import android.graphics.SurfaceTexture;
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;
@@ -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);
@@ -50,20 +47,20 @@ public class AndroidCamera2Capabilities extends CameraCapabilities {
mSupportedPreviewFpsRange.add(new int[] { fpsRange.getLower(), fpsRange.getUpper() });
}
- // TODO: We only support SurfaceView preview rendering
+ // TODO: We only support TextureView preview rendering
mSupportedPreviewSizes.addAll(Size.buildListFromAndroidSizes(Arrays.asList(
- s.getOutputSizes(SurfaceHolder.class))));
+ s.getOutputSizes(SurfaceTexture.class))));
for (int format : s.getOutputFormats()) {
mSupportedPreviewFormats.add(format);
}
- // TODO: We only support MediaRecorder videos capture
+ // TODO: We only support MediaRecorder video capture
mSupportedVideoSizes.addAll(Size.buildListFromAndroidSizes(Arrays.asList(
s.getOutputSizes(MediaRecorder.class))));
- // TODO: We only support ImageReader image capture
+ // TODO: We only support JPEG image capture
mSupportedPhotoSizes.addAll(Size.buildListFromAndroidSizes(Arrays.asList(
- s.getOutputSizes(ImageReader.class))));
+ s.getOutputSizes(ImageFormat.JPEG))));
mSupportedPhotoFormats.addAll(mSupportedPreviewFormats);
buildSceneModes(p);
@@ -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..efa68e8 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,457 @@
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();
+ Integer currentFocusMode = mTemplateSettings.get(CONTROL_AF_MODE);
+ if (currentFocusMode != null) {
+ mCurrentFocusMode = AndroidCamera2Capabilities.focusModeFromInt(currentFocusMode);
+ }
+ Integer currentSceneMode = mTemplateSettings.get(CONTROL_SCENE_MODE);
+ if (currentSceneMode != null) {
+ mCurrentSceneMode = AndroidCamera2Capabilities.sceneModeFromInt(currentSceneMode);
+ }
+ Integer whiteBalance = mTemplateSettings.get(CONTROL_AWB_MODE);
+ if (whiteBalance != null) {
+ mWhiteBalance = AndroidCamera2Capabilities.whiteBalanceFromInt(whiteBalance);
+ }
+
+ 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 5afbd15..6885cee 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) {
@@ -438,7 +438,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;
}
@@ -567,7 +574,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..6a4c72c 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.
@@ -485,6 +488,8 @@ public class CameraCapabilities {
mMaxNumOfFacesSupported = src.mMaxNumOfFacesSupported;
mMaxNumOfFocusAreas = src.mMaxNumOfFocusAreas;
mMaxNumOfMeteringArea = src.mMaxNumOfMeteringArea;
+ mMaxZoomIndex = src.mMaxZoomIndex;
+ mZoomRatioList.addAll(src.mZoomRatioList);
mMaxZoomRatio = src.mMaxZoomRatio;
mHorizontalViewAngle = src.mHorizontalViewAngle;
mVerticalViewAngle = src.mVerticalViewAngle;
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;
}