做项目用到了多路摄像头,但是原生固件最多支持两路同时打开(cameraID 0和1),又不想对固件修改,所以打算采用uvc方式打开多路USB摄像头。找了几篇博客,以及github上的项目,down下来发现存在各种各样的问题(编译问题,打开多路问题)。从这个角度出发,打算把自己的过程以及代码分享出来。
硬件环境:
rk3399,rk3288,展讯9853
Logitech摄像头两个,WCH双目摄像头两个,一共六个镜头
六路,厉害了haha
ndk版本一定要注意,否者会出现打不开的问题,笔者之前配置的android-ndk-r16b,怎么搞也不行,结果改成android-ndk-r14b就ok了,这点要注意一下
USBMonitor/usb设备控制类
主要的能力:usb设备节点(设备信息)的获取,对USB设备操作的广播监听(设备的连接与断开,usb权限请求的监听)
当我们点击demo中视频预览区域会弹出当前camera设备的列表,列表数据就是由它得来,需要注意得地方是dialog这里做了一个Camera设备的筛选
public void updateDevices() {
// mUSBMonitor.dumpDevices();
final List 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连接摄像头
到这里相机就打开了,要注意的是数据回调这部分我对源码修改了一下。
嗯,就是这些,还是挺简单的,代码会后续优化
原项目地址(是真的牛,不过我下载编译后有些问题):
https://github.com/saki4510t/UVCCamera
我做了一些修改的项目地址:
https://github.com/yyfd2013zy/UvcCameraDemo