当前位置: 首页 > 编程日记 > 正文

Qt/Linux 下的摄像头捕获(Video4Linux2)

Linux下使用各种设备是一件令人兴奋的事情。在Unix的世界里,用户与硬件打交待总是简单的。最近笔者在Linux下搞了摄像头的开发,有一点感想发于此处。

Linux中操作一个设备一般都是打开(open),读取(read)和关闭(close)。使用Read的大多是一些字符型设备,然而对于显示屏 或者摄像头这种字符设备而已,挨个字的读写将使得系统调用变得频繁,众所周之,系统调用对于系统而已是个不小的开销。于是有内存映射(mmap)等物,本 例中将讲述在Linux下开发摄像头的一般过程以及使用Qt进行界面开发的实例。

使用mmap方式获取摄像头数据的方式过程一般为:

打开设备 -> 获取设备的信息 -> 请求设备的缓冲区 -> 获得缓冲区的开始地址及大小 -> 使用mmap获得进程地址空间的缓冲区起始地址 -> 读取缓冲区。

Mmap就是所谓内存映射。很多设备带有自己的数据缓冲区,或者驱动本身在内核空间中维护一片内存区域,为了让用户空间程序安全地访问,内核往往要 从设备内存或者内核空间内存复制数据到用户空间。这样一来便多了复制内存这个环节,浪费了时间。因此mmap就将目标存储区域映射到一个用户空间的一片内 存,这样用户进程访问这片内存时,内核将自动转换为访问这个目标存储区。这种转换往往是地址的线性变化而已(很多设备的存储空间在所谓外围总线地址空间 (X86)或者总的地址空间(ARM)上都是连续的),所以不必担心其转换的效率。

现在开始叙述Video4Linux2的使用。

  1 /* 打开设备并进行错误检查 */
