В Android 10 представлены необязательные API управления буфером camera HAL3, которые позволяют реализовать логику управления буфером для достижения различных компромиссов между памятью и задержкой захвата в реализациях camera 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. Интерфейс Camera 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 камеры в другом порядке, чем они были вызваны в фреймворке камеры. Чтобы устранить эту проблему асинхронности, в StreamConfiguration было добавлено поле streamConfigCounter, а в метод 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++.