ImageWriter
ImageWriter
是一个用于在 android.view.Surface
中生成图像数据,并且可以被其他组件(例如 android.hardware.camera2.CameraDevice
)消费的类。
它允许应用程序将图像数据生成到指定的 Surface
中,供其他组件使用。可以从多种 Android API 类获取输入 Surface
对象,供 ImageWriter
生成数据,包括 MediaCodec
(编码器)、android.hardware.camera2.CameraCaptureSession
(重新处理输入)、ImageReader
等。
图像数据被封装在 Image
对象中。要将图像数据生成到目标 Surface
中,应用程序可以通过 dequeueInputImage
方法获取一个输入 Image
,然后向其中写入图像数据。可以同时获取多个这样的 Image
对象,并以任意顺序将其排队回去,数量受构造函数参数 maxImages
的限制。
如果应用程序已经有了来自 ImageReader
的 Image
,可以直接将此 Image
排队到 ImageWriter
中(通过 queueInputImage
方法),可能不需要进行缓冲区复制。即使 ImageWriter
的图像格式是 ImageFormat#PRIVATE
,在 Android P 之前,这也是将图像排队到此类 ImageWriter
的唯一方法。从 Android P 开始,私有图像也可以通过它们的硬件缓冲区(在可用时)通过 Image#getHardwareBuffer()
方法访问。尝试访问私有图像的平面将返回一个空数组。
一旦新的输入 Image
被排队到 ImageWriter
中,就由下游组件(例如 ImageReader
或 android.hardware.camera2.CameraDevice
)来消费这些 Image
。如果下游组件无法以与 ImageWriter
生产速率至少相同的速度消费这些 Image
,则 dequeueInputImage
调用最终会阻塞,并且应用程序将不得不丢弃输入帧。
如果提供输入 Surface
的消费者组件放弃了该 Surface
,则尝试对 Image
进行排队或出队操作时会抛出 IllegalStateException
异常。
newInstance(Surface,maxImages)
参数说明
surface
:目标Surface
,表示该ImageWriter
将把图像数据生成到这个Surface
中。maxImages
:用户同时希望访问的最大Image
数量。这个参数决定了可以同时从ImageWriter
中出队的Image
对象的最大数量。请求更多的缓冲区会使用更多的内存,因此应该尽量使用最小必要数量。
返回值
- 返回一个新的
ImageWriter
实例。
Surface surface = ...; // 获取一个目标 Surface 对象,用于 ImageWriter 生成图像数据
int maxImages = 3; // 设置最大同时访问的 Image 数量
// 使用 newInstance 静态方法创建 ImageWriter 实例
ImageWriter imageWriter = ImageWriter.newInstance(surface, maxImages);
// 现在可以使用 imageWriter 对象进行操作,如出队或排队 Image 数据
在上面的示例中,我们首先获取一个目标 Surface
对象(surface
),然后定义了最大同时访问的 Image
数量(maxImages
)。接着,调用 ImageWriter.newInstance(surface, maxImages)
方法来创建一个新的 ImageWriter
实例(imageWriter
)。创建完成后,我们可以使用这个 imageWriter
对象来操作 Image
数据,如出队或排队 Image
。
newInstance(Surface,maxImages,format,width,height)
-
参数说明
surface
:目标Surface
,表示该ImageWriter
将把图像数据生成到这个Surface
中。maxImages
:用户同时希望访问的最大Image
数量。这个参数决定了可以同时从ImageWriter
中出队的Image
对象的最大数量。请求更多的缓冲区会使用更多的内存,因此应该尽量使用最小必要数量。format
:指定ImageWriter
的图像格式,可以是任何由ImageFormat
或PixelFormat
指定的有效格式。width
:输入图像的宽度。height
:输入图像的高度。
返回值
- 返回一个新的
ImageWriter
实例。
Surface surface = ...; // 获取一个目标 Surface 对象,用于 ImageWriter 生成图像数据 int maxImages = 3; // 设置最大同时访问的 Image 数量 int format = ImageFormat.YUV_420_888; // 设置图像格式为 YUV_420_888 int width = 1920; // 设置输入图像的宽度 int height = 1080; // 设置输入图像的高度 // 使用 newInstance 静态方法创建 ImageWriter 实例 ImageWriter imageWriter = ImageWriter.newInstance(surface, maxImages, format, width, height); // 现在可以使用 imageWriter 对象进行操作,如出队或排队 Image 数据
在上面的示例中,我们通过调用
ImageWriter.newInstance(surface, maxImages, format, width, height)
方法创建了一个新的ImageWriter
实例(imageWriter
)。我们指定了目标Surface
(surface
)、最大同时访问的Image
数量(maxImages
)、图像格式(format
)以及输入图像的尺寸(width
和height
)。创建完成后,我们可以使用这个imageWriter
对象来操作Image
数据,如出队或排队Image
。
通常会更倾向于使用newInstance(Surface,maxImages)
- 简单性和默认行为: 第一个
newInstance
方法不需要显式指定图像格式和尺寸。它使用默认的图像格式ImageFormat.UNKNOWN
和默认的尺寸-1
,这些默认值通常足够满足大多数情况。使用这个方法可以避免需要手动指定这些参数,简化了调用过程。 - 灵活性: 第一个方法不涉及显式设置图像格式和尺寸,使得它更具灵活性。它可以根据提供的
Surface
自动适应图像格式和尺寸,与下游消费者的要求一致。这对于快速生成图像数据而不需要详细设置的情况很有用。 - 适用性: 大多数情况下,应用程序不需要关注具体的图像格式和尺寸设置,而是希望快速创建一个
ImageWriter
实例以开始生成图像数据。第一个方法提供了一个简洁且常用的方式来实现这一目的。 - 避免错误: 第二个方法需要显式设置图像格式和尺寸,这增加了用户的责任和可能的错误。如果用户不了解或错误地设置了这些参数,可能会导致不可预见的问题或错误的图像生成行为。
因此,一般情况下,使用第一个 newInstance
方法更为常见和方便,特别是在只需生成图像数据而不需要关注详细设置的情况下。只有当需要显式控制图像格式和尺寸时,才会考虑使用第二个方法。
newInstance(Surface,maxImages,format)
参数说明
surface
:目标Surface
,表示该ImageWriter
将把图像数据生成到这个Surface
中。maxImages
:用户同时希望访问的最大Image
数量。这个参数决定了可以同时从ImageWriter
中出队的Image
对象的最大数量。请求更多的缓冲区会使用更多的内存,因此应该尽量使用最小必要数量。format
:指定ImageWriter
的图像格式,可以是任何由ImageFormat
或PixelFormat
指定的有效格式。
返回值
- 返回一个新的
ImageWriter
实例。
Surface surface = ...; // 获取一个目标 Surface 对象,用于 ImageWriter 生成图像数据
int maxImages = 3; // 设置最大同时访问的 Image 数量
int format = ImageFormat.YUV_420_888; // 设置图像格式为 YUV_420_888
// 使用 newInstance 静态方法创建 ImageWriter 实例
ImageWriter imageWriter = ImageWriter.newInstance(surface, maxImages, format);
// 现在可以使用 imageWriter 对象进行操作,如出队或排队 Image 数据
在上面的示例中,我们通过调用 ImageWriter.newInstance(surface, maxImages, format)
方法创建了一个新的 ImageWriter
实例(imageWriter
)。我们指定了目标 Surface
(surface
)、最大同时访问的 Image
数量(maxImages
)以及图像格式(format
)。创建完成后,我们可以使用这个 imageWriter
对象来操作 Image
数据,如出队或排队 Image
。
getMaxImages()
方法说明
getMaxImages()
:获取可以同时从ImageWriter
中出队的最大Image
数量。
返回值
- 返回一个整数,表示可以同时从
ImageWriter
中出队的最大Image
数量。
ImageWriter imageWriter = ...; // 假设已经创建了一个 ImageWriter 实例
// 获取可以同时从 ImageWriter 中出队的最大 Image 数量
int maxImages = imageWriter.getMaxImages();
System.out.println("Maximum number of Images that can be dequeued: " + maxImages);
在上面的示例中,我们通过调用 imageWriter.getMaxImages()
方法获取了可以同时从 ImageWriter
中出队的最大 Image
数量。这个值通常由创建 ImageWriter
时指定的 maxImages
参数确定。
getWidth() | getHeight()
getWidth()
- 方法说明: 获取
Image
对象的预期实际宽度(以像素为单位)。 - 返回值: 返回一个整数,表示
Image
对象的预期实际宽度。
getHeight()
- 方法说明: 获取
Image
对象的高度(以像素为单位)。 - 返回值: 返回一个整数,表示
Image
对象的高度。
Image image = ...; // 假设已经获取了一个 Image 对象
// 获取 Image 对象的宽度和高度
int width = image.getWidth();
int height = image.getHeight();
System.out.println("Image width: " + width);
System.out.println("Image height: " + height);
在上面的示例中,我们通过调用 image.getWidth()
和 image.getHeight()
方法分别获取了 Image
对象的宽度和高度。这些值通常由创建 Image
对象时指定的宽度和高度参数确定,如果没有显式设置宽度和高度,则取决于提供 Surface
的终端消费者端点的默认值。
dequeueInputImage()
方法说明
- 作用: 从
ImageWriter
中获取下一个可用的输入Image
,应用程序在此方法调用后拥有该Image
。应用程序在填充完Image
数据后,应将该Image
返回给ImageWriter
,以便下游消费者组件(例如CameraDevice
)进行消费。 - 阻塞行为: 如果应用程序已将所有可用的输入
Image
排队,并且下游消费者尚未消费任何Image
,则此调用将阻塞。当下游消费者消费并释放一个Image
时,将触发OnImageReleasedListener#onImageReleased
回调,表示有一个新的输入Image
可用。对于非PRIVATE
格式的ImageWriter
(即getFormat() != ImageFormat.PRIVATE
),建议在此回调稳定状态下触发后再获取下一个Image
。 - 返回值: 返回获取到的下一个可用的输入
Image
对象。 - 异常情况:
- 如果已经从
ImageWriter
中取出了最大数量(即maxImages
)的Image
,则抛出IllegalStateException
。 - 如果提供
Surface
的消费者组件放弃了输入Surface
,或者ImageWriter
的格式为PRIVATE
,则抛出IllegalStateException
。在 Android P 之前,如果ImageWriter
的格式为PRIVATE
,同样会抛出IllegalStateException
。
- 如果已经从
public Image dequeueInputImage() {
if (mDequeuedImages.size() >= mMaxImages) {
throw new IllegalStateException(
"Already dequeued max number of Images " + mMaxImages);
}
WriterSurfaceImage newImage = new WriterSurfaceImage(this);
nativeDequeueInputImage(mNativeContext, newImage);
mDequeuedImages.add(newImage);
newImage.mIsImageValid = true;
return newImage;
}
//使用
ImageWriter imageWriter = ...; // 假设已经创建了一个 ImageWriter 实例
try {
// 获取下一个可用的输入 Image
Image inputImage = imageWriter.dequeueInputImage();
// 在这里填充 inputImage 的数据...
// 将填充完数据的 inputImage 返回给 ImageWriter
imageWriter.queueInputImage(inputImage);
} catch (IllegalStateException e) {
// 处理异常情况
e.printStackTrace();
}
queueInputImage(Image image)
方法说明
- 作用: 将输入的
Image
排入队列,使得下游消费者可以访问。 - 参数:
image
:要排入队列的Image
。
- 异常:
- 如果
image
为null
,则抛出IllegalArgumentException
。 - 如果
image
已经被排入队列过,或者之前已经被丢弃过,或者输入的Surface
已被消费者组件放弃,则抛出IllegalStateException
。
- 如果
- 实现细节:
- 检查传入的
image
是否为null
,如果是则抛出异常。 - 检查传入的
image
是否由当前ImageWriter
拥有,如果是则进行下一步操作。 - 根据
image
的类型,可能需要将其从先前的所有者中分离,然后再将其附加到当前ImageWriter
,以确保image
可以被正确地排入队列。 - 调用本地方法将
image
排入队列,传递相关参数,如时间戳、数据空间等。 - 如果
image
由当前ImageWriter
拥有,则从mDequeuedImages
中移除该image
,并清理相关资源。 - 如果
image
不是由当前ImageWriter
拥有,则将其附加到当前ImageWriter
,然后再进行排队操作。 - 最后,关闭
image
或清理其状态,以确保资源正确释放。
- 检查传入的
注意事项
- 使用此方法前,请确保传入的
image
参数有效且未被排入队列过。 - 调用此方法后,传入的
image
将不再有效,不应再对其进行访问。 - 在传入
image
后,下游消费者将使用它,直到释放,并触发OnImageReleasedListener#onImageReleased
回调。
ImageWriter imageWriter = ...; // 假设已经创建了一个 ImageWriter 实例
// 从 ImageReader 中获取一个 Image
Image image = imageReader.acquireNextImage();
// 将获取的 Image 排入队列到 ImageWriter
imageWriter.queueInputImage(image);
// Image 使用完成后,不再需要对其进行操作,因为它已经被排入队列
// 下游消费者会在需要时处理该 Image
在上面的示例中,我们从 ImageReader
中获取了一个 Image
,然后将其排入队列到 ImageWriter
中。在此之后,不再需要对该 Image
进行其他操作,因为它已经被排入队列,供下游消费者处理。
getFormat()
方法说明
- 作用: 获取
ImageWriter
的图像格式。该格式可能与通过Image
对象的getFormat()
方法返回的图像格式不同。 - 返回值: 返回一个整数,表示
ImageWriter
的图像格式。 - 注意事项:
- 如果
ImageWriter
的格式为PRIVATE
(即mWriterFormat
为ImageFormat.PRIVATE
),则调用dequeueInputImage()
方法将导致抛出IllegalStateException
。在此情况下,无法直接从ImageWriter
中获取图像数据,而需要通过其他方式获取图像数据并将其排队到ImageWriter
中。
- 如果
ImageWriter imageWriter = ...; // 假设已经创建了一个 ImageWriter 实例
// 获取 ImageWriter 的图像格式
int writerFormat = imageWriter.getFormat();
System.out.println("ImageWriter format: " + writerFormat);
在上面的示例中,我们通过调用 imageWriter.getFormat()
方法获取了 ImageWriter
的图像格式。这个返回的整数值对应于 ImageFormat
中的常量,可以用于判断 ImageWriter
的图像格式是公共格式还是 PRIVATE
格式。
getUsage()
方法说明
- 作用: 获取
ImageWriter
的使用标志(usage flag)。该标志表示ImageWriter
的用途,例如用于输入、输出或其他特定的用途。 - 返回值: 返回一个
long
类型的值,表示ImageWriter
的使用标志。 - 注意事项:
- 如果在创建
ImageWriter
实例时未调用Builder
的setUsage
方法设置使用标志,或者设置了无效的使用标志值,那么调用getUsage()
方法可能返回无效的值。
- 如果在创建
ImageWriter imageWriter = ...; // 假设已经创建了一个 ImageWriter 实例
// 获取 ImageWriter 的使用标志
long usage = imageWriter.getUsage();
System.out.println("ImageWriter usage flag: " + usage);
在上面的示例中,我们通过调用 imageWriter.getUsage()
方法获取了 ImageWriter
的使用标志。这个值可以用于确定 ImageWriter
的用途,例如用于输入、输出或其他特定的用途。注意,在使用此方法之前,应确保在创建 ImageWriter
时已经调用了 Builder
的 setUsage
方法来设置使用标志.
getHardwareBufferFormat()
方法说明
- 作用: 获取
ImageWriter
的硬件缓冲区格式,这适用于使用构建器模式ImageWriter.Builder
创建ImageWriter
实例,并且在构建过程中使用了Builder
的setHardwareBufferFormat
和setDataSpace
方法设置了硬件缓冲区格式和数据空间。 - 返回值: 返回一个
int
类型的值,表示ImageWriter
的硬件缓冲区格式。 - 注意事项:
- 只有在使用
ImageWriter.Builder
创建ImageWriter
实例,并且在构建过程中设置了硬件缓冲区格式和数据空间时,才能使用此方法获取硬件缓冲区格式。 - 硬件缓冲区格式是指
ImageWriter
使用的硬件缓冲区的像素格式,例如AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM
等。
- 只有在使用
ImageWriter.Builder builder = new ImageWriter.Builder(...);
// 设置硬件缓冲区格式和数据空间
builder.setHardwareBufferFormat(AHardwareBuffer.Format.RGBA_8888);
builder.setDataSpace(DataSpace.V0_SRGB);
ImageWriter imageWriter = builder.build(); // 创建 ImageWriter 实例
// 获取 ImageWriter 的硬件缓冲区格式
int hardwareBufferFormat = imageWriter.getHardwareBufferFormat();
System.out.println("ImageWriter hardwareBuffer format: " + hardwareBufferFormat);
在上面的示例中,我们使用 ImageWriter.Builder
构建了一个 ImageWriter
实例,并在构建过程中设置了硬件缓冲区格式和数据空间。然后,我们通过调用 imageWriter.getHardwareBufferFormat()
方法获取了 ImageWriter
的硬件缓冲区格式。这个值表示 ImageWriter
使用的硬件缓冲区的像素格式。
getDataSpace()
方法说明
- 作用: 获取
ImageWriter
的数据空间,这适用于使用构建器模式ImageWriter.Builder
创建ImageWriter
实例,并且在构建过程中使用了Builder
的setDataSpace
方法设置了数据空间。 - 返回值: 返回一个
int
类型的值,表示ImageWriter
的数据空间。 - 注意事项:
- 只有在使用
ImageWriter.Builder
创建ImageWriter
实例,并且在构建过程中设置了数据空间时,才能使用此方法获取数据空间。 - 数据空间是指
ImageWriter
使用的数据格式和颜色空间,例如DataSpace.V0_SRGB
表示标准的 sRGB 颜色空间。
- 只有在使用
ImageWriter.Builder builder = new ImageWriter.Builder(...);
// 设置数据空间
builder.setDataSpace(DataSpace.V0_SRGB);
ImageWriter imageWriter = builder.build(); // 创建 ImageWriter 实例
// 获取 ImageWriter 的数据空间
int dataSpace = imageWriter.getDataSpace();
System.out.println("ImageWriter dataspace: " + dataSpace);
在上面的示例中,我们使用 ImageWriter.Builder
构建了一个 ImageWriter
实例,并在构建过程中设置了数据空间为 DataSpace.V0_SRGB
。然后,我们通过调用 imageWriter.getDataSpace()
方法获取了 ImageWriter
的数据空间。这个值表示 ImageWriter
使用的数据格式和颜色空间。
OnImageReleasedListener
public interface OnImageReleasedListener {
/**
* <p>
* Callback that is called when an input Image is released back to
* ImageWriter after the data consumption.
* </p>
* <p>
* The client can use this callback to be notified that an input Image
* has been consumed and released by the downstream consumer. More
* specifically, this callback will be fired for below cases:
* <li>The application dequeues an input Image via the
* {@link ImageWriter#dequeueInputImage dequeueInputImage()} method,
* uses it, and then queues it back to this ImageWriter via the
* {@link ImageWriter#queueInputImage queueInputImage()} method. After
* the downstream consumer uses and releases this image to this
* ImageWriter, this callback will be fired. This image will be
* available to be dequeued after this callback.</li>
* <li>The application obtains an Image from some other component (e.g.
* an {@link ImageReader}), uses it, and then queues it to this
* ImageWriter via {@link ImageWriter#queueInputImage queueInputImage()}.
* After the downstream consumer uses and releases this image to this
* ImageWriter, this callback will be fired.</li>
* </p>
*
* @param writer the ImageWriter the callback is associated with.
* @see ImageWriter
* @see Image
*/
void onImageReleased(ImageWriter writer);
}
OnImageReleasedListener
接口,用于监听当输入 Image
被消费并释放回 ImageWriter
时的回调事件。
接口说明
- 作用:
OnImageReleasedListener
接口用于监听当输入Image
被消费并释放回ImageWriter
时的事件。当下游消费者使用完输入Image
并将其释放回ImageWriter
后,将触发此回调。 - 方法:
void onImageReleased(ImageWriter writer)
:当输入Image
被消费并释放回ImageWriter
时调用的回调方法。通过此回调,客户端可以得知输入Image
已被消费和释放,并可以执行相应的处理。
- 回调场景:
- 当应用程序通过
ImageWriter
的dequeueInputImage()
方法获取一个输入Image
,使用完后通过queueInputImage()
方法将其返回给ImageWriter
,然后下游消费者使用并释放该Image
后,将触发此回调。 - 当应用程序从其他组件(例如
ImageReader
)获取一个Image
,使用完后通过queueInputImage()
方法将其传递给ImageWriter
,然后下游消费者使用并释放该Image
后,同样会触发此回调。
- 当应用程序通过
- 参数:
writer
:触发回调的ImageWriter
实例。
// 创建一个 ImageWriter 实例
ImageWriter imageWriter = ...;
// 设置 OnImageReleasedListener 监听器
imageWriter.setOnImageReleasedListener(new ImageWriter.OnImageReleasedListener() {
@Override
public void onImageReleased(ImageWriter writer) {
// 当输入 Image 被消费并释放回 ImageWriter 时触发该回调
System.out.println("Image released back to ImageWriter");
// 可以在这里执行相应的处理逻辑
}
});
// 在使用完 Image 后将其返回给 ImageWriter
Image inputImage = imageWriter.dequeueInputImage();
// 处理 inputImage 数据...
imageWriter.queueInputImage(inputImage);
setOnImageReleasedListener
方法说明
- 作用:
setOnImageReleasedListener()
方法用于注册一个监听器,当输入Image
返回到ImageWriter
时触发该监听器。 - 参数:
listener
:要注册的监听器,当输入Image
返回到ImageWriter
时将调用该监听器的回调方法。handler
:指定回调方法执行的Handler
,如果为null
,则表示在调用线程的 Looper 上执行回调方法。
- 异常:
- 如果未指定
handler
且调用线程没有 Looper,则抛出IllegalArgumentException
。
- 如果未指定
- 实现细节:
- 通过
synchronized
同步块确保线程安全。 - 如果指定了
listener
,则根据handler
获取对应的Looper
。 - 如果
handler
为null
,则使用当前线程的 Looper。 - 如果没有 Looper,则抛出异常。
- 如果当前的
mListenerHandler
为null
或者其 Looper 不同于获取到的 Looper,则创建一个新的ListenerHandler
。 - 将
listener
和mListenerHandler
分别设置为传入的参数或null
。
- 通过
ImageWriter imageWriter = ...; // 假设已经创建了一个 ImageWriter 实例
// 创建一个 OnImageReleasedListener 监听器
ImageWriter.OnImageReleasedListener listener = new ImageWriter.OnImageReleasedListener() {
@Override
public void onImageReleased(ImageWriter writer) {
// 当输入 Image 被消费并释放回 ImageWriter 时触发该回调
System.out.println("Image released back to ImageWriter");
// 可以在这里执行相应的处理逻辑
}
};
// 创建一个 Handler,用于指定回调执行的线程
Handler handler = new Handler(Looper.getMainLooper()); // 在主线程的 Looper 上执行回调
// 注册监听器
imageWriter.setOnImageReleasedListener(listener, handler);
在上面的示例中,我们创建了一个 ImageWriter.OnImageReleasedListener
监听器,并通过 setOnImageReleasedListener()
方法将其注册到 ImageWriter
实例中。当输入 Image
被消费并释放回 ImageWriter
时,将触发监听器的 onImageReleased()
回调方法。我们还通过 Handler
指定了回调方法在主线程的 Looper 上执行,以确保在主线程中处理回调逻辑。
attachAndQueueInputImage
方法说明
- 作用: 将源
Image
附加到ImageWriter
并将其排入队列,使得下游消费者可以使用该Image
。 - 参数:
image
:要附加和排入队列的源Image
。
- 异常:
- 如果
Image
没有从其先前的所有者中分离,或者Image
已经附加到此ImageWriter
,或者源Image
无效,则抛出IllegalStateException
。
- 如果
- 实现细节:
- 检查传入的
image
是否为null
,如果是则抛出异常。 - 检查传入的
image
是否已经属于当前ImageWriter
,如果是则抛出异常。 - 检查传入的
image
是否可以附加,如果不可以则抛出异常。 - 获取
image
的裁剪区域和硬件缓冲区格式。 - 根据
image
的类型调用相应的本地方法来附加和排入队列。 - 在附加和排入队列完成后,释放相关资源并关闭
image
。
- 检查传入的
注意事项
- 使用此方法前,必须确保源
Image
已经从其先前的所有者中分离。 - 调用此方法后,
ImageWriter
将拥有该Image
,直到消费者使用并释放该Image
。 - 调用者需要负责在适当的时候关闭
Image
,以释放其占用的资源。
ImageWriter imageWriter = ...; // 假设已经创建了一个 ImageWriter 实例
// 获取一个源 Image(例如从 ImageReader 中获取)
Image sourceImage = imageReader.acquireNextImage();
// 将源 Image 附加并排入队列到 ImageWriter
imageWriter.attachAndQueueInputImage(sourceImage);
// 使用完毕后,记得关闭源 Image
sourceImage.close();
ImageWriter.Builder
用于构建 ImageWriter
实例的构建器模式。构建器模式允许您设置 ImageWriter
实例的各种属性,然后通过 build()
方法创建相应的实例。
构造函数
java
Copy code
public Builder(@NonNull Surface surface)
- 构造一个新的
ImageWriter.Builder
实例。 surface
参数是用于生产图像数据的目标Surface
。
方法概述
setWidthAndHeight(int width, int height)
- 设置图像的宽度和高度。
- 默认大小取决于由下游终端点提供的
Surface
。
setMaxImages(int maxImages)
- 设置图像的最大数量。
- 默认值为 1。
setImageFormat(int imageFormat)
- 设置
ImageWriter
的图像格式。 - 如果设置了此属性,则会覆盖
Surface
提供的默认格式。
- 设置
setHardwareBufferFormat(int hardwareBufferFormat)
- 设置
ImageWriter
的硬件缓冲区格式。 - 与
setDataSpace
方法一起使用,用于替换setImageFormat
。
- 设置
setDataSpace(int dataSpace)
- 设置
ImageWriter
的数据空间。
- 设置
setUsage(long usage)
- 设置
ImageWriter
的使用标志。
- 设置
build()
- 构建并返回一个新的
ImageWriter
对象,根据设置的属性。
- 构建并返回一个新的
注意事项
- 构建器模式允许逐步构建对象,通过设置不同的属性。
- 每个方法都返回
Builder
实例,以便进行链式调用。 - 构建器通过设置不同的属性来创建不同配置的
ImageWriter
实例
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 long mUsage = -1;
private @HardwareBuffer.Format int mHardwareBufferFormat = HardwareBuffer.RGBA_8888;
private @NamedDataSpace int mDataSpace = DataSpace.DATASPACE_UNKNOWN;
private boolean mUseSurfaceImageFormatInfo = true;
private boolean mUseLegacyImageFormat = false;
/**
* Constructs a new builder for {@link ImageWriter}.
*
* @param surface The destination Surface this writer produces Image data into.
*
* @throws IllegalArgumentException if the surface is already abandoned.
*/
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.
*
* @throws IllegalArgumentException if {@code imageFormat} is invalid.
*/
@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 int dataSpace) {
mDataSpace = dataSpace;
mImageFormat = ImageFormat.UNKNOWN;
mUseLegacyImageFormat = false;
mUseSurfaceImageFormatInfo = false;
return this;
}
/**
* Set the usage flag of this ImageWriter.
*
* <p>If this function is not called, usage bit will be set
* to {@link HardwareBuffer#USAGE_CPU_WRITE_OFTEN} if the image format is not
* {@link ImageFormat#PRIVATE PRIVATE}.</p>
*
* @param usage The intended usage of the images produced by this ImageWriter.
* @return the Builder instance with customized usage flag.
*
* @see HardwareBuffer
* @see #getUsage
*/
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);
}
}
}
Surface surface = ...; // 假设已经有一个 Surface 实例
// 创建一个 ImageWriter.Builder 对象
ImageWriter.Builder builder = new ImageWriter.Builder(surface);
// 设置图像的宽度和高度
builder.setWidthAndHeight(1920, 1080);
// 设置图像的最大数量
builder.setMaxImages(2);
// 设置图像格式为 JPEG
builder.setImageFormat(ImageFormat.JPEG);
// 设置硬件缓冲区格式为 RGBA_8888
builder.setHardwareBufferFormat(HardwareBuffer.RGBA_8888);
// 设置数据空间为 REC709
builder.setDataSpace(DataSpace.DATASPACE_V0_SCRGB_LINEAR);
// 构建 ImageWriter 对象
ImageWriter imageWriter = builder.build();
在上面的示例中,我们创建了一个 ImageWriter.Builder
对象,并设置了一些属性,最后通过 build()
方法构建了一个 ImageWriter
实例。通过构建器模式,可以根据需要定制 ImageWriter
对象的配置。
小结:
ImageWriter
类用于将图像数据写入指定的 Surface
,供后续消费者(例如摄像头设备)使用。它允许应用程序创建、管理和操作图像,以便在图像流或其他图像数据源中使用。
主要特性和方法
- 图像写入
dequeueInputImage()
: 从ImageWriter
中获取下一个可用的输入图像。应用程序需要填充这些图像数据,然后通过queueInputImage()
将其提交给消费者。
- 图像队列管理
queueInputImage(Image image)
: 将输入图像排入队列,供消费者使用。setMaxImages(int maxImages)
: 设置ImageWriter
允许同时排队的最大图像数量。
- 图像格式和属性
getFormat()
: 获取ImageWriter
的图像格式。getWidth()
,getHeight()
: 获取图像的宽度和高度。getMaxImages()
: 获取ImageWriter
允许的最大图像数量。
- 图像监听器和回调
setOnImageReleasedListener(OnImageReleasedListener listener, Handler handler)
: 设置图像释放监听器,用于监控图像何时被消费者释放。
- 图像属性设置(Builder 模式)
ImageWriter.Builder
: 通过构建器模式创建ImageWriter
实例,可以逐步设置图像的参数,如图像格式、宽度、高度等。
使用示例
javaCopy code// 创建 ImageWriter 实例
ImageWriter imageWriter = ImageWriter.newInstance(surface, 2, ImageFormat.RGBA_8888);
// 获取图像并填充数据
Image image = imageWriter.dequeueInputImage();
if (image != null) {
// 填充图像数据
fillImageData(image);
// 将图像提交给 ImageWriter
imageWriter.queueInputImage(image);
}
// 设置图像释放监听器
imageWriter.setOnImageReleasedListener(new ImageWriter.OnImageReleasedListener() {
@Override
public void onImageReleased(ImageWriter writer) {
// 处理图像释放事件
Log.d(TAG, "Image released: " + writer);
}
}, null);
以上示例演示了如何使用 ImageWriter
类创建图像、填充图像数据并提交给消费者。通过设置图像释放监听器,可以及时处理图像的释放事件。
总的来说,ImageWriter
类提供了一组功能丰富的方法,用于管理和操作图像数据,是 Android 图像处理和传输中重要的组件之一。