51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Android UVCCamera扫描USB Device的过程和原理分析

UVCCamera工程中的测试用例,需要扫描USB设备并以列表的方式展示出来,这里以usbCameraTest中的MainActivity为例说明,其他示例也类似。

进入Activity一般都是黑屏,在左上角有一个按钮,这个按钮点击后会打开一个dialog样式的对话框,用来选择自己需要操作的USB设备,一般选择依据是通过设备的pid,vid来选择,前提是这个设备需要是支持UVC协议的摄像头,否则即使选了也不能预览摄像头画面。

|------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 | private final OnClickListener mOnClickListener = new OnClickListener() { @Override public void onClick(final View view) { synchronized (mSync) { if (mUVCCamera == null) { CameraDialog.showDialog(MainActivity.this); } else { releaseCamera(); } } } }; |

上述代码中,CameraDialog.showDialog(MainActivity.this);这一行就是展开对话框显示当前所有过滤的设备。

CameraDialogonResume方法中有updateDevices();

|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 | 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); } |

第一行便是调用getDeviceFilters进行xml的解析,这里解析的是res下的xml中device_filter.xml文件,将解析的内容放进list中:

|------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | /** * 指定したxmlリソースからDeviceFilterリストを生成する * @param context * @param deviceFilterXmlId * @return */ public static List<DeviceFilter> getDeviceFilters(final Context context, final int deviceFilterXmlId) { final XmlPullParser parser = context.getResources().getXml(deviceFilterXmlId); final List<DeviceFilter> deviceFilters = new ArrayList<DeviceFilter>(); try { int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_TAG) { final DeviceFilter deviceFilter = readEntryOne(context, parser); if (deviceFilter != null) { deviceFilters.add(deviceFilter); } } eventType = parser.next(); } } catch (final XmlPullParserException e) { Log.d(TAG, "XmlPullParserException", e); } catch (final IOException e) { Log.d(TAG, "IOException", e); } return Collections.unmodifiableList(deviceFilters); } |

其中readEntryOne表示读取其中一项内容,代码如下:

|------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | public static DeviceFilter readEntryOne(final Context context, final XmlPullParser parser) throws XmlPullParserException, IOException { int vendorId = -1; int productId = -1; int deviceClass = -1; int deviceSubclass = -1; int deviceProtocol = -1; boolean exclude = false; String manufacturerName = null; String productName = null; String serialNumber = null; boolean hasValue = false; String tag; int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { tag = parser.getName(); if (!TextUtils.isEmpty(tag) && (tag.equalsIgnoreCase("usb-device"))) { if (eventType == XmlPullParser.START_TAG) { hasValue = true; vendorId = getAttributeInteger(context, parser, null, "vendor-id", -1); if (vendorId == -1) { vendorId = getAttributeInteger(context, parser, null, "vendorId", -1); if (vendorId == -1) vendorId = getAttributeInteger(context, parser, null, "venderId", -1); } productId = getAttributeInteger(context, parser, null, "product-id", -1); if (productId == -1) productId = getAttributeInteger(context, parser, null, "productId", -1); deviceClass = getAttributeInteger(context, parser, null, "class", -1); deviceSubclass = getAttributeInteger(context, parser, null, "subclass", -1); deviceProtocol = getAttributeInteger(context, parser, null, "protocol", -1); manufacturerName = getAttributeString(context, parser, null, "manufacturer-name", null); if (TextUtils.isEmpty(manufacturerName)) manufacturerName = getAttributeString(context, parser, null, "manufacture", null); productName = getAttributeString(context, parser, null, "product-name", null); if (TextUtils.isEmpty(productName)) productName = getAttributeString(context, parser, null, "product", null); serialNumber = getAttributeString(context, parser, null, "serial-number", null); if (TextUtils.isEmpty(serialNumber)) serialNumber = getAttributeString(context, parser, null, "serial", null); exclude = getAttributeBoolean(context, parser, null, "exclude", false); } else if (eventType == XmlPullParser.END_TAG) { if (hasValue) { return new DeviceFilter(vendorId, productId, deviceClass, deviceSubclass, deviceProtocol, manufacturerName, productName, serialNumber, exclude); } } } eventType = parser.next(); } return null; } |

这个解析是主要解析过程,后面会用到,这里要明白这个解析出来的规则后面会用来进行设备过滤,符合规则的设备就进行显示,不符合规则的设备就不显示。

这里解析主要解析了vendorIdproductIdclasssubclassprotocolmanufactureserialexclude等字段,也就是说这些字段其实都可以配置在xml中,如果xml中定义了这些内容,则会被解析到,如果没有定义,则返回结果为-1。
exclude字段是排除的意思,也就是如果定义了这个字段,则这一项xml为排除项,如果匹配上则要把这个device排除掉,这是一个反向过滤标志。

在读到xml的末尾标签,则会创建DeviceFilter对象:

|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 | } else if (eventType == XmlPullParser.END_TAG) { if (hasValue) { return new DeviceFilter(vendorId, productId, deviceClass, deviceSubclass, deviceProtocol, manufacturerName, productName, serialNumber, exclude); } } |

在之前的上一层会循环解析这个xml文件,将所有解析到的DeviceFilter放在list里面,用于之后的设备匹配。

updateDevices的第二行可以看到:

|-----------|--------------------------------------------------------------------------------------------------------------| | 1 | mDeviceListAdapter = new DeviceListAdapter(getActivity(), mUSBMonitor.getDeviceList(filter.get(0))); |

这里有个点需要注意,代码写的是:
mUSBMonitor.getDeviceList(filter.get(0)),如果想用filter解析出来的xml项都进行匹配,则直接传入filter即可,不用filter.get(0)选择第一个,如果写了filter.get(0),则xml中的内容次序就会有关系,也就是说只会使用第一个进行匹配,其他的会忽略。
如果写filter.get(0),又想多匹配设备,则xml可以写成如下:

|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 | <?xml version="1.0" encoding="utf-8"?> <resources> <!-- 以下内容的 vendor-id、product-id就是USB的vid和pid了--> <usb-device class="239" subclass="2" /> <!-- all device of UVC --> <!-- <usb-device vendor-id="xxx" product-id="xxx"/> --> </resources> |

这里便进行了过滤匹配:

|---------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /** * return device list, return empty list if no device matched * @param filter * @return * @throws IllegalStateException */ public List<UsbDevice> getDeviceList(final DeviceFilter filter) throws IllegalStateException { if (destroyed) throw new IllegalStateException("already destroyed"); final HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList(); final List<UsbDevice> result = new ArrayList<UsbDevice>(); if (deviceList != null) { for (final UsbDevice device: deviceList.values() ) { if ((filter == null) || (filter.matches(device) && !filter.isExclude)) { result.add(device); } } } return result; } |

对于mUsbManager获取到的每个Device,如果设备的pidvidfilter中的匹配上,也就是相等,并且excludefalse(上面已经对该字段进行了说明),则把这个Device加入list,并赋值给Adapter进行展示。当然前提是需要有filter,没有的话可以创建任意一个即可。

那么现在逻辑就清楚了,如果要想展示自己想要展示的设备,可以编辑xml文件,加入自己的设备的pidvid即可,也可以以classsubclass等来进行过滤,这样很多设备可能都会满足条件。

UVCCamera相关内容 {#UVCCamera相关内容}

参考链接:https://blog.csdn.net/bawang_cn/article/details/121166865


赞(1)
未经允许不得转载:工具盒子 » Android UVCCamera扫描USB Device的过程和原理分析