Loading core/api/current.txt +16 −0 Original line number Diff line number Diff line Loading @@ -22080,14 +22080,30 @@ package android.media { public class ImageWriter implements java.lang.AutoCloseable { method public void close(); method public android.media.Image dequeueInputImage(); method public long getDataSpace(); method public int getFormat(); method public int getHardwareBufferFormat(); method public int getHeight(); method public int getMaxImages(); method public long getUsage(); method public int getWidth(); method @NonNull public static android.media.ImageWriter newInstance(@NonNull android.view.Surface, @IntRange(from=1) int); method @NonNull public static android.media.ImageWriter newInstance(@NonNull android.view.Surface, @IntRange(from=1) int, int); method public void queueInputImage(android.media.Image); method public void setOnImageReleasedListener(android.media.ImageWriter.OnImageReleasedListener, android.os.Handler); } public static final class ImageWriter.Builder { ctor public ImageWriter.Builder(@NonNull android.view.Surface); method @NonNull public android.media.ImageWriter build(); method @NonNull public android.media.ImageWriter.Builder setDataSpace(long); method @NonNull public android.media.ImageWriter.Builder setHardwareBufferFormat(int); method @NonNull public android.media.ImageWriter.Builder setImageFormat(int); method @NonNull public android.media.ImageWriter.Builder setMaxImages(@IntRange(from=1) int); method @NonNull public android.media.ImageWriter.Builder setUsage(long); method @NonNull public android.media.ImageWriter.Builder setWidthAndHeight(@IntRange(from=1) int, @IntRange(from=1) int); } public static interface ImageWriter.OnImageReleasedListener { method public void onImageReleased(android.media.ImageWriter); } media/java/android/media/ImageWriter.java +306 −40 Original line number Diff line number Diff line Loading @@ -18,12 +18,16 @@ 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.PixelFormat; 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.params.StreamConfigurationMap; import android.hardware.camera2.utils.SurfaceUtils; import android.os.Handler; Loading Loading @@ -95,10 +99,18 @@ public class ImageWriter implements AutoCloseable { private ListenerHandler mListenerHandler; private long mNativeContext; private int mWidth; private int mHeight; private final int mMaxImages; private @Usage long mUsage = HardwareBuffer.USAGE_CPU_WRITE_OFTEN; private @HardwareBuffer.Format int mHardwareBufferFormat; private @NamedDataSpace long mDataSpace; private boolean mUseLegacyImageFormat; private boolean mUseSurfaceImageFormatInfo; // Field below is used by native code, do not access or modify. private int mWriterFormat; private final int mMaxImages; // Keep track of the currently dequeued Image. This need to be thread safe as the images // could be closed by different threads (e.g., application thread and GC thread). private List<Image> mDequeuedImages = new CopyOnWriteArrayList<>(); Loading Loading @@ -131,7 +143,7 @@ public class ImageWriter implements AutoCloseable { */ public static @NonNull ImageWriter newInstance(@NonNull Surface surface, @IntRange(from = 1) int maxImages) { return new ImageWriter(surface, maxImages, ImageFormat.UNKNOWN, -1 /*width*/, return new ImageWriter(surface, maxImages, true, ImageFormat.UNKNOWN, -1 /*width*/, -1 /*height*/); } Loading Loading @@ -183,7 +195,7 @@ public class ImageWriter implements AutoCloseable { if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) { throw new IllegalArgumentException("Invalid format is specified: " + format); } return new ImageWriter(surface, maxImages, format, width, height); return new ImageWriter(surface, maxImages, false, format, width, height); } /** Loading Loading @@ -232,48 +244,49 @@ public class ImageWriter implements AutoCloseable { if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) { throw new IllegalArgumentException("Invalid format is specified: " + format); } return new ImageWriter(surface, maxImages, format, -1 /*width*/, -1 /*height*/); return new ImageWriter(surface, maxImages, false, format, -1 /*width*/, -1 /*height*/); } /** * @hide */ protected ImageWriter(Surface surface, int maxImages, int format, int width, int height) { private void initializeImageWriter(Surface surface, int maxImages, boolean useSurfaceImageFormatInfo, boolean useLegacyImageFormat, int imageFormat, int hardwareBufferFormat, long dataSpace, int width, int height, long usage) { if (surface == null || maxImages < 1) { throw new IllegalArgumentException("Illegal input argument: surface " + surface + ", maxImages: " + maxImages); } mMaxImages = maxImages; mUseSurfaceImageFormatInfo = useSurfaceImageFormatInfo; mUseLegacyImageFormat = useLegacyImageFormat; // Note that the underlying BufferQueue is working in synchronous mode // to avoid dropping any buffers. mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, format, width, height); mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, width, height, useSurfaceImageFormatInfo, hardwareBufferFormat, dataSpace, usage); if (useSurfaceImageFormatInfo) { // nativeInit internally overrides UNKNOWN format. So does surface format query after // nativeInit and before getEstimatedNativeAllocBytes(). if (format == ImageFormat.UNKNOWN) { format = SurfaceUtils.getSurfaceFormat(surface); } imageFormat = SurfaceUtils.getSurfaceFormat(surface); // Several public formats use the same native HAL_PIXEL_FORMAT_BLOB. The native // allocation estimation sequence depends on the public formats values. To avoid // possible errors, convert where necessary. if (format == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) { if (imageFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) { int surfaceDataspace = SurfaceUtils.getSurfaceDataspace(surface); switch (surfaceDataspace) { case StreamConfigurationMap.HAL_DATASPACE_DEPTH: format = ImageFormat.DEPTH_POINT_CLOUD; imageFormat = ImageFormat.DEPTH_POINT_CLOUD; break; case StreamConfigurationMap.HAL_DATASPACE_DYNAMIC_DEPTH: format = ImageFormat.DEPTH_JPEG; imageFormat = ImageFormat.DEPTH_JPEG; break; case StreamConfigurationMap.HAL_DATASPACE_HEIF: format = ImageFormat.HEIC; imageFormat = ImageFormat.HEIC; break; default: format = ImageFormat.JPEG; imageFormat = ImageFormat.JPEG; } } mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat); mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat); } // 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 Loading @@ -282,12 +295,49 @@ public class ImageWriter implements AutoCloseable { // complex, and 1 buffer is enough for the VM to treat the ImageWriter as being of some // size. Size surfSize = SurfaceUtils.getSurfaceSize(surface); mWidth = width == -1 ? surfSize.getWidth() : width; mHeight = height == -1 ? surfSize.getHeight() : height; mEstimatedNativeAllocBytes = ImageUtils.getEstimatedNativeAllocBytes(surfSize.getWidth(),surfSize.getHeight(), format, /*buffer count*/ 1); ImageUtils.getEstimatedNativeAllocBytes(mWidth, mHeight, useLegacyImageFormat ? imageFormat : hardwareBufferFormat, /*buffer count*/ 1); VMRuntime.getRuntime().registerNativeAllocation(mEstimatedNativeAllocBytes); } private ImageWriter(Surface surface, int maxImages, boolean useSurfaceImageFormatInfo, int imageFormat, int width, int height) { mMaxImages = maxImages; // update hal format and dataspace only if image format is overridden by producer. mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat); mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat); initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, true, imageFormat, mHardwareBufferFormat, mDataSpace, width, height, mUsage); } private ImageWriter(Surface surface, int maxImages, boolean useSurfaceImageFormatInfo, int imageFormat, int width, int height, long usage) { mMaxImages = maxImages; mUsage = usage; mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat); mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat); initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, true, imageFormat, mHardwareBufferFormat, mDataSpace, width, height, usage); } private ImageWriter(Surface surface, int maxImages, boolean useSurfaceImageFormatInfo, int hardwareBufferFormat, long dataSpace, int width, int height, long usage) { mMaxImages = maxImages; mUsage = usage; mHardwareBufferFormat = hardwareBufferFormat; mDataSpace = dataSpace; int publicFormat = PublicFormatUtils.getPublicFormat(hardwareBufferFormat, dataSpace); initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, false, publicFormat, hardwareBufferFormat, dataSpace, width, height, usage); } /** * <p> * Maximum number of Images that can be dequeued from the ImageWriter Loading Loading @@ -315,6 +365,30 @@ public class ImageWriter implements AutoCloseable { return mMaxImages; } /** * The width of {@link Image Images}, in pixels. * * <p>If {@link Builder#setWidthAndHeight} is not called, the default width of the Image * depends on the Surface provided by customer end-point.</p> * * @return the expected actual width of an Image. */ public int getWidth() { return mWidth; } /** * The height of {@link Image Images}, in pixels. * * <p>If {@link Builder#setWidthAndHeight} is not called, the default height of the Image * depends on the Surface provided by customer end-point.</p> * * @return the expected height of an Image. */ public int getHeight() { return mHeight; } /** * <p> * Dequeue the next available input Image for the application to produce Loading Loading @@ -489,6 +563,41 @@ public class ImageWriter implements AutoCloseable { return mWriterFormat; } /** * Get the ImageWriter usage flag. * * @return The ImageWriter usage flag. */ public @Usage long getUsage() { return mUsage; } /** * Get the ImageWriter hardwareBuffer format. * * <p>Use this function if the ImageWriter instance is created by builder pattern * {@code ImageWriter.Builder} and using {@link Builder#setHardwareBufferFormat} and * {@link Builder#setDataSpace}.</p> * * @return The ImageWriter hardwareBuffer format. */ public @HardwareBuffer.Format int getHardwareBufferFormat() { return mHardwareBufferFormat; } /** * Get the ImageWriter dataspace. * * <p>Use this function if the ImageWriter instance is created by builder pattern * {@code ImageWriter.Builder} and {@link Builder#setDataSpace}.</p> * * @return The ImageWriter dataspace. */ @SuppressLint("MethodNameUnits") public @NamedDataSpace long getDataSpace() { return mDataSpace; } /** * ImageWriter callback interface, used to to asynchronously notify the * application of various ImageWriter events. Loading Loading @@ -755,6 +864,155 @@ public class ImageWriter implements AutoCloseable { return true; } /** * Builder class for {@link ImageWriter} objects. */ public static final class Builder { private Surface mSurface; private int mWidth = -1; private int mHeight = -1; private int mMaxImages = 1; private int mImageFormat = ImageFormat.UNKNOWN; private @Usage long mUsage = HardwareBuffer.USAGE_CPU_WRITE_OFTEN; private @HardwareBuffer.Format int mHardwareBufferFormat = HardwareBuffer.RGBA_8888; private @NamedDataSpace long mDataSpace = DataSpace.DATASPACE_UNKNOWN; private boolean mUseSurfaceImageFormatInfo = true; // set this as true temporarily now as a workaround to get correct format // when using surface format by default without overriding the image format // in the builder pattern private boolean mUseLegacyImageFormat = true; /** * Constructs a new builder for {@link ImageWriter}. * * @param surface The destination Surface this writer produces Image data into. */ public Builder(@NonNull Surface surface) { mSurface = surface; } /** * Set the width and height of images. Default size is dependent on the Surface that is * provided by the downstream end-point. * * @param width The width in pixels that will be passed to the producer. * @param height The height in pixels that will be passed to the producer. * @return the Builder instance with customized width and height. */ @SuppressLint("MissingGetterMatchingBuilder") public @NonNull Builder setWidthAndHeight(@IntRange(from = 1) int width, @IntRange(from = 1) int height) { mWidth = width; mHeight = height; return this; } /** * Set the maximum number of images. Default value is 1. * * @param maxImages The maximum number of Images the user will want to access simultaneously * for producing Image data. * @return the Builder instance with customized usage value. */ public @NonNull Builder setMaxImages(@IntRange(from = 1) int maxImages) { mMaxImages = maxImages; return this; } /** * Set the image format of this ImageWriter. * Default format depends on the Surface provided. * * @param imageFormat The format of the {@link ImageWriter}. It can be any valid specified * by {@link ImageFormat} or {@link PixelFormat}. * @return the Builder instance with customized image format. */ @SuppressLint("MissingGetterMatchingBuilder") public @NonNull Builder setImageFormat(@Format int imageFormat) { if (!ImageFormat.isPublicFormat(imageFormat) && !PixelFormat.isPublicFormat(imageFormat)) { throw new IllegalArgumentException( "Invalid imageFormat is specified: " + imageFormat); } mImageFormat = imageFormat; mUseLegacyImageFormat = true; mHardwareBufferFormat = HardwareBuffer.RGBA_8888; mDataSpace = DataSpace.DATASPACE_UNKNOWN; mUseSurfaceImageFormatInfo = false; return this; } /** * Set the hardwareBuffer format of this ImageWriter. The default value is * {@link HardwareBuffer#RGBA_8888 HardwareBuffer.RGBA_8888}. * * <p>This function works together with {@link #setDataSpace} for an * {@link ImageWriter} instance. Setting at least one of these two replaces * {@link #setImageFormat} function.</p> * * @param hardwareBufferFormat The HardwareBuffer format of the image that this writer * will produce. * @return the Builder instance with customized buffer format. * * @see #setDataSpace * @see #setImageFormat */ public @NonNull Builder setHardwareBufferFormat( @HardwareBuffer.Format int hardwareBufferFormat) { mHardwareBufferFormat = hardwareBufferFormat; mImageFormat = ImageFormat.UNKNOWN; mUseLegacyImageFormat = false; mUseSurfaceImageFormatInfo = false; return this; } /** * Set the dataspace of this ImageWriter. * The default value is {@link DataSpace#DATASPACE_UNKNOWN}. * * @param dataSpace The dataspace of the image that this writer will produce. * @return the builder instance with customized dataspace value. * * @see #setHardwareBufferFormat */ public @NonNull Builder setDataSpace(@NamedDataSpace long dataSpace) { mDataSpace = dataSpace; mImageFormat = ImageFormat.UNKNOWN; mUseLegacyImageFormat = false; mUseSurfaceImageFormatInfo = false; return this; } /** * Set the usage flag of this ImageWriter. * Default value is {@link HardwareBuffer#USAGE_CPU_WRITE_OFTEN}. * * @param usage The intended usage of the images produced by this ImageWriter. * @return the Builder instance with customized usage flag. * * @see HardwareBuffer */ public @NonNull Builder setUsage(@Usage long usage) { mUsage = usage; return this; } /** * Builds a new ImageWriter object. * * @return The new ImageWriter object. */ public @NonNull ImageWriter build() { if (mUseLegacyImageFormat) { return new ImageWriter(mSurface, mMaxImages, mUseSurfaceImageFormatInfo, mImageFormat, mWidth, mHeight, mUsage); } else { return new ImageWriter(mSurface, mMaxImages, mUseSurfaceImageFormatInfo, mHardwareBufferFormat, mDataSpace, mWidth, mHeight, mUsage); } } } private static class WriterSurfaceImage extends android.media.Image { private ImageWriter mOwner; // This field is used by native code, do not access or modify. Loading @@ -774,6 +1032,13 @@ public class ImageWriter implements AutoCloseable { public WriterSurfaceImage(ImageWriter writer) { mOwner = writer; mWidth = writer.mWidth; mHeight = writer.mHeight; if (!writer.mUseLegacyImageFormat) { mFormat = PublicFormatUtils.getPublicFormat( writer.mHardwareBufferFormat, writer.mDataSpace); } } @Override Loading Loading @@ -969,8 +1234,9 @@ public class ImageWriter implements AutoCloseable { } // Native implemented ImageWriter methods. private synchronized native long nativeInit(Object weakSelf, Surface surface, int maxImgs, int format, int width, int height); private synchronized native long nativeInit(Object weakSelf, Surface surface, int maxImages, int width, int height, boolean useSurfaceImageFormatInfo, int hardwareBufferFormat, long dataSpace, long usage); private synchronized native void nativeClose(long nativeCtx); Loading media/jni/android_media_ImageWriter.cpp +16 −17 Original line number Diff line number Diff line Loading @@ -375,7 +375,8 @@ static void ImageWriter_classInit(JNIEnv* env, jclass clazz) { } static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobject jsurface, jint maxImages, jint userFormat, jint userWidth, jint userHeight) { jint maxImages, jint userWidth, jint userHeight, jboolean useSurfaceImageFormatInfo, jint hardwareBufferFormat, jlong dataSpace, jlong ndkUsage) { status_t res; ALOGV("%s: maxImages:%d", __FUNCTION__, maxImages); Loading Loading @@ -450,7 +451,7 @@ static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobje // Query surface format if no valid user format is specified, otherwise, override surface format // with user format. if (userFormat == IMAGE_FORMAT_UNKNOWN) { if (useSurfaceImageFormatInfo) { if ((res = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &surfaceFormat)) != OK) { ALOGE("%s: Query Surface format failed: %s (%d)", __FUNCTION__, strerror(-res), res); jniThrowRuntimeException(env, "Failed to query Surface format"); Loading @@ -458,13 +459,13 @@ static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobje } } else { // Set consumer buffer format to user specified format PublicFormat publicFormat = static_cast<PublicFormat>(userFormat); int nativeFormat = mapPublicFormatToHalFormat(publicFormat); android_dataspace nativeDataspace = mapPublicFormatToHalDataspace(publicFormat); res = native_window_set_buffers_format(anw.get(), nativeFormat); android_dataspace nativeDataspace = static_cast<android_dataspace>(dataSpace); int userFormat = static_cast<int>(mapHalFormatDataspaceToPublicFormat( hardwareBufferFormat, nativeDataspace)); res = native_window_set_buffers_format(anw.get(), hardwareBufferFormat); if (res != OK) { ALOGE("%s: Unable to configure consumer native buffer format to %#x", __FUNCTION__, nativeFormat); __FUNCTION__, hardwareBufferFormat); jniThrowRuntimeException(env, "Failed to set Surface format"); return 0; } Loading @@ -484,16 +485,14 @@ static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobje env->SetIntField(thiz, gImageWriterClassInfo.mWriterFormat, reinterpret_cast<jint>(surfaceFormat)); if (!isFormatOpaque(surfaceFormat)) { res = native_window_set_usage(anw.get(), GRALLOC_USAGE_SW_WRITE_OFTEN); res = native_window_set_usage(anw.get(), ndkUsage); if (res != OK) { ALOGE("%s: Configure usage %08x for format %08x failed: %s (%d)", __FUNCTION__, static_cast<unsigned int>(GRALLOC_USAGE_SW_WRITE_OFTEN), __FUNCTION__, static_cast<unsigned int>(ndkUsage), surfaceFormat, strerror(-res), res); jniThrowRuntimeException(env, "Failed to SW_WRITE_OFTEN configure usage"); return 0; } } int minUndequeuedBufferCount = 0; res = anw->query(anw.get(), Loading Loading @@ -1093,7 +1092,7 @@ static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz, static JNINativeMethod gImageWriterMethods[] = { {"nativeClassInit", "()V", (void*)ImageWriter_classInit }, {"nativeInit", "(Ljava/lang/Object;Landroid/view/Surface;IIII)J", {"nativeInit", "(Ljava/lang/Object;Landroid/view/Surface;IIIZIJJ)J", (void*)ImageWriter_init }, {"nativeClose", "(J)V", (void*)ImageWriter_close }, {"nativeAttachAndQueueImage", Loading Loading
core/api/current.txt +16 −0 Original line number Diff line number Diff line Loading @@ -22080,14 +22080,30 @@ package android.media { public class ImageWriter implements java.lang.AutoCloseable { method public void close(); method public android.media.Image dequeueInputImage(); method public long getDataSpace(); method public int getFormat(); method public int getHardwareBufferFormat(); method public int getHeight(); method public int getMaxImages(); method public long getUsage(); method public int getWidth(); method @NonNull public static android.media.ImageWriter newInstance(@NonNull android.view.Surface, @IntRange(from=1) int); method @NonNull public static android.media.ImageWriter newInstance(@NonNull android.view.Surface, @IntRange(from=1) int, int); method public void queueInputImage(android.media.Image); method public void setOnImageReleasedListener(android.media.ImageWriter.OnImageReleasedListener, android.os.Handler); } public static final class ImageWriter.Builder { ctor public ImageWriter.Builder(@NonNull android.view.Surface); method @NonNull public android.media.ImageWriter build(); method @NonNull public android.media.ImageWriter.Builder setDataSpace(long); method @NonNull public android.media.ImageWriter.Builder setHardwareBufferFormat(int); method @NonNull public android.media.ImageWriter.Builder setImageFormat(int); method @NonNull public android.media.ImageWriter.Builder setMaxImages(@IntRange(from=1) int); method @NonNull public android.media.ImageWriter.Builder setUsage(long); method @NonNull public android.media.ImageWriter.Builder setWidthAndHeight(@IntRange(from=1) int, @IntRange(from=1) int); } public static interface ImageWriter.OnImageReleasedListener { method public void onImageReleased(android.media.ImageWriter); }
media/java/android/media/ImageWriter.java +306 −40 Original line number Diff line number Diff line Loading @@ -18,12 +18,16 @@ 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.PixelFormat; 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.params.StreamConfigurationMap; import android.hardware.camera2.utils.SurfaceUtils; import android.os.Handler; Loading Loading @@ -95,10 +99,18 @@ public class ImageWriter implements AutoCloseable { private ListenerHandler mListenerHandler; private long mNativeContext; private int mWidth; private int mHeight; private final int mMaxImages; private @Usage long mUsage = HardwareBuffer.USAGE_CPU_WRITE_OFTEN; private @HardwareBuffer.Format int mHardwareBufferFormat; private @NamedDataSpace long mDataSpace; private boolean mUseLegacyImageFormat; private boolean mUseSurfaceImageFormatInfo; // Field below is used by native code, do not access or modify. private int mWriterFormat; private final int mMaxImages; // Keep track of the currently dequeued Image. This need to be thread safe as the images // could be closed by different threads (e.g., application thread and GC thread). private List<Image> mDequeuedImages = new CopyOnWriteArrayList<>(); Loading Loading @@ -131,7 +143,7 @@ public class ImageWriter implements AutoCloseable { */ public static @NonNull ImageWriter newInstance(@NonNull Surface surface, @IntRange(from = 1) int maxImages) { return new ImageWriter(surface, maxImages, ImageFormat.UNKNOWN, -1 /*width*/, return new ImageWriter(surface, maxImages, true, ImageFormat.UNKNOWN, -1 /*width*/, -1 /*height*/); } Loading Loading @@ -183,7 +195,7 @@ public class ImageWriter implements AutoCloseable { if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) { throw new IllegalArgumentException("Invalid format is specified: " + format); } return new ImageWriter(surface, maxImages, format, width, height); return new ImageWriter(surface, maxImages, false, format, width, height); } /** Loading Loading @@ -232,48 +244,49 @@ public class ImageWriter implements AutoCloseable { if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) { throw new IllegalArgumentException("Invalid format is specified: " + format); } return new ImageWriter(surface, maxImages, format, -1 /*width*/, -1 /*height*/); return new ImageWriter(surface, maxImages, false, format, -1 /*width*/, -1 /*height*/); } /** * @hide */ protected ImageWriter(Surface surface, int maxImages, int format, int width, int height) { private void initializeImageWriter(Surface surface, int maxImages, boolean useSurfaceImageFormatInfo, boolean useLegacyImageFormat, int imageFormat, int hardwareBufferFormat, long dataSpace, int width, int height, long usage) { if (surface == null || maxImages < 1) { throw new IllegalArgumentException("Illegal input argument: surface " + surface + ", maxImages: " + maxImages); } mMaxImages = maxImages; mUseSurfaceImageFormatInfo = useSurfaceImageFormatInfo; mUseLegacyImageFormat = useLegacyImageFormat; // Note that the underlying BufferQueue is working in synchronous mode // to avoid dropping any buffers. mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, format, width, height); mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, width, height, useSurfaceImageFormatInfo, hardwareBufferFormat, dataSpace, usage); if (useSurfaceImageFormatInfo) { // nativeInit internally overrides UNKNOWN format. So does surface format query after // nativeInit and before getEstimatedNativeAllocBytes(). if (format == ImageFormat.UNKNOWN) { format = SurfaceUtils.getSurfaceFormat(surface); } imageFormat = SurfaceUtils.getSurfaceFormat(surface); // Several public formats use the same native HAL_PIXEL_FORMAT_BLOB. The native // allocation estimation sequence depends on the public formats values. To avoid // possible errors, convert where necessary. if (format == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) { if (imageFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) { int surfaceDataspace = SurfaceUtils.getSurfaceDataspace(surface); switch (surfaceDataspace) { case StreamConfigurationMap.HAL_DATASPACE_DEPTH: format = ImageFormat.DEPTH_POINT_CLOUD; imageFormat = ImageFormat.DEPTH_POINT_CLOUD; break; case StreamConfigurationMap.HAL_DATASPACE_DYNAMIC_DEPTH: format = ImageFormat.DEPTH_JPEG; imageFormat = ImageFormat.DEPTH_JPEG; break; case StreamConfigurationMap.HAL_DATASPACE_HEIF: format = ImageFormat.HEIC; imageFormat = ImageFormat.HEIC; break; default: format = ImageFormat.JPEG; imageFormat = ImageFormat.JPEG; } } mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat); mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat); } // 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 Loading @@ -282,12 +295,49 @@ public class ImageWriter implements AutoCloseable { // complex, and 1 buffer is enough for the VM to treat the ImageWriter as being of some // size. Size surfSize = SurfaceUtils.getSurfaceSize(surface); mWidth = width == -1 ? surfSize.getWidth() : width; mHeight = height == -1 ? surfSize.getHeight() : height; mEstimatedNativeAllocBytes = ImageUtils.getEstimatedNativeAllocBytes(surfSize.getWidth(),surfSize.getHeight(), format, /*buffer count*/ 1); ImageUtils.getEstimatedNativeAllocBytes(mWidth, mHeight, useLegacyImageFormat ? imageFormat : hardwareBufferFormat, /*buffer count*/ 1); VMRuntime.getRuntime().registerNativeAllocation(mEstimatedNativeAllocBytes); } private ImageWriter(Surface surface, int maxImages, boolean useSurfaceImageFormatInfo, int imageFormat, int width, int height) { mMaxImages = maxImages; // update hal format and dataspace only if image format is overridden by producer. mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat); mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat); initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, true, imageFormat, mHardwareBufferFormat, mDataSpace, width, height, mUsage); } private ImageWriter(Surface surface, int maxImages, boolean useSurfaceImageFormatInfo, int imageFormat, int width, int height, long usage) { mMaxImages = maxImages; mUsage = usage; mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat); mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat); initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, true, imageFormat, mHardwareBufferFormat, mDataSpace, width, height, usage); } private ImageWriter(Surface surface, int maxImages, boolean useSurfaceImageFormatInfo, int hardwareBufferFormat, long dataSpace, int width, int height, long usage) { mMaxImages = maxImages; mUsage = usage; mHardwareBufferFormat = hardwareBufferFormat; mDataSpace = dataSpace; int publicFormat = PublicFormatUtils.getPublicFormat(hardwareBufferFormat, dataSpace); initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, false, publicFormat, hardwareBufferFormat, dataSpace, width, height, usage); } /** * <p> * Maximum number of Images that can be dequeued from the ImageWriter Loading Loading @@ -315,6 +365,30 @@ public class ImageWriter implements AutoCloseable { return mMaxImages; } /** * The width of {@link Image Images}, in pixels. * * <p>If {@link Builder#setWidthAndHeight} is not called, the default width of the Image * depends on the Surface provided by customer end-point.</p> * * @return the expected actual width of an Image. */ public int getWidth() { return mWidth; } /** * The height of {@link Image Images}, in pixels. * * <p>If {@link Builder#setWidthAndHeight} is not called, the default height of the Image * depends on the Surface provided by customer end-point.</p> * * @return the expected height of an Image. */ public int getHeight() { return mHeight; } /** * <p> * Dequeue the next available input Image for the application to produce Loading Loading @@ -489,6 +563,41 @@ public class ImageWriter implements AutoCloseable { return mWriterFormat; } /** * Get the ImageWriter usage flag. * * @return The ImageWriter usage flag. */ public @Usage long getUsage() { return mUsage; } /** * Get the ImageWriter hardwareBuffer format. * * <p>Use this function if the ImageWriter instance is created by builder pattern * {@code ImageWriter.Builder} and using {@link Builder#setHardwareBufferFormat} and * {@link Builder#setDataSpace}.</p> * * @return The ImageWriter hardwareBuffer format. */ public @HardwareBuffer.Format int getHardwareBufferFormat() { return mHardwareBufferFormat; } /** * Get the ImageWriter dataspace. * * <p>Use this function if the ImageWriter instance is created by builder pattern * {@code ImageWriter.Builder} and {@link Builder#setDataSpace}.</p> * * @return The ImageWriter dataspace. */ @SuppressLint("MethodNameUnits") public @NamedDataSpace long getDataSpace() { return mDataSpace; } /** * ImageWriter callback interface, used to to asynchronously notify the * application of various ImageWriter events. Loading Loading @@ -755,6 +864,155 @@ public class ImageWriter implements AutoCloseable { return true; } /** * Builder class for {@link ImageWriter} objects. */ public static final class Builder { private Surface mSurface; private int mWidth = -1; private int mHeight = -1; private int mMaxImages = 1; private int mImageFormat = ImageFormat.UNKNOWN; private @Usage long mUsage = HardwareBuffer.USAGE_CPU_WRITE_OFTEN; private @HardwareBuffer.Format int mHardwareBufferFormat = HardwareBuffer.RGBA_8888; private @NamedDataSpace long mDataSpace = DataSpace.DATASPACE_UNKNOWN; private boolean mUseSurfaceImageFormatInfo = true; // set this as true temporarily now as a workaround to get correct format // when using surface format by default without overriding the image format // in the builder pattern private boolean mUseLegacyImageFormat = true; /** * Constructs a new builder for {@link ImageWriter}. * * @param surface The destination Surface this writer produces Image data into. */ public Builder(@NonNull Surface surface) { mSurface = surface; } /** * Set the width and height of images. Default size is dependent on the Surface that is * provided by the downstream end-point. * * @param width The width in pixels that will be passed to the producer. * @param height The height in pixels that will be passed to the producer. * @return the Builder instance with customized width and height. */ @SuppressLint("MissingGetterMatchingBuilder") public @NonNull Builder setWidthAndHeight(@IntRange(from = 1) int width, @IntRange(from = 1) int height) { mWidth = width; mHeight = height; return this; } /** * Set the maximum number of images. Default value is 1. * * @param maxImages The maximum number of Images the user will want to access simultaneously * for producing Image data. * @return the Builder instance with customized usage value. */ public @NonNull Builder setMaxImages(@IntRange(from = 1) int maxImages) { mMaxImages = maxImages; return this; } /** * Set the image format of this ImageWriter. * Default format depends on the Surface provided. * * @param imageFormat The format of the {@link ImageWriter}. It can be any valid specified * by {@link ImageFormat} or {@link PixelFormat}. * @return the Builder instance with customized image format. */ @SuppressLint("MissingGetterMatchingBuilder") public @NonNull Builder setImageFormat(@Format int imageFormat) { if (!ImageFormat.isPublicFormat(imageFormat) && !PixelFormat.isPublicFormat(imageFormat)) { throw new IllegalArgumentException( "Invalid imageFormat is specified: " + imageFormat); } mImageFormat = imageFormat; mUseLegacyImageFormat = true; mHardwareBufferFormat = HardwareBuffer.RGBA_8888; mDataSpace = DataSpace.DATASPACE_UNKNOWN; mUseSurfaceImageFormatInfo = false; return this; } /** * Set the hardwareBuffer format of this ImageWriter. The default value is * {@link HardwareBuffer#RGBA_8888 HardwareBuffer.RGBA_8888}. * * <p>This function works together with {@link #setDataSpace} for an * {@link ImageWriter} instance. Setting at least one of these two replaces * {@link #setImageFormat} function.</p> * * @param hardwareBufferFormat The HardwareBuffer format of the image that this writer * will produce. * @return the Builder instance with customized buffer format. * * @see #setDataSpace * @see #setImageFormat */ public @NonNull Builder setHardwareBufferFormat( @HardwareBuffer.Format int hardwareBufferFormat) { mHardwareBufferFormat = hardwareBufferFormat; mImageFormat = ImageFormat.UNKNOWN; mUseLegacyImageFormat = false; mUseSurfaceImageFormatInfo = false; return this; } /** * Set the dataspace of this ImageWriter. * The default value is {@link DataSpace#DATASPACE_UNKNOWN}. * * @param dataSpace The dataspace of the image that this writer will produce. * @return the builder instance with customized dataspace value. * * @see #setHardwareBufferFormat */ public @NonNull Builder setDataSpace(@NamedDataSpace long dataSpace) { mDataSpace = dataSpace; mImageFormat = ImageFormat.UNKNOWN; mUseLegacyImageFormat = false; mUseSurfaceImageFormatInfo = false; return this; } /** * Set the usage flag of this ImageWriter. * Default value is {@link HardwareBuffer#USAGE_CPU_WRITE_OFTEN}. * * @param usage The intended usage of the images produced by this ImageWriter. * @return the Builder instance with customized usage flag. * * @see HardwareBuffer */ public @NonNull Builder setUsage(@Usage long usage) { mUsage = usage; return this; } /** * Builds a new ImageWriter object. * * @return The new ImageWriter object. */ public @NonNull ImageWriter build() { if (mUseLegacyImageFormat) { return new ImageWriter(mSurface, mMaxImages, mUseSurfaceImageFormatInfo, mImageFormat, mWidth, mHeight, mUsage); } else { return new ImageWriter(mSurface, mMaxImages, mUseSurfaceImageFormatInfo, mHardwareBufferFormat, mDataSpace, mWidth, mHeight, mUsage); } } } private static class WriterSurfaceImage extends android.media.Image { private ImageWriter mOwner; // This field is used by native code, do not access or modify. Loading @@ -774,6 +1032,13 @@ public class ImageWriter implements AutoCloseable { public WriterSurfaceImage(ImageWriter writer) { mOwner = writer; mWidth = writer.mWidth; mHeight = writer.mHeight; if (!writer.mUseLegacyImageFormat) { mFormat = PublicFormatUtils.getPublicFormat( writer.mHardwareBufferFormat, writer.mDataSpace); } } @Override Loading Loading @@ -969,8 +1234,9 @@ public class ImageWriter implements AutoCloseable { } // Native implemented ImageWriter methods. private synchronized native long nativeInit(Object weakSelf, Surface surface, int maxImgs, int format, int width, int height); private synchronized native long nativeInit(Object weakSelf, Surface surface, int maxImages, int width, int height, boolean useSurfaceImageFormatInfo, int hardwareBufferFormat, long dataSpace, long usage); private synchronized native void nativeClose(long nativeCtx); Loading
media/jni/android_media_ImageWriter.cpp +16 −17 Original line number Diff line number Diff line Loading @@ -375,7 +375,8 @@ static void ImageWriter_classInit(JNIEnv* env, jclass clazz) { } static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobject jsurface, jint maxImages, jint userFormat, jint userWidth, jint userHeight) { jint maxImages, jint userWidth, jint userHeight, jboolean useSurfaceImageFormatInfo, jint hardwareBufferFormat, jlong dataSpace, jlong ndkUsage) { status_t res; ALOGV("%s: maxImages:%d", __FUNCTION__, maxImages); Loading Loading @@ -450,7 +451,7 @@ static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobje // Query surface format if no valid user format is specified, otherwise, override surface format // with user format. if (userFormat == IMAGE_FORMAT_UNKNOWN) { if (useSurfaceImageFormatInfo) { if ((res = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &surfaceFormat)) != OK) { ALOGE("%s: Query Surface format failed: %s (%d)", __FUNCTION__, strerror(-res), res); jniThrowRuntimeException(env, "Failed to query Surface format"); Loading @@ -458,13 +459,13 @@ static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobje } } else { // Set consumer buffer format to user specified format PublicFormat publicFormat = static_cast<PublicFormat>(userFormat); int nativeFormat = mapPublicFormatToHalFormat(publicFormat); android_dataspace nativeDataspace = mapPublicFormatToHalDataspace(publicFormat); res = native_window_set_buffers_format(anw.get(), nativeFormat); android_dataspace nativeDataspace = static_cast<android_dataspace>(dataSpace); int userFormat = static_cast<int>(mapHalFormatDataspaceToPublicFormat( hardwareBufferFormat, nativeDataspace)); res = native_window_set_buffers_format(anw.get(), hardwareBufferFormat); if (res != OK) { ALOGE("%s: Unable to configure consumer native buffer format to %#x", __FUNCTION__, nativeFormat); __FUNCTION__, hardwareBufferFormat); jniThrowRuntimeException(env, "Failed to set Surface format"); return 0; } Loading @@ -484,16 +485,14 @@ static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobje env->SetIntField(thiz, gImageWriterClassInfo.mWriterFormat, reinterpret_cast<jint>(surfaceFormat)); if (!isFormatOpaque(surfaceFormat)) { res = native_window_set_usage(anw.get(), GRALLOC_USAGE_SW_WRITE_OFTEN); res = native_window_set_usage(anw.get(), ndkUsage); if (res != OK) { ALOGE("%s: Configure usage %08x for format %08x failed: %s (%d)", __FUNCTION__, static_cast<unsigned int>(GRALLOC_USAGE_SW_WRITE_OFTEN), __FUNCTION__, static_cast<unsigned int>(ndkUsage), surfaceFormat, strerror(-res), res); jniThrowRuntimeException(env, "Failed to SW_WRITE_OFTEN configure usage"); return 0; } } int minUndequeuedBufferCount = 0; res = anw->query(anw.get(), Loading Loading @@ -1093,7 +1092,7 @@ static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz, static JNINativeMethod gImageWriterMethods[] = { {"nativeClassInit", "()V", (void*)ImageWriter_classInit }, {"nativeInit", "(Ljava/lang/Object;Landroid/view/Surface;IIII)J", {"nativeInit", "(Ljava/lang/Object;Landroid/view/Surface;IIIZIJJ)J", (void*)ImageWriter_init }, {"nativeClose", "(J)V", (void*)ImageWriter_close }, {"nativeAttachAndQueueImage", Loading