相机 HAL3 缓冲区管理 API

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 接口。

Android 9 或更低版本中的缓冲区管理

图 1. Android 9 及更低版本中的相机 HAL 接口

Android 10 中的缓冲区管理

图 2. 使用缓冲区管理 API 的 Android 10 中的相机 HAL 接口

实现缓冲区管理 API

要实现缓冲区管理 API,相机 HAL 必须执行以下操作:

相机 HAL 使用 ICameraDeviceCallback.hal 中的 requestStreamBuffersreturnStreamBuffers 方法来请求并返回缓冲区。该 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 方法调用会失败,并且相机框架会在干净状态下继续工作。然后相机框架会调用 configureStreamsprocessCaptureRequest 方法。如果相机框架调用 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++ 代码实现缓冲区管理策略中介绍的“最大限度地节省内存”策略。