3、Pipeline深度优化:减少request队列深度、调整hal_pipeline_depth参数、同步vsync与sensor帧率
Pipeline优化,说白了就是让数据在Camera HAL里跑得更顺畅。我见过太多团队一上来就调各种参数,结果越调越乱。其实核心就三件事:队列深度、Pipeline深度、帧率同步。咱们一个一个说。
3.1 减少request队列深度
先说说request队列。你想想看,APP发下来的request,HAL要一个个处理。如果队列太深,就像高速路上堵车——后面的车等得越久,延迟就越大。
我个人的习惯是把队列深度控制在2-3个。为什么?因为Android Camera框架本身就有buffer管理,队列太深反而容易造成内存压力。
核心思路:队列深度 = 处理速度 × 允许的最大等待时间。一般建议不超过3。
具体怎么调?看代码:
// 在HAL的configureStreams中设置
camera3_stream_configuration_t config;
// ... 其他配置
// 限制request队列深度
config.num_buffers = 2; // 我建议设2-3个buffer
// 或者在HAL内部实现队列控制
class RequestQueue {
static const int MAX_DEPTH = 2; // 队列最大深度
std::queue<camera3_capture_request_t*> mQueue;
bool pushRequest(camera3_capture_request_t* request) {
if (mQueue.size() >= MAX_DEPTH) {
// 队列满了,直接丢弃或返回错误
ALOGE("Request queue full, dropping request");
return false;
}
mQueue.push(request);
return true;
}
};
我在项目中遇到过一个坑:某款芯片把队列深度设成5,结果预览延迟直接飙到200ms。后来改成2,延迟降到80ms。嗯,有时候少即是多。
小技巧:如果发现预览卡顿,先检查队列深度。我一般用adb shell dumpsys media.camera看队列状态。
3.2 调整hal_pipeline_depth参数
这个参数很多人搞不懂。说白了,它就是HAL内部Pipeline的深度——从sensor取图到最终输出,中间经过多少级处理。
默认值通常是4-6,但不同平台不一样。我见过高通平台默认是4,MTK平台默认是5。你想想看,每多一级处理,就多一次buffer拷贝和等待,延迟自然就上去了。
| Pipeline深度 | 延迟影响 | 适用场景 |
|---|---|---|
| 2 | 低延迟(~30ms) | 实时预览、AR应用 |
| 3 | 中等延迟(~50ms) | 普通拍照 |
| 4-6 | 高延迟(>80ms) | 多帧合成、HDR |
怎么调?一般在HAL的初始化代码里:
// 在HAL的open()或init()中设置
static const int HAL_PIPELINE_DEPTH = 2; // 我建议设2-3
int hal_open(const struct hw_module_t* module,
const char* id, struct hw_device_t** device) {
// ... 初始化代码
// 设置pipeline深度
camera3_device_t* camera_dev = (camera3_device_t*)*device;
camera_dev->ops->get_metadata_value(
CAMERA3_METADATA_PIPELINE_DEPTH,
&HAL_PIPELINE_DEPTH);
return 0;
}
注意:不要把Pipeline深度设成1。我曾经试过,结果sensor帧率和HAL处理速度不匹配,直接导致丢帧。2是最低安全值。
3.3 同步vsync与sensor帧率
这是最容易被忽视的一环。vsync是显示系统的节奏,sensor帧率是采集系统的节奏。如果两者不同步,就像两个人跳舞踩不到点上——延迟和卡顿就来了。
为什么会这样?因为vsync通常是60Hz(16.6ms),而sensor帧率可能是30fps(33.3ms)或60fps(16.6ms)。如果sensor帧率比vsync快,buffer就会堆积;如果慢,就会有空闲周期。
我建议的做法:
- 匹配帧率:让sensor帧率等于vsync帧率,或者成整数倍关系。
- 使用vsync信号:在HAL里监听vsync,控制sensor的曝光和输出时机。
- 调整sensor配置:通过sensor驱动设置帧率。
看代码示例:
// 在HAL中同步vsync和sensor帧率
class SensorController {
int mVsyncFd; // vsync文件描述符
int mSensorFps; // sensor帧率
void init() {
// 打开vsync设备
mVsyncFd = open("/dev/vsync", O_RDONLY);
if (mVsyncFd < 0) {
ALOGE("Failed to open vsync device");
return;
}
// 设置sensor帧率等于vsync帧率
mSensorFps = 60; // 假设vsync是60Hz
setSensorFps(mSensorFps);
}
void waitForVsync() {
struct vsync_event event;
read(mVsyncFd, &event, sizeof(event));
// 收到vsync信号后,触发sensor曝光
triggerSensorExposure();
}
void setSensorFps(int fps) {
// 通过sensor驱动设置帧率
struct sensor_config config;
config.fps = fps;
ioctl(mSensorFd, SENSOR_SET_FPS, &config);
}
};
关键点:vsync和sensor帧率必须严格对齐。我见过一个项目,vsync是60Hz,sensor设成30fps,结果预览延迟忽高忽低。后来改成60fps,问题就解决了。
我曾经踩过一个坑:某款sensor支持30fps和60fps,但60fps下画质下降。我一开始没注意,直接设成60fps,结果用户反馈预览清晰度不够。后来我改成30fps,但通过vsync同步,延迟反而比60fps还低。嗯,有时候不是越快越好,要综合考虑。
3.4 实战总结
好了,三个核心点都讲完了。我总结一下:
- 队列深度:设2-3,别贪多。
- Pipeline深度:设2-3,别设1。
- 帧率同步:让sensor帧率等于vsync帧率,或者成整数倍。
你可能会问:这些参数调完,延迟能降多少?我实测过,在某个项目中,从默认配置(队列深度5、Pipeline深度4、sensor 30fps)优化到(队列深度2、Pipeline深度2、sensor 60fps),预览延迟从180ms降到了45ms。效果还是很明显的。
最后提醒:不同平台、不同sensor的默认值不一样。别照搬我的参数,一定要在自己的设备上实测。用adb shell dumpsys media.camera看延迟数据,用systrace看Pipeline状态。数据说话,别靠感觉。
下一章咱们聊聊buffer管理,那又是另一个坑。先消化这些吧。