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工作流程:

  1. 环形Buffer:HAL内部维护一个环形Buffer,持续缓存最近N帧的YUV或RAW数据。
  2. Preview OUTPUT Stream:正常输出预览帧到Surface。
  3. ZSL INPUT Stream:当用户按下快门时,应用创建一个新的CaptureRequest,将INPUT Stream指向环形Buffer中的某一帧。
  4. 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类型的配置关系:

Camera HAL3 Stream类型与典型场景配置架构 Camera HAL3 Stream类型与典型场景配置架构 Camera HAL3 Preview (OUTPUT) Video (OUTPUT) Snapshot (OUTPUT) ZSL (INPUT+OUTPUT) SurfaceFlinger MediaCodec ImageReader/JPEG 环形Buffer (INPUT) Reprocessing请求 图例说明 OUTPUT Stream:数据从HAL流向消费者 INPUT Stream:数据从外部流入HAL(ZSL重处理) 环形Buffer:HAL内部缓存的历史帧数据 Reprocessing路径:应用请求重处理历史帧

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组合。