Introducción al SVE

EVS (Sistema de vista externa))

Introducción de Google: https://source.android.com/devices/automotive/camera-hal?hl=zh-cn#ievscamera

1. Ventajas del SVE

El entendimiento personal de EVS es transmitir rápidamente los datos de imagen adquiridos por la cámara a la pantalla de visualización Comparado con las cámaras tradicionales, tiene las siguientes ventajas:

1. Inicio rápido

El diseño de EVS HAL minimiza la dependencia del proceso de la cámara a la pantalla. Una vez que se inicia el kernel, también se iniciará la pantalla y el tiempo de transmisión entre los dos se reduce considerablemente;

2. Escalabilidad

EVS proporciona una forma segura para que la aplicación obtenga la retroalimentación de la cámara del automóvil en forma de solo lectura, de modo que la aplicación

El terminal puede realizar algunas funciones avanzadas, como reconocimiento facial, detección de señales, informe de tráfico, etc. Consulte config.json para obtener más información.

3. Configurabilidad

El diseño de la capa HAL de EVS es más simple que la capa HAL de la cámara completa, y en el lado del cliente, a través de config.json para

La cámara está configurada y Google también proporciona algunas demostraciones, puede verificarlas cuando lo desee

CameraName_Backup, LaneView, cámara de giro a la derecha.

2. El marco del SVE

Utiliza la tecnología de hal para dividir este proceso en los siguientes niveles:

aplicación: capa de aplicación, implementada por los desarrolladores

Administrador de Evs: proporciona servicios de interfaz relevantes para la capa de aplicación

hardware_service: proporciona una implementación de interfaz específica al administrador

controlador: dividido en controlador de cámara y controlador de pantalla

Describa brevemente el proceso: la aplicación se comunica con el servicio de hardware a través de EVSmanager para obtener la autoridad y el agente para operar la cámara y la pantalla.

Los objetos son IEvsCamera e IEvsDisplay respectivamente. A través del objeto proxy, se realiza la interacción entre la cámara y la pantalla, y la cámara

La imagen se muestra en la pantalla a tiempo.

3. Proceso detallado

Ordenemos los ejemplos proporcionados por Android

1. Inicialización de EvsManager y EvsHardware_service

Ruta del código: /packages/services/Car/evs/manager

const static char kHardwareEnumeratorName[] = "EvsEnumeratorHw";

const static char kMockEnumeratorName[] = "EvsEnumeratorHw-Mock";

La primera es registrar el servicio al iniciar y luego obtenerlo a través de getservice() El código de registro es el siguiente (Nota: el código es solo un proceso clave, no todos los códigos) 

int main(int argc, char** argv) {
    ALOGI("EVS manager starting\n");

    const char* evsHardwareServiceName = kHardwareEnumeratorName;

    std::thread registrationThread(startService, evsHardwareServiceName, kManagedEnumeratorName); //在这里注册startService这个线程

    joinRpcThreadpool();
    ALOGE("EVS Hardware Enumerator is shutting down");
    return 1;
}
static void startService(const char *hardwareServiceName, const char * managerServiceName) {
  
    android::sp<Enumerator> service = new Enumerator();
    //先初始化hardwareService,在初始化中获取hardservice的服务
    if (!service->init(hardwareServiceName)) {
        ALOGE("Failed to connect to hardware service - quitting from registrationThread");
        exit(1);
    }
    ALOGI("EVS managed service is starting as %s", managerServiceName);
    status_t status = service->registerAsService(managerServiceName);//注册服务

    ALOGD("Registration complete");
}
bool Enumerator::init(const char* hardwareServiceName) {
    mHwEnumerator = IEvsEnumerator::getService(hardwareServiceName);
    bool result = (mHwEnumerator.get() != nullptr);
    return result;
}

El HardWare_Service anterior también se inicia y registra en el sistema a través de init.rc cuando se enciende el sistema, de la siguiente manera

hardware/interfaces/automotive/evs/1.0/default/[email protected]

HardWard_Service iniciado por el comando [email protected] en init.rc, nota

El proceso de registro es el mismo que el del servicio EvsManager anterior. Después de completar el registro, espere a que llame el administrador.

La entidad de HardWard_Service aquí se implementa como el objeto EvsEnumerator de HardWard y, en su función de inicialización, la cámara del sistema se agregará a sCameraList.

