有的自定义系统,对于自己外接的USB摄像头,android原生的camera和camera2都无法打开,CameraX也用不了。这时候就要用libusbCamera,这个库可以打开摄像头,还可以多摄像头同时预览。本文主要是同时打开3个USB摄像头的项目记录,详细的接口介绍请参见原博客。
特别感谢(原博客):UVCAndroid,安卓UVC相机通用开发库(支持多预览和多摄像头)_android com.herohan.uvcapp-CSDN博客
0,测试效果:
1,new一个project
2,增加依赖
implementation 'com.herohan:UVCAndroid:1.0.5'
implementation 'com.github.getActivity:XXPermissions:13.5'
3,增加配置
android.enableJetifier = true
4,增加仓库
maven { url 'https://jitpack.io' }
5,存储权限(这个我觉得不是必须的)
android:requestLegacyExternalStorage="true"
6,布局源码:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:orientation="vertical"><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:orientation="vertical"><TextViewandroid:id="@+id/textViewLeft"android:text="左相机"android:layout_gravity="center"android:layout_width="wrap_content"android:layout_height="wrap_content"/><com.serenegiant.widget.AspectRatioSurfaceViewandroid:id="@+id/svCameraViewLeft"android:layout_gravity="center"android:layout_width="440dp"android:layout_height="220dp"/></LinearLayout><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="10dp"android:orientation="vertical"><TextViewandroid:id="@+id/textViewRight"android:text="右相机"android:layout_gravity="center"android:layout_width="wrap_content"android:layout_height="wrap_content"/><com.serenegiant.widget.AspectRatioSurfaceViewandroid:id="@+id/svCameraViewRight"android:layout_gravity="center"android:layout_width="440dp"android:layout_height="220dp"/></LinearLayout></LinearLayout><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginTop="5dp"android:orientation="vertical"><TextViewandroid:id="@+id/textViewCenter"android:text="中间相机"android:layout_gravity="center"android:layout_width="wrap_content"android:layout_height="wrap_content"/><com.serenegiant.widget.AspectRatioSurfaceViewandroid:id="@+id/svCameraViewCenter"android:layout_gravity="center"android:layout_width="440dp"android:layout_height="220dp"/></LinearLayout></LinearLayout></androidx.coordinatorlayout.widget.CoordinatorLayout>
7,页面源码
package com.qz.camera;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;import android.Manifest;
import android.content.Intent;
import android.hardware.usb.UsbDevice;
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;import com.herohan.uvcapp.CameraHelper;
import com.herohan.uvcapp.ICameraHelper;
import com.hjq.permissions.XXPermissions;
import com.serenegiant.usb.Size;
import com.serenegiant.usb.UVCCamera;
import com.serenegiant.usb.UVCParam;
import com.serenegiant.widget.AspectRatioSurfaceView;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;public class MainActivity extends AppCompatActivity {private static final boolean DEBUG = true;private static final String TAG = MainActivity.class.getSimpleName();private static final int DEFAULT_WIDTH = 640;private static final int DEFAULT_HEIGHT = 480;private TextView mCameraNameLeft,mCameraNameRight, mCameraNameCenter;private UsbDevice mUsbDeviceLeft,mUsbDeviceRight, mUsbDeviceCenter;private ICameraHelper mCameraHelperLeft, mCameraHelperRight, mCameraHelperCenter;private AspectRatioSurfaceView svCameraViewLeft, svCameraViewRight, svCameraViewCenter;private ConcurrentLinkedQueue<UsbDevice> mReadyUsbDeviceList = new ConcurrentLinkedQueue<>();private ConditionVariable mReadyDeviceConditionVariable = new ConditionVariable();private final Object mSync = new Object();private HandlerThread mHandlerThread;private Handler mAsyncHandler;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//setTitle(R.string.entry_multi_camera_new);if (DEBUG) Log.d(TAG, "huahua--onCreate:");mCameraNameLeft = findViewById(R.id.textViewLeft);mCameraNameRight = findViewById(R.id.textViewRight);mCameraNameCenter = findViewById(R.id.textViewCenter);List<String> needPermissions = new ArrayList<>();needPermissions.add(Manifest.permission.CAMERA);XXPermissions.with(this).permission(needPermissions).request((permissions, all) -> {if (!all) {return;}initViews();mHandlerThread = new HandlerThread(TAG);mHandlerThread.start();mAsyncHandler = new Handler(mHandlerThread.getLooper());initCameraHelper();});}@Overrideprotected void onDestroy() {super.onDestroy();mHandlerThread.quitSafely();mAsyncHandler.removeCallbacksAndMessages(null);}private void initViews() {setCameraViewLeft();setCameraViewRight();setCameraViewCenter();}private void setCameraViewLeft() {svCameraViewLeft = findViewById(R.id.svCameraViewLeft);svCameraViewLeft.setAspectRatio(DEFAULT_WIDTH, DEFAULT_HEIGHT);svCameraViewLeft.getHolder().addCallback(new SurfaceHolder.Callback() {@Overridepublic void surfaceCreated(@NonNull SurfaceHolder holder) {if (mCameraHelperLeft != null) {mCameraHelperLeft.addSurface(holder.getSurface(), false);}}@Overridepublic void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(@NonNull SurfaceHolder holder) {if (mCameraHelperLeft != null) {mCameraHelperLeft.removeSurface(holder.getSurface());}}});}private void setCameraViewRight() {svCameraViewRight = findViewById(R.id.svCameraViewRight);svCameraViewRight.setAspectRatio(DEFAULT_WIDTH, DEFAULT_HEIGHT);svCameraViewRight.getHolder().addCallback(new SurfaceHolder.Callback() {@Overridepublic void surfaceCreated(@NonNull SurfaceHolder holder) {if (mCameraHelperRight != null) {mCameraHelperRight.addSurface(holder.getSurface(), false);}}@Overridepublic void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(@NonNull SurfaceHolder holder) {if (mCameraHelperRight != null) {mCameraHelperRight.removeSurface(holder.getSurface());}}});}private void setCameraViewCenter() {svCameraViewCenter = findViewById(R.id.svCameraViewCenter);svCameraViewCenter.setAspectRatio(DEFAULT_WIDTH, DEFAULT_HEIGHT);svCameraViewCenter.getHolder().addCallback(new SurfaceHolder.Callback() {@Overridepublic void surfaceCreated(@NonNull SurfaceHolder holder) {if (mCameraHelperCenter != null) {mCameraHelperCenter.addSurface(holder.getSurface(), false);}}@Overridepublic void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(@NonNull SurfaceHolder holder) {if (mCameraHelperCenter != null) {mCameraHelperCenter.removeSurface(holder.getSurface());}}});}@Overrideprotected void onStart() {if (DEBUG) Log.d(TAG, "huahua--onStart:");super.onStart();}@Overrideprotected void onStop() {if (DEBUG) Log.d(TAG, "onStop:");super.onStop();clearCameraHelper();}public void initCameraHelper() {if (DEBUG) Log.d(TAG, "initCameraHelper:");if (mCameraHelperLeft == null) {mCameraHelperLeft = new CameraHelper();mCameraHelperLeft.setStateCallback(mStateListenerLeft);}if (mCameraHelperRight == null) {mCameraHelperRight = new CameraHelper();mCameraHelperRight.setStateCallback(mStateListenerRight);}if (mCameraHelperCenter == null) {mCameraHelperCenter = new CameraHelper();mCameraHelperCenter.setStateCallback(mStateListenerCenter);}}private void clearCameraHelper() {if (DEBUG) Log.d(TAG, "clearCameraHelper:");if (mCameraHelperLeft != null) {mCameraHelperLeft.release();mCameraHelperLeft = null;}if (mCameraHelperRight != null) {mCameraHelperRight.release();mCameraHelperRight = null;}if (mCameraHelperCenter != null) {mCameraHelperCenter.release();mCameraHelperCenter = null;}}private void selectDeviceLeft(final UsbDevice device) {if (DEBUG) Log.v(TAG, "selectDeviceLeft:device=" + device.getDeviceName());mUsbDeviceLeft = device;mAsyncHandler.post(() -> {waitCanSelectDevice(device);if (mCameraHelperLeft != null) {mCameraHelperLeft.selectDevice(device);}});}private void selectDeviceRight(final UsbDevice device) {if (DEBUG) Log.v(TAG, "selectDeviceRight:device=" + device.getDeviceName());mUsbDeviceRight = device;mAsyncHandler.post(() -> {waitCanSelectDevice(device);if (mCameraHelperRight != null) {mCameraHelperRight.selectDevice(device);}});}private void selectDeviceCenter(final UsbDevice device) {if (DEBUG) Log.v(TAG, "selectDeviceCenter:device=" + device.getDeviceName());mUsbDeviceCenter = device;mAsyncHandler.post(() -> {waitCanSelectDevice(device);if (mCameraHelperCenter != null) {mCameraHelperCenter.selectDevice(device);}});}/*** wait for only one camera need request permission** @param device*/private void waitCanSelectDevice(UsbDevice device) {mReadyUsbDeviceList.add(device);while (mReadyUsbDeviceList.size() > 1) {mReadyDeviceConditionVariable.block();mReadyDeviceConditionVariable.close();}}/*** remove ready camera that wait for select** @param device*/private void removeSelectedDevice(UsbDevice device) {mReadyUsbDeviceList.remove(device);mReadyDeviceConditionVariable.open();}private final ICameraHelper.StateCallback mStateListenerLeft = new ICameraHelper.StateCallback() {private final String LOG_PREFIX = "ListenerLeft#";@Overridepublic void onAttach(UsbDevice device) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onAttach:");synchronized (mSync) {if (mUsbDeviceLeft == null && !device.equals(mUsbDeviceRight) && !device.equals(mUsbDeviceCenter)) {selectDeviceLeft(device);mCameraNameLeft.setText("左相机("+device.getDeviceName()+")");}}}@Overridepublic void onDeviceOpen(UsbDevice device, boolean isFirstOpen) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onDeviceOpen:");if (mCameraHelperLeft != null && device.equals(mUsbDeviceLeft)) {UVCParam param = new UVCParam();param.setQuirks(UVCCamera.UVC_QUIRK_FIX_BANDWIDTH);mCameraHelperLeft.openCamera(param);}removeSelectedDevice(device);}@Overridepublic void onCameraOpen(UsbDevice device) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onCameraOpen:");if (mCameraHelperLeft != null && device.equals(mUsbDeviceLeft)) {mCameraHelperLeft.startPreview();Size size = mCameraHelperLeft.getPreviewSize();if (size != null) {int width = size.width;int height = size.height;//auto aspect ratiosvCameraViewLeft.setAspectRatio(width, height);}mCameraHelperLeft.addSurface(svCameraViewLeft.getHolder().getSurface(), false);}}@Overridepublic void onCameraClose(UsbDevice device) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onCameraClose:");if (device.equals(mUsbDeviceLeft)) {if (mCameraHelperLeft != null) {mCameraHelperLeft.removeSurface(svCameraViewLeft.getHolder().getSurface());}}}@Overridepublic void onDeviceClose(UsbDevice device) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onDeviceClose:");}@Overridepublic void onDetach(UsbDevice device) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onDetach:");if (device.equals(mUsbDeviceLeft)) {mUsbDeviceLeft = null;}removeSelectedDevice(device);}@Overridepublic void onCancel(UsbDevice device) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onCancel:");if (device.equals(mUsbDeviceLeft)) {mUsbDeviceLeft = null;}removeSelectedDevice(device);}};private final ICameraHelper.StateCallback mStateListenerRight = new ICameraHelper.StateCallback() {private final String LOG_PREFIX = "ListenerRight#";@Overridepublic void onAttach(UsbDevice device) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onAttach:");synchronized (mSync) {if (mUsbDeviceRight == null && !device.equals(mUsbDeviceLeft) && !device.equals(mUsbDeviceCenter)) {selectDeviceRight(device);mCameraNameRight.setText("右相机("+device.getDeviceName()+")");}}}@Overridepublic void onDeviceOpen(UsbDevice device, boolean isFirstOpen) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onDeviceOpen:");if (mCameraHelperRight != null && device.equals(mUsbDeviceRight)) {UVCParam param = new UVCParam();param.setQuirks(UVCCamera.UVC_QUIRK_FIX_BANDWIDTH);mCameraHelperRight.openCamera(param);}removeSelectedDevice(device);}@Overridepublic void onCameraOpen(UsbDevice device) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onCameraOpen:");if (mCameraHelperRight != null && device.equals(mUsbDeviceRight)) {mCameraHelperRight.startPreview();Size size = mCameraHelperRight.getPreviewSize();if (size != null) {int width = size.width;int height = size.height;//auto aspect ratiosvCameraViewRight.setAspectRatio(width, height);}mCameraHelperRight.addSurface(svCameraViewRight.getHolder().getSurface(), false);}}@Overridepublic void onCameraClose(UsbDevice device) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onCameraClose:");if (device.equals(mUsbDeviceRight)) {if (mCameraHelperRight != null) {mCameraHelperRight.removeSurface(svCameraViewRight.getHolder().getSurface());}}}@Overridepublic void onDeviceClose(UsbDevice device) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onDeviceClose:");}@Overridepublic void onDetach(UsbDevice device) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onDetach:");if (device.equals(mUsbDeviceRight)) {mUsbDeviceRight = null;}removeSelectedDevice(device);}@Overridepublic void onCancel(UsbDevice device) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onCancel:");if (device.equals(mUsbDeviceRight)) {mUsbDeviceRight = null;}removeSelectedDevice(device);}};private final ICameraHelper.StateCallback mStateListenerCenter = new ICameraHelper.StateCallback() {private final String LOG_PREFIX = "ListenerCenter#";@Overridepublic void onAttach(UsbDevice device) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onAttach:");synchronized (mSync) {if (mUsbDeviceCenter == null && !device.equals(mUsbDeviceRight) && !device.equals(mUsbDeviceLeft)) {selectDeviceCenter(device);mCameraNameCenter.setText("中间相机("+device.getDeviceName()+")");}}}@Overridepublic void onDeviceOpen(UsbDevice device, boolean isFirstOpen) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onDeviceOpen:");if (mCameraHelperCenter != null && device.equals(mUsbDeviceCenter)) {UVCParam param = new UVCParam();param.setQuirks(UVCCamera.UVC_QUIRK_FIX_BANDWIDTH);mCameraHelperCenter.openCamera(param);}removeSelectedDevice(device);}@Overridepublic void onCameraOpen(UsbDevice device) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onCameraOpen:");if (mCameraHelperCenter != null && device.equals(mUsbDeviceCenter)) {mCameraHelperCenter.startPreview();Size size = mCameraHelperCenter.getPreviewSize();if (size != null) {int width = size.width;int height = size.height;//auto aspect ratiosvCameraViewCenter.setAspectRatio(width, height);}mCameraHelperCenter.addSurface(svCameraViewCenter.getHolder().getSurface(), false);}}@Overridepublic void onCameraClose(UsbDevice device) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onCameraClose:");if (device.equals(mUsbDeviceCenter)) {if (mCameraHelperCenter != null) {mCameraHelperCenter.removeSurface(svCameraViewCenter.getHolder().getSurface());}}}@Overridepublic void onDeviceClose(UsbDevice device) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onDeviceClose:");}@Overridepublic void onDetach(UsbDevice device) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onDetach:");if (device.equals(mUsbDeviceCenter)) {mUsbDeviceCenter = null;}removeSelectedDevice(device);}@Overridepublic void onCancel(UsbDevice device) {if (DEBUG) Log.v(TAG, LOG_PREFIX + "onCancel:");if (device.equals(mUsbDeviceCenter)) {mUsbDeviceCenter = null;}removeSelectedDevice(device);}};}
对于较为标准的android系统调用,可以使用更加简洁的CameraX调用,博文链接:
Android相机调用-CameraX【外接摄像头】【USB摄像头】_安卓调取摄像头设备的方式-CSDN博客