مکانیسم اطلاع رسانی رویداد و فریم

در نسخه قبلی سیستم نمای خارجی (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 را ارائه می دهد که از سه فیلد تشکیل شده است:

  • نوع رویداد
  • رشته ای برای شناسایی منشاء رویداد.
  • داده های کلمه 4×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 و سایر اجزای گرافیکی اندروید، BufferDesc برای استفاده از HardwareBuffer وارد شده از رابط android.hardware.graphics.common@1.2 مجدداً تعریف شده است. HardwareBuffer حاوی HardwareBufferDescription است که همتای HIDL Android NDK AHardwareBuffer_Desc است، با یک دسته بافر.

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

نسخه جدیدتر روش فراخوانی فریم برای ارائه چند توصیفگر بافر طراحی شده است. بنابراین، پیاده‌سازی دوربین EVS در صورتی که چندین منبع را مدیریت کند، می‌تواند چندین فریم را با یک تماس ارسال کند.

همچنین، پروتکل قبلی برای اطلاع از پایان جریان، که در حال ارسال فریم پوچ بود، منسوخ شده و با رویداد STREAM_STOPPED جایگزین شده است.

نمودار توالی اعلان رویداد

شکل 1. نمودار توالی اطلاع رسانی رویداد

از مکانیسم اطلاع رسانی رویداد و فریم استفاده کنید

شناسایی نسخه IEvsCameraStream که توسط مشتری پیاده سازی شده است

این سرویس می‌تواند نسخه رابط ورودی IEvsCameraStream را که توسط کلاینت پیاده‌سازی شده است، با تلاش برای 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
    ...
}

notify() callback

EvsEvent از طریق callback 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();
}

از BufferDesc استفاده کنید

AHardwareBuffer_Desc نوع داده Android NDK برای نمایش یک بافر سخت افزاری بومی است که می تواند به EGL/OpenGL و Vulkan اولیه متصل شود. این شامل اکثر ابرداده های بافر از 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;