背景描述:
Android11 Camera HAL层需要实现框架定义的一组HIDL Interface。以Google Demo为例子,HAL层接口实现如下:
namespace default_camera_hal {
...
const camera3_device_ops_t Camera::sOps = {.initialize = default_camera_hal::initialize,.configure_streams = default_camera_hal::configure_streams,.register_stream_buffers = nullptr,.construct_default_request_settings =default_camera_hal::construct_default_request_settings,.process_capture_request = default_camera_hal::process_capture_request,.get_metadata_vendor_tag_ops = nullptr,.dump = default_camera_hal::dump,.flush = default_camera_hal::flush,.reserved = {0},
};}
HAL层该如何实现flush处理呢?接口该如何实现,
- 可以参考官方的设计说明文档
- 可以看接口定义
- 可以参考Demo实现
先思考一下:相机工作(这里指request&result处理)有哪些线程,这些线程是如何工作的。
Android11 Camera HAL Demo中request&result处理
Camera HAL在相机初始化阶段会创建两个线程:
- 一个负责从缓存队列中取框架下发的request进行处理,对应执行函数enqueueRequestBuffers。
- 一个负责将处理结果通过result回调回框架,对应执行函数dequeueRequestBuffers。
request&result正常处理过程如下图,
从框架下来的有效request先缓存到request_queue_队列中,然后process_capture_request()接口返回。enqueueRequestBuffes线程从request_queue_队列中一帧一帧的取request,送设备节点进行处理(HAL Demo V4L2节点是1个Buffer, 这里可以根据自己需要调大, 上图假设是5个buffer)。dequeueRequestBuffers线程不断查询设备节点是不是处理完一帧,如果有帧输出则取出图像数据,处理后回调result给框架。
从上图观察request是有转移的,框架下来的一个request可能在两个位置:
- request_queue_队列,等待被送往设备
- 从request_queue_队列出来了,已经送到设备了,这时候在buffers_里关联着。
有没有一个统一的位置可以跟踪一个框架下来的request,直到它返回框架?有的,就是mInFlightTracker。一个有效的request被添加到request_queue_队列的同时会加到mInFlightTracker跟踪队列,直到回调回框架。
Android11 Camera HAL Demo中的mInFlightTracker
mInFlightTracker(RequestTracker类的实例)是Camera类中用来跟踪框架request的跟踪器。从RequestTracker可以观察到一个request从进入HAL层到出HAL层的整个时间。接下来分析RequestTracker是如何跟踪一个request的。
RequestTracker是通过一个map表(std::map<uint32_t, std::shared_ptr<CaptureRequest>>)实现对request跟踪管理。
mInFlightTracker对request的跟踪涉及四个操作:
- mInFlightTracker->Remove(request) 取消对一个request的跟踪。
- mInFlightTracker->Clear(&requests) 取出跟踪的所有request
- mInFlightTracker->CanAddRequest(*request) 检查是不是能添加对这个request的跟踪。
- mInFlightTracker->Add(request) 添加一个request到跟踪队列。
Camera HAL Demo中跟踪事务流如下图:
注:notify和sendResult都是向框架回调的接口,这里为了强调"notify回调->result回调"图中只画了sendResult向外箭头。
如上图可以看出mInFlightTracker跟踪request涉及:
- 一个"入":框架调process capture request处理流程,如果mInFlightTracker容量有余,并且是有效request,工作状态的HAL将"接收"这个request。
- 三个"出":
- 正常输出:dequeueRequestBuffers线程对于正常处理的一帧结果回调
- 异常输出:enqueueRequestBuffers线程对于异常处理的一帧回调和flush流程将HAL层未返回的请求立即回调。
Android11 Camera HAL Demo中flush处理
对于上图四个线程同时执行调度,Demo HAL实现逻辑是这样的:
- request处理过程中,如果flush先被调到,mInFlightTrackerLock将mInFlightTracker锁住,这时flush线程将所有未返回的request立即返回。process capture request在flush执行期间被调到,由于mInFlightTrackerLock锁被获取,需要等flush执行完之后获取mInFlightTrackerLock再执行。
- request处理过程中,如果process capture request先被调到,mInFlightTrackerLock被获取。flush在process capture request执行期间被调用,由于mInFlightTrackerLock锁被获取,需要等process capture request执行完后获取mInFlightTrackerLock再执行。
flush和processCaptureRequest实现中有如下注释:
int Camera::processCaptureRequest(camera3_capture_request_t *temp_request)
{// TODO(b/32917568): A capture request submitted or ongoing during a flush// should be returned with an error; for now they are mutually exclusive.android::Mutex::Autolock tl(mInFlightTrackerLock);...
}int Camera::flush()
{// TODO(b/32917568): Synchronization. Behave "appropriately"// (i.e. according to camera3.h) if process_capture_request()// is called concurrently with this (in eigher order).// Since the callback to completeRequest also may happen on a separate// thread, this function should behavae nicely concurrently with that too.android::Mutex::Autolock tl(mInFlightTrackerLock);...
}
说明flush期间的request应尽快以error状态返回框架。
回看当前flush()和processCaptureRequest() mInFlightTrackerLock锁的使用会导致两个结果:
- flush和processCaptureRequest不能并发执行,如果processCaptureRequest先获取到锁,flush不能被立即处理,不能达到最佳加速冲刷效果。
- flush和processCaptureRequest不能并发执行,如果flush先获取到锁,processCaptureRequest在处理期间下来的request被识别为有效的request。我们知道框架request和flush是异步并发的,flush被调用是为了将HAL层遗留的request尽快清空。
- 如果flush和processCaptureRequest并发执行,flush开始执行一段时间这时processCaptureRequest输入一个request,这个request该如何处理?立即返回。(确保flush执行后HAL层无request待返回)。
- 如果flush和processCaptureRequest并发执行,processCaptureRequest先执行一段时间这时flush被调用,最近输入的这个request应立即返回。(确保flush执行后HAL层无request待返回)。
所以flush和processCaptureRequest改如何协调,关键是为了达到一个效果,即flush执行完HAL无request要返回。