You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 12:40:14