有的自定义系统,对于自己外接的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
<?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">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/textViewLeft"
android:text="左相机"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<com.serenegiant.widget.AspectRatioSurfaceView
android:id="@+id/svCameraViewLeft"
android:layout_gravity="center"
android:layout_width="440dp"
android:layout_height="220dp"
/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:orientation="vertical">
<TextView
android:id="@+id/textViewRight"
android:text="右相机"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<com.serenegiant.widget.AspectRatioSurfaceView
android:id="@+id/svCameraViewRight"
android:layout_gravity="center"
android:layout_width="440dp"
android:layout_height="220dp"
/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:orientation="vertical">
<TextView
android:id="@+id/textViewCenter"
android:text="中间相机"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<com.serenegiant.widget.AspectRatioSurfaceView
android:id="@+id/svCameraViewCenter"
android:layout_gravity="center"
android:layout_width="440dp"
android:layout_height="220dp"
/>
</LinearLayout>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
7,页面源码
java
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;
@Override
protected 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();
});
}
@Override
protected 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() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
if (mCameraHelperLeft != null) {
mCameraHelperLeft.addSurface(holder.getSurface(), false);
}
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
}
@Override
public 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() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
if (mCameraHelperRight != null) {
mCameraHelperRight.addSurface(holder.getSurface(), false);
}
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
}
@Override
public 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() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
if (mCameraHelperCenter != null) {
mCameraHelperCenter.addSurface(holder.getSurface(), false);
}
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
if (mCameraHelperCenter != null) {
mCameraHelperCenter.removeSurface(holder.getSurface());
}
}
});
}
@Override
protected void onStart() {
if (DEBUG) Log.d(TAG, "huahua--onStart:");
super.onStart();
}
@Override
protected 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#";
@Override
public 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()+")");
}
}
}
@Override
public 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);
}
@Override
public 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 ratio
svCameraViewLeft.setAspectRatio(width, height);
}
mCameraHelperLeft.addSurface(svCameraViewLeft.getHolder().getSurface(), false);
}
}
@Override
public void onCameraClose(UsbDevice device) {
if (DEBUG) Log.v(TAG, LOG_PREFIX + "onCameraClose:");
if (device.equals(mUsbDeviceLeft)) {
if (mCameraHelperLeft != null) {
mCameraHelperLeft.removeSurface(svCameraViewLeft.getHolder().getSurface());
}
}
}
@Override
public void onDeviceClose(UsbDevice device) {
if (DEBUG) Log.v(TAG, LOG_PREFIX + "onDeviceClose:");
}
@Override
public void onDetach(UsbDevice device) {
if (DEBUG) Log.v(TAG, LOG_PREFIX + "onDetach:");
if (device.equals(mUsbDeviceLeft)) {
mUsbDeviceLeft = null;
}
removeSelectedDevice(device);
}
@Override
public 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#";
@Override
public 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()+")");
}
}
}
@Override
public 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);
}
@Override
public 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 ratio
svCameraViewRight.setAspectRatio(width, height);
}
mCameraHelperRight.addSurface(svCameraViewRight.getHolder().getSurface(), false);
}
}
@Override
public void onCameraClose(UsbDevice device) {
if (DEBUG) Log.v(TAG, LOG_PREFIX + "onCameraClose:");
if (device.equals(mUsbDeviceRight)) {
if (mCameraHelperRight != null) {
mCameraHelperRight.removeSurface(svCameraViewRight.getHolder().getSurface());
}
}
}
@Override
public void onDeviceClose(UsbDevice device) {
if (DEBUG) Log.v(TAG, LOG_PREFIX + "onDeviceClose:");
}
@Override
public void onDetach(UsbDevice device) {
if (DEBUG) Log.v(TAG, LOG_PREFIX + "onDetach:");
if (device.equals(mUsbDeviceRight)) {
mUsbDeviceRight = null;
}
removeSelectedDevice(device);
}
@Override
public 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#";
@Override
public 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()+")");
}
}
}
@Override
public 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);
}
@Override
public 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 ratio
svCameraViewCenter.setAspectRatio(width, height);
}
mCameraHelperCenter.addSurface(svCameraViewCenter.getHolder().getSurface(), false);
}
}
@Override
public void onCameraClose(UsbDevice device) {
if (DEBUG) Log.v(TAG, LOG_PREFIX + "onCameraClose:");
if (device.equals(mUsbDeviceCenter)) {
if (mCameraHelperCenter != null) {
mCameraHelperCenter.removeSurface(svCameraViewCenter.getHolder().getSurface());
}
}
}
@Override
public void onDeviceClose(UsbDevice device) {
if (DEBUG) Log.v(TAG, LOG_PREFIX + "onDeviceClose:");
}
@Override
public void onDetach(UsbDevice device) {
if (DEBUG) Log.v(TAG, LOG_PREFIX + "onDetach:");
if (device.equals(mUsbDeviceCenter)) {
mUsbDeviceCenter = null;
}
removeSelectedDevice(device);
}
@Override
public void onCancel(UsbDevice device) {
if (DEBUG) Log.v(TAG, LOG_PREFIX + "onCancel:");
if (device.equals(mUsbDeviceCenter)) {
mUsbDeviceCenter = null;
}
removeSelectedDevice(device);
}
};
}
对于较为标准的android系统调用,可以使用更加简洁的CameraX调用,博文链接: