Meccanismo di notifica di evento e frame

Nella release precedente di Exterior View System (EVS), l'IEvsCameraStream interfaccia definiva un singolo metodo di callback per inviare solo i frame video acquisiti. Sebbene questa semplificazione abbia semplificato le implementazioni dei client di servizio EVS, ha anche ostacolato la possibilità per i client di identificare eventuali incidenti di streaming e, di conseguenza, di gestirli correttamente. Per migliorare l'esperienza di sviluppo EVS, AOSP ora contiene un callback aggiuntivo per inviare eventi in streaming.

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

Questo metodo restituisce EvsEventDesc, composto da tre campi:

  • Tipo di evento.
  • Stringa per identificare l'origine dell'evento.
  • 4 dati di parole a 32 bit per contenere possibili informazioni sugli eventi.
/**
 * 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;
};

Inoltre, per evitare qualsiasi divergenza nella descrizione del buffer grafico tra EVS e altri componenti grafici di Android, BufferDesc è stato ridefinito per utilizzare HardwareBuffer importato dall'interfaccia android.hardware.graphics.common@1.2. HardwareBuffer contiene HardwareBufferDescription, che è la controparte HIDL di AHardwareBuffer_Desc di Android NDK, con un handle del buffer.

/**
 * 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
};

Nota: HardwareBufferDescription è definito come un array di dieci parole a 32 bit. Ti consigliamo di trasmetterlo come tipo AHardwareBuffer_Desc e di compilare i contenuti.

EvsEventDesc è una struct di enum EvsEventType, che elenca diversi eventi di streaming e un payload di parole di 32 bit, in cui lo sviluppatore può inserire possibili informazioni aggiuntive. Ad esempio, lo sviluppatore può inserire un codice di errore per l'evento di errore di streaming.

/**
 * 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,
};

Caricamento frame

Con un nuovo BufferDesc, IEvsCameraStream introduce anche nuovi metodi di callback per ricevere i frame e gli eventi di streaming dalle implementazioni del servizio.

/**
 * 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 delivers 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);
};

Una versione più recente di un metodo di callback del frame è progettata per fornire più descrittori del buffer. Pertanto, le implementazioni delle videocamere EVS possono inoltrare più frame con una singola chiamata se gestiscono più origini.

Inoltre, il protocollo precedente per la notifica della fine dello stream, che inviava il frame null, è stato ritirato e sostituito con l'evento STREAM_STOPPED.

Diagramma di sequenza di notifica degli eventi

Figura 1. Diagramma di sequenza della notifica di eventi

Utilizzare il meccanismo di notifica di eventi e frame

Identificare la versione di IEvsCameraStream implementata dal client

Il servizio può identificare la versione dell'interfaccia IEvsCameraStream in entrata implementata dal client tentando di eseguire il downcast:

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 succeeds 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
    ...
}

Richiamata notify()

EvsEvent viene passato tramite il callback notify() e il client può quindi identificarne il tipo in base al discriminatore, come mostrato di seguito:

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

Utilizzare BufferDesc

AHardwareBuffer_Desc è il tipo di dati di Android NDK per rappresentare un buffer hardware nativo che può essere associato alle primitive EGL/OpenGL e Vulkan. Contiene la maggior parte dei metadati del buffer della precedente descrizione buffer EVS e, pertanto, la sostituisce nella nuova definizione di BufferDesc. Tuttavia, poiché questo è definito come un array nell'interfaccia HIDL, non è possibile indicizzare direttamente le variabili membro. In alternativa, puoi eseguire il cast dell'array come tipo AHardwareBuffer_Desc, come mostrato di seguito:

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;