当前位置:   article > 正文

Android中多USB摄像头解决方案——UVCCamera_opencvwithuvccamera

opencvwithuvccamera

      先贴上采用的开源库链接:https://github.com/saki4510t/UVCCamera

https://github.com/saki4510t/OpenCVwithUVC  -《给USBCamera添加了OpenCV》

      业余时间捣鼓了下Android 板子连接多个usb摄像头的方案,一开始使用系统的CameraV1的api,但是取到的摄像头数量一直不对(api: Camera.getNumberOfCameras()),然后又去网上查了方案(传送门:https://blog.csdn.net/xiangzhihong8/article/details/82877901)发现Android P之后原生就支持多摄像头,心里美滋滋,这么快就大结局了,但是果然天不遂人愿,于是改用CameraV2的api,但还是识别不到完整的摄像头列表。没查到具体原因,但是猜测是跟Android板子有关,虽然在软件上已经支持多摄像头,但还需要底层可能还是限制了最大连接数量。然后去应用市场下了一个usb摄像头app,发现是可以正常识别出所有的摄像头,遂反编译之,发现是使用了UVCCamera。这个开源库貌似已经很久没有维护,并且之前使用的经验来看也有不少bug(主要是兼容性方面),但是手头的硬件只有一个型号,并不需要做太多设备兼容性的适配,因此还是可以拿来一用。下面就分享一下UVCCamera的接入过程。

      无论使用哪种Camera的api,Camera的封装都可以大致分为两个流程:数据采集、渲染。于是我们就可以定义出这两块功能的接口:

数据采集

  1. public interface ICamera {
  2. List<String> getCameras();
  3. interface OnPhotoTake {
  4. void onPhotoTake(Bitmap reader,String path);
  5. }
  6. boolean open(String id);
  7. void close();
  8. void takePicture(OnPhotoTake onPhotoTake,String id);
  9. }

渲染

  1. public interface CameraViewInterface extends IAspectRatioView {
  2. interface Callback {
  3. void onSurfaceCreated(CameraViewInterface view, Surface surface);
  4. void onSurfaceChanged(CameraViewInterface view, Surface surface, int width, int height);
  5. void onSurfaceDestroy(CameraViewInterface view, Surface surface);
  6. }
  7. void onPause();
  8. void onResume();
  9. void setCallback(Callback callback);
  10. SurfaceTexture getSurfaceTexture();
  11. Surface getSurface();
  12. boolean hasSurface();
  13. void setVideoEncoder(final IVideoEncoder encoder);
  14. Bitmap captureStillImage(int width, int height);
  15. }

      虽然是多个摄像头,但是我们可以用一个单例类来统一管理——UVCCameraHelper。在这个类中我们要做的事有:连接上usb摄像头设备,开启摄像头采集数据并将数据渲染到surface上,此外还需要提供拍照、视频录制等api。那么这里就需要用到UVCCcamera这个开源库了。我们需要在UVCCameraHelper定义一个USBMonitor对象,这个类的核心接口提供了USB设备的连接状态维护。

  1. public interface OnDeviceConnectListener {
  2. /**
  3. * called when device attached
  4. * @param device
  5. */
  6. public void onAttach(UsbDevice device);
  7. /**
  8. * called when device dettach(after onDisconnect)
  9. * @param device
  10. */
  11. public void onDettach(UsbDevice device);
  12. /**
  13. * called after device opend
  14. * @param device
  15. * @param ctrlBlock
  16. * @param createNew
  17. */
  18. public void onConnect(UsbDevice device, UsbControlBlock ctrlBlock, boolean createNew);
  19. /**
  20. * called when USB device removed or its power off (this callback is called after device closing)
  21. * @param device
  22. * @param ctrlBlock
  23. */
  24. public void onDisconnect(UsbDevice device, UsbControlBlock ctrlBlock);
  25. /**
  26. * called when canceled or could not get permission from user
  27. * @param device
  28. */
  29. public void onCancel(UsbDevice device);
  30. }

      UVCCameraHelper中开启USB摄像头预览的方法:

  1. public void requestPermission(int index) {
  2. List<UsbDevice> devList = getUsbDeviceList();
  3. if (devList == null || devList.size() == 0) {
  4. return;
  5. }
  6. int count = devList.size();
  7. if (index >= count)
  8. new IllegalArgumentException("index illegal,should be < devList.size()");
  9. if (mUSBMonitor != null) {
  10. mUSBMonitor.requestPermission(getUsbDeviceList().get(index));
  11. }
  12. }
  13. public int getUsbDeviceCount() {
  14. List<UsbDevice> devList = getUsbDeviceList();
  15. if (devList == null || devList.size() == 0) {
  16. return 0;
  17. }
  18. return devList.size();
  19. }
  20. public List<UsbDevice> getUsbDeviceList() {
  21. List<DeviceFilter> deviceFilters = DeviceFilter
  22. .getDeviceFilters(mActivity.getApplicationContext(), R.xml.device_filter_uvc);
  23. if (mUSBMonitor == null || deviceFilters == null)
  24. return null;
  25. return mUSBMonitor.getDeviceList(deviceFilters.get(0));
  26. }

      可以看到在requestPermission方法中先根据指定的index从usb设备列表中拿到设备,然后再调用了USBMonitor的requestPermission方法。我们继续看USBMonitor的requestPermission方法:

  1. public synchronized boolean requestPermission(final UsbDevice device) {
  2. // if (DEBUG) Log.v(TAG, "requestPermission:device=" + device);
  3. boolean result = false;
  4. if (isRegistered()) {
  5. if (device != null) {
  6. if (mUsbManager.hasPermission(device)) {
  7. // call onConnect if app already has permission
  8. processConnect(device);
  9. } else {
  10. try {
  11. // パーミッションがなければ要求する
  12. mUsbManager.requestPermission(device, mPermissionIntent);
  13. } catch (final Exception e) {
  14. // Android5.1.xのGALAXY系でandroid.permission.sec.MDM_APP_MGMTという意味不明の例外生成するみたい
  15. Log.w(TAG, e);
  16. processCancel(device);
  17. result = true;
  18. }
  19. }
  20. } else {
  21. processCancel(device);
  22. result = true;
  23. }
  24. } else {
  25. processCancel(device);
  26. result = true;
  27. }
  28. return result;
  29. }

      在该方法中注解居然是日本的(因为作者是日本人),居然让我感到有种莫名的中二感……言归正传,该方法主要就是先判断是否已经对当前USB设备授权,如果未授权则先弹授权弹窗,如果已经授权那么就执行processConnect方法:

  1. private final void processConnect(final UsbDevice device) {
  2. if (destroyed) return;
  3. updatePermission(device, true);
  4. mAsyncHandler.post(new Runnable() {
  5. @Override
  6. public void run() {
  7. if (DEBUG) Log.v(TAG, "processConnect:device=" + device);
  8. UsbControlBlock ctrlBlock;
  9. final boolean createNew;
  10. ctrlBlock = mCtrlBlocks.get(device);
  11. if (ctrlBlock == null) {
  12. ctrlBlock = new UsbControlBlock(USBMonitor.this, device);
  13. mCtrlBlocks.put(device, ctrlBlock);
  14. createNew = true;
  15. } else {
  16. createNew = false;
  17. }
  18. if (mOnDeviceConnectListener != null) {
  19. mOnDeviceConnectListener.onConnect(device, ctrlBlock, createNew);
  20. }
  21. }
  22. });
  23. }

      在该方法中会创建一个UsbControlBlock对象,在该对象中会根据传入的UsbDevice维护一个UsbDeviceConnection,这个类是被用来对USB设备做收发数据以及控制命令的(This class is used for sending and receiving data and control messages to a USB device.)然后就会回调到我们之前定义的OnDeviceConnectListener接口的onConnect方法。之后在onConnect中就会真正开启摄像头预览。
我们定义一个CameraThread继承自Thread,将所有操作放到子线程中,然后再定一个Handler类来维护所有预览(数据采集+渲染)相关操作,这样我们就可以通过消息机制来控制所有操作的执行顺序了。
      我们先来看onConnect中调用的openCamera(USBMonitor.UsbControlBlock ctrlBlock),它实际就是通过Handler来执行消息的发送与处理:

  1. private void openCamera(USBMonitor.UsbControlBlock ctrlBlock) {
  2. if (mCameraHandler != null) {
  3. mCameraHandler.open(ctrlBlock);
  4. }
  5. }
  1. public void open(final USBMonitor.UsbControlBlock ctrlBlock) {
  2. checkReleased();
  3. sendMessage(obtainMessage(MSG_OPEN, ctrlBlock));
  4. }
  1. case MSG_OPEN:
  2. thread.handleOpen((USBMonitor.UsbControlBlock) msg.obj);

      这里需要提的是在thread的handleOpen方法中会创建UVCCamera这个核心类,它会调用到jni层的api去做真正的相机相关操作。
我们继续回到onConnect,在开启相机之后会接着调用startPreview方法来将采集到的数据绑定到一块surface上:

  1. public void startPreview(final Object surface) {
  2. checkReleased();
  3. if (!((surface instanceof SurfaceHolder) || (surface instanceof Surface) || (surface instanceof SurfaceTexture))) {
  4. throw new IllegalArgumentException("surface should be one of SurfaceHolder, Surface or SurfaceTexture: " + surface);
  5. }
  6. sendMessage(obtainMessage(MSG_PREVIEW_START, surface));
  7. }
  1. case MSG_PREVIEW_START:
  2. thread.handleStartPreview(msg.obj);

      预览开启之后对于拍照等操作也是类似:

  1. public void captureStill(final String path, AbstractUVCCameraHandler.OnCaptureListener listener) {
  2. AbstractUVCCameraHandler.mCaptureListener = listener;
  3. checkReleased();
  4. sendMessage(obtainMessage(MSG_CAPTURE_STILL, path));
  5. isCaptureStill = true;
  6. }
  1. case MSG_CAPTURE_STILL:
  2. thread.handleStillPicture((String) msg.obj);

      这样一个基本的摄像头开启、拍照流程就走完了,当然还有很多细节处理没有一一列出来,如异常处理、各种状态判断等等,这些需要在实际项目开发中根据自己的业务来做处理即可。
未来我会继续去扒jni层的代码,看看底层开启相机具体又做了哪些事情,就请继续期待后续的分享文章。
      从事的工作刚好是自己的爱好,是一件无比幸福的事情。哪怕在业余时间也会像在打游戏一样乐在其中。慢慢找回自己吧。


 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/263001
推荐阅读
相关标签
  

闽ICP备14008679号