이벤트 및 프레임 알림 메커니즘

이전 버전의 Exterior View System(EVS)에서 IEvsCameraStream 인터페이스는 캡처된 동영상 프레임 제공하는 단일 콜백 메서드를 정의했습니다. 그로 인해 EVS 서비스 클라이언트 구현은 간소화되었지만 그와 동시에 클라이언트가 스트리밍 이슈를 식별하기가 어려워져 그러한 이슈를 올바르게 처리하기가 힘들어졌습니다. EVS 개발 환경을 개선하기 위해 AOSP에는 이제 스트리밍 이벤트를 전달하는 추가 콜백이 포함되었습니다.

package android.hardware.automotive.evs@1.1;

import @1.0::IEvsCameraStream;

/**
 * Implemented on client side to receive asynchronous video frame deliveries.
 */
interface IEvsCameraStream extends @1.0::IEvsCameraStream {
    /**
     * Receives calls from the HAL each time a video frame is ready for inspection.
     * Buffer handles received by this method must be returned via calls to
     * IEvsCamera::doneWithFrame_1_1(). When the video stream is stopped via a call
     * to IEvsCamera::stopVideoStream(), this callback may continue to happen for
     * some time as the pipeline drains. Each frame must still be returned.
     * When the last frame in the stream has been delivered, STREAM_STOPPED
     * event must be delivered. No further frame deliveries may happen
     * thereafter.
     *
     * @param buffer a buffer descriptor of a delivered image frame.
     */
    oneway deliverFrame_1_1(BufferDesc buffer);

    /**
     * Receives calls from the HAL each time an event happens.
     *
     * @param  event EVS event with possible event information.
     */
    oneway notify(EvsEvent event);
};

이 메서드는 3개의 필드로 구성된 EvsEventDesc를 제공합니다.

  • 이벤트 유형.
  • 이벤트 출처를 식별하는 문자열.
  • 가능한 이벤트 정보를 포함하는 32비트 단어 데이터 4개.
/**
 * Structure that describes informative events occurred during EVS is streaming
 */
struct EvsEvent {
    /**
     * Type of an informative event
     */
    EvsEventType aType;
    /**
     * Device identifier
     */
    string deviceId;
    /**
     * Possible additional information
     */
    uint32_t[4] payload;
};

또한 EVS와 다른 Android 그래픽 구성요소 간의 그래픽 버퍼 설명에 차이가 발생하지 않도록 BufferDesc는 android.hardware.graphics.common@1.2 인터페이스에서 가져온 HardwareBuffer를 사용하도록 다시 정의되었습니다. HardwareBuffer에는 HardwareBufferDescription(Android NDK AHardwareBuffer_Desc의 HIDL에 대응되는 요소)이 버퍼 핸들과 함께 포함되어 있습니다.

/**
 * HIDL counterpart of AHardwareBuffer_Desc.
 *
 * An AHardwareBuffer_Desc object can be converted to and from a
 * HardwareBufferDescription object by memcpy().
 *
 * @sa +ndk libnativewindow#AHardwareBuffer_Desc.
 */
typedef uint32_t[10] HardwareBufferDescription;

/**
 * HIDL counterpart of AHardwareBuffer.
 *
 * AHardwareBuffer_createFromHandle() can be used to convert a HardwareBuffer
 * object to an AHardwareBuffer object.
 *
 * Conversely, AHardwareBuffer_getNativeHandle() can be used to extract a native
 * handle from an AHardwareBuffer object. Paired with AHardwareBuffer_Desc,
 * AHardwareBuffer_getNativeHandle() can be used to convert between
 * HardwareBuffer and AHardwareBuffer.
 *
 * @sa +ndk libnativewindow#AHardwareBuffer".
 */
struct HardwareBuffer {
    HardwareBufferDescription description;
    handle nativeHandle;
}

/**
 * Structure representing an image buffer through our APIs
 *
 * In addition to the handle to the graphics memory, need to retain
 * the properties of the buffer for easy reference and reconstruction of
 * an ANativeWindowBuffer object on the remote side of API calls.
 * (Not least because OpenGL expect an ANativeWindowBuffer* for us as a
 * texture via eglCreateImageKHR().
 */
struct BufferDesc {
    /**
     * HIDL counterpart of AHardwareBuffer_Desc. Please see
     * hardware/interfaces/graphics/common/1.2/types.hal for more details.
     */
    HardwareBuffer buffer;
    /**
     * The size of a pixel in the units of bytes
     */
    uint32_t pixelSize;
    /**
     * Opaque value from driver
     */
    uint32_t bufferId;
    /**
     * Unique identifier of the physical camera device that produces this buffer.
     */
    string deviceId;
    /**
     * Time that this buffer is being filled
     */
    int64_t timestamp;
    /**
     * Frame metadata. This is opaque to EVS manager
     */
    vec<uint8_t> metadata
};

참고: HardwareBufferDescription는 32비트 단어 10개로 구성된 배열로 정의됩니다. 이 배열을 AHardwareBuffer_Desc 유형으로 변환하고 콘텐츠를 채우는 것이 좋습니다.

EvsEventDesc는 몇몇 스트리밍 이벤트와 32비트 단어 페이로드가 나열되는 enum EvsEventType의 구조체입니다. 개발자는 여기에 가능한 추가 정보를 입력할 수 있습니다. 예를 들어 개발자는 스트리밍 오류 이벤트의 오류 코드를 배치할 수 있습니다.

/**
 * Types of informative streaming events
 */
