迷茫的小小程序员 2015-12-31
最近有一个裁剪修改Samrt210开发板支持双摄像切换工作,但是原始的210 Android只能支持一个摄像头工作,切换摄像头是需要reboot系统的。我刚开始以为不能同时支持两个摄像头是因为每次系统都只会加载CMOS和USB两个HAL中一个导致的,所以就一直在修改加载HAL的代码,改了半天发现然并卵。
虽然修改了加载HAL没用,但是我还是一直都觉得这是可行的方法,问题就在于Android Hal一般开发板只会提供so文件不会又源码提供的,虽然没有源码但是一般so文件打开的系统设备路径都是字符串,那就意味着我可以通过strings命令看看camera.usb.so和camera.cmos.so,所以对两个so文件执行如下的命令:
这个两个so文件好像都会去打开/dev/video0这个设备,难怪我之前怎么修改hal的加载都只能有一个摄像头工作,看到这个基本已经知道问题在哪里了,但是没有源码要怎么办呢?video0, video2, video3又是什么呢?
首先要搞清楚video0, video2, video3到底是啥都够可以一个系列的博客,所以这里就先直接说结果了。
动作 | 系统设备 | 可用摄像头 |
开机未插入USB摄像头 | video0(fimc0), video1(fimc1), video2(fimc2) | CMOS |
开机插入USB摄像头 | video0(usb), video1(fimc0), video2(fimc1), video3(fimc2) | USB |
开机未插入USB摄像头, 开机完成以后插入摄像头 | video0(fimc0), video1(fimc1), video2(fimc2), video3(usb) | CMOS |
由于USB是热插拔设备所以设备符会有变化(重点在于开机之前插上和开机以后插入的设备符是不一样的),结合上面打开设备可以得出下面的结论,
CMOS要正常工作需要video0(fimc0)和video2(fimc2); USB要正常工作需要video0(usb)和video3(fimc2);
那么现在问题就在于怎么修改so文件中的字符串,刚开始想通过直接修改so文件实现的,不过鉴于本人学艺不精就没去弄了,所以就在android源码下面的device/samsung/找了一个有源码的libcamera试试看。
最后选择android-4.0.3_r1/device/samsung/crespo/libcamera这个代码,编译以后直接替换camera.cmos.so文件,cmos摄像头竟然可以用了。索性我就把源码中的/dev/video0改成/dev/video1, /dev/video2改/dev/video3试试,开机的时候把USB摄像头插上,试试看CMOS能不能用。结果就是CMOS果然能用,但有个前提就是要先把USB摄像头插上。
那么既然在开机的时候插入USB摄像头的情况下,CMOS摄像头也是可用的,那现在的问题就会到修改HAL加载了,这个就比较简单了,直接上代码就行了
修改smart210/Android/android-4.0.3_r1/frameworks/base/services/camera/libcameraservice/CameraService.cpp文件
1. 添加load hal的代码,直接重拷贝即可;
2. 在添加一个USB的hw_module_t;
3. 定义CMOS_CAMERA和USB_CAMERA的ID;
4. 把原来直接调用mModule地方替换成getModule(cameraId)。
// This is ugly and only safe if we never re-create the CameraService, but
// should be ok for now.
static CameraService *gCameraService;
#define HAL_MODULE_INFO_SYM_AS_STR "HMI"
int CameraService::load(const char *id,
const char *path,
const struct hw_module_t **pHmi)
{
int status;
void *handle;
struct hw_module_t *hmi;
/*
* load the symbols resolving undefined symbols before
* dlopen returns. Since RTLD_GLOBAL is not or'd in with
* RTLD_NOW the external symbols will not be global
*/
handle = dlopen(path, RTLD_NOW);
if (handle == NULL) {
char const *err_str = dlerror();
LOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
status = -EINVAL;
/* success */
status = 0;
if (status != 0) {
hmi = NULL;
if (handle != NULL) {
dlclose(handle);
handle = NULL;
}
} else {
LOGE("loaded HAL id=%s path=%s hmi=%p handle=%p",
id, path, *pHmi, handle);
}
*pHmi = hmi;
return status;
}
/* Get the address of the struct hal_module_info. */
const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
hmi = (struct hw_module_t *)dlsym(handle, sym);
if (hmi == NULL) {
LOGE("load: couldn't find symbol %s", sym);
status = -EINVAL;
/* success */
status = 0;
if (status != 0) {
hmi = NULL;
if (handle != NULL) {
dlclose(handle);
handle = NULL;
}
} else {
LOGE("loaded HAL id=%s path=%s hmi=%p handle=%p",
id, path, *pHmi, handle);
}
*pHmi = hmi;
return status;
}
/* Check that the id matches */
if (strcmp(id, hmi->id) != 0) {
LOGE("load: id=%s != hmi->id=%s", id, hmi->id);
status = -EINVAL;
/* success */
status = 0;
if (status != 0) {
hmi = NULL;
if (handle != NULL) {
dlclose(handle);
handle = NULL;
}
} else {
LOGE("loaded HAL id=%s path=%s hmi=%p handle=%p",
id, path, *pHmi, handle);
}
*pHmi = hmi;
return status;
}
hmi->dso = handle;
/* success */
status = 0;
if (status != 0) {
hmi = NULL;
if (handle != NULL) {
dlclose(handle);
handle = NULL;
}
} else {
LOGE("loaded HAL id=%s path=%s hmi=%p handle=%p",
id, path, *pHmi, handle);
}
*pHmi = hmi;
return status;
}
#define USB_CAMERA 1
#define CMOS_CAMERA 0
camera_module_t * CameraService::getModule(int cameraid){
switch(cameraid){
case USB_CAMERA:
return mUsbModule;
case CMOS_CAMERA:
return mCMOSModule;
}
return NULL;
}
CameraService::CameraService()
:mSoundRef(0), mCMOSModule(0), mUsbModule(0)
{
LOGI("CameraService222 started (pid=%d)", getpid());
gCameraService = this;
}
void CameraService::onFirstRef()
{
BnCameraService::onFirstRef();
if (load(CAMERA_HARDWARE_MODULE_ID, "/system/lib/hw/camera.usb.so",
(const hw_module_t **)&mUsbModule) < 0) {
LOGE("Could not load camera HAL module");
mNumberOfCameras = 0;
}
if (load(CAMERA_HARDWARE_MODULE_ID, "/system/lib/hw/camera.cmos.so",
(const hw_module_t **)&mCMOSModule) < 0) {
LOGE("Could not load camera HAL module");
mNumberOfCameras = 0;
}
mNumberOfCameras = 2;
for (int i = 0; i < mNumberOfCameras; i++) {
setCameraFree(i);
}
#if 0
else {
mNumberOfCameras = mModule->get_number_of_cameras();
if (mNumberOfCameras > MAX_CAMERAS) {
LOGE("Number of cameras(%d) > MAX_CAMERAS(%d).",
mNumberOfCameras, MAX_CAMERAS);
mNumberOfCameras = MAX_CAMERAS;
}
}
#endif
// Read the system property to determine if we have to use the
// AUDIO_STREAM_ENFORCED_AUDIBLE type.
char value[PROPERTY_VALUE_MAX];
property_get("ro.camera.sound.forced", value, "0");
if (strcmp(value, "0") != 0) {
mAudioStreamType = AUDIO_STREAM_ENFORCED_AUDIBLE;
} else {
mAudioStreamType = AUDIO_STREAM_MUSIC;
}
}
除了上面的修改以外,还需要修改两个地方。
status_t CameraService::getCameraInfo(int cameraId,
struct CameraInfo* cameraInfo) {
if (!getModule(cameraId)) {
return NO_INIT;
}
if (cameraId < 0 || cameraId >= mNumberOfCameras) {
return BAD_VALUE;
}
struct camera_info info;
status_t rc = getModule(cameraId)->get_camera_info(0, &info);
<span style="color:#ff0000;">cameraInfo->facing = cameraId;//info.facing;</span>
cameraInfo->orientation = info.orientation;
return rc;
}
sp<ICamera> CameraService::connect(
const sp<ICameraClient>& cameraClient, int cameraId) {
int callingPid = getCallingPid();
sp<CameraHardwareInterface> hardware = NULL;
LOG1("CameraService::connect E (pid %d, id %d)", callingPid, cameraId);
if (!getModule(cameraId)) {
LOGE("Camera HAL module not loaded");
return NULL;
}
sp<Client> client;
if (cameraId < 0 || cameraId >= mNumberOfCameras) {
LOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).",
callingPid, cameraId);
return NULL;
}
char value[PROPERTY_VALUE_MAX];
property_get("sys.secpolicy.camera.disabled", value, "0");
if (strcmp(value, "1") == 0) {
// Camera is disabled by DevicePolicyManager.
LOGI("Camera is disabled. connect X (pid %d) rejected", callingPid);
return NULL;
}
Mutex::Autolock lock(mServiceLock);
if (mClient[cameraId] != 0) {
client = mClient[cameraId].promote();
if (client != 0) {
if (cameraClient->asBinder() == client->getCameraClient()->asBinder()) {
LOG1("CameraService::connect X (pid %d) (the same client)",
callingPid);
return client;
} else {
LOGW("CameraService::connect X (pid %d) rejected (existing client).",
callingPid);
return NULL;
}
}
mClient[cameraId].clear();
}
if (mBusy[cameraId]) {
LOGW("CameraService::connect X (pid %d) rejected"
" (camera %d is still busy).", callingPid, cameraId);
return NULL;
}
struct camera_info info;
if (getModule(cameraId)->get_camera_info(0, &info) != OK) {
LOGE("Invalid camera id %d", cameraId);
return NULL;
}
char camera_device_name[10];
<span style="color:#ff0000;">snprintf(camera_device_name, sizeof(camera_device_name), "%d", 0);</span>
hardware = new CameraHardwareInterface(camera_device_name);
if (hardware->initialize(&getModule(cameraId)->common) != OK) {
hardware.clear();
return NULL;
}
client = new Client(this, cameraClient, hardware, cameraId, info.facing, callingPid);
mClient[cameraId] = client;
LOG1("CameraService::connect X");
return client;
}
至此修改基本都完成,但是你会发现这么修改了以后CMOS的摄像头还是不能工作。会提示打开/dev/video1失败,这是为什么呢?
其实这是因为系统在其他地方会使用到这个设备,然后在整个源码里面搜索这个字符串,找到以后直接把/dev/video1改成/dev/video2即可了
./device/friendly-arm/mini210/proprietary/libfimc/SecFimc.cpp:32:#define FIMC2_DEV_NAME "/dev/video2"
这么修改了以后其实是由局限性的:
1. 目前开机的时候必须要插入USB摄像头,否则会导致所有的摄像头不能使用。
修改方案其实也比较简单的,在打开设备的之前判断一下USB摄像的设备即可,这边没这么做主要是由于没时间在去弄一个camera.usb.so,所以目前就先这样了。