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

Commit cd599a96 authored by Sally Qi's avatar Sally Qi Committed by Android (Google) Code Review
Browse files

Merge "Introduce Builder in ImageReader class for setup and construct an ImageReader instance."

parents d7fe72bf aba398d8
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -21897,16 +21897,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