enum EvsEventType : uint32_t {
    /**
     * Video stream is started
     */
    STREAM_STARTED = 0,
    /**
     * Video stream is stopped
     */
    STREAM_STOPPED,
    /**
     * Video frame is dropped
     */
    FRAME_DROPPED,
    /**
     * Timeout happens
     */
    TIMEOUT,
    /**
     * Camera parameter is changed; payload contains a changed parameter ID and
     * its value
     */
    PARAMETER_CHANGED,
    /**
     * Master role has become available
     */
    MASTER_RELEASED,
};

프레임 전송

BufferDesc에서 IEvsCameraStream에는 서비스 구현에서 프레임과 스트리밍 이벤트를 수신하는 새로운 콜백 메서드도 도입되었습니다.

/**
 * Implemented on client side to receive asynchronous streaming event deliveries.
 */
interface IEvsCameraStream extends @1.0::IEvsCameraStream {
   /**
    * Receives calls from the HAL each time video frames are ready for inspection.
    * Buffer handles received by this method must be returned via calls to
    * IEvsCamera::doneWithFrame_1_1(). When the video stream is stopped via a call
    * to IEvsCamera::stopVideoStream(), this callback may continue to happen for
    * some time as the pipeline drains. Each frame must still be returned.
    * When the last frame in the stream has been delivered, STREAM_STOPPED
    * event must be delivered. No further frame deliveries may happen
    * thereafter.
    *
    * A camera device will deliver the same number of frames as number of
    * backing physical camera devices; it means, a physical camera device
    * sends always a single frame and a logical camera device sends multiple
    * frames as many as the number of backing physical camera devices.
    *
    * @param buffer Buffer descriptors of delivered image frames.
    */
   oneway deliverFrame_1_1(vec<BufferDesc> buffer);

   /**
    * Receives calls from the HAL each time an event happens.
    *
    * @param  event EVS event with possible event information.
    */
   oneway notify(EvsEventDesc event);
};

새 버전의 프레임 콜백 메서드는 버퍼 설명어를 여러 개 전송하도록 설계되었습니다. 따라서 EVS 카메라 구현은 여러 소스를 관리하는 경우 단일 호출로 여러 개의 프레임을 전달할 수 있습니다.

또한 null 프레임을 전송 중인 스트림 끝을 알리는 이전 프로토콜이 지원 중단되고 STREAM_STOPPED 이벤트로 대체되었습니다.

이벤트 알림 시퀀스 다이어그램

그림 1. 이벤트 알림 시퀀스 다이어그램

이벤트 및 프레임 알림 메커니즘 사용

클라이언트가 구현한 IEvsCameraStream 버전 식별

이 서비스는 클라이언트가 구현한 수신 IEvsCameraStream 인터페이스의 버전을 다운캐스트하여 그 버전을 확인할 수 있습니다.

using IEvsCameraStream_1_0 =
    ::android::hardware::automotive::evs::V1_0::IEvsCameraStream;
using IEvsCameraStream_1_1 =
    ::android::hardware::automotive::evs::V1_1::IEvsCameraStream;

Return<EvsResult> EvsV4lCamera::startVideoStream(
    const sp<IEvsCameraStream_1_0>& stream)  {

    IEvsCameraStream_1_0 aStream = stream;
    // Try to downcast. This will succeed if the client implements
    // IEvsCameraStream v1.1.
    IEvsCameraStream_1_1 aStream_1_1 =
        IEvsCameraStream_1_1::castFrom(aStream).withDefault(nullptr);
    if (aStream_1_1 == nullptr) {
        ALOGI("Start a stream for v1.0 client.");
    } else {
        ALOGI("Start a stream for v1.1 client.");
    }

    // Start a video stream
    ...
}

notify() 콜백

아래와 같이 EvsEventnotify() 콜백을 통해 전달되면 클라이언트가 분류자에 따라 그 유형을 식별할 수 있습니다.

Return<void> StreamHandler::notify(const EvsEvent& event) {
    ALOGD("Received an event id: %u", event.aType);
    // Handle each received event.
    switch(event.aType) {
        case EvsEventType::ERROR:
            // Do something to handle an error
            ...
            break;
        [More cases]
    }
    return Void();
}

BufferDesc 사용

AHardwareBuffer_Desc는 EGL/OpenGL 및 Vulkan 프리미티브에 바인딩할 수 있는 네이티브 하드웨어 버퍼를 나타내는 Android NDK의 데이터 유형입니다. 이 항목은 이전 EVS BufferDesc의 버퍼 메타데이터를 대부분 포함하고 있어 관련 메타데이터를 새 BufferDesc 정의에서 대체합니다. 그러나 이 항목은 HIDL 인터페이스에서 배열로 정의되었기 때문에 개발자는 멤버 변수의 색인을 직접 생성할 수 없습니다. 대신 아래와 같이 그 배열을 AHardwareBuffer_Desc 유형으로 변환할 수 있습니다.

BufferDesc bufDesc = {};
AHardwareBuffer_Desc* pDesc =
    reinterpret_cast<AHardwareBuffer_Desc *>(&bufDesc.buffer.description);
pDesc->width  = mVideo.getWidth();
pDesc->height = mVideo.getHeight();
pDesc->layers = 1;
pDesc->format = mFormat;
pDesc->usage  = mUsage;
pDesc->stride = mStride;
bufDesc_1_1.buffer.nativeHandle = mBuffers[idx].handle;
bufDesc_1_1.bufferId = idx;