java自学教程|www.konglongmei.com

作者: 123456790
查看: 87|回复: 0

more +社区更新Forums

more +随机图赏Gallery

[技术知识] 2019年12月2日Linux开发手记

[技术知识] 2019年12月2日Linux开发手记

[复制链接]
123456790 | 显示全部楼层 发表于: 2019-12-3 00:59:48
123456790 发表于: 2019-12-3 00:59:48 | 显示全部楼层 |阅读模式
查看: 87|回复: 0

你还没有注册,无法下载本站所有资源,请立即注册!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
开始学习在Linux下视频源捕获驱动框架,也就是V4L2(video4linux),本次关于v4l2的知识准备主要在于其的官方例程,理解官方例程也就差不多掌握了v4l2的基本内容。例程在:http://blog.chinaunix.net/uid-23983143-id-3351976.html
知识准备:
v42视频编程的流程和对文件操作并没有什么本质的不同,大概的流程如下
       1.打开视频设备(通常是/dev/video0)
       2.获得设备信息。
       3.根据需要更改设备的相关设置。
       4.获得采集到的图像数据(在这里v4l提供了两种方式,直接通过打开的设备读取数据,使用mmap内存映射的方式获取数据)。
       5.对采集到的数据进行操作(如显示到屏幕,图像处理,存储成图片文件)。
       6.关闭视频设备。
知道了流程之后,我们就需要根据流程完成相应的函数。
第一步:
那么我们首先完成第1步打开视频设备,需要完成int v4l_open(char *, v4l_device *);
具体的函数如下
#define DEFAULT_DEVICE “/dev/video0”
int v4l_open(char *dev , v4l_device *vd)
{
       if(!dev)dev= DEFAULT_DEVICE;
       if((vd-fd=open(dev,O_RDWR))fd);return 0;}
第二步:
现在我们完成第2步中获得设备信息的任务,下面先给出函数在对函数作出相应的说明。
int v4l_get_capability(v4l_device *vd)
{  
   if (ioctl(vd->fd, VIDIOCGCAP, &(vd->capability)) < 0) {  
      perror("v4l_get_capability:");  
      return -1;  
   }  
   return 0;  
}
int v4l_get_picture(v4l_device *vd)  
{  
   if (ioctl(vd->fd, VIDIOCGPICT, &(vd->picture)) < 0) {  
      perror("v4l_get_picture:");  
      return -1;  
   }  
   return 0;  
}
对于以上两个函数我们不熟悉的地方可有vd->capability和vd->picture两个结构体,和这两个函数中最主要的语句ioctl。对于ioctl的行为它是由驱动程序提供和定义的,在这里当然是由v4l所定义的,其中宏VIDIOCGCAP和VIDIOCGPICT的分别表示获得视频设备的capability和picture。对于其他的宏功能定义可以在你的Linux系统中的/usr/include/linux/videodev.h中找到,这个头文件也包含了capability和picture的定义。例如:
struct video_capability
{
       char name[32];
       int type;
       int channels;   /* Num channels */
       int audios;      /* Num audio devices */
       int maxwidth; /* Supported width *
       int maxheight; /* And height */
       int minwidth;  /* Supported width */
       int minheight; /* And height */
};capability结构它包括了视频设备的名称,频道数,音频设备数,支持的最大最小宽度和高度等信息。
struct video_picture
{
       __u16     brightness;
       __u16     hue;
       __u16     colour;
       __u16     contrast;
       __u16     whiteness;       /* Black and white only */
       __u16     depth;            /* Capture depth */
       __u16   palette;    /* Palette in use */
}picture结构包括了亮度,对比度,色深,调色板等等信息。头文件里还列出了palette相关的值,这里并没有给出。
       了解了以上也就了解了这两个简单函数的作用,现在我们已经获取到了相关视频设备的capabilty和picture属性。
这里直接给出另外一个函数
int v4l_get_mbuf(v4l_device *vd)  
{  
   if (ioctl(vd->fd, VIDIOCGMBUG ,&(vd->mbuf)) < 0) {  
      perror("v4l_get_mbuf:");  
      return -1;  
   }  
   return 0;  
}
对于结构体video_mbuf在v4l中的定义如下,video_mbuf结构体是为了服务使用mmap内存映射来获取图像的方法而设置的结构体,通过这个结构体可以获得摄像头设备存储图像的内存大小。具体的定义如下,各变量的使用也会在下文详细说明。
struct video_mbuf
{
       int   size;        可映射的摄像头内存大小
       int   frames;    摄像头可同时存储的帧数
       int   offsets[VIDEO_MAX_FRAME];每一帧图像的偏移量
};
第三步:
下面完成第3步按照需要更改设备的相应设置,事实上可以更改的设置很多,本文以更改picture属性为例说明更改属性的一般方法。
       那么我们就完成extern int v4l_set_picture(v4l_device *, int, int, int, int, int,);这个函数吧
