【Android车载学习笔记】第六天:QCarCamera介绍

SA8295平台QCarCamera

QCarCamera(简称QCarCam)是高通车载AIS(Automotive Imaging Subsystem)成像子系统对外统一C语言API,是SA8295座舱/智驾开发板读取GMSL摄像头图像的标准中间件。

核心特性:

  1. 上层API跨高通SoC通用:SA8155/SA8295/SA8650函数名、调用流程完全一致;
  2. 底层资源板级强绑定libqcarcam.so、头文件、硬件配置文件仅适配对应8295项目BSP,无法跨开发板复用;
  3. 双系统支持:Android IVI座舱、QNX ADAS智驾共用同一套API,仅内存/权限编译配置存在差异。

SA8295 QCarCam整体业务运行流程

1. 整体时序流程图

完整执行链路:

qcarcam_initializeqcarcam_query_inputsqcarcam_openqcarcam_s_buffersqcarcam_start → 循环qcarcam_get_frame图像处理 → qcarcam_release_frameqcarcam_stopqcarcam_closeqcarcam_uninitialize

2. 分步业务说明(SA8295专属要点)

  1. 全局初始化 qcarcam_initialize
    建立应用与AIS后台服务通信通道,SA8295必须传入匹配BSP基线的版本号,版本不匹配直接初始化失败。
  2. 枚举摄像头 qcarcam_query_inputs
    读取qcarcam_config.xml硬件配置,获取8295开发板CSI通道、GMSL解串器、各路Camera ID、支持分辨率/格式;8295最多支持6路GMSL3摄像头,8155仅4路,结构体字段存在差异。
  3. 打开指定摄像头 qcarcam_open
    根据Input ID创建相机句柄,多路AVM环视场景可并行打开4路/6路句柄。
  4. 配置图像缓冲区 qcarcam_s_buffers
    SA8295推荐buffer数量5~8个,底层使用ION物理共享内存;QNX平台使用pmem物理内存,缓冲区分配逻辑与Android不同。
  5. 启动数据流 qcarcam_start
    触发Sensor上电、SerDes寄存器初始化、ISP流水线启动,开始持续输出图像帧。
  6. 取流+释放帧(核心业务)
    qcarcam_get_frame阻塞/非阻塞获取一帧图像数据,完成算法/预览处理后必须调用qcarcam_release_frame归还buffer,否则缓冲区耗尽断流。
  7. 停止、关闭、反初始化
    必须完整执行逆序销毁,否则8295会出现Camera资源锁死,进程重启也无法重新打开摄像头。

核心API

头文件依赖:#include "QCarCam.h"#include "QCarCam_types.h",仅能使用当前SA8295项目BSP内的头文件,其他平台头文件结构体字段缺失。

1. 生命周期基础API

(1)全局初始化
c 复制代码
qcarcam_ret_t qcarcam_initialize(qcarcam_init_t* p_init_params);
  • 入参:版本号、日志等级、事件回调;
  • SA8295坑点:qc_version必须和libqcarcam.so编译基线一致,混用8155头文件会报参数错误407;
  • 返回值:QCARCAM_RET_OK成功,其余为错误码。
(2)枚举可用摄像头
c 复制代码
qcarcam_ret_t qcarcam_query_inputs(qcarcam_input_t* p_inputs, unsigned int size, unsigned int* ret_size);
  • 作用:查询8295开发板硬件配置的所有Camera,获取ID、宽高、色彩格式;
  • 使用方式:先传size=0获取摄像头总数,再分配数组二次读取详细信息。
(3)打开摄像头获取句柄
c 复制代码
qcarcam_hndl_t qcarcam_open(qcarcam_input_desc_t desc);
  • 入参:input_id摄像头编号(0~5,8295最大6路)、流模式(预览/RAW);
  • 返回:非空句柄代表打开成功;NULL表示CSI通道被占用、硬件配置错误。
