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

Commit aba398d8 authored by Sally Qi's avatar Sally Qi
Browse files

Introduce Builder in ImageReader class for setup and construct an

ImageReader instance.

- Builder pattern allows the app to provide HardwareBuffer.Format
constant which is 1:1 mapping with the HAL PixelFormat
- create an JNI binding for PublicFormat.cpp functions and directly map
imageFormat to the pairings of hardwareBufferformat and dataspace
in the ImageReader class.
- involve dataspace setting option into ImageReader

Bug: 205734633
Test: android.hardware.camera2.cts.ImageReaderTest pass
Change-Id: Idd4c610a710d123615449af76763f1c04afb2bda
parent 94341546
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -21681,16 +21681,29 @@ package android.media {
    method public android.media.Image acquireNextImage();
    method public void close();
    method public void discardFreeBuffers();
    method public long getDataSpace();
    method public int getHardwareBufferFormat();
    method public int getHeight();
    method public int getImageFormat();
    method public int getMaxImages();
    method public android.view.Surface getSurface();
    method public long getUsage();
    method public int getWidth();
    method @NonNull public static android.media.ImageReader newInstance(@IntRange(from=1) int, @IntRange(from=1) int, int, @IntRange(from=1) int);
    method @NonNull public static android.media.ImageReader newInstance(@IntRange(from=1) int, @IntRange(from=1) int, int, @IntRange(from=1) int, long);
    method public void setOnImageAvailableListener(android.media.ImageReader.OnImageAvailableListener, android.os.Handler);
  }
  public static final class ImageReader.Builder {
    ctor public ImageReader.Builder(@IntRange(from=1) int, @IntRange(from=1) int);
    method @NonNull public android.media.ImageReader build();
    method @NonNull public android.media.ImageReader.Builder setDefaultDataSpace(long);
    method @NonNull public android.media.ImageReader.Builder setDefaultHardwareBufferFormat(int);
    method @NonNull public android.media.ImageReader.Builder setImageFormat(int);
    method @NonNull public android.media.ImageReader.Builder setMaxImages(int);
    method @NonNull public android.media.ImageReader.Builder setUsage(long);
  }
  public static interface ImageReader.OnImageAvailableListener {
    method public void onImageAvailable(android.media.ImageReader);
  }
+278 −36
Original line number Diff line number Diff line
@@ -18,10 +18,13 @@ package android.media;

