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标志。