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相关内容

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