Camera基础摄像头驱动原理和开发&&V4L2子系统驱动架构

Posted 邢仕冲的一亩三分地

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Camera基础摄像头驱动原理和开发&&V4L2子系统驱动架构相关的知识,希望对你有一定的参考价值。

摄像头驱动原理和开发&&V4L2子系统驱动架构

一、导入

二、摄像头数据输出格式

1、USB、VS、MIPI摄像头架构

  • MIPI接口摄像头支持的分辨率、帧率更高,功率低。
  • MIPI比USB成本更低,USB摄像头昂贵。
  • USB摄像头开发工作量少,一般都是免驱的。

2、RAW RGB DATA
RAW RGB是sensor的bayer阵列获取的数据(每种传感器获得对应的颜色亮度),摄像头sensor经过光电转换后输出的数据就是RAW data(RAW RGB)。在Sensor中,每一个感光点只能感光RGB其中的一种颜色。所以,通常所说的30万像素或130万像素等,指的是有30万或130万个感光点,每一个感光点只能感光一种颜色。
RGB:传统的红绿蓝格式。比如RGB565,其16bit数据格式为5bitR+6bitG+5BitB,G多一位,原因是人眼对绿色比较敏感。

3、RAW RGB与RGB的区别

  • RAW RGB每个像素只有一种颜色(R、G、B中的一种)
  • RGB每个像素都有三种颜色,每一个值在0-255之间。
  • 由sensor输出的数据RAW DATA(RAW RGB),经过ISP的彩色插值转换就变成了RGB。

4、YUV
Y表示亮度分量(Luma):如果只显示Y的话,图像看起来会是一张黑白照。
U(Cb)表示色度分量:是照片蓝色部分去掉亮度(Y)。
V(Cr)表示色度分量:是照片红色部分去掉亮度(Y)。
①YUV的优点:
与RGB相比,他的优点在于占用更少的空间
②YUV的采样格式
主流的采样方式有三种,YUV4:4:4,YUV4:2:2,YUV4:2:0
图像质量:YUV4:4:4>YUV4:2:2>YUV4:2:0

YUV4:4:4采样,每一个Y对应一组UV分量。意思是4个像素里面的数据有4个Y,4个U,4个V。
YUV4:2:2采样,每一个Y对应一组UV分量。意思是4个像素里面的数据有4个Y,2个U,2个V。
YUV4:2:0采样,每一个Y对应一组UV分量。意思是4个像素里面的数据有4个Y,1个U,1个V。
③YUV的存储格式
YUV的存储格式分为打包格式(packet formats)和平面格式(planar formats)。

  • 对于planar的YUV格式,先连续存储所有像素点的Y,随后存储U、V。存储类型表示为采样方式后缀加P,如YUV422P、YUV420P。
  • 对于packet的YUV格式,每个像素点的Y,U,V是连续交替存储的。存储类型表示为采样方式后缀加SP,如YUV422SP、YUV420SP。

    一帧YUV420图像中,u和v分量占用的空间大小分别是原来像素数的1/4,占用的总大小就是widthheigth(3/2)
    一帧YUV422图像中,u和v分量占用的空间大小分别是原来像素数的一半,占用的总大小就是widthheigth2
    5、MJPEG/H264编码格式
    有些摄像头sensor支持输出MJPEG/H264编码格式的,是因为其内置了DSP处理器,将YUV数据编码后输出MJPEG/H264码流。
    问题:什么情况下需要用到编码?有什么作用?
  • 互联网传输,减少带宽。
  • 视频编码兼容,统一视频编码标准。

三、视频编码原理


1、视频编码的发展历程

2、视频编码原理
I帧:关键帧,采用帧内压缩技术。
P帧:向前参考帧,在压缩时,只参考前面处理过的帧,采用帧间压缩技术。
B帧:双向参考帧,在压缩时,它既参考前面的帧,又参考它后面的帧,采用帧间压缩技术。
除了I/P/B帧外,还有图像序列GOP。
GOP:两个I帧之间是一个图像序列,在一个图像序列中只有一个帧。在码率不变的前提下,GOP值越大,P、B帧的数量会越多,平均每个I、P、B帧所占用的字节数就越多,也就更容易获取较好的图像质量。如图:

四、V4L2子系统驱动架构

V4L2:是linux内核中关于视频设备的内核驱动框架,为上层访问底层的视频设备提供了统一的接口。

V4L2驱动核心:主要是构建一个内核中标准视频设备驱动的框架,为视频操作提供统一的接口函数。
平台V4L2设备驱动:在V4L2框架下,根据平台自身的特性实现与平台相关的V4L2驱动部分,包括注册video_device和v4l2_dev
具体的sensor驱动:主要上电、提供工作时钟、视频图像裁剪、流IO开启等,实现各种设备控制方法供上层调用并注册v4l2_subdev。
UVC:是一种usb视频设备驱动。用来支持usb视频设备,凡是usb接口的摄像头都能够支持。

V4L2的核心源码位于drivers/media/v4l2-core,根据功能可以划分为四类:

  • 字符设备模块:由v4l2-dev.c实现,主要作用申请字符主设备号、注册class和提供video device注册、注销等相关函数。
  • v4l2基础框架L:由v4l2-device.c、v3l2-subdev.c、v3l2-fh.c、v4l2-ctrls.c等文件构建v4l2基础框架。
  • videobuf管理:由videobuf2-core.c、videobuf2-dma-contig.c、videobuf2-dma-sg.c、videobuf2-memops.c、videobuf2-vmalloc.c、v3l2-mem2mem.c等文件实现,完成videobuffer的分配、管理和注销。
  • loctl框架:由v4l2-ioctl.c文件实现,构建v4l2 ioctl的框架。

V4L2注册流程:

videobuf管理:

V4L2 ioctl框架
用户空间对V4L2设备的操作基本都是ioctl来实现的,V4L2设备都有大量可操作的功能(配置寄存器),所以V4L2的ioctl也是十分庞大的。

V4L2驱动主要使用的ioctl命令值如下:

五、摄像头视频采集流程

摄像头数据流:

六、常见典型问题

1、摄像头画面卡顿

  • 帧率不足
  • usb带宽不足
  • 视频采集格式设置出错:YUV、MJPEG或H264格式
  • 编解码问题
  • CPU占用高

2、摄像头画面旋转90度或180度问题

3、摄像头画面打不开问题

如何让视频有camera

参考技术A 音视频开发(四):Camera视频采集 原创
2022-04-07 14:16:11

音视频开发老舅

码龄1年

关注
目录
Camera基础知识

视频采集的流程

遇到的问题和常见的坑(重点)

一、 Camera基础知识
Camera 有几个重要的基础概念。

facing相机的方向,一般后置摄像头和前置摄像头。

Orientation:相机采集图片的角度,摄像头的传感器在手机中是横向的,在预览的时候根据Camera的预览方向进行顺时针旋转对应角度进行设置即可正常预览。如果不正确设置会导致预览时出现倒立、镜像等问题。把预览的图片保存为相册也要单独设置方向,注意这个方向和预览方向互不相干。

预览图片的大小 预览容器的大小和摄像头支持的图片预览的图片大小,如果设置了Camera不支持的预览大小,会导致黑屏。

可以设置帧回调然后,在每一帧中进行业务处理,比如,人脸识别等功能

Camera预览的图片格式有NV21 YUV420sp等

Camera需要一个容器把的Surface显示在屏幕上,一般SurfaceView,TextureView等。

二、视频采集的流程
通过SurfaceView拿到SurfaceHolder,然后设置addCallback回调,当Surface创建、销毁、改变时触发对应的回调,在其中可以进行Camera的初始化以及参数设置。音视频开发知识点导图:https://docs.qq.com/doc/DQm1VTHBlQmdmTlN2

通过new Camera(cameralId)生成一个对象。然后Camera.getParams获取到相关的参数,可以把重要的或者说比较关系的parmas打印出来,比如说支持多少个摄像头、支持的预览图片的大小、每个摄像头的方向等信息。可以根据需要设置对应的参数,比如图片的格式、图片的预览大小等。当然有一个必须要设置的就是Camera的预览展示方向,否则预览的到图片和正常的方向不一致。

可以设置Camera的setPreviewCallback获取每一帧的回调,根据需要设置处理,开始预览startPreview以及帧回调的处理