import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.graphics.GraphicBuffer;
import android.graphics.ImageFormat;
import android.graphics.ImageFormat.Format;
import android.graphics.Rect;
import android.hardware.DataSpace;
import android.hardware.DataSpace.NamedDataSpace;
import android.hardware.HardwareBuffer;
import android.hardware.HardwareBuffer.Usage;
import android.hardware.camera2.MultiResolutionImageReader;
@@ -136,8 +139,7 @@ public class ImageReader implements AutoCloseable {
        // If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not
        // work, and is inscrutable anyway
        return new ImageReader(width, height, format, maxImages,
                format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN,
                /*parent*/ null);
                format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN, null);
    }

    /**
@@ -268,44 +270,32 @@ public class ImageReader implements AutoCloseable {
        // If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not
        // work, and is inscrutable anyway
        return new ImageReader(width, height, format, maxImages,
                format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN,
                parent);
                format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN, parent);
    }


    /**
     * @hide
     */
    protected ImageReader(int width, int height, int format, int maxImages, long usage,
            MultiResolutionImageReader parent) {
        mWidth = width;
        mHeight = height;
        mFormat = format;
        mUsage = usage;
        mMaxImages = maxImages;
        mParent = parent;

    private void initializeImageReader(int width, int height, int imageFormat, int maxImages,
            long usage, int hardwareBufferFormat, long dataSpace, boolean useLegacyImageFormat) {
        if (width < 1 || height < 1) {
            throw new IllegalArgumentException(
                "The image dimensions must be positive");
        }
        if (mMaxImages < 1) {

        if (maxImages < 1) {
            throw new IllegalArgumentException(
                "Maximum outstanding image count must be at least 1");
        }

        if (format == ImageFormat.NV21) {
        if (imageFormat == ImageFormat.NV21) {
            throw new IllegalArgumentException(
                "NV21 format is not supported");
        }

        mNumPlanes = ImageUtils.getNumPlanesForFormat(mFormat);
        nativeInit(new WeakReference<>(this), width, height, maxImages, usage,
                hardwareBufferFormat, dataSpace);

        nativeInit(new WeakReference<>(this), width, height, format, maxImages, usage);
        mIsReaderValid = true;

        mSurface = nativeGetSurface();

        mIsReaderValid = true;
        // Estimate the native buffer allocation size and register it so it gets accounted for
        // during GC. Note that this doesn't include the buffers required by the buffer queue
        // itself and the buffers requested by the producer.
@@ -313,10 +303,46 @@ public class ImageReader implements AutoCloseable {
        // complex, and 1 buffer is enough for the VM to treat the ImageReader as being of some
        // size.
        mEstimatedNativeAllocBytes = ImageUtils.getEstimatedNativeAllocBytes(
                width, height, format, /*buffer count*/ 1);
            width, height, useLegacyImageFormat ? imageFormat : hardwareBufferFormat,
            /*buffer count*/ 1);
        VMRuntime.getRuntime().registerNativeAllocation(mEstimatedNativeAllocBytes);
    }

    private ImageReader(int width, int height, int imageFormat, int maxImages, long usage,
            MultiResolutionImageReader parent) {
        mWidth = width;
        mHeight = height;
        mFormat = imageFormat;
        mUsage = usage;
        mMaxImages = maxImages;
        mParent = parent;
        // retrieve hal Format and hal dataspace from imageFormat
        mHardwareBufferFormat = PublicFormatUtils.getHalFormat(mFormat);
        mDataSpace = PublicFormatUtils.getHalDataspace(mFormat);
        mUseLegacyImageFormat = true;
        mNumPlanes = ImageUtils.getNumPlanesForFormat(mFormat);

        initializeImageReader(width, height, imageFormat, maxImages, usage, mHardwareBufferFormat,
                mDataSpace, mUseLegacyImageFormat);
    }

    private ImageReader(int width, int height, int maxImages, long usage,
            MultiResolutionImageReader parent, int hardwareBufferFormat, long dataSpace) {
        mWidth = width;
        mHeight = height;
        mFormat = ImageFormat.UNKNOWN; // set default image format value as UNKNOWN
        mUsage = usage;
        mMaxImages = maxImages;
        mParent = parent;
        mHardwareBufferFormat = hardwareBufferFormat;
        mDataSpace = dataSpace;
        mUseLegacyImageFormat = false;
        mNumPlanes = ImageUtils.getNumPlanesForHardwareBufferFormat(mHardwareBufferFormat);

        initializeImageReader(width, height, mFormat, maxImages, usage, hardwareBufferFormat,
                dataSpace, mUseLegacyImageFormat);
    }

    /**
     * The default width of {@link Image Images}, in pixels.
     *
@@ -354,6 +380,10 @@ public class ImageReader implements AutoCloseable {
     * As of now, each format is only compatible to itself.
     * The actual format of the images can be found using {@link Image#getFormat}.</p>
     *
     * <p>Use this function if the ImageReader instance is created by factory method
     * {@code newInstance} function or by builder pattern {@code ImageReader.Builder} and using
     * {@link Builder#setImageFormat}.</p>
     *
     * @return the expected format of an Image
     *
     * @see ImageFormat
@@ -362,6 +392,32 @@ public class ImageReader implements AutoCloseable {
        return mFormat;
    }

    /**
     * The default {@link HardwareBuffer} format of {@link Image Images}.
     *
     * <p>Use this function if the ImageReader instance is created by builder pattern
     * {@code ImageReader.Builder} and using {@link Builder#setDefaultHardwareBufferFormat} and
     * {@link Builder#setDefaultDataSpace}.</p>
     *
     * @return the expected {@link HardwareBuffer} format of an Image.
     */
    public @HardwareBuffer.Format int getHardwareBufferFormat() {
        return mHardwareBufferFormat;
    }

    /**
     * The default dataspace of {@link Image Images}.
     *
     * <p>Use this function if the ImageReader instance is created by builder pattern
     * {@code ImageReader.Builder} and {@link Builder#setDefaultDataSpace}.</p>
     *
     * @return the expected dataspace of an Image.
     */
    @SuppressLint("MethodNameUnits")
    public @NamedDataSpace long getDataSpace() {
        return mDataSpace;
    }

    /**
     * Maximum number of images that can be acquired from the ImageReader by any time (for example,
     * with {@link #acquireNextImage}).
@@ -383,6 +439,15 @@ public class ImageReader implements AutoCloseable {
        return mMaxImages;
    }

    /**
     * The usage flag of images that can be produced by the ImageReader.
     *
     * @return The usage flag of the images for this ImageReader.
     */
    public @Usage long getUsage() {
        return mUsage;
    }

    /**
     * <p>Get a {@link Surface} that can be used to produce {@link Image Images} for this
     * {@code ImageReader}.</p>
@@ -469,7 +534,12 @@ public class ImageReader implements AutoCloseable {
     * @hide
     */
    public Image acquireNextImageNoThrowISE() {
        SurfaceImage si = new SurfaceImage(mFormat);
        SurfaceImage si;
        if (mUseLegacyImageFormat) {
            si = new SurfaceImage(mFormat);
        } else {
            si = new SurfaceImage(mHardwareBufferFormat, mDataSpace);
        }
        return acquireNextSurfaceImage(si) == ACQUIRE_SUCCESS ? si : null;
    }

@@ -492,7 +562,7 @@ public class ImageReader implements AutoCloseable {
            // A null image will eventually be returned if ImageReader is already closed.
            int status = ACQUIRE_NO_BUFS;
            if (mIsReaderValid) {
                status = nativeImageSetup(si);
                status = nativeImageSetup(si, mUseLegacyImageFormat);
            }

            switch (status) {
@@ -545,7 +615,12 @@ public class ImageReader implements AutoCloseable {
    public Image acquireNextImage() {
        // Initialize with reader format, but can be overwritten by native if the image
        // format is different from the reader format.
        SurfaceImage si = new SurfaceImage(mFormat);
        SurfaceImage si;
        if (mUseLegacyImageFormat) {
            si = new SurfaceImage(mFormat);
        } else {
            si = new SurfaceImage(mHardwareBufferFormat, mDataSpace);
        }
        int status = acquireNextSurfaceImage(si);

        switch (status) {
@@ -838,13 +913,161 @@ public class ImageReader implements AutoCloseable {
        }
    }

    /**
     * Builder class for {@link ImageReader} objects.
     */
    public static final class Builder {
        private int mWidth;
        private int mHeight;
        private int mMaxImages = 1;
        private int mImageFormat = ImageFormat.UNKNOWN;
        private int mHardwareBufferFormat = HardwareBuffer.RGBA_8888;
        private long mDataSpace = DataSpace.DATASPACE_UNKNOWN;
        private long mUsage = HardwareBuffer.USAGE_CPU_READ_OFTEN;
        private boolean mUseLegacyImageFormat = false;

        /**
         * Constructs a new builder for {@link ImageReader}.
         *
         * @param width The default width in pixels that will be passed to the producer.
         *              May be overridden by the producer.
         * @param height The default height in pixels that will be passed to the producer.
         *              May be overridden by the producer.
         * @see Image
         */
        public Builder(@IntRange(from = 1) int width, @IntRange(from = 1) int height) {
            mWidth = width;
            mHeight = height;
        }

        /**
         * Set the maximal number of images.
         *
         * @param maxImages The maximum number of images the user will want to
         *            access simultaneously. This should be as small as possible to
         *            limit memory use. Default value is 1.
         * @return the Builder instance with customized usage value.
         */
        public @NonNull Builder setMaxImages(int maxImages) {
            mMaxImages = maxImages;
            return this;
        }

        /**
         * Set the consumer usage flag.
         *
         * @param usage The intended usage of the images consumed by this ImageReader.
         *              See the usages on {@link HardwareBuffer} for a list of valid usage bits.
         *              Default value is {@link HardwareBuffer#USAGE_CPU_READ_OFTEN}.
         * @return the Builder instance with customized usage value.
         *
         * @see HardwareBuffer
         */
        public @NonNull Builder setUsage(long usage) {
            mUsage = usage;
            return this;
        }

        /**
         * Set the default image format passed by the producer. May be overridden by the producer.
         *
         * <p>{@link #setImageFormat} function replaces the combination of
         * {@link #setDefaultHardwareBufferFormat} and {@link #setDefaultDataSpace} functions.
         * Either this or these two functions must be called to initialize an {@code ImageReader}
         * instance.</p>
         *
         * @param imageFormat The format of the image that this reader will produce. This
         *                    must be one of the {@link android.graphics.ImageFormat} or
         *                   {@link android.graphics.PixelFormat} constants. Note that not
         *                   all formats are supported, like ImageFormat.NV21. The default value is
         *                   {@link ImageFormat#UNKNOWN}.
         * @return the builder instance with customized image format value.
         *
         * @see #setDefaultHardwareBufferFormat
         * @see #setDefaultDataSpace
         */
        public @NonNull Builder setImageFormat(@Format int imageFormat) {
            mImageFormat = imageFormat;
            mUseLegacyImageFormat = true;
            mHardwareBufferFormat = HardwareBuffer.RGBA_8888;
            mDataSpace = DataSpace.DATASPACE_UNKNOWN;
            return this;
        }

        /**
         * Set the default hardwareBuffer format passed by the producer.
         * May be overridden by the producer.
         *
         * <p>This function works together with {@link #setDefaultDataSpace} for an
         * {@link ImageReader} instance. Setting at least one of these two replaces
         * {@link #setImageFormat} function.</p>
         *
         * <p>The format of the Image can be overridden after {@link #setImageFormat} by calling
         * this function and then {@link #setDefaultDataSpace} functions.
         * <i>Warning:</i> Missing one of callings for initializing or overriding the format may
         * involve undefined behaviors.</p>
         *
         * @param hardwareBufferFormat The HardwareBuffer format of the image that this reader
         *                             will produce. The default value is
         *                             {@link HardwareBuffer#RGBA_8888 HardwareBuffer.RGBA_8888}.
         * @return the builder instance with customized hardwareBuffer value.
         *
         * @see #setDefaultDataSpace
         * @see #setImageFormat
         */
        @SuppressLint("MissingGetterMatchingBuilder")
        public @NonNull Builder setDefaultHardwareBufferFormat(
                @HardwareBuffer.Format int hardwareBufferFormat) {
            mHardwareBufferFormat = hardwareBufferFormat;
            mUseLegacyImageFormat = false;
            mImageFormat = ImageFormat.UNKNOWN;
            return this;
        }

        /**
         * Set the default dataspace passed by the producer.
         * May be overridden by the producer.
         *
         * <p>This function works together with {@link #setDefaultHardwareBufferFormat} for an
         * {@link ImageReader} instance. Setting at least one of these two replaces
         * {@link #setImageFormat} function.</p>
         *
         * @param dataSpace The dataspace of the image that this reader will produce.
         *                  The default value is {@link DataSpace#DATASPACE_UNKNOWN}.
         * @return the builder instance with customized dataspace value.
         *
         * @see #setDefaultHardwareBufferFormat
         */
        @SuppressLint("MissingGetterMatchingBuilder")
        public @NonNull Builder setDefaultDataSpace(@NamedDataSpace long dataSpace) {
            mDataSpace = dataSpace;
            mUseLegacyImageFormat = false;
            mImageFormat = ImageFormat.UNKNOWN;
            return this;
        }

        /**
         * Builds a new ImageReader object.
         *
         * @return The new ImageReader object.
         */
        public @NonNull ImageReader build() {
            if (mUseLegacyImageFormat) {
                return new ImageReader(mWidth, mHeight, mImageFormat, mMaxImages, mUsage, null);
            } else {
                return new ImageReader(mWidth, mHeight, mMaxImages, mUsage, null,
                    mHardwareBufferFormat, mDataSpace);
            }
        }
    }

    private final int mWidth;
    private final int mHeight;
    private final int mFormat;
    private final long mUsage;
    private final int mMaxImages;
    private final int mNumPlanes;
    private final Surface mSurface;
    private Surface mSurface;
    private int mEstimatedNativeAllocBytes;

    private final Object mListenerLock = new Object();
@@ -861,6 +1084,12 @@ public class ImageReader implements AutoCloseable {
    // MultiResolutionImageReader.
    private final MultiResolutionImageReader mParent;

    private final int mHardwareBufferFormat;

    private final long mDataSpace;

    private final boolean mUseLegacyImageFormat;

    /**
     * This field is used by native code, do not access or modify.
     */
@@ -897,6 +1126,12 @@ public class ImageReader implements AutoCloseable {
            mFormat = format;
        }

        SurfaceImage(int hardwareBufferFormat, long dataSpace) {
            mHardwareBufferFormat = hardwareBufferFormat;
            mDataSpace = dataSpace;
            mFormat = PublicFormatUtils.getPublicFormat(mHardwareBufferFormat, mDataSpace);
        }

        @Override
        public void close() {
            ImageReader.this.releaseImage(this);
@@ -909,10 +1144,15 @@ public class ImageReader implements AutoCloseable {
        @Override
        public int getFormat() {
            throwISEIfImageIsInvalid();
            // update mFormat only if ImageReader is initialized by factory pattern.
            // if using builder pattern, mFormat has been updated upon initialization.
            // no need update here.
            if (ImageReader.this.mUseLegacyImageFormat) {
                int readerFormat = ImageReader.this.getImageFormat();
                // Assume opaque reader always produce opaque images.
                mFormat = (readerFormat == ImageFormat.PRIVATE) ? readerFormat :
                    nativeGetFormat(readerFormat);
            }
            return mFormat;
        }

@@ -1125,6 +1365,8 @@ public class ImageReader implements AutoCloseable {

        private SurfacePlane[] mPlanes;
        private int mFormat = ImageFormat.UNKNOWN;
        private int mHardwareBufferFormat = HardwareBuffer.RGBA_8888;
        private long mDataSpace = DataSpace.DATASPACE_UNKNOWN;
        // If this image is detached from the ImageReader.
        private AtomicBoolean mIsDetached = new AtomicBoolean(false);

@@ -1137,8 +1379,8 @@ public class ImageReader implements AutoCloseable {
        private synchronized native HardwareBuffer nativeGetHardwareBuffer();
    }

    private synchronized native void nativeInit(Object weakSelf, int w, int h,
                                                    int fmt, int maxImgs, long consumerUsage);
    private synchronized native void nativeInit(Object weakSelf, int w, int h, int maxImgs,
            long consumerUsage, int hardwareBufferFormat, long dataSpace);
    private synchronized native void nativeClose();
    private synchronized native void nativeReleaseImage(Image i);
    private synchronized native Surface nativeGetSurface();
@@ -1152,7 +1394,7 @@ public class ImageReader implements AutoCloseable {
     * @see #ACQUIRE_NO_BUFS
     * @see #ACQUIRE_MAX_IMAGES
     */
    private synchronized native int nativeImageSetup(Image i);
    private synchronized native int nativeImageSetup(Image i, boolean legacyValidateImageFormat);

    /**
     * @hide
+29 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.media;

import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.hardware.HardwareBuffer;
import android.media.Image.Plane;
import android.util.Size;

@@ -76,6 +77,34 @@ class ImageUtils {
        }
    }

    /**
     * Only a subset of the formats defined in
     * {@link android.graphics.HardwareBuffer.Format} constants are supported by ImageReader.
     */
    public static int getNumPlanesForHardwareBufferFormat(int hardwareBufferFormat) {
        switch(hardwareBufferFormat) {
            case HardwareBuffer.YCBCR_420_888:
                return 3;
            case HardwareBuffer.RGBA_8888:
            case HardwareBuffer.RGBX_8888:
            case HardwareBuffer.RGB_888:
            case HardwareBuffer.RGB_565:
            case HardwareBuffer.RGBA_FP16:
            case HardwareBuffer.RGBA_1010102:
            case HardwareBuffer.BLOB:
            case HardwareBuffer.D_16:
            case HardwareBuffer.D_24:
            case HardwareBuffer.DS_24UI8:
            case HardwareBuffer.D_FP32:
            case HardwareBuffer.DS_FP32UI8:
            case HardwareBuffer.S_UI8:
                return 1;
            default:
                throw new UnsupportedOperationException(
                    String.format("Invalid hardwareBuffer format specified %d",
                            hardwareBufferFormat));
        }
    }
    /**
     * <p>
     * Copy source image data to destination Image.
+34 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 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.media;

/**
 * Package private utility class for PublicFormat related methods.
 */
class PublicFormatUtils {
    public static int getHalFormat(int imageFormat) {
        return nativeGetHalFormat(imageFormat);
    }
    public static long getHalDataspace(int imageFormat) {
        return nativeGetHalDataspace(imageFormat);
    }
    public static int getPublicFormat(int imageFormat, long dataspace) {
        return nativeGetPublicFormat(imageFormat, dataspace);
    }
    private static native int nativeGetHalFormat(int imageFormat);
    private static native long nativeGetHalDataspace(int imageFormat);
    private static native int nativeGetPublicFormat(int imageFormat, long dataspace);
}
+1 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ cc_library_shared {
        "android_media_MediaProfiles.cpp",
        "android_media_MediaRecorder.cpp",
        "android_media_MediaSync.cpp",
        "android_media_PublicFormatUtils.cpp",
        "android_media_ResampleInputStream.cpp",
        "android_media_Streams.cpp",
        "android_media_SyncParams.cpp",
Loading