Android USB摄像头插拔监听

  • 一、简要介绍
  • 二、使用广播监听USB摄像头插拔情况
  • 1、注册监听USB设备插拔广播
  • 2、判断UsbDevice是否是Camera设备 (重点)
  • 二、通过UsbManager来获取当前设备的Usb摄像头信息
  • Camera设备节点
  • 使用设备节点要注意:
  • 共勉:人生没有一条路是白走的。每个不满意的现在,都有个不努力的曾经。一个人至少拥有一个梦想,有一个理由去坚强。


部分Android开发设备可以插入USB摄像头使用,刚好工作用到了,本文将对USBCamera拔插监听进行总结记录。

一、简要介绍

USB摄像头,可以通过广播监听,监听到USB摄像头的插拔情况;
还可以使用UsbManager来获取当前设备存在的外设,从中获取USB摄像头信息。
还可以在底层监听Camera变化,不过这个比较麻烦,本文不做介绍。

使用广播可以动态获取UsbCamera,并作出相应的操作;
使用UsbManager,主要是用在即时性的操作,
比如某个页面准备要用UsbCamera可以提前判断是否已存在UsbCamera设备。

二、使用广播监听USB摄像头插拔情况

1、注册监听USB设备插拔广播

private BroadcastReceiver mBroadcastReceiver;

    //注册监听Usb设备插拔广播
    private void registerBroadcastReceiver() {
        Log.i(TAG, "registerBroadcastReceiver");
        if (mBroadcastReceiver == null) {
            mBroadcastReceiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    if (intent == null) {
                        return;
                    }
                    // 这里可以拿到插入的USB设备对象
                    UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                    switch (intent.getAction()) {
                        case UsbManager.ACTION_USB_DEVICE_ATTACHED: // 插入USB设备
                            if (isUsbCameraDevice(usbDevice)) {
                                Toast.makeText(UsbCameraActivity.this, "Usb摄像头已插入", Toast.LENGTH_LONG).show();
                                //do some thing
                            }
                            break;
                        case UsbManager.ACTION_USB_DEVICE_DETACHED: // 拔出USB设备
                            if (isUsbCameraDevice(usbDevice)) {
                                Toast.makeText(UsbCameraActivity.this, "Usb摄像头已拔出", Toast.LENGTH_LONG).show();
                                //do some thing
                            }
                            break;
                        default:
                            break;
                    }

                }
            };
        }
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); //外设插入广播
        intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); //外设拔出广播
        registerReceiver(mBroadcastReceiver, intentFilter);
    }



    //反注册监听Usb设备插拔广播
    private void unregisterBroadcastReceiver() {
        Log.i(TAG, "unregisterBroadcastReceiver");
        if (mBroadcastReceiver != null) {
            unregisterReceiver(mBroadcastReceiver);
        }
    }

2、判断UsbDevice是否是Camera设备 (重点)

private static final int USB_CAMERA_TYPE = 14; //可能跟不同系统设备相关,一般是某个固定值,可以打Log验证。

    /**
     * 判断当前Usb设备是否是Camera设备
     */
    private boolean isUsbCameraDevice(UsbDevice usbDevice) {
        Log.i(TAG, "isUsbCameraDevice  usbDevice " + usbDevice.getProductName() + usbDevice.getDeviceClass() + ", subclass = " + usbDevice.getDeviceSubclass());
        if (usbDevice == null) {
            return false;
        }
        boolean isCamera = false;
        int interfaceCount = usbDevice.getInterfaceCount();
        for (int interIndex = 0; interIndex < interfaceCount; interIndex++) {
            UsbInterface usbInterface = usbDevice.getInterface(interIndex);
            //usbInterface.getName()遇到过为null的情况
            if ((usbInterface.getName() == null || usbDevice.getProductName().equals(usbInterface.getName())) && usbInterface.getInterfaceClass() == USB_CAMERA_TYPE) {
                isCamera = true;
                break;
            }
        }
        Log.i(TAG, "onReceive usbDevice = " + usbDevice.getProductName() + "isCamera = " + isCamera);
        return isCamera;
    }

网上是有很多判断UsbDevice是否是Camera设备的逻辑代码,但是好像没有完全正确的。

比如下面这个:

//不准确的判断
    public boolean isUsbCamera(UsbDevice usbDevice) {
        return usbDevice != null && 239 == usbDevice.getDeviceClass() && 2 == usbDevice.getDeviceSubclass();
    }

很多设备都会符合这个条件,比分mic设备,无线WiFi设备等等。所以使用这个逻辑会有概率的误判断。

通过我的多次研究判断,usbInterface.getInterfaceClass()也是不同设备的类型值,但是这个值在api中未定义。
这个值在MTK848,9950,USB摄像头设备获取到的都是等于14。

二、通过UsbManager来获取当前设备的Usb摄像头信息

/**
     * 通过 UsbManager获取当前外设摄像头信息
     */
    private List<UsbDevice> getCameraList() {
        Log.i(TAG, "getCameraList");
        List<UsbDevice> cameraList = new ArrayList<>();
        UsbManager mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE); //这个只适用于Usb设备
        HashMap<String, UsbDevice> mDeviceMap = mUsbManager.getDeviceList();
        if (mDeviceMap != null) {
            for (UsbDevice usbDevice : mDeviceMap.values()) {
                if (isUsbCameraDevice(usbDevice)) {
                    cameraList.add(usbDevice);
                }
            }
        }
        return cameraList;
    }


    /**
     * 通过 UsbManager获取当前外设摄像头名称信息
     */
    private List<String> getCameraStringList() {
        List<String> cameraList = new ArrayList<>();
        UsbManager mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
        HashMap<String, UsbDevice> mDeviceMap = mUsbManager.getDeviceList();
        if (mDeviceMap != null) {
            for (UsbDevice usbDevice : mDeviceMap.values()) {
                if (isUsbCameraDevice(usbDevice)) {
                    cameraList.add(usbDevice.getProductName());
                }
            }
        }
        return cameraList;
    }

同样是通过遍历所有UsbDevice对象来判断是否是Usb摄像头设备。

Camera设备节点

外部设备节点都是在dev下的,相机节点的关键字是video,
比如:/dev/video0 表示第一个插入的摄像头,video1表示插入的第二个摄像头,以此类推。
有系统权限的应用,可以直接读取这个节点的名称,

private void updateDevVideoList() {

    File file = new File("/dev");
    if (file.isDirectory()) {
        File[] list = file.listFiles();
        if (list != null) {
            for (File it : list) {
                if (it.getName().contains("video")){
                    //videoX
                }
            }
        }
    }

}

使用设备节点要注意:

1、节点数据里面,没有设备名称
2、并不是所有外接摄像头都是只有一个设备节点,
部分设备存在两个节点,需要在C++底层对节点进行分析判断(这部分内容有写文章专门分析)

共勉:人生没有一条路是白走的。每个不满意的现在,都有个不努力的曾经。一个人至少拥有一个梦想,有一个理由去坚强。