(4)缓冲区配置(关键)
c 复制代码
qcarcam_ret_t qcarcam_s_buffers(qcarcam_hndl_t hndl, qcarcam_buffers_t* p_buffers);
  • 配置:buffer个数、图像宽高、NV12/NV21/RAW格式、内存平面;
  • SA8295规范:n_buffers≥5,低于2帧直接启动失败。
(5)启停数据流
c 复制代码
qcarcam_ret_t qcarcam_start(qcarcam_hndl_t hndl);  // 开始出图
qcarcam_ret_t qcarcam_stop(qcarcam_hndl_t hndl);    // 停止出图
(6)帧存取核心接口
c 复制代码
// 获取一帧图像
qcarcam_ret_t qcarcam_get_frame(qcarcam_hndl_t hndl, qcarcam_frame_t* p_frame, uint32_t timeout_ms);
// 处理完成归还buffer(不可省略)
qcarcam_ret_t qcarcam_release_frame(qcarcam_hndl_t hndl, qcarcam_frame_t* p_frame);
  • timeout_ms:0非阻塞,>0阻塞等待帧;
  • 漏调用release_frame:8295底层buffer池耗尽,几秒后断流,无报错日志。
(7)销毁资源
c 复制代码
qcarcam_ret_t qcarcam_close(qcarcam_hndl_t hndl);    // 关闭单路相机
qcarcam_ret_t qcarcam_uninitialize(void);            // 全局反初始化
  • 强制规范:所有句柄close完成后再调用uninitialize,否则下次初始化直接失败。

2. 辅助控制API

  1. qcarcam_s_param:设置事件回调、曝光、增益、镜像翻转等ISP参数;
  2. qcarcam_get_param:读取当前帧率、硬件状态、错误事件;
  3. qcarcam_get_error_str:错误码转可读字符串,用于8295日志调试。

3. 标准极简Demo代码片段(SA8295通用)

c 复制代码
#include "QCarCam.h"
#include <stdio.h>

int main() {
    // 1. 全局初始化
    qcarcam_init_t init_param = {0};
    init_param.qc_version = QCARCAM_VERSION;
    qcarcam_ret_t ret = qcarcam_initialize(&init_param);
    if(ret != QCARCAM_RET_OK) {
        printf("初始化失败 err:%d\n", ret);
        return -1;
    }

    // 2. 查询摄像头数量
    unsigned int cam_cnt = 0;
    qcarcam_query_inputs(NULL, 0, &cam_cnt);
    qcarcam_input_t inputs[6] = {0}; // SA8295最大6路
    qcarcam_query_inputs(inputs, cam_cnt, &cam_cnt);

    // 3. 打开0号摄像头
    qcarcam_input_desc_t desc = {.input_id = 0};
    qcarcam_hndl_t hCam = qcarcam_open(desc);
    if(hCam == NULL) {
        printf("打开摄像头0失败\n");
        goto deinit;
    }

    // 4. 配置Buffer
    qcarcam_buffers_t buf_cfg = {0};
    buf_cfg.n_buffers = 5;
    buf_cfg.color_fmt = QCARCAM_FMT_NV12;
    buf_cfg.width = 1920;
    buf_cfg.height = 1080;
    qcarcam_s_buffers(hCam, &buf_cfg);

    // 5. 启动采集
    qcarcam_start(hCam);

    // 循环取流100帧
    for(int i=0; i<100; i++) {
        qcarcam_frame_t frame = {0};
        ret = qcarcam_get_frame(hCam, &frame, 1000);
        if(ret == QCARCAM_RET_OK) {
            // 图像处理逻辑
            printf("获取帧 %d, 宽度:%d\n", i, frame.planes[0].width);
            qcarcam_release_frame(hCam, &frame); // 必须归还
        }
    }

    // 逆序销毁
    qcarcam_stop(hCam);
    qcarcam_close(hCam);
deinit:
    qcarcam_uninitialize();
    return 0;
}

