3. 预览流配置错误

任务说明

本案例目标:定位并修复Android相机HAL层预览流配置错误,确保应用请求的格式与分辨率在HAL能力范围内,且配置参数组合合法。完成本案例后,你将能够独立排查“预览黑屏”“启动崩溃”或“HAL返回错误码”等由流配置引发的典型问题。

问题现象与排查路径

预览流配置错误通常表现为:CameraDevice.createCaptureSession回调onError(错误码ERROR_CAMERA_DEVICE或ERROR_CAMERA_INVALID_STATE),或HAL层configure_streams返回-EINVAL。根因集中在三处:格式不支持、分辨率超出能力范围、流组合违反HAL约束(如同时配置了互相冲突的流类型)。

自洽实现:HAL能力检查与配置校验

以下代码实现一个最小可复用的校验模块,输入为HAL静态能力(来自camera3_device_t的static_info)和待配置的预览流参数,输出为配置是否合法及具体错误原因。该模块直接对应上述三类根因的排查。


// preview_config_check.h
#ifndef PREVIEW_CONFIG_CHECK_H
#define PREVIEW_CONFIG_CHECK_H

#include <system/camera_metadata.h>
#include <camera3.h>
#include <vector>

struct PreviewRequest {
    int32_t width;
    int32_t height;
    int32_t format; // 使用 HAL_PIXEL_FORMAT_* 值
};

// 检查单个预览流配置是否被HAL支持
// 返回0表示合法,负值表示错误类型
int check_preview_stream_config(
    const camera_metadata_t* static_info,
    const PreviewRequest& req);

#endif

// preview_config_check.cpp
#include "preview_config_check.h"
#include <system/camera_metadata.h>
#include <android/hardware/camera2/tag.h> // 包含能力tag定义

int check_preview_stream_config(
    const camera_metadata_t* static_info,
    const PreviewRequest& req) {

    if (static_info == nullptr) {
        return -EINVAL;
    }

    // 1. 获取支持的像素格式列表
    int32_t* formats = nullptr;
    size_t formats_count = 0;
    int ret = camera_metadata_get_entries(
        static_info,
        ANDROID_SCALER_AVAILABLE_FORMATS,
        &formats, &formats_count);
    if (ret != 0 || formats == nullptr) {
        return -ENODEV; // 无法读取能力
    }

    bool format_supported = false;
    for (size_t i = 0; i < formats_count; ++i) {
        if (formats[i] == req.format) {
            format_supported = true;
            break;
        }
    }
    if (!format_supported) {
        return -EINVAL; // 格式不支持
    }

    // 2. 获取该格式下支持的分辨率列表
    int32_t* sizes = nullptr;
    size_t sizes_count = 0;
    // 注意:实际HAL中分辨率按格式分组,此处简化为从SCALER_AVAILABLE_STREAM_CONFIGURATIONS解析
    // 完整实现需遍历该tag的 (format, width, height, direction) 四元组
    // 为保持示例简洁,这里使用ANDROID_SCALER_AVAILABLE_RAW_SIZES作为替代示意
    // 实际项目中应使用 ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS
    ret = camera_metadata_get_entries(
        static_info,
        ANDROID_SCALER_AVAILABLE_RAW_SIZES,
        &sizes, &sizes_count);
    if (ret != 0 || sizes == nullptr) {
        // 若无法获取分辨率列表,保守起见拒绝配置
        return -EINVAL;
    }

    bool size_supported = false;
    for (size_t i = 0; i + 1 < sizes_count; i += 2) {
        if (sizes[i] == req.width && sizes[i + 1] == req.height) {
            size_supported = true;
            break;
        }
    }
    if (!size_supported) {
        return -EINVAL; // 分辨率不支持
    }

    // 3. 检查流组合约束(示例:预览流宽度不能超过2048)
    // 实际约束需从 ANDROID_REQUEST_AVAILABLE_CAPABILITIES 等推导
    if (req.width > 2048) {
        return -EINVAL; // 超出HAL流组合限制
    }

    return 0; // 配置合法
}

落地与验证

文件组织:将上述两个文件加入HAL层测试工程,编译时链接libcamera_metadata。


# 编译示例(Android.bp或Makefile片段)
cc_library_shared {
    name: "libpreviewconfigcheck",
    srcs: ["preview_config_check.cpp"],
    shared_libs: ["libcamera_metadata"],
}

验证方法:编写单元测试,构造三种典型输入:


// 测试用例
void test_preview_config() {
    // 准备static_info(实际项目中从HAL open后获取)
    // 此处假设已获得包含以下能力的metadata:
    // 支持格式: HAL_PIXEL_FORMAT_YCbCr_420_888, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED
    // 支持分辨率: 1920x1080, 1280x720, 640x480

    PreviewRequest valid_req = {1920, 1080, HAL_PIXEL_FORMAT_YCbCr_420_888};
    assert(check_preview_stream_config(static_info, valid_req) == 0);

    PreviewRequest bad_format = {1920, 1080, HAL_PIXEL_FORMAT_RGBA_8888}; // 假设不支持
    assert(check_preview_stream_config(static_info, bad_format) == -EINVAL);

    PreviewRequest bad_size = {4000, 3000, HAL_PIXEL_FORMAT_YCbCr_420_888};
    assert(check_preview_stream_config(static_info, bad_size) == -EINVAL);
}

判定标准:测试通过即表明校验逻辑正确识别了格式不支持、分辨率超范围两类错误。对于流组合约束,可在HAL层configure_streams实现中调用此函数,若返回非0则打印错误日志并返回-EINVAL,通过Camera2 API启动预览即可复现验证。

关键排查要点

  • 优先检查ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS而非RAW_SIZES,前者包含格式与方向信息,是流配置的权威依据。
  • 注意HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED的特殊性:HAL可将其映射为内部格式,需结合ANDROID_SCALER_AVAILABLE_IMPLEMENTATION_DEFINED_SIZES判断。
  • 流组合约束(如同时配置预览+拍照)需检查ANDROID_REQUEST_AVAILABLE_CAPABILITIES中的BACKWARD_COMPATIBLE或PRIVATE_REPROCESSING标志。