EVS介绍

EVS介绍EVS External View System 谷歌的介绍 https source android com devices automotive camera hal hl zh cn ievscamera 一 EVS 的优势 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/

在init.rc 中通过android.hardware.automotive.evs@1.0-service命令启动的 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; }

小讯
上一篇 2025-01-11 07:01
下一篇 2025-01-27 08:16

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/62705.html