从深圳回来已经20多天了,除了完善毕业设计程序和论文,其他时间都去玩游戏了。真的是最后的一段时间能够无忧无虑在校园里挥霍自己的青春了。今天完成的答辩,比想象的要简单,一直以来想把我现在的这个流媒体的东西用文字记录下来,但是都去玩了。但是今天开始还是把这些东西都记录下来。其实整个项目最开始接触的是socket编程,用socket写一个很简单的机遇POP3协议的邮件发送程序都觉得沾沾自喜。现在看来但是确实还是很幼稚的。。。

  其实V4L2就是LINUX下的一套API,我刚刚开始接触的时候觉得好难,完全就TMD看不懂啊。。。反正就是各种不靠谱,其实现在看来这些东西不难,其实很简单。只是当时没有决心去做而已。其实大多数的初学者都有我这样的想法,看着这些不熟悉的东西都会很烦躁,沉不住气不想去看。但是事实是多看看多GOOGLE查查基本就能理解了。下面是API的代码解析:

1)打开一个视频设备:

fd = open("/dev/video0", O_RDWR/*|O_NONBLOCK*/, );

  要从摄像头中回去到图像首先当然要打开一个摄像头,在LINUX中对摄像头的操作是对相应的设备文件进行操作实现的。在LINUX的根文件系统中/dev目录有很多设备文件,其中摄像头对应的是viode0,使用open函数打开,O_RDWR表示读写,O_NONBLOCK表示非阻塞,屏蔽掉表示阻塞方式(使用非阻塞方式调用视频设备,即使尚未捕获到视频信息,驱动依旧会把缓存里面的东西返回给应用程序,如果使用阻塞方式调用视频设备,正好相反,在没捕获到视频设备之前,程序会阻塞在OPEN函数所在的位置)open函数返回一个文件描述符fd,以后的程序中就是fd进行操作。

2)ioctl()函数

  具体的ioctl()函数是啥玩意儿我也不知道,百度百科里面说是一种获得设备信息和向设备发送控制参数的手段。那么在我们的代码中就是通过这个函数来和摄像头进行“交互”。比如我要知道这个摄像头是什么型号,有多大的视野,我要获取多大的图像。。。等等,具体设置在后文中会有详细解释。ioctl()函数有3个参数,第一个是前面提到的文件描述符fd,就是我们要操作摄像头。第二个参数是命令,第三个参数是一个结构体,根据第二个参数的不同使用不同的结构体。

a)在这个程序中ioclt函数用到的命令:

VIDIOC_QUERYCAP  //查询设备功能信息

VIDIOC_CROPCAP  //查询驱动的修剪能力

VIDIOC_S_CROP  //设置视频信号的矩形边框

VIDIOC_S_FMT  //设置当前驱动的频捕获格式

VIDIOC_REQBUFS  //分配内存

VIDIOC_QUERYBUF  //把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址

VIDIOC_QBUF  //把数据从缓存中读取出来

VIDIOC_STREAMON  //开始视频流的获取

VIDIOC_STREAMOFF  //结束视频流的获取

VIDIOC_DQBUF  //把数据放回缓存队列

此处可戳这里:http://www.cnblogs.com/xmphoenix/archive/2011/08/20/2147064.html(反正这个V4L2比我写得好)

b)程序中用到的结构体

struct v4l2_capability cap  //返回当前视频设备所支持的功能;

struct v4l2_cropcap cropcap  //设置设备的捕捉能力参数;

struct v4l2_crop crop  //设置窗口的捕捉能力参数;

struct v4l2_format fmt  //设置帧的格式,比如宽度,高度等;

struct v4l2_requestbuffers req  //向驱动申请帧缓冲的请求,里面包含申请的个数;

struct v4l2_buffer buf  //代表驱动中的一帧;

(以上所有的命令和结构体都是在程序中出现的,但是绝对不是完整的,还有很多没有没有一一列举出来)

3)V4L2操作流程

a.打开设备文件:

fd = open("/dev/video0", O_RDWR/*|O_NONBLOCK*/, 0);

使用open函数打开"/dev/video0"这个设备文件可以分为阻塞方式和非阻塞方式,如果使用非阻塞方式调用视频设备,即使尚未捕获到视频信息,驱动依旧会把缓存(DQBUFF)里面的东西返回给应用程序,如果使用阻塞方式调用视频设备,正好相反,在没捕获到视频设备之前,程序会阻塞在OPEN函数所在的位置。

b.查询视频设备支持的功能:

struct v4l2_capability cap;

ioctl(fd, VIDIOC_QUERYCAP, &cap);

