赞
踩
在android8.1系统中调用摄像头是通过 CameraManager::getCameraIdList() 方法获取系统摄像头列表,从列表中选择满足需要摄像头,用以拍照、录像或全景拍照。
上篇中以介绍如何把v4l2loopback移植到android内核,本章介绍如何配置虚拟摄像头参数,以满足android用户程序直接使用。
首先,我们看一下 class CameraBase 这个类的定义,文件路径:
@frameworks/av/camera/include/camera/CameraBase.h
class CameraBase : public IBinder::DeathRecipient { public: static status_t getCameraInfo(int cameraId, /*out*/ struct hardware::CameraInfo* cameraInfo); sp<TCamUser> remote(); // Status is set to 'UNKNOWN_ERROR' after successful (re)connection status_t getStatus(); protected: Mutex mLock; // helper function to obtain camera service handle static const sp<::android::hardware::ICameraService> getCameraService(); sp<TCamUser> mCamera; status_t mStatus; sp<TCamListener> mListener; const int mCameraId; typedef CameraBase<TCam> CameraBaseT; }
基类中 getCameraInfo() 是获取摄像头配置信息,其实现方法如下:
@frameworks/av/camera/CameraBase.cpp
// this can be in BaseCamera but it should be an instance method
template <typename TCam, typename TCamTraits>
status_t CameraBase<TCam, TCamTraits>::getCameraInfo(int cameraId,
struct hardware::CameraInfo* cameraInfo) {
const sp<::android::hardware::ICameraService> cs = getCameraService();
if (cs == 0) return UNKNOWN_ERROR;
binder::Status res = cs->getCameraInfo(cameraId, cameraInfo);
return res.isOk() ? OK : res.serviceSpecificErrorCode();
}
template class CameraBase<Camera>;
因为camera在android系统中是采用C/S结构,/system/bin/mediaserverr是服务端守护进程,frameworks/av/camera下实现的是client内容,
此方法是通过 getCameraService() 获取 sp<::android::hardware::ICameraService> cs 智能指针,从而调用服务端的
cs->getCameraInfo(cameraId, cameraInfo) 的方法,我们进一步跟踪。
文件路径
@frameworks/av/services/camera/libcameraservice/CameraService.cpp
Status CameraService::getCameraInfo(int cameraId, CameraInfo* cameraInfo) { ATRACE_CALL(); Mutex::Autolock l(mServiceLock); if (!mInitialized) { return STATUS_ERROR(ERROR_DISCONNECTED, "Camera subsystem is not available"); } if (cameraId < 0 || cameraId >= mNumberOfCameras) { return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "CameraId is not valid"); } Status ret = Status::ok(); status_t err = mCameraProviderManager->getCameraInfo(std::to_string(cameraId), cameraInfo); if (err != OK) { ret = STATUS_ERROR_FMT(ERROR_INVALID_OPERATION, "Error retrieving camera info from device %d: %s (%d)", cameraId, strerror(-err), err); } return ret; }
通过跟踪源码、发现摄像属性参数是在CameraHAL.cpp文件中,构造摄像头时配置 Facing和Orientation属性值,
源码路径如下:
@vendor/nxp-opensource/imx/libcamera3/CameraHAL.cpp文件中,笔者使用的NXP的android8.1软件系统,
硬件平台IMX8QM。以此类推,摄像头属性是在摄像头HAL层构建设备时通过源码类配置该属性。
因此参数因硬件平台的差异,配置文件会有差异,请读者根据自有硬件平台代码追踪此过程。
@vendor/nxp-opensource/imx/libcamera3/CameraHAL.cpp
CameraHAL::CameraHAL() : mCameraCount(0), mCallbacks(NULL) { // Allocate camera array and instantiate camera devices mCameras = new Camera*[MAX_CAMERAS]; memset(mSets, 0, sizeof(mSets)); memset(mCameras, 0, MAX_CAMERAS * sizeof(Camera*)); // enumerate all camera sensors. enumSensorSet(); //> 配置 camera 的参数子函数 // check if camera exists. for (int32_t index=0; index<MAX_CAMERAS; index++) { if (!mSets[index].mExisting) { continue; } mCameras[index] = Camera::createCamera(index, mSets[index].mSensorName, mSets[index].mFacing, mSets[index].mOrientation, mSets[index].mDevPath); if (mCameras[index] == NULL) { // camera sensor is not supported now. // So, camera count should not change. ALOGW("Error: camera:%d, %s create failed", index, mSets[index].mSensorName); } else { mCameraCount++; } } ALOGI("camera number is %d", mCameraCount); mHotplugThread = new HotplugThread(this); } //> 配置 camera 的参数子函数 void CameraHAL::enumSensorSet() { // get property from init.rc mSets. char orientStr[CAMERA_SENSOR_LENGTH]; ALOGI("%s", __func__); // get back camera property. memset(orientStr, 0, sizeof(orientStr)); property_get("back_camera_name", mSets[BACK_CAMERA_ID].mPropertyName, "0"); property_get("back_camera_orient", orientStr, "0"); mSets[BACK_CAMERA_ID].mOrientation = atoi(orientStr); mSets[BACK_CAMERA_ID].mFacing = CAMERA_FACING_BACK; mSets[BACK_CAMERA_ID].mExisting = false; // get front camera property. memset(orientStr, 0, sizeof(orientStr)); property_get("front_camera_name", mSets[FRONT_CAMERA_ID].mPropertyName, "0"); property_get("front_camera_orient", orientStr, "0"); mSets[FRONT_CAMERA_ID].mOrientation = atoi(orientStr); mSets[FRONT_CAMERA_ID].mFacing = CAMERA_FACING_FRONT; mSets[FRONT_CAMERA_ID].mExisting = false; // make sure of back&front camera parameters. matchDevNodes(); }
通过源码分析、camera的参数配置是通过property设置来管理,此参数配置是在与硬件平台相关的 init.rc 中
进行配置,IMX8QM的摄像配置参数如下:
@device/fsl/imx8qm/mek_8q/init.rc 文件
#Define the config for dual camera
#For landscape mode, orient is 0
#For portrait mode, orient is 90
#the android before honycomb are all in portrait mode
setprop camera.disable_zsl_mode 1
# 后置摄像头
setprop back_camera_name imx8_ov5640_mipi,max9286_mipi,imx8_ov5640
setprop back_camera_orient 0
# 前置摄像头
setprop front_camera_name imx8_ov5640,imx8_ov5640_mipi,uvc
setprop front_camera_orient 0
我们只需要把此摄像头名称修改为 v4l2loopback_back 和 v4l2loopback_front 名称,在构建 CameraHAL 库
时,采用此名称就可以标注出并区别开来。
int32_t CameraHAL::matchDevNodes() { DIR *vidDir = NULL; struct dirent *dirEntry; size_t nameLen = CAMERA_SENSOR_LENGTH - 1; nodeSet *nodes = NULL, *node = NULL, *last = NULL; ALOGI("%s", __func__); vidDir = opendir("/sys/class/video4linux"); if (vidDir == NULL) { return -1; } while ((dirEntry = readdir(vidDir)) != NULL) { if (strncmp(dirEntry->d_name, "video", 5)) { continue; } node = (nodeSet*)malloc(sizeof(nodeSet)); if (node == NULL) { ALOGE("%s malloc failed", __func__); break; } memset(node, 0, sizeof(nodeSet)); if (nodes == NULL) { nodes = node; } else { last->next = node; } last = node; sprintf(node->devNode, "/dev/%s", dirEntry->d_name); getNodeName(node->devNode, node->nodeName, nameLen); } closedir(vidDir); for (int32_t index=0; index<MAX_CAMERAS; index++) { matchPropertyName(nodes, index); } node = nodes; while (node != NULL) { last = node->next; free(node); node = last; } return 0; }
我们看一下 sys/class/video4linux 路径下都有哪些摄像头设备
1|mek_8q:/ # ls sys/class/video4linux/
video0 video1 video12 video13 video2 video3 video4 video5
int32_t CameraHAL::getNodeName(const char* devNode, char name[], size_t length) { int32_t ret = -1; int32_t fd = -1; size_t strLen = 0; struct v4l2_capability vidCap; struct v4l2_dbg_chip_ident vidChip; ALOGI("getNodeName: dev path:%s", devNode); if ((fd = open(devNode, O_RDWR, O_NONBLOCK)) < 0) { ALOGW("%s open dev path:%s failed:%s", __func__, devNode, strerror(errno)); return ret; } ret = ioctl(fd, VIDIOC_QUERYCAP, &vidCap); if (ret < 0) { ALOGW("%s QUERYCAP dev path:%s failed", __func__, devNode); close(fd); fd = -1; return ret; } if (!(vidCap.capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_CAPTURE_MPLANE))) { ALOGW("%s dev path:%s is not capture", __func__, devNode); close(fd); fd = -1; ret = -1; return ret; } strncat(name, (const char*)vidCap.driver, length); strLen = strlen((const char*)vidCap.driver); length -= strLen; ALOGI("getNodeName: node name:%s", name); ret = ioctl(fd, VIDIOC_DBG_G_CHIP_IDENT, &vidChip); if (ret < 0) { ALOGI("%s CHIP_IDENT dev path:%s failed", __func__, devNode); strncat(name, ",", length); strLen = 1; length -= strLen; strncat(name, (const char*)vidCap.card, length); ALOGI("getNodeNames: node name:%s", name); close(fd); fd = -1; return ret; } strncat(name, ",", length); strLen = 1; length -= strLen; strncat(name, vidChip.match.name, length); ALOGI("getNodeNames: node name:%s", name); close(fd); return ret; }
摄像头名称属性是在驱动中标注的,需修改v4l2loopback驱动中节点名称,以此把该摄像头配置为
前置或后置摄像头。
至此位置,我们把 android8.1 中的、摄像头前置或后置的属性配置逻辑给梳理清晰了,如果感觉对你
有所启发或帮助,请点赞或关注,以资鼓励笔者,感谢喽。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。