Introduction to EVS

EVS(External View System))

Google's introduction: https://source.android.com/devices/automotive/camera-hal?hl=zh-cn#ievscamera

1. Advantages of EVS

EVS personal understanding is to quickly transmit the image data acquired by the camera to the display screen. Compared with traditional cameras, it has the following advantages:

1. Quick start

The design of EVS HAL minimizes the process dependence from camera to display. Once the kernel is started, the display will also be started, and the transmission time between the two is greatly reduced;

2. Scalability

EVS provides a safe way for the application to obtain the feedback of the car camera in a read-only way, so that the application

The terminal can realize some advanced functions such as face recognition, sign detection, traffic report, etc., see config.json for details.

3. Configurability

The HAL layer design of EVS is simpler than the complete camera HAL layer, and on the client side, through config.json to

The camera is configured, and google also provides some demos, you can check them at will

CameraName_Backup, LaneView, right turn camera.

2. The framework of EVS

It uses hal's technology to divide this process into the following levels:

application: application layer, implemented by developers

Evs manager: provide relevant interface services to the application layer

hardware_service: Provide specific interface implementation to the manager

driver: divided into camera driver and display driver

Briefly describe the process: the APP communicates with the hardware service through EVSmanager to obtain the authority and agent to operate the camera and display

The objects are IEvsCamera and IEvsDisplay respectively. Through the proxy object, the interaction between camera and display is realized, and the camera

The image is displayed on the display in time.

3. Detailed process

Let's sort out the examples provided by Android

1. EvsManager and EvsHardware_service initialization

Code path: /packages/services/Car/evs/manager

const static char kHardwareEnumeratorName[] = "EvsEnumeratorHw";

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

The first is to register the service when starting up, and then obtain it through getservice(). The registration code is as follows (Note: the code is only a key process, not all codes) 

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

The above HardWare_Service is also started and registered in the system through init.rc when the system is turned on, as follows

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

HardWard_Service started by the [email protected] command in init.rc, note

The registration process is the same as the EvsManager service above. After the registration is completed, wait for the manager to call.

The entity of HardWard_Service here is implemented as HardWard's EvsEnumerator object, and in its initialization function, the camera in the system will be added to sCameraList.

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

You can see that there are three cameras by default: rear, left view and right view

At this point, the Hardward Service and the upper-level Evs manager and application are all initialized, and the application can pass through

The proxy object of Evs manager interacts with Hardward Service, obtains the proxy objects of Camera and Display, and uses

for subsequent operations.

2. The process of opencamera

Since there are multiple cameras in the system, it is necessary to specify the cameraId to open the corresponding camera, and the cameraId is obtained through the getcameraID() method

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

The parameter of getCameraList is a function pointer, which is passed to Hardward Service through Evs Manager, and is controlled by

Hardward Service callback, pass cameraList to the above callback function. In this callback function, it will traverse

cameraList, and compare it with config.json, if there is a cameraId corresponding to cameraList in config.json, then add this

camera into mCameraInfo.

The specific implementation of getCameraList in Hardward Service is:

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

In the getCameraList method above, the _hidl_cb function passed in will be called back and the EvsEnumerator will be initialized

sCameraList is passed, then the APP will get all the cameraIds, and then call the opencamera() method of the specified cameraID.

openCamera will first call the openCamera method of Enumerator.cpp in 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;
}

In the openCamera method of the above code Evs Manager Enumerator, it can be summarized as the following points:

1. Use the getCameraInfo method to verify the cameraId, and check whether there is a camera corresponding to the CameraId;

2. Through the openCamera method of mHwEnumerator, call Hardward service to open the corresponding cameraId

camera;

3. Encapsulate the IevsCamera returned by Hardward service as HalCamera;

4. Call the makeVirtualCamera method of HalCamera to configure the buffers of HardWard service camera

information, and will return the value return.

It can be summarized into two main processes:

1)mHwEnumerator->openCamera

2)hwCamera->makeVirtualCamera()

1) mHwEnumerator->openCamera, call the opencamera() method in the hardware service

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

In the above code Hardward's openCamera method, it can be summarized as follows:

1. Determine whether the incoming cameraId is valid;

2. Whether the corresponding cameraId has been opened, if it has been opened, then closeCamera;

3. Create an EvsCamera object. In the constructor of EvsCamera, assign values ​​to width, height and format, and return them to the EVS Manager.

2) hwCamera->makeVirtualCamera(), the main function is to apply for an image buffer of a specified size, the specific process is as follows

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

The default value in the client->getAllowedBuffers() code is 1, which is set in /packages/services/Car/evs/manager/VirtualCamera.h

Application process:

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

Enter the EvsCamera of 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;
}

The above APP has completed the operation of opencamera.

4. Realization of Renesas

The above example shows the relationship between APP, Evsmanager and hardware. Then, for an OEM manufacturer like Renesas, he

What is to be realized is all the API functions of the hardware service. These API definitions

in /hardware/interfaces/automotive/evsthe .hal file in . In the code /vendor/renesas/hal/evs/, it is the function realization of the above API by Renesas, or look at the process of 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;
}

Look at 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;
}

Guess you like

Origin blog.csdn.net/qq_37057338/article/details/127411086