카메라 HAL3 버퍼 관리 API

Android 10에는 카메라 HAL3 버퍼 관리 API 옵션이 도입되었으며, 이로 인해 카메라 HAL 구현 시 다양한 메모리 및 캡처 지연 시간 보완을 위한 버퍼 관리 로직 구현이 가능합니다.

카메라 HAL은 파이프라인에 대기 중인 N개의 요청(N은 파이프라인 깊이와 같음)을 해야 하지만, 출력 버퍼 세트 N개 전체를 동시에 요구하는 경우는 많지 않습니다.

예를 들어, 파이프라인에 8개의 요청이 대기 중이어도 HAL은 파이프라인의 마지막 단계에 있는 두 개의 요청에 관한 출력 버퍼만 필요로 합니다. 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.halrequestStreamBuffersreturnStreamBuffers 메서드를 사용하여 버퍼를 요청하고 반환합니다. 또한, HAL은 ICameraDeviceSession.halsignalStreamFlush 메서드를 구현하여 카메라 HAL에 버퍼를 반환하도록 신호를 보냅니다.

requestStreamBuffers

카메라 프레임워크의 버퍼를 요청하려면 requestStreamBuffers 메서드를 사용합니다. 카메라 HAL3 버퍼 관리 API를 사용하는 경우, 카메라 프레임워크의 캡처 요청에는 출력 버퍼가 포함되지 않습니다. 즉, StreamBufferbufferId 필드는 0입니다. 따라서, 카메라 HAL은 requestStreamBuffers를 사용하여 카메라 프레임워크의 버퍼를 요청해야 합니다.

requestStreamBuffers 메서드를 사용하면 호출자가 한 번의 호출에 여러 출력 스트림의 여러 버퍼를 요청하므로 HIDL IPC 호출 횟수를 줄일 수 있습니다. 그러나, 동시에 더 많은 버퍼를 요청하면 호출에 더 많은 시간이 소요되며 요청에서 결과가 나오기까지 걸리는 총 지연 시간에 부정적인 영향을 미칠 수 있습니다. 또한, 카메라 서비스에서 requestStreamBuffers 호출은 직렬화되므로 카메라 HAL에서 높은 우선순위의 전용 스레드를 사용하여 버퍼를 요청하는 것이 좋습니다.

버퍼 요청이 실패하면 카메라 HAL은 치명적이지 않은 오류를 적절하게 처리할 수 있어야 합니다. 다음 목록은 버퍼 요청이 실패하는 일반적인 이유와 카메라 HAL에서 이러한 상황을 어떻게 처리해야 하는가에 관해 설명합니다.

  • 앱이 출력 스트림에서 연결 해제됨: 이 오류는 치명적이지 않은 오류입니다. 카메라 HAL은 연결 해제된 스트림을 타겟팅하는 캡처 요청에 ERROR_REQUEST를 전송하고, 후속 요청을 정상적으로 처리할 수 있도록 준비해야 합니다.
  • 시간 초과: 이 오류는 앱이 일부 버퍼 요청을 처리하는 동시에 많은 작업을 처리하는 동안 발생할 수 있습니다. 카메라 HAL은 시간 초과 오류로 인해 처리할 수 없는 캡처 요청에 ERROR_REQUEST를 전송하고, 후속 요청을 정상적으로 처리할 수 있도록 준비해야 합니다.
  • 카메라 프레임워크에서 새 스트림 구성을 준비하는 중: 카메라 HAL은 requestStreamBuffers를 다시 호출하기 전에 다음 configureStreams 호출이 완료될 때까지 대기해야 합니다.
  • 카메라 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 메서드라는 점이며, 이는 HAL이 signalStreamFlush 호출을 수신하기 전에 카메라 프레임워크에서 다른 차단 API를 호출할 수도 있음을 의미합니다. 즉, signalStreamFlush 메서드와 다른 메서드(특히, configureStreams 메서드)는 카메라 프레임워크에서 호출된 순서와 다른 순서로 카메라 HAL에 도착할 수 있습니다. 이 비동기 문제를 해결하기 위해 streamConfigCounter 필드가 StreamConfiguration에 추가되고 signalStreamFlush 메서드에 인수로 추가되었습니다. 카메라 HAL 구현은 streamConfigCounter 인수를 사용하여 signalStreamFlush 호출이 이에 대응하는 configureStreams 호출보다 늦게 도착하는지 확인해야 합니다. 예는 그림 3을 참고하세요.

