Camera2: Fix the JPEG format issues
Also add unit test. Bug: 13836016 Change-Id: I0af4bfe299f99c99e70faf5d3159eb0cbcc147eb
This commit is contained in:
@@ -461,6 +461,10 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable {
|
||||
return (T) getFaces();
|
||||
} else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) {
|
||||
return (T) getFaceRectangles();
|
||||
} else if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS)) {
|
||||
return (T) getAvailableStreamConfigurations();
|
||||
} else if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS)) {
|
||||
return (T) getAvailableMinFrameDurations();
|
||||
}
|
||||
|
||||
// For other keys, get() falls back to getBase()
|
||||
@@ -481,6 +485,50 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable {
|
||||
return availableFormats;
|
||||
}
|
||||
|
||||
private int[] getAvailableStreamConfigurations() {
|
||||
final int NUM_ELEMENTS_IN_CONFIG = 4;
|
||||
int[] availableConfigs =
|
||||
getBase(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
|
||||
if (availableConfigs != null) {
|
||||
if (availableConfigs.length % NUM_ELEMENTS_IN_CONFIG != 0) {
|
||||
Log.w(TAG, "availableStreamConfigurations is malformed, length must be multiple"
|
||||
+ " of " + NUM_ELEMENTS_IN_CONFIG);
|
||||
return availableConfigs;
|
||||
}
|
||||
|
||||
for (int i = 0; i < availableConfigs.length; i += NUM_ELEMENTS_IN_CONFIG) {
|
||||
// JPEG has different value between native and managed side, need override.
|
||||
if (availableConfigs[i] == NATIVE_JPEG_FORMAT) {
|
||||
availableConfigs[i] = ImageFormat.JPEG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return availableConfigs;
|
||||
}
|
||||
|
||||
private long[] getAvailableMinFrameDurations() {
|
||||
final int NUM_ELEMENTS_IN_DURATION = 4;
|
||||
long[] availableMinDurations =
|
||||
getBase(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
|
||||
if (availableMinDurations != null) {
|
||||
if (availableMinDurations.length % NUM_ELEMENTS_IN_DURATION != 0) {
|
||||
Log.w(TAG, "availableStreamConfigurations is malformed, length must be multiple"
|
||||
+ " of " + NUM_ELEMENTS_IN_DURATION);
|
||||
return availableMinDurations;
|
||||
}
|
||||
|
||||
for (int i = 0; i < availableMinDurations.length; i += NUM_ELEMENTS_IN_DURATION) {
|
||||
// JPEG has different value between native and managed side, need override.
|
||||
if (availableMinDurations[i] == NATIVE_JPEG_FORMAT) {
|
||||
availableMinDurations[i] = ImageFormat.JPEG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return availableMinDurations;
|
||||
}
|
||||
|
||||
private Face[] getFaces() {
|
||||
final int FACE_LANDMARK_SIZE = 6;
|
||||
|
||||
@@ -607,12 +655,56 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable {
|
||||
return setAvailableFormats((int[]) value);
|
||||
} else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) {
|
||||
return setFaceRectangles((Rect[]) value);
|
||||
} else if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS)) {
|
||||
return setAvailableStreamConfigurations((int[])value);
|
||||
} else if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS)) {
|
||||
return setAvailableMinFrameDurations((long[])value);
|
||||
}
|
||||
|
||||
// For other keys, set() falls back to setBase().
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean setAvailableStreamConfigurations(int[] value) {
|
||||
final int NUM_ELEMENTS_IN_CONFIG = 4;
|
||||
int[] availableConfigs = value;
|
||||
if (value == null) {
|
||||
// Let setBase() to handle the null value case.
|
||||
return false;
|
||||
}
|
||||
|
||||
int[] newValues = new int[availableConfigs.length];
|
||||
for (int i = 0; i < availableConfigs.length; i++) {
|
||||
newValues[i] = availableConfigs[i];
|
||||
if (i % NUM_ELEMENTS_IN_CONFIG == 0 && availableConfigs[i] == ImageFormat.JPEG) {
|
||||
newValues[i] = NATIVE_JPEG_FORMAT;
|
||||
}
|
||||
}
|
||||
|
||||
setBase(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS, newValues);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean setAvailableMinFrameDurations(long[] value) {
|
||||
final int NUM_ELEMENTS_IN_DURATION = 4;
|
||||
long[] availableDurations = value;
|
||||
if (value == null) {
|
||||
// Let setBase() to handle the null value case.
|
||||
return false;
|
||||
}
|
||||
|
||||
long[] newValues = new long[availableDurations.length];
|
||||
for (int i = 0; i < availableDurations.length; i++) {
|
||||
newValues[i] = availableDurations[i];
|
||||
if (i % NUM_ELEMENTS_IN_DURATION == 0 && availableDurations[i] == ImageFormat.JPEG) {
|
||||
newValues[i] = NATIVE_JPEG_FORMAT;
|
||||
}
|
||||
}
|
||||
|
||||
setBase(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS, newValues);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean setAvailableFormats(int[] value) {
|
||||
int[] availableFormat = value;
|
||||
if (value == null) {
|
||||
|
||||
@@ -552,29 +552,72 @@ public class CameraMetadataTest extends junit.framework.TestCase {
|
||||
};
|
||||
int availableFormatTag = CameraMetadataNative.getTag("android.scaler.availableFormats");
|
||||
|
||||
// Write
|
||||
mMetadata.set(CameraCharacteristics.SCALER_AVAILABLE_FORMATS, availableFormats);
|
||||
Key<int[]> formatKey = CameraCharacteristics.SCALER_AVAILABLE_FORMATS;
|
||||
|
||||
byte[] availableFormatValues = mMetadata.readValues(availableFormatTag);
|
||||
validateArrayMetadataReadWriteOverride(formatKey, availableFormats,
|
||||
expectedIntValues, availableFormatTag);
|
||||
|
||||
ByteBuffer bf = ByteBuffer.wrap(availableFormatValues).order(ByteOrder.nativeOrder());
|
||||
//
|
||||
// android.scaler.availableStreamConfigurations (int x n x 4 array)
|
||||
//
|
||||
final int OUTPUT = CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT;
|
||||
int[] availableStreamConfigs = new int[] {
|
||||
0x20, 3280, 2464, OUTPUT, // RAW16
|
||||
0x23, 3264, 2448, OUTPUT, // YCbCr_420_888
|
||||
0x23, 3200, 2400, OUTPUT, // YCbCr_420_888
|
||||
0x100, 3264, 2448, OUTPUT, // ImageFormat.JPEG
|
||||
0x100, 3200, 2400, OUTPUT, // ImageFormat.JPEG
|
||||
0x100, 2592, 1944, OUTPUT, // ImageFormat.JPEG
|
||||
0x100, 2048, 1536, OUTPUT, // ImageFormat.JPEG
|
||||
0x100, 1920, 1080, OUTPUT // ImageFormat.JPEG
|
||||
};
|
||||
int[] expectedAvailableStreamConfigs = new int[] {
|
||||
0x20, 3280, 2464, OUTPUT, // RAW16
|
||||
0x23, 3264, 2448, OUTPUT, // YCbCr_420_888
|
||||
0x23, 3200, 2400, OUTPUT, // YCbCr_420_888
|
||||
0x21, 3264, 2448, OUTPUT, // BLOB
|
||||
0x21, 3200, 2400, OUTPUT, // BLOB
|
||||
0x21, 2592, 1944, OUTPUT, // BLOB
|
||||
0x21, 2048, 1536, OUTPUT, // BLOB
|
||||
0x21, 1920, 1080, OUTPUT // BLOB
|
||||
};
|
||||
int availableStreamConfigTag =
|
||||
CameraMetadataNative.getTag("android.scaler.availableStreamConfigurations");
|
||||
|
||||
assertEquals(expectedIntValues.length * 4, availableFormatValues.length);
|
||||
for (int i = 0; i < expectedIntValues.length; ++i) {
|
||||
assertEquals(expectedIntValues[i], bf.getInt());
|
||||
}
|
||||
// Read
|
||||
byte[] availableFormatsAsByteArray = new byte[expectedIntValues.length * 4];
|
||||
ByteBuffer availableFormatsByteBuffer =
|
||||
ByteBuffer.wrap(availableFormatsAsByteArray).order(ByteOrder.nativeOrder());
|
||||
for (int value : expectedIntValues) {
|
||||
availableFormatsByteBuffer.putInt(value);
|
||||
}
|
||||
mMetadata.writeValues(availableFormatTag, availableFormatsAsByteArray);
|
||||
Key<int[]> configKey = CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS;
|
||||
validateArrayMetadataReadWriteOverride(configKey, availableStreamConfigs,
|
||||
expectedAvailableStreamConfigs, availableStreamConfigTag);
|
||||
|
||||
int[] resultFormats = mMetadata.get(CameraCharacteristics.SCALER_AVAILABLE_FORMATS);
|
||||
assertNotNull("result available formats shouldn't be null", resultFormats);
|
||||
assertArrayEquals(availableFormats, resultFormats);
|
||||
//
|
||||
// android.scaler.availableMinFrameDurations (int x n x 4 array)
|
||||
|
||||
//
|
||||
long[] availableMinDurations = new long[] {
|
||||
0x20, 3280, 2464, 33333336, // RAW16
|
||||
0x23, 3264, 2448, 33333336, // YCbCr_420_888
|
||||
0x23, 3200, 2400, 33333336, // YCbCr_420_888
|
||||
0x100, 3264, 2448, 33333336, // ImageFormat.JPEG
|
||||
0x100, 3200, 2400, 33333336, // ImageFormat.JPEG
|
||||
0x100, 2592, 1944, 33333336, // ImageFormat.JPEG
|
||||
0x100, 2048, 1536, 33333336, // ImageFormat.JPEG
|
||||
0x100, 1920, 1080, 33333336 // ImageFormat.JPEG
|
||||
};
|
||||
long[] expectedAvailableMinDurations = new long[] {
|
||||
0x20, 3280, 2464, 33333336, // RAW16
|
||||
0x23, 3264, 2448, 33333336, // YCbCr_420_888
|
||||
0x23, 3200, 2400, 33333336, // YCbCr_420_888
|
||||
0x21, 3264, 2448, 33333336, // BLOB
|
||||
0x21, 3200, 2400, 33333336, // BLOB
|
||||
0x21, 2592, 1944, 33333336, // BLOB
|
||||
0x21, 2048, 1536, 33333336, // BLOB
|
||||
0x21, 1920, 1080, 33333336 // BLOB
|
||||
};
|
||||
int availableMinDurationsTag =
|
||||
CameraMetadataNative.getTag("android.scaler.availableMinFrameDurations");
|
||||
|
||||
Key<long[]> durationKey = CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS;
|
||||
validateArrayMetadataReadWriteOverride(durationKey, availableMinDurations,
|
||||
expectedAvailableMinDurations, availableMinDurationsTag);
|
||||
|
||||
//
|
||||
// android.statistics.faces (Face x n array)
|
||||
@@ -639,4 +682,59 @@ public class CameraMetadataTest extends junit.framework.TestCase {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate metadata array tag read/write override.
|
||||
*
|
||||
* <p>Only support long and int array for now, can be easily extend to support other
|
||||
* primitive arrays.</p>
|
||||
*/
|
||||
private <T> void validateArrayMetadataReadWriteOverride(Key<T> key, T writeValues,
|
||||
T readValues, int tag) {
|
||||
Class<T> type = key.getType();
|
||||
if (!type.isArray()) {
|
||||
throw new IllegalArgumentException("This function expects an key with array type");
|
||||
} else if (type != int[].class && type != long[].class) {
|
||||
throw new IllegalArgumentException("This function expects long or int array values");
|
||||
}
|
||||
|
||||
// Write
|
||||
mMetadata.set(key, writeValues);
|
||||
|
||||
byte[] readOutValues = mMetadata.readValues(tag);
|
||||
|
||||
ByteBuffer bf = ByteBuffer.wrap(readOutValues).order(ByteOrder.nativeOrder());
|
||||
|
||||
int readValuesLength = Array.getLength(readValues);
|
||||
int readValuesNumBytes = readValuesLength * 4;
|
||||
if (type == long[].class) {
|
||||
readValuesNumBytes = readValuesLength * 8;
|
||||
}
|
||||
|
||||
assertEquals(readValuesNumBytes, readOutValues.length);
|
||||
for (int i = 0; i < readValuesLength; ++i) {
|
||||
if (type == int[].class) {
|
||||
assertEquals(Array.getInt(readValues, i), bf.getInt());
|
||||
} else if (type == long[].class) {
|
||||
assertEquals(Array.getLong(readValues, i), bf.getLong());
|
||||
}
|
||||
}
|
||||
|
||||
// Read
|
||||
byte[] readOutValuesAsByteArray = new byte[readValuesNumBytes];
|
||||
ByteBuffer readOutValuesByteBuffer =
|
||||
ByteBuffer.wrap(readOutValuesAsByteArray).order(ByteOrder.nativeOrder());
|
||||
for (int i = 0; i < readValuesLength; ++i) {
|
||||
if (type == int[].class) {
|
||||
readOutValuesByteBuffer.putInt(Array.getInt(readValues, i));
|
||||
} else if (type == long[].class) {
|
||||
readOutValuesByteBuffer.putLong(Array.getLong(readValues, i));
|
||||
}
|
||||
}
|
||||
mMetadata.writeValues(tag, readOutValuesAsByteArray);
|
||||
|
||||
T result = mMetadata.get(key);
|
||||
assertNotNull(key.getName() + " result shouldn't be null", result);
|
||||
assertArrayEquals(writeValues, result);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user