EVS (External View System) 外部查看系统
我的理解,就是可以将 外界摄像头的视频流快速显示到屏幕上的 一套系统。
那么为什么选择 EVS ,而不使用之前传统的 显示流程:
相比传统的 camera,Evs 的优点是什么:
可配置性:
EVS 的 HAL 层 设计的要比完整的 camera HAL 层简单,并且在客户端,通过 config.json 对
camera 进行配置, 并且 google也提供了一些 demo,可以随意查看
CameraName_Backup、LaneView、right turn 摄像头。
快速启动:
因为车机开机后,camera 中的视频流要尽快显示到 display 上,
EVS HAL 的设计,使 camera → display 的流程依赖性最小化,他完全又 native 代码实现,内
核一旦启动运行,他就可以去启动显示了, 它目前的相应时间为 2S 左右。
可扩展性:
EVS 提供了一种安全的方式,来让应用通过只读的方式,获取车载摄像机的反馈,这样应用
端就可以由此实现 脸部识别,标志检测,路况报告等一些高级功能。
车机对 摄像头的要求性,比较高,要在用户打开车门后,尽早看到倒车影像,而使用 EVS
能使 用户尽早的看到影响。
EVS的流程
EVS 主要是 camera、application、display这三个 modle 进行交互,他的大概流程为:
hal 层 camera 上报 视频图像 -----→ 应用层处理视频图像(实现人脸识别等功能)
–-----→ display 显示视频图像 。
官方图如下:
它使用了 hal 的
技术,将这个流
程分为 三个层
次:
application: 应用层, 由开发者实现
Evs manager: 向 应用层提供,相关接口服务
hardware_service: 向 manager 提供具体的接口实现
我对它流程,理解的图为:
EVS Application
EVS Manager
EVS HardWard_Service
Camera Driver
Display Driver
application 通过 Evs manager 与 hardware_service 通信,
获取操作 camera与 display的权限与 代理对象 IEvsCamera、IEvsDisplay,
通过 代理对象,实现 camera 与 display 的交互,
将 camera 的图像及时的显示到 display 上。
具体的流程为:
1、EvsManager与 Evs Hardward_service 的初始化
2、application 通过 Evs manager 获取 代理对象 IEvsCamera、IEvsDisplay
3、使用 IEvsCamera、IEvsDisplay 实现, 图像的获取与显示。
1、EvsManager与 Evs Hardward_service 的初始化
const static char kEvsServiceName[] = "EvsSharedEnumerator";
android::sp pEvs = IEvsEnumerator::getService(kEvsServiceName);
通过 getService 获取 名为"EvsSharedEnumerator"的 Evs Manager,
Evs manager 应该是开机时注册到系统中的,他的注册流程为:
const static char kManagedEnumeratorName[] = "EvsSharedEnumerator";
const static char kHardwareEnumeratorName[] = "EvsEnumeratorHw-Mock";
android::sp service = new Enumerator();
service→init(kHardwareEnumeratorName)
status_t status = service->registerAsService(kManagedEnumeratorName);
joinRpcThreadpool();
application 获取到的 IEvsEnumerator 代理,其实就是 Enumerator对象,他是在系统开机时通
过 registerAsService 函数注册到 系统中的,
Enumerator 是在 init 中 获取 HardWard_Service的代理对象的:
bool Enumerator::init(const char* hardwareServiceName) {
sp mHwEnumerator = IevsEnumerator::getService(hardwareServiceName);
bool result = (mHwEnumerator.get() != nullptr);
return result;
}
hardwareServiceName == kHardwareEnumeratorName == "EvsEnumeratorHw-Mock"
在其 init 函数中 也是通过 getService 函数来获取 HardWard_Service 代理对象的,
HardWare_Service 也是在系统开机,通过 init.rc时启动注册到系统中的:
hardware/interfaces/automotive/evs/1.0/default/android.hardware.automotive.evs@1.0-service.rc
service evs-hal-1-0 /vendor/bin/hw/android.hardware.automotive.evs@1.0-service
class hal
user cameraserver
group camera
在 init.rc 中通过 android.hardware.automotive.evs@1.0-service,命令启动的 HardWard_Service
const static char kEnumeratorServiceName[] = "EvsEnumeratorHw-Mock";
int main() {
android::sp service = new EvsEnumerator();
configureRpcThreadpool(1, true /* callerWillJoin */);
status_t status = service->registerAsService(kEnumeratorServiceName);
if (status == OK) {
joinRpcThreadpool();
} else {
ALOGE("Could not register service %s (%d).", kEnumeratorServiceName, status);
}
return 1;
}
HardWard_Service 的实体实现为 HardWard的 EvsEnumerator 对象,在他的初始化函数中会,
将系统中的 camera 添加到 sCameraList 中,并且也通过 registerAsService 函数注册到 系统中,
然后等待 manager 的调用。
EvsEnumerator::EvsEnumerator() {
sCameraList.emplace_back(EvsCamera::kCameraName_Backup);
sCameraList.emplace_back("LaneView");
sCameraList.emplace_back("right turn");
}
EvsEnumerator现在默认是支持三个 camera:后置、左、右
到此 Hardward Service 与 上层的 Evs manager、application都初始化完毕,
application 可以通过 Evs manager 的代理对象,与 Hardward Service 进行交互,获取 Camera
与 Display 的代理对象,用以进行后续操作。
2、application 通过 Evs manager 获取 代理对象 IEvsCamera、IEvsDisplay
sp pDisplay = pEvs→openDisplay();
sp mCurrentCamera = mEvs->openCamera(mCameraInfo[desiredState].cameraId);
通过 Evs Manager 的代理对象 IEvsEnumerator,来获取 camera 与 display 的代理对象:
因为系统最多只能有一个 EVS显示对象,所以需要通过此方法,获取独家访问
pEvs→openDisplay():
IEvsDisplay的权限,此方法在 Evs Manager 中的具体实现为:
Return> Enumerator::openDisplay() {
sp pActiveDisplay = mHwEnumerator->openDisplay();
mActiveDisplay = pActiveDisplay;
return pActiveDisplay;
}
Evs Manager 中会调用 HardWard Service 的 openDisplay 方法,获取 IEvsDisplay 对象,并将
其返回,HardWard Service 端 的实现为:
Return> EvsEnumerator::openDisplay() {
sp pActiveDisplay = sActiveDisplay.promote();
if (pActiveDisplay != nullptr) {
closeDisplay(pActiveDisplay);
}
pActiveDisplay = new EvsDisplay();
sActiveDisplay = pActiveDisplay;
return pActiveDisplay;
}
因为系统中存在多个 Camera 所以使用此方法时,需要指定 要打开的 cameraId,而系
在 Hardward Service 的 openDisplay 方法中 会进行判断,如果之前已经有人 获取了 Display
权限,将其关闭。并创建一个新的 EvsDisplay 对象 将其返回,
而在 EvsDisplay 的构造方法中 会初始化一个用以与 surface 通信的 BufferDesc buffer。
到此 application就可以获取到 EvsDisplay 的代理对象 IEvsDisplay
mEvs→openCamera(mCameraInfo[desiredState].cameraId):
统中所有的 CameraId是通过 mEvs→getCameraList(**) 获取到的:
mEvs->getCameraList([this, &config]
(hidl_vec cameraList) {
for (auto&& cam: cameraList) {
bool cameraConfigFound = false;
for (auto&& info: config.getCameras()) {
if (cam.cameraId == info.cameraId) {
if (info.function.find("reverse") != std::string::npos) {
mCameraInfo[State::REVERSE] = info;
}
if (info.function.find("right") != std::string::npos) {
mCameraInfo[State::RIGHT] = info;
}
if (info.function.find("left") != std::string::npos) {
mCameraInfo[State::LEFT] = 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 EvsEnumerator::getCameraList(getCameraList_cb _hidl_cb) {
const unsigned numCameras = sCameraList.size();
std::vector descriptions;
descriptions.reserve(numCameras);
for (const auto& cam : sCameraList) {
descriptions.push_back( cam.desc );
}
hidl_vec hidlCameras(descriptions);
_hidl_cb(hidlCameras);
return Void();
}
在 Hardward 的 getCameraList 方法中,会回调传入的_hidl_cb 函数,并将 EvsEnumerator 初
始化 sCameraList 传过去。
到此 Application 就获取到所有包含所有 Camera Id 的 List 集合。
之后使用 openCamera 来打开指定 CameraId的 camera:
sp mCurrentCamera = mEvs->openCamera(mCameraInfo[desiredState].cameraId);
openCamera 会先调用到 Evs Manager 中 Enumerator.cpp 的 openCamera 方法:
Return> Enumerator::openCamera(const hidl_string& cameraId) {
sp hwCamera;
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;
}
}
if (hwCamera == nullptr) {
sp device = mHwEnumerator->openCamera(cameraId);
hwCamera = new HalCamera(device);
}
sp clientCamera;
clientCamera = hwCamera->makeVirtualCamera();
mCameras.push_back(hwCamera);
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。
5、将 hwCamera 保存在 list集合中 mCameras 中,
之后分为两个流程
a、mHwEnumerator->openCamera
b、 hwCamera->makeVirtualCamera()
a、mHwEnumerator->openCamera,调用到Hardward service 去打开对应cameraId 的相机
openCamera 会调用到 Hardward EvsEnumerator 的 openCamera 函数
Return> EvsEnumerator::openCamera(const hidl_string& cameraId) {
CameraRecord *pRecord = nullptr;
for (auto &&cam : sCameraList) {
if (cam.desc.cameraId == cameraId) {
pRecord = &cam;
break;
}
}
if (!pRecord) {
ALOGE("Requested camera %s not found", cameraId.c_str());
return nullptr;
}
sp 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;
if (pActiveCamera == nullptr) {
ALOGE("Failed to allocate new EvsCamera object for %s\n", cameraId.c_str());
}
return pActiveCamera;
}
在 Hardward 的 openCamera 方法中,会进行以下操作:
1、判断 传入的 cameraId 是否有效
2、对应的 cameraId是否已经打开,如果已经打开 closeCamera
3、创建 EvsCamera 对象,并将其返回给 EVS Manager
在 EvsCamera 的构造方法中,会初始化 camera 对应的参数:Width、Height、mFormat 。
到此 E 就获取到了 hardWard EvsCamera 的代理对象 IEvsCamera。
b、 hwCamera->makeVirtualCamera()
makeVirtualCamera 方法的主要作用是去申请指定大小的图像缓冲区
sp HalCamera::makeVirtualCamera() {
sp client = new VirtualCamera(this);
if (client == nullptr) {
return nullptr;
}
if (!changeFramesInFlight(client->getAllowedBuffers())) {
client = nullptr;
return nullptr;
}
mClients.push_back(client);
return client;
}
在此方法中,做的操作为:
1、将 hwCamera 封装为 VirtualCamera 对象 client
2、通过 changeFramesInFlight 去想底层申请指定大小的图像缓冲区
3、将 client 对象添加 进入 mClients
//client→getAllowedBuffers() 默认值为1
changeFramesInFlight 为:
bool HalCamera::changeFramesInFlight(int delta) {
unsigned bufferCount = 0;
for (auto&& client : mClients) {
sp virtCam = client.promote();
if (virtCam != nullptr) {
bufferCount += virtCam->getAllowedBuffers();
}
}
bufferCount += delta;
if (bufferCount < 1) {
bufferCount = 1;
}
Return result = mHwCamera→setMaxFramesInFlight(bufferCount);
return success;
他会通过 mHwCamera 调用到 Hardward对端 EvsCamera 的 setMaxFramesInFlight 方法传入的
参数默认初始值为 1,
而在 Hardward 端也是经过一系列的调用,去申请对应大小的图像缓冲区 mBuffer
调用流程为:
EvsCamera::setMaxFramesInFlight(uint32_t bufferCount)
}
EvsCamera::setAvailableFrames_Locked(unsigned bufferCount)
EvsCamera::increaseAvailableFrames_Locked(unsigned numToAdd)