Loading core/api/current.txt +13 −0 Original line number Diff line number Diff line Loading @@ -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); } media/java/android/media/ImageReader.java +278 −36 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } /** Loading Loading @@ -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. Loading @@ -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. * Loading Loading @@ -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 Loading @@ -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}). Loading @@ -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> Loading Loading @@ -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; } Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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(); Loading @@ -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. */ Loading Loading @@ -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); Loading @@ -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; } Loading Loading @@ -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); Loading @@ -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(); Loading @@ -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 Loading media/java/android/media/ImageUtils.java +29 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading media/java/android/media/PublicFormatUtils.java 0 → 100644 +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); } media/jni/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
core/api/current.txt +13 −0 Original line number Diff line number Diff line Loading @@ -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); }
media/java/android/media/ImageReader.java +278 −36 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } /** Loading Loading @@ -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. Loading @@ -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. * Loading Loading @@ -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 Loading @@ -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}). Loading @@ -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> Loading Loading @@ -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; } Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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(); Loading @@ -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. */ Loading Loading @@ -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); Loading @@ -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; } Loading Loading @@ -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); Loading @@ -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(); Loading @@ -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 Loading
media/java/android/media/ImageUtils.java +29 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading
media/java/android/media/PublicFormatUtils.java 0 → 100644 +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); }
media/jni/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -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