摄像头的切换

如果出发Camera的切换,需要把前一个Camera释放,重新生成和设置Camera切换预览
和Activity生命周期的关系,这个是由SurfaceView决定的,当页面可见时(onResume)创建或重新创建,页面不可见时(onPause)销毁释放

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

具体实现如下

SurfaceView的设置

private SurfaceHolder mSurfaceHolder;

private void initSurfaceView()
mSurfaceHolder = surfaceview.getHolder();
mSurfaceHolder.addCallback(new SurfaceHolder.Callback()
@Override
public void surfaceCreated(SurfaceHolder holder)
Log.d(TAG, "surfaceCreated: ");
handleSurfaceCreated();


@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)


@Override
public void surfaceDestroyed(SurfaceHolder holder)
Log.d(TAG, "surfaceDestroyed: ");
handleSurfaceDestroyed();

);


private void handleSurfaceDestroyed()
releaseCamera();
mSurfaceHolder = null;
Log.i(TAG, "handleSurfaceDestroyed: ");


private void handleSurfaceCreated()
Log.i(TAG, "handleSurfaceCreated: start");
if (mSurfaceHolder == null)
mSurfaceHolder = surfaceview.getHolder();

if (mCamera == null)
initCamera(curCameraId);

try
//问题2:页面重新打开后SurfaceView的内容黑屏
//Camera is being used after Camera.release() was called
//在surfaceDestroyed时调用了Camera的release 但是没有设置为null,
//--》如何解耦合,把生命周期相关的方法和Camera的生命周期绑定而不时在回调中处理,方便业务实现
//onResume--》surfaceCreated
//onPause--》surfaceDestroyed
mCamera.setPreviewDisplay(mSurfaceHolder);
catch (IOException e)
e.printStackTrace();
Log.e(TAG, "handleSurfaceCreated: " + e.getMessage());

startPreview();
Log.i(TAG, "handleSurfaceCreated: end");


private void startPreview()
// mCamera.setPreviewCallback(new Camera.PreviewCallback()
// @Override
// public void onPreviewFrame(byte[] data, Camera camera)
// Log.i(TAG, "onPreviewFrame: setPreviewCallback");
//
// );
//问题:很多时候,不仅仅要预览,在预览视频的时候,希望能做一些检测,比如人脸检测等。这就需要获得预览帧视频,该如何做呐?
mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback()
@Override
public void onPreviewFrame(byte[] data, Camera camera)
Log.i(TAG, "onPreviewFrame: setOneShotPreviewCallback");
Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, previewSize.width, previewSize.height, null);
ByteArrayOutputStream os = new ByteArrayOutputStream(data.length);
if(!yuvImage.compressToJpeg(new Rect(0,0,previewSize.width,previewSize.height),100,os))
Log.e(TAG, "onPreviewFrame: compressToJpeg error" );
return;

byte[] bytes = os.toByteArray();
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

//这里的处理方式是简单的把预览的一帧图保存下。如果需要做人脸别或者其他操作,可以拿到这个bitmap进行分析处理
//我们可以通过找出这张图片发现预览保存的图片的方向是不对的,还是Camera的原始方向,需要旋转一定角度才可以。
if(curCameraId == Camera.CameraInfo.CAMERA_FACING_BACK)
bitmap = BitmapUtils.rotate(bitmap,90);
else
bitmap = BitmapUtils.mirror(BitmapUtils.rotate(bitmap,270));

FileUtils.saveBitmapToFile(bitmap,"oneShot.jpg");

);

// mCamera.setPreviewCallbackWithBuffer(new Camera.PreviewCallback()
// @Override
// public void onPreviewFrame(byte[] data, Camera camera)
// Log.i(TAG, "onPreviewFrame: setPreviewCallbackWithBuffer");
//
// );

以上是关于Camera基础摄像头驱动原理和开发&&V4L2子系统驱动架构的主要内容,如果未能解决你的问题,请参考以下文章

Camera | 1.Camera基础知识

Camera | 1.Camera基础知识

Camera知识体系架构之驱动&应用&调试学习笔记-以瑞芯微RV1109为例

如何让视频有camera

camera驱动

高通平台开发系列讲解(摄像头篇)QCM6490 上摄像头驱动开发