QCarCam_types.h 和 QCarCam_diag_types.h

  1. QCarCam_types.h :业务主类型,所有相机生命周期、帧、buffer、输入设备、参数、返回码都在这里,写采集程序必包含
  2. QCarCam_diag_types.h:诊断/调试专用类型,日志等级、故障码、诊断事件、性能统计、ISP诊断信息,用于问题定位、量产诊断;
  3. 两者均板级强绑定:SA8295 基线内的头文件不能和 SA8155/8650 混用,结构体存在扩展字段差异;
  4. 依赖关系:QCarCam.h 内部依赖 QCarCam_types.h,诊断接口函数依赖 QCarCam_diag_types.h

第一部分 QCarCam_types.h 核心结构体/枚举详解

1. 基础返回码枚举 qcarcam_ret_t

所有 API 统一返回该类型,判断接口成败

c 复制代码
typedef enum {
    QCARCAM_RET_OK = 0,                 // 成功
    QCARCAM_RET_FAILED,                 // 通用失败
    QCARCAM_RET_INVALID_PARAM,          // 参数非法
    QCARCAM_RET_OUT_OF_MEM,             // 内存不足
    QCARCAM_RET_TIMEOUT,                // 取帧超时
    QCARCAM_RET_NO_FRAME,               // 无可用帧
    QCARCAM_RET_BUSY,                   // 相机被占用
    QCARCAM_RET_NOT_SUPPORTED,          // 当前硬件/ISP不支持该功能
    QCARCAM_RET_INVALID_HANDLE,         // 无效相机句柄
    QCARCAM_RET_VERSION_MISMATCH,       // 应用版本与AIS服务不匹配(8295高频报错407根源)
    QCARCAM_RET_RESOURCE_LOCKED,        // 硬件资源锁死
    QCARCAM_RET_IO_ERROR,               // CSI/SerDes硬件IO错误
} qcarcam_ret_t;

SA8295 重点坑:QCARCAM_RET_VERSION_MISMATCH 绝大多数是头文件/libqcarcam.so/AIS基线三套不配套导致。

2. 全局初始化结构体 qcarcam_init_t

qcarcam_initialize() 入参,控制全局客户端配置

c 复制代码
typedef struct {
    uint32_t qc_version;               // QCARCAM_VERSION,必须与lib基线一致
    uint32_t log_level;                // 日志等级(与diag日志枚举互通)
    void (*event_cb)(qcarcam_event_t event, void* user_data); // 全局事件回调
    void* user_data;                   // 回调透传私有数据
    uint32_t reserved[8];
} qcarcam_init_t;
  • qc_version:8295 BSP 内宏 QCARCAM_VERSION,直接赋值即可,手写数字极易版本不匹配。

3. 图像色彩格式枚举 qcarcam_color_fmt_t

配置 buffer、帧数据格式,SA8295 支持 GMSL RAW+YUV 多路输出

c 复制代码
typedef enum {
    QCARCAM_FMT_INVALID = 0,
    QCARCAM_FMT_NV12,      // 主流预览、AVM环视使用
    QCARCAM_FMT_NV21,
    QCARCAM_FMT_YUYV,
    QCARCAM_FMT_UYVY,
    QCARCAM_FMT_RAW8,
    QCARCAM_FMT_RAW10,     // 8295 ISP原生10bit RAW(8155部分基线缺失)
    QCARCAM_FMT_RAW12,
    QCARCAM_FMT_RGB888,
    QCARCAM_FMT_ARGB8888,
} qcarcam_color_fmt_t;

4. 相机输入设备信息 qcarcam_input_t / qcarcam_input_desc_t

qcarcam_input_t(qcarcam_query_inputs 输出,硬件静态信息)

c 复制代码
typedef struct {
    uint32_t input_id;                 // 相机编号 0~5(SA8295最大6路)
    char sensor_name[32];              // 传感器名称 ar0144/ox08b等
    uint32_t width;                    // 硬件最大分辨率宽
    uint32_t height;                   // 硬件最大分辨率高
    qcarcam_color_fmt_t fmt_list[16];   // 该摄像头支持的格式列表
    uint32_t fmt_cnt;
    uint32_t csi_port;                 // 绑定的CSI通道号
    uint32_t gmsl_ver;                 // GMSL1/GMSL2/GMSL3(8295独有)
    uint32_t reserved[16];
} qcarcam_input_t;

