UVC相机设计实践
# UVC相机设计和实践
# 目录介绍
- 01.Camera基础理论
- 1.1 Camera工作原理
- 1.2 相机系统介绍
- 1.3 什么是相机模组
- 1.4 图像处理器ISP
- 1.5 常见双摄方案
- 02.UVC基础概念介绍
- 2.1 UVC概念须知
- 2.2 Android UVC标准
- 2.3 UVCCamera相机功能
- 2.4 UVCCamera项目介绍
- 2.5 UVCCamera库介绍
- 03.UVC库设计架构思想
- 3.1 UVC整体架构设计
- 3.2 UVC中USB连接设计
- 3.3 UVC协议解析设计
- 3.4 UVC图像数据采集设计
- 3.5 UVC图像渲染设计
- 04.UVCCamera实践
- 4.1 UVC最简单实践
- 4.2 USB驱动连接和断开
- 4.3 UVC相机视图渲染
- 4.4 UVC相机采集数据
- 4.5 UVC相机数据处理
- 4.6 改变相机预览参数
- 4.7 开启和改变灯光
- 4.8 如何编译jni为so库
- 05.UVCCamera原理分析
- 5.1 USB连接流程分析
- 5.2 打开UVC相机流程
- 5.3 开始渲染View
- 5.4 相机预览流程分析
- 5.5 UVC相机如何采集
- 5.6 图片编码和解码
- 5.7 高效高频传数据
- 06.遇到的疑难杂症
- 6.1 开启灯光遇到异常
# 01.Camera基础理论
# 1.1 Camera工作原理
- Camera工作原理
- 光线---->镜头Lens---->IR Filter过滤红外光---->sensor将光信号转化为电信号---->ADC转为数字信号---->DSP处理---->RGB/YUV格数输出
- 镜头:
- 常用的镜头结构有1P、2P、1G1P、1G2P、。。5G等,透镜越多,成本越高,相对成像效果越好。
- 红外滤光片IR Filter
- 人眼无法看到红外光,但是sensor能感受到,所以需过滤掉红外光,使得图像更接近人眼所看到的效果。
- 其他一些相机基础原理
- https://blog.csdn.net/qq_40405527/article/details/108131655
# 1.2 相机系统介绍
- 相机系统可以分为两个部分
- 一个是相机模组,一个是图像处理器ISP,相机模组是用来进行进行光电转换的,而图像处理器是用于处理图像的。
# 1.3 什么是相机模组
- 相机模组往往做得十分精致小巧
- 里面主要包括了镜头、对焦马达、滤光片以及感光器(Sensor)。
- 镜头主要存在以下几个参数
- 视场角FOV,该参数表明了通过镜头可以成像多大范围的场景,一般FOV越大就越能看到大范围的景物,但是有可能会带来严重的畸变,通常使用后期的畸变矫正算法来修正大FOV所带来的畸变。
- 焦距F ,规定所有平行于透镜主轴的光线汇聚到的那点叫做焦点,而焦点到透镜中心的距离便是这里的焦距,一般焦距越大,镜头的FOV也就越小。而越短的焦距,往往FOV越大。
- 光圈值f,通过镜头焦距与实际光圈的直径比值来指定,该值越小,说明进光量也就越大,手机镜头一般采用f/2.0的固定光圈。
# 1.4 图像处理器ISP
- ISP(Image Signal Processor)是一种专门用于图像处理的芯片或模块。
- 它通常嵌入在数字相机、智能手机、摄像头和其他图像设备中,用于处理从图像传感器捕获的原始图像数据。
- ISP的主要功能是对原始图像数据进行各种处理和优化,以提高图像质量和增强图像特性。
- ISP在图像设备中起着关键作用,它能够实时处理图像数据,并根据设备和应用的需求进行优化和增强。通过ISP的处理,用户可以获得更好的图像质量和更多的图像处理选项。
# 1.5 常见双摄方案
- 双摄技术如何理解
- 采用了两个摄像头模组分别成像,并通过特定的算法处理,融合成一张图像,达到特定成像需求的目的。
- 普遍地,现在双摄方案主要用于实现背景虚化、提升暗光/夜景条件下成像质量、光学变焦。
- 什么场景会用到双摄
- 待定
# 02.UVC基础概念介绍
# 2.1 UVC概念须知
- UVC全称为USB Video Class,直接翻译过来的意思就是:USB视频类,它是一种专门为USB视频捕获设备定义的协议标准。
- 这个标准是Microsoft与另外几家设备厂商联合推出的为USB视频捕获设备定义的协议标准,已经成为USB org标准之一。
- 它定义了一组规范和命令,使得视频设备(如摄像头)可以以通用的方式与主机设备进行交互。
- 现在的主流操作系统,都已提供UVC设备驱动,因此符合UVC规格的硬件设备在不需要安装任何的驱动程序下即可在主机中正常使用。是的,目前Android系统已经支持uvc设备。
- UVC协议的一些关键特点
- 标准化接口:UVC定义了一组标准化的接口和命令,使得视频设备可以与主机设备进行通信,而无需特定的驱动程序。这使得UVC设备可以在不同的操作系统和平台上通用使用。
- 视频格式支持:UVC支持多种视频格式,包括常见的MJPEG(Motion JPEG)和YUV(YCbCr)格式。这使得不同类型的摄像头可以通过UVC协议进行通信,并提供兼容性。
- 分辨率和帧率:UVC支持不同的视频分辨率和帧率设置。这使得用户可以根据需要选择适当的设置,以满足特定的应用需求。
- 控制命令:UVC协议还定义了一组控制命令,用于控制和配置视频设备的各种参数,如亮度、对比度、饱和度等。这使得用户可以通过UVC协议与设备进行交互,并进行必要的调整和配置。
- 插拔支持:UVC设备可以在运行时进行插拔,而无需重新启动或重新配置。这使得用户可以方便地连接和断开UVC设备,而不会中断正在进行的视频通信。
# 2.2 Android UVC标准
- Android UVC(USB Video Class)是指Android设备通过USB接口支持外部摄像头的标准。
- UVC是一种通用的视频设备类别,它允许摄像头以一种标准化的方式与计算机和移动设备进行通信。
- 通过UVC,Android设备可以通过USB连接外部摄像头,并使用相应的应用程序进行视频捕捉、录制和实时传输等操作。
- 这为用户提供了更多的摄像头选择,以满足不同的需求和应用场景。
- 使用UVC外部摄像头的步骤如下:
- 确保外部摄像头支持UVC标准,并具有与Android设备兼容的接口(通常是USB)。
- 将外部摄像头通过USB连接到Android设备。
- 在Android设备上安装支持UVC的应用程序,如视频通话应用、摄像头应用或其他需要使用外部摄像头的应用。
- 通过应用程序,选择并且打开外部摄像头作为视频输入设备。
- 根据应用程序的功能和需求,进行视频捕捉、录制、实时传输或其他操作。
# 2.3 UVCCamera相机功能
- 为什么Android要使用UVCCamera?
- 在Android手机上 如果我们使用 USBCamera设备 就得需要OTG功能,大部分手机的OTG功能都被厂商屏蔽掉了,如果想用就得ROOT设备,这个不现实。
- 而开源项目UVCCamera,实现了手机无需root就支持USBCamera设备的检测、连接、预览和音视频数据采集等功能。
- UVC相机的功能介绍
- (1) 支持USB Camera设备检测,画面实时预览;
- (2) 支持抓拍jpg格式图片,可设置图片压缩质量;
- (3) 支持录制mp4格式视频,可屏蔽音频,可设置视频和音频的录制参数;
- (4) 支持获取camera支持的分辨率,和分辨率切换;
- (5) 支持预览自动识别各种相机的分辨率;
- (6) 支持旋转摄像头90度、180度、270度;
- (7) 支持调整对比度、亮度、色调、饱和度、白平衡等等一些相机控制参数;
- (8) 支持多预览和多摄像头;
# 2.4 UVCCamera项目介绍
- 项目介绍
- https://github.com/saki4510t/UVCCamera
- 1)USBCameraTest0
- 显示如何使用SurfaceView来启动/停止预览。
- 2)USBCameraTest
- 显示如何启动/停止预览。这与USBCameraTest0几乎相同,
- 但是使用自定义的TextureView来显示相机图像而不是使用SurfaceView。
- 3)USBCameraTest2
- 演示如何使用MediaCodec编码器将UVC相机(无音频)的视频记录为.MP4文件。
- 此示例需要API>=18,因为MediaMuxer仅支持API>=18。
- 4)USBCameraTest3
- 演示如何将音频(来自内部麦克风)的视频(来自UVC相机)录制为.MP4文件。
- 这也显示了几种捕捉静止图像的方式。此示例可能最适用于您的定制应用程序的基础项目。
- 5)USBCameraTest4
- 显示了访问UVC相机并将视频图像保存到后台服务的方式。
- 这是最复杂的示例之一,因为这需要使用AIDL的IPC。
- 6)USBCameraTest5
- 和USBCameraTest3几乎相同,但使用IFrameCallback接口保存视频图像,
- 而不是使用来自MediaCodec编码器的输入Surface。
- 在大多数情况下,您不应使用IFrameCallback来保存图像,因为IFrameCallback比使用Surface要慢很多。
- 但是,如果您想获取视频帧数据并自行处理它们或将它们作为字节缓冲区传递给其他外部库,则IFrameCallback将非常有用。
- 7)USBCameraTest6
- 这显示了如何将视频图像分割为多个Surface。你可以在这个应用程序中看到视频图像并排观看。
- 这个例子还展示了如何使用EGL来渲染图像。
- 如果您想在添加视觉效果/滤镜效果后显示视频图像,则此示例可能会对您有所帮助。
- 8)USBCameraTest7
- 这显示了如何使用两个摄像头并显示来自每个摄像头的视频图像。这仍然是实验性的,可能有一些问题。
- 9)usbCameraTest8
- 这显示了如何设置/获取uvc控件。目前这只支持亮度和对比度。
- 工程主要依赖库有
- libuvccamera和usbCameraCommon
- libuvccamera库介绍
- 其中libuvccamera库是底层库,包括c++实现部分,外加几个基本控制类,如USBMonitor和UVCCamera类等。
- USBMonitor类负责管理usb设备类,从该类可以选出自己需要的usb camera设备。
- UVCCamera则表示该类就是一个usb的摄像头,基本方法均调用native,通过jni方式直接调用c++实现。该类是一个桥接类,连接上层控制到底层实现的基本事件转发。比如聚焦,亮度,缩放,白平衡,预览,打开,关闭等,均实际通过该类来实现。
- CameraDialog则是以对话框的形式提供对USBMonitor的调用,如发现并找寻自己需要操作的设备,里面用到了DeviceFilter类,主要用来过滤设备,如果不过滤设备,则认为是所有usb设备,主要还是通过USBManager来获取设备,然后进行筛选。
- DeviceFilter类,这个是实际的筛选类,主要依据xml中的filter进行筛选,也就是说可以在xml中定义自己想要的设备的class,或者说不想要的设备的class,然后用xmlparser进行解析该xml文件,之后用此filter就可以过滤出实际想要的deviceList。
- usbCameraCommon库介绍
- usbCameraCommon库是上层封装库,主要用来操作调用摄像头,也就是USBMonitor和UVCCamera等。
- AbstractUVCCameraHandler类,该类基本提供了对于UVCCamera的调用,然后同时开放api给Activity使用。是一个Handler类,也就是说它主要负责消息发送接收,任何调用它的方法它都会转发给内部类,也就是其实内部主要维护的是一个Thread类,叫CameraThread。
- CameraThread类为static类。任何发送给AbstractUVCCameraHandler的方法都会以消息的方式发送给CameraThread进行处理,当没有消息的时候,就什么也不做。CameraThread类内部持有UVCCamera类实例,所有调用CameraThread的方法又会发送给UVCCamera实例进行处理
# 2.5 UVCCamera库介绍
- 关于核心so库文件介绍
- libjpeg-turbo1500.so,这个是jpeg图像编码,解码和转码的库
- libusb100.so,usb通信库
- libuvc.so,建立在libusb库上的跨平台的usb视频设备库
- libUVCCamera.so,UVCCamera库
# 03.UVC库设计架构思想
# 3.1 UVC整体架构设计
- UVCCamera是一个用于Android平台的开源库,用于支持通过USB连接的UVC(USB Video Class)摄像头。下面是UVCCamera的整体架构设计:
- USB连接管理:UVCCamera库通过Android的USB API与USB设备进行通信。它负责检测和管理USB设备的连接和断开,并处理USB设备的插拔事件。
- UVC协议解析:UVCCamera库实现了UVC协议的解析和处理。它能够与UVC摄像头进行通信,并解析UVC协议中的命令和数据,以实现对摄像头的控制和数据传输。
- 视频数据采集:UVCCamera库通过UVC协议从摄像头中获取原始的视频数据。它使用Android的Camera2 API或Camera API来采集视频数据,并将其传递给后续的处理模块。
- 图像处理和渲染:UVCCamera库提供了一系列图像处理和渲染功能,用于对采集到的视频数据进行处理和显示。这包括白平衡校正、曝光控制、色彩校正、锐化、降噪、图像稳定等功能。
- 预览和录制:UVCCamera库支持实时预览和视频录制功能。它能够将处理后的视频数据显示在Android设备的屏幕上,并提供录制功能,将视频数据保存为文件。
- 回调和事件处理:UVCCamera库通过回调机制提供了与应用程序交互的接口。它可以通知应用程序有关连接状态、摄像头参数变化、图像数据可用等事件,并提供相应的回调方法供应用程序处理。
- 用户界面和控制:UVCCamera库可以与应用程序的用户界面进行交互,以提供用户控制和设置摄像头参数的功能。它可以显示预览视图、提供拍照和录制按钮等用户界面元素。
# 3.2 UVC中USB连接设计
- 在UVCCamera库中,USB连接管理是通过Android的USB API实现的。UVCCamera库使用以下步骤来管理USB连接:
- 初始化USB功能:在应用程序启动时,UVCCamera库会初始化USB功能,并获取USB设备的权限。这是通过请求USB权限和注册USB连接广播接收器来完成的。
- 检测USB设备连接:UVCCamera库会监听USB连接广播,以检测USB设备的连接和断开事件。当USB设备连接时,库会获取USB设备的信息,如设备ID、接口等。
- 打开和关闭USB设备:一旦检测到USB设备连接,UVCCamera库会尝试打开USB设备,并与之建立通信。这是通过Android的UsbManager和UsbDeviceConnection来实现的。
- UVC协议通信:一旦USB设备打开成功,UVCCamera库会使用UVC协议与USB设备进行通信。它会发送UVC命令和请求,以控制摄像头的参数和功能。
- 轮训监听USB设备断开:UVCCamera库会持续监听USB连接广播,以检测USB设备的断开事件。一旦USB设备断开,库会关闭设备连接,并释放相关资源。
# 3.3 UVC协议解析设计
- 在UVCCamera库中,UVC协议解析是通过与UVC摄像头进行通信来实现的。以下是UVCCamera库中UVC协议解析的一般步骤:
- 打开UVC摄像头:在UVCCamera库中,首先需要打开UVC摄像头设备。这是通过与USB设备建立连接,并获取与UVC摄像头通信的接口。
- 发送和接收UVC命令:一旦UVC摄像头打开成功,UVCCamera库可以通过发送UVC命令与摄像头进行通信。这些命令可以用于控制摄像头的各种参数和功能,如曝光、白平衡、对焦等。
- 解析UVC命令响应:UVCCamera库会等待UVC摄像头对发送的命令进行响应。一旦收到响应,库会解析响应数据,以获取摄像头的当前状态和参数设置。
- 获取和处理视频数据:除了控制命令,UVCCamera库还可以通过UVC协议获取摄像头传输的视频数据。它会解析视频数据的格式和帧率,并进行相应的处理和渲染,以显示或保存视频。
- 关闭UVC摄像头:当不再需要与UVC摄像头通信时,UVCCamera库会关闭摄像头连接,并释放相关资源。
# 3.4 UVC图像数据采集设计
- 在UVCCamera库中,图像数据采集是通过使用UVC协议与UVC摄像头进行通信来实现的。以下是图像数据采集的一般步骤:
- 打开UVC摄像头:首先,UVCCamera库会打开UVC摄像头设备,建立与摄像头的连接。这是通过与USB设备进行通信,并获取与UVC摄像头通信的接口来实现的。
- 发送UVC命令:一旦UVC摄像头打开成功,UVCCamera库可以通过发送UVC命令与摄像头进行通信。这些命令可以用于控制摄像头的各种参数和功能,如曝光、白平衡、对焦等。
- 获取图像数据:通过UVC协议,UVCCamera库可以获取UVC摄像头传输的图像数据。这些数据可以是原始的YUV或RGB格式,也可以是经过压缩的数据。库会接收和缓存图像数据,以便后续的处理和显示。
- 图像数据处理:一旦图像数据被获取,UVCCamera库可以对其进行处理。这包括解析图像数据的格式、调整图像的亮度、对比度、饱和度等,以及应用其他图像处理算法。
- 图像数据显示或保存:处理后的图像数据可以通过UVCCamera库进行显示或保存。库可以将图像数据渲染到Android设备的屏幕上,或将其保存为图像文件。
- 关闭UVC摄像头:当不再需要与UVC摄像头通信时,UVCCamera库会关闭摄像头连接,并释放相关资源。
# 3.5 UVC图像渲染设计
- 在UVCCamera库中,图像渲染是通过使用Android的图像渲染API来实现的。以下是UVCCamera库中图像渲染的一般步骤:
- 获取图像数据:UVCCamera库会从UVC摄像头获取图像数据,这可以是原始的YUV或RGB格式,也可以是经过压缩的数据。
- 创建渲染表面:UVCCamera库会创建一个用于图像渲染的表面。这是一个SurfaceView的Surface。
- 图像数据转换:如果图像数据的格式与渲染表面的格式不匹配,UVCCamera库会进行图像数据的格式转换。这可以包括颜色空间转换、像素格式转换等。
- 图像数据传输:一旦图像数据准备就绪,UVCCamera库会将图像数据传输到渲染表面。这可以通过SurfaceView的绘制等方式来实现。
- 图像渲染:一旦图像数据传输完成,UVCCamera库会使用图像渲染API将图像数据渲染到Android设备的屏幕上。这可以通过SurfaceView的绘制等方式来实现。
# 04.UVCCamera介绍
# 4.1 UVC最简单实践
- 关于UVC最简单的调用如下琐事
CameraConfig config = new CameraConfig.Builder() .bindDisplayView(textureView) .setCameraId(cameraRgbId) .setDisplayOrientation(270) .setPreviewSize(mDefaultPreviewWidth, mDefaultPreviewHeight) .setDataCallback(mRgbCallBack) .build(); rgbCamera = new AndroidCameraUVC(config); rgbCamera.init(activity);1
2
3
4
5
6
7
8
9
# 4.2 USB驱动连接和断开
# 在什么时候注册usb监听,什么时候取消
- 通过广播自动监听usb连接和断开
- 1
- 通过OnDeviceConnectListener监听事件,即可监听到usb连接和断开事件
mUSBMonitor = new USBMonitor(this, new OnDeviceConnectListener() { @Override public void onConnect(UsbDevice device, UsbControlBlock ctrlBlock, boolean createNew) { Log.e(TAG, "OnDeviceConnectListener onConnect " + device.getDeviceName() + " , " + device.getDeviceId()); } @Override public void onDisconnect(UsbDevice device, UsbControlBlock ctrlBlock) { Log.e(TAG, "OnDeviceConnectListener onDisconnect " + device.getDeviceName() + " , " + device.getDeviceId()); } });1
2
3
4
5
6
7
8
9
10
11
# 4.3 UVC相机视图渲染
- 视频渲染这块,UVC相机选择的是:TextureView。当USB连接成功后,即开始进行视频渲染逻辑操作
- 在Android UI中对于纹理的封装就是SurfaceView或者TextureView,而在UVCCamera中就是UVCCameraTextureView,它继承自android.view.TextureView。
mUSBMonitor = new USBMonitor(this, new OnDeviceConnectListener() { @Override public void onConnect(UsbDevice device, UsbControlBlock ctrlBlock, boolean createNew) { Log.e(TAG, "OnDeviceConnectListener onConnect " + device.getDeviceName() + " , " + device.getDeviceId()); openCamera(ctrlBlock); new Thread(() -> { // 休眠50ms,等待Camera创建完毕 UVCCameraLogger.logWrite("onConnect 休眠50ms,等待Camera创建完毕"); try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } // 开启预览 UVCCameraLogger.logWrite("onConnect 开启预览"); startPreview(mCamView); }).start(); } @Override public void onDisconnect(UsbDevice device, UsbControlBlock ctrlBlock) { Log.e(TAG, "OnDeviceConnectListener onDisconnect " + device.getDeviceName() + " , " + device.getDeviceId()); } });1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 4.4 UVC相机采集数据
- 如何监听相机采集的图片数据
private final ICamera.CameraCallBack mRgbCallBack = (data, config) -> { //处理相机回调数据 };1
2
3 - 那么这个是怎么监听相机采集frame数据呢
// 具体看:AbstractUVCCameraHandler private final IFrameCallback mIFrameCallback = new IFrameCallback() { @Override public void onFrame(final ByteBuffer frame) { //这个是相机数据的回调 //UVCLogger.logWrite("CameraThread IFrameCallback onFrame " + frame); int len = frame.capacity(); //创建yuv数据 final byte[] yuv = new byte[len]; frame.get(yuv); if (CameraThread.this.mOnPreViewResultListener != null) { CameraThread.this.mOnPreViewResultListener.onPreviewResult(yuv); } } };1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 4.5 UVC相机数据处理
# 4.6 改变相机预览参数
- 改变摄像机预览参数(包括帧格式、宽度、高度、FPS)
//停止相机预览 mCameraHelper.stopPreview(); //设置摄像机预览参数 mCameraHelper.setPreviewSize(size); //开始相机预览 mCameraHelper.startPreview(); //需要自适应摄像头分辨率的话,设置新的宽高比 mCameraViewMain.setAspectRatio(mPreviewWidth, mPreviewHeight);1
2
3
4
5
6
7
8
# 4.7 开启和改变灯光
- 这块针对AndroidCameraUVC封装一个api控制灯光
- 大概调用api在UVCCamera类中的,nativeSetBrightness方法则是控制灯光的api,范围在0-255之间。
- 遇到调用api调用后没有反应,网上查阅资源,对c++代码改造和放开判断条件
- https://cloud.tencent.com/developer/article/2344014
- 还是没有反应,该问题待排查中
# 4.8 如何编译jni为so库
- 现在 so 库的编译,已经非常的方便了,我们在 as 的 Terminal 终端界面,切到 jni 目录下,直接ndk-build,就可以生成我们需要的 so 库文件了。
- 把 Application.mk 这个文件的位置圈出来了。如果是 32 位,这里边 APP_ABI 的内容修改为 armeabi-v7a即可,64 位则是 arm64-v8a,其它平台的类推。
# 05.UVCCamera原理分析
# 5.1 USB连接流程分析
- USB驱动连接和断开大概的实现思路是:
- 第一步:通过注册广播,监听usb连接attach和detach操作。attach操作,当设备连接时调用,然后开始申请权限
- 第二步:通过UsbManager类中的requestPermission申请权限,这一步很主要,要不然后面肯定是读取不到数据的。
- 第三步:当获取到权限后,广播会收到回调,然后开始打开专用USB设备
- 当我们启动相机的时候,第一件要做的事情就是要连接上摄像头,依然是usb摄像头,那么自然我们会需要尝试建立usb连接。而连接usb设备要做的第一件事就是获取权限:
//USBMonitor类中 @Override public synchronized boolean requestPermission(final UsbDevice device) { boolean hasPermission = UsbManagerHelper.getInstance().getUsbManager().hasPermission(device); if (hasPermission) { processConnect(device); } else { try { //如果没有权限,需要先申请权限,这一步很主要,要不然后面肯定是读取不到串口的数据的。 UsbManagerHelper.getInstance().getUsbManager().requestPermission(device, mPermissionIntent); } catch (final Exception e) { processCancel(device); result = true; } } return result; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 - 在获取到权限之后继而调用了processConnect方法来尝试建立usb连接:
- 在USBMonitor#processConnect(),可以看到在第一次建立连接的时候会新建一个UsbControlBlock,这个类主要是用来管理USBMonitor、UsbDevice以及诸如vendorId等参数。
- 在UsbControlBlock#(构造函数),在它的构造函数里会调用USBMonitor中mUsbManager的openDevice方法来创建连接。
# 5.2 打开UVC相机流程
- 继续回到processConnect方法,在usb连接建立之后,会调用USBMonitor中的监听接口:mOnDeviceConnectListener
- 这个接口是从外部创建USBMonitor时候实现的,而在该接口的onConnect方法里我们就可以拿到usb连接建立成功的回调,继续往下看
- 接着我们看一下onConnect回调中做了什么事情
- 在该回调里就可以调用UVCCameraHandler的open方法来准备真正启动相机。UVCCameraHandler是一个Handler,在其内部是通过Android的消息机制来管理整个相机的生命周期。
@Override public void onConnect(final UsbDevice device, UsbControlBlock ctrlBlock, boolean createNew) { mCtrlBlock = ctrlBlock; openCamera(ctrlBlock); new Thread(() -> { // 休眠50ms,等待Camera创建完毕 UVCCameraLogger.logWrite("onConnect 休眠50ms,等待Camera创建完毕"); try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } // 开启预览 UVCCameraLogger.logWrite("onConnect 开启预览"); startPreview(mCamView); }).start(); if (listener != null) { listener.onConnectDev(device, true); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 在该回调里就可以调用UVCCameraHandler的open方法来准备真正启动相机。UVCCameraHandler是一个Handler,在其内部是通过Android的消息机制来管理整个相机的生命周期。
- 然后开始分析打开相机的操作流程,最终会通过Java调用到native开启相机
- UVCCameraHandler#openCamera(),UVCCameraHandler是一个Handler,在其内部是通过Android的消息机制来管理整个相机的生命周期。
- AbstractUVCCameraHandler#open(),调用open方法的时候,其实是发送了一个message
- CameraThread#handleOpen(),在handleMessage中会调用创建UVCCameraHandler时候同时创建的CameraThread的handleOpen方法。
public void handleOpen(final UsbControlBlock ctrlBlock) { UVCLogger.logWrite("CameraThread handleOpen 打开相机"); handleClose(); try { final UVCCamera camera = new UVCCamera(); camera.open(ctrlBlock); synchronized (mSync) { mUVCCamera = camera; } //通知外部开启相机 callOnOpen(); } catch (final Exception e) { callOnError(e); UVCLogger.logWrite("CameraThread handleOpen 异常 " + e.getMessage()); } UVCLogger.logWrite("CameraThread handleOpen supportedSize:" + (mUVCCamera != null ? mUVCCamera.getSupportedSize() : null)); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 - UVCCamera#open(),可以看到UVCCamera的open方法中调用了nativeConnect、nativeGetSupportedSize、nativeSetPreviewSize 这三个native的方法来真正启动相机。
- CameraThread#callOnOpen(),相机启动之后会继续回到CameraThread的handleOpen方法,在该方法中又调用了callOnOpen来通知外部相机开启继而完成整个相机的启动过程。
# 5.3 开始渲染View
- 具体定位到OnDeviceConnectListener回调onConnect方法中。这里面打开相机后,就开始渲染view
mUSBMonitor = new USBMonitor(this, new OnDeviceConnectListener() { @Override public void onConnect(UsbDevice device, UsbControlBlock ctrlBlock, boolean createNew) { //开始渲染view操作 startPreview(mCamView); } });1
2
3
4
5
6
7 - 在开启相机之后会接着调用startPreview方法来将采集到的数据绑定到一块surface上
protected void startPreview(final Object surface) { checkReleased(); if (!((surface instanceof SurfaceHolder) || (surface instanceof Surface) || (surface instanceof SurfaceTexture))) { throw new IllegalArgumentException("surface should be one of SurfaceHolder, Surface or SurfaceTexture"); } sendMessage(obtainMessage(MSG_PREVIEW_START, surface)); }1
2
3
4
5
6
7 - 往下分析可以知道,关于设置渲染熟悉,开始渲染,最终都通过jni调用到c++中实现
# 5.4 相机预览流程分析
- 首先看一段代码,如下所示:
private CameraViewInterface.Callback mCallback = new CameraViewInterface.Callback() { @Override public void onSurfaceCreated(CameraViewInterface cameraViewInterface, Surface surface) { if (!isPreview && mCameraHelper.isCameraOpened()) { mCameraHelper.startPreview(mPreviewView); isPreview = true; } } };1
2
3
4
5
6
7
8
9 - 开始预览的事件传递
- ---->Activity,在这个里面创建UVC相机然后初始化,这个时候直接调用AndroidCameraUVC,具体看该类中初始化方法
- ---->AndroidCameraUVC#init,然后在接着看onSurfaceCreated方法回调的处理逻辑
- ---->UVCCameraHelper#startPreview,在画面创建后,开始准备加载View渲染,接着往下看
- ---->UVCCameraHandler#startPreview,然后接着调用super,看父类代码
- ---->AbstractUVCCameraHandler#startPreview,发送一个handler消息,开始准备相机渲染
- ---->AbstractUVCCameraHandler.CameraThread#handleStartPreview,发送给UVCCameraHandler的事件都经过消息发送给了CameraThread
- ---->UVCCamera#startPreview(),这个最终调用native方法,最后调用nativeStartPreview方法开始渲染
- 然后看看native层调用次序
- native层由java类UVCCamera发起调用,调用传递为jni-->UVCCamera-->UVCCamera.cpp--->UVCPreview.cpp
- 关于相机初始化到预览的时序图
# 5.5 UVC相机如何拍照
# 5.6 图片编码和解码
# 5.7 高效高频传数据
# 06.遇到的疑难杂症
# 6.1 开启灯光遇到异常
- 调用开启灯光的api遇到异常,异常原因如下所示:
时间:2024-07-17 13:56:39.986-线程id:20533-tag:PalmSdk |: -打印消息:PSensor-Vendor关闭休眠模式,打开相机; 时间:2024-07-17 13:56:39.986-线程id:20533-tag:PalmSdk |: -打印消息:PSensor-Vendor调用开启相机 onResumeCamera 1; 时间:2024-07-17 13:56:39.995-线程id:20533-tag:PalmSdk |: -打印消息:UVCCamera :: 开始注册usb连接,然后恢复视图渲染 com.serenegiant.widget.UVCCameraTextureView{7e0fa1e V.ED..... ........ 0,-90-400,90 #7f080125 app:id/id_rgbView}; 时间:2024-07-17 13:56:40.004-线程id:20533-tag:PalmSdk |: -打印消息:UVCCamera :: 开始注册usb连接,然后恢复视图渲染 com.serenegiant.widget.UVCCameraTextureView{894b8cc V.ED..... ........ 400,-90-800,90 #7f080123 app:id/id_irView}; 时间:2024-07-17 13:56:40.004-线程id:20533-tag:PalmSdk |: -打印消息:PSensor-Vendor调用开启相机 onResumeCamera 2; 时间:2024-07-17 13:56:40.005-线程id:20533-tag:PalmSdk |: -打印消息:PSensor 距离感应器,有物体靠近呢; 时间:2024-07-17 13:56:40.005-线程id:20533-tag:PalmSdk |: -打印消息:UVCCamera :: setBrightness Exception java.lang.IllegalStateException; 时间:2024-07-17 13:56:40.005-线程id:20533-tag:PalmSdk |: -打印消息:UVCCamera :: setBrightness Exception java.lang.IllegalStateException; 时间:2024-07-17 13:56:40.005-线程id:20533-tag:PalmSdk |: -打印消息:BrightCallBack打开灯光,3; 时间:2024-07-17 13:56:40.999-线程id:20588-tag:PalmSdk |: -打印消息:UVCCamera :: requestPermission 申请相机权限 2 相机索引 0; 时间:2024-07-17 13:56:41.004-线程id:20592-tag:PalmSdk |: -打印消息:UVCCamera :: requestPermission 申请相机权限 2 相机索引 1; 时间:2024-07-17 13:56:41.023-线程id:20588-tag:PalmSdk |: -打印消息:UVCCamera :: ConnectListener uvc相机 Connect camera id 0, isConnected = true; 时间:2024-07-17 13:56:41.027-线程id:20592-tag:PalmSdk |: -打印消息:UVCCamera :: ConnectListener uvc相机 Connect camera id 1, isConnected = true; 时间:2024-07-17 13:56:41.029-线程id:20591-tag:PalmSdk |: -打印消息:UVCCamera :: open 打开并开始连接相机; 时间:2024-07-17 13:56:41.032-线程id:20595-tag:PalmSdk |: -打印消息:UVCCamera :: open 打开并开始连接相机;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 - 遇到该异常分析:
- 开启灯光和调用相机开启几乎是同一时刻。但这里有一点要注意:必须等到相机连接成功后,才可以调用灯光设置api成功。相机从初始化到预览大概过程花费至少50毫秒!
- 相机开启流程:【初始化->申请权限->attach->open相机->connect连接->preview预览->frame回调】
- 解决办法如下:
- 调用灯光api时,判断相机是否已经预览成功,如果未成功则延迟50毫秒再设置灯光,只有这样灯光才可以OK
# 参考博客
- 关于uvc相机灯光的技术博客
- V4L2 driver -整体架构:https://www.cnblogs.com/linhaostudy/p/9486511.html
- Camera的基础理论和工作原理
- https://blog.csdn.net/qq_40405527/article/details/108131655
- Android Camera开发系列(干货满满)
- https://zhuanlan.zhihu.com/p/465126122
- Uvc Usb Camera 调节亮度无效问题,搞定
- https://cloud.tencent.com/developer/article/2344014
- UvcCamera介绍
- https://blog.csdn.net/qq_27489007/article/details/131326292
- 基于UVC封装的库
- https://github.com/jiangdongguo/AndroidUSBCamera
- Android中多USB摄像头解决方案
- https://www.jianshu.com/p/f7f548c2c0e7
- Camera的基础理论和工作原理
- https://blog.csdn.net/qq_40405527/article/details/108131655
- Android Camera开发系列(干货满满)
- https://zhuanlan.zhihu.com/p/465126122
- Uvc Usb Camera 调节亮度无效问题,搞定
- https://cloud.tencent.com/developer/article/2344014
- https://blog.csdn.net/andrexpert/article/details/78324181
上次更新: 2026/06/10, 11:13:41
