ImageReader常用方法讲解
ImageReader
类允许应用程序直接访问渲染到Surface中的图像数据
几个Android媒体API类接受Surface对象作为渲染目标,包括MediaPlayer, MediaCodec, Android .hardware.camera2。CameraDevice, ImageWriter和RenderScript分配。可用于每个源的图像大小和格式各不相同,应在特定API的文档中进行检查。
图像数据被封装在image对象中,并且可以同时访问多个这样的对象,最多可以访问由maxImages构造函数参数指定的数量。通过Surface发送到ImageReader的新图像被排队,直到通过acquireLatestImage或acquireNextImage调用访问。由于内存限制,如果ImageReader不能以与生成速率相等的速率获取和释放图像,那么图像源最终会在试图渲染到Surface时停止或丢弃图像。
newInstance(width,height,format,maxImages)
ImageReader newInstance(@IntRange(from = 1) int width,@IntRange(from = 1) int height,@Format int format,@IntRange(from = 1) int maxImages)
文档注释解释
- 创建图像阅读器:
- 此方法用于创建一个新的图像阅读器 (
ImageReader
) 实例,用于获取指定大小和格式的图像数据。
- 此方法用于创建一个新的图像阅读器 (
maxImages
参数:maxImages
参数确定可以同时从ImageReader
中获取的最大Image
对象数量。请求更多缓冲区会占用更多内存,因此应根据使用情况仅使用必要的最小数量。
- 图像大小和格式:
- 可用的大小和格式取决于图像数据的来源。
- 私有格式 (
ImageFormat#PRIVATE
):- 如果指定的
format
是ImageFormat#PRIVATE
,则创建的ImageReader
将生成应用程序无法直接访问的图像。应用程序仍然可以从该ImageReader
获取图像,并通过ImageWriter
接口将其发送到相机 (CameraDevice
) 进行重新处理。 - 对于
ImageFormat#PRIVATE
格式的图像,调用Image#getPlanes()
将返回一个空数组。应用程序可以通过调用getImageFormat()
来检查现有阅读器的格式。android.graphics.ImageFormat.java
- 如果指定的
- 私有格式的效率:
- 使用
ImageFormat#PRIVATE
格式的ImageReader
在应用程序不需要访问图像数据时更高效,相比使用其他格式(例如ImageFormat#YUV_420_888
)的ImageReader
更加高效。
- 使用
方法实现解释
该方法实现了 newInstance
,根据传入的参数创建一个新的 ImageReader
实例,并返回该实例。
- 参数说明:
width
和height
:生成图像的默认宽度和高度(以像素为单位)。format
:生成图像的格式,必须是android.graphics.ImageFormat
或android.graphics.PixelFormat
常量之一。maxImages
:用户同时想要访问的最大图像数量,应尽可能小以限制内存使用。
- 创建实例:
- 根据
format
的值判断是否为ImageFormat#PRIVATE
,如果是,则创建ImageReader
实例时不指定HardwareBuffer.USAGE_CPU_READ_OFTEN
。 - 最终创建并返回一个新的
ImageReader
实例。
- 根据
注意事项
- 调用该方法前,应确保传入的参数合法且符合预期的图像大小和格式。
- 需要根据应用程序的需求和设备支持情况选择合适的图像格式和大小。
- 注意
maxImages
参数的设置,应根据实际需求尽量保持较小的值,以限制内存使用。
public static @NonNull ImageReader newInstance(
@IntRange(from = 1) int width,
@IntRange(from = 1) int height,
@Format int format,
@IntRange(from = 1) int maxImages) {
// 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, null);
}
newInstance(width,height,format,maxImages,usage)
ImageReader newInstance(@IntRange(from = 1) int width,@IntRange(from = 1) int height,@Formatint format,@IntRange(from = 1) int maxImages,@Usage long usage)
文档注释解释
- 创建图像阅读器:
- 此方法用于创建一个新的图像阅读器 (
ImageReader
) 实例,用于获取指定大小、格式和使用标志的图像数据。
- 此方法用于创建一个新的图像阅读器 (
maxImages
参数:maxImages
参数确定可以同时从ImageReader
中获取的最大Image
对象数量。请求更多缓冲区会占用更多内存,因此应根据使用情况仅使用必要的最小数量。
- 图像大小和格式:
- 可用的大小和格式取决于图像数据的来源。
- 格式和使用标志组合:
format
和usage
标志组合描述了缓冲区将被消费端点如何使用的方式。例如,如果应用程序打算将图像发送到MediaCodec
或MediaRecorder
进行硬件视频编码,则格式和使用标志组合需要是ImageFormat#PRIVATE
和HardwareBuffer#USAGE_VIDEO_ENCODE
。- 当使用有效大小和此类格式/使用标志组合创建
ImageReader
对象时,应用程序可以将图像发送到通过MediaCodec
或MediaRecorder
提供的输入Surface
的ImageWriter
。
- 私有格式 (
ImageFormat#PRIVATE
):- 如果指定的
format
是ImageFormat#PRIVATE
,则创建的ImageReader
将生成应用程序无法直接访问的图像。应用程序仍然可以从该ImageReader
获取图像,并将其发送到CameraDevice
进行重新处理,或者通过ImageWriter
接口将其发送到MediaCodec
/MediaRecorder
进行硬件视频编码。
- 如果指定的
- 有效性和效率:
- 使用
ImageFormat#PRIVATE
格式的ImageReader
在应用程序不需要访问图像数据时更高效,相比使用其他格式(如ImageFormat#YUV_420_888
)的ImageReader
更加高效。
- 使用
- 支持的格式和使用标志组合:
- 文档中列出了
ImageReader
支持的格式和使用标志组合列表,如果使用不支持的组合可能会导致IllegalArgumentException
异常。
- 文档中列出了
- 参数说明:
width
和height
:生成图像的默认宽度和高度(以像素为单位)。format
:生成图像的格式,必须是android.graphics.ImageFormat
或android.graphics.PixelFormat
常量之一。maxImages
:用户同时想要访问的最大图像数量,应尽可能小以限制内存使用。usage
:用于指定图像使用方式的标志,应遵循HardwareBuffer
类的使用位。
public static @NonNull ImageReader newInstance(
@IntRange(from = 1) int width,
@IntRange(from = 1) int height,
@Format int format,
@IntRange(from = 1) int maxImages,
@Usage long usage) {
// TODO: Check this - can't do it just yet because format support is different
// Unify formats! The only reliable way to validate usage is to just try it and see.
// if (!HardwareBuffer.isSupported(width, height, format, 1, usage)) {
// throw new IllegalArgumentException("The given format=" + Integer.toHexString(format)
// + " & usage=" + Long.toHexString(usage) + " is not supported");
// }
return new ImageReader(width, height, format, maxImages, usage, /*parent*/ null);
}
@Usage
注解
@Retention(RetentionPolicy.SOURCE)
: 指定注解仅在源代码级别保留,不会包含在编译后的字节码中。@LongDef(flag = true, value = { ... })
: 指定该注解用于标记 long 类型的标志常量,其值是给定数组中列出的常量之一。
常量定义
USAGE_CPU_READ_RARELY
(2):- 表示缓冲区偶尔会被 CPU 读取。
USAGE_CPU_READ_OFTEN
(3):- 表示缓冲区经常会被 CPU 读取。
USAGE_CPU_WRITE_RARELY
(32):- 表示缓冲区偶尔会被 CPU 写入。
USAGE_CPU_WRITE_OFTEN
(48):- 表示缓冲区经常会被 CPU 写入。
USAGE_GPU_SAMPLED_IMAGE
(256):- 表示缓冲区将被 GPU 用作采样图像。
USAGE_GPU_COLOR_OUTPUT
(512):- 表示缓冲区将被 GPU 用作颜色输出。
USAGE_COMPOSER_OVERLAY
(2048):- 表示缓冲区将作为硬件合成器叠加层使用,通过
SurfaceControl
显示。
- 表示缓冲区将作为硬件合成器叠加层使用,通过
USAGE_PROTECTED_CONTENT
(16384):- 表示缓冲区包含受保护内容,不允许在受保护的硬件路径之外使用。
USAGE_VIDEO_ENCODE
(65536):- 表示缓冲区将被硬件视频编码器读取。
USAGE_SENSOR_DIRECT_DATA
(8388608):- 表示缓冲区将用于传感器直接数据。
USAGE_GPU_DATA_BUFFER
(16777216):- 表示缓冲区将用作着色器存储或统一缓冲对象。
USAGE_GPU_CUBE_MAP
(33554432):- 表示缓冲区将用作立方体贴图。
USAGE_GPU_MIPMAP_COMPLETE
(67108864):- 表示缓冲区包含完整的 mipmap 层次结构。
USAGE_FRONT_BUFFER
(4294967296):- 表示缓冲区将用于前缓冲区渲染,用于指定前缓冲区渲染时的行为。
@Retention(RetentionPolicy.SOURCE)
@LongDef(flag = true, value = {USAGE_CPU_READ_RARELY, USAGE_CPU_READ_OFTEN,
USAGE_CPU_WRITE_RARELY, USAGE_CPU_WRITE_OFTEN, USAGE_GPU_SAMPLED_IMAGE,
USAGE_GPU_COLOR_OUTPUT, USAGE_COMPOSER_OVERLAY, USAGE_PROTECTED_CONTENT,
USAGE_VIDEO_ENCODE, USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA,
USAGE_GPU_CUBE_MAP, USAGE_GPU_MIPMAP_COMPLETE, USAGE_FRONT_BUFFER})
public @interface Usage {};
@Usage
/** Usage: The buffer will sometimes be read by the CPU */
public static final long USAGE_CPU_READ_RARELY = 2;
/** Usage: The buffer will often be read by the CPU */
public static final long USAGE_CPU_READ_OFTEN = 3;
/** Usage: The buffer will sometimes be written to by the CPU */
public static final long USAGE_CPU_WRITE_RARELY = 2 << 4;
/** Usage: The buffer will often be written to by the CPU */
public static final long USAGE_CPU_WRITE_OFTEN = 3 << 4;
/** Usage: The buffer will be read from by the GPU */
public static final long USAGE_GPU_SAMPLED_IMAGE = 1 << 8;
/** Usage: The buffer will be written to by the GPU */
public static final long USAGE_GPU_COLOR_OUTPUT = 1 << 9;
/**
* The buffer will be used as a hardware composer overlay layer. That is, it will be displayed
* using the system compositor via {@link SurfaceControl}
*
* This flag is currently only needed when using
* {@link android.view.SurfaceControl.Transaction#setBuffer(SurfaceControl, HardwareBuffer)}
* to set a buffer. In all other cases, the framework adds this flag
* internally to buffers that could be presented in a composer overlay.
*/
public static final long USAGE_COMPOSER_OVERLAY = 1 << 11;
/** Usage: The buffer must not be used outside of a protected hardware path */
public static final long USAGE_PROTECTED_CONTENT = 1 << 14;
/** Usage: The buffer will be read by a hardware video encoder */
public static final long USAGE_VIDEO_ENCODE = 1 << 16;
/** Usage: The buffer will be used for sensor direct data */
public static final long USAGE_SENSOR_DIRECT_DATA = 1 << 23;
/** Usage: The buffer will be used as a shader storage or uniform buffer object */
public static final long USAGE_GPU_DATA_BUFFER = 1 << 24;
/** Usage: The buffer will be used as a cube map texture */
public static final long USAGE_GPU_CUBE_MAP = 1 << 25;
/** Usage: The buffer contains a complete mipmap hierarchy */
public static final long USAGE_GPU_MIPMAP_COMPLETE = 1 << 26;
/** Usage: The buffer is used for front-buffer rendering. When front-buffering rendering is
* specified, different usages may adjust their behavior as a result. For example, when
* used as USAGE_GPU_COLOR_OUTPUT the buffer will behave similar to a single-buffered window.
* When used with USAGE_COMPOSER_OVERLAY, the system will try to prioritize the buffer
* receiving an overlay plane & avoid caching it in intermediate composition buffers. */
public static final long USAGE_FRONT_BUFFER = 1L << 32;
注意事项
- 这些常量用于描述图像缓冲区的使用方式,包括 CPU 和 GPU 的读写操作,以及特定用途(如硬件视频编码、传感器数据等)。
- 使用
@Usage
注解标记这些常量,便于在代码中理解和指定图像缓冲区的预期使用方式。 - 不同的使用标志组合对应不同的图像缓冲区行为和性能特征。
- 在使用这些常量时,需要根据实际需求和设备支持情况选择合适的使用标志组合,以获得最佳的图像处理效果和性能表现。
getWidth()
/**
* The default width of {@link Image Images}, in pixels.
*
* <p>The width may be overridden by the producer sending buffers to this
* ImageReader's Surface. If so, the actual width of the images can be
* found using {@link Image#getWidth}.</p>
*
* @return the expected width of an Image
*/
public int getWidth() {
return mWidth;
}
- 默认宽度:
- 这个方法返回的宽度是
ImageReader
对象预期接收的图像宽度。 - 如果图像生成者向
ImageReader
的Surface
发送的缓冲区覆盖了宽度,实际图像的宽度可以通过Image
对象的getWidth()
方法获得。
- 这个方法返回的宽度是
- 注意事项:
- 方法返回的宽度值是预期值,实际图像的宽度可能会因为生产者发送的图像缓冲区而有所不同。
- 对于实际获取的图像,应通过
Image
对象的getWidth()
方法获取准确的宽度值。
getHeight()
/**
* The default height of {@link Image Images}, in pixels.
*
* <p>The height may be overridden by the producer sending buffers to this
* ImageReader's Surface. If so, the actual height of the images can be
* found using {@link Image#getHeight}.</p>
*
* @return the expected height of an Image
*/
public int getHeight() {
return mHeight;
}
- 作用:
getHeight()
方法用于获取图像 (Image
) 的预期高度,即ImageReader
期望接收的图像高度。
- 返回值:
- 返回
int
类型的预期图像高度值。
- 返回
getImageFormat()
/**
* The default {@link ImageFormat image format} of {@link Image Images}.
*
* <p>Some color formats may be overridden by the producer sending buffers to
* this ImageReader's Surface if the default color format allows. ImageReader
* guarantees that all {@link Image Images} acquired from ImageReader
* (for example, with {@link #acquireNextImage}) will have a "compatible"
* format to what was specified in {@link #newInstance}.
* 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
*/
public int getImageFormat() {
return mFormat;
}
方法说明:
- 作用:
getImageFormat()
方法用于获取图像 (Image
) 的预期格式,即ImageReader
期望接收的图像格式。
- 返回值:
- 返回
int
类型的预期图像格式,该格式是通过ImageFormat
类中定义的常量来表示的。
- 返回
注释解释:
- 默认图像格式:
- 这个方法返回的格式是
ImageReader
对象预期接收的图像格式。 - 图像生成者向
ImageReader
的Surface
发送的缓冲区可能会覆盖默认的颜色格式,如果默认颜色格式允许的话。 ImageReader
保证从其获取的所有图像 (Image
)(例如使用acquireNextImage
方法获取的图像)将具有与在newInstance
方法或ImageReader.Builder
的setImageFormat
方法中指定的“兼容”格式相同。- 实际图像的格式可以通过
Image
对象的getFormat()
方法获得。
- 这个方法返回的格式是
getHardwareBufferFormat()
方法说明:
- 作用:
getHardwareBufferFormat()
方法用于获取图像 (Image
) 的预期硬件缓冲区 (HardwareBuffer
) 格式,即ImageReader
期望接收的图像硬件缓冲区格式。
- 返回值:
- 返回
int
类型的预期硬件缓冲区格式,该格式是通过HardwareBuffer.Format
注解中定义的常量来表示的。
- 返回
注释解释:
- 默认硬件缓冲区格式:
- 这个方法返回的格式是
ImageReader
对象预期接收的图像硬件缓冲区格式。 - 如果使用
ImageReader.Builder
的setDefaultHardwareBufferFormat
和setDefaultDataSpace
方法创建ImageReader
实例,则可以使用此方法来获取预期的硬件缓冲区格式。
- 这个方法返回的格式是
/**
* 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;
}
ImageReader imageReader = ...; // 创建或获取 ImageReader 对象
// 获取预期的硬件缓冲区格式
int expectedFormat = imageReader.getHardwareBufferFormat();
// 处理从 ImageReader 获取的图像
Image image = imageReader.acquireNextImage();
if (image != null) {
// 获取实际图像的硬件缓冲区格式
HardwareBuffer hardwareBuffer = image.getHardwareBuffer();
int actualFormat = hardwareBuffer.getFormat();
// 使用图像数据进行处理
// ...
// 释放图像资源
image.close();
}
getDataSpace()
方法说明:
- 作用:
getDataSpace()
方法用于获取图像 (Image
) 的预期数据空间 (dataspace),即ImageReader
期望接收的图像数据空间。
- 返回值:
- 返回
int
类型的预期数据空间值,该值通过NamedDataSpace
注解中定义的常量来表示。
- 返回
注释解释:
- 默认数据空间:
- 这个方法返回的数据空间是
ImageReader
对象预期接收的图像数据空间。 - 如果使用
ImageReader.Builder
的setDefaultDataSpace
方法创建ImageReader
实例,则可以使用此方法来获取预期的数据空间。
- 这个方法返回的数据空间是
/**
* 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 int getDataSpace() {
return mDataSpace;
}
ImageReader imageReader = ...; // 创建或获取 ImageReader 对象
// 获取预期的数据空间
int expectedDataSpace = imageReader.getDataSpace();
// 处理从 ImageReader 获取的图像
Image image = imageReader.acquireNextImage();
if (image != null) {
// 获取实际图像的数据空间
int actualDataSpace = image.getDataSpace();
// 使用图像数据进行处理
// ...
// 释放图像资源
image.close();
}
getMaxImages()
方法说明:
- 作用:
getMaxImages()
方法用于获取ImageReader
可以同时获取的最大图像数量。这个数量由ImageReader
在实例化时指定的maxImages
参数确定。
- 返回值:
- 返回一个
int
值,表示ImageReader
可以同时获取的最大图像数量。
- 返回一个
注释解释:
- 最大图像数量:
- 这个方法返回的值代表了
ImageReader
可以同时获取的最大图像数量。一旦获取的图像数量达到这个最大值,后续尝试获取图像会导致acquire
方法抛出IllegalStateException
异常。
- 这个方法返回的值代表了
- 图像获取和释放:
- 图像在被
ImageReader
获取后,直到调用Image#close()
方法将图像释放回ImageReader
,期间被视为已获取状态。
- 图像在被
- 并发限制:
- 如果尝试同时获取超过
maxImages
个图像,ImageReader
的获取方法会抛出IllegalStateException
异常。 - 当
ImageReader
用户已获取的图像达到最大数量时,生产者可能会因为无法再将图像入队而处于阻塞状态,直到至少释放了一个图像。
- 如果尝试同时获取超过
/**
* Maximum number of images that can be acquired from the ImageReader by any time (for example,
* with {@link #acquireNextImage}).
*
* <p>An image is considered acquired after it's returned by a function from ImageReader, and
* until the Image is {@link Image#close closed} to release the image back to the ImageReader.
* </p>
*
* <p>Attempting to acquire more than {@code maxImages} concurrently will result in the
* acquire function throwing a {@link IllegalStateException}. Furthermore,
* while the max number of images have been acquired by the ImageReader user, the producer
* enqueueing additional images may stall until at least one image has been released. </p>
*
* @return Maximum number of images for this ImageReader.
*
* @see Image#close
*/
public int getMaxImages() {
return mMaxImages;
}
ImageReader imageReader = ...; // 创建或获取 ImageReader 对象
// 获取 ImageReader 可以同时获取的最大图像数量
int maxImages = imageReader.getMaxImages();
// 尝试获取图像
try {
Image image = imageReader.acquireNextImage();
// 处理获取的图像
// ...
// 释放图像资源
image.close();
} catch (IllegalStateException e) {
// 处理无法获取图像的异常情况
e.printStackTrace();
}
getUsage()
方法说明:
- 作用:
getUsage()
方法用于获取ImageReader
可以生成的图像的使用标志 (usage flag)。这个标志由ImageReader
在实例化时指定的usage
参数确定。
- 返回值:
- 返回一个
long
值,表示ImageReader
可以生成的图像的使用标志。
- 返回一个
注释解释:
- 图像使用标志:
- 这个方法返回的值代表了
ImageReader
可以生成的图像的使用标志,指示图像可以用于何种用途。
- 这个方法返回的值代表了
/**
* 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;
}
getSurface()
方法说明:
- 作用:
getSurface()
方法用于获取一个Surface
对象,该对象可用于生成图像供ImageReader
使用。
- 返回值:
- 返回一个
Surface
对象,用作生成图像的绘制目标。
- 返回一个
注释解释:
- 图像生成的
Surface
:- 这个方法返回的
Surface
对象是用于生成图像的绘制目标。在向这个Surface
渲染有效的图像数据之前,调用acquireNextImage
方法将返回null
。
- 这个方法返回的
- 单一数据源:
- 同一时间只能有一个数据源向这个
Surface
中产生数据。但是,一旦第一个数据源与Surface
断开连接,同一个Surface
可以重新用于不同的 API。
- 同一时间只能有一个数据源向这个
Surface
的持有:- 需要注意的是,仅持有由此方法返回的
Surface
对象并不能阻止其父ImageReader
被回收。从这个意义上说,Surface
的行为类似于对提供它的ImageReader
的弱引用 (WeakReference
)。
- 需要注意的是,仅持有由此方法返回的
/**
* <p>Get a {@link Surface} that can be used to produce {@link Image Images} for this
* {@code ImageReader}.</p>
*
* <p>Until valid image data is rendered into this {@link Surface}, the
* {@link #acquireNextImage} method will return {@code null}. Only one source
* can be producing data into this Surface at the same time, although the
* same {@link Surface} can be reused with a different API once the first source is
* disconnected from the {@link Surface}.</p>
*
* <p>Please note that holding on to the Surface object returned by this method is not enough
* to keep its parent ImageReader from being reclaimed. In that sense, a Surface acts like a
* {@link java.lang.ref.WeakReference weak reference} to the ImageReader that provides it.</p>
*
* @return A {@link Surface} to use for a drawing target for various APIs.
*/
public Surface getSurface() {
return mSurface;
}
ImageReader imageReader = ...; // 创建或获取 ImageReader 对象
// 获取用于生成图像的 Surface
Surface surface = imageReader.getSurface();
// 使用 Surface 进行图像渲染等操作(需要其他API来渲染有效图像数据)
acquireLatestImage()
- 获取最新图像:
acquireLatestImage()
方法会获取ImageReader
队列中的最新图像,丢弃队列中较旧的图像。如果没有新的图像可用,则返回null
。
- 适用性:
- 对于大多数实时处理的用例,推荐使用
acquireLatestImage()
方法,因为它更适合获取最新的图像数据。
- 对于大多数实时处理的用例,推荐使用
- 丢弃旧图像:
- 要使用
acquireLatestImage()
方法,maxImages
应至少为 2,以便丢弃除最新图像之外的所有图像。这是因为临时同时获取两个图像来实现丢弃旧图像的操作。
- 要使用
- 异常情况:
- 如果已经通过
acquireLatestImage()
或acquireNextImage()
获取了maxImages
个图像,并且没有及时释放图像资源 (Image#close
),则会抛出IllegalStateException
异常。
- 如果已经通过
/**
* <p>
* Acquire the latest {@link Image} from the ImageReader's queue, dropping older
* {@link Image images}. Returns {@code null} if no new image is available.
* </p>
* <p>
* This operation will acquire all the images possible from the ImageReader,
* but {@link #close} all images that aren't the latest. This function is
* recommended to use over {@link #acquireNextImage} for most use-cases, as it's
* more suited for real-time processing.
* </p>
* <p>
* Note that {@link #getMaxImages maxImages} should be at least 2 for
* {@link #acquireLatestImage} to be any different than {@link #acquireNextImage} -
* discarding all-but-the-newest {@link Image} requires temporarily acquiring two
* {@link Image Images} at once. Or more generally, calling {@link #acquireLatestImage}
* with less than two images of margin, that is
* {@code (maxImages - currentAcquiredImages < 2)} will not discard as expected.
* </p>
* <p>
* This operation will fail by throwing an {@link IllegalStateException} if
* {@code maxImages} have been acquired with {@link #acquireLatestImage} or
* {@link #acquireNextImage}. In particular a sequence of {@link #acquireLatestImage}
* calls greater than {@link #getMaxImages} without calling {@link Image#close} in-between
* will exhaust the underlying queue. At such a time, {@link IllegalStateException}
* will be thrown until more images are
* released with {@link Image#close}.
* </p>
*
* @return latest frame of image data, or {@code null} if no image data is available.
* @throws IllegalStateException if too many images are currently acquired
*/
public Image acquireLatestImage() {
Image image = acquireNextImage();
if (image == null) {
return null;
}
try {
for (;;) {
Image next = acquireNextImageNoThrowISE();
if (next == null) {
Image result = image;
image = null;
return result;
}
image.close();
image = next;
}
} finally {
if (image != null) {
image.close();
}
if (mParent != null) {
mParent.flushOther(this);
}
}
}
ImageReader imageReader = ...; // 创建或获取 ImageReader 对象
try {
Image latestImage = imageReader.acquireLatestImage();
if (latestImage != null) {
// 处理最新的图像数据
// ...
// 使用完毕后记得释放图像资源
latestImage.close();
} else {
// 没有可用的最新图像数据
}
} catch (IllegalStateException e) {
// 处理图像获取过多的异常情况
e.printStackTrace();
}
方法说明:
- 作用:
acquireNextImage()
方法用于从ImageReader
的队列中获取下一个图像,如果成功获取到图像则返回相应的Image
对象,否则返回null
。
- 返回值:
- 如果成功获取到图像,则返回对应的
Image
对象;如果没有可用的图像数据,则返回null
。
- 如果成功获取到图像,则返回对应的
注释解释:
- 推荐用法:
- 在注释中提到,建议使用
acquireLatestImage()
方法而不是直接使用acquireNextImage()
。acquireLatestImage()
方法会自动释放旧图像,允许处理速度较慢的处理程序跟上最新的帧。直接使用acquireNextImage()
可能会导致图像显示出现越来越大的延迟,最终完全停滞,看不到新的图像。
- 在注释中提到,建议使用
- 异常处理:
- 如果已经获取了与
maxImages
相同数量的图像,调用acquireNextImage()
将抛出IllegalStateException
异常。这是为了防止超出可同时获取图像的最大限制。
- 如果已经获取了与
acquireNextImage()
- 功能:
acquireNextImage()
用于从ImageReader
的图像队列中获取下一个可用的图像。
- 返回值:
- 如果成功获取到图像,则返回对应的
Image
对象;如果没有可用的图像数据,则返回null
。
- 如果成功获取到图像,则返回对应的
- 使用场景:
- 适合需要按顺序处理图像的情况,每次调用都会返回图像队列中最旧的未处理图像。
- 注意事项:
- 如果图像队列已满(即达到了
maxImages
的限制),调用此方法会抛出IllegalStateException
异常。
- 如果图像队列已满(即达到了
/**
* <p>
* Acquire the next Image from the ImageReader's queue. Returns {@code null} if
* no new image is available.
* </p>
*
* <p><i>Warning:</i> Consider using {@link #acquireLatestImage()} instead, as it will
* automatically release older images, and allow slower-running processing routines to catch
* up to the newest frame. Usage of {@link #acquireNextImage} is recommended for
* batch/background processing. Incorrectly using this function can cause images to appear
* with an ever-increasing delay, followed by a complete stall where no new images seem to
* appear.
* </p>
*
* <p>
* This operation will fail by throwing an {@link IllegalStateException} if
* {@code maxImages} have been acquired with {@link #acquireNextImage} or
* {@link #acquireLatestImage}. In particular a sequence of {@link #acquireNextImage} or
* {@link #acquireLatestImage} calls greater than {@link #getMaxImages maxImages} without
* calling {@link Image#close} in-between will exhaust the underlying queue. At such a time,
* {@link IllegalStateException} will be thrown until more images are released with
* {@link Image#close}.
* </p>
*
* @return a new frame of image data, or {@code null} if no image data is available.
* @throws IllegalStateException if {@code maxImages} images are currently acquired
* @see #acquireLatestImage
*/
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;
si = new SurfaceImage(mFormat);
int status = acquireNextSurfaceImage(si);
switch (status) {
case ACQUIRE_SUCCESS:
return si;
case ACQUIRE_NO_BUFS:
return null;
case ACQUIRE_MAX_IMAGES:
throw new IllegalStateException(
String.format(
"maxImages (%d) has already been acquired, " +
"call #close before acquiring more.", mMaxImages));
default:
throw new AssertionError("Unknown nativeImageSetup return code " + status);
}
}
区别
- 使用
acquireNextImage()
适合需要顺序处理图像数据的情况,对于每次调用,会返回图像队列中最旧的图像。 - 使用
acquireLatestImage()
适合需要实时处理最新图像数据的情况,它会自动丢弃旧图像,始终返回最新的图像。但要注意合理设置maxImages
,以免导致图像队列耗尽或性能问题。
setOnImageAvailableListener(OnImageAvailableListener, Handler)
- 功能:
setOnImageAvailableListener
用于注册一个监听器,在ImageReader
接收到新图像时触发回调。
- 参数:
listener
:要注册的OnImageAvailableListener
监听器对象。handler
:指定监听器回调所在的Handler
,如果为null
,则回调将在调用线程的 looper 上执行。
- 异常:
- 如果未指定
handler
且调用线程没有 looper,则抛出IllegalArgumentException
异常。
- 如果未指定
- 实现步骤:
- 在同步块中对监听器进行处理,确保线程安全。
- 检查传入的
listener
对象是否为null
,如果不为null
,则继续注册监听器。 - 根据传入的
handler
获取对应的 looper 对象。 - 如果
handler
不为null
,则使用其所属的 looper;如果为null
,则使用当前线程的 looper。 - 检查当前线程是否有 looper,如果没有且
handler
为null
,则抛出异常。 - 如果当前监听器处理的
Handler
为null
或者其 looper 与新的 looper 不同,重新创建ListenerHandler
对象和HandlerExecutor
对象。 - 否则,如果传入的
listener
为null
,则清空mListenerHandler
和mListenerExecutor
。 - 最后将传入的
listener
对象赋值给mListener
。
/**
* Register a listener to be invoked when a new image becomes available
* from the ImageReader.
*
* @param listener
* The listener that will be run.
* @param handler
* The handler on which the listener should be invoked, or null
* if the listener should be invoked on the calling thread's looper.
* @throws IllegalArgumentException
* If no handler specified and the calling thread has no looper.
*/
public void setOnImageAvailableListener(OnImageAvailableListener listener, Handler handler) {
synchronized (mListenerLock) {
if (listener != null) {
Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
if (looper == null) {
throw new IllegalArgumentException(
"handler is null but the current thread is not a looper");
}
if (mListenerHandler == null || mListenerHandler.getLooper() != looper) {
mListenerHandler = new ListenerHandler(looper);
mListenerExecutor = new HandlerExecutor(mListenerHandler);
}
} else {
mListenerHandler = null;
mListenerExecutor = null;
}
mListener = listener;
}
}
OnImageAvailableListener
-
接口功能:
OnImageAvailableListener
是一个回调接口,用于通知应用程序当ImageReader
接收到新图像时。
-
方法:
-
onImageAvailable(ImageReader reader)
:当
ImageReader
接收到新图像时,将调用此方法。
reader
参数表示与回调相关联的ImageReader
对象,即接收到新图像的ImageReader
实例。
-
-
回调频率:
onImageAvailable
方法是基于每个图像的基础调用的,也就是说,每当ImageReader
接收到新帧时都会触发此回调。
-
使用方法:
- 开发者需要实现
OnImageAvailableListener
接口,并将其传递给ImageReader
对象的setOnImageAvailableListener
方法,以便在新图像可用时接收通知。 - 当
ImageReader
接收到新图像时,将调用开发者实现的onImageAvailable
方法,并将ImageReader
实例作为参数传递给此方法,以便开发者可以处理接收到的新图像。
public interface OnImageAvailableListener { /** * Callback that is called when a new image is available from ImageReader. * * @param reader the ImageReader the callback is associated with. * @see ImageReader * @see Image */ void onImageAvailable(ImageReader reader); }
- 开发者需要实现
close()
- 方法功能:
close()
方法用于释放ImageReader
实例占用的所有资源,包括释放图像流(Image
对象)、释放关联的Surface
等。
- 操作效果:
- 调用
close()
方法后,将不再能够使用该ImageReader
实例。对已释放的ImageReader
调用任何方法,包括acquireNextImage
或acquireLatestImage
,都将导致抛出IllegalStateException
异常。 - 对之前通过
acquireNextImage
或acquireLatestImage
获取的Image
对象调用方法,例如从Image.Plane#getBuffer
获取的ByteBuffer
,将会导致行为未定义。
- 调用
- 具体操作:
- 首先,通过
setOnImageAvailableListener(null, null)
方法移除任何注册的图像可用监听器,确保不再接收图像可用的通知。 - 然后,释放关联的
Surface
对象,调用mSurface.release()
方法释放Surface
资源。 - 接下来,遍历并关闭所有已获取但尚未释放的
Image
对象。这是通过迭代mAcquiredImages
列表中的每个Image
对象,并调用其close()
方法来实现的。 - 最后,调用
nativeClose()
方法关闭与ImageReader
相关的本地资源。
- 首先,通过
- 同步操作:
- 在操作过程中使用了同步块
synchronized (mCloseLock)
,确保关闭操作的线程安全性。在同步块中,会将mIsReaderValid
标志设置为false
,表示ImageReader
已无效。 - 对于已获取但尚未释放的
Image
对象,使用了同步块来确保在关闭ImageReader
时可以安全地关闭这些图像,避免并发访问问题。
- 在操作过程中使用了同步块
/**
* Free up all the resources associated with this ImageReader.
*
* <p>
* After calling this method, this ImageReader can not be used. Calling
* any methods on this ImageReader and Images previously provided by
* {@link #acquireNextImage} or {@link #acquireLatestImage}
* will result in an {@link IllegalStateException}, and attempting to read from
* {@link ByteBuffer ByteBuffers} returned by an earlier
* {@link Image.Plane#getBuffer Plane#getBuffer} call will
* have undefined behavior.
* </p>
*/
@Override
public void close() {
setOnImageAvailableListener(null, null);
if (mSurface != null) mSurface.release();
/**
* Close all outstanding acquired images before closing the ImageReader. It is a good
* practice to close all the images as soon as it is not used to reduce system instantaneous
* memory pressure. CopyOnWrite list will use a copy of current list content. For the images
* being closed by other thread (e.g., GC thread), doubling the close call is harmless. For
* the image being acquired by other threads, mCloseLock is used to synchronize close and
* acquire operations.
*/
synchronized (mCloseLock) {
mIsReaderValid = false;
for (Image image : mAcquiredImages) {
image.close();
}
mAcquiredImages.clear();
nativeClose();
if (mEstimatedNativeAllocBytes > 0) {
VMRuntime.getRuntime().registerNativeFree(mEstimatedNativeAllocBytes);
mEstimatedNativeAllocBytes = 0;
}
}
}
discardFreeBuffers()
- 方法功能:
discardFreeBuffers()
方法用于丢弃ImageReader
持有的所有空闲缓冲区。- 通常情况下,
ImageReader
会缓存已分配的缓冲区,以便在需要时重用,以实现最佳性能。但有时可能需要释放所有缓存的未使用缓冲区,以节省内存。
- 作用范围:
- 调用该方法将丢弃所有空闲的缓冲区,但不包括以下内容:
- 已从
ImageReader
获取的图像关联的任何缓冲区。 - 等待获取的任何已填充缓冲区。
- 当前由源渲染到
ImageReader
的Surface
中的缓冲区。
- 已从
- 调用该方法将丢弃所有空闲的缓冲区,但不包括以下内容:
- 方法影响:
- 调用此方法后,
ImageReader
仍然可用,但可能需要在需要更多缓冲区进行渲染时重新分配缓冲区。 - 丢弃空闲缓冲区可以在某些情况下释放内存,但需要注意,这可能会导致在后续使用中重新分配缓冲区,可能会影响性能。
- 调用此方法后,
/**
* Discard any free buffers owned by this ImageReader.
*
* <p>
* Generally, the ImageReader caches buffers for reuse once they have been
* allocated, for best performance. However, sometimes it may be important to
* release all the cached, unused buffers to save on memory.
* </p>
* <p>
* Calling this method will discard all free cached buffers. This does not include any buffers
* associated with Images acquired from the ImageReader, any filled buffers waiting to be
* acquired, and any buffers currently in use by the source rendering buffers into the
* ImageReader's Surface.
* <p>
* The ImageReader continues to be usable after this call, but may need to reallocate buffers
* when more buffers are needed for rendering.
* </p>
*/
public void discardFreeBuffers() {
synchronized (mCloseLock) {
nativeDiscardFreeBuffers();
}
}
new ImageReader.Builder(width, height)
- 构造方法:
Builder(int width, int height)
: 构造一个新的Builder
对象,设置默认的图像宽度和高度。
- 配置方法:
setMaxImages(int maxImages)
: 设置用户希望同时访问的最大图像数量。默认值为 1。setUsage(long usage)
: 设置图像使用标志,指定被ImageReader
使用的图像的预期使用方式。setImageFormat(int imageFormat)
: 设置图像的默认格式,可能会被生产者覆盖。使用此方法会将setDefaultHardwareBufferFormat
和setDefaultDataSpace
的组合替换为新的方式。setDefaultHardwareBufferFormat(int hardwareBufferFormat)
: 设置默认的HardwareBuffer
格式,可能会被生产者覆盖。与setDefaultDataSpace
配合使用。setDefaultDataSpace(int dataSpace)
: 设置默认的数据空间,可能会被生产者覆盖。与setDefaultHardwareBufferFormat
配合使用。
- 构建方法:
build()
: 根据Builder
对象的配置,创建一个新的ImageReader
对象。
在使用 Builder
类时,可以根据需求调用上述方法来配置 ImageReader
对象的属性,最后调用 build()
方法创建 ImageReader
对象。这种模式可以使得 ImageReader
对象的创建过程更加灵活和可配置化。
/**
* 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 int 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(@Usage 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 int 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);
}
}
}
import android.media.ImageFormat;
import android.media.ImageReader;
public class ImageReaderExample {
public static void main(String[] args) {
// 设置图像的默认宽度和高度
int width = 640;
int height = 480;
// 设置最大图像数量
int maxImages = 2;
// 设置图像使用标志
long usage = HardwareBuffer.USAGE_GPU_COLOR_OUTPUT;
// 使用ImageReader.Builder构建ImageReader对象
ImageReader imageReader = new ImageReader.Builder(width, height)
.setMaxImages(maxImages)
.setUsage(usage)
.setImageFormat(ImageFormat.YUV_420_888) // 设置图像格式为YUV_420_888
.build();
// 现在可以使用imageReader对象进行图像捕获和处理等操作
// ...
}
}
ImagePlane(int rowStride, int pixelStride, ByteBuffer buffer)
ImagePlane
类是一个继承自 android.media.Image.Plane
抽象类的具体实现。它表示与 Image
对象关联的图像数据的单个平面。在使用 YUV_420_888 等格式时,每个图像可能包含多个平面。
类的主要内容:
- 构造方法
ImagePlane(int rowStride, int pixelStride, ByteBuffer buffer)
初始化了ImagePlane
对象,设置了行跨距、像素跨距和缓冲区。rowStride
:相邻两行像素数据之间的字节距离。pixelStride
:相同颜色分量的两个相邻像素之间的字节距离。buffer
:包含该平面像素数据的ByteBuffer
。
getBuffer()
、getPixelStride()
和getRowStride()
方法重写了android.media.Image.Plane
中的抽象方法。getBuffer()
:返回包含该平面像素数据的ByteBuffer
。getPixelStride()
:返回该平面的像素跨距。getRowStride()
:返回该平面的行跨距。
该类用于表示图像数据的特定平面(如颜色通道),并提供了访问其属性和像素数据的方法。通常与 Image
对象一起使用,以提取和处理图像数据的各个平面,特别是在涉及 YUV 或类似格式的场景中,图像数据被分割到多个平面中。
public static class ImagePlane extends android.media.Image.Plane {
private ImagePlane(int rowStride, int pixelStride, ByteBuffer buffer) {
mRowStride = rowStride;
mPixelStride = pixelStride;
mBuffer = buffer;
/**
* Set the byteBuffer order according to host endianness (native
* order), otherwise, the byteBuffer order defaults to
* ByteOrder.BIG_ENDIAN.
*/
mBuffer.order(ByteOrder.nativeOrder());
}
@Override
public ByteBuffer getBuffer() {
return mBuffer;
}
@Override
public int getPixelStride() {
return mPixelStride;
}
@Override
public int getRowStride() {
return mRowStride;
}
final private int mPixelStride;
final private int mRowStride;
private ByteBuffer mBuffer;
}
小结:
- ImageReader 类和相关方法:
ImageReader
是 Android 中用于从某些生产者(如摄像头)接收图像数据的类。acquireNextImage()
方法用于从ImageReader
的队列中获取下一个可用的图像。acquireLatestImage()
方法用于获取最新可用的图像,丢弃旧图像,适合实时处理。discardFreeBuffers()
方法用于丢弃ImageReader
持有的空闲缓冲区,节省内存。setOnImageAvailableListener()
方法用于注册当ImageReader
有新图像可用时触发的监听器。
- Image 类和相关方法:
Image
类代表从ImageReader
中获取的图像对象。getWidth()
和getHeight()
方法用于获取图像的宽度和高度。getFormat()
方法用于获取图像的格式。close()
方法用于释放图像对象,必须在使用完图像后调用。
- Builder 模式和图像处理:
Builder
类是用于构建ImageReader
对象的构建器模式。- 通过
Builder
类可以设置ImageReader
的属性,如宽度、高度、图像格式、最大图像数量等。 ImageReader.Builder
提供了build()
方法来构建最终的ImageReader
对象。