summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/hardware/photography/CameraMetadata.java91
-rw-r--r--core/java/android/hardware/photography/impl/MetadataMarshalClass.java67
-rw-r--r--core/java/android/hardware/photography/impl/MetadataMarshalRect.java68
-rw-r--r--core/java/android/hardware/photography/impl/MetadataMarshalSize.java61
-rw-r--r--core/java/android/hardware/photography/impl/MetadataMarshalString.java81
-rw-r--r--core/jni/android_hardware_photography_CameraMetadata.cpp10
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java85
7 files changed, 454 insertions, 9 deletions
diff --git a/core/java/android/hardware/photography/CameraMetadata.java b/core/java/android/hardware/photography/CameraMetadata.java
index 4633b2f..c024c05 100644
--- a/core/java/android/hardware/photography/CameraMetadata.java
+++ b/core/java/android/hardware/photography/CameraMetadata.java
@@ -16,6 +16,10 @@
package android.hardware.photography;
+import android.hardware.photography.impl.MetadataMarshalClass;
+import android.hardware.photography.impl.MetadataMarshalRect;
+import android.hardware.photography.impl.MetadataMarshalSize;
+import android.hardware.photography.impl.MetadataMarshalString;
import android.os.Parcelable;
import android.os.Parcel;
import android.util.Log;
@@ -85,6 +89,11 @@ public class CameraMetadata implements Parcelable, AutoCloseable {
public <T> void set(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.mType, nativeType, /* sizeOnly */true);
@@ -265,6 +274,11 @@ public class CameraMetadata implements Parcelable, AutoCloseable {
private static <T> int packClass(T value, ByteBuffer buffer, Class<T> type, int nativeType,
boolean sizeOnly) {
+ MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType);
+ if (marshaler != null) {
+ return marshaler.marshal(value, buffer, nativeType, sizeOnly);
+ }
+
/**
* FIXME: This doesn't actually work because getFields() returns fields in an unordered
* manner. Although we could sort and get the data to come out correctly on the *java* side,
@@ -558,6 +572,11 @@ public class CameraMetadata implements Parcelable, AutoCloseable {
private static <T> T unpackClass(ByteBuffer buffer, Class<T> type, int nativeType) {
+ MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType);
+ if (marshaler != null) {
+ return marshaler.unmarshal(buffer, nativeType);
+ }
+
/**
* FIXME: This doesn't actually work because getFields() returns fields in an unordered
* manner. Although we could sort and get the data to come out correctly on the *java* side,
@@ -611,14 +630,44 @@ public class CameraMetadata implements Parcelable, AutoCloseable {
Class<?> componentType = type.getComponentType();
Object array;
- int remaining = buffer.remaining();
- // FIXME: Assumes that the rest of the ByteBuffer is part of the array.
- int arraySize = remaining / getTypeSize(nativeType);
+ int elementSize = getTypeSize(nativeType);
+
+ MetadataMarshalClass<?> marshaler = getMarshaler(componentType, nativeType);
+ if (marshaler != null) {
+ elementSize = marshaler.getNativeSize(nativeType);
+ }
+
+ if (elementSize != MetadataMarshalClass.NATIVE_SIZE_DYNAMIC) {
+ int remaining = buffer.remaining();
+ int arraySize = remaining / elementSize;
+
+ Log.v(TAG,
+ String.format(
+ "Attempting to unpack array (count = %d, element size = %d, bytes " +
+ "remaining = %d) for type %s",
+ arraySize, elementSize, remaining, type));
+
+ array = Array.newInstance(componentType, arraySize);
+ for (int i = 0; i < arraySize; ++i) {
+ Object elem = unpackSingle(buffer, componentType, nativeType);
+ Array.set(array, i, elem);
+ }
+ } else {
+ // Dynamic size, use an array list.
+ ArrayList<Object> arrayList = new ArrayList<Object>();
+
+ int primitiveSize = getTypeSize(nativeType);
+ while (buffer.remaining() >= primitiveSize) {
+ Object elem = unpackSingle(buffer, componentType, nativeType);
+ arrayList.add(elem);
+ }
- array = Array.newInstance(componentType, arraySize);
- for (int i = 0; i < arraySize; ++i) {
- Object elem = unpackSingle(buffer, componentType, nativeType);
- Array.set(array, i, elem);
+ array = arrayList.toArray((T[]) Array.newInstance(componentType, 0));
+ }
+
+ if (buffer.remaining() != 0) {
+ Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking "
+ + type);
}
return (T) array;
@@ -927,11 +976,39 @@ public class CameraMetadata implements Parcelable, AutoCloseable {
return values[ordinal];
}
+ static HashMap<Class<?>, MetadataMarshalClass<?>> sMarshalerMap = new
+ HashMap<Class<?>, MetadataMarshalClass<?>>();
+
+ private static <T> void registerMarshaler(MetadataMarshalClass<T> marshaler) {
+ sMarshalerMap.put(marshaler.getMarshalingClass(), marshaler);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T> MetadataMarshalClass<T> getMarshaler(Class<T> type, int nativeType) {
+ MetadataMarshalClass<T> marshaler = (MetadataMarshalClass<T>) sMarshalerMap.get(type);
+
+ if (marshaler != null && !marshaler.isNativeTypeSupported(nativeType)) {
+ throw new UnsupportedOperationException("Unsupported type " + nativeType +
+ " to be marshalled to/from a " + type);
+ }
+
+ return marshaler;
+ }
+
/**
* We use a class initializer to allow the native code to cache some field offsets
*/
static {
System.loadLibrary("media_jni");
nativeClassInit();
+
+ Log.v(TAG, "Shall register metadata marshalers");
+
+ // load built-in marshallers
+ registerMarshaler(new MetadataMarshalRect());
+ registerMarshaler(new MetadataMarshalSize());
+ registerMarshaler(new MetadataMarshalString());
+
+ Log.v(TAG, "Registered metadata marshalers");
}
}
diff --git a/core/java/android/hardware/photography/impl/MetadataMarshalClass.java b/core/java/android/hardware/photography/impl/MetadataMarshalClass.java
new file mode 100644
index 0000000..a70784d
--- /dev/null
+++ b/core/java/android/hardware/photography/impl/MetadataMarshalClass.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2013 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 android.hardware.photography.impl;
+
+import java.nio.ByteBuffer;
+
+public interface MetadataMarshalClass<T> {
+
+ /**
+ * Marshal the specified object instance (value) into a byte buffer.
+ *
+ * @param value the value of type T that we wish to write into the byte buffer
+ * @param buffer the byte buffer into which the marshalled object will be written
+ * @param nativeType the native type, e.g.
+ * {@link android.hardware.photography.CameraMetadata#TYPE_BYTE TYPE_BYTE}.
+ * Guaranteed to be one for which isNativeTypeSupported returns true.
+ * @param sizeOnly if this is true, don't write to the byte buffer. calculate the size only.
+ * @return the size that needs to be written to the byte buffer
+ */
+ int marshal(T value, ByteBuffer buffer, int nativeType, boolean sizeOnly);
+
+ /**
+ * Unmarshal a new object instance from the byte buffer.
+ * @param buffer the byte buffer, from which we will read the object
+ * @param nativeType the native type, e.g.
+ * {@link android.hardware.photography.CameraMetadata#TYPE_BYTE TYPE_BYTE}.
+ * Guaranteed to be one for which isNativeTypeSupported returns true.
+ * @return a new instance of type T read from the byte buffer
+ */
+ T unmarshal(ByteBuffer buffer, int nativeType);
+
+ Class<T> getMarshalingClass();
+
+ /**
+ * Determines whether or not this marshaller supports this native type. Most marshallers
+ * will are likely to only support one type.
+ *
+ * @param nativeType the native type, e.g.
+ * {@link android.hardware.photography.CameraMetadata#TYPE_BYTE TYPE_BYTE}
+ * @return true if it supports, false otherwise
+ */
+ boolean isNativeTypeSupported(int nativeType);
+
+ public static int NATIVE_SIZE_DYNAMIC = -1;
+
+ /**
+ * How many bytes T will take up if marshalled to/from nativeType
+ * @param nativeType the native type, e.g.
+ * {@link android.hardware.photography.CameraMetadata#TYPE_BYTE TYPE_BYTE}
+ * @return a size in bytes, or NATIVE_SIZE_DYNAMIC if the size is dynamic
+ */
+ int getNativeSize(int nativeType);
+}
diff --git a/core/java/android/hardware/photography/impl/MetadataMarshalRect.java b/core/java/android/hardware/photography/impl/MetadataMarshalRect.java
new file mode 100644
index 0000000..d6636ac
--- /dev/null
+++ b/core/java/android/hardware/photography/impl/MetadataMarshalRect.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 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 android.hardware.photography.impl;
+
+import android.graphics.Rect;
+import android.hardware.photography.CameraMetadata;
+
+import java.nio.ByteBuffer;
+
+public class MetadataMarshalRect implements MetadataMarshalClass<Rect> {
+ private static final int SIZE = 16;
+
+ @Override
+ public int marshal(Rect value, ByteBuffer buffer, int nativeType, boolean sizeOnly) {
+ if (sizeOnly) {
+ return SIZE;
+ }
+
+ buffer.putInt(value.left);
+ buffer.putInt(value.top);
+ buffer.putInt(value.width());
+ buffer.putInt(value.height());
+
+ return SIZE;
+ }
+
+ @Override
+ public Rect unmarshal(ByteBuffer buffer, int nativeType) {
+
+ int left = buffer.getInt();
+ int top = buffer.getInt();
+ int width = buffer.getInt();
+ int height = buffer.getInt();
+
+ int right = left + width;
+ int bottom = top + height;
+
+ return new Rect(left, top, right, bottom);
+ }
+
+ @Override
+ public Class<Rect> getMarshalingClass() {
+ return Rect.class;
+ }
+
+ @Override
+ public boolean isNativeTypeSupported(int nativeType) {
+ return nativeType == CameraMetadata.TYPE_INT32;
+ }
+
+ @Override
+ public int getNativeSize(int nativeType) {
+ return SIZE;
+ }
+}
diff --git a/core/java/android/hardware/photography/impl/MetadataMarshalSize.java b/core/java/android/hardware/photography/impl/MetadataMarshalSize.java
new file mode 100644
index 0000000..430219c
--- /dev/null
+++ b/core/java/android/hardware/photography/impl/MetadataMarshalSize.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 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 android.hardware.photography.impl;
+
+import android.hardware.photography.CameraMetadata;
+import android.hardware.photography.Size;
+
+import java.nio.ByteBuffer;
+
+public class MetadataMarshalSize implements MetadataMarshalClass<Size> {
+
+ private static final int SIZE = 8;
+
+ @Override
+ public int marshal(Size value, ByteBuffer buffer, int nativeType, boolean sizeOnly) {
+ if (sizeOnly) {
+ return SIZE;
+ }
+
+ buffer.putInt(value.getWidth());
+ buffer.putInt(value.getHeight());
+
+ return SIZE;
+ }
+
+ @Override
+ public Size unmarshal(ByteBuffer buffer, int nativeType) {
+ int width = buffer.getInt();
+ int height = buffer.getInt();
+
+ return new Size(width, height);
+ }
+
+ @Override
+ public Class<Size> getMarshalingClass() {
+ return Size.class;
+ }
+
+ @Override
+ public boolean isNativeTypeSupported(int nativeType) {
+ return nativeType == CameraMetadata.TYPE_INT32;
+ }
+
+ @Override
+ public int getNativeSize(int nativeType) {
+ return SIZE;
+ }
+}
diff --git a/core/java/android/hardware/photography/impl/MetadataMarshalString.java b/core/java/android/hardware/photography/impl/MetadataMarshalString.java
new file mode 100644
index 0000000..81123ee
--- /dev/null
+++ b/core/java/android/hardware/photography/impl/MetadataMarshalString.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2013 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 android.hardware.photography.impl;
+
+import android.hardware.photography.CameraMetadata;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+public class MetadataMarshalString implements MetadataMarshalClass<String> {
+
+ private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
+
+ @Override
+ public int marshal(String value, ByteBuffer buffer, int nativeType, boolean sizeOnly) {
+ byte[] arr = value.getBytes(UTF8_CHARSET);
+
+ if (!sizeOnly) {
+ buffer.put(arr);
+ buffer.put((byte)0); // metadata strings are NULL-terminated
+ }
+
+ return arr.length + 1;
+ }
+
+ @Override
+ public String unmarshal(ByteBuffer buffer, int nativeType) {
+
+ buffer.mark(); // save the current position
+
+ boolean foundNull = false;
+ int stringLength = 0;
+ while (buffer.hasRemaining()) {
+ if (buffer.get() == (byte)0) {
+ foundNull = true;
+ break;
+ }
+
+ stringLength++;
+ }
+ if (!foundNull) {
+ throw new IllegalArgumentException("Strings must be null-terminated");
+ }
+
+ buffer.reset(); // go back to the previously marked position
+
+ byte[] strBytes = new byte[stringLength + 1];
+ buffer.get(strBytes, /*dstOffset*/0, stringLength + 1); // including null character
+
+ // not including null character
+ return new String(strBytes, /*offset*/0, stringLength, UTF8_CHARSET);
+ }
+
+ @Override
+ public Class<String> getMarshalingClass() {
+ return String.class;
+ }
+
+ @Override
+ public boolean isNativeTypeSupported(int nativeType) {
+ return nativeType == CameraMetadata.TYPE_BYTE;
+ }
+
+ @Override
+ public int getNativeSize(int nativeType) {
+ return NATIVE_SIZE_DYNAMIC;
+ }
+}
diff --git a/core/jni/android_hardware_photography_CameraMetadata.cpp b/core/jni/android_hardware_photography_CameraMetadata.cpp
index 5070d2c..5190a37 100644
--- a/core/jni/android_hardware_photography_CameraMetadata.cpp
+++ b/core/jni/android_hardware_photography_CameraMetadata.cpp
@@ -267,7 +267,13 @@ static void CameraMetadata_writeValues(JNIEnv *env, jobject thiz, jint tag, jbyt
if (src == NULL) {
// If array is NULL, delete the entry
- res = metadata->erase(tag);
+ if (metadata->exists(tag)) {
+ res = metadata->erase(tag);
+ ALOGV("%s: Erase values (res = %d)", __FUNCTION__, res);
+ } else {
+ res = OK;
+ ALOGV("%s: Don't need to erase", __FUNCTION__);
+ }
} else {
// Copy from java array into native array
ScopedByteArrayRO arrayReader(env, src);
@@ -275,6 +281,8 @@ static void CameraMetadata_writeValues(JNIEnv *env, jobject thiz, jint tag, jbyt
res = Helpers::updateAny(metadata, static_cast<uint32_t>(tag),
tagType, arrayReader.get(), arrayReader.size());
+
+ ALOGV("%s: Update values (res = %d)", __FUNCTION__, res);
}
if (res == OK) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index 0c2f3a3..131441b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -19,8 +19,10 @@ package com.android.mediaframeworktest.unit;
import android.os.Parcel;
import android.test.suitebuilder.annotation.SmallTest;
import android.graphics.ImageFormat;
+import android.graphics.Rect;
import android.hardware.photography.CameraMetadata;
import android.hardware.photography.Rational;
+import android.hardware.photography.Size;
import static android.hardware.photography.CameraMetadata.*;
@@ -223,6 +225,10 @@ public class CameraMetadataTest extends junit.framework.TestCase {
assertEquals(null, mMetadata.readValues(ANDROID_COLOR_CORRECTION_MODE));
+ // Write/read null values
+ mMetadata.writeValues(ANDROID_COLOR_CORRECTION_MODE, null);
+ assertEquals(null, mMetadata.readValues(ANDROID_COLOR_CORRECTION_MODE));
+
// Write 0 values
mMetadata.writeValues(ANDROID_COLOR_CORRECTION_MODE, new byte[] {});
@@ -286,13 +292,21 @@ public class CameraMetadataTest extends junit.framework.TestCase {
}
private <T> void checkKeyGetAndSet(String keyStr, Class<T> type, T value) {
+ assertFalse("Use checkKeyGetAndSetArray to compare array Keys", type.isArray());
+
Key<T> key = new Key<T>(keyStr, type);
assertNull(mMetadata.get(key));
+ mMetadata.set(key, null);
+ assertNull(mMetadata.get(key));
mMetadata.set(key, value);
- assertEquals(value, mMetadata.get(key));
+
+ T actual = mMetadata.get(key);
+ assertEquals(value, actual);
}
private <T> void checkKeyGetAndSetArray(String keyStr, Class<T> type, T value) {
+ assertTrue(type.isArray());
+
Key<T> key = new Key<T>(keyStr, type);
assertNull(mMetadata.get(key));
mMetadata.set(key, value);
@@ -508,4 +522,73 @@ public class CameraMetadataTest extends junit.framework.TestCase {
assertEquals(expectedIntValues[i], bf.getInt());
}
}
+
+ @SmallTest
+ public void testReadWriteSize() {
+ // int32 x n
+ checkKeyGetAndSet("android.jpeg.thumbnailSize", Size.class, new Size(123, 456));
+
+ // int32 x 2 x n
+ checkKeyGetAndSetArray("android.scaler.availableJpegSizes", Size[].class, new Size[] {
+ new Size(123, 456),
+ new Size(0xDEAD, 0xF00D),
+ new Size(0xF00, 0xB00)
+ });
+ }
+
+ @SmallTest
+ public void testReadWriteRectangle() {
+ // int32 x n
+ checkKeyGetAndSet("android.scaler.cropRegion", Rect.class, new Rect(10, 11, 1280, 1024));
+
+ // int32 x 2 x n
+ checkKeyGetAndSetArray("android.statistics.faceRectangles", Rect[].class, new Rect[] {
+ new Rect(110, 111, 11280, 11024),
+ new Rect(210, 111, 21280, 21024),
+ new Rect(310, 111, 31280, 31024)
+ });
+ }
+
+ @SmallTest
+ public void testReadWriteString() {
+ // (byte) string
+ Key<String> gpsProcessingMethodKey =
+ new Key<String>("android.jpeg.gpsProcessingMethod", String.class);
+
+ String helloWorld = new String("HelloWorld");
+ byte[] helloWorldBytes = new byte[] {
+ 'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '\0' };
+
+ mMetadata.set(gpsProcessingMethodKey, helloWorld);
+
+ String actual = mMetadata.get(gpsProcessingMethodKey);
+ assertEquals(helloWorld, actual);
+
+ byte[] actualBytes = mMetadata.readValues(getTag(gpsProcessingMethodKey.getName()));
+ assertArrayEquals(helloWorldBytes, actualBytes);
+
+ // Does not yet test as a string[] since we don't support that in native code.
+
+ // (byte) string
+ Key<String[]> gpsProcessingMethodKeyArray =
+ new Key<String[]>("android.jpeg.gpsProcessingMethod", String[].class);
+
+ String[] gpsStrings = new String[] { "HelloWorld", "FooBar", "Shazbot" };
+ byte[] gpsBytes = new byte[] {
+ 'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '\0',
+ 'F', 'o', 'o', 'B', 'a', 'r', '\0',
+ 'S', 'h', 'a', 'z', 'b', 'o', 't', '\0'};
+
+ mMetadata.set(gpsProcessingMethodKeyArray, gpsStrings);
+
+ String[] actualArray = mMetadata.get(gpsProcessingMethodKeyArray);
+ assertArrayEquals(gpsStrings, actualArray);
+
+ byte[] actualBytes2 = mMetadata.readValues(getTag(gpsProcessingMethodKeyArray.getName()));
+ assertArrayEquals(gpsBytes, actualBytes2);
+ }
+
+ <T> void compareGeneric(T expected, T actual) {
+ assertEquals(expected, actual);
+ }
}