В 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
, но он может учитывать только запросы захвата, отправленные в фреймворк камеры 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 камеры в поле
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++.