2
3 int fd = open ("/dev/video",O_RDONLY);
4
5 if (fd==-1){
6
7 perror ("Can't open device");
8
9 return -1;
10
11 }
12
13
14
15 /* 查询设备的输出格式 */
16
17 struct v4l2_format format;
18
19 memset (&format,0,sizoef(format));
20
21 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
22
23 if (-1==ioctl(fd,VIDIOC_G_FMT,&format)){
24
25 perror ("While getting format");
26
27 return -2;
28
29 }
30
31
32
33 /*
34
35 * 这里要将struct v4l2_format结构体置零,然后将
36
37 * format.type设定为V4L2_BUF_TYPE_VIDEO_CAPTURE,
38
39 * 这样在进行 VIDIOC_G_FMT 的ioctl时,驱动就会知
40
41 * 道是在捕获视频的情形下获取格式的内容。
42
43 * 成功返回后,format就含有捕获视频的尺寸大小及格
44
45 * 式。格式存储在 format.fmt.pix.pixelformat这个32
46
47 * 位的无符号整数中,共四个字节,以小头序存储。这里
48
49 * 介绍一种获取的方法。
50
51 */
52
53
54
55 char code[5];
56
57 unsigned int i;
58
59 for (i=0;i<4;i++) {
60
61 code[i] = (format.fmt.pix.pixelformat & (0xff<<i*8))>>i*8;
62
63 }
64
65 code[4]=0;
66
67
68
69 /* 现在的code是一个以/0结束的字符串。很多摄像头都是以格式MJPG输出视频的。
70
71 * MJPG是Motion JPEG的缩写,其实就是一些没填霍夫曼表的JPEG图片。
72
73 */
74
75
76
77 /* 请求一定数量的缓冲区。
78
79 * 但是不一定能请求到那么多。据体还得看返回的数量
80
81 */
82
83 struct v4l2_requestbuffers req;
84
85 memset (&req,0,sizeof(req));
86
87 req.count = 10;
88
89 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
90
91 req.memory = V4L2_MEMORY_MMAP;
92
93 if (-1==ioctl(fd,VIDIOC_REQBUFS,&req)){
94
95 perror ("While requesting buffers");
96
97 return -3;
98
99 }
100
101 if (req.count < 5){
102
103 fprintf (stderr, "Can't get enough buffers!/n");
104
105 return -4;
106
107 }
108
109
110
111 /* 这里请求了10块缓存区,并将其类型设为MMAP型。 */
112
113
114
115 /* 获取缓冲区的信息
116
117 * 在操作之前,我们必须要能记录下我们
118
119 * 申请的缓存区,并在最后使用munmap释放它们
120
121 * 这里使用结构体
122
123 * struct buffer {
124
125 * void * start;
126
127 * ssize_t length;
128
129 * } 以及buffer数量
130
131 * static int nbuffer
132
133 * 来表示
134
135 */
136
137 struct buffer * buffers = (struct buffer *)malloc (nbuffer*sizeof(*buffers));
138
139 if (!buffers){
140
141 perror ("Can't allocate memory for buffers!");
142
143 return -4;
144
145 }
146
147
148
149 struct v4l2_buffer buf;
150
151 for (nbuffer=0;nbuffer<req.count;++nbuffer) {
152
153 memset (&buf,0,sizeof(buf));
154
155 buf.type= V4L2_BUF_TYPE_VIDEO_CAPTURE;
156
157 buf.memory= V4L2_MEMORY_MMAP;
158
159 buf.index = nbuffer;
160
161
162
163 if (-1==ioctl(fd,VIDIOC_QUERYBUF,&buf)){
164
165 perror ("While querying buffer");
166
167 return -5;
168
169 }
170
171
172
173 buffers[nbuffer].length = buf.length;
174
175 buffers[nbuffer].start = mmap (
176
177 NULL,
178
179 buf.length,
180
181 PROT_READ, /* 官方文档说要加上PROT_WRITE,但加上会出错 */
182
183 MAP_SHARED,
184
185 fd,
186
187 buf.m.offset
188
189 );
190
191
192
193 if (MAP_FAILED == buffers[nbuffer].start) {
194
195 perror ("While mapping memory");
196
197 return -6;
198
199 }
200
201 }
202
203
204
205 /*这个循环完成后,所有缓存区都保存在
206
207 *了buffers这个数组里了,完了就再将它们munmap即可。
208
209 */
210
211
212
213 /* 打开视频捕获 */
214
215 enum v4l2_buf_type type;
216
217 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
218
219 if (-1==ioctl(fd,VIDIOC_STREAMON,&type)){
220
221 perror ("While opening stream");
222
223 return -7;
224
225 }
226
227
228
229 /* 与内核交换缓冲区 */
230
231 unsigned int i;
232
233 i=0;
234
235 while(1) {
236
237 memset (&buf,0,sizeof(buf));
238
239 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
240
241 buf.memory = V4L2_MEMORY_MMAP;
242
243 buf.index = i;
244
245
246
247 if (-1==ioctl(fd,VIDIOC_DQBUF,&buf)){
248
249 perror ("While getting buffer's data");
250
251 return -8;
252
253 }
254
255
256
257 /* 现在就得到了一片缓冲区的数据,发送处理 */
258
259 process_image ( buffers+buf.index,buf.index );
260
261
262
263 /* 将缓冲区交还给内核 */
264
265 if (-1==ioctl(fd,VIDIOC_QBUF,&buf)){
266
267 perror ("While returning buffer's data");
268
269 return -9;
270
271 }
272
273
274
275 i = (i+1) & nbuffer;
276
277 }

