EVS介绍

EVS(External View System))

谷歌的介绍:https://source.android.com/devices/automotive/camera-hal?hl=zh-cn#ievscamera

一、EVS的优势

EVS个人理解就是将相机获取的图像数据,快速传送到显示屏上,相比于传统的相机,有一下优势:

1、快速启动

EVS HAL的设计,使得camera到display的流程依赖最小化,内核一旦启动,显示也会随之启动,两者之间的传输时间较大减小;

2、可扩展性

EVS 提供了一种安全的方式,来让应用通过只读的方式,获取车载摄像机的反馈,这样应用

端就可以由此实现 脸部识别,标志检测,路况报告等一些高级功能,具体见config.json。

3、可配置性

EVS 的HAL 层设计的要比完整的camera HAL 层简单,并且在客户端,通过config.json 对

camera 进行配置, 并且google也提供了一些 demo,可以随意查看

CameraName_Backup、LaneView、right turn 摄像头。

二、EVS的框架

它使用了hal 的技术,将这个流程分为以下几个层次:

application: 应用层,由开发者实现

Evs manager: 向应用层提供,相关接口服务

hardware_service: 向manager提供具体的接口实现

driver:分为camera driver和display driver

简述流程:APP通过EVSmanager与hardware service通信,获取操作camera与display的权限与代理

对象分别为IEvsCamera和IEvsDisplay,通过代理对象,实现 camera与display的交互,将camera

的图像及时的显示到display 上。

三、详细流程

下面以Android提供的实例来进行梳理

1、EvsManager和EvsHardware_service初始化

代码路径:/packages/services/Car/evs/manager

const static char kHardwareEnumeratorName[] = "EvsEnumeratorHw";

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

首先是开机的时候先注册服务,之后再通过getservice()进行获取,注册代码如下(注:代码仅为关键流程,不是全部代码) 

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

上面HardWare_Service 也是在系统开机,通过init.rc时启动注册到系统中的,如下

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

在init.rc 中通过[email protected]命令启动的 HardWard_Service,注

册流程和上面的EvsManager服务是相同的,注册完成后,等待manager的调用。

这里的HardWard_Service 的实体实现为 HardWard 的 EvsEnumerator 对象,在他的初始化函数中会,将系统中的camera 添加到sCameraList 中。

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

可以看到默认有三个camera:后置、左视和右视

到此Hardward Service 与 上层的Evs manager、application都初始化完毕,application 可以通过

Evs manager 的代理对象,与 Hardward Service 进行交互,获取Camera与Display 的代理对象,用

以进行后续操作。

2、opencamera的流程

由于系统中存在多个相机,需要指定cameraId打开对应的相机,cameraId的获取则是通过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;
                                    }
                                }

getCameraList 的参数是一个 函数指针,它通过Evs Manager传入到Hardward Service, 并由

Hardward Service 回调,将cameraList传入到上面的回调函数。在此回调函数中,会去遍历

cameraList,并与config.json 对比,如果config.json 存在cameraList 对应的cameraId,那么将此

camera放入mCameraInfo 中。

Hardward Service 中getCameraList 的具体实现为:

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

上面的getCameraList 方法中,会回调传入的_hidl_cb 函数,并将EvsEnumerator 初始化

sCameraList 传过去,那么APP也就获取到了所有cameraId,之后调用指定cameraID的opencamera()方法。

openCamera 会先调用到 Evs Manager 中Enumerator.cpp 的 openCamera 方法:

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

上代码Evs Manager Enumerator的openCamera 方法中,可以归纳为以下几点:

1、使用getCameraInfo 方法对 cameraId 进行校验,检验是否存在对应CameraId 的相机;

2、通过 mHwEnumerator 的openCamera 方法,调用到Hardward service 去打开对应cameraId

的相机;

3、将 Hardward service 返回的 IevsCamera 封装为 HalCamera;

4、调用 HalCamera 的 makeVirtualCamera 方法,去配置HardWard service camera 的buffers

信息,并将返回值 return。

可以总结分为两个主要流程:

1)mHwEnumerator->openCamera

2)hwCamera->makeVirtualCamera()

1)mHwEnumerator->openCamera,调用到hardware service中opencamera()方法

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

上述代码Hardward 的openCamera 方法中,可以归纳为以下几点:

1、判断传入的cameraId 是否有效;

2、对应的 cameraId是否已经打开,如果已经打开,则closeCamera;

3、创建EvsCamera 对象,在EvsCamera的构造函数中,对width、height和format进行了赋值,并将其返回给EVS Manager。

2)hwCamera->makeVirtualCamera(),主要作用申请指定大小的图像缓冲区,具体过程如下

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

client->getAllowedBuffers()代码中默认值是1,在/packages/services/Car/evs/manager/VirtualCamera.h中有设置

申请过程:

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

进入到hardwareservice的EvsCamera中

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

以上APP已经完成opencamera的操作。

四、瑞萨实现

上面实例展示了APP、Evsmanager和hardware三块的联系,那么,对于瑞萨这种OEM厂商来说,他

要实现的就是hardware service这块的所有API功能,这些API定义

/hardware/interfaces/automotive/evs 中的 .hal 文件中。代码/vendor/renesas/hal/evs/中,就是瑞萨对上面API的功能实现,还是看下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;
}

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

猜你喜欢

转载自blog.csdn.net/qq_37057338/article/details/127411086