Camera HAL3 缓冲区管理 API

Android 10 引入了可选的 camera HAL3 缓冲区管理 API,让您可以实现缓冲区管理逻辑,以便在相机 HAL 实现中实现不同的内存和捕获延迟权衡。

相机 HAL 需要在其流水线中排队 N 个请求(其中 N 等于流水线深度),但通常不需要同时处理所有 N 组输出缓冲区。

例如,HAL 可能在其流水线中排队了八个请求,但它只需要用于流水线最后阶段的两个请求的输出缓冲区。在运行 Android 9 及更低版本的设备上,当请求在 HAL 中排队时,相机框架会分配缓冲区,因此 HAL 中可能有六组缓冲区未被使用。在 Android 10 中,相机 HAL3 缓冲区管理 API 允许将输出缓冲区解耦,以释放六组缓冲区。这可以在高端设备上节省数百兆字节的内存,并且也可能对低内存设备有利。

图 1 显示了运行 Android 9 及更低版本的设备的相机 HAL 接口示意图。图 2 显示了 Android 10 中使用相机 HAL3 缓冲区管理 API 实现的相机 HAL 接口。

Buffer management in 9 or lower

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

Buffer management in Android 10

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

实现缓冲区管理 API

要实现缓冲区管理 API,相机 HAL 必须

相机 HAL 使用 requestStreamBuffersreturnStreamBuffers 方法(位于 ICameraDeviceCallback.hal 中)来请求和返回缓冲区。HAL 还必须实现 signalStreamFlush 方法(位于 ICameraDeviceSession.hal 中)来向相机 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 方法,这意味着相机框架可能会在 HAL 收到 signalStreamFlush 调用之前调用其他阻塞 API。这意味着 signalStreamFlush 方法和其他方法(特别是 configureStreams 方法)到达相机 HAL 的顺序可能与它们在相机框架中被调用的顺序不同。为了解决这种异步问题,streamConfigCounter 字段被添加到 StreamConfiguration 中,并作为参数添加到 signalStreamFlush 方法中。相机 HAL 实现应使用 streamConfigCounter 参数来确定 signalStreamFlush 调用是否晚于其对应的 configureStreams 调用到达。有关示例,请参见图 3。

Handling calls that arrive late

图 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 中,它已更新为包含 ExternalCameraDeviceSession.cpp,即缓冲区管理 API 的实现。此外部相机 HAL 实现了 缓冲区管理策略 中提到的最大化内存节省策略,只需几百行 C++ 代码。