diff options
author | Zhijun He <zhijunhe@google.com> | 2013-10-02 11:39:43 -0700 |
---|---|---|
committer | Zhijun He <zhijunhe@google.com> | 2013-10-07 08:02:25 -0700 |
commit | b7bfdc7cf7f45805e8e7ebea77a15051b8ad3e8d (patch) | |
tree | 4d0780b06d0c60095e1c99c9cb7f46c812843a43 /core/java/android/hardware | |
parent | d5102f19653f6b00ce872435c5efb5cb630deed7 (diff) | |
download | frameworks_base-b7bfdc7cf7f45805e8e7ebea77a15051b8ad3e8d.zip frameworks_base-b7bfdc7cf7f45805e8e7ebea77a15051b8ad3e8d.tar.gz frameworks_base-b7bfdc7cf7f45805e8e7ebea77a15051b8ad3e8d.tar.bz2 |
Camera2: add metadata override
There are interface difference between managed and native side for some
metadata, like Face and JPEG format. we need override them when they are set/get
between native and managed sides.
Also fix some issues in the CameraMetadataTest.
Bug: 10406212
Change-Id: Ibd8be0415968445e711b3ea89f45e1edfa193ee2
Diffstat (limited to 'core/java/android/hardware')
3 files changed, 179 insertions, 32 deletions
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index c9626f1..30bffc4 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -60,11 +60,6 @@ public final class CaptureResult extends CameraMetadata { @Override public <T> T get(Key<T> key) { - if (key == STATISTICS_FACES) { // Don't throw IllegalArgumentException - // TODO: Implement android.statistics.faces - return null; - } - return mResults.get(key); } diff --git a/core/java/android/hardware/camera2/Face.java b/core/java/android/hardware/camera2/Face.java index ef068ca..4ac04dd 100644 --- a/core/java/android/hardware/camera2/Face.java +++ b/core/java/android/hardware/camera2/Face.java @@ -58,6 +58,9 @@ public final class Face { * Create a new face with all fields set. * * <p>The id, leftEyePosition, rightEyePosition, and mouthPosition are considered optional. + * They are only required when the {@link #CaptureResult} reports that the value of key + * {@link CaptureResult#STATISTICS_FACE_DETECT_MODE} is + * {@link CameraMetadata#STATISTICS_FACE_DETECT_MODE_FULL}. * If the id is {@value #ID_UNSUPPORTED} then the leftEyePosition, rightEyePosition, and * mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition, * rightEyePosition, and mouthPosition may be independently null or not-null.</p> @@ -107,7 +110,11 @@ public final class Face { * <p>The id, leftEyePosition, rightEyePosition, and mouthPosition are considered optional. * If the id is {@value #ID_UNSUPPORTED} then the leftEyePosition, rightEyePosition, and * mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition, - * rightEyePosition, and mouthPosition may be independently null or not-null.</p> + * rightEyePosition, and mouthPosition may be independently null or not-null. When devices + * report the value of key {@link CaptureResult#STATISTICS_FACE_DETECT_MODE} as + * {@link CameraMetadata#STATISTICS_FACE_DETECT_MODE_SIMPLE} in {@link #CaptureResult}, + * the face id of each face is expected to be {@value #ID_UNSUPPORTED}, the leftEyePosition, + * rightEyePosition, and mouthPositions are expected to be {@code null} for each face.</p> * * @param bounds Bounds of the face. * @param score Confidence level between {@value #SCORE_MIN}-{@value #SCORE_MAX}. @@ -168,7 +175,10 @@ public final class Face { * <p>This is an optional field, may not be supported on all devices. * If the id is {@value #ID_UNSUPPORTED} then the leftEyePosition, rightEyePosition, and * mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition, - * rightEyePosition, and mouthPosition may be independently null or not-null.</p> + * rightEyePosition, and mouthPosition may be independently null or not-null. When devices + * report the value of key {@link CaptureResult#STATISTICS_FACE_DETECT_MODE} as + * {@link CameraMetadata#STATISTICS_FACE_DETECT_MODE_SIMPLE} in {@link #CaptureResult}, + * the face id of each face is expected to be {@value #ID_UNSUPPORTED}.</p> * * <p>This value will either be {@value #ID_UNSUPPORTED} or * otherwise greater than {@code 0}.</p> @@ -219,7 +229,7 @@ public final class Face { * field, may not be supported on all devices. If not * supported, the value will always be set to null. * This value will always be null only if {@link #getId()} returns - * {@value #ID_UNSUPPORTED}.</p> them are. + * {@value #ID_UNSUPPORTED}.</p> * </p> * * @return The mouth position, or {@code null} if unknown. diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 89ffd93..eb82ed4 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -16,7 +16,13 @@ package android.hardware.camera2.impl; +import android.graphics.ImageFormat; +import android.graphics.Point; +import android.graphics.Rect; +import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraMetadata; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.Face; import android.hardware.camera2.Rational; import android.os.Parcelable; import android.os.Parcel; @@ -36,6 +42,8 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { private static final String TAG = "CameraMetadataJV"; private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); + // this should be in sync with HAL_PIXEL_FORMAT_BLOB defined in graphics.h + private static final int NATIVE_JPEG_FORMAT = 0x21; public CameraMetadataNative() { super(); @@ -84,16 +92,12 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { @SuppressWarnings("unchecked") @Override public <T> T get(Key<T> key) { - int tag = key.getTag(); - byte[] values = readValues(tag); - if (values == null) { - return null; + T value = getOverride(key); + if (value != null) { + return value; } - int nativeType = getNativeType(tag); - - ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); - return unpackSingle(buffer, key.getType(), nativeType); + return getBase(key); } public void readFromParcel(Parcel in) { @@ -110,24 +114,11 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { * type to the key. */ public <T> void set(Key<T> key, T value) { - int tag = key.getTag(); - - if (value == null) { - writeValues(tag, null); + if (setOverride(key, value)) { return; } - int nativeType = getNativeType(tag); - - int size = packSingle(value, null, key.getType(), nativeType, /* sizeOnly */true); - - // TODO: Optimization. Cache the byte[] and reuse if the size is big enough. - byte[] values = new byte[size]; - - ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); - packSingle(value, buffer, key.getType(), nativeType, /*sizeOnly*/false); - - writeValues(tag, values); + setBase(key, value); } // Keep up-to-date with camera_metadata.h @@ -435,6 +426,157 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { return (T) array; } + private <T> T getBase(Key<T> key) { + int tag = key.getTag(); + byte[] values = readValues(tag); + if (values == null) { + return null; + } + + int nativeType = getNativeType(tag); + + ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); + return unpackSingle(buffer, key.getType(), nativeType); + } + + // Need overwrite some metadata that has different definitions between native + // and managed sides. + @SuppressWarnings("unchecked") + private <T> T getOverride(Key<T> key) { + if (key == CameraCharacteristics.SCALER_AVAILABLE_FORMATS) { + return (T) getAvailableFormats(); + } else if (key == CaptureResult.STATISTICS_FACES) { + return (T) getFaces(); + } + + // For other keys, get() falls back to getBase() + return null; + } + + private int[] getAvailableFormats() { + int[] availableFormats = getBase(CameraCharacteristics.SCALER_AVAILABLE_FORMATS); + for (int i = 0; i < availableFormats.length; i++) { + // JPEG has different value between native and managed side, need override. + if (availableFormats[i] == NATIVE_JPEG_FORMAT) { + availableFormats[i] = ImageFormat.JPEG; + } + } + return availableFormats; + } + + private Face[] getFaces() { + final int FACE_LANDMARK_SIZE = 6; + + Integer faceDetectMode = getBase(CaptureResult.STATISTICS_FACE_DETECT_MODE); + if (faceDetectMode == null) { + throw new AssertionError("Expect non-null face detect mode"); + } + + if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_OFF) { + return new Face[0]; + } + if (faceDetectMode != CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE && + faceDetectMode != CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) { + throw new AssertionError("Unknown face detect mode: " + faceDetectMode); + } + + // Face scores and rectangles are required by SIMPLE and FULL mode. + byte[] faceScores = getBase(CaptureResult.STATISTICS_FACE_SCORES); + Rect[] faceRectangles = getBase(CaptureResult.STATISTICS_FACE_RECTANGLES); + if (faceScores == null || faceRectangles == null) { + throw new AssertionError("Expect face scores and rectangles to be non-null"); + } else if (faceScores.length != faceRectangles.length) { + throw new AssertionError( + String.format("Face score size(%d) doesn match face rectangle size(%d)!", + faceScores.length, faceRectangles.length)); + } + + // Face id and landmarks are only required by FULL mode. + int[] faceIds = getBase(CaptureResult.STATISTICS_FACE_IDS); + int[] faceLandmarks = getBase(CaptureResult.STATISTICS_FACE_LANDMARKS); + int numFaces = faceScores.length; + if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) { + if (faceIds == null || faceLandmarks == null) { + throw new AssertionError("Expect face ids and landmarks to be non-null for " + + "FULL mode"); + } else if (faceIds.length != numFaces || + faceLandmarks.length != numFaces * FACE_LANDMARK_SIZE) { + throw new AssertionError( + String.format("Face id size(%d), or face landmark size(%d) don't match " + + "face number(%d)!", + faceIds.length, faceLandmarks.length * FACE_LANDMARK_SIZE, + numFaces)); + } + } + + Face[] faces = new Face[numFaces]; + if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE) { + for (int i = 0; i < numFaces; i++) { + faces[i] = new Face(faceRectangles[i], faceScores[i]); + } + } else { + // CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL + for (int i = 0; i < numFaces; i++) { + Point leftEye = new Point(faceLandmarks[i*6], faceLandmarks[i*6+1]); + Point rightEye = new Point(faceLandmarks[i*6+2], faceLandmarks[i*6+3]); + Point mouth = new Point(faceLandmarks[i*6+4], faceLandmarks[i*6+5]); + faces[i] = new Face(faceRectangles[i], faceScores[i], faceIds[i], + leftEye, rightEye, mouth); + } + } + return faces; + } + + private <T> void setBase(Key<T> key, T value) { + int tag = key.getTag(); + + if (value == null) { + writeValues(tag, null); + return; + } + + int nativeType = getNativeType(tag); + + int size = packSingle(value, null, key.getType(), nativeType, /* sizeOnly */true); + + // TODO: Optimization. Cache the byte[] and reuse if the size is big enough. + byte[] values = new byte[size]; + + ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); + packSingle(value, buffer, key.getType(), nativeType, /*sizeOnly*/false); + + writeValues(tag, values); + } + + // Set the camera metadata override. + private <T> boolean setOverride(Key<T> key, T value) { + if (key == CameraCharacteristics.SCALER_AVAILABLE_FORMATS) { + return setAvailableFormats((int[]) value); + } + + // For other keys, set() falls back to setBase(). + return false; + } + + private boolean setAvailableFormats(int[] value) { + int[] availableFormat = value; + if (value == null) { + // Let setBase() to handle the null value case. + return false; + } + + int[] newValues = new int[availableFormat.length]; + for (int i = 0; i < availableFormat.length; i++) { + newValues[i] = availableFormat[i]; + if (availableFormat[i] == ImageFormat.JPEG) { + newValues[i] = NATIVE_JPEG_FORMAT; + } + } + + setBase(CameraCharacteristics.SCALER_AVAILABLE_FORMATS, newValues); + return true; + } + private long mMetadataPtr; // native CameraMetadata* private native long nativeAllocate(); @@ -538,7 +680,7 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { * @hide */ public byte[] readValues(int tag) { - // TODO: Optimization. Native code returns a ByteBuffer instead. + // TODO: Optimization. Native code returns a ByteBuffer instead. return nativeReadValues(tag); } |