Механизм уведомления о событиях и кадрах

В предыдущем выпуске 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);
};

Этот метод предоставляет EvsEventDesc , который состоит из трех полей:

  • Тип события.
  • Строка для определения источника события.
  • 4x 32-битных слова для хранения информации о возможном событии.
/**
 * 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 был переопределен для использования HardwareBuffer , импортированного из интерфейса android.hardware.graphics.common@1.2. HardwareBuffer содержит HardwareBufferDescription , который является HIDL-аналогом AHardwareBuffer_Desc из Android NDK , с дескриптором буфера.

/**
 * 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-битных слов. Вы можете указать его как тип AHardwareBuffer_Desc и заполнить содержимое.

EvsEventDesc — это структура enum EvsEventType , в которой перечислены несколько потоковых событий и полезная нагрузка из 32-битных слов, в которую разработчик может поместить возможную дополнительную информацию. Например, разработчик может поместить код ошибки для события ошибки потоковой передачи.

/**
 * 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 может пересылать несколько кадров одним вызовом, если она управляет несколькими источниками.

Кроме того, предыдущий протокол для уведомления об окончании потока, который отправлял нулевой кадр, устарел и заменен событием 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
    ...
}

уведомить() Обратный вызов

EvsEvent будет передан через обратный вызов notify notify() , после чего клиент сможет определить его тип на основе дискриминатора, как показано ниже:

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();
}

Использование описания буфера

AHardwareBuffer_Desc — это тип данных Android NDK, представляющий собственный аппаратный буфер, который можно привязать к примитивам EGL/OpenGL и Vulkan. Он содержит большую часть метаданных буфера из предыдущего определения BufferDesc EVS и, следовательно, заменяет его в новом определении 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;