UVCCamera工程中的测试用例,一般会开头扫描usb设备,这里以Test5 中的MainActivity为例说明,其实其他也一样。
进入Activity一般是黑屏,在左上角有一个按钮,这个按钮点击后会打开一个dialog样式的activity,来供选择自己需要操作的usb设备,一般选择依据是通过设备的pid,vid来选择,前提是这个设备需要是支持UVC协议的摄像头,否则即使选了也不能预览摄像头画面。
private final CompoundButton.OnCheckedChangeListener mOnCheckedChangeListener
= new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(final CompoundButton compoundButton, final boolean isChecked) {
switch (compoundButton.getId()) {
case R.id.camera_button:
if (isChecked && !mCameraHandler.isOpened()) {
CameraDialog.showDialog(MainActivity.this);
} else {
mCameraHandler.close();
mCaptureButton.setVisibility(View.INVISIBLE);
setCameraButton(false);
}
break;
}
}
};
上述代码中,CameraDialog.showDialog(MainActivity.this);这一行便是展开对话框显示当前所有过滤的设备。
在CameraDialog的onResume方法中有updateDevices();
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中:
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表示读取其中一项内容,代码如下:
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;
}
这个解析是主要解析过程,后面会用到,这里要明白这个解析出来的规则后面会用来进行设备过滤,符合规则的设备就进行显示,不符合规则的设备就不显示。
这里解析主要解析了
vendorId,productId,class,subclass,protocol,manufacture,serial,exclude等字段,也就是说这些字段其实都可以配在xml中,如果xml中定义了这些内容,则会被解析到,如果没有定义,则结果为-1.
exclude字段是排除的意识,也就是如果定义了这个字段,则这一项xml为排除项,遇到了如果匹配上,则要把这个device排除掉,而不是像其他的如果匹配上则需要进行展示。是一个反向过滤标志。
在读到xml的末尾标签,则会创建DeviceFilter对象:
else if (eventType == XmlPullParser.END_TAG) {
if (hasValue) {
return new DeviceFilter(vendorId, productId, deviceClass,
deviceSubclass, deviceProtocol, manufacturerName, productName,
serialNumber, exclude);
}
}
在之前的上一层会循环解析这个xml文件,将所有解析到的DeviceFilter放在list里面,用于之后的设备匹配。
在updateDevices的第二行可以看到:
mDeviceListAdapter = new DeviceListAdapter(getActivity(), mUSBMonitor.getDeviceList(filter.get(0)));
这里有个点需要注意,代码写的是:
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可以写成如下:
<?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>
这里便进行了过滤匹配:
/**
* 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,如果设备的pid,vid和filter中的匹配上,也就是相等,并且exclude为false(上面已经对该字段进行了说明),则把这个Device加入list,并赋值给Adapter进行展示。当然前提是需要有filter,没有的话可以创建任意一个即可。
那么现在逻辑就清楚了,如果要想展示自己想要展示的设备,可以编辑xml文件,加入自己的设备的pid,vid即可,也可以以class,subclass等来进行过滤,这样很多设备可能都会满足条件。
这里只对设备过滤,展示进行分析,其他再说。