Hi3559AV100外接UVC/MJPEG相机实时采图设计(二):V4L2接口的实现(以YUV422为例)

  下面将给出Hi3559AV100外接UVC/MJPEG相机实时采图设计的整体流程,主要实现是通过V4L2接口将UVC/MJPEG相机采集的数据送入至MPP平台,经过VDEC、VPSS、VO最后通过HDMI的输出,给出(二)V4L2接口的实现。

可以先参照前面随笔介绍(一)Linux USB摄像头驱动加载与分析:https://www.cnblogs.com/iFrank/p/14399421.html

板载平台:BOXER-8410AI

芯片型号:Hi3559AV100

相机型号:Logitch c270

开发环境:VM15.5+ubuntu16.04+Hilinux

1、V4L2接口说明

  V4L2是Video for linux2的简称,为linux中关于视频设备的内核驱动。在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video*下,如果只有一个视频设备,通常为/dev/video0。V4L2是内核提供给应用程序访问音、视频驱动的统一接口。V4L2 的相关定义包含在头文件<linux/videodev2.h> 中。

  V4L2 支持两种方式来采集图像:内存映射(mmap)和直接读取方式(read)。V4L2 在/usr/include/linux/videodev2.h 文件下定义了一些重要的数据结构,在采集图像的过程中,就是通过对这些数据的操作来获得最终的图像数据。Linux 系统 V4L2 使能可在内核编译阶段配置,默认情况下是在 make menuconfig 是打开的。应用程序可以通过 V4L2 进行视频采集。V4L2 支持内存映射(mmap)方式和直接读取方式(read)方式采集数据。前者
一般用于连续的视频数据采集,后者常用静态图片数据采集。v4l2 中不仅定义了通用 API 元素,图像的格式,输入/输出方法,还定义了Linux 内核驱动处理视频信息的一系列接口,这些接口主要有:
  视频采集接口——Video Capture interface;
  视频输出接口——Video Output Interface;
  视频覆盖/预览接口——Video Overlay Interface;
  视频输出覆盖接口——Video Output Overlay Interface;
  编解码接口——Codec Interface

IOCTL的实现V4L2的控制

  打开视频设备后,可以设置该视频设备的属性,例如裁剪、缩放等。这一步是可选的。在Linux编程中,一般使用ioctl函数来对设备的I/O通道进行管理:
extern int ioctl (int __fd, unsigned long int __request, …) __THROW;
__fd:设备的ID,例如刚才用open函数打开视频通道后返回的cameraFd;
__request:具体的命令标志符。
在进行V4L2开发中,一般会用到以下的命令标志符:
 1:分配内存
2 VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址
3 VIDIOC_QUERYCAP:查询驱动功能
4 VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式
5 VIDIOC_S_FMT:设置当前驱动的频捕获格式
6 VIDIOC_G_FMT:读取当前驱动的频捕获格式
7 VIDIOC_TRY_FMT:验证当前驱动的显示格式
8 VIDIOC_CROPCAP:查询驱动的修剪能力
9 VIDIOC_S_CROP:设置视频信号的边框
10 VIDIOC_G_CROP:读取视频信号的边框
11 VIDIOC_QBUF:把数据放回缓存队列
12 VIDIOC_DQBUF:把数据从缓存中读取出来
13 VIDIOC_STREAMON:开始视频显示函数
14 VIDIOC_STREAMOFF:结束视频显示函数
15 VIDIOC_QUERYSTD:检查当前视频设备支持的标准,例如PAL或NTSC。

这些IO调用,有些是必须的,有些是可选择的,其命令标志符在在头文件<linux/videodev2.h> 定义,具体如下:

1 #define VIDIOC_QUERYCAP         _IOR('V',  0, struct v4l2_capability)
2 #define VIDIOC_RESERVED _IO('V', 1)
3 #define VIDIOC_ENUM_FMT _IOWR('V', 2, struct v4l2_fmtdesc)
4 #define VIDIOC_G_FMT _IOWR('V', 4, struct v4l2_format)
5 #define VIDIOC_S_FMT _IOWR('V', 5, struct v4l2_format)
6 #define VIDIOC_REQBUFS _IOWR('V', 8, struct v4l2_requestbuffers)

Logitch c270支持的像素格式有两种:

1 YUYV 4:2:2 2 Motion-JPEG

2、V4L2的实现流程

一般来说V4L2 采集视频数据分为五个步骤:
  首先,打开视频设备文件,进行视频采集的参数初始化,通过 V4L2 接口设置视频图像的采集窗口、采集的点阵大小和格式;
  其次,申请若干视频采集的帧缓冲区,并将这些帧缓冲区从内核空间映射到用户空间,便于应用程序读取/处理视频数据;
  第三,将申请到的帧缓冲区在视频采集输入队列排队,并启动视频采集;
  第四,驱动开始视频数据的采集,应用程序从视频采集输出队列取出帧缓冲区,处理完后,将帧缓冲区重新放入视频采集输入队列,循环往复采      集连续的视频数据;
  第五,停止视频采集。在本次设计中,定义了几个函数实现对摄像头的配置和采集。
