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 nbuffer132 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.offset188 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作画。然而paintEvent20 21 * 是由事件驱动层调用的,我们不能手工,22 23 * 所以在我们自己的的重载类里要保存一个全局 24 25 * 的QPixmap。这里设为 QPixmap * m_pixmap26 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

Qt/Linux 下的摄像头捕获(Video4Linux2)相关推荐

  1. C语言高级应用---操作linux下V4L2摄像头应用程序

    目录(?)[-]采集方式V4L2操作流程点击这个网址说得很详细了这里不多说httpbaikebaiducomview5494174htm我们都知道,想要驱动Linux下的摄像头,其实很简单,照着V4L ...

  2. linux 远程挂载摄像头_如何实现嵌入式Linux下USB摄像头视频采集

    展开全部 在linux下所e5a48de588b662616964757a686964616f31333337613134有设备都是文件.所以对摄像头的操作其实就是对文件的操作.USB摄像头的设备文件 ...

  3. Linux的uvc的摄像头驱动,[OpenWrt Wiki] Linux下的摄像头驱动UVC

    ==== Linux下的摄像头驱动UVC ==== 以下操作及配置均基于Logitech Quickcam Pro for Notebooks (2007)的摄像头.在Trunk开发版本的OpenWr ...

  4. 借助motion操控Linux下的摄像头

    介绍 本文介绍如何使用motion来操控Linux下的摄像头. 安装 apt-get install motion 配置文件 输入命令后面的命令编辑配置文件,vim /etc/motion/motio ...

  5. linux下的摄像头驱动怎么安装方法,linux下良田摄像头驱动怎么装?

    2011-07-28 回答 下载驱动精灵更新一下就行了 追问: 驱动精灵是在windows下运行的 看题目linux下 我下载了libland_10018_linux.tar.gz的驱动 怎么命令来装 ...

  6. linux 下笔记本摄像头设备名称,Ubuntu下关闭笔记本电脑的内置摄像头的一种方法...

    先說明:我用的是Lenovo G450,用的是uvc攝像頭驅動. 對於在網上論壇找不到合適的方法,以及Ubuntu隻有音頻設置軟件,我感到十分無奈.也許是大蝦們不想為那些對Linux沒有學習興趣的人費 ...

  7. linux 移植qt,Linux下移植QT(2)---移植QT

    准备:ubantu12.04   内核 3.0.8(最好用同样的内核,3.2.0时没成功) 交叉编译工具:arm-cortex_a8-linux-gnueabi-gcc-4.4.6 QT版本5.4.2 ...

  8. linux开发板usb摄像头,ARM9+Linux下USB摄像头图像采集

    终于实现了在Micro2440+Linux2.6.29环境下通过NFS文件系统实现USB摄像头图像采集.在此,做一个总结,以备后续参考. 实验条件:RedHat9.0(内核版本2.4.18),Micr ...

  9. linux强行卸载qt,Linux下卸载QT SDK

    unbuntu下卸载QT 方法一: you can remove it like this, those developers should add this somewhere ! like nex ...

最新文章

  1. ReSharper 配置及用法
  2. C++(一)——存储持续性、作用域、链接性
  3. Android Studio中关于9-patch格式图片的编译错误
  4. GitHub|基于强化学习自动化剪枝
  5. GNOME如何使用Git
  6. android wifi 连不上手机助手,usb调试断断续续的很烦?试试adb wifi调试
  7. 锁和并发性----隔离级别
  8. HDU-1176-免费馅饼(dp)
  9. Linux 安装 nginx 服务教程
  10. “思考”绝非一时的想法——读大前研一《思考的技术》
  11. ashx获取input file 文件_通过Ajax方式上传文件(input file),使用FormData进行Ajax请求...
  12. 带有RNN循环神经网络的机器学习 4 NLP 从零到英雄 ML with Recurrent Neural Networks
  13. unity期末大作业消消乐小游戏(附下载链接)
  14. T100 ERP 开发说明(一)
  15. XMLSpy的主要功能介绍
  16. 字节日常实习生面试 无了~
  17. Elasticsearch笔记
  18. 多人的证件照放在一张纸上怎么排版
  19. php redis 传递闭包,有向图传递闭包 - osc_wff1160a的个人空间 - OSCHINA - 中文开源技术交流社区...
  20. 论文阅读——A higher-order CRF model for road network extraction

热门文章

  1. 2022-2028年中国氨基酸表面活性剂行业研究及发展前瞻报告
  2. Jquery DIV滚动至浏览器顶部后固定不动代码
  3. debian10 简单的CA使用
  4. linux 文件查找与文件中注释去除
  5. photoshop小结
  6. java中调用python
  7. 协程和任务 异步IO 重点
  8. Rust和C / C ++的跨语言链接时间优化LTO
  9. 开放式神经网络交换-ONNX(下)
  10. 小目标检测的增强算法