Android 10 引入了可选的相机 HAL3 缓冲区管理 API,使您能够实现缓冲区管理逻辑,以便在相机 HAL 实现中达成不同的内存和拍摄延迟折衷权衡。
相机 HAL 需要 N 个请求(其中 N 等于管道深度)在其管道中排队,但通常不需要同时使用所有 N 组输出缓冲区。
例如,在 HAL 管道中排队的请求可能有 8 个,但 HAL 只需使用管道最后阶段的 2 个请求的输出缓冲区。在搭载 Android 9 及更低版本的设备上,当请求在 HAL 中排队时,相机框架会分配缓冲区,从而使 HAL 中有 6 组未使用的缓冲区。在 Android 10 中,相机 HAL3 缓冲区管理 API 允许分离输出缓冲区,从而释放 6 组缓冲区的空间。就高端设备而言,这样可以节省数百兆的内存,而对于低内存设备来说,这也非常有用。
图 1 为搭载 Android 9 及更低版本的设备的相机 HAL 接口示意图。图 2 显示的是已实现相机 HAL3 缓冲区管理 API 的 Android 10 中的相机 HAL 接口。
图 1. Android 9 及更低版本中的相机 HAL 接口
图 2. 使用缓冲区管理 API 的 Android 10 中的相机 HAL 接口
实现缓冲区管理 API
要实现缓冲区管理 API,相机 HAL 必须执行以下操作:
- 实现 HIDL
ICameraDevice@3.5
。 - 将相机特征密钥
android.info.supportedBufferManagementVersion
设置为HIDL_DEVICE_3_5
。
相机 HAL 使用 ICameraDeviceCallback.hal
中的 requestStreamBuffers
和 returnStreamBuffers
方法来请求并返回缓冲区。该 HAL 还必须实现 ICameraDeviceSession.hal
中的 signalStreamFlush
方法来指示相机 HAL 返回缓冲区。
requestStreamBuffers
使用 requestStreamBuffers
方法从相机框架请求缓冲区。使用相机 HAL3 缓冲区管理 API 时,相机框架中的拍摄请求不包含输出缓冲区,也就是说,StreamBuffer
中的 bufferId
字段为 0
。因此,相机 HAL 必须使用 requestStreamBuffers
从相机框架请求缓冲区。
通过 requestStreamBuffers
方法,调用方可以在一次调用中从多个输出流请求多个缓冲区,从而减少 HIDL IPC 调用的次数。不过,如果同时请求的缓冲区增加,调用所需的时间也会增加,这可能会导致从请求到结果的总延迟时间增加。此外,由于 requestStreamBuffers
调用已在相机服务中序列化,因此建议相机 HAL 使用专用的高优先级线程来请求缓冲区。
如果缓冲区请求失败,相机 HAL 必须能够妥善处理非严重错误。下表介绍了缓冲区请求失败的常见原因以及相机 HAL 应如何处理这些失败。
- 应用与输出流断开连接:这种情况属于非严重错误。相机 HAL 应针对任何以已断开连接的输出流为目标的拍摄请求发送
ERROR_REQUEST
,并做好正常处理后续请求的准备。 - 超时:如果应用在忙于执行密集型处理操作,同时保留某些缓冲区,则可能会发生超时情况。相机 HAL 应针对因超时错误而无法完成的拍摄请求发送
ERROR_REQUEST
,并做好正常处理后续请求的准备。 - 相机框架正在准备新的输出流配置:相机 HAL 应等到下一次
configureStreams
调用完成后,再重新调用requestStreamBuffers
。 - 相机 HAL 已达到其缓冲区限制(
maxBuffers
字段):相机 HAL 应等到返回输出流的至少一个缓冲区后,再重新调用requestStreamBuffers
。
returnStreamBuffers
使用 returnStreamBuffers
方法将额外的缓冲区返回到相机框架。相机 HAL 通常会通过 processCaptureResult
方法将缓冲区返回到相机框架,但是它只能考虑已发送至相机 HAL 的拍摄请求。通过 requestStreamBuffers
方法,相机 HAL 实现保留的缓冲区数可能会超过相机框架所请求的缓冲区数。这时应使用 returnStreamBuffers
方法。如果 HAL 实现保留的缓冲区数从未超过请求的缓冲区数,则相机 HAL 实现无需调用 returnStreamBuffers
方法。
signalStreamFlush
signalStreamFlush
方法由相机框架调用,用于通知相机 HAL 返回当前可用的所有缓冲区。通常在以下情况下调用此方法:相机框架即将调用 configureStreams
且必须排空相机拍摄管道。与 returnStreamBuffers
方法类似,如果相机 HAL 实现保留的缓冲区数不超过请求的缓冲区数,则可以不实现此方法。
相机框架调用 signalStreamFlush
后,该框架会停止向相机 HAL 发送新的拍摄请求,直到所有缓冲区均已返回到相机框架为止。返回所有缓冲区后,requestStreamBuffers
方法调用会失败,并且相机框架会在干净状态下继续工作。然后相机框架会调用 configureStreams
或 processCaptureRequest
方法。如果相机框架调用 configureStreams
方法,相机 HAL 可以在 configureStreams
调用成功返回后重新开始请求缓冲区。如果相机框架调用 processCaptureRequest
方法,相机 HAL 可以在 processCaptureRequest
调用期间开始请求缓冲区。
signalStreamFlush
方法和 flush
方法的语义有所不同。调用 flush
方法时,HAL 会终止待处理的拍摄请求并显示 ERROR_REQUEST
,以尽快排空管道。调用 signalStreamFlush
方法时,HAL 必须正常完成所有待处理的拍摄请求,并将所有缓冲区返回到相机框架。
signalStreamFlush
方法与其他方法之间的另一个差异在于 signalStreamFlush
是单向 HIDL 方法,也就是说,相机框架可能会先调用其他屏蔽 API,然后 HAL 再接收 signalStreamFlush
调用。这意味着,signalStreamFlush
方法和其他方法(特别是 configureStreams
方法)到达相机 HAL 的顺序,可能与它们在相机框架中调用的顺序不同。为解决此异步问题,streamConfigCounter
字段已添加到 StreamConfiguration
并作为参数添加到 signalStreamFlush
方法。相机 HAL 实现应使用 streamConfigCounter
参数来确定 signalStreamFlush
调用的到达时间是否晚于其相应的 configureStreams
调用的到达时间。如需查看示例,请参阅图 3。
图 3. 相机 HAL 应如何检测和处理迟到的 signalStreamFlush 调用
实现缓冲区管理 API 时发生的行为变更
在使用缓冲区管理 API 去实现缓冲区管理逻辑时,请考虑相机和相机 HAL 实现可能会发生的行为变更:
拍摄请求到达相机 HAL 的速度更快、频率更高:如果不使用缓冲区管理 API,相机框架会先为每个拍摄请求请求输出缓冲区,然后再向相机 HAL 发送拍摄请求。而使用缓冲区管理 API 后,相机框架无需再等待缓冲区请求完成,因此可以更早地向相机 HAL 发送拍摄请求。
此外,如果不使用缓冲区管理 API,当拍摄请求的其中一个输出流达到 HAL 一次可容纳的最大缓冲区数(此值由相机 HAL 在
configureStreams
调用的返回值中的HalStream::maxBuffers
字段中指定)时,相机框架会停止发送拍摄请求。如果使用缓冲区管理 API,此限制行为不再存在,而且如果 HAL 中排队的拍摄请求过多,相机 HAL 实现不得接受processCaptureRequest
调用。requestStreamBuffers
调用延迟变化显著:导致requestStreamBuffers
调用所需时间超过平均时间的原因有很多。例如:- 对于新建输出流的前几个缓冲区而言,调用所需时间可能较长,因为设备需要分配内存。
- 预期延迟时间会按每次调用时请求的缓冲区数成比例增加。
- 应用正在保留缓冲区并忙于处理操作。这可能会导致缓冲区请求因缓冲区不足或 CPU 繁忙而降速或超时。
缓冲区管理策略
缓冲区管理 API 支持实现各种类型的缓冲区管理策略。以下是一些示例:
- 向后兼容:HAL 在调用
processCaptureRequest
期间为拍摄请求请求缓冲区。此策略不会节省任何内存,但可用作缓冲区管理 API 的第一个实现,几乎不需要更改现有相机 HAL 的代码。 - 最大限度地节省内存:相机 HAL 仅在需要填充缓冲区之前立即请求输出缓冲区。此策略可最大限度地节省内存。此策略的潜在弊端是,当完成缓冲区请求所需的时间过长时,相机管道会出现更多卡顿。
- 已缓存:相机 HAL 缓存少许缓冲区,以降低缓冲区请求速度偶尔较慢时受到影响的可能性。
相机 HAL 可针对特定使用情形采用不同的策略,例如,对于使用大量内存的使用情形采用“最大限度地节省内存”策略,对于其他使用情形则采用“向后兼容”策略。
外部相机 HAL 中的实现示例
外部相机 HAL 是在 Android 9 中引入的,可在 hardware/interfaces/camera/device/3.5/
上的源代码树中找到。在 Android 10 中,此外部相机 HAL 已进行更新,其中新增了 ExternalCameraDeviceSession.cpp
,这是缓冲区管理 API 的一种实现。此外部相机 HAL 使用几百行 C++ 代码实现缓冲区管理策略中介绍的“最大限度地节省内存”策略。