一帧图片采集流程:
Hi3559AV100外接UVC/MJPEG相机实时采图设计(二):V4L2接口的实现(以YUV422为例)

动态视频采集流程:

Hi3559AV100外接UVC/MJPEG相机实时采图设计(二):V4L2接口的实现(以YUV422为例) 

  图解过程已经很详细了,重新总结下。整个过程:
  首先:先启动视频采集,驱动程序开始采集一帧数据,把采集的数据放入视频采集输入队列的第一个帧缓冲区,一帧数据采集完成,也就是第
一个帧缓冲区存满一帧数据后,驱动程序将该帧缓冲区移至视频采集输出队列,等待应用程序从输出队列取出。驱动程序则继续采集下一帧数据放入第二个缓冲区,同样帧缓冲区存满下一帧数据后,被放入视频采集输出队列。
  然后:应用程序从视频采集输出队列中取出含有视频数据的帧缓冲区,处理帧缓冲区中的视频数据,如存储或压缩。
  最后:应用程序将处理完数据的帧缓冲区重新放入视频采集输入队列,这样可以循环采集。我们从摄像头中获取的视频帧数据会放入视频缓存队列中,当其他模块需要处理对应的视频帧的时候,就会占用缓存块,也就是这一块内存被占用,当处理完之后,对应的数据通过 VO/VENC/VDA 显示之后,这一缓存块就没有用了,可以回收利用。现在来看,其实海思的底层处理和 linux 的底层处理是一样的。不过海思本身使用的就是 linux 内核。应该也就是对这一块进行封装了而已吧!
  海思的公共视频缓存池按我的理解应该有两部分,一部分是视频采集输入队列,另一部分是视频采集输出队列,VI 通道是是视频采集输出队列中获取的视频帧,而中间 linux 内核的驱动程序会在视频采集输入队列中填充视频帧,变成视频输出队列。

部分代码实现:

 1     /*打开视频*/
2 if ((fd = open(FILE_VIDEO, O_RDWR)) == -1)
3 {
4 printf("Error opening V4L interface\n");
5 return (FALSE);
6 }
7
8 /*读video_capability中信息。
9 通过调用IOCTL函数和接口命令VIDIOC_QUERYCAP查询
10 摄像头的信息*/
11 if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1)
12 {
13 printf("Error opening device %s: unable to query device.\n",FILE_VIDEO);
14 return (FALSE);
15 }
16 else
17 {
18 printf("driver:\t\t%s\n",cap.driver);
19 printf("card:\t\t%s\n",cap.card);
20 printf("bus_info:\t%s\n",cap.bus_info);
21 printf("version:\t%d\n",cap.version);
22 printf("capabilities:\t%x\n",cap.capabilities);
23
24 /*其中capabilities: 4000001通过与各种宏位与,
25 可以获得物理设备的功能属性*/
26 if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE)
27 {
28 printf("Device %s: supports capture.\n",FILE_VIDEO);
29 }
30
31 if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING)
32 {
33 printf("Device %s: supports streaming.\n",FILE_VIDEO);
34 }
35 } //VIDIOC_QUERYCAP对应唯一结构体

3、V4L2测试(640×480像素一帧图片输出)

  我板载上装的是Logitch c270摄像头,从摄像头支持的图像像素输出的信息可以看出,在Hi3559板载上可以支持2种像素格式,这里选用的是V4L2_PIX_FMT_YUV422 格式。从生成image的大小可以判断出是正确的(YUV422数据大小 = 长 * 宽 * 1.5 = 640 * 480 * 2 = 614400 bytes = 600k)可以将image文件拷出来,使用pYUV 软件查看YUV图片。这里需要注意,使用pYUV 查看YUV图片的时候,需要正确设置图片格式,按我上面代码采集的数据格式,最后一帧图片输出结果如下:

Hi3559AV100外接UVC/MJPEG相机实时采图设计(二):V4L2接口的实现(以YUV422为例)

  之后随笔将推出结合MPP平台实现视频流的输出。

问题:

Hi3559AV100外接UVC/MJPEG相机实时采图设计(二):V4L2接口的实现(以YUV422为例)

  在虚拟机上,脚本运行正常,但是会卡在视频采集处,个人认为是虚拟机的缓存问题,而板载缓存比较充足,所以能够很好实现。

上一篇:【LOJ4632】[PKUSC2018]真实排名


下一篇:Activity与Service之间交互并播放歌曲的实现代码