java自学教程|www.konglongmei.com

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

more +社区更新Forums

more +随机图赏Gallery

Spring 5核心原理与30个类手写实战 PDF 电子书 百度云 网盘下载Spring 5核心原理与30个类手写实战 PDF 电子书 百度云 网盘下载
疯狂Java讲义(第4版) PDF 电子书 百度云 网盘下载疯狂Java讲义(第4版) PDF 电子书 百度云 网盘下载
价值825元 牛客算法通关课程视频教程 第六期 百度云 网盘下载价值825元 牛客算法通关课程视频教程 第六期 百度云 网盘下载
Spring 5核心原理与30个类手写实战+Spring Boot编程思想核心篇pdfSpring 5核心原理与30个类手写实战+Spring Boot编程思想核心篇pdf
Spring Boot编程思想核心篇+Spring 5核心原理与30个类手写实战pdfSpring Boot编程思想核心篇+Spring 5核心原理与30个类手写实战pdf
java电子书]微服务架构设计模式 PDF 电子书 百度云 网盘下载java电子书]微服务架构设计模式 PDF 电子书 百度云 网盘下载

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

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

[复制链接]
123456790 | 显示全部楼层 发表于: 2019-12-3 00:59:48
123456790 发表于: 2019-12-3 00:59:48 | 显示全部楼层 |阅读模式
查看: 54|回复: 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, 2019-12-13 08:20 , Processed in 0.141238 second(s), 47 queries .

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