V4L2视频应用程序编程架构
V4L(video4linux是一些视频系统,视频软件、音频软件的基础,经常时候在需要采集图像的场合,如视频监控,webcam,可视电话,经常使用在embedded linux中是linux嵌入式开发中经常使用的系统接口。它是linux内核提供给用户空间的编程接口,各种的视频和音频设备开发相应的驱动程序后,就可以通过v4l提供的系统API来控制视频和音频设备,也就是说v4l分为两层,底层为音视频设备在内核中的驱动,上层为系统提供的API,而对于我们来说需要的就是使用这些系统API。
V4L2较V4L1有较大的改动,并已成为2.6的标准接口。下边先就V4L2在视频捕捉或camera方面的应用框架。
V4L2采用流水线的方式,操作更简单直观,基本遵循打开视频设备、设置格式、处理数据、关闭设备,更多的具体操作通过ioctl函数来实现。
1、打开视频设备
在V4L2中,视频设备被看做一个文件。使用open函数打开这个设备:
int cameraFd;
cameraFd = open("/dev/video0", O_RDWR, 0); //用阻塞模式打开摄像头设备
2、设定属性及采集方式
打开视频设备后,可以设置该视频设备的属性,例如裁剪、缩放等。这一步是可选的。在Linux编程中,一般使用ioctl函数来对设备的I/O通道进行管理:
int ioctl (int __fd, unsigned long int __request, .../*args*/) ;
在进行V4L2开发中,常用的命令标志符如下(some are optional)即常用的IOCTL接口命令在include/linux/videodev2.h中定义:
VIDIOC_REQBUFS:分配内存
VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址
VIDIOC_QUERYCAP:查询驱动功能
VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式
VIDIOC_S_FMT:设置当前驱动的频捕获格式
VIDIOC_G_FMT:读取当前驱动的频捕获格式
VIDIOC_TRY_FMT:验证当前驱动的显示格式
VIDIOC_CROPCAP:查询驱动的修剪能力
VIDIOC_S_CROP:设置视频信号的边框
VIDIOC_G_CROP:读取视频信号的边框
VIDIOC_QBUF:把数据从缓存中读取出来
VIDIOC_DQBUF:把数据放回缓存队列
VIDIOC_STREAMON:开始视频显示函数
VIDIOC_STREAMOFF:结束视频显示函数
VIDIOC_QUERYSTD:检查当前视频设备支持的标准,例如PAL或NTSC。
常用的结构体在内核目录include/linux/videodev2.h中定义
struct v4l2_requestbuffers //申请帧缓冲,对应命令VIDIOC_REQBUFS
struct v4l2_capability//视频设备的功能,对应命令VIDIOC_QUERYCAP
struct v4l2_input//视频输入信息,对应命令VIDIOC_ENUMINPUT
struct v4l2_standard//视频的制式,比如PAL,NTSC,对应命令VIDIOC_ENUMSTD
struct v4l2_format //帧的格式,对应命令VIDIOC_G_FMT、VIDIOC_S_FMT等
struct v4l2_buffer//驱动中的一帧图像缓存,对应命令VIDIOC_QUERYBUF
struct v4l2_crop//视频信号矩形边
v4l2_std_id//视频制式
2.1检查当前视频设备支持的标准
在亚洲,一般使用PAL(720X576)制式的摄像头,而欧洲一般使用NTSC(720X480),使用VIDIOC_QUERYSTD来检测:
v4l2_std_id std;
do {
ret = ioctl(fd, VIDIOC_QUERYSTD, &std);
} while (ret == -1 && errno == EAGAIN);
switch (std) {
case V4L2_STD_NTSC:
//……
case V4L2_STD_PAL:
//……
}
2.2 设置视频捕获格式
当检测完视频设备支持的标准后,还需要设定视频捕获格式,结构如下:
struct v4l2_format fmt;
memset ( &fmt, 0, sizeof(fmt) );
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 720;
fmt.fmt.pix.height = 576;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
return -1;
}
v4l2_format结构如下:
struct v4l2_format
{
enum v4l2_buf_type type; // 数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE
union
{
struct v4l2_pix_format pix;
struct v4l2_window win;
struct v4l2_vbi_format vbi;
__u8 raw_data[200];
} fmt;
};
struct v4l2_pix_format
{
__u32 width; // 宽,必须是16的倍数
__u32 height; // 高,必须是16的倍数
__u32 pixelformat; // 视频数据存储类型YUV422、RGB
enum v4l2_field field;
__u32 bytesperline;
__u32 sizeimage;
enum v4l2_colorspace colorspace;
__u32 priv;
};
2.3 分配内存
接下来可以为视频捕获分配内存:
struct v4l2_requestbuffers req;
if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
return -1;
}
v4l2_requestbuffers 结构如下:
struct v4l2_requestbuffers
{
__u32 count; // 缓存数量
enum v4l2_buf_type type; // 数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE
enum v4l2_memory memory; // V4L2_MEMORY_MMAP 或 V4L2_MEMORY_USERPTR
__u32 reserved[2];
};
2.4 获取并记录缓存的物理空间
使用VIDIOC_REQBUFS,我们获取了req.count个缓存,下一步通过调用VIDIOC_QUERYBUF命令来获取这些缓存的地址,然后使用mmap函数转换成应用程序中的绝对地址,最后把这段缓存放入缓存队列:
图1 摄像头内存映射示意图
typedef struct VideoBuffer {
void *start;
size_t length;
} VideoBuffer;
VideoBuffer* buffers = calloc( req.count, sizeof(*buffers) );
struct v4l2_buffer buf;
for (numBufs = 0; numBufs < req.count; numBufs++) {
memset( &buf, 0, sizeof(buf) );
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = numBufs;
// 读取缓存
if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {
return -1;
}
buffers[numBufs].length = buf.length;
// 转换成相对地址
buffers[numBufs].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,
MAP_SHARED,fd, buf.m.offset);
if (buffers[numBufs].start == MAP_FAILED) {
return -1;
}
// 放入缓存队列
if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
return -1;
}
}
2.5 视频采集方式
操作系统一般把系统使用的内存划分成用户空间和内核空间,分别由应用程序管理和操作系统管理。应用程序可以直接访问内存的地址,而内核空间存放的是供内核访问的代码和数据,用户不能直接访问。v4l2捕获的数据,最初是存放在内核空间的,这意味着用户不能直接访问该段内存,必须通过某些手段来转换地址。
一共有三种视频采集方式:使用read、write方式;内存映射方式和用户指针模式。
read、write方式,在用户空间和内核空间不断拷贝数据,占用了大量用户内存空间,效率不高。
内存映射方式:把设备里的内存映射到应用程序中的内存控件,直接处理设备内存,这是一种有效的方式。上面的mmap函数就是使用这种方式。
用户指针模式:内存片段由应用程序自己分配。这点需要在v4l2_requestbuffers里将memory字段设置成V4L2_MEMORY_USERPTR。
2.6 处理采集数据
V4L2有一个数据缓存,存放req.count数量的缓存数据。数据缓存采用FIFO的方式,当应用程序调用缓存数据时,缓存队列将最先采集到的视频数据缓存送出,并重新采集一张视频数据。这个过程需要用到两个ioctl命令,VIDIOC_DQBUF和VIDIOC_QBUF:
struct v4l2_buffer buf;
memset(&buf,0,sizeof(buf));
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_MMAP;
buf.index=0;
//读取缓存
if (ioctl(cameraFd, VIDIOC_DQBUF, &buf) == -1)
{
return -1;
}
//…………视频处理算法
//重新放入缓存队列
if (ioctl(cameraFd, VIDIOC_QBUF, &buf) == -1) {
return -1;
}
3. 关闭视频设备
使用close函数关闭一个视频设备
close(cameraFd)
如果使用mmap,最后还需要使用munmap方法。
本程序中使用了mmap的读取图像的方法,缓存中仅申请了1帧图像空间。下图为调用V4L2获取摄像头图像的基本流程:
图2 V4L2读取摄像头图像流程图SHI
上面的V4L2应用程序编程架构只是一般步骤,自己使用时可能稍有不同,
这里我分享一个测试虚拟视频驱动vivi.c的程序,可以作为很不错的基础架构参考
capture_image.rar
将在下一个章节中结合用户层和kernel层具体详细分析驱动的实现过程,直观体现用户层的每一个动作,驱动所作出的响应
待续。。。。。。
V4L2视频应用程序编程架构相关推荐
- 如何选择视频聊天程序搭建视频聊天网站
搭建1个视频聊天网站,特别是带有精彩视频的聊天网站,无疑是一种非常快捷的网络创业方式.可是自己没有开发能力怎么办?那么你就需要选购一些视频聊天程序或则视频聊天源码.那么如何选择最适合自己的视频聊天程序 ...
- ironbot智能编程机器人_视频 | 多模式编程机器人,“程序猿”培养从小抓起
原标题:视频 | 多模式编程机器人,"程序猿"培养从小抓起 一谈到机器人,男孩子的心中必定莫名的兴奋,已经三十好几的我,谈到机器人依然兴奋不已.男孩子的心中对于机器总有一丝莫名的冲 ...
- V4L2视频驱动程序开发之驱动方法poll 和 应用程序select
V4L2视频驱动程序开发已经进入尾声,本次视频支持多个通道的stream同时传输,即有多个设备文件关联到驱动.最高支持48个stream同时输入. 应用程序在获取stream的时候,需要用到selec ...
- 基于Linux视频驱动接口V4L2视频采集编程
视频采集基本步骤流程如下: 打开视频设备,设置视频设备属性及采集方式.视频数据处理,关闭视频设备,如下图所示: 一.打开视频设备 打开视频设备非常简单,在V4L2中,视频设备被看做一个文件.使用ope ...
- API(Application Programming Interface,应用程序编程接口)
API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码 ...
- DM6446开发攻略:V4L2视频驱动和应用分析
针对DAVINCI DM6446平台,网络上也有很多网友写了V4L2的驱动,但只是解析Montavistalinux-2.6.10 V4L2的原理.结构和函数,深度不够.本文决定把Montavista ...
- C语言高级应用---操作linux下V4L2摄像头应用程序
目录(?)[-]采集方式V4L2操作流程点击这个网址说得很详细了这里不多说httpbaikebaiducomview5494174htm我们都知道,想要驱动Linux下的摄像头,其实很简单,照着V4L ...
- 程序员架构修炼之道:软件架构基本概念和思维
引子 互联网发展到今天,软件系统早就不是一个万行代码加上一台服务器这样的作坊玩具.BAT的服务器规模已经达到甚至超过百万级.传统企业向互联网的靠拢,势不可挡. 优秀的软件系统架构师就像大海航船舵手,指 ...
- iPhone应用程序编程指南
介绍 请注意:本文档之前命名为iPhone OS编程指南. iPhone SDK为创建iPhone的本地应用程序提供必需的工具和资源.在用户的Home屏幕上,iPhone的本地应用程序表示为图标.它们 ...
最新文章
- 前端token刷新并发处理
- Python格式化字符串、占位符、合并数组
- Windows10 编译 Open3D 时出现 error C2220: 以下警告被视为错误 (编译源文件
- XML 和 HTML中常用的转义字符
- C++shell sort希尔排序的实现算法之一(附完整源码)
- 怎么调用系统通讯录并向被选中联系人发送短信
- 选择文字就能选择复选框
- 问题解决之--无法识别的属性“targetFramework”。请注意属性名称区分大小写。
- java获取配置文件_JAVA读取配置文件的方法
- jfinal 和bjui 常用前后交互方式
- 海康威视摄像头网线连接笔记本电脑,客户端提示:1连接设备失败。设备不在线或网络原因引起的连接超时等。(HCNetSDK.dll[7].)解决方案
- OSGi bundle activator不工作解决
- 为串的模式匹配。模式匹
- leetcode347——前K个高频元素——java实现
- 循环神经网络--RNN GRU LSTM 对比分析
- 电脑端知乎不显示图片
- 金融错配程度/信贷错配程度/资本错配程度/资本资源错配程度(1998-2021年)
- sqlzoo错题总结-1
- qst -sim 出现 license 错误
- 成都 工资 java_成都java工资一般多少,成都java工资水平,成都java工资底薪能到多少...
热门文章
- KeyMob聚合平台:为开发者塑造广告变现形式
- Android入门学习2
- 几种排序算法的比较(冒泡、快速、Shell排序)
- railscasts #1 Caching with Instance Variables
- html基础知识补全
- Flask 框架 是 Python 中最流行的 Web 框架之一
- 12 个轻量级的 JavaScript 库
- jQuery/CSS3炫酷动画效果插件 animate
- linux到windows的ssh,ssh如何使用pxsh从linux到windows
- rbw设计_华为5G最新突破!我国每周增加1万个5G基站!频谱分析仪中RBW和VBW的区别...