EvsEnumerator::EvsEnumerator() {
    sCameraList.emplace_back(EvsCamera::kCameraName_Backup);
    sCameraList.emplace_back("LaneView");
    sCameraList.emplace_back("right turn");
}

Puede ver que hay tres cámaras por defecto: trasera, vista izquierda y vista derecha

En este punto, el servicio Hardward y el administrador y la aplicación Evs de nivel superior están todos inicializados, y la aplicación puede pasar

El objeto proxy de Evs manager interactúa con Hardward Service, obtiene los objetos proxy de Camera y Display y utiliza

para operaciones posteriores.

2. El proceso de cámara abierta

Como hay múltiples cámaras en el sistema, es necesario especificar el cameraId para abrir la cámara correspondiente, y el cameraId se obtiene a través del método getcameraID()

    mEvs->getCameraList([this, &config](hidl_vec<CameraDesc> cameraList) {
                            ALOGI("Camera list callback received %zu cameras",
                                  cameraList.size());
                            for (auto&& cam: cameraList) {
                                ALOGD("Found camera %s", cam.cameraId.c_str());
                                bool cameraConfigFound = false;

                                for (auto&& info: config.getCameras()) {
                                    if (cam.cameraId == info.cameraId) {
                                        // We found a match!
                                        if (info.function.find("reverse") != std::string::npos) {
                                            mCameraList[State::REVERSE].push_back(info);
                                        }
                                        if (info.function.find("right") != std::string::npos) {
                                            mCameraList[State::RIGHT].push_back(info);
                                        }
                                        if (info.function.find("left") != std::string::npos) {
                                            mCameraList[State::LEFT].push_back(info);
                                        }
                                        if (info.function.find("park") != std::string::npos) {
                                            mCameraList[State::PARKING].push_back(info);
                                        }
                                        cameraConfigFound = true;
                                        break;
                                    }
                                }

El parámetro de getCameraList es un puntero de función, que se pasa a Hardward Service a través de Evs Manager y está controlado por

Devolución de llamada de Hardward Service, pase cameraList a la función de devolución de llamada anterior. En esta función de devolución de llamada, atravesará

cameraList y compárelo con config.json, si hay un cameraId correspondiente a cameraList en config.json, agregue esto

cámara en mCameraInfo.

La implementación específica de getCameraList en Hardward Service es:

Return<void> EvsEnumerator::getCameraList(getCameraList_cb _hidl_cb)  {
    const unsigned numCameras = sCameraList.size();

    std::vector<CameraDesc> descriptions;
    descriptions.reserve(numCameras);
    for (const auto& cam : sCameraList) {
        descriptions.push_back( cam.desc );
    }

    hidl_vec<CameraDesc> hidlCameras(descriptions);

    ALOGD("reporting %zu cameras available", hidlCameras.size());
    _hidl_cb(hidlCameras);//这里callback
    return Void();
}

En el método getCameraList anterior, se volverá a llamar a la función _hidl_cb pasada y se inicializará EvsEnumerator

Se pasa sCameraList, luego la aplicación obtendrá todos los ID de cámara y luego llamará al método opencamera () del ID de cámara especificado.

openCamera llamará primero al método openCamera de Enumerator.cpp en Evs Manager:

Return<sp<IEvsCamera>> Enumerator::openCamera(const hidl_string& cameraId) {
    sp<HalCamera> hwCamera;//有封装了一个HalCamera
    for (auto &&cam : mCameras) {
        bool match = false;
        cam->getHwCamera()->getCameraInfo([cameraId, &match](CameraDesc desc) {
                                      if (desc.cameraId == cameraId) {
                                          match = true;
                                      }
                                  }
        );
        if (match) {
            hwCamera = cam;
            break;
        }
    
       sp<IEvsCamera> device = mHwEnumerator->openCamera(cameraId);
       
    sp<VirtualCamera> clientCamera;
    if (hwCamera != nullptr) {
        clientCamera = hwCamera->makeVirtualCamera();
    }
    
    return clientCamera;
}

En el método openCamera del código anterior Evs Manager Enumerator, se puede resumir en los siguientes puntos:

1. Use el método getCameraInfo para verificar el cameraId y verifique si hay una cámara que corresponda al CameraId;

2. A través del método openCamera de mHwEnumerator, llame al servicio Hardward para abrir el cameraId correspondiente

cámara;

3. Encapsule la IevsCamera devuelta por el servicio Hardward como HalCamera;

4. Llame al método makeVirtualCamera de HalCamera para configurar los búferes de la cámara de servicio HardWard

información, y devolverá el valor devuelto.

Se puede resumir en dos procesos principales:

1)mHwEnumerator->openCamera

2)hwCamera->makeVirtualCamera()

1) mHwEnumerator->openCamera, llame al método opencamera() en el servicio de hardware

Return<sp<IEvsCamera>> EvsEnumerator::openCamera(const hidl_string& cameraId) {
    CameraRecord *pRecord = nullptr;
    for (auto &&cam : sCameraList) {
        if (cam.desc.cameraId == cameraId) {
            // Found a match!
            pRecord = &cam;
            break;
        }//先匹配到对应的cameraId
    }
    sp<EvsCamera> pActiveCamera = pRecord->activeInstance.promote();
    if (pActiveCamera != nullptr) {
        ALOGW("Killing previous camera because of new caller");
        closeCamera(pActiveCamera);
    }   
    pActiveCamera = new EvsCamera(cameraId.c_str());
    pRecord->activeInstance = pActiveCamera;
 
    return pActiveCamera;
}

En el método openCamera de Hardward del código anterior, se puede resumir de la siguiente manera:

1. Determinar si el cameraId entrante es válido;

2. Si se ha abierto el cameraId correspondiente, si se ha abierto, entonces closeCamera;

3. Cree un objeto EvsCamera En el constructor de EvsCamera, asigne valores de ancho, alto y formato, y devuélvalos al Administrador de EVS.

2) hwCamera->makeVirtualCamera(), la función principal es solicitar un búfer de imagen de un tamaño específico, el proceso específico es el siguiente