qcarcam_input_desc_t(qcarcam_open 入参,打开相机配置)

c 复制代码
typedef struct {
    uint32_t input_id;                 // 指定打开哪一路相机
    uint32_t stream_mode;              // 预览流/RAW流/算法流
    uint32_t reserved[4];
} qcarcam_input_desc_t;

5. 缓冲区配置结构体 qcarcam_buffers_t

qcarcam_s_buffers() 设置帧池,采集核心配置

c 复制代码
typedef struct {
    uint32_t n_buffers;                // buffer数量,SA8295推荐5~8
    uint32_t width;
    uint32_t height;
    qcarcam_color_fmt_t color_fmt;
    uint32_t stride[4];                // 各平面行宽
    uint32_t plane_cnt;                // YUV2平面、RAW单平面
    uint32_t buf_size;                 // 单帧总字节大小
    uint32_t reserved[8];
} qcarcam_buffers_t;

坑:n_buffers < 2 时 qcarcam_start 直接返回 NOT_SUPPORTED。

6. 帧数据结构体 qcarcam_frame_t(业务最常用)

qcarcam_get_frame 输出,承载一帧图像内存、时间戳、元数据

c 复制代码
typedef struct {
    // 图像平面地址(ION/PMEM物理虚拟地址)
    qcarcam_plane_t planes[4];
    uint32_t plane_cnt;

    uint64_t timestamp_us;             // Sensor出图时间戳,同步AVM拼接关键
    uint32_t frame_id;                 // 帧序号,丢帧可通过序号判断
    uint32_t width;
    uint32_t height;
    qcarcam_color_fmt_t fmt;

    // 帧状态标记
    uint32_t flags;
    #define QCARCAM_FRAME_FLAG_DROPPED  (1 << 0)  // 丢帧标记
    #define QCARCAM_FRAME_FLAG_ERROR    (1 << 1)  // 该帧图像损坏

    void* priv_buf_handle;             // 底层buffer私有句柄,release时内部使用,业务不可修改
    uint32_t reserved[8];
} qcarcam_frame_t;

附属:qcarcam_plane_t 单平面内存信息

c 复制代码
typedef struct {
    uint8_t* virt_addr;        // 用户态虚拟地址(直接取图像数据)
    uint64_t phys_addr;        // 物理地址,GPU/算法渲染使用
    int32_t fd;                // ION buffer fd(Android特有,QNX无意义)
    uint32_t stride;
    uint32_t size;
} qcarcam_plane_t;

7. 参数控制类型:qcarcam_param_t

qcarcam_s_param / qcarcam_get_param 用于曝光、增益、镜像、回调设置

c 复制代码
typedef enum {
    QCARCAM_PARAM_EXPOSURE,
    QCARCAM_PARAM_GAIN,
    QCARCAM_PARAM_FLIP_H,          // 水平翻转
    QCARCAM_PARAM_FLIP_V,          // 垂直翻转
    QCARCAM_PARAM_EVENT_CB,         // 单路相机帧事件回调
    QCARCAM_PARAM_FPS,
    QCARCAM_PARAM_AE_MODE,         // 自动曝光开关
    QCARCAM_PARAM_AWB_MODE,         // 自动白平衡
    // SA8295扩展:多路同步、HDR、畸变矫正参数
    QCARCAM_PARAM_MULTI_SYNC,
    QCARCAM_PARAM_HDR_ENABLE,
} qcarcam_param_id_t;

// 参数值通用联合体
typedef union {
    uint32_t u32;
    int32_t i32;
    float f32;
    void (*cb)(...);
    uint8_t data[64];
} qcarcam_param_val_t;

typedef struct {
    qcarcam_param_id_t id;
    qcarcam_param_val_t val;
} qcarcam_param_t;

