Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4888904
  • 博文数量: 22
  • 博客积分: 731
  • 博客等级: 军士长
  • 技术积分: 260
  • 用 户 组: 普通用户
  • 注册时间: 2010-11-25 17:53
个人简介

技术宅,开源软件爱好者 虚拟机,编译器,编辑器,MCU IP,数据结构,操作系统,TCP/IP协议

文章分类

全部博文(22)

文章存档

2015年(1)

2014年(2)

2013年(2)

2011年(4)

2010年(13)

分类: LINUX

2010-12-06 19:34:28

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

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


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

现在开始叙述Video4Linux2的使用。


/* 打开设备并进行错误检查 */
int fd = open ("/dev/video",O_RDONLY);
if (fd==-1){
perror ("Can't open device");
return -1;
}
 
/* 查询设备的输出格式 */
struct v4l2_format format;
memset (&format,0,sizoef(format));
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1==ioctl(fd,VIDIOC_G_FMT,&format)){
perror ("While getting format");
return -2;
}
 
/*
 * 这里要将struct v4l2_format结构体置零,然后将
 * format.type设定为V4L2_BUF_TYPE_VIDEO_CAPTURE,
 * 这样在进行 VIDIOC_G_FMT 的ioctl时,驱动就会知
 * 道是在捕获视频的情形下获取格式的内容。
 * 成功返回后,format就含有捕获视频的尺寸大小及格
 * 式。格式存储在 format.fmt.pix.pixelformat这个32
 * 位的无符号整数中,共四个字节,以小头序存储。这里
 * 介绍一种获取的方法。
 */

 
char code[5];
unsigned int i;
for (i=0;i<4;i++) {
code[i] = (format.fmt.pix.pixelformat & (0xff<<i*8))>>i*8;
}
code[4]=0;
 
/* 现在的code是一个以\0结束的字符串。很多摄像头都是以格式MJPG输出视频的。
 * MJPG是Motion JPEG的缩写,其实就是一些没填霍夫曼表的JPEG图片。
 */

 
/* 请求一定数量的缓冲区。
 * 但是不一定能请求到那么多。据体还得看返回的数量
 */

struct v4l2_requestbuffers req;
memset (&req,0,sizeof(req));
req.count = 10;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (-1==ioctl(fd,VIDIOC_REQBUFS,&req)){
perror ("While requesting buffers");
return -3;
}
if (req.count < 5){
fprintf (stderr, "Can't get enough buffers!\n");
return -4;
}
 
/* 这里请求了10块缓存区,并将其类型设为MMAP型。 */
 
/* 获取缓冲区的信息
 * 在操作之前,我们必须要能记录下我们
 * 申请的缓存区,并在最后使用munmap释放它们
 * 这里使用结构体
 * struct buffer {
 * void * start;
 * ssize_t length;
 * } 以及buffer数量
 * static int nbuffer
 * 来表示
 */

struct buffer * buffers = (struct buffer *)malloc (nbuffer*sizeof(*buffers));
if (!buffers){
perror ("Can't allocate memory for buffers!");
return -4;
}
 
struct v4l2_buffer buf;
for (nbuffer=0;nbuffer<req.count;++nbuffer) {
 memset (&buf,0,sizeof(buf));
 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 buf.memory = V4L2_MEMORY_MMAP;
 buf.index = nbuffer;
 
 if (-1==ioctl(fd,VIDIOC_QUERYBUF,&buf)){
  perror ("While querying buffer");
  return -5;
 }
 
 buffers[nbuffer].length = buf.length;
 buffers[nbuffer].start = mmap (
  NULL,
  buf.length,
  PROT_READ, /* 官方文档说要加上PROT_WRITE,但加上会出错 */
  MAP_SHARED,
  fd,
  buf.m.offset
 );
 
 if (MAP_FAILED == buffers[nbuffer].start) {
  perror ("While mapping memory");
  return -6;
 }
}
 


阅读(2454) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~