这就是所有获取的过程了。至于图像的处理,则是由解码函数和Qt来处理。现在先进行一些思路的设计。设想在进行图像的转换时,必须提供一块内存区域 来进行,我们当然可以在转换时使用malloc来进行动态分配,转换完成并显示后,再将它free。然而这样做对内核而言是一个不小的负担:每次为一整张 图片分配内存,最少也有上百KB,如此大的分配量和释放量,很容易造成内存碎片加重内核的负担。由于仅是每转换一次才显示一次图像,所以这片用于转换的内 存区域可以安全地复用,不会同时由两个线程操作之。因此在初始化时我们为每一块内存映射缓冲区分配一块内存区域作为转换用。对于MJPEG到JPEG的转 换,使用相同的内存大小。代码就不在此列出了。这片假设这个内存区域的起始指针为convertion_buffers,在process_image (struct buffer * buf, int index ) 中,有

 1 void process_image (struct buffer *buf, int index){
2
3 struct * buffer conv_buf = convertion_buffers+index;
4
5 do_image_conversion (
6
7 buf->start, buf->length, /* 要转换的区域 */
8
9 conv_buf->start, conv_buf->length, /* 保存转换数据的区域 */
10
11 );
12
13
14
15 /* 现在就可以把数据取出并交给QPixmap处理
16
17 * 要在一个QWidget里作图,必须重载paintEvent
18
19 * 函数并用QPainter作画。然而paintEvent
20
21 * 是由事件驱动层调用的,我们不能手工,
22
23 * 所以在我们自己的的重载类里要保存一个全局
24
25 * 的QPixmap。这里设为 QPixmap * m_pixmap
26
27 */
28
29 m_pixmap -> loadFromData (conv_buf->start,conv_buf->length);
30
31 /* 立即安排一次重绘事件 */
32
33 repaint ();
34
35 }
36
37
38
39 /* 重载的paintEvent示例 */
40
41 MyWidget::paintEvent (QPaintEvent * evt) {
42
43 QPainter painter(this);
44
45 painter.drawPixmap (QPoint(0,0),*m_pixmap);
46
47 QWidget::paintEvent(evt);
48
49 }

这里讲Pixmap画到了(0,0)处。

考虑的改进之处:

虽然上述程序已经可以工作了,但是有一些细节可以改进。比如图像转换之处,可能相当耗时。解决的办法之一可以考虑多线程,用一个线程进行数据的收 集,每收集一帧数据便通知显示的进程。显示的进程使用一个FIFO收集数据,用一个定时器,在固定的时间到时,然后从FIFO中取出数据进行转换然后显 示。两个线程互不干扰,可以更有效地利用CPU,使收集、转换和显示协调地工作。

VN:F [1.9.6_1107]

转载于:https://www.cnblogs.com/elect-fans/archive/2011/12/06/2408701.html

相关文章:

ubuntu chm文档阅读器

一,chm阅读器名称 KchmViewer 安装方法 sudo apt-get install kchmviewer 使用 kchmviewer #非root用户可以直接使用 转载于:https://www.cnblogs.com/jiangfeilong/p/11184226.html

c++语言中,vector容器与list容器的区别和联系?_百度知道

C STL 提供了3个序列容器 &#xff1a;vector, deque, list vector 中的元素是顺序存放的&#xff0c;所以随机访问很快,但是要插入和删除&#xff0c;这个时间复杂度就很高了&#xff0c;vector初始化时有一个capacity,如果元素个数超出capacity,那vector就会重新分配一个新的…

《Java技术》第三次作业--面向对象——继承、抽象类、接口

1.阅读下面程序&#xff0c;分析是否能编译通过&#xff1f;如果不能&#xff0c;说明原因。应该如何修改&#xff1f;程序的运行结果是什么&#xff1f;为什么子类的构造方法在运行之前&#xff0c;必须调用父 类的构造方法&#xff1f;能不能反过来&#xff1f; class Grandp…

《挑战30天C++入门极限》新手入门:C/C++中枚举类型(enum)

新手入门&#xff1a;C/C中枚举类型(enum)   如果一个变量你需要几种可能存在的值&#xff0c;那么就可以被定义成为枚举类型。之所以叫枚举就是说将变量或者叫对象可能存在的情况也可以说是可能的值一一例举出来。   举个例子来说明一吧&#xff0c;为了让大家更明白一点&…

【二级java】 二分法查找

例题1 &#xff1a;对长度为n的线性表进行顺序查找&#xff0c;在最坏情况下所需要的比较次数为______。 解析&#xff1a; 如果线性表中的第一个元素就是被查找元素&#xff0c;则只需做一次比较就查找成功 查找次数为1 如果线形表中不存在该数据&#xff0c;查找次数为n 例…

List和ObservableCollection的相互转化

在WPF &#xff0c; silverlight &#xff0c;WP7中经常会用到List<T>和ObservableCollection<T>。这里简单讲一下他们之间的相互转换。 1.List<T>的简单介绍&#xff1a; List<T>代表的是强类型的Ojbect集合&#xff0c;可以通过索引访问并且提供了查…

