В Android 10 представлены дополнительные API-интерфейсы управления буфером камеры HAL3 , которые позволяют реализовать логику управления буфером для достижения различных компромиссов в отношении памяти и задержки при реализации HAL камеры.
Камере HAL требуется N запросов (где N равно глубине конвейера ), поставленных в очередь в ее конвейере, но часто ей не требуются все N наборов выходных буферов одновременно.
Например, в очереди конвейера HAL может находиться восемь запросов, но выходные буферы требуются только для двух запросов на последних этапах конвейера. На устройствах под управлением Android 9 и ниже фреймворк камеры выделяет буферы, когда запрос помещается в очередь HAL, поэтому в HAL может быть шесть неиспользуемых наборов буферов. В Android 10 API управления буферами камеры HAL3 позволяют разделить выходные буферы, чтобы освободить шесть наборов буферов. Это может привести к экономии сотен мегабайт памяти на высокопроизводительных устройствах и также может быть полезно для устройств с небольшим объёмом памяти.
На рисунке 1 показана схема интерфейса HAL камеры для устройств под управлением Android 9 и более ранних версий. На рисунке 2 показан интерфейс HAL камеры в Android 10 с реализованными API управления буфером камеры HAL3.
Рисунок 1. Интерфейс HAL камеры в Android 9 и ниже
Рисунок 2. Интерфейс HAL камеры в Android 10 с использованием API управления буфером
Реализовать API управления буферами
Для реализации API управления буфером HAL камеры должен:
- Внедрите HIDL
ICameraDevice@3.5
. - Установите ключ характеристик камеры
android.info.supportedBufferManagementVersion
наHIDL_DEVICE_3_5
.
HAL камеры использует методы requestStreamBuffers
и returnStreamBuffers
в ICameraDeviceCallback.hal
для запроса и возврата буферов. HAL также должен реализовать метод signalStreamFlush
в ICameraDeviceSession.hal
, чтобы подать сигнал HAL камеры о необходимости возврата буферов.
requestStreamBuffers
Используйте метод requestStreamBuffers
для запроса буферов из фреймворка камеры. При использовании API управления буферами камеры HAL3 запросы на захват из фреймворка камеры не содержат выходных буферов, то есть поле bufferId
в StreamBuffer
равно 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
, но он может учитывать только запросы на захват, отправленные в фреймворк камеры. С помощью метода requestStreamBuffers
реализация HAL камеры может сохранять больше буферов, чем было запрошено фреймворком камеры. В этом случае следует использовать метод returnStreamBuffers
. Если реализация 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 камеры в поле
HalStream::maxBuffers
в возвращаемом значении вызоваconfigureStreams
). С появлением API управления буферами такое ограничение больше не применяется, и реализация HAL камеры не должна принимать вызовыprocessCaptureRequest
, когда в очереди HAL слишком много запросов на захват.Задержка вызова
requestStreamBuffers
может значительно варьироваться: существует множество причин, по которым вызовrequestStreamBuffers
может занимать больше времени, чем обычно. Например:- Для первых нескольких буферов вновь созданного потока вызовы могут занимать больше времени, поскольку устройству необходимо выделить память.
- Ожидаемая задержка увеличивается пропорционально количеству буферов, запрошенных в каждом вызове.
- Приложение удерживает буферы и выполняет обработку. Это может привести к замедлению запросов к буферам или тайм-ауту из-за нехватки буферов или загруженности процессора.
Стратегии управления буфером
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++.