늦게 도착하는 호출 처리

그림 3. 카메라 HAL이 나중에 도착하는 signalStreamFlush 호출을 감지하고 처리하는 방법

버퍼 관리 API 구현 시 동작 변경

버퍼 관리 API를 사용하여 버퍼 관리 로직을 구현할 때에는 카메라 및 카메라 HAL 구현 시 다음과 같이 동작이 변경될 수 있다는 점을 고려합니다.

  • 캡처 요청이 카메라 HAL에 더 빠르게 더 자주 도착함: 버퍼 관리 API가 없으면 카메라 프레임워크는 캡처 요청을 카메라 HAL에 전송하기 전에 각 캡처 요청의 출력 버퍼를 요청합니다. 버퍼 관리 API를 사용하면 카메라 프레임워크는 더 이상 버퍼를 기다릴 필요가 없으므로 조기에 카메라 HAL로 캡처 요청을 보낼 수 있습니다.

    또한, 버퍼 관리 API가 없으면 캡처 요청의 출력 스트림 중 하나가 HAL이 한 번에 유지할 수 있는 최대 버퍼 수(이 값은 configureStreams 호출 반환 값에 있는 HalStream::maxBuffers 필드의 카메라 HAL에서 지정)에 도달하는 경우 카메라 프레임워크는 캡처 요청 전송을 중단합니다. 버퍼 관리 API를 사용하면 이 제한 동작이 더 이상 없으며, HAL에 너무 많은 캡처 요청이 대기 중인 경우 카메라 HAL 구현은 processCaptureRequest 호출을 허용하지 않아야 합니다.

  • requestStreamBuffers 호출 지연 시간이 크게 달라짐: requestStreamBuffers를 호출하는 데 평균보다 더 오랜 시간이 걸리는 이유는 다양합니다. 예:

    • 새로 생성된 스트림의 처음 몇 개 버퍼에서는 기기가 메모리를 할당해야 하므로 호출하는 데 더 오래 걸릴 수 있습니다.
    • 예상 지연 시간은 각 호출에서 요청한 버퍼 수에 비례하여 증가합니다.
    • 앱에서 버퍼를 유지하고 있으며 처리 중입니다. 버퍼가 부족하거나 CPU가 사용 중이므로 버퍼 요청이 느려지거나 시간 초과가 발생할 수 있습니다.

버퍼 관리 전략

버퍼 관리 API를 사용하면 여러 종류의 버퍼 관리 전략을 구현할 수 있습니다. 다음은 몇 가지 예입니다.

  • 하위 호환성: processCaptureRequest를 호출하는 동안 HAL이 캡처 요청을 위한 버퍼를 요청합니다. 이 전략은 메모리를 절약하지는 않지만, 버퍼 관리 API를 최초로 구현하는 역할을 하므로 기존 카메라 HAL의 코드를 거의 변경할 필요가 없습니다.
  • 메모리 절약 극대화: 카메라 HAL이 출력 버퍼를 채우기 직전에만 출력 버퍼를 요청합니다. 이 전략을 사용하면 메모리를 최대한 절약할 수 있습니다. 하지만 버퍼 요청을 완료하는 데 비정상적으로 오랜 시간이 걸리는 경우 카메라 파이프라인에서 버벅거림이 더 자주 발생할 수 있다는 단점이 있습니다.
  • 캐시됨: 가끔 느린 버퍼 요청으로 인한 영향을 줄이기 위해 카메라 HAL에서 몇 개의 버퍼를 캐시합니다.

카메라 HAL은 메모리 사용량이 많은 사용 사례에는 메모리 절약 최대화 전략을 사용하고, 다른 사용 사례에는 하위 호환성 전략을 사용하는 등 특정 사용 사례에 맞게 다른 전략을 채택할 수 있습니다.

외부 카메라 HAL 구현 샘플

외부 카메라 HAL은 Android 9에서 도입되었으며 hardware/interfaces/camera/device/3.5/의 소스 트리에서 확인할 수 있습니다. Android 10에서 외부 카메라 HAL은 버퍼 관리 API의 구현인 ExternalCameraDeviceSession.cpp를 포함하도록 업데이트되었습니다. 이 외부 카메라 HAL은 수백 줄의 C++ 코드를 사용하여 버퍼 관리 전략에서 언급한 메모리 절약 최대화 전략을 구현합니다.