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/evs
el 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;
}