Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8d637b81 authored by Zhijun He's avatar Zhijun He
Browse files

Camera2: Fix the JPEG format issues

Also add unit test.

Bug: 13836016

Change-Id: I0af4bfe299f99c99e70faf5d3159eb0cbcc147eb
parent 972d0488
Loading
Loading
Loading
Loading
+92 −0
Original line number Diff line number Diff line
@@ -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) {
+117 −19
Original line number Diff line number Diff line
@@ -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);
    }
}