linux下通过V4L2驱动USB摄像头
目录
文章目录
- 目录
- 前言
- `v4l2`解析
- `v4l2`介绍
- 应用程序通过`V4L2`接口采集视频数据步骤
- 相关结构体解析
- 总结
- 参考链接
前言
在移植罗技C270摄像头到6818的过程中,内核已经检测到了USB摄像头,但是直接用OpenCV的API(比如CvCapture*cvCaptureFromCAM(int index)
接口,无法打开USB摄像头,至少目前我是这么认为的。然后,网上搜索答案说是要使用V4l2
进行操作。没有别的办法!只有一边学一边试试看行不行喽!
感谢超群天晴大神的(原创)基于ZedBoard的Webcam设计(一):USB摄像头(V4L2接口)的图片采集这篇博文中提供的源码,我直接移植后,在6818是成功获取bmp图片和yuv图片。虽然在Ubuntu中打开bmp图片和yuv图片是错误的!!!
在找到超群天晴的博文后,我又找到这两篇博客和菜鸟一起学linux之V4L2摄像头应用流程和嵌入式LINUX环境下视频采集知识验证成功后,就是花时间理解,然后获取视频流了!希望获取的视频流能够流畅!
学习!分享!感谢!
v4l2
解析
v4l2
介绍
v4l2
是linux操作系统下用于采集图片、视频和音频数据的API接口,配合适当的视频采集设备和相应的驱动程序,可以实现图片、视频、音频等的采集。在远程会议、可视电话、视频监控系统和嵌入式多媒体终端中都有广泛应用。
在linux下,所有外设都被看成一种特殊的文件,称为"设备文件",可以像访问普通文件一样对设备文件进行访问。
V4L2支持两种方式来采集图像:内存映射(mmap)和直接读取方式(read)。V4L2在include/linux/video.h
文件下定义了一些重要的数据结构,在采集图像的过程中,就是通过对这些数据的操作来获得最终的图像数据。Linux系统V4L2使能可在内核编译阶段配置,默认情况下是在make menuconfig
是打开的。
linux下视频捕捉具体的linux调用参见下图:
应用程序可以通过
V4L2
进行视频采集。V4L2
支持内存映射(mmap)方式和直接读取方式(read)方式采集数据。前者一般用于连续的视频数据采集,后者常用静态图片数据采集。
v4l2
中不仅定义了通用API元素,图像的格式,输入/输出方法,还定义了Linux内核驱动处理视频信息的一系列接口,这些接口主要有:
视频采集接口——Video Capture interface 视频输出接口 ——Video Output Interface; 视频覆盖/预览接口——Video Overlay Interface; 视频输出覆盖接口—— Video Output Overlay Interface; 编解码接口——Codec Interface
应用程序通过V4L2
接口采集视频数据步骤
打开视频设备文件,通过视频采集的参数初始化,通过
V4L2
接口设置视频图像属性。申请若干视频采集的帧缓存区,并将这些帧缓冲区从内核空间映射到用户空间,便于应用程序读取/处理视频数据。
将申请到的帧缓冲区在视频采集输入队列排队,并启动视频采集。
驱动开始视频数据的采集,应用程序从视频采集输出队列中取出帧缓冲区,处理后,将帧缓冲区重新放入视频采集输入队列,循环往复采集连续的视频数据。
停止视频采集。
具体实现过程如下图:
整理下其中ioctl控制符:
VIDIOC_QUERYCAP 查询设备的属性
VIDIOC_ENUM_FMT 帧格式
VIDIOC_S_FMT 设置视频帧格式,对应struct v4l2_format
VIDIOC_G_FMT 获取视频帧格式等
VIDIOC_REQBUFS 请求/申请若干个帧缓冲区,一般为不少于3个
VIDIOC_QUERYBUF 查询帧缓冲区在内核空间的长度和偏移量
VIDIOC_QBUF 将申请到的帧缓冲区全部放入视频采集输出队列
VIDIOC_STREAMON 开始视频流数据的采集
VIDIOC_DQBUF 应用程序从视频采集输出队列中取出已含有采集数据的帧缓冲区
VIDIOC_STREAMOFF 应用程序将该帧缓冲区重新挂入输入队列
其实,图解过程已经很详细了,但是比较笨,而且图片也比较容易眼花,重新总结下。整个过程:
首先:先启动视频采集,驱动程序开始采集一帧数据,把采集的数据放入视频采集输入队列的第一个帧缓冲区,一帧数据采集完成,也就是第一个帧缓冲区存满一帧数据后,驱动程序将该帧缓冲区移至视频采集输出队列,等待应用程序从输出队列取出。驱动程序则继续采集下一帧数据放入第二个缓冲区,同样帧缓冲区存满下一帧数据后,被放入视频采集输出队列。
然后:应用程序从视频采集输出队列中取出含有视频数据的帧缓冲区,处理帧缓冲区中的视频数据,如存储或压缩。
最后:应用程序将处理完数据的帧缓冲区重新放入视频采集输入队列,这样可以循环采集。
看到这里,摘录一段Hi3519的开发文档中《系统控制》部分的介绍:
视频缓存池主要向媒体业务提供大块物理内存管理功能,负责内存的分配和回收,充分发挥内存缓存池的作用,让物理内存资源在各个媒体处理模块中合理使用。
一组大小相同、物理地址连续的缓存块组成一个视频缓存池。
视频输入通道需要使用公共视频缓存池。所有的视频输入通道都可以从公共视频缓存池中获取视频缓存块用于保存采集的图像。由于视频输入通道不提供创建爱你和销毁公共视频缓存池功能。因此,在系统初始化之前,必须为视频输入通道配置公共缓存池。根据业务的不同,公共缓存池的数量、缓存块的大小和数量不同。缓存块的生存期是指经过VPSS通道传给后续模块的情形。如果该缓存块完全没有经过VPSS通道传给其他模块,则将在VPSS模块处理后被放回公共缓存池。
所以,我们从摄像头中获取的视频帧数据会放入视频缓存队列中,当其他模块需要处理对应的视频帧的时候,就会占用缓存块,也就是这一块内存被占用,当处理完之后,对应的数据通过VO/VENC/VDA显示之后,这一缓存块就没有用了,可以回收利用。现在来看,其实海思的底层处理和linux的底层处理是一样的。不过海思本身使用的就是linux内核。应该也就是对这一块进行封装了而已吧!
从这张图可以看出,海思的公共视频缓存池按我的理解应该有两部分,一部分是视频采集输入队列,另一部分是视频采集输出队列,VI通道是是视频采集输出队列中获取的视频帧,而中间linux内核的驱动程序会在视频采集输入队列中填充视频帧,变成视频输出队列。
每一个帧缓冲区都有一个对应的状态标志变量,其中每一个比特代表一个状态:
V4L2_BUF_FLAG_UNMAPPED 0B0000
V4L2_BUF_FLAG_MAPPED 0B0001
V4L2_BUF_FLAG_ENQUEUED 0B0010
V4L2_BUF_FLAG_DONE 0B0100
缓冲区的状态转化如图:
在申请了缓冲区(VIDIOC_REQBUFS
)后标志为0000,但是这时候还没有映射到应用层,所以在Mmap之后,缓冲区的表示为0001。
应用程序(VIDIOC_DQBUF
)从视频采集输出队列中取出已含有采集数据的帧缓冲区,这时候(V4L2_BUF_FLAG_DONE|V4L2_BUF_FLAG_MAPPED
),所以缓冲区标志为0101.
VIDIOC_QBUF
将申请到的帧缓冲区全部放入视频采集输出队列:V4L2_BUF_FLAG_ENQUEUED|V4L2_BUF_FLAG_MAPPED
相关结构体解析
VIDIOC_QUERYCAP
-------->struct v4l2_capability
struct v4l2_capability {__u8 driver[16]; // 驱动模块的名字__u8 card[32]; // 设备名字__u8 bus_info[32]; // 总线信息__u32 version; // 内核版本__u32 capabilities; // 整个物理设备支持的功能__u32 device_caps; // 通过这个特定设备访问的功能__u32 reserved[3];
};
如下是我的罗技C270摄像头的通过VIDIOC_QUERYCAP
获取的设备功能
driver: uvcvideo
card: UVC Camera (046d:0825)
bus_info: usb-nxp-ehci-1.3
version: 197671
capabilities: 4000001
Device /dev/video9: supports capture.
Device /dev/video9: supports streaming.
其中capabilities: 4000001
通过与各种宏位与,可以获得物理设备的功能属性。比如:
//#define V4L2_CAP_VIDEO_CAPTURE 0x00000001 /* Is a video capture device */ // 是否支持视频捕获if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE) {printf("Device %s: supports capture.\n", FILE_VIDEO);}//#define V4L2_CAP_STREAMING 0x04000000 /* streaming I/O ioctls */ // 是否支持输入输出流控制if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING) {printf("Device %s: supports streaming.\n", FILE_VIDEO);}
VIDIOC_ENUM_FMT
-------->struct v4l2_fmtdesc
/** F O R M A T E N U M E R A T I O N*/
struct v4l2_fmtdesc {__u32 index; /* Format number */__u32 type; /* enum v4l2_buf_type */__u32 flags;__u8 description[32]; /* Description string */__u32 pixelformat; /* Format fourcc */__u32 reserved[4];
};
通过这个结构体,可以显示对应的摄像头所支持视频帧格式。
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("Supportformat:/n");
while(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1)
{
printf("/t%d.%s/n",fmtdesc.index+1,fmtdesc.description);
fmtdesc.index++;
}
我的摄像头输出如下:
Support format:1.YUV 4:2:2 (YUYV)2.MJPEG
所以,我要读取视频帧的时候就要使用YUV422
这种格式。
3. VIDIOC_S_FMT&VIDIOC_G_FMT
-------->struct v4l2_format
查看或设置视频帧格式
struct v4l2_format {__u32 type; // 帧类型union {/* V4L2_BUF_TYPE_VIDEO_CAPTURE */struct v4l2_pix_format pix; //像素格式/* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */struct v4l2_pix_format_mplane pix_mp; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */struct v4l2_window win; /* V4L2_BUF_TYPE_VBI_CAPTURE */struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SDR_CAPTURE */struct v4l2_sdr_format sdr; /* user-defined */ __u8 raw_data[200]; } fmt;
};
/** V I D E O I M A G E F O R M A T*/
struct v4l2_pix_format {__u32 width; // 像素高度__u32 height; // 像素宽度__u32 pixelformat; // 像素格式__u32 field; /* enum v4l2_field */__u32 bytesperline; /* for padding, zero if unused */__u32 sizeimage;__u32 colorspace; /* enum v4l2_colorspace */__u32 priv; /* private data, depends on pixelformat */__u32 flags; /* format flags (V4L2_PIX_FMT_FLAG_*) */__u32 ycbcr_enc; /* enum v4l2_ycbcr_encoding */__u32 quantization; /* enum v4l2_quantization */__u32 xfer_func; /* enum v4l2_xfer_func */
};
仔细看,发现这些其实和海思中的一些结构体非常类似,linux真的很强大!
4. VIDIOC_CROPCAP
-------->struct v4l2_cropcap
5. VIDIOC_G_PARM&VIDIOC_S_PARM
-------->struct v4l2_streamparm
设置Stream
信息,主要设置帧率。
struct v4l2_streamparm {__u32 type; /* enum v4l2_buf_type */union {struct v4l2_captureparm capture;struct v4l2_outputparm output;__u8 raw_data[200]; /* user-defined */} parm;
};
struct v4l2_captureparm {/* Supported modes */__u32 capability; /* Current mode */ __u32 capturemode; /* Time per frame in seconds */struct v4l2_fract timeperframe; /* Driver-specific extensions */__u32 extendedmode; /* # of buffers for read */__u32 readbuffers; __u32 reserved[4];
};
// timeperframe
// numerator和denominator可描述为每numerator秒有denominator帧
struct v4l2_fract {__u32 numerator; // 分子__u32 denominator; // 分母
};
VIDIOC_REQBUFS
-------->struct v4l2_requestbuffers
申请和管理缓冲区,应用程序和设备有三种交换数据方法,直接read/write(裸机)、内存映射(系统),用户指针(…)。一般在操作系统管理下,都是使用内存映射的方式。
/** M E M O R Y - M A P P I N G B U F F E R S*/
struct v4l2_requestbuffers {__u32 count; // 缓冲区内缓冲帧的数目__u32 type; // 缓冲帧数据格式__u32 memory; // __u32 reserved[2];
};enum v4l2_memory {V4L2_MEMORY_MMAP = 1, // 内存映射V4L2_MEMORY_USERPTR = 2, // 用户指针V4L2_MEMORY_OVERLAY = 3,V4L2_MEMORY_DMABUF = 4,
};
VIDIOC_QUERYBUF
-------->struct v4l2_buffer
struct v4l2_buffer {__u32 index; // buffer的id__u32 type; // enum v4l2_buf_type__u32 bytesused; // buf中已经使用的字节数__u32 flags; // MMAP 或 USERPTR__u32 field;struct timeval timestamp; // 帧时间戳struct v4l2_timecode timecode;__u32 sequence; // 队列中的序号/* memory location */__u32 memory;union {__u32 offset; // 设备内存起始offsetunsigned long userptr; // 指向用户空间的指针struct v4l2_plane *planes;__s32 fd;} m;__u32 length; // 缓存帧长度__u32 reserved2;__u32 reserved;
};
VIDIOC_DQBUF
应用程序从视频采集输出队列中取出已含有采集数据的帧缓冲区VIDIOC_STREAMON&VIDIOC_STREAMOFF
开始视频采集和关闭视频采集VIDIOC_QBUF
应用程序将该帧缓冲区重新挂入输入队列
总结
使用C语言高级应用—操作linux下V4L2摄像头应用程序源码成功的在我的开发板上显示出了800600的视频图像。我的开发板的显示屏是1024600的,当我设置为1024600时,实际显示为1024576,感觉很奇怪!而且显示的视频是反的,都是小问题。总之,显示视频的那一刻真的好开心!
参考链接
和菜鸟一起学linux之V4L2摄像头应用流程
(原创)基于ZedBoard的Webcam设计(一):USB摄像头(V4L2接口)的图片采集
V4L2 API详解 <二> Camera详细设置
V4L2 API
V4L2采集yuv视频花屏:Linux视频采集与编码(一)
C语言高级应用—操作linux下V4L2摄像头应用程序
v4l2 编程接口(一) — ioctl
capture.c
支持的uvc设备查询
linux下通过V4L2驱动USB摄像头相关推荐
- Linux下的硬件驱动——USB设备配置以及开发
Linux下的硬件驱动--USB设备(上)(驱动配置部分) USB设备越来越多,而Linux在硬件配置上仍然没有做到完全即插即用,对于Linux怎样配置和使用他们,也越来越成为困扰我们的一大问题.本文 ...
- Linux下的硬件驱动——USB设备
想起当初对于破安卓手机,挂在系统上可是费了好些劲,今偶遇USB驱动开发,收集备用,哪天一生气,说不定也写一个linux下的手机驱动,类似于91手机助手的,也不用配置了. Linux下的硬件驱动--US ...
- Linux下基于XScale的USB摄像头图像采集
1.引言 摄像头分为数字摄像头和模拟摄像头两大类.传统的模拟摄像头,获取图像信息需要先将视频采集设备产生的模拟视频信号经过特定的视频捕捉卡转换成数字信号,进而才能进行存储等处理.数字摄像头可以直接捕捉 ...
- Linux下用FFMPEG采集usb摄像头到RTMP
Linux下用 FFMPEG 采集 usb摄像头视频 和 摄像头内置麦克风音频 到RTMP服务 ffmpeg -f video4linux2 -qscale 10 -r 12 -s 640x480 - ...
- Linux下的硬件驱动——USB设备(下)
Linux下的硬件驱动--USB设备(下)(驱动开发部分) 文档选项 打印本页 将此页作为电子邮件发送 未显示需要 JavaScript 的文档选项 级别: 初级 赵明, 联想软件设计中心嵌入式研发处 ...
- NanoPi NEO Air使用十五:使用V4L2驱动USB摄像头
USB摄像头初识 Linux UVC driver(uvc) 该驱动适用于符合USB视频类(USB Video Class)规范的摄像头设备,它包括V4L2内核设备驱动和用户空间工具补丁.大多数大 ...
- Linux下的硬件驱动——USB设备(上)(驱动配置部分)
USB设备越来越多,而Linux在硬件配置上仍然没有做到完全即插即用,对于Linux怎样配置和使用他们,也越来越成为困扰我们的一大问题.本文着力从Linux系统下设备驱动的架构,去阐述怎样去使用和配置 ...
- linux下uvc协议访问usb摄像头,Ubuntu调用USB摄像头
FreeBSD Webcam:传送门 1 查看摄像头USB驱动 CMD ls /dev/v* Result /dev/vcs /dev/vcs4 /dev/vcsa1 /dev/vcsa5 /dev/ ...
- omap3530 linux串口驱动,LINUX下OMAP3530接MT9P031的摄像头驱动调试 给力的一周
最近有两个项目都挺着急的,尤其是LINUX下OMAP3530接MT9P031的摄像头驱动弄了很长时间.本来是让新来的兄弟负责驱动开发来着,无奈兄弟没有接触过硬件,也没有linux下视频驱动开发的经验, ...
- Linux 下UVCamp;V4L2技术简单介绍(二)
通过前文Linux 下UVC&V4L2技术简单介绍(一)我们了解了UVC和V4L2的简单知识. 这里是USB设备的文档描写叙述:http://www.usb.org/developers/do ...
最新文章
- 9名华人当选,包揽总人数1/6!2017 ACM Fellow名单公布,华人强势亮相
- 009_Raphael绘制图形
- jQuery的三种bind/One/Live事件绑定使用方法
- CTF 这个看起来有点简单
- 云图说|DRS数据对比——带您随时观测数据一致性
- (98)FPGA时序裕量
- mysql 递归_mysql5.7递归使用
- apktool(android app逆向)
- 物质之学 —— 等离子(物质的第四态)
- 正态分布的前世今生:误差分布曲线的确立
- idea使用教程-idea简介
- mysql drop_MySQL DROP TABLE语句语法
- php查找最高分最低分,​热播网剧评分最高8.6分,最低只有5.3分,你猜到哪部剧最低吗?...
- 计算机分盘的时候c盘留多少,电脑分盘c盘多大合适
- 微信破解WiFi密码如何操作?一招帮你查看密码!
- jasper 引入字体_MAC下JasperStudio创建及引用字体
- 奶牛专题2:奶牛晒衣服
- 用Marvelous Designer是如何构建人物角色3d服装的
- 无烦恼厨房android版,无烦恼厨房安卓版
- html语言中如何设置字体,css中如何设置字体样式?