利用ioctl()函数来对打开的设备文件而获得的句柄fd进行读写,第二个参数VIDIOC_QUERYCAP是查询设备属性命令,获取到的设备信息保存到 struct v4l2_capability声明的结构体中,通过判断结构体中capabilities成员的值来判断设备文件是不是支持视频捕捉(V4L2_CAP_VIDEO_CAPTURE)等操作。

c.设置设备和窗口参数:

struct v4l2_cropcap cropcap;

struct v4l2_crop crop;

ioctl(fd, VIDIOC_CROPCAP, &cropcap);

ioctl(fd, VIDIOC_S_CROP, &crop);

同样利用ioctl()函数,VIDIOC_CROPCAP用于查询设备的窗口属性,包括最大窗口左上角坐标和宽高、默认窗口左上角坐标和宽高等属性,根据查询到的值再使用VIDIOC_S_CROP命令和struct v4l2_crop声明的结构体来设置我们的窗口。

d.设置获取帧的格式:

struct v4l2_format fmt;

ioctl(fd, VIDIOC_S_FMT, &fmt);

先是为声明的结构体变量fmt即帧的属性赋值,包括帧类型、宽、高、帧的数据存储类型(YUV\RGB)等,然后是ioctl()函数和对应的命令VIDIOC_S_FMT进行设置。

e.向驱动申请帧缓冲和地址映射:

struct v4l2_requestbuffers req;

struct v4l2_buffer buf;

buffers = calloc(req.count, sizeof(*buffers));

ioctl(fd, VIDIOC_REQBUFS, &req);

 ioctl(fd, VIDIOC_QUERYBUF, &buf);

mmap(NULL, // start anywhere

                     buf.length,

                     PROT_READ | PROT_WRITE,

                     MAP_SHARED,

                     fd, buf.m.offset);

 ioctl(fd, VIDIOC_QBUF, &buf);

首先是用VIDIOC_REQBUFS命令向驱动申请帧(一般不超过5个),在结构体struct v4l2_requestbuffers有我们需要设置的参数,然后在我们的计算机内存中定义帧空间,用VIDIOC_QUERYBUF命令查询设备中的帧信息,并把查询到的信息保存到struct v4l2_buffer定义的结构体中,使用mmap()函数将设备中的帧的缓存地址一一映射成计算机内存中的绝对地址,然后使用VIDIOC_QBUF命令把这些帧放到缓存中,这样我们就可以方便对这些帧进行读取。

f.采集和处理数据:

enum v4l2_buf_type type;

struct v4l2_buffer queue_buf;

ioctl(fd, VIDIOC_STREAMON, &type);

ioctl(fd, VIDIOC_DQBUF, &queue_buf);

ioctl(fd, VIDIOC_QBUF, &queue_buf);

V4L2有一个数据缓存,存放了前面已经申请数量的缓存数据。数据缓存采用FIFO的方式,当应用程序调用缓存数据时,缓存队列将最先采集到的视频数据送出,并重新回到缓存队列中等待接收数据。这个过程中需要用到两个命令VIDIOC_DQBUF和VIDIOC_QBUF来送去和放入缓存。当然在这之前我们需要使用VIDIOC_STREAMON来打开视频流。

全部代码如下,我现在发现我封装得特别傻逼,但是还是写下来,有时间再改改,文件是video.c

#include "video.h"

