Android_OTG实现对接UVC摄像头流程

在Android中通过OTG连接UVC摄像头并获取数据需要处理USB通信和UVC协议。以下是实现的基本步骤和代码示例:

1. 添加权限和特性到AndroidManifest.xml

<uses-feature android:name="android.hardware.usb.host" />
<uses-permission android:name="android.permission.USB_PERMISSION" />

<activity ...>
    <intent-filter>
        <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
    </intent-filter>
    <meta-data
        android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
        android:resource="@xml/device_filter" />
</activity>

创建res/xml/device_filter.xml(可选,用于自动检测):

<resources>
    <usb-device class="239" subclass="2" protocol="1" />
</resources>

2. 检测和请求USB权限

public class MainActivity extends Activity {
    private static final String TAG = "UVCCamera";
    private UsbManager usbManager;
    private UsbDevice device;
    private static final int USB_PERMISSION_REQUEST_CODE = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);

        // 检测连接的USB设备
        Intent intent = getIntent();
        if (intent != null && intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
            device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
            if (device != null) {
                requestPermission();
            }
        } else {
            // 手动枚举设备
            HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
            for (UsbDevice dev : deviceList.values()) {
                if (isUVCDevice(dev)) {
                    device = dev;
                    requestPermission();
                    break;
                }
            }
        }
    }

    private void requestPermission() {
        if (usbManager.hasPermission(device)) {
            onPermissionGranted();
        } else {
            usbManager.requestPermission(device, PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0));
        }
    }

    private boolean isUVCDevice(UsbDevice dev) {
        // UVC设备类代码为0x0e(14),子类1,协议1
        return dev.getInterface(0).getInterfaceClass() == UsbConstants.USB_CLASS_VIDEO;
    }
}

3. 连接设备并初始化UVC摄像头

private UsbDeviceConnection connection;
private UsbInterface usbInterface;
private UsbEndpoint videoEndpoint;

private void onPermissionGranted() {
    UsbInterface intf = findUVCInterface(device);
    if (intf == null) {
        Log.e(TAG, "No UVC interface found");
        return;
    }

    connection = usbManager.openDevice(device);
    if (!connection.claimInterface(intf, true)) {
        Log.e(TAG, "Failed to claim interface");
        return;
    }

    // 查找视频流端点
    for (int i = 0; i < intf.getEndpointCount(); i++) {
        UsbEndpoint ep = intf.getEndpoint(i);
        if (ep.getDirection() == UsbConstants.USB_DIR_IN && ep.getType() == UsbConstants.USB_ENDPOINT_XFER_ISOC) {
            videoEndpoint = ep;
            break;
        }
    }

    if (videoEndpoint == null) {
        Log.e(TAG, "No video endpoint found");
        return;
    }

    // 配置摄像头参数(示例:MJPEG格式,640x480)
    configureCamera();
    startStreaming();
}

private UsbInterface findUVCInterface(UsbDevice device) {
    for (int i = 0; i < device.getInterfaceCount(); i++) {
        UsbInterface intf = device.getInterface(i);
        if (intf.getInterfaceClass() == UsbConstants.USB_CLASS_VIDEO) {
            return intf;
        }
    }
    return null;
}

4. 配置摄像头参数

private void configureCamera() {
    // 发送UVC控制命令(示例:设置PROBE控制)
    byte[] probeControl = new byte[34]; // 根据UVC规范填充
    int result = connection.controlTransfer(
        0x21, // 请求类型:Host到设备,Class,接口
        0x01, // SET_CUR
        0x100, // VS_PROBE_CONTROL << 8
        usbInterface.getId(),
        probeControl, probeControl.length, 1000
    );
    // 处理结果并发送COMMIT控制
}

5. 开始传输视频流

private void startStreaming() {
    // 使用等时传输读取数据
    int bufferSize = videoEndpoint.getMaxPacketSize() * 10;
    ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
    UsbRequest request = new UsbRequest();
    request.initialize(connection, videoEndpoint);
    request.queue(buffer, bufferSize);

    new Thread(() -> {
        while (true) {
            if (connection.requestWait() == request) {
                processVideoData(buffer);
                buffer.clear();
                request.queue(buffer, bufferSize);
            }
        }
    }).start();
}

private void processVideoData(ByteBuffer buffer) {
    // 解析UVC头部并提取视频帧
    // 示例:MJPEG数据以0xFFD8开头,0xFFD9结尾
    byte[] data = new byte[buffer.remaining()];
    buffer.get(data);
    // 处理数据并显示
    runOnUiThread(() -> updateImageView(data));
}

private void updateImageView(byte[] jpegData) {
    Bitmap bitmap = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
    ImageView imageView = findViewById(R.id.imageView);
    imageView.setImageBitmap(bitmap);
}

6. 布局文件(activity_main.xml)

<ImageView
    android:id="@+id/imageView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

注意事项:

  1. UVC协议复杂性:上述代码简化了UVC配置过程,实际需根据摄像头支持的格式构造正确的控制请求。
  2. 性能优化:直接解码JPEG可能在高分辨率下卡顿,建议使用SurfaceView和MediaCodec硬件解码。
  3. 依赖库:推荐使用成熟的库如UVCCamera简化开发。

使用UVCCamera库的示例:

  1. 添加依赖:
implementation 'com.github.jiangdongguo:AndroidUSBCamera:3.3.3'
  1. 快速实现预览:
UVCCamera uvcCamera = new UVCCamera();
uvcCamera.open(usbDevice);
uvcCamera.setPreviewSize(1280, 720);
uvcCamera.setPreviewDisplay(surfaceHolder);
uvcCamera.startPreview();

此方法更高效且处理了底层协议细节,建议在实际项目中使用。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容