【安卓相关】安卓外置USB相机开发 ( UVC )

2021-01-16  本文已影响0人  norkm

近期在做物联网设备的开发,上位机是安卓一体机,有需要连接多个外置USB相机的录像和拍照的需要,记录一下,希望可以帮到有缘人。

首先你的安卓设备需要支持UVC协议 ( 一种为USB视频捕获设备定义的协议标准 )

如果需要使用多个摄像头的话,我这边的方案是使用 HUB 将多个USB摄像头连接起来,也就是这个。


连接多个摄像头后,如何区分各个摄像头呢,每一个USB device 有一个pid或者vid 参数,也就是产品id和版本id 吧。

android.hardware.usb.UsbDevice
如图所示:

image.png

这个PID可以找摄像头厂家定制,或者找厂家给你烧录工具和烧录文件,自己去烧录,这样使用的时候按照PID进行安装。

现在开始部署吧

首先我使用的是这个库
https://github.com/jiangdongguo/AndroidUSBCamera

build.gradle

allprojects {
        repositories {
            ...
            maven { url 'http://raw.github.com/saki4510t/libcommon/master/repository/' }
            maven { url 'https://jitpack.io' }
        }
    }

saki4510t 构建的时候 如果出现 这个错误的话

Could not resolve com.serenegiant:common:2.12.4

可以改成 liuchaoya 这个试试

maven { url 'https://gitee.com/liuchaoya/libcommon/raw/master/repository/' }



接下来是 dependencies

implementation 'com.github.jiangdongguo:AndroidUSBCamera:2.3.1'



使用

我这里写一个基于该库,使用uvc相机来录制视频的demo吧。

布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".UVCCameraActivity">
    <com.serenegiant.usb.widget.UVCCameraTextureView
        android:id="@+id/textureView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:layout_gravity="center"/>
    <EditText
        android:id="@+id/cameraNumber"
        android:text="1"
        android:inputType="number"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <Button
        android:onClick="StartAndStop"
        android:text="录制 / 停止"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

Activity


import android.hardware.usb.UsbDevice;
import android.os.Bundle;
import android.os.Environment;
import android.os.Looper;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Surface;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import com.fenfeneco.recycle.util.AndroidDeviceSDK;
import com.fenfeneco.recycle.util.UvcUtil;
import com.jiangdg.usbcamera.UVCCameraHelper;
import com.jiangdg.usbcamera.utils.FileUtils;
import com.serenegiant.usb.common.AbstractUVCCameraHandler;
import com.serenegiant.usb.encoder.RecordParams;
import com.serenegiant.usb.widget.CameraViewInterface;
import com.serenegiant.usb.widget.UVCCameraTextureView;

import java.io.File;

public class UVCCameraActivity extends AppCompatActivity{
    public static boolean isRequest;
    public static boolean isPreview;
    private UVCCameraHelper mCameraHelper;
    private UVCCameraTextureView uvcCameraTextureView;
    private final static String TAG = "UVC相机";
    private EditText cameraNumber;

