Android相机调用-libusbCamera【外接摄像头】【USB摄像头】 【多摄像头预览】

有的自定义系统,对于自己外接的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调用,博文链接:

Android相机调用-CameraX【外接摄像头】【USB摄像头】_安卓调取摄像头设备的方式-CSDN博客

相关推荐
顾林海3 分钟前
Flutter 图片组件全面解析:从基础加载到高级应用
android·前端·flutter
molong9314 分钟前
Android开发鸿蒙环境问题记录
android·华为·harmonyos
顾林海5 分钟前
深度解析LinkedHashSet工作原理
android·java·面试
好的佩奇1 小时前
Dart 之任务
android·flutter·dart
zhangphil2 小时前
Android ViewStub显示VISIBLE与消失GONE,Kotlin
android·kotlin
API小爬虫3 小时前
利用 PHP 爬虫获取京东商品详情 API 返回值说明及代码示例
android·爬虫·php
范纹杉想快点毕业8 小时前
双相机结合halcon的条码检测
数码相机·idea
_一条咸鱼_10 小时前
深入剖析 Android Hilt 框架的依赖生命周期管理模块(六)
android·kotlin·android jetpack
_一条咸鱼_10 小时前
深入剖析 Android Hilt 的模块配置与初始化模块(五)
android·kotlin·android jetpack
斗锋在干嘛12 小时前
Android里面开子线程的方法
android