Smart210 支持双摄像头工作实现

本文解决了一个特定开发板上的双摄像头切换问题,通过修改HAL加载方式和设备文件路径,实现了开机状态下USB摄像头与CMOS摄像头的同时支持。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

迷茫的小小程序员   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,所以目前就先这样了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值