从深圳回来已经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. Swift得知——使用和分类功能(四)

    Swift得知--使用和分类功能(四) 总结Swift该功能使用的总可分为七类 1 ---- 没有返回值,没有參数的函数 2 ---- 有參数和返回值的函数 3 ---- 使用元祖来返回多个值 4 - ...

  2. 基于Linux平台病毒BlackHole病毒的决心

    今天会见了病毒,少量的代码,但在使用小漏洞的函数的,真正的杀伤力相当惊人. 转载请注明出处:http://blog.csdn.net/u010484477谢谢^_^ watermark/2/text/ ...

  3. Android - JNI加入标准C++文件

    JNI加入标准C++文件 本文地址: http://blog.csdn.net/caroline_wendy 其余參考: http://blog.csdn.net/caroline_wendy/art ...

  4. http层负载均衡之haproxy

    http层负载均衡之haproxy实践篇(一) 方案 上篇文章讲到了负载均衡的相关理论知识,这篇文章我打算讲讲实践方法以及实践中遇到的问题 方案:haproxy http层负载均衡 安装一个hapro ...

  5. TCP连接状态

    TCP 连接状态按 TCP 协议的标准表示法, TCP 可具有如下几种状态,为讨论方便,如下讨论中区分服务端和客户端,实际软件处理上对二者一视同仁. CLOSED关闭状态.在两个通信端使用“三路握手” ...

  6. 下载的youtube视频

    youtube 视频下载方法[详解]   1.打开网址: http://kej.tw/flvretriever/. 2.输入要下载的youtube视频的网址. 3.点击右侧RETRIEVE NOW ! ...

  7. 《浪潮之巅》完全系类——IT人士必读经典

    浪潮之巅 第一章 帝国的余辉(AT&T) 浪潮之巅第二章 — 蓝色巨人(IBM) 浪潮之巅第三章 — “水果”公司的复兴 (乔布斯和苹果公司) 浪潮之巅第四章 — 计算机工业的生态链 浪潮之巅 ...

  8. 基于OCR的SeeTest框架可行性分析总结

    总的来说相比其他几个免费框架,SeeTest功能更全面和易用,但收费有点昂贵:License 3500/年:多平台和多语言(基于OCR)还需要额外购买,分别是500/Year和1750$/.详情请查看 ...

  9. jQuery邮箱验证正则表达式验证邮箱合法

    if($.trim(email)==''||$.trim(email)=='邮    箱:'||$.trim(email)==null){ alert('邮箱不能为空!'); return false ...

  10. 编译预处理 -- 带参数的宏定义--【sky原创】

    原文:编译预处理 -- 带参数的宏定义--[sky原创] 如有转载请注明出处   编译预处理  --  带参数的宏定义 前面为输出文件,后面为输入文件 gcc -E -o test.i test.c ...