获取局域网打印机列表

/// <summary> /// 获取局域网打印机列表 /// </summary> /// <param name"DefaultPrinter">默认打印机</param> /// <returns>局域网中所有打印机列表</returns> public static List&…

戏说 .NET GDI+系列学习教程(三、Graphics类的应用_验证码)

关于Graphics也有了基本了解下面想说的的是学这个东东干什么呢&#xff0c;到底如何应用目前常见应用1、验证码&#xff08;参照网上的&#xff09;2、打印排版&#xff08;会提到关于条形码大小设置&#xff09;3、自定义控件 一、验证码 1 class ValidateCode2 {3 …

转载:HBuilder常用快捷键

原文&#xff1a;http://www.cnblogs.com/DCL1314/p/8625110.htmlHBuilder常用快捷键1.文件新建 Ctrl N 关闭 Ctrl F4 全部关闭 Ctrl Shift F4 属性 Alt Enter 2.编辑激活代码助手 Alt / 激活快捷键视图 Ctrl Shift L开启关闭注释整行 Ctrl / 开启关闭注释已选内容 Ct…

java源程序结构

JAVA培训 一个完整的java源程序应该包括下列部分&#xff1a;  package语句&#xff1b; //该部分至多只有一句&#xff0c;必须放在源程序的第一句  import语句&#xff1b; /*该部分可以有若干import语句或者没有&#xff0c;必须放在所有的          类定义之前…

【二级java】排序技术

例题1 &#xff1a;希尔排序属于下列哪种排序法 解析&#xff1a; 希尔排序法的基本思想是&#xff1a; 将整个无序序列分割成若干小的子序列分别进行插入排序&#xff0c;所以属于插入排序 例题2 &#xff1a; 在下列几种排序方法中&#xff0c;要求内存量最大的是 解析&am…

HDU 1257 - 最少拦截系统 ( LIS / 贪心 )

题目 现在有一种拦截系统&#xff0c;第一发拦截可以是任意高度&#xff0c;但是之后的拦截高度不能比上次高。为了拦截下所有的炮弹&#xff0c;最少需要准备几套拦截系统&#xff1f; 思路 可能是语文没学好吧&#xff0c;一开始被题意卡了一下。&#xff08;而且题目连数据范…

python练习:猜价钱小游戏

#猜价钱 trueprice 202 price input("Please guess the price:") while (int(price) ! trueprice):if(int(price) > trueprice):price input("Your price is higher,Please try again:")else:price input("Your price is lower,Please try aga…

android系统短信库的一些用法

1、查询所有短信&#xff0c;按发件人进行分组Cursor mCursor managedQuery(Uri.parse("content://sms"),new String[] {"_id,address,date,read,status,type,body,count(address) as " "totleCount from (select _id,substr(address,4) as address,…

【二级java】操作题知识点积累

1、java中^代表亦或&#xff08;相同为0&#xff0c;不同为1&#xff09; 2、使用下标直接访问字符串中字符的方法 string.charAt&#xff08;&#xff09; 3、从主方法派出异常给jvm时 使用的是 throws 而不是 throw 4、使用InputStreamReader 、BufferedReader时需要使用的包…

Utilize Sql Tuning Advisor from Script

Sql Tuning Advisor是10g以后出现的一个十分有用的调优工具&#xff0c;大多数情况下我们可以通过dbconsole或者Grid Control的web界面调用SQL Advisor&#xff1b;但如果系统中没有配置dbconsole或者Grid Control的话&#xff0c;我们则需要通过手动调用DBMS_SQLTUNE PL/SQL程…

Isight 命令行运行任务

说明书参考:https://abaqus-docs.mit.edu/2017/English/DSSIMULIA_Established.htm 不一定对版本。但是大部分还可以。 不对的可以在命令里敲help 首先&#xff0c;说明书里的命令行客户端默认加入环境变量。 实测win/linux安装没有环境变量。可以自行添加也可找到目录自行运行…

【二级java】软件工程基础