static struct buffer * buffers = NULL;
static unsigned int n_buffers = ; extern findex;
extern fd; int open_device()
{
fd = open("/dev/video0", O_RDWR/*|O_NONBLOCK*/, );
if(- == fd)
{
printf("open error\n");
return -;
}
return ;
} int close_device()
{
if(- == close(fd))
{
printf("close error\n");
return -;
}
return ;
} int init_device()
{
struct v4l2_capability cap;
struct v4l2_cropcap cropcap;
struct v4l2_crop crop;
struct v4l2_format fmt; if(- == ioctl(fd, VIDIOC_QUERYCAP, &cap))
{
printf("querycap error\n");
return -;
}
printf("**************************************************\n");
printf("Driver Name:%s\nCard Name:%s\nBus info:%s\nDriver Version:%u.%u.%u\nCapabilities:%u \n",cap.driver,cap.card,cap.bus_info,(cap.version>>)&0xff, (cap.version>>)&0xff,cap.version&0xff,cap.capabilities);
printf("**************************************************\n"); if(!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
{
printf("Capture error\n");
return -;
} if(!(cap.capabilities & V4L2_CAP_STREAMING))
{
printf("Streaming error\n");
return -;
} CLEAR(cropcap); cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if( == ioctl(fd, VIDIOC_CROPCAP, &cropcap))
{ CLEAR(crop);
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect;
if(- == ioctl(fd, VIDIOC_S_CROP, &crop))
{
switch (errno)
{
case EINVAL:
printf("not support crop\n");
}
printf("can't set VIDIOC_S_CROP\n");
//return -1;
}
}
CLEAR(fmt); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = ;
fmt.fmt.pix.height = ;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; if(- == ioctl(fd, VIDIOC_S_FMT, &fmt))
{
printf("VIDIOC_S_FMT error\n");
return -;
}
return ;
} int init_mmap()
{
struct v4l2_requestbuffers req;
CLEAR(req); req.count = ;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP; if(- == ioctl(fd, VIDIOC_REQBUFS, &req))
{
printf("VIDIOC_REQBUFS\n");
return -;
} if(req.count < )
{
printf("Insufficient buffer memory\n");
return -;
} buffers = calloc(req.count, sizeof(*buffers)); if(!buffers)
{
printf("out of memory\n");
return -;
} for(n_buffers = ; n_buffers < req.count; ++n_buffers)
{
struct v4l2_buffer buf;
CLEAR(buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffers; if(- == ioctl(fd, VIDIOC_QUERYBUF, &buf))
{
printf("VIDIOC_QUERYBUF\n");
return -;
} buffers[n_buffers].length = buf.length;
buffers[n_buffers].start =
mmap(NULL, // start anywhere
buf.length,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd, buf.m.offset); if(MAP_FAILED == buffers[n_buffers].start)
{
printf("mmap error\n");
return -;
}
}
return ; } int start_capturing()
{
unsigned int i;
for(i = ; i < n_buffers; ++i)
{
struct v4l2_buffer buf;
CLEAR(buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory =V4L2_MEMORY_MMAP;
buf.index = i;
// fprintf(stderr, "n_buffers: %d\n", i); if(- == ioctl(fd, VIDIOC_QBUF, &buf))
{
printf("VIDIOC_QBUF error\n");
return -;
}
} enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if(- == ioctl(fd, VIDIOC_STREAMON, &type))
{
printf("VIDIOC_STREAMON error\n");
return -;
}
return ;
} int stop_capturing()
{
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if(- == ioctl(fd, VIDIOC_STREAMOFF, &type))
{
printf(" VIDIOC_STREAMOFF error\n");
return -;
}
return ;
} int uninit_mmap()
{
unsigned int i;
for(i = ; i < n_buffers; ++i)
{
if(- == munmap(buffers[i].start, buffers[i].length))
{
printf("munmap error\n");
return -;
} }
free(buffers);
return ;
} int get_frame(void **frame_buf, size_t* len)
{
struct v4l2_buffer queue_buf;
CLEAR(queue_buf); queue_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
queue_buf.memory = V4L2_MEMORY_MMAP; if(- == ioctl(fd, VIDIOC_DQBUF, &queue_buf))
{
printf("VIDIOC_DQBUF error\n");
return -;
}
printf("run to here\n");
*frame_buf = buffers[queue_buf.index].start;
*len = buffers[queue_buf.index].length;
findex = queue_buf.index;
return ; } int unget_frame()
{
if(findex != -)
{
struct v4l2_buffer queue_buf;
CLEAR(queue_buf); queue_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
queue_buf.memory = V4L2_MEMORY_MMAP;
queue_buf.index = findex; if(- == ioctl(fd, VIDIOC_QBUF, &queue_buf))
{
printf("VIDIOC_QBUF error\n");
return -;
}
return ;
}
return -;
}

这些代码我忘记当初是在那抄的了!!!!

总结:get_frame的第一个参数是一个双重指针是由于C语言的值传递,在这个函数中获取到了每一帧的开始地址和每一帧的大小,那么就是获取到一帧图像啦!

流媒体:V4L2视频获取的更多相关文章

  1. Linux之V4L2视频采集编程详解

     V4L2(Video For Linux Two) 是内核提供给应用程序访问音.视频驱动的统一接口. Linux系统中,视频设备被当作一个设备文件来看待,设备文件存放在 /dev目录下,完整路径的设 ...

  2. DAVINCI DM6446 开发攻略——V4L2视频驱动和应用分析

     针对DAVINCI DM6446平台,网络上也有很多网友写了V4L2的驱动,但只是解析Montavista linux-2.6.10 V4L2的原理.结构和函数,深度不够.本文决定把Montavis ...

  3. 基于Linux的v4l2视频架构驱动编写(转载)

    转自:http://www.linuxidc.com/Linux/2011-03/33022.htm 其实,我刚开始一直都不知道怎么写驱动,什么都不懂的,只知道我需要在做项目的过程中学习,所以,我就自 ...

  4. 基于Linux的v4l2视频架构驱动编写

    其实,我刚开始一直都不知道怎么写驱动,什么都不懂的,只知道我需要在做项目的过程中学习,所以,我就自己找了一个关于编写Linux下的视频采集监控项目做,然后上学期刚开学的时候听师兄说,跟院长做项目,没做 ...

  5. 9、基于Linux的v4l2视频架构应用编写

    Linux系统中,视频设备被当作一个设备文件来看待,设备文件存放在 /dev目录下,完整路径的设备文件名为: /dev/video0 . 视频采集基本步骤流程如下: 打开视频设备,设置视频设备属性及采 ...

  6. V4L2视频采集原理

    一.简介 Video for Linuxtwo(Video4Linux2)简称V4L2,是V4L的改进版.V4L2是linux操作系统下用于采集图片.视频和音频数据的API接口,配合适当的视频采集设备 ...

  7. javaCV开发详解之12:视频转apng动态图片实现,支持透明通道,也支持摄像机、桌面屏幕、流媒体等视频源转apng动态图

    wjavaCV系列文章: javacv开发详解之1:调用本机摄像头视频 javaCV开发详解之2:推流器实现,推本地摄像头视频到流媒体服务器以及摄像头录制视频功能实现(基于javaCV-FFMPEG. ...

  8. 基于opencv网络摄像头在ubuntu下的视频获取

     基于opencv网络摄像头在ubuntu下的视频获取 1  工具 原料 平台 :UBUNTU12.04 安装库  Opencv-2.3 2  安装编译运行步骤 安装编译opencv-2.3  参 ...

  9. 基于opencv在摄像头ubuntu根据视频获取

     基于opencv在摄像头ubuntu根据视频获取 1  工具 原料 平台 :UBUNTU12.04 安装库  Opencv-2.3 2  安装编译执行步骤 安装编译opencv-2.3  參考h ...

随机推荐

  1. 背包问题 (DP)

    利用记忆化数组.记dp[i][j]为根据rec的定义,从第i个物品开始挑选总重小于j时,总价值的最大值. 递推式: dp[i][j]=0     (j<w[i]) dp[i][j] dp[i][ ...

  2. Studio-Class Diagram

    UML Design Via Visual Studio-Class Diagram 用过几个建模设计工具,小的有staruml,大的有rational rose,EA.最后发现还是Visual St ...

  3. string.format大全

    字符串的数字格式 stringstr1 =string.Format("{0:N1}",56789);               //result: 56,789.0 strin ...

  4. Android开发手册 (Android的手工教程MtAndroid开发手册)

    放出版许可协议 1.0 或者更新版本号. 未经版权全部者明白授权,禁止发行本文档及其被实质上改动的版本号.  未经版权全部者事先授权.禁止将此作品及其衍生作品以标准(纸质)书籍形式发行. 假设有兴趣再 ...

  5. java_tomcat_Server at localhost was unable to start within 45 seconds 小喵咪死活启动报错-二

    错误:Server Tomcat v6.0 Server at localhost was unable to start within 45 seconds 错误提示就是我们限定了部署的时间导致的错 ...

  6. Atitit.异步编程 java .net php python js 对照

    Atitit.异步编程 java .net php python js 的比較 1. 1.异步任务,异步模式,  APM模式,,  EAP模式, TAP 1 1.1.       APM模式: Beg ...

  7. struts2 模型分配问题和延迟加载问题

    傅型号值问题: 首先须要说明的是:Action在请求到达ActionProxy时已经创建出来了,而且对应的创建了一个值栈. 在拦截器到达之前这个图片已经OK了.Action已经创建.并且压入了值栈vs ...

  8. Mac下Android配置及unity3d的导出Android

    昨晚实在弄的太晚了,费尽脑汁才弄出来. ok,关于mac下的eclipse的安卓配置,我仅仅贴一个网址,就ok了 http://developer.android.com/sdk/index.html ...

  9. java程序连接MongoDB副本集测试

    三个节点有一个节点挂掉也不会影响应用程序客户端对整个副本集的读写! public class TestMongoDBReplSet { public static void main(String[] ...

  10. 小言HTTP Authentication

    什么是Authentication? 首先解释两个长的非常像.easy混淆的单词,Authentication(鉴定.认证)和Authorization(授权). Authentication就是要证 ...