8. 事件枚举 qcarcam_event_t

全局/单路回调上报硬件异常、状态变更

c 复制代码
typedef enum {
    QCARCAM_EVENT_FRAME_READY,      // 帧就绪
    QCARCAM_EVENT_ERROR,            // 硬件故障
    QCARCAM_EVENT_SENSOR_LOST,       // Sensor断线、GMSL断开
    QCARCAM_EVENT_STREAM_STOP,      // 流主动停止
    QCARCAM_EVENT_OVERFLOW,         // buffer池耗尽
} qcarcam_event_t;

第二部分 QCarCam_diag_types.h 诊断类型头文件

该头文件不参与基础图像采集,用于日志调试、故障诊断、性能统计、产线检测,定位8295相机卡顿、丢帧、硬件断线问题必备。

1. 诊断日志等级枚举 qcarcam_diag_log_level_t

qcarcam_init_t.log_level 一一对应

c 复制代码
typedef enum {
    QCARCAM_DIAG_LOG_LEVEL_NONE = 0,    // 关闭日志
    QCARCAM_DIAG_LOG_LEVEL_ERROR,       // 仅错误
    QCARCAM_DIAG_LOG_LEVEL_WARN,        // 警告+错误
    QCARCAM_DIAG_LOG_LEVEL_INFO,       // 普通信息
    QCARCAM_DIAG_LOG_LEVEL_DEBUG,       // 调试日志(开发使用)
    QCARCAM_DIAG_LOG_LEVEL_VERBOSE,     // 全量底层打印(性能损耗大)
} qcarcam_diag_log_level_t;

开发调试设置 log_level = QCARCAM_DIAG_LOG_LEVEL_DEBUG;量产版本仅保留 ERROR。

2. 诊断故障码 qcarcam_diag_fault_code_t

用于读取相机硬件永久/瞬时故障,产线检测、售后定位

c 复制代码
typedef enum {
    // 通用故障
    QCARCAM_DIAG_FAULT_NONE = 0,
    QCARCAM_DIAG_FAULT_SENSOR_PWR_FAIL,     // Sensor供电失败
    QCARCAM_DIAG_FAULT_SERDES_COMM_ERR,     // GMSL解串器通讯失败
    QCARCAM_DIAG_FAULT_CSI_LANE_ERR,        // CSI Lane信号丢失
    QCARCAM_DIAG_FAULT_BUFFER_OVERRUN,      // buffer溢出丢帧
    QCARCAM_DIAG_FAULT_ISP_HANG,            // ISP流水线卡死
    QCARCAM_DIAG_FAULT_CLOCK_ERR,           // 相机时钟异常
    // SA8295 GMSL3专属故障
    QCARCAM_DIAG_FAULT_GMSL_LINK_DEGRADE,   // GMSL链路信号衰减
} qcarcam_diag_fault_code_t;

3. 单路相机性能统计结构体 qcarcam_diag_stream_stats_t

用于统计帧率、丢帧、处理耗时,分析AVM卡顿根源

c 复制代码
typedef struct {
    uint64_t total_frames;          // 总出帧数量
    uint64_t dropped_frames;        // 累计丢帧
    uint32_t avg_fps;               // 平均帧率
    uint32_t min_fps;
    uint32_t max_fps;
    uint64_t total_get_frame_latency_us; // 取帧平均耗时
    uint32_t underrun_count;        // 底层buffer空次数
    uint32_t overrun_count;         // buffer占满次数
    uint32_t reserved[16];
} qcarcam_diag_stream_stats_t;

配套API:qcarcam_diag_get_stream_stats(),传入相机句柄获取实时统计。

读取GMSL、CSI物理层状态,排查图像花屏、闪屏

c 复制代码
typedef struct {
    uint32_t input_id;
    uint32_t gmsl_lane_rate;        // GMSL链路速率
    uint32_t csi_lane_count;        // CSI lane数量 2/4
    uint32_t ber_count;             // 误码计数,非0代表线路干扰
    uint32_t serdes_chip_id;        // 解串器芯片ID MAX96717/MAX9296
    uint8_t link_health;            // 0=正常,>0链路衰减
    uint32_t reserved[8];
} qcarcam_diag_link_info_t;

