4. Stream类型与用途:INPUT、OUTPUT、BIDIRECTIONAL
在Camera HAL3架构中,Stream是数据流的抽象载体,它定义了图像数据在Camera子系统中的流向、用途和生命周期。正确理解Stream类型是配置Pipeline、选择图像格式、优化性能的前提。HAL3定义了三种Stream类型:INPUT、OUTPUT和BIDIRECTIONAL,它们分别对应数据流入、流出和双向复用场景。
4.1 三种Stream类型的核心区别
下表从数据流向、典型用途、Buffer所有权和HAL实现约束四个维度对比三种Stream类型:
| 对比维度 | INPUT | OUTPUT | BIDIRECTIONAL |
|---|---|---|---|
| 数据流向 | 外部 → Camera HAL | Camera HAL → 消费者 | 双向:HAL可读写 |
| 典型用途 | Reprocessing(重处理)、ZSL输入 | Preview、Video、Snapshot、YUV回调 | 内部算法Buffer复用、GPU纹理共享 |
| Buffer所有权 | HAL从外部获取Buffer | HAL填充Buffer后交给消费者 | HAL内部管理,可读可写 |
| HAL实现约束 | 需实现processCaptureRequest_input | 需实现processCaptureRequest_output | 需实现processCaptureRequest_bidirectional |
| Stream配置标记 | CAMERA3_STREAM_INPUT | CAMERA3_STREAM_OUTPUT | CAMERA3_STREAM_BIDIRECTIONAL |
关键认知:在Android Camera HAL3中,绝大多数应用场景只使用OUTPUT Stream。INPUT和BIDIRECTIONAL主要用于高级特性如ZSL(零快门延迟)、HDR+、多帧降噪等需要重处理(Reprocessing)的场景。BIDIRECTIONAL在Android 10+中已标记为deprecated,建议新开发优先使用INPUT+OUTPUT组合替代。
4.2 典型应用场景详解
4.2.1 Preview(预览)—— OUTPUT Stream
Preview是Camera最基本的场景,用于在屏幕上显示实时取景画面。它使用OUTPUT Stream,HAL将Sensor数据经过ISP处理后输出到SurfaceFlinger或TextureView。
- 典型格式:IMPLEMENTATION_DEFINED(通常为NV12或NV21)
- 分辨率:通常匹配屏幕分辨率或更小(如1080p)
- 帧率:30fps或60fps
- Buffer数量:至少3个(双缓冲+1个备用)
- Stream配置标记:CAMERA3_STREAM_OUTPUT
性能要点:Preview Stream的Buffer队列深度直接影响预览延迟。建议配置至少4个Buffer以应对HAL处理抖动。如果Preview与Video同时运行,两者应共享同一路Stream以减少内存开销。
4.2.2 Video(视频录制)—— OUTPUT Stream
Video场景与Preview类似,但输出目标为MediaCodec编码器。它同样使用OUTPUT Stream,但格式和分辨率要求不同。
| 参数 | Preview | Video |
|---|---|---|
| 目标消费者 | SurfaceFlinger/TextureView | MediaCodec(视频编码器) |
| 推荐格式 | IMPLEMENTATION_DEFINED | YUV420_888 / NV12 |
| 分辨率 | 屏幕分辨率或更低 | 标准视频分辨率(如1080p、4K) |
| 帧率稳定性要求 | 中等 | 高(需恒定帧率) |
| Buffer使用模式 | 轮转显示 | 编码器消费后释放 |
4.2.3 Snapshot(拍照)—— OUTPUT Stream
Snapshot用于拍摄高分辨率静态图像。它使用OUTPUT Stream,但通常需要更高的分辨率和更丰富的格式支持。
- 典型格式:JPEG、YUV420_888、RAW10/RAW16
- 分辨率:Sensor最大分辨率或用户指定分辨率
- Buffer数量:通常1~2个(拍照完成后释放)
- Stream配置标记:CAMERA3_STREAM_OUTPUT
- 特殊要求:JPEG格式需要HAL内部完成编码,或输出YUV后由应用层编码
常见陷阱:当同时配置Preview和Snapshot Stream时,如果两者分辨率差异过大(如Preview为1080p,Snapshot为48MP),HAL可能需要Binning或Downscale操作。务必在configure_streams中检查Stream组合的可行性,避免运行时崩溃。
4.2.4 ZSL(零快门延迟)—— INPUT + OUTPUT Stream组合
ZSL是高级拍照特性,它允许用户在按下快门时获取按下瞬间之前的帧,从而消除快门延迟。ZSL需要INPUT Stream和OUTPUT Stream协同工作。
ZSL工作流程:
- 环形Buffer:HAL内部维护一个环形Buffer,持续缓存最近N帧的YUV或RAW数据。
- Preview OUTPUT Stream:正常输出预览帧到Surface。
- ZSL INPUT Stream:当用户按下快门时,应用创建一个新的CaptureRequest,将INPUT Stream指向环形Buffer中的某一帧。
- Reprocessing:HAL从INPUT Stream读取缓存的帧,进行重处理(如降噪、锐化、JPEG编码),输出到Snapshot OUTPUT Stream。
ZSL Stream配置要点:INPUT Stream的格式必须与环形Buffer中缓存的格式一致(通常为YUV420或RAW)。HAL需要在configure_streams时声明支持CAMERA3_STREAM_INPUT,并在static_metadata中标记ANDROID_REQUEST_AVAILABLE_CAPABILITIES_REPROCESSING。
4.3 Stream类型与典型场景的对应关系
下表总结了四种典型场景与Stream类型的映射关系:
| 场景 | Stream类型 | 数量 | 典型格式 | 备注 |
|---|---|---|---|---|
| Preview | OUTPUT | 1 | IMPLEMENTATION_DEFINED | 基础场景,必须支持 |
| Video | OUTPUT | 1 | YUV420_888 / NV12 | 可与Preview共享Stream |
| Snapshot | OUTPUT | 1~2 | JPEG / YUV420 / RAW | 高分辨率,可配置多个 |
| ZSL | INPUT + OUTPUT | 2~3 | YUV420 / RAW | 需Reprocessing能力 |
| BIDIRECTIONAL(旧) | BIDIRECTIONAL | 1 | YUV420 / NV12 | Android 10+已弃用 |
4.4 Stream类型与图像格式的关联
不同的Stream类型对图像格式有不同约束:
- OUTPUT Stream:支持所有标准格式(NV12、NV21、YUV420_888、JPEG、RAW等)。格式选择取决于消费者需求。
- INPUT Stream:通常只支持YUV420或RAW格式。JPEG格式不能作为INPUT Stream,因为JPEG是有损压缩,重处理会引入质量损失。
- BIDIRECTIONAL Stream:格式限制与INPUT类似,主要用于内部算法处理。
格式转换提示:当INPUT Stream与OUTPUT Stream格式不同时(如INPUT为RAW,OUTPUT为JPEG),HAL需要在Reprocessing过程中完成Demosaic、颜色校正、缩放和JPEG编码。这要求HAL实现完整的ISP Pipeline,对性能要求较高。
4.5 Stream类型配置的SVG架构图
下图展示了四种典型场景中Stream类型的配置关系:
4.6 Stream配置代码示例
以下代码展示了在HAL的configure_streams中配置不同Stream类型的典型实现:
// Camera HAL3 Stream配置示例
int configure_streams(const camera3_device_t *device,
camera3_stream_configuration_t *stream_list) {
// 遍历所有Stream
for (size_t i = 0; i < stream_list->num_streams; i++) {
camera3_stream_t *stream = stream_list->streams[i];
switch (stream->stream_type) {
case CAMERA3_STREAM_OUTPUT:
// OUTPUT Stream:预览、视频、拍照
ALOGI("Configuring OUTPUT Stream #%zu", i);
ALOGI(" Format: %d, Width: %d, Height: %d",
stream->format, stream->width, stream->height);
// 根据格式分配不同的Buffer
if (stream->format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
// Preview/Video场景
stream->max_buffers = 4; // 至少4个Buffer
stream->usage = GRALLOC_USAGE_HW_TEXTURE |
GRALLOC_USAGE_HW_RENDER;
} else if (stream->format == HAL_PIXEL_FORMAT_BLOB) {
// JPEG Snapshot场景
stream->max_buffers = 2;
stream->usage = GRALLOC_USAGE_SW_WRITE_OFTEN;
} else if (stream->format == HAL_PIXEL_FORMAT_YCbCr_420_888) {
// YUV Snapshot或Video场景
stream->max_buffers = 3;
stream->usage = GRALLOC_USAGE_HW_CAMERA_WRITE;
}
break;
case CAMERA3_STREAM_INPUT:
// INPUT Stream:ZSL重处理输入
ALOGI("Configuring INPUT Stream #%zu (ZSL Reprocessing)", i);
ALOGI(" Format: %d, Width: %d, Height: %d",
stream->format, stream->width, stream->height);
// INPUT Stream只支持YUV或RAW
if (stream->format != HAL_PIXEL_FORMAT_YCbCr_420_888 &&
stream->format != HAL_PIXEL_FORMAT_RAW16) {
ALOGE("Unsupported INPUT format: %d", stream->format);
return -EINVAL;
}
stream->max_buffers = 1; // 每次只处理一帧
stream->usage = GRALLOC_USAGE_HW_CAMERA_READ;
break;
case CAMERA3_STREAM_BIDIRECTIONAL:
// BIDIRECTIONAL Stream(已弃用,仅兼容旧代码)
ALOGW("BIDIRECTIONAL Stream detected (deprecated)");
stream->max_buffers = 2;
stream->usage = GRALLOC_USAGE_HW_CAMERA_READ |
GRALLOC_USAGE_HW_CAMERA_WRITE;
break;
default:
ALOGE("Unknown stream type: %d", stream->stream_type);
return -EINVAL;
}
}
// 验证Stream组合的合法性
if (!validate_stream_combination(stream_list)) {
ALOGE("Invalid stream combination");
return -EINVAL;
}
return 0;
}
代码注意事项:
- INPUT Stream的
max_buffers通常设为1,因为每次Reprocessing只处理一帧。如果设为大于1,HAL需要支持多帧并行重处理。 - BIDIRECTIONAL Stream的
usage必须同时包含HW_CAMERA_READ和HW_CAMERA_WRITE,否则Gralloc分配可能失败。 - 在
validate_stream_combination中,需要检查INPUT Stream的分辨率是否与环形Buffer匹配,以及OUTPUT Stream的格式是否支持编码。
4.7 本章小结
Stream类型是Camera HAL3 Pipeline的基石。理解INPUT、OUTPUT和BIDIRECTIONAL的区别,能够帮助开发者:
- 正确配置Pipeline:根据场景需求选择合适的Stream类型和格式。
- 优化内存使用:合理设置Buffer数量和usage标志,避免内存浪费或分配失败。
- 支持高级特性:ZSL、HDR+、多帧降噪等特性依赖INPUT Stream的正确实现。
- 避免兼容性问题:BIDIRECTIONAL Stream已弃用,新开发应使用INPUT+OUTPUT组合。