    int nowCamera = 4;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_uvc_camera);
        AndroidDeviceSDK.hideStatus(this,false);


        cameraNumber = findViewById(R.id.cameraNumber);
        cameraNumber.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                if(!TextUtils.isEmpty(s)){
                    int number = Integer.parseInt(s.toString());

                    //  切换摄像头
                    mCameraHelper.closeCamera();
                    requestCamera(number);
                }
            }
        });


        uvcCameraTextureView = findViewById(R.id.textureView);
        CameraViewInterface mUVCCameraView = (CameraViewInterface) uvcCameraTextureView;
        //  设置 UVC 预览界面回调
        mUVCCameraView.setCallback(new CameraViewInterface.Callback() {
            @Override
            public void onSurfaceCreated(CameraViewInterface view, Surface surface) {
                Log.i(TAG,"画面创建");
            }

            @Override
            public void onSurfaceChanged(CameraViewInterface view, Surface surface, int width, int height) {
                Log.i(TAG,"画面改变");
            }

            @Override
            public void onSurfaceDestroy(CameraViewInterface view, Surface surface) {
                Log.i(TAG,"画面销毁");
            }
        });
        mCameraHelper = UVCCameraHelper.getInstance();
        mCameraHelper.setDefaultPreviewSize(640,480);
        // 设置默认帧格式,默认格式为 UVCCameraHelper.Frame_FORMAT_MPEG
        // 如果 Frame_FORMAT_MPEG 模式不能使用,可以尝试 YUYV
        // mCameraHelper.setDefaultFrameFormat(UVCCameraHelper.FRAME_FORMAT_YUYV);
        mCameraHelper.initUSBMonitor(this, mUVCCameraView, new UVCCameraHelper.OnMyDevConnectListener() {
            @Override
            public void onAttachDev(UsbDevice device) {
                // 插拔的时候会触发
                Log.i(TAG,"插拔扫描到的设备 (ID) :" + device.getProductId());
            }

            @Override
            public void onDettachDev(UsbDevice device) {
                // 关闭相机
                if (isRequest) {
                    isRequest = false;
                    mCameraHelper.closeCamera();

                    //  USB 相机被拔出
                    Log.i(TAG,"被拔出");
                }
            }

            @Override
            public void onConnectDev(UsbDevice device, boolean isConnected) {

                Log.i(TAG,"连接成功,结果:" + isConnected);

                if (!isConnected) {
                    //  连接失败,检测连接参数
                    isPreview = false;
                } else {
                    isPreview = true;
                    //  连接成功
                    // 等待 UVC 初始化成功

                }
            }

            @Override
            public void onDisConnectDev(UsbDevice device) {
                //  设备被断开
                Log.i(TAG,"设备断开");
            }
        });

        mCameraHelper.registerUSB();

        //  寻找第一个可用的USB相机
        requestCamera(nowCamera);
    }

    private void requestCamera(int index){
        //  寻找第一个可用的USB相机
        for(int i = 0 ; i < mCameraHelper.getUsbDeviceList().size();i++){
            Log.i(TAG,"扫描到摄像头" + mCameraHelper.getUsbDeviceList().get(i).getProductId());

            if(mCameraHelper.getUsbDeviceList().get(i).getProductId() == UvcUtil.doorNumberToPid(index)){
                    Log.i(TAG,"找到了第一个相机");

                    mCameraHelper.requestPermission(i);

                    return;
            }
        }
    }



    //  开始或停止
    public void StartAndStop(View view){
        //  如果没有开始录制
        if (!mCameraHelper.isPushing()) {

            String videoPath = Environment.getExternalStorageDirectory() + File.separator + System.currentTimeMillis() + UVCCameraHelper.SUFFIX_MP4;
            
            // 录制的一些参数
            RecordParams params = new RecordParams();
            params.setRecordPath(videoPath);
            params.setRecordDuration(0);
            params.setVoiceClose(true);

            //  开始录制
            mCameraHelper.startPusher(params, new AbstractUVCCameraHandler.OnEncodeResultListener() {
                @Override
                public void onEncodeResult(byte[] data, int offset, int length, long timestamp, int type) {
                    // type = 1,h264 video stream
                    //  视频类型
                    if (type == 1) {
                        FileUtils.putFileStream(data, offset, length);
                    }
                    
                    // type = 0,aac audio stream
                    //  音乐类型
                    if(type == 0) {
                        //  FileUtils.createfile(FileUtils.ROOT_PATH + "test666.h264");
                    }
                }

                @Override
                public void onRecordResult(String videoPath) {
                    if(TextUtils.isEmpty(videoPath)) {
                        return;
                    }
                    Toast.makeText(UVCCameraActivity.this,"录制结束,视频地址" + videoPath,Toast.LENGTH_LONG ).show();
                }
            });
            // 如果只想推送视频流
            // mCameraHelper.startPusher(listener);
            Toast.makeText(this, "开始录制", Toast.LENGTH_SHORT).show();
        } else {
            FileUtils.releaseFile();
            mCameraHelper.stopPusher();
            Toast.makeText(this, "停止录制", Toast.LENGTH_SHORT).show();
        }
    }

}

缺少 UvcUtil 这个类大家不用管,只是根据我自身业务写的一个序号和pid转换类。

上一篇下一篇

猜你喜欢

热点阅读