Android+OpenCV无法识别DeviceClass239(0xEF)的USB摄像头咨询
解决Android OpenCV无法识别非标准类USB摄像头的问题
问题根源
OpenCV的VideoCapture默认仅识别**标准USB视频类(DeviceClass 0xF)**的设备,而你的摄像头属于复合设备类(DeviceClass 0xEF),这类设备的视频功能通常封装在接口级(接口类为0x0E,即Video类),原生VideoCapture无法直接识别。
解决方案
放弃直接使用VideoCapture(0),改用Android USB API手动读取摄像头的视频流,再转换为OpenCV的Mat进行处理。以下是具体修改步骤:
1. 补充AndroidManifest配置
确保已添加USB主机权限和相关特性:
<uses-feature android:name="android.hardware.usb.host" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.USB_PERMISSION" />
2. 修改代码逻辑,手动处理USB视频流
替换原有的startVideoStreaming方法,添加USB数据读取和格式转换逻辑:
private var usbConnection: UsbDeviceConnection? = null private var videoInterface: UsbInterface? = null private var videoEndpoint: UsbEndpoint? = null private val buffer = ByteArray(1024 * 1024) // 根据摄像头分辨率调整缓冲区大小 private fun startVideoStreaming() { // OpenCV初始化 if (!OpenCVLoader.initDebug()) { Log.e("OpenCV", "Initialization Failed") return } // 查找摄像头的视频接口和端点 findVideoInterfaceAndEndpoint(device) ?: run { Log.e(TAG, "找不到视频接口或端点") return } // 打开USB连接并设置接口 usbConnection = usbManager.openDevice(device) usbConnection?.claimInterface(videoInterface, true) ?: run { Log.e(TAG, "无法获取接口权限") return } // 启动视频流读取线程 Thread { val frameMat = Mat() while (usbConnection != null && videoEndpoint != null) { val bytesRead = usbConnection?.bulkTransfer(videoEndpoint, buffer, buffer.size, 5000) if (bytesRead != null && bytesRead > 0) { // 示例:假设是MJPEG格式,直接解码为Bitmap再转Mat val bitmap = BitmapFactory.decodeByteArray(buffer, 0, bytesRead) Utils.bitmapToMat(bitmap, frameMat) renderFrame(frameMat) } } }.start() } // 查找视频接口(接口类0x0E)和输入端点 private fun findVideoInterfaceAndEndpoint(device: UsbDevice): UsbInterface? { for (i in 0 until device.interfaceCount) { val intf = device.getInterface(i) if (intf.interfaceClass == 0x0E) { // Video类接口 videoInterface = intf // 查找输入端点(摄像头是向主机传输数据,所以是INPUT) for (j in 0 until intf.endpointCount) { val endpoint = intf.getEndpoint(j) if (endpoint.type == UsbConstants.USB_ENDPOINT_XFER_BULK && endpoint.direction == UsbConstants.USB_DIR_IN) { videoEndpoint = endpoint return intf } } } } return null } // 更新stopVideoStreaming方法 private fun stopVideoStreaming() { usbConnection?.releaseInterface(videoInterface) usbConnection?.close() usbConnection = null }
3. 注意事项
- 视频格式适配:不同摄像头的输出格式可能不同(如YUYV、NV21、MJPEG),上述示例假设是MJPEG格式,如果是YUYV等原始格式,需要手动编写格式转换代码(比如将YUYV转为RGB)。
- 缓冲区大小:根据摄像头的分辨率调整
buffer的大小,避免数据溢出。 - 权限处理:确保USB权限申请成功后再启动视频流,避免重复调用
startVideoStreaming(比如surfaceCreated和usbReceiver中都调用了,需要加判断避免重复初始化)。
额外优化
可以添加格式检测逻辑,读取摄像头的UVC描述符,自动识别支持的视频格式和分辨率,提升兼容性。
内容的提问来源于stack exchange,提问作者Mary Kim
相关产品推荐
相关产品推荐