int v4l_set_picture(v4l_device *vd,int br,int hue,int col,int cont,int white)
{
   if(br) vd->picture.brightnesss=br;
   if(hue) vd->picture.hue=hue;
   if(col) vd->picture.color=col;
   if(cont) vd->picture.contrast=cont;
   if(white) vd->picture.whiteness=white;
   if(ioctl(vd->fd,VIDIOCSPICT,&(vd->picture))map),size)==0)return -1;
   return 0;
}
该函数的使用也很简单,就是给出图像数据的大小,vd->map所指向的数据就是图像数据。而图像数据的大小你要根据设备的属性自己计算获得。
2).使用mmap内存映射来获取图像
在这部分涉及到下面几个函数,它们配合来完成最终图像采集的功能。
extern int v4l_mmap_init(v4l_device *);该函数把摄像头图像数据映射到进程内存中,也就是只要使用vd->map指针就可以使用采集到的图像数据(下文详细说明)
extern int v4l_grab_init(v4l_device *, int, int);该函数完成图像采集前的初始化工作。
extern int v4l_grab_frame(v4l_device *, int);该函数是真正完成图像采集的一步,在本文使用了一个通常都会使用的一个小技巧,可以在处理一帧数据时同时采集下一帧的数据,因为通常我们使用的摄像头都可以至少存储两帧的数据。
extern int v4l_grab_sync(v4l_device *);该函数用来完成截取图像的同步工作,在截取一帧图像后调用,返回表明一帧截取结束。
下面分别介绍这几个函数。
mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必在调用read(),write()等操作。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时访问进程B对共享内存中数据的更新,反之亦然。
采用共享内存通信的一个显而易见的好处是减少I/O操作提高读取效率,因为使用mmap后进程可以直接读取内存而不需要任何数据的拷贝。
mmap的函数原型如下
void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset )
addr:共享内存的起始地址,一般设为0,表示由系统分配。
len:指定映射内存的大小。在我们这里,该值为摄像头mbuf结构体的size值,即图像数据的总大小。

port:指定共享内存的访问权限 PROT_READ(可读),PROT_WRITE(可写)
flags:一般设置为MAP_SHARED
fd:同享文件的文件描述符。
介绍完了mmap的使用,就可以介绍上文中定义的函数extern int v4l_mmap_init(v4l_device *);了。先给出这个函数的代码,再做说明。
int v4l_mmap_init(v4l_device *vd)  
{  
   if (v4l_get_mbuf(vd) < 0)  
   return -1;  
   if ((vd->map = mmap(0, vd->mbuf.size, PROT_READ|PROT_WRITE, MAP_SHARED, vd->fd, 0)) < 0) {  
      perror("v4l_mmap_init:mmap");  
      return -1;  
   }  
   return 0;  
}
这个函数首先使用v4l_get_mbuf(vd)获得一个摄像头重要的参数,就是需要映射内存的大小,即vd->mbuf.size,然后调用mmap,当我们在编程是调用v4l_mmap_init后,vd.map指针所指向的内存空间即为我们将要采集的图像数据。
获得图像前的初始化工作v4l_grab_init();该函数十分简单直接粘上去,其中将。vd->frame_using[0]和vd->frame_using[1]都设为FALSE,表示两帧的截取都没有开始。
int v4l_grab_init(v4l_device *vd, int width, int height)  
{  
   vd->mmap.width = width;   
   vd->mmap.height = height;   
   vd->mmap.format = vd->picture.palette;   
   vd->frame_current = 0;  
   vd->frame_using[0] = FALSE;  
   vd->frame_using[1] = FALSE;  
   return v4l_grab_frame(vd, 0);  
}
真正获得图像的函数extern int v4l_grab_frame(v4l_device *, int);
int v4l_grab_frame(v4l_device *vd, int frame)  
{  
   if (vd->frame_using[frame]) {  
      fprintf(stderr, "v4l_grab_frame: frame %d is already used./n", frame);  
      return -1;  
   }  
  
   vd->mmap.frame = frame;  
   if (ioctl(vd->fd, VIDIOCMCAPTURE, &(vd->mmap)) < 0) {  
      perror("v4l_grab_frame");  
      return -1;  
   }  
   vd->frame_using[frame] = TRUE;  
   vd->frame_current = frame;  
   return 0;  
}  
读到这里,应该觉得这个函数也是相当的简单。最关键的一步即为调用ioctl(vd->fd, VIDIOCMCAPTURE, &(vd->mmap)),调用后相应的图像就已经获取完毕。其他的代码是为了完成双缓冲就是截取两帧图像用的,可以自己理解下。
在截取图像后还要进行同步操作,就是调用extern int v4l_grab_sync(v4l_device *);函数,该函数如下
int v4l_grab_sync(v4l_device *vd)  
{  
   if (ioctl(vd->fd, VIDIOCSYNC, &(vd->frame_current)) < 0) {  
      perror("v4l_grab_sync");  
   }  
   vd->frame_using[vd->frame_current] = FALSE;  
   return 0;  
}  
该函数返回0说明你想要获取的图像帧已经获取完毕。
图像存在了哪里?
最终我们使用v4l的目的是为了获取设备中的图像,那么图像存在哪里?从上面的文章可以知道,vd.map指针所指就是你要获得的第一帧图像。图像的位置,存在vd.map+vd.mbuf.offsets[vd.frame_current]处。其中vd.frame_current=0,即为第一帧的位置,vd.frame_current=1,为第二帧的位置

鲁班 Java架构师VIP课程一期共89G视频教程 luban it教程下载:http://www.77cxw.com/download/78
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|网站地图|java自学教程|www.konglongmei.com

GMT+8, 2020-7-13 09:56 , Processed in 0.097656 second(s), 27 queries .

快速回复 返回顶部 返回列表