1. 项目背景与核心问题
在Android相机开发中,高通Camx框架作为底层硬件抽象层(HAL)的核心组件,通常需要通过标准API接收来自上层应用的参数配置。但实际开发中,我们经常遇到需要绕过标准流程直接控制相机硬件的场景。比如:
- 快速原型验证时避免反复修改APP层代码
- 开发独立的后台服务进程时
- 实现特殊拍摄模式(如高速连拍)的性能优化
传统做法是通过APP下发参数并备份到HAL层,这种方式存在两个明显缺陷:
- 参数传递链路长,调试效率低
- 无法实现完全脱离APP的自主控制
关键痛点:如何在不依赖APP的情况下,直接在C++层构造参数并调用Camx接口?
2. 原生Camx接口深度解析
2.1 基础接口结构分析
高通Camx框架的核心接口采用面向对象设计,主要操作集中在CamX::HAL3Device类中。两个最关键的接口是:
cpp复制// 设备打开接口
CamxResult Open(
UINT32 cameraId,
const HwModule* pHwModule);
// 流配置接口
CamxResult ConfigureStreams(
const camera3_stream_configuration_t* pStreamConfig);
Open接口关键点:
- 仅需传入
cameraId(0表示后置主摄) - 内部会初始化ISP、Sensor等硬件模块
- 返回
CamxResultSuccess表示设备就绪
ConfigureStreams接口关键点:
- 参数是
camera3_stream_configuration_t结构体指针 - 需要预配置输入/输出流数组
- 必须指定每个流的格式、尺寸、用途
2.2 参数结构体内存布局
camera3_stream_configuration_t的定义值得深入研究:
cpp复制typedef struct camera3_stream_configuration {
uint32_t num_streams; // 流数量
camera3_stream_t **streams; // 流指针数组
uint32_t operation_mode; // 操作模式
// ...其他元数据字段
} camera3_stream_configuration_t;
其中每个camera3_stream_t需要配置:
stream_type(INPUT/OUTPUT/BIDIRECTIONAL)width/height(必须为ISP支持的尺寸)format(如HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED)usage(GRALLOC_USAGE_HW_CAMERA_xxx)
3. 自定义参数实现方案
3.1 静态参数定义方案
对于固定场景,可以直接硬编码参数:
cpp复制// 相机ID定义
const UINT32 MAIN_CAMERA_ID = 0;
// 预览流配置
camera3_stream_t previewStream = {
.stream_type = CAMERA3_STREAM_OUTPUT,
.width = 1920,
.height = 1080,
.format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
.usage = GRALLOC_USAGE_HW_CAMERA_WRITE,
// ...其他字段初始化为0
};
// 快照流配置
camera3_stream_t snapshotStream = {
.stream_type = CAMERA3_STREAM_OUTPUT,
.width = 4032,
.height = 3024,
.format = HAL_PIXEL_FORMAT_BLOB,
.usage = GRALLOC_USAGE_SW_READ_OFTEN,
};
3.2 动态参数构造方案
更灵活的写法是动态构造参数:
cpp复制std::vector<camera3_stream_t*> CreateStreamConfig(
bool enablePreview,
bool enableSnapshot) {
std::vector<camera3_stream_t*> streams;
if (enablePreview) {
auto* pStream = new camera3_stream_t();
pStream->width = 1920;
// ...其他字段初始化
streams.push_back(pStream);
}
if (enableSnapshot) {
// 类似构造快照流
}
return streams;
}
3.3 完整调用示例
cpp复制void StartCustomPreview() {
// 1. 打开设备
CamxResult ret = g_HAL3Device.Open(MAIN_CAMERA_ID, nullptr);
if (ret != CamxResultSuccess) {
ALOGE("Open camera failed: %d", ret);
return;
}
// 2. 构造流配置
camera3_stream_configuration config = {};
config.num_streams = 2;
config.operation_mode = CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE;
camera3_stream_t* streams[] = {&previewStream, &snapshotStream};
config.streams = streams;
// 3. 配置流
ret = g_HAL3Device.ConfigureStreams(&config);
if (ret != CamxResultSuccess) {
ALOGE("Configure streams failed: %d", ret);
}
// 4. 启动预览(需补充request逻辑)
}
4. 关键问题与实战技巧
4.1 常见崩溃场景排查
问题1:流配置后立即崩溃
- 检查
streams指针的生命周期 - 确保所有
camera3_stream_t字段已正确初始化
问题2:格式不支持错误
- 必须使用ISP支持的格式组合:
- 预览:
HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED - 拍照:
HAL_PIXEL_FORMAT_BLOB或HAL_PIXEL_FORMAT_YCbCr_420_888
- 预览:
4.2 性能优化技巧
-
流数量最小化:
- 每增加一个流意味着额外的内存和带宽开销
- 典型场景只需1个预览流+1个拍照流
-
尺寸对齐优化:
cpp复制// 好的做法:使用ISP原生支持的分辨率 pStream->width = 1920; pStream->height = 1080; // 避免使用非常规尺寸 pStream->width = 1936; // 可能触发软件缩放 -
内存复用配置:
cpp复制
pStream->usage |= GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER;
4.3 高级用法:动态参数热切换
通过维护参数池实现模式切换:
cpp复制class StreamConfigPool {
public:
void AddPreset(const char* name, const camera3_stream_configuration& config);
bool SwitchTo(const char* name);
private:
std::map<std::string, camera3_stream_configuration> m_presets;
};
// 使用示例
pool.AddPreset("4K", Create4KConfig());
pool.SwitchTo("4K"); // 实时切换分辨率
5. 工程实践建议
-
参数验证机制:
cpp复制bool ValidateConfig(const camera3_stream_configuration& config) { if (config.num_streams == 0) return false; for (uint32_t i = 0; i < config.num_streams; ++i) { if (config.streams[i]->width % 32 != 0) { ALOGW("Width not aligned: %d", config.streams[i]->width); return false; } } return true; } -
错误恢复策略:
- 首次配置失败后尝试回退到默认配置
- 记录错误码和配置参数便于离线分析
-
跨版本兼容处理:
cpp复制#if defined(CAMX_API_VERSION) && (CAMX_API_VERSION >= 0x3000) config.session_parameters = nullptr; // 新版本新增字段 #endif -
调试工具链推荐:
camxhal3test:高通官方验证工具GStreamer:用于验证帧数据通路adb logcat -s CamX:查看底层日志