sp<VirtualCamera> HalCamera::makeVirtualCamera() {
    //将 hwCamera 封装为VirtualCamera 对象client
    sp<VirtualCamera> client = new VirtualCamera(this);

    //通过changeFramesInFlight来设置buffer数量,
    if (!changeFramesInFlight(client->getAllowedBuffers())) {
        client = nullptr;
        return nullptr;
    }
    mClients.push_back(client);
    return client;
}

El valor predeterminado en el código cliente->getAllowedBuffers() es 1, que se establece en /packages/services/Car/evs/manager/VirtualCamera.h

Proceso de solicitud:

bool HalCamera::changeFramesInFlight(int delta) {
    // Walk all our clients and count their currently required frames
    // 上注释也说的很明白,统计每个client所需的frame数量
    unsigned bufferCount = 0;
    for (auto&& client :  mClients) {
        sp<VirtualCamera> virtCam = client.promote();
        if (virtCam != nullptr) {
            bufferCount += virtCam->getAllowedBuffers();
        }
    }
    
    bufferCount += delta;
    //调用setMaxFramesInFlight()方法设置下去
    Return<EvsResult> result = mHwCamera->setMaxFramesInFlight(bufferCount);

Entrar en la EvsCamera de hardwareservice

Return<EvsResult> EvsCamera::setMaxFramesInFlight(uint32_t bufferCount) {

    if (setAvailableFrames_Locked(bufferCount)) {
        return EvsResult::OK;
    } else {
        return EvsResult::BUFFER_NOT_AVAILABLE;
    }
bool EvsCamera::setAvailableFrames_Locked(unsigned bufferCount) {
    //根据当前情况,申请所需的buffer
    if (mFramesAllowed < bufferCount) {
        // An increase is required
        unsigned needed = bufferCount - mFramesAllowed;
        ALOGI("Allocating %d buffers for camera frames", needed);

        //通过increaseAvailableFrames_Locked()方法完成buffer申请
        unsigned added = increaseAvailableFrames_Locked(needed);
        if (added != needed) {
            ALOGE("Rolling back to previous frame queue size");
            decreaseAvailableFrames_Locked(added);
            return false;
        }
    } else if (mFramesAllowed > bufferCount) {
        // A decrease is required
        unsigned framesToRelease = mFramesAllowed - bufferCount;
        ALOGI("Returning %d camera frame buffers", framesToRelease);

        unsigned released = decreaseAvailableFrames_Locked(framesToRelease);
        if (released != framesToRelease) {
            ALOGE("Buffer queue shrink failed -- too many buffers currently in use?");
        }
    }

    return true;
}
unsigned EvsCamera::increaseAvailableFrames_Locked(unsigned numToAdd) {
    
    GraphicBufferAllocator &alloc(GraphicBufferAllocator::get());
    unsigned added = 0;
    while (added < numToAdd) {
        buffer_handle_t memHandle = nullptr;
        //根据width、height、format等信息申请buffer
        status_t result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage,
                                         &memHandle, &mStride, 0, "EvsCamera");
        bool stored = false;
        for (auto&& rec : mBuffers) {
            if (rec.handle == nullptr) {
                rec.handle = memHandle;
                rec.inUse = false;
                stored = true;
                break;
            }
        }
        mFramesAllowed++;//在这里++
        added++;//已经申请的个数累加,累加的个数应该与传入的参数numToAdd相等
    }

    return added;
}

La aplicación anterior ha completado la operación de opencamera.

4. Realización de Renesas

El ejemplo anterior muestra la relación entre APP, Evsmanager y hardware. Entonces, para un fabricante OEM como Renesas, él

Lo que se debe realizar son todas las funciones API del servicio de hardware.Estas definiciones API

en /hardware/interfaces/automotive/evsel archivo .hal en . En el código /vendor/renesas/hal/evs/, es la realización de la función de la API anterior por parte de Renesas, o mira el proceso de opencamera().

Return<sp<IEvsCamera>> EvsEnumerator::openCamera(const hidl_string& cameraId) {

    CameraRecord *pRecord = findCameraById(cameraId);
    if (!pRecord) {
        ALOGE("Requested camera %s not found", cameraId.c_str());
        return nullptr;
    }
    sp<EvsCamera> pActiveCamera = pRecord->activeInstance.promote();
    if (pActiveCamera != nullptr) {
        ALOGW("Killing previous camera because of new caller");
        closeCamera(pActiveCamera);
    }
    //创建EvsCamera,初始化width、height等信息
    pActiveCamera = new EvsCamera(cameraId.c_str(), pRecord->dim.width, pRecord->dim.height);
    pRecord->activeInstance = pActiveCamera;
    if (pActiveCamera == nullptr) {
        ALOGE("Failed to allocate new EvsCamera object for %s\n", cameraId.c_str());
    }

    return pActiveCamera;
}

Mira EvsCamera

EvsCamera::EvsCamera(const char *id, uint32_t initWidth, uint32_t initHeight) :
        mFramesAllowed(0),
        mFramesInUse(0),
        mStreamState(STOPPED),
        mFd(-1),
        mBufType(V4L2_BUF_TYPE_VIDEO_CAPTURE),
        mMemType(V4L2_MEMORY_USERPTR) {

    ALOGD("EvsCamera instantiated");

    mDescription.cameraId = id;
    //设置基本属性
    mWidth  = initWidth;
    mHeight = initHeight;
    mFormat = BUFFER_FORMAT;
    mUsage  = BUFFER_USAGE;

    mComposer = IComposer::getService();
    mDisplayWidth = mComposer->getDisplayWidth();
    mDisplayHeight = mComposer->getDisplayHeight();
    //进行初始化
    if(!initialize(id)) {
        ALOGE("Failed to open v4l device %s\n", id);
    }
}
bool EvsCamera::initialize(const char* deviceName)
{
    //先打开设备
    if ((mFd = open(deviceName, O_RDWR)) == -1) {
        ALOGE("Error while opening device %s: %s", deviceName, strerror(errno));
        return false;
    }
    //填充v4l2
    v4l2_format format = {};
    format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    format.fmt.pix.pixelformat = CAMERA_FORMAT;
    format.fmt.pix.width = mWidth;
    format.fmt.pix.height = mHeight;
    format.fmt.pix.field = V4L2_FIELD_NONE;
    //通过ictol与driver通信
    if (ioctl(mFd, VIDIOC_S_FMT, &format) < 0) {
        ALOGE("VIDIOC_S_FMT: %s", strerror(errno));
        return false;
    }//VIDIOC_S_FMT这个在文档中对它的解释是:Set scaling and the data format

    // Report the current output format
    format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    //VIDIOC_G_FMT表示获取format
    if (ioctl(mFd, VIDIOC_G_FMT, &format) == 0) {
        ALOGI("Current camera output format:  fmt=0x%X, %dx%d, pitch=%d",
               format.fmt.pix.pixelformat,
               format.fmt.pix.width,
               format.fmt.pix.height,
               format.fmt.pix.bytesperline);
    } else {
        ALOGE("VIDIOC_G_FMT: %s", strerror(errno));
        return false;
    }

    return true;
}

Supongo que te gusta

Origin blog.csdn.net/qq_37057338/article/details/127411086
Recomendado
Clasificación