5. ISP 诊断参数结构体 qcarcam_diag_isp_info_t

读取AE/AWB/ISP内部状态,图像偏暗、偏色调试使用

c 复制代码
typedef struct {
    float current_exposure;
    float current_gain;
    uint32_t ae_status;         // 自动曝光锁定/调整中
    uint32_t awb_status;
    float lux_value;            // 环境亮度
    uint32_t isp_temp_c;        // ISP硬件温度,高温会降帧
    uint32_t reserved[8];
} qcarcam_diag_isp_info_t;

6. 全局诊断配置结构体 qcarcam_diag_config_t

动态控制诊断日志、故障上报开关

c 复制代码
typedef struct {
    qcarcam_diag_log_level_t log_lvl;
    uint32_t fault_report_enable;   // 故障实时上报开关
    uint32_t stats_collect_enable;  // 性能统计采集开关
    uint32_t reserved[4];
} qcarcam_diag_config_t;

配套接口:

  • qcarcam_diag_set_config():动态修改日志等级
  • qcarcam_diag_get_fault():查询指定相机故障码

两者头文件使用场景区分

1. 只做图像采集(AVM/预览/算法取图)

仅需包含:

c 复制代码
#include "QCarCam.h"
#include "QCarCam_types.h"

无需引入 diag 头文件,减少编译依赖。

2. 需要调试、性能分析、故障检测、产线自检

双文件都要引入:

c 复制代码
#include "QCarCam.h"
#include "QCarCam_types.h"
#include "QCarCam_diag_types.h"

适用场景:

  1. 开发阶段打印详细调试日志;
  2. 排查丢帧、花屏、GMSL断线;
  3. 统计帧率延迟做性能优化;
  4. 量产自检程序,上报相机硬件故障。

双头文件完整demo片段

c 复制代码
#include <stdio.h>
#include "QCarCam.h"
#include "QCarCam_types.h"
#include "QCarCam_diag_types.h"

int main() {
    qcarcam_init_t init = {0};
    init.qc_version = QCARCAM_VERSION;
    // 开启诊断调试日志
    init.log_level = QCARCAM_DIAG_LOG_LEVEL_DEBUG;
    qcarcam_ret_t ret = qcarcam_initialize(&init);

    // 打开相机省略...
    qcarcam_hndl_t hCam;

    // 读取诊断性能统计
    qcarcam_diag_stream_stats_t stats = {0};
    qcarcam_diag_get_stream_stats(hCam, &stats);
    printf("丢帧总数:%llu,平均帧率:%d\n", stats.dropped_frames, stats.avg_fps);

    // 读取硬件链路故障
    qcarcam_diag_fault_code_t fault;
    qcarcam_diag_get_fault(hCam, &fault);
    if(fault != QCARCAM_DIAG_FAULT_NONE) {
        printf("相机硬件故障码:%d\n", fault);
    }

    // 释放资源省略
    return 0;
}

总结

  1. 代码复用 vs 编译环境隔离
    上层C业务逻辑(初始化、取流)可直接在8155/8295通用;但编译工具链、libqcarcam、头文件、硬件配置文件必须每块8295项目独立,不可跨板拷贝。
  2. 版本强绑定三要素
    头文件、libqcarcam.so、开发板AIS服务基线三者必须来自同一套SA8295 BSP,任意一个版本不一致都会初始化失败。
  3. 资源销毁严格逆序
    不完整关闭会导致Camera硬件锁死,只能重启开发板恢复。
  4. 缓冲区配置规范
    SA8295推荐5~8个buffer,低于2帧直接启动失败,取流后必须release_frame。
  5. 编译环境红线
    禁止使用PC本地NDK、系统GCC编译QCarCam程序,仅允许BSP自带交叉工具链。