精华内容
下载资源
问答
  • V4L2编程代码

    2014-05-11 21:03:46
    V4L2编程初体验 关于 驱动的编程实现
  • linux V4L2编程拍照

    2019-05-21 15:48:12
    linux环境下基于V4L2实现的拍照程序,包括代码和说明文档,可编译通过
  • 详细介绍使用mmap实现V4L2视频处理机制,包括具体流程,部分参考代码,原理讲解,相信对初学同学有一定帮助
  • V4L2 编程框架

    2012-04-06 14:02:18
    V4L2 较 V4L 有较大的改动,并已成为 2.6 的标准接口,函盖 video\dvb\FM...,多数驱动都在向 V4l2 迁移。更好地了解 V4L2 先从应 用入手,然后再深入到内核中结合物理设备/接口的规范实现相应的驱动。本文先就 V4L2 在...
  • V4L2编程

    2017-06-13 14:06:01
    使用的中星微的摄像头,移植了spcaview进行图像的获取,后来用了2.6.29的核,发现以前移植的spcaview不能用了,后来查了一下,发现2.6.29核采用了UVC的驱动(万能驱动),采用了V4L2框架,而spcaview是基于V4L1的...
     

    转载:

    以前做的智能家居的项目用的是Linux2.6.13的核,使用的中星微的摄像头,移植了spcaview进行图像的获取,后来用了2.6.29的核,发现以前移植的spcaview不能用了,后来查了一下,发现2.6.29核采用了UVC的驱动(万能驱动),采用了V4L2框架,而spcaview是基于V4L1的框架,API接口存在差异。所以需要自己写图片获取的应用程序。

     

    下面主要是一些资料的总结,并给出一个可以结果测试的代码:

     

    一.什么是video4linux
    Video4linux2(简称V4L2),是linux中关于视频设备的内核驱动。在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video0下。

     

    二.一般操作流程(视频设备):

    1.打开设备文件。 int fd=open(”/dev/video0″,O_RDWR);
    2.取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等。VIDIOC_QUERYCAP,struct v4l2_capability
    3.设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等。
    VIDIOC_S_STD,VIDIOC_S_FMT,struct v4l2_std_id,struct v4l2_format
    4.向驱动申请帧缓冲,一般不超过5个。struct v4l2_requestbuffers
    5.将申请到的帧缓冲映射到用户空间,这样就可以直接操作采集到的帧了,而不必去复制。mmap
    6.将申请到的帧缓冲全部入队列,以便存放采集到的数据.VIDIOC_QBUF,struct v4l2_buffer
    7.开始视频的采集。VIDIOC_STREAMON
    8.出队列以取得已采集数据的帧缓冲,取得原始采集数据。VIDIOC_DQBUF
    9.将缓冲重新入队列尾,这样可以循环采集。VIDIOC_QBUF
    10.停止视频的采集。VIDIOC_STREAMOFF
    11.关闭视频设备。close(fd);


    三、常用的结构体(参见/usr/include/linux/videodev2.h):

    struct v4l2_requestbuffers reqbufs;//向驱动申请帧缓冲的请求,里面包含申请的个数
    struct v4l2_capability cap;//这个设备的功能,比如是否是视频输入设备
    struct v4l2_standard std;//视频的制式,比如PAL,NTSC
    struct v4l2_format fmt;//帧的格式,比如宽度,高度等

    struct v4l2_buffer buf;//代表驱动中的一帧
    v4l2_std_id stdid;//视频制式,例如:V4L2_STD_PAL_B
    struct v4l2_queryctrl query;//查询的控制
    struct v4l2_control control;//具体控制的值

     

    下面具体说明开发流程(网上找的)

    打开视频设备

    在V4L2中,视频设备被看做一个文件。使用open函数打开这个设备:

    //用非阻塞模式打开摄像头设备

    intcameraFd;

    cameraFd= open(“/dev/video0″, O_RDWR| O_NONBLOCK, 0);

    //如果用阻塞模式打开摄像头设备,上述代码变为:

    //cameraFd = open(”/dev/video0″, O_RDWR, 0);

    关于阻塞模式和非阻塞模式

    应用程序能够使用阻塞模式或非阻塞模式打开视频设备,如果使用非阻塞模式调用视频设备,即使尚未捕获到信息,驱动依旧会把缓存(DQBUFF)里的东西返回给应用程序。

    设定属性及采集方式

    打开视频设备后,可以设置该视频设备的属性,例如裁剪、缩放等。这一步是可选的。在Linux编程中,一般使用ioctl函数来对设备的I/O通道进行管理:

    extern intioctl(int__fd, unsigned long int__request, …) __THROW;

    __fd:设备的ID,例如刚才用open函数打开视频通道后返回的cameraFd;

    __request:具体的命令标志符。

    在进行V4L2开发中,一般会用到以下的命令标志符:

    1 VIDIOC_REQBUFS:分配内存

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

    3 VIDIOC_QUERYCAP:查询驱动功能

    4 VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式

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

    6 VIDIOC_G_FMT:读取当前驱动的频捕获格式

    7 VIDIOC_TRY_FMT:验证当前驱动的显示格式

    8 VIDIOC_CROPCAP:查询驱动的修剪能力

    9 VIDIOC_S_CROP:设置视频信号的边框

    10 VIDIOC_G_CROP:读取视频信号的边框

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

    12 VIDIOC_DQBUF:把数据放回缓存队列

    13 VIDIOC_STREAMON:开始视频显示函数

    14 VIDIOC_STREAMOFF:结束视频显示函数

    15 VIDIOC_QUERYSTD:检查当前视频设备支持的标准,例如PAL或NTSC。

    这些IO调用,有些是必须的,有些是可选择的。

    检查当前视频设备支持的标准

    在亚洲,一般使用PAL(720X576)制式的摄像头,而欧洲一般使用NTSC(720X480),使用VIDIOC_QUERYSTD来检测:

    v4l2_std_id std;

    do{

    ret= ioctl(fd, VIDIOC_QUERYSTD, &std);

    } while(ret== -1 && errno== EAGAIN);

    switch(std) {

    caseV4L2_STD_NTSC:

    //……

    caseV4L2_STD_PAL:

    //……

    }

    设置视频捕获格式

    当检测完视频设备支持的标准后,还需要设定视频捕获格式:

    structv4l2_format    fmt;

    memset( &fmt, 0, sizeof(fmt) );

    fmt.type= V4L2_BUF_TYPE_VIDEO_CAPTURE;

    fmt.fmt.pix.width= 720;

    fmt.fmt.pix.height= 576;

    fmt.fmt.pix.pixelformat= V4L2_PIX_FMT_YUYV;

    fmt.fmt.pix.field= V4L2_FIELD_INTERLACED;

    if(ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {

    return-1;

    }

    v4l2_format结构体定义如下:

    structv4l2_format

    {

    enumv4l2_buf_type type;    //数据流类型,必须永远是//V4L2_BUF_TYPE_VIDEO_CAPTURE

    union

    {

    structv4l2_pix_format    pix;

    structv4l2_window        win;

    structv4l2_vbi_format    vbi;

    __u8    raw_data[200];

    } fmt;

    };

    structv4l2_pix_format

    {

    __u32                   width;         //宽,必须是16的倍数

    __u32                   height;        //高,必须是16的倍数

    __u32                   pixelformat;   //视频数据存储类型,例如是//YUV4:2:2还是RGB

    enumv4l2_field         field;

    __u32                   bytesperline;

    __u32                   sizeimage;

    enumv4l2_colorspace    colorspace;

    __u32                   priv;

    };

    分配内存

    接下来可以为视频捕获分配内存:

    structv4l2_requestbuffers  req;

    if(ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {

    return-1;

    }

    v4l2_requestbuffers定义如下:

    structv4l2_requestbuffers

    {

    __u32               count;  //缓存数量,也就是说在缓存队列里保持多少张照片

    enumv4l2_buf_type  type;   //数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE

    enumv4l2_memory    memory; // V4L2_MEMORY_MMAP或 V4L2_MEMORY_USERPTR

    __u32               reserved[2];

    };

    获取并记录缓存的物理空间

    使用VIDIOC_REQBUFS,我们获取了req.count个缓存,下一步通过调用VIDIOC_QUERYBUF命令来获取这些缓存的地址,然后使用mmap函数转换成应用程序中的绝对地址,最后把这段缓存放入缓存队列:


    <!--[if !supportLineBreakNewLine]-->
    <!--[endif]-->

    typedef structVideoBuffer{

    void*start;

    size_t  length;

    } VideoBuffer;

     

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

    structv4l2_buffer    buf;

     

    for(numBufs= 0; numBufs< req.count; numBufs++) {

    memset( &buf, 0, sizeof(buf) );

    buf.type= V4L2_BUF_TYPE_VIDEO_CAPTURE;

    buf.memory= V4L2_MEMORY_MMAP;

    buf.index= numBufs;

    //读取缓存

    if(ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {

    return-1;

    }

    buffers[numBufs].length= buf.length;

    //转换成相对地址

    buffers[numBufs].start= mmap(NULL, buf.length,

    PROT_READ| PROT_WRITE,

    MAP_SHARED,

    fd, buf.m.offset);

     

    if(buffers[numBufs].start== MAP_FAILED) {

    return-1;

    }

     

    //放入缓存队列

    if(ioctl(fd, VIDIOC_QBUF, &buf) == -1) {

    return-1;

    }

    }

    关于视频采集方式

    操作系统一般把系统使用的内存划分成用户空间和内核空间,分别由应用程序管理和操作系统管理。应用程序可以直接访问内存的地址,而内核空间存放的是供内核访问的代码和数据,用户不能直接访问。v4l2捕获的数据,最初是存放在内核空间的,这意味着用户不能直接访问该段内存,必须通过某些手段来转换地址。

    一共有三种视频采集方式:使用read、write方式;内存映射方式和用户指针模式。

    read、write方式:在用户空间和内核空间不断拷贝数据,占用了大量用户内存空间,效率不高。

    内存映射方式:把设备里的内存映射到应用程序中的内存控件,直接处理设备内存,这是一种有效的方式。上面的mmap函数就是使用这种方式。

    用户指针模式:内存片段由应用程序自己分配。这点需要在v4l2_requestbuffers里将memory字段设置成V4L2_MEMORY_USERPTR。

    处理采集数据

    V4L2有一个数据缓存,存放req.count数量的缓存数据。数据缓存采用FIFO的方式,当应用程序调用缓存数据时,缓存队列将最先采集到的 视频数据缓存送出,并重新采集一张视频数据。这个过程需要用到两个ioctl命令,VIDIOC_DQBUF和VIDIOC_QBUF:

    structv4l2_buffer buf;

    memset(&buf,0,sizeof(buf));

    buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

    buf.memory=V4L2_MEMORY_MMAP;

    buf.index=0;

    //读取缓存

    if(ioctl(cameraFd, VIDIOC_DQBUF, &buf) == -1)

    {

    return-1;

    }

    //…………视频处理算法

    //重新放入缓存队列

    if(ioctl(cameraFd, VIDIOC_QBUF, &buf) == -1) {

    return-1;

    }

    关闭视频设备

    使用close函数关闭一个视频设备

    close(cameraFd)

    还需要使用munmap方法。

     

    下面是代码,加了一部分注释:

    //#加了点注释

     

    //#Rockie Cheng

     

     

    #include <stdio.h>

    #include <stdlib.h>

    #include <string.h>

    #include <assert.h>

     

    #include <getopt.h>           

     

    #include <fcntl.h>            

    #include <unistd.h>

    #include <errno.h>

    #include <malloc.h>

    #include <sys/stat.h>

    #include <sys/types.h>

    #include <sys/time.h>

    #include <sys/mman.h>

    #include <sys/ioctl.h>

     

    #include <asm/types.h>        

    #include <linux/videodev2.h>

     

    #define CLEAR(x) memset (&(x), 0, sizeof (x))

     

    struct buffer {

            void *                  start;

            size_t                  length;

    };

     

    static char *           dev_name        = "/dev/video0";//摄像头设备名

    static int              fd              = -1;

    struct buffer *         buffers         = NULL;

    static unsigned int     n_buffers       = 0;

     

    FILE *file_fd;

    static unsigned long file_length;

    static unsigned char *file_name;

    //

    //获取一帧数据

    //

    static int read_frame (void)

    {

    struct v4l2_buffer buf;

    unsigned int i;

     

    CLEAR (buf);

    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    buf.memory = V4L2_MEMORY_MMAP;

    int ff = ioctl (fd, VIDIOC_DQBUF, &buf);

    if(ff<0)

    printf("failture\n"); //出列采集的帧缓冲

     

    assert (buf.index < n_buffers);

       printf ("buf.index dq is %d,\n",buf.index);

     

    fwrite(buffers[buf.index].start, buffers[buf.index].length, 1, file_fd); //将其写入文件中

     

    ff=ioctl (fd, VIDIOC_QBUF, &buf); //再将其入列

    if(ff<0)

    printf("failture VIDIOC_QBUF\n");

    return 1;

    }

     

    int main (int argc,char ** argv)

    {

    struct v4l2_capability cap;

    struct v4l2_format fmt;

    unsigned int i;

    enum v4l2_buf_type type;

     

    file_fd = fopen("test-mmap.jpg", "w");//图片文件名

     

    fd = open (dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);//打开设备

     

    int ff=ioctl (fd, VIDIOC_QUERYCAP, &cap);//获取摄像头参数

    if(ff<0)

    printf("failture VIDIOC_QUERYCAP\n");

     

           struct v4l2_fmtdesc fmt1;

            int ret;

           memset(&fmt1, 0, sizeof(fmt1));

           fmt1.index = 0;

           fmt1.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

           while ((ret = ioctl(fd, VIDIOC_ENUM_FMT, &fmt1)) == 0)

           {

                  fmt1.index++;

                  printf("{ pixelformat = '%c%c%c%c', description = '%s' }\n",

                                fmt1.pixelformat & 0xFF, (fmt1.pixelformat >> 8) & 0xFF,

                                (fmt1.pixelformat >> 16) & 0xFF, (fmt1.pixelformat >> 24) & 0xFF,

                                fmt1.description);

                 

           }

    CLEAR (fmt);

    fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    fmt.fmt.pix.width       = 640;

    fmt.fmt.pix.height      = 480;

    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;//V4L2_PIX_FMT_YUYV;//V4L2_PIX_FMT_YVU420;//V4L2_PIX_FMT_YUYV;

    fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;

    ff = ioctl (fd, VIDIOC_S_FMT, &fmt); //设置图像格式

    if(ff<0)

    printf("failture VIDIOC_S_FMT\n");

    file_length = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height; //计算图片大小

     

    struct v4l2_requestbuffers req;

    CLEAR (req);

    req.count               = 1;

    req.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    req.memory              = V4L2_MEMORY_MMAP;

     

    ioctl (fd, VIDIOC_REQBUFS, &req); //申请缓冲,count是申请的数量

    if(ff<0)

    printf("failture VIDIOC_REQBUFS\n");

    if (req.count < 1)

       printf("Insufficient buffer memory\n");

     

    buffers = calloc (req.count, sizeof (*buffers));//内存中建立对应空间

     

    for (n_buffers = 0; 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 (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buf)) //映射用户空间

        printf ("VIDIOC_QUERYBUF error\n");

     

       buffers[n_buffers].length = buf.length;

       buffers[n_buffers].start =

       mmap (NULL /* start anywhere */,    //通过mmap建立映射关系

        buf.length,

        PROT_READ | PROT_WRITE /* required */,

        MAP_SHARED /* recommended */,

        fd, buf.m.offset);

     

       if (MAP_FAILED == buffers[n_buffers].start)

        printf ("mmap failed\n");

            }

     

    for (i = 0; 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;

     

       if (-1 == ioctl (fd, VIDIOC_QBUF, &buf))//申请到的缓冲进入列队

        printf ("VIDIOC_QBUF failed\n");

    }

                   

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

     

    if (-1 == ioctl (fd, VIDIOC_STREAMON, &type)) //开始捕捉图像数据

       printf ("VIDIOC_STREAMON failed\n");

     

    for (;;) //这一段涉及到异步IO

    {

       fd_set fds;

       struct timeval tv;

       int r;

     

       FD_ZERO (&fds);//将指定的文件描述符集清空

       FD_SET (fd, &fds);//在文件描述符集合中增加一个新的文件描述符

     

       /* Timeout. */

       tv.tv_sec = 2;

       tv.tv_usec = 0;

     

       r = select (fd + 1, &fds, NULL, NULL, &tv);//判断是否可读(即摄像头是否准备好),tv是定时

     

       if (-1 == r) {

        if (EINTR == errno)

         continue;

        printf ("select err\n");

                            }

       if (0 == r) {

        fprintf (stderr, "select timeout\n");

        exit (EXIT_FAILURE);

                            }

     

       if (read_frame ())//如果可读,执行read_frame ()函数,并跳出循环

       break;

    }

     

    unmap:

    for (i = 0; i < n_buffers; ++i)

       if (-1 == munmap (buffers[i].start, buffers[i].length))

        printf ("munmap error");

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

            if (-1 == ioctl(fd, VIDIOC_STREAMOFF, &type))  

                printf("VIDIOC_STREAMOFF");

    close (fd);

    fclose (file_fd);

    exit (EXIT_SUCCESS);

    return 0;

    }

     

     

    展开全文
  • Linux下的V4L2的API编程总结

    千次阅读 2016-09-11 11:58:30
    由于工作的需要,经过几天的了解之后,终于熟悉了V4L2的API应用的一个整体框架,在此感谢两位博主的分享,无私贡献这两篇有关V4L2介绍的博客: 博客一: ... 博客二: ...
    由于工作的需要,经过几天的了解之后,终于熟悉了V4L2的API应用的一个整体框架,在此感谢两位博主的分享,无私贡献这两篇有关V4L2介绍的博客:
    
    博客一:
    http://blog.csdn.net/eastmoon502136/article/details/8190262
    博客二:
    http://blog.chinaunix.net/uid-26833883-id-3249346.html

        下面我就直接贴了,在根据这两篇博客的基础上,加以自己的一些注释,希望和我一样的初学者在看有关V4L2的API编程的时候或多或少有些思路吧!

    一:V4L2的应用流程

           Video for Linux two(Video4Linux2)简称V4L2,是V4L的改进版。V4L2是linux操作系统下用于采集图片、视频和音频数据的API接口,配合适当的视频采集设备和相应的驱动程序,可以实现图片、视频、音频等的采集。在远程会议、可视电话、视频监控系统和嵌入式多媒体终端中都有广泛的应用。

        在Linux下,所有外设都被看成一种特殊的文件,成为“设备文件”,可以象访问普通文件一样对其进行读写。一般来说,采用V4L2驱动的摄像头设备文件是/dev/video0。V4L2支持两种方式来采集图像:内存映射方式(mmap)和直接读取方式(read)。V4L2在include/linux/videodev.h文件中定义了一些重要的数据结构,在采集图像的过程中,就是通过对这些数据的操作来获得最终的图像数据。Linux系统V4L2的能力可在Linux内核编译阶段配置,默认情况下都有此开发接口。

           而摄像头所用的主要是capature了,视频的捕捉,具体linux的调用可以参考下图。



    应用程序通过V4L2进行视频采集的原理

        V4L2支持内存映射方式(mmap)和直接读取方式(read)来采集数据,前者一般用于连续视频数据的采集,后者常用于静态图片数据的采集,本文重点讨论内存映射方式的视频采集。

    应用程序通过V4L2接口采集视频数据分为五个步骤:

    首先,打开视频设备文件,进行视频采集的参数初始化,通过V4L2接口设置视频图像的采集窗口、采集的点阵大小和格式;

    其次,申请若干视频采集的帧缓冲区,并将这些帧缓冲区从内核空间映射到用户空间,便于应用程序读取/处理视频数据;

    第三,将申请到的帧缓冲区在视频采集输入队列排队,并启动视频采集;

    第四,驱动开始视频数据的采集,应用程序从视频采集输出队列取出帧缓冲区,处理完后,将帧缓冲区重新放入视频采集输入队列,循环往复采集连续的视频数据;

    第五,停止视频采集。

    具体的程序实现流程可以参考下面的流程图:


        其实其他的都比较简单,就是通过ioctl这个接口去设置一些参数。最主要的就是buf管理。他有一个或者多个输入队列和输出队列。

    启动视频采集后,驱动程序开始采集一帧数据,把采集的数据放入视频采集输入队列的第一个帧缓冲区,一帧数据采集完成,也就是第一个帧缓冲区存满一帧数据后,驱动程序将该帧缓冲区移至视频采集输出队列,等待应用程序从输出队列取出。驱动程序接下来采集下一帧数据,放入第二个帧缓冲区,同样帧缓冲区存满下一帧数据后,被放入视频采集输出队列。

        应用程序从视频采集输出队列中取出含有视频数据的帧缓冲区,处理帧缓冲区中的视频数据,如存储或压缩。

    最后,应用程序将处理完数据的帧缓冲区重新放入视频采集输入队列,这样可以循环采集,如图所示。

     

    每一个帧缓冲区都有一个对应的状态标志变量,其中每一个比特代表一个状态

      V4L2_BUF_FLAG_UNMAPPED 0B0000

      V4L2_BUF_FLAG_MAPPED 0B0001

      V4L2_BUF_FLAG_ENQUEUED 0B0010

      V4L2_BUF_FLAG_DONE 0B0100




    缓冲区的状态转化如图所示。

     

    下面的程序注释的很好,就拿来参考下:


    V4L2 编程

    1. 定义

    V4L2(Video ForLinux Two) 是内核提供给应用程序访问音、视频驱动的统一接口。


    2. 工作流程:

    打开设备-> 检查和设置设备属性->设置帧格式-> 设置一种输入输出方法(缓冲区管理)-> 循环获取数据-> 关闭设备。


    3. 设备的打开和关闭:


    #include

    int open(constchar *device_name, int flags);


    #include 

    int close(intfd);

    例:

    1. int fd=open(“/dev/video0”,O_RDWR);// 打开设备  

    2. close(fd);// 关闭设备  


    注意:V4L2 的相关定义包含在头文件中.


    4. 查询设备属性: VIDIOC_QUERYCAP

    相关函数:

    1. int ioctl(intfd, int request, struct v4l2_capability *argp);  

    相关结构体:

    1. structv4l2_capability  

    2. {  

    3. __u8 driver[16];     // 驱动名字  

    4. __u8 card[32];       // 设备名字  

    5. __u8bus_info[32]; // 设备在系统中的位置  

    6. __u32 version;       // 驱动版本号  

    7. __u32capabilities;  // 设备支持的操作  

    8. __u32reserved[4]; // 保留字段  

    9. };  

    10. capabilities 常用值:  

    11. V4L2_CAP_VIDEO_CAPTURE    // 是否支持图像获取  

    12.    


    例:显示设备信息

    1. structv4l2_capability cap;  

    2. ioctl(fd,VIDIOC_QUERYCAP,&cap);  

    3. printf(“DriverName:%s/nCard Name:%s/nBus info:%s/nDriverVersion:%u.%u.%u/n”,cap.driver,cap.card,cap.bus_info,(cap.version>>16)&0XFF,(cap.version>>8)&0XFF,cap.version&OXFF);  



    5. 帧格式:

    1. VIDIOC_ENUM_FMT// 显示所有支持的格式  

    2. int ioctl(intfd, int request, struct v4l2_fmtdesc *argp);  

    3. structv4l2_fmtdesc  

    4. {  

    5. __u32 index;   // 要查询的格式序号,应用程序设置  

    6. enumv4l2_buf_type type;     // 帧类型,应用程序设置  

    7. __u32 flags;    // 是否为压缩格式  

    8. __u8       description[32];      // 格式名称  

    9. __u32pixelformat; // 格式  

    10. __u32reserved[4]; // 保留  

    11. };  


    例:显示所有支持的格式

    1. structv4l2_fmtdesc fmtdesc;  

    2. fmtdesc.index=0;  

    3. fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;  

    4. printf("Supportformat:/n");  

    5. while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)  

    6. {  

    7. printf("/t%d.%s/n",fmtdesc.index+1,fmtdesc.description);  

    8. fmtdesc.index++;  

    9. }  



    // 查看或设置当前格式

    VIDIOC_G_FMT,VIDIOC_S_FMT

    // 检查是否支持某种格式

    1. VIDIOC_TRY_FMT  

    2. int ioctl(intfd, int request, struct v4l2_format *argp);  

    3. structv4l2_format  

    4. {  

    5. enumv4l2_buf_type type;// 帧类型,应用程序设置  

    6. union fmt  

    7. {  

    8. structv4l2_pix_format pix;// 视频设备使用  

    9. structv4l2_window win;  

    10. structv4l2_vbi_format vbi;  

    11. structv4l2_sliced_vbi_format sliced;  

    12. __u8raw_data[200];  

    13. };  

    14. };  



    1. structv4l2_pix_format  

    2. {  

    3. __u32 width;  // 帧宽,单位像素  

    4. __u32 height;  // 帧高,单位像素  

    5. __u32pixelformat; // 帧格式  

    6. enum v4l2_fieldfield;  

    7. __u32bytesperline;  

    8. __u32 sizeimage;  

    9. enumv4l2_colorspace colorspace;  

    10. __u32 priv;  

    11. };  


    例:显示当前帧的相关信息

    1. structv4l2_format fmt;  

    2. fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;  

    3. ioctl(fd,VIDIOC_G_FMT,&fmt);  

    4. printf(“Currentdata format information:  

    5. /n/twidth:%d/n/theight:%d/n”,fmt.fmt.width,fmt.fmt.height);  

    6. structv4l2_fmtdesc fmtdesc;  

    7. fmtdesc.index=0;  

    8. fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;  

    9. while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)  

    10. {  

    11. if(fmtdesc.pixelformat& fmt.fmt.pixelformat)  

    12. {  

    13. printf(“/tformat:%s/n”,fmtdesc.description);  

    14. break;  

    15. }  

    16. fmtdesc.index++;  

    17. }  


    例:检查是否支持某种帧格式

    1. structv4l2_format fmt;  

    2. fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;  

    3. fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_RGB32;  

    4. if(ioctl(fd,VIDIOC_TRY_FMT,&fmt)==-1)  

    5. if(errno==EINVAL)  

    6. printf(“notsupport format RGB32!/n”);  



    6. 图像的缩放

    1. VIDIOC_CROPCAP  

    2. int ioctl(int fd,int request, struct v4l2_cropcap *argp);  

    3. structv4l2_cropcap  

    4. {  

    5. enumv4l2_buf_type type;// 应用程序设置  

    6. struct v4l2_rectbounds;//     最大边界  

    7. struct v4l2_rectdefrect;// 默认值  

    8. structv4l2_fract pixelaspect;  

    9. };  


    // 设置缩放

    1. VIDIOC_G_CROP,VIDIOC_S_CROP  

    2. int ioctl(intfd, int request, struct v4l2_crop *argp);  

    3. int ioctl(intfd, int request, const struct v4l2_crop *argp);  

    4. struct v4l2_crop  

    5. {  

    6. enumv4l2_buf_type type;// 应用程序设置  

    7. struct v4l2_rectc;  

    8. }  


    7. 申请和管理缓冲区,应用程序和设备有三种交换数据的方法,直接read/write ,内存映射(memorymapping) ,用户指针。这里只讨论 memorymapping.

    // 向设备申请缓冲区

    1. VIDIOC_REQBUFS  

    2. int ioctl(intfd, int request, struct v4l2_requestbuffers *argp);  

    3. structv4l2_requestbuffers  

    4. {  

    5. __u32 count;  // 缓冲区内缓冲帧的数目  

    6. enumv4l2_buf_type type;     // 缓冲帧数据格式  

    7. enum v4l2_memorymemory;       // 区别是内存映射还是用户指针方式  

    8. __u32 reserved[2];  

    9. };  

    10.    

    11. enum v4l2_memoy{V4L2_MEMORY_MMAP,V4L2_MEMORY_USERPTR};  

    12. //count,type,memory都要应用程序设置  


    例:申请一个拥有四个缓冲帧的缓冲区

    1. structv4l2_requestbuffers req;  

    2. req.count=4;  

    3. req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;  

    4. req.memory=V4L2_MEMORY_MMAP;  

    5. ioctl(fd,VIDIOC_REQBUFS,&req);  



    获取缓冲帧的地址,长度:

    VIDIOC_QUERYBUF

    int ioctl(intfd, int request, struct v4l2_buffer *argp);

    1. structv4l2_buffer  

    2. {  

    3. __u32 index;   //buffer 序号  

    4. enumv4l2_buf_type type;     //buffer 类型  

    5. __u32 byteused;     //buffer 中已使用的字节数  

    6. __u32 flags;    // 区分是MMAP 还是USERPTR  

    7. enum v4l2_fieldfield;  

    8. struct timevaltimestamp;// 获取第一个字节时的系统时间  

    9. structv4l2_timecode timecode;  

    10. __u32 sequence;// 队列中的序号  

    11. enum v4l2_memorymemory;//IO 方式,被应用程序设置  

    12. union m  

    13. {  

    14. __u32 offset;// 缓冲帧地址,只对MMAP 有效  

    15. unsigned longuserptr;  

    16. };  

    17. __u32 length;// 缓冲帧长度  

    18. __u32 input;  

    19. __u32 reserved;  

    20. };  



    定义一个结构体来保存每个缓冲帧的地址和长度。

    1. Struct buffer  

    2. {  

    3. void* start;  

    4. unsigned intlength;  

    5. }*buffers;  



    #include

    void *mmap(void*addr, size_t length, int prot, int flags, int fd, off_t offset);

    //addr 映射起始地址,一般为NULL ,让内核自动选择

    //length 被映射内存块的长度

    //prot 标志映射后能否被读写,其值为PROT_EXEC,PROT_READ,PROT_WRITE,PROT_NONE

    //flags 确定此内存映射能否被其他进程共享,MAP_SHARED,MAP_PRIVATE

    //fd,offset, 确定被映射的内存地址

    返回成功映射后的地址,不成功返回MAP_FAILED ((void*)-1);


    int munmap(void*addr, size_t length);// 断开映射

    //addr 为映射后的地址,length 为映射后的内存长度


    例:将四个已申请到的缓冲帧映射到应用程序,用buffers 指针记录。

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

    2. if (!buffers) {  

    3. fprintf (stderr,"Out of memory/n");  

    4. exit(EXIT_FAILURE);  

    5. }  


    // 映射

    1. for (unsignedint n_buffers = 0; n_buffers < req.count; ++n_buffers) {  

    2. struct v4l2_bufferbuf;  

    3. memset(&buf,0,sizeof(buf));  

    4. buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;  

    5. buf.memory =V4L2_MEMORY_MMAP;  

    6. buf.index =n_buffers;  

    7. // 查询序号为n_buffers 的缓冲区,得到其起始物理地址和大小  

    8. if (-1 == ioctl(fd, VIDIOC_QUERYBUF, &buf))  

    9. exit(-1);  

    10. buffers[n_buffers].length= buf.length;  

    11. // 映射内存  

    12. buffers[n_buffers].start=mmap (NULL,buf.length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd, buf.m.offset);  

    13. if (MAP_FAILED== buffers[n_buffers].start)  

    14. exit(-1);  

    15. }  



    8. 缓冲区处理好之后,就可以开始获取数据了

    1. // 启动/ 停止数据流  

    2. VIDIOC_STREAMON,VIDIOC_STREAMOFF  

    3. int ioctl(intfd, int request, const int *argp);  

    4. //argp 为流类型指针,如V4L2_BUF_TYPE_VIDEO_CAPTURE.  

    5. 在开始之前,还应当把缓冲帧放入缓冲队列:  

    6. VIDIOC_QBUF// 把帧放入队列  

    7. VIDIOC_DQBUF// 从队列中取出帧  

    8. int ioctl(intfd, int request, struct v4l2_buffer *argp);  


    例:把四个缓冲帧放入队列,并启动数据流

    1. unsigned int i;  

    2. enum v4l2_buf_typetype;  

    3. // 将缓冲帧放入队列  

    4. for (i = 0; i< 4; ++i)  

    5. {  

    6. structv4l2_buffer buf;  

    7. buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;  

    8. buf.memory =V4L2_MEMORY_MMAP;  

    9. buf.index = i;  

    10. ioctl (fd,VIDIOC_QBUF, &buf);  

    11. }  

    12. type =V4L2_BUF_TYPE_VIDEO_CAPTURE;  

    13. ioctl (fd,VIDIOC_STREAMON, &type);  



    例:获取一帧并处理

    1. structv4l2_buffer buf;  

    2. CLEAR (buf);  

    3. buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;  

    4. buf.memory =V4L2_MEMORY_MMAP;  

    5. // 从缓冲区取出一个缓冲帧  

    6. ioctl (fd,VIDIOC_DQBUF, &buf);  

    7. // 图像处理  

    8. process_image(buffers[buf.index].start);  

    9. // 将取出的缓冲帧放回缓冲区  

    10. ioctl (fd, VIDIOC_QBUF,&buf);  


     二:完整的代码及相应注释如下:
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 

    typedef struct     //定义一个结构体来记录每个缓冲帧映射后的地址和长度。
    {
        void *start;
        int length;
    }BUFTYPE;

    BUFTYPE *user_buf;
    int n_buffer = 0;

    //打开摄像头设备
    int open_camer_device()
    {
        int fd;

         if((fd = open("/dev/video0",O_RDWR | O_NONBLOCK)) < 0)  //以非阻塞的方式打开摄像头,返回摄像头的fd
        {
            perror("Fail to open");
            exit(EXIT_FAILURE);
        } 

        return fd;
    }

    int init_mmap(int fd)
    {
        int i = 0;
        struct v4l2_requestbuffers reqbuf;          //向驱动申请帧缓冲的请求,里面包含申请的个数

        bzero(&reqbuf,sizeof(reqbuf));               //查看Man手册,我们知道,这个是初始化reqbuf这个变量里边的值为0
        reqbuf.count = 4;                            //缓冲区内缓冲帧的数目
        reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   //缓冲帧数据格式
        reqbuf.memory = V4L2_MEMORY_MMAP;            //区别是内存映射还是用户指针方式,在这里是内存映射

        //申请视频缓冲区(这个缓冲区位于内核空间,需要通过mmap映射)
        //这一步操作可能会修改reqbuf.count的值,修改为实际成功申请缓冲区个数
        if(-1 == ioctl(fd,VIDIOC_REQBUFS,&reqbuf))     //向设备申请缓冲区
        {
            perror("Fail to ioctl 'VIDIOC_REQBUFS'");
            exit(EXIT_FAILURE);
        }

        n_buffer = reqbuf.count;  //把实际成功申请缓冲区个数的值赋给n_buffer这个变量,因为在申请的时候可能会修改reqbuf.count的值

        printf("n_buffer = %d\n",n_buffer);

        user_buf = calloc(reqbuf.count,sizeof(*user_buf));   //为这个结构体变量分配内存,这个结构体主要的目的保存的是
                                                                                    //每一个缓冲帧的地址和大小。
        if(user_buf == NULL)
        {
        fprintf(stderr,"Out of memory\n");
        exit(EXIT_FAILURE);
        }

    /*******************************函数注释*********************************/
    //将这些帧缓冲区从内核空间映射到用户空间,便于应用程序读取/处理视频数据;
    //void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
    //addr 映射起始地址,一般为NULL ,让内核自动选择
    //length 被映射内存块的长度
    //prot 标志映射后能否被读写,其值为PROT_EXEC,PROT_READ,PROT_WRITE, PROT_NONE
    //flags 确定此内存映射能否被其他进程共享,MAP_SHARED,MAP_PRIVATE
    //fd,offset, 确定被映射的内存地址
    //返回成功映射后的地址,不成功返回MAP_FAILED ((void*)-1);
    //int munmap(void *addr, size_t length);// 断开映射
    //addr 为映射后的地址,length 为映射后的内存长度
     /*******************************函数注释*********************************/


     //思考:在mmap映射之后,在用户空间可以对视频采集的帧缓冲区进行怎样的操作?能否举个实例?
    //在read_frame()的process_image(user_buf[buf.index].start,user_buf[buf.index].length)中可以找到答案
        for(i = 0; i < reqbuf.count; i ++)
        {
            struct v4l2_buffer buf;

            bzero(&buf,sizeof(buf));
            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            buf.memory = V4L2_MEMORY_MMAP;
            buf.index = i;

        //查询申请到内核缓冲区的信息
            if(-1 == ioctl(fd,VIDIOC_QUERYBUF,&buf))  //通过fd就可以找到内核的缓冲区么?
            {
            perror("Fail to ioctl : VIDIOC_QUERYBUF");
            exit(EXIT_FAILURE);
            }

            user_buf[i].length = buf.length;
            user_buf[i].start =                               //返回映射后的地址
            mmap(                                               //关于mmap的注释,可参考上方
            NULL,/*start anywhere*/
            buf.length,
            PROT_READ | PROT_WRITE,
            MAP_SHARED,
            fd,buf.m.offset
            );
            if(MAP_FAILED == user_buf[i].start)  //MAP_FAILED表示mmap没有成功映射,其返回的值
            {
            perror("Fail to mmap");
            exit(EXIT_FAILURE);
            }
        } 

    return 0;
    }


    /**********************注释*******************************************
    //VIDIOC_ENUM_FMT// 显示所有支持的格式  
    //int ioctl(intfd, int request, struct v4l2_fmtdesc *argp);  
    //structv4l2_fmtdesc  
    //{  
    //__u32 index;   // 要查询的格式序号,应用程序设置  
    //enumv4l2_buf_type type;     // 帧类型,应用程序设置  
    //__u32 flags;    // 是否为压缩格式  
    //__u8       description[32];      // 格式名称  
    //__u32pixelformat; // 格式  
    //__u32reserved[4]; // 保留  
    //};  
    *****************************注释*************************************/
    /****************************注释*********************************************
    //structv4l2_capability  
    //{  
    //__u8 driver[16];     // 驱动名字  
    //__u8 card[32];       // 设备名字  
    //__u8bus_info[32]; // 设备在系统中的位置  
    //__u32 version;       // 驱动版本号  
    //__u32capabilities;  // 设备支持的操作  
    //__u32reserved[4]; // 保留字段  
    //};  
    //capabilities 常用值:  
    //V4L2_CAP_VIDEO_CAPTURE    // 是否支持图像获取
    *******************************注释*******************************************/
    /******************************注释********************************************
    //structv4l2_format  
    //{  
    //enumv4l2_buf_type type;// 帧类型,应用程序设置  
    //union fmt  
    //{  
    //structv4l2_pix_format pix;// 视频设备使用  
    //structv4l2_window win;  
    //structv4l2_vbi_format vbi;  
    //structv4l2_sliced_vbi_format sliced;  
    //__u8raw_data[200];  
    //};  
    //};  
    ********************************注释*******************************************/


    //初始化视频设备
    int init_camer_device(int fd)
    {
        struct v4l2_fmtdesc fmt;              
        struct v4l2_capability cap;         
        struct v4l2_format stream_fmt;       
        int ret;

        //当前视频设备支持的视频格式
        memset(&fmt,0,sizeof(fmt));
        fmt.index = 0;
        fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;


        while((ret = ioctl(fd,VIDIOC_ENUM_FMT,&fmt)) == 0)  
        {
            fmt.index ++ ;


            printf("{pixelformat = %c%c%c%c},description = '%s'\n",
            fmt.pixelformat & 0xff,(fmt.pixelformat >> 8)&0xff,
            (fmt.pixelformat >> 16) & 0xff,(fmt.pixelformat >> 24)&0xff,
            fmt.description);
        }


        //查询设备属性
        ret = ioctl(fd,VIDIOC_QUERYCAP,&cap);
        if(ret < 0){
        perror("FAIL to ioctl VIDIOC_QUERYCAP");
        exit(EXIT_FAILURE);
        }


        //判断是否是一个视频捕捉设备
        if(!(cap.capabilities & V4L2_BUF_TYPE_VIDEO_CAPTURE))
        {
        printf("The Current device is not a video capture device\n");
        exit(EXIT_FAILURE);

        }


        //判断是否支持视频流形式
        if(!(cap.capabilities & V4L2_CAP_STREAMING))
        {
        printf("The Current device does not support streaming i/o\n");
        exit(EXIT_FAILURE);
        }


        //设置摄像头采集数据格式,如设置采集数据的
        //长,宽,图像格式(JPEG,YUYV,MJPEG等格式)
        stream_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        stream_fmt.fmt.pix.width = 680;
        stream_fmt.fmt.pix.height = 480;
        stream_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
        stream_fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

        if(-1 == ioctl(fd,VIDIOC_S_FMT,&stream_fmt))
        {
        perror("Fail to ioctl");
        exit(EXIT_FAILURE);
        }

        //初始化视频采集方式(mmap)
        init_mmap(fd);

        return 0;
    }


    /************************注释**************************************************
    //structv4l2_buffer  
    //{  
    //__u32 index;   //buffer 序号  
    //enumv4l2_buf_type type;     //buffer 类型  
    //__u32 byteused;     //buffer 中已使用的字节数  
    //__u32 flags;    // 区分是MMAP 还是USERPTR  
    //enum v4l2_fieldfield;  
    //struct timevaltimestamp;// 获取第一个字节时的系统时间  
    //structv4l2_timecode timecode;  
    //__u32 sequence;// 队列中的序号  
    //enum v4l2_memorymemory;//IO 方式,被应用程序设置  
    //union m  
    //{  
    //__u32 offset;// 缓冲帧地址,只对MMAP 有效  
    //unsigned longuserptr;  
    //};  
    //__u32 length;// 缓冲帧长度  
    //__u32 input;  
    //__u32 reserved;  
    //};  
    *****************************注释************************************************/


     //开始采集数据
    int start_capturing(int fd)          //启动视频采集后,驱动程序开始采集一帧数据,把采集的数据放入视频采集输入队列的第一个帧缓冲区,
    {                                             //一帧数据采集完成,也就是第一个帧缓冲区存满一帧数据后,驱动程序将该帧缓冲区移至视频采集
          unsigned int i;                     //输出队列,等待应用程序 从输出队列取出。驱动程序接下来采集下一帧数据,放入第二个帧缓冲区,
           enum v4l2_buf_type type;   //同样帧缓冲区存满下一帧数据后,被放入视频采集输出队列。所以在开始采集视频数据之前,
                                                  //我们需要将申请的缓冲区放入视频采集输入队列中排队,这样视频采集输入队列中才有帧缓冲                                                                 //区,这样也才能保存我们才采集的数据
        
        //将申请的内核缓冲区放入视频采集输入队列中排队
        for(i = 0;i < n_buffer;i ++)
        {
            struct v4l2_buffer buf;

            bzero(&buf,sizeof(buf));
            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            buf.memory = V4L2_MEMORY_MMAP;
            buf.index = i;

            if(-1 == ioctl(fd,VIDIOC_QBUF,&buf))       //思考:申请的内核缓冲区和视频采集输入队列排队怎么联系起来的?                                                                                                  //貌似根本就没有与前面申请 的缓冲区,
            {                                                          //不知道V4L2底层驱动是如何实现的,这有待自己进一步去深钻V4L2驱动的实现
                    perror("Fail to ioctl 'VIDIOC_QBUF'");
                    exit(EXIT_FAILURE);
            }

        }


        //开始采集数据
        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;                //在采集数据的时候,它是怎么和视频输入队列中的帧缓冲区联系起来的?                                                                                      //参数中都没有给出,如何联系?
        if(-1 == ioctl(fd,VIDIOC_STREAMON,&type))          //这有得去深钻V4L2的底层驱动的实现,听说ioctl涉及79个回调函数
        {
            printf("i = %d.\n",i);
            perror("Fail to ioctl 'VIDIOC_STREAMON'");
            exit(EXIT_FAILURE);
        }

        return 0;
    }


    //将采集好的数据放到文件中
    int process_image(void *addr,int length)   //思考一下addr是指啥?在read_frame()中可以找到答案,先可以跳过process_image,
    {                                         //直接在read_frame函数中去看,在看的过程中,遇到process_image函数的时候再过来看
        FILE *fp;
        static int num = 0;
        char picture_name[20];

        // sprintf(picture_name,"test.avi");             //将一帧帧的数据存入test.avi文件中
        sprintf(picture_name,"picture%d.jpg",num ++);  //格式化picture_name这个变量

        if((fp = fopen(picture_name,"w")) == NULL)   //这个方式和上述的sprintf结合起来就实现了文件的自动命名,此方法可以借鉴一下
        {
            perror("Fail to fopen");
            exit(EXIT_FAILURE);
        }


        fwrite(addr,length,1,fp);   //从addr写到fp中
        usleep(500);

        fclose(fp);

        return 0;
    }


    int read_frame(int fd)
    {
        struct v4l2_buffer buf;       
        unsigned int i;               

        bzero(&buf,sizeof(buf));
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;

        //从输出队列中去取缓冲区
        if(-1 == ioctl(fd,VIDIOC_DQBUF,&buf)) //buf和fd是怎样的一个连接方式或者说对应关系?也就是不明白,在ioctl中,                                                                                       //是如何把这两者联 系起来的
        {                                                     //归根结底,一个问题就是搞清楚V4L2中的ioctl的具体底层驱动的实现,待需要再去钻吧!
            perror("Fail to ioctl 'VIDIOC_DQBUF'");
            exit(EXIT_FAILURE);
        }


        assert(buf.index < n_buffer);      //assert函数的作用是判断计算表达式 expression ,如果其值为假(即为0),                                                                                    //那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行。

        //读取进程空间的数据到一个文件中
        process_image(user_buf[buf.index].start,user_buf[buf.index].length);

        if(-1 == ioctl(fd,VIDIOC_QBUF,&buf))      //这个的作用是:将视频输出的缓冲帧放回到视频输入的缓冲区中去  
        {
            perror("Fail to ioctl 'VIDIOC_QBUF'");
            exit(EXIT_FAILURE);
        }

        return 1;
    }


    int mainloop(int fd)         //这个mainloop()函数中主要涉及的知识就是select()函数了,可见上一篇我转载的博客

        int count = 10;

        while(count -- > 0)     
        {
            for(;;)
            {
                fd_set fds;
                struct timeval tv;
                int r;

                FD_ZERO(&fds);
                FD_SET(fd,&fds);


                /*Timeout*/
                tv.tv_sec = 2;
                tv.tv_usec = 0;

                r = select(fd + 1,&fds,NULL,NULL,&tv);   


                    if(-1 == r)
                    {
                        if(EINTR == errno)
                        continue;

                        perror("Fail to select");
                        exit(EXIT_FAILURE);
                    }


                    if(0 == r)
                    {
                        fprintf(stderr,"select Timeout\n");
                        exit(EXIT_FAILURE);
                    }


                    if(read_frame(fd))
                    break;

                }

            }

        return 0;

    }


    void stop_capturing(int fd)
    {
        enum v4l2_buf_type type;

        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        if(-1 == ioctl(fd,VIDIOC_STREAMOFF,&type))
        {
            perror("Fail to ioctl 'VIDIOC_STREAMOFF'");
            exit(EXIT_FAILURE);
        }

        return;

    }


    void uninit_camer_device()
    {
        unsigned int i;


        for(i = 0;i < n_buffer;i ++)
        {
            if(-1 == munmap(user_buf[i].start,user_buf[i].length))
            {
                exit(EXIT_FAILURE);
            }

        }

        free(user_buf);

        return;

    }


    void close_camer_device(int fd)
    {
        if(-1 == close(fd))
        {
            perror("Fail to close fd");
            exit(EXIT_FAILURE);
        }

        return;

    }


    int main()
    {
        int fd;       
                             
        fd = open_camer_device();

        init_camer_device(fd);

        start_capturing(fd);

        mainloop(fd);

        stop_capturing(fd);

        uninit_camer_device(fd);

        close_camer_device(fd);

        return 0;

    }

    展开全文
  • Linux摄像头V4L2编程

    千次阅读 2016-06-21 09:49:18
    Linux下V4L2编程,使能摄像头拍照

    一、摘要

        在Linux操作系统下,摄像头的工作都要遵循V4L2的机制,下面介绍如何从无到有,编写代码去使能摄像头拍照。

    使用V4L2基本上就是使用内核提供的函数接口,填充相应的函数,主要依靠ioctl()函数填充函数域,然后按照一定操作顺序(工作流程),就能拍照了。


    二、操作顺序(工作流程)

       前奏:

        1、打开USB摄像头设备文件

        2、获取驱动信息(VIDIOC_QUERYCAP)

        3、设置图像格式(VIDIOC_S_FMT)

       帧缓冲:

        4、申请帧缓冲(VIDIOC_REQBUFS)

        5、获取帧缓冲地址长度信息(VIDIOC_QUERYBUF)

        6、使用mmap吧内核空间的帧缓冲映射到用户空间

        7、帧缓冲入队列

        8、开始采集图像(VIDIOC_STREAMON)

        9、取出帧缓冲(VIDIOC_DQBUF)

        10、访问帧缓冲(可以读写操作等等)

        11、帧缓冲入队列,重新入队(VIDIOC_QBUF)

    写代码的时候按照这个编写就行了。




    下面的代码,直接复制过去,然后gcc编译就能用了,如果是用在嵌入式板子上,只用交叉编译以后就能用了


    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <assert.h>
    #include <getopt.h>           
    #include <fcntl.h>            
    #include <unistd.h>
    #include <errno.h>
    #include <malloc.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <sys/time.h>
    #include <sys/mman.h>
    #include <sys/ioctl.h>
    #include <asm/types.h>        
    #include <linux/videodev2.h>
      
    struct buffer {
            void *                  start;
            size_t                  length;
    };
     
    struct buffer *buffers;
    unsigned long  n_buffers;
    unsigned long file_length;
    
    int file_fd;
    char *dev_name = "/dev/video3";   //这是我摄像头节点,根据自己的节点填写
    int fd;
    
    static int read_frame (void)
    {
         struct v4l2_buffer buf;
         
         /*帧出列*/
         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
         buf.memory = V4L2_MEMORY_MMAP;
         ioctl (fd, VIDIOC_DQBUF, &buf);
    
         write(file_fd,buffers[buf.index].start,buffers[buf.index].length);
         
         /*buf入列*/
         ioctl(fd, VIDIOC_QBUF, &buf);
    
         return 1;
    }
     
    int main (int argc,char ** argv)
    {
         struct v4l2_capability cap;
         struct v4l2_format fmt;
         struct v4l2_requestbuffers req;
         struct v4l2_buffer buf; 
         unsigned int i;
         enum v4l2_buf_type type;
         
         
         file_fd = open("test.jpg", O_RDWR | O_CREAT, 0777); //用来保存图片
        
         fd = open (dev_name, O_RDWR | O_NONBLOCK, 0);
    
         /*获取驱动信息*/
          ioctl (fd, VIDIOC_QUERYCAP, &cap);   //驱动信息保存在cap结构体
          printf("Driver Name:%s\n Card Name:%s\n Bus info:%s\n\n",cap.driver,cap.card,cap.bus_info);
              
         /*设置图像格式*/
         fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;   //这是设置传输流类型
         fmt.fmt.pix.width       = 320;   //设置分辨率
         fmt.fmt.pix.height      = 240;
         fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;
         fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;   //图像格式,此处是jpg
    
         ioctl (fd, VIDIOC_S_FMT, &fmt) ;
          
         /*申请图像缓冲区*/
         req.count               = 4;
         req.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
         req.memory              = V4L2_MEMORY_MMAP;
         ioctl (fd, VIDIOC_REQBUFS, &req);
       
         buffers = calloc (req.count, sizeof (*buffers));
        
      
         for (n_buffers = 0; n_buffers < req.count; ++n_buffers)
         { 
               /*获取图像缓冲区的信息*/
               buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
               buf.memory      = V4L2_MEMORY_MMAP;
               buf.index       = n_buffers;
     
               ioctl (fd, VIDIOC_QUERYBUF, &buf); 
                 
               buffers[n_buffers].length = buf.length; 
               
               // 把内核空间中的图像缓冲区映射到用户空间
              buffers[n_buffers].start = mmap (NULL ,    //通过mmap建立映射关系
                                            buf.length,
                                            PROT_READ | PROT_WRITE ,
                                            MAP_SHARED ,
                                            fd,
                                            buf.m.offset);
         }
       
         /*图像缓冲入队*/ 
           
           for (i = 0; i < n_buffers; ++i)
           {
                   buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                   buf.memory      = V4L2_MEMORY_MMAP;
                   buf.index       = i; 
                   ioctl (fd, VIDIOC_QBUF, &buf);
                   
           }
        
        //开始捕捉图像数据  
        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ioctl (fd, VIDIOC_STREAMON, &type);
    
       fd_set fds;
    
       FD_ZERO (&fds);
       FD_SET (fd, &fds);
    
       select(fd + 1, &fds, NULL, NULL, NULL);
       
       /*读取一幅图像*/
       read_frame();
    
    
       for (i = 0; i < n_buffers; ++i)
          munmap (buffers[i].start, buffers[i].length);   
    
    
       close (fd);
       close (file_fd);
       printf("Camera Done.\n");
    
       return 0;
    }
    


    上面代码可直接使用。




    展开全文
  • 使用V4L2进行视频采集,一般有下面的五个主要步骤: 1.打开设备(一般是/dev/video),进行初始化参数设置,包括分辨率,像素格式等; 2.申请图像帧缓冲,并进行内存映射mmap; 3.把帧缓冲进行入队操作,开始视频流...
  • inux V4L2 Camera 编程

    2020-08-10 21:27:56
    V4L2(Video For Linux Two) 是Linux内核提供给应用程序访问音、视频驱动的统一接口。这里描述的是如何从遵循V4L2规范的Camera设备读取Video帧。 1. 打开设备 int fd = open (“/dev/video0”, O_RDWR | O_...
  • 此示例采用v4l2编程,从摄像头采集到的原始数据yuv422,转换成rgb24格式,并显示在Qt界面上,此外还用jpeg-8b库进行图片压缩。在使用此示例代码前,需安装jpeg库
  • V4L2全称是Video for Linux two(Video4Linux2),是V4L2改进衍生版本。V4L2是linux操作系统下的一个标注化的音频、视频设备驱动框架,向下屏蔽底层设备的差异,向上提供标准统一的访问接口,提高用户在音视频方面的...
  • V4L2(Video For Linux Two) 是内核提供给应用程序访问音、视频驱动的统一接口。也就是Linux系统下视频设备驱动好后,应用程序怎样调用相关的视频设备的编程接口。 2.工作步骤 打开设备-&gt; 检查和设置设备...
  • Linux 之V4L2基本编程

    千次阅读 2017-07-29 20:55:25
    Linux之V4L2基础编程 本文内容来源于网络,本博客进行整理。 1. 定义 V4L2(Video For Linux Two) 是内核提供给应用程序访问音、视频驱动的统一接口。 2. 工作流程: 打开设备-&gt; 检查和设置设备属性-&...
  • V4L2支持三种方式来采集图像,内存映射(mmap),直接读取方式(read)和用户指针,内存映射一般用于连续视频数据的采集,直接读取的方式相对速度慢一些,常用于静态图片数据的采集;用户指针直接传一个buffer指针给...
  • V4L2 驱动随着硬件的变化也越来越复杂,现在大部分设备有里面包含了多个IC, 在/dev目录下不仅要建立 V4L2 的节点,而且还需要建立如:DVB、ALSA、FB、I2C、input等设备节点。事实上 V4L2 驱动需要支持音频/视频的...
  • v4l2学习以及代码

    2014-03-24 01:48:52
    3. 使用V4L2编程: 使用V4L2(Video for Linux 2) API的过程大致如下: Opening the device Changing device properties, selecting a video and audio input,video standard, picture brightness a.  o....
  • 通过上两篇文章,我们已经成功的建立了/dev/video0这个字符设备,此时,在UserSpace就可以打开该设备,完成相应的调用。 ... 总结如何使用V4L2架构建立我们自己的设备驱动,其实就是以下... struct v4l2_device
  • 代码示例: 单平面示例: https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/capture.c.html 多平面示例: https://blog.csdn.net/airk000/article/details/25033269 https://github.com/FFmpeg/FFmpeg ...
  • 使用方法:解压,当然是第一步,有用的文件就两个:v4l2test.c和gm_video.h, 直接gcc v4l2test.c 就行了,生成一个a.out的可执行文件,运行后就会输出一些 提示信息,至于是什么意思,自己看看代码。运行完成后就会...
  • 在移植android中camrea中间件时候,v4l2是必须要掌握的,实际上很多人在移植过程中对v4l2的结构都是似懂非懂,所以在此对v4l2中所涉及到的一些结构体做个详细讲解。 v4l2的api在其官方网站上文档,大家可以下载,大...
  • 该文件是基于v4l2接口编程的从USB摄像头采集图像信息,并在LCD实时显示的代码,里面是纯c写的,由Makefile编译
  • 如果想进一步了解V4L2编程,可以访问V4L2编程帮助手册官网 http://v4l.videotechnology.com/dwg/ 或者在我的网盘上下载 https://pan.baidu.com/s/15gmKHxXrXqT-lCr5HnVYVQ 里面包含了所有关于...
  • Linux下使用V4L2编程获取摄像头设备信息 看懂之后再研究下面的程序: /***************************************************** * 文件名:GetYuyv.c * 文件描述:linux下使用v4l2编程操作摄像头...
  • Linux下的V4L2编程总结

    千次阅读 2017-07-29 15:56:36
    V4L2介绍的博客: 博客一: http://blog.csdn.net/eastmoon502136/article/details/8190262 博客二: http://blog.chinaunix.net/uid-26833883-id-3249346.html  下面我就直接贴了,在根据这两篇博客的基础上,...
  • v4l2采集图像

    2018-05-14 15:02:00
    用于v4l2编程框架下的普通USB摄像头图像采集代码,完成初始化摄像头,创建缓冲buf读取缓冲buf功能
  • Linux V4L2编程

    2018-10-15 16:41:57
    3. 使用V4L2编程: 使用V4L2(Video for Linux 2) API的过程大致如下: Opening the device Changing device properties, selecting a video and audio input,video standard, picture brightness a. o. ...
  • USB摄像头驱动配置及V4L2编程

    千次阅读 2017-06-26 10:25:06
    V4L2核心是Linux系统自带的组件,它可以屏蔽摄像头驱动层的差异,不管底层的摄像头有什么差异,上层应用统一调用V4L2来实现对摄像头的操作。因此驱动程序和应用程序都需要遵循V4L2规范 1.2 摄像头驱动使能 #make ...
  • V4L2框架概述

    万次阅读 多人点赞 2018-06-17 15:25:07
    本文开启 linux 内核 V4L2 框架部分的学习之旅,本文仅先对 V4L2 的框架做一个综述性的概括介绍,然后接下来的文章中会对 V4L2 框架的各个子模块进行一个全面的介绍,包括每一部分的实现原理,如何使用,用在什么...
  • 我最近在Tiny6410进行摄像头的编程时,从网上找了大量的资料进行了学习,可是效果并不好,因为网上的资料大部分都不是很详细,假如给有过V4L2编程经验的人学习还差不多,可是像我这种刚入门的人来说就太麻烦了。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,290
精华内容 916
关键字:

v4l2编程代码