Android UVC 同时打开多路摄像头

    前言
    硬件环境
    效果图
    注意问题
    代码分析(摄像头的打开步骤)
        1.找到摄像头设备
        打开摄像头
    项目地址

前言

做项目用到了多路摄像头,但是原生固件最多支持两路同时打开(cameraID 0和1),又不想对固件修改,所以打算采用uvc方式打开多路USB摄像头。找了几篇博客,以及github上的项目,down下来发现存在各种各样的问题(编译问题,打开多路问题)。从这个角度出发,打算把自己的过程以及代码分享出来。
硬件环境

硬件环境:
rk3399,rk3288,展讯9853
Logitech摄像头两个,WCH双目摄像头两个,一共六个镜头
效果图

六路,厉害了haha
在这里插入图片描述
注意问题

ndk版本一定要注意,否者会出现打不开的问题,笔者之前配置的android-ndk-r16b,怎么搞也不行,结果改成android-ndk-r14b就ok了,这点要注意一下
代码分析(摄像头的打开步骤)
1.找到摄像头设备

USBMonitor/usb设备控制类
主要的能力:usb设备节点(设备信息)的获取,对USB设备操作的广播监听(设备的连接与断开,usb权限请求的监听)
当我们点击demo中视频预览区域会弹出当前camera设备的列表,列表数据就是由它得来,需要注意得地方是dialog这里做了一个Camera设备的筛选

public void updateDevices() {
 //        mUSBMonitor.dumpDevices();
         final List<DeviceFilter> filter = DeviceFilter.getDeviceFilters(getActivity(), R.xml.device_filter);
         mDeviceListAdapter = new DeviceListAdapter(getActivity(), mUSBMonitor.getDeviceList(filter.get(0)));
         mSpinner.setAdapter(mDeviceListAdapter);
     }

mUSBMonitor对象就是在MainActivity中初始化的那个,根据device_filter(class = 239)生成一个筛选器,至于为什么根据这个筛选我也不是很清楚,不过debug看来,确实camera数据的class = 239
在这里插入图片描述

这样就筛选出了想要的Camera数据
打开摄像头

关键类 AbstractUVCCameraHandler,UVCCamera
UVCCamera负责jni驱动的调用,可以看作是android APi 的“Camera”类,具有打开,释放,调焦等功能
AbstractUVCCameraHandler负责控制UVCCamera,一些状态回调,数据回调,录制视频文件的编码等
它有一个内部线程类“CameraThread”很重要,主要方法都在这里面,但是蛋疼的是并没有看懂异步处理的核心用意,还是菜啊,望指正
打开逻辑很简单的,具体流程如下:
1.点击想要打开的设备节点,进行权限检查

if (item instanceof UsbDevice) {
                     mUSBMonitor.requestPermission((UsbDevice)item);//获取设备信息,并检查打开此设备的权限
                     ((CameraDialogParent)getActivity()).onDialogResult(false);
                 }  
2.这个设备是否有打开权限,有的话直接打开,没有的话去请求,请求成功了直接打开
if (mUsbManager.hasPermission(device)) {
                     // 如果应用已经拥有权限,请调用onConnect
                     processConnect(device);
                 } else {
                     try {
                         //未经许可请求
                         mUsbManager.requestPermission(device, mPermissionIntent);
                     } catch (final Exception e) {
                         // 它似乎在Android 5.1.x的GALAXY系统中生成一个名为android.permission.sec.MDM_APP_MGMT的未知异常
                         Log.w(TAG, e);
                         processCancel(device);
                         result = true;
                     }
                 }

 

3.假设已经有权限了,直接进到打开摄像头方法里面

private final void processConnect(final UsbDevice device) {
         if (destroyed) return;
         updatePermission(device, true);
         mAsyncHandler.post(new Runnable() {
             @Override
             public void run() {
                 if (DEBUG) Log.v(TAG, "processConnect:device=" + device);
                 UsbControlBlock ctrlBlock;
                 final boolean createNew;
                 ctrlBlock = mCtrlBlocks.get(device);
                 if (ctrlBlock == null) {
                     ctrlBlock = new UsbControlBlock(USBMonitor.this, device);//封装了一个模块数据
                     mCtrlBlocks.put(device, ctrlBlock);
                     createNew = true;
                 } else {
                     createNew = false;
                 }
                 if (mOnDeviceConnectListener != null) {
                     mOnDeviceConnectListener.onConnect(device, ctrlBlock, createNew);
                 }
             }
         });
     }

主要就是封装了一个UsbControlBlock(设备数据),还有一些是第一次打开还是反复打开的逻辑,缓存逻辑

4.回调给了MainActivity,记得前面说的“AbstractUVCCameraHandler负责控制UVCCamera”,没错,就是要用mHandlerFirst来控制打开camera

mHandlerFirst.open(ctrlBlock);//打开摄像机
                 final SurfaceTexture st = mUVCCameraViewFirst.getSurfaceTexture();
                 mHandlerFirst.startPreview(new Surface(st));                runOnUiThread(new Runnable() {
                     @Override
                     public void run() {
                         mCaptureButtonFirst.setVisibility(View.VISIBLE);
                     }
                 });

最终调用到了UVCCamera内的nativeConnect 方法,调用jni连接摄像头

到这里相机就打开了,要注意的是数据回调这部分我对源码修改了一下。

嗯,就是这些,还是挺简单的,代码会后续优化
项目地址