Android USB Host bulkTransfer返回有效长度但无数据及流数据问题
问题解决方案
一、解决bulkTransfer返回有效长度但数组全0的问题
1. 确认端点方向
摄像头是输入设备,必须选择方向为USB_DIR_IN的端点,你可以通过以下代码筛选:
val inputEndpoint = usbInterface.endpoints.find { it.direction == UsbConstants.USB_DIR_IN }
如果选了USB_DIR_OUT的端点,读取到的只是你发送的空数据,自然全为0。
2. 先通过控制传输初始化摄像头
USB摄像头不会主动输出数据,必须先发送UVC(USB视频类)控制指令配置参数(格式、分辨率、帧率等)。示例配置逻辑:
private fun configureCamera(connection: UsbDeviceConnection, usbInterface: UsbInterface) { // 参考UVC协议规范,发送SET_CUR命令配置视频流参数 val requestType = UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_DIR_OUT val request = 0x01 // SET_CUR指令 val value = 0x0000 // 格式参数,需根据摄像头支持的格式调整 val index = usbInterface.id // 示例配置数据(需替换为你的摄像头支持的分辨率、格式) val configData = byteArrayOf(0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00) connection.controlTransfer(requestType, request, value, index, configData, configData.size, 1000) }
3. 确保接口独占
在调用bulkTransfer前,必须确保成功独占接口:
if (!connection.claimInterface(usbInterface, true)) { Log.e("CameraDebug", "无法独占USB接口") return }
二、实现持续获取数据流
1. 在协程IO线程循环读取
利用协程在IO调度器中循环调用bulkTransfer,同时处理协程取消、设备断开的情况:
override fun onReceive(context: Context?, intent: Intent?) = goAsync(Dispatchers.IO) { if (intent?.action != ACTION_USB_PERMISSION) return@goAsync val usbManager = context?.getSystemService(Context.USB_SERVICE) as UsbManager val device: UsbDevice? = intent.getParcelable(UsbManager.EXTRA_DEVICE) ?: return@goAsync val usbInterface = device.getInterface(0) val inputEndpoint = usbInterface.endpoints.find { it.direction == UsbConstants.USB_DIR_IN } ?: return@goAsync usbManager.openDevice(device)?.use { connection -> if (!connection.claimInterface(usbInterface, true)) { Log.e("CameraDebug", "接口抢占失败") return@goAsync } // 先配置摄像头 configureCamera(connection, usbInterface) val buffer = ByteArray(inputEndpoint.maxPacketSize) var isRunning = true // 持续读取数据,协程取消时停止 while (isRunning && isActive) { val bytesRead = connection.bulkTransfer(inputEndpoint, buffer, buffer.size, 1000) when { bytesRead < 0 -> { Log.e("CameraDebug", "读取失败: $bytesRead") isRunning = false } bytesRead == 0 -> continue else -> { // 拷贝数据避免覆盖,再进行展示/录制处理 val data = buffer.copyOf(bytesRead) Log.d("CameraDebug", "读取到${bytesRead}字节数据") // 这里添加数据处理逻辑:比如转Bitmap展示、写入文件 } } } // 释放资源 connection.releaseInterface(usbInterface) } }
2. 优化权限申请的PendingIntent
Android 12+版本需要指定PendingIntent的flag,避免权限申请失败:
val permissionIntent = PendingIntent.getBroadcast( context, 0, Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT )
3. 监听设备断开事件
注册ACTION_USB_DEVICE_DETACHED广播,当设备断开时立即停止循环并释放资源,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Alon Shlider
相关产品推荐
相关产品推荐