1、软件工程三要素 &#xff1a; 方法 工具 过程 2、软件工程中根本上来说是为了研究软件开发技术 3、软件工程的定义 &#xff1a; 应用于计算机软件的定义、开发维护的一整套方法、工具、文档、实践标准和工序 4、软件危机的表现 &#xff1a; 软件的生命周期&#xff1a;…

ASP.NET WebAPI 11 参数验证

在绑定完Action的所有参数后,WebAPI并不会马上执行该方法,而要对参数进行验证,以保证输入的合法性. ModelState 在ApiController中一个ModelState属性用来获取参数验证结果. public abstract class ApiController : IHttpController, IDisposable{public ModelStateDictionary …

C#WinForm的线程及Invoke应用(转)

C#多线程异步访问winform中控件 http://zwkufo.blog.163.com/blog/static/25882512009111453957552/?fromdm&fromSearch&isFromSearchEngineyes 我们在做winform应用的时候&#xff0c;大部分情况下都会碰到使用多线程控制界面上控件信息的问题。然而我们并不能用传…

C/C++语言点滴

C/C学习笔记 1.C语言中使用关键字sizeof来求数组长度 char temp[3]; int tempLen sizeof(temp)/sizeof(char); strlen只能用来求字符串长度 2.交换两个数据的几种方法及其区别 方法一&#xff1a;值传递&#xff0c;在main函数中并未改变a,b 的值&#xff0c;虽然在swap中a…

[C#][EF] 添加表添加不进来

确认此表有没有主键&#xff0c;没有主键时就会这样。转载于:https://www.cnblogs.com/z5337/p/8891230.html

DNN 数据访问策略 (转)

经过几天断断续续的努力&#xff0c;这篇文章终于翻译结束&#xff0c;文章主要讲了DNN的数据访问策略&#xff0c;对于了解系统整体上是如何工作的有一定的帮助&#xff0c;希望能给dnn的初学者一些有用的信息。由于翻译的匆忙水平有限&#xff0c;错误或不当之处在所难免&…

Windows程序设计学习笔记(1):一个简单的windows程序

《Windows程序设计》(第五版)(美Charles Petzold著) 1 #include<windows.h>2 3 LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);4 5 int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdline,int iCmdShow) 6 //第一个参数是实例句柄&#…

【二级java】模拟题知识点总结

算法的复杂度 &#xff1a; &#xff08;一&#xff09;时间复杂度 &#xff1a; 执行算法所需要的计算工作量&#xff0c;与计算机的运行速度和存储空间无关 &#xff08;二&#xff09;空间复杂度 &#xff1a; 算法在运行过程中临时占用的存储空间的大小。 零散知识点 &am…

虚拟化市场成熟

调查称虚拟化市场已经成熟 VMware份额领先 存储在线 11年03月18日 WangFei 导读&#xff1a;据市场研究公司Forrester Research最新研究表明&#xff0c;虚拟服务器技术的应用实际上是从去年开始的&#xff0c;虚拟服务器技术终于完成了测试和研发阶段。 关键词&#xff1a…

python day two,while

一、运算符号 算数运算符&#xff1a; 、-、*、/、//&#xff08;取整除&#xff09;、%&#xff08;去余&#xff09;、** 比较运算符&#xff1a;>、< 、>、<、 赋值运算符&#xff1a;、、-、/、%、** 逻辑预算符&#xff1a;and、or、not 布尔值 成员运算符&am…

【模板】树状数组 2

题目描述 如题&#xff0c;已知一个数列&#xff0c;你需要进行下面两种操作&#xff1a; 1.将某区间每一个数数加上x 2.求出某一个数的值 输入输出格式 输入格式&#xff1a; 第一行包含两个整数N、M&#xff0c;分别表示该数列数字的个数和操作的总个数。 第二行包含N个用空格…

【java】Maven工程引入各种jar包的功能

1、mysql驱动 &#xff1a; 代码实现 &#xff1a; <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>6.0.6</version></dependency> 2、数据库操作工具 &#xff1a; 代码实现…

[转]Android横竖屏切换解决方案

本文转自&#xff1a;http://www.cnblogs.com/domybest/archive/2011/06/30/2094779.html 首先在Mainifest.xml的Activity元素中加入android:configChanges"orientation|keyboardHidden"属性<activity android:name".FileBrowser" android:label"s…