zoukankan      html  css  js  c++  java
  • v4l2视频采集摄像头

    v4l2 --是Linux内核中关于视频设备的内核驱动框架,为上层访问底层的视频设备提供了统一的接口。
    /dev/vidioX

    1.打开设备文件
    fd=open("/dev/video3",O_RDWR);
    /dev/video3:视频设备文件名
    O_RDWR:可读可写
    fd: open成功反返回文件描述符

    jpeg


    yuv


    2.查询设备支持哪种格式
    struct v4l2_fmtdesc fmt; //查询设备格式所用结构体
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.index = 0;
    int ret;
    //通过ioctl(文件描述符,命令,补充参数(依赖于命令))函数发送命令,成功返回0
    ret = ioctl(fd,VIDIOC_ENUM_FMT,&fmt);
    printf("format:%s ",fmt.description);//成功可显示视频所支持的格式
    3.设置视频格式
    struct v4l2_format s_fmt;
    s_fmt.fmt.pix.width = 650;
    s_fmt.fmt.pix.height = 480;
    s_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;
    s_fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;//4
    s_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ret = ioctl(fd,VIDIOC_S_FMT,&s_fmt);

    4.申请内核态缓冲
    struct v4l2_requestbuffers reqbuf;
    //memset(&reqbuf,0,sizeof(reqbuf));
    reqbuf.count = 1;
    reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.memory = V4L2_MEMORY_MMAP;

    ret = ioctl(fd,VIDIOC_REQBUFS,&reqbuf);

    5.查询内核缓冲
    struct v4l2_buffer buf;
    //memset(&buf,0,sizeof(buf));
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.index = 0;
    ret = ioctl(fd,VIDIOC_QUERYBUF,&buf);

    6.把内核空间分配好的缓冲映射到用户空间
    ub.len = buf.length;
    ub.start = mmap(NULL,buf.length,
    PROT_READ|PROT_WRITE,
    MAP_SHARED,
    fd,buf.m.offset);
    if(ub.start == MAP_FAILED)
    {
    perror("mmap");
    return -1;
    }

    7.添加到采集队列
    ret = ioctl(fd,VIDIOC_QBUF,&buf);

    8.启动视频采集
    enum v4l2_buf_type type;
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ioctl(fd,VIDIOC_STREAMON,&type);

    9. 监测视频采集是否完成
    int select(int nfds, //最大文件描述符加1
    fd_set *readfds,//?
    fd_set *writefds, //0
    fd_set *exceptfds, //0
    struct timeval *timeout);
    10.从队列中取出缓冲
    ioctl(fd,VIDIOC_DQBUF,&buf);
    11.处理图像
    process_image(ub.start,ub.len);
    12.停止/再次采集
    ioctl(fd,VIDIOC_STREAMOFF,&type);/ioctl(fd,VIDIOC_QBUF,&buf);

    13.资源回收
    munmap(ub.start,ub.len);
    close(fd);

    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/ioctl.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/mman.h>
    #include <linux/videodev2.h>

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

    int fd_fb;
    char* pfb;
    char yuyv[640*480*2];
    char rgb[640*480*3];

    struct buffer
    {
    void* start;
    unsigned int length;
    };

    void process_image(struct buffer bb)
    {
    int i, j;
    int y0, u0, y1, v0;
    int r, g, b;
    memcpy(yuyv, bb.start, bb.length);
    //保存原始yuyv数据

    //把yuyv转为rgb数据
    for (i = 0; i < 640*480/2 ; i++)
    {
    y0 = yuyv[i*4];
    y1 = yuyv[i*4+2];
    u0 = yuyv[i*4+1];
    v0 = yuyv[i*4+3];

    //第i*2个点
    b = (1.164383 * (y0 - 16) + 2.017232*(u0 - 128));
    if (b > 255) b = 255;
    if (b < 0) b = 0;
    g= 1.164383 * (y0 - 16) - 0.391762*(u0 - 128) - 0.812968*(v0 - 128);
    if (g > 255) g = 255;
    if (g < 0) g = 0;
    r= 1.164383 * (y0 - 16) + 1.596027*(v0 - 128);
    if (r > 255) r = 255;
    if (r < 0) r = 0;
    rgb[i*3*2 + 0] = b;
    rgb[i*3*2 + 1] = g;
    rgb[i*3*2 + 2] = r;

    //第i*2+1个点
    b = (1.164383 * (y1 - 16) + 2.017232*(u0 - 128));
    if (b > 255) b = 255;
    if (b < 0) b = 0;
    g= 1.164383 * (y1 - 16) - 0.391762*(u0 - 128) - 0.812968*(v0 - 128);
    if (g > 255) g = 255;
    if (g < 0) g = 0;
    r= 1.164383 * (y1 - 16) + 1.596027*(v0 - 128);
    if (r > 255) r = 255;
    if (r < 0) r = 0;
    rgb[i*3*2 + 3] = b;
    rgb[i*3*2 + 4] = g;
    rgb[i*3*2 + 5] = r;
    }

    for ( i = 0; i < 480; i++)
    {
    for(j=0; j<80; j++)
    {
    *(pfb+(i*800+j)*4 + 0) = 0x00;
    *(pfb+(i*800+j)*4 + 1) = 0x00;
    *(pfb+(i*800+j)*4 + 2) = 0x00;
    }
    for (j = 80; j < 720; j++)//????????640
    {
    *(pfb+(i*800+j)*4 + 0) = rgb[(i*640+j-80)*3 + 0];
    *(pfb+(i*800+j)*4 + 1) = rgb[(i*640+j-80)*3 + 1];
    *(pfb+(i*800+j)*4 + 2) = rgb[(i*640+j-80)*3 + 2];
    }
    for(j=720; j<800; j++)
    {
    *(pfb+(i*800+j)*4 + 0) = 0x00;
    *(pfb+(i*800+j)*4 + 1) = 0x00;
    *(pfb+(i*800+j)*4 + 2) = 0x00;
    }
    }
    }

    int main()
    {
    int fd_video;
    //打开摄像头设备
    fd_video = open("/dev/video3", O_RDWR);
    if (fd_video < 0)
    {
    perror("video3 open");
    return fd_video;
    }
    //打开屏幕设备
    fd_fb = open("/dev/fb0", O_RDWR);
    if (fd_fb < 0)
    {
    perror("fb open");
    return fd_fb;
    }
    //设置视频的格式
    struct v4l2_format s_fmt;
    s_fmt.fmt.pix.width = 800;
    s_fmt.fmt.pix.height = 480;
    s_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    //s_fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;//4
    s_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    int flag= ioctl(fd_video,VIDIOC_S_FMT,&s_fmt);
    if(flag != 0)
    {
    printf("set format error ");
    return -1;
    }

    printf("%d %d ",s_fmt.fmt.pix.width,s_fmt.fmt.pix.height);

    //映射
    pfb = (char *)mmap(NULL, 800*480*4, PROT_WRITE|PROT_READ,MAP_SHARED, fd_fb, 0);
    if (pfb == NULL)
    {
    perror ("mmap");
    return -1;
    }
    //此处作用为申请缓冲区
    struct v4l2_requestbuffers req;
    req.count=4;
    req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory=V4L2_MEMORY_MMAP;
    ioctl(fd_video,VIDIOC_REQBUFS,&req);
    //缓冲区与应用程序关联
    struct buffer *buffers;//start, length
    //申请4个struct buffer空间
    buffers = (struct buffer*)calloc (req.count, sizeof (struct buffer));
    if (!buffers)
    {
    perror ("Out of memory");
    exit (EXIT_FAILURE);
    }
    unsigned int n_buffers;
    for (n_buffers = 0; n_buffers < req.count; n_buffers++)
    {
    struct v4l2_buffer buf;
    memset(&buf,0,sizeof(buf));
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.index = n_buffers;
    if (-1 == ioctl (fd_video, VIDIOC_QUERYBUF, &buf))
    exit(-1);
    buffers[n_buffers].length = buf.length;
    buffers[n_buffers].start = mmap (NULL,
    buf.length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd_video, buf.m.offset);
    if (MAP_FAILED == buffers[n_buffers].start)
    exit(-1);
    }

    enum v4l2_buf_type type;
    int i;
    for (i = 0; i < 4; ++i)
    {
    struct v4l2_buffer buf;
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.index = i;
    ioctl (fd_video, VIDIOC_QBUF, &buf);
    }
    while(1)
    {
    //开始捕获图像
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ioctl (fd_video, VIDIOC_STREAMON, &type);


    struct v4l2_buffer buf;
    CLEAR (buf);
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    //取出图像数据
    ioctl (fd_video, VIDIOC_DQBUF, &buf);
    //在LCD屏幕上显示图像
    process_image (buffers[buf.index]); //YUYV
    //告知buf可以使用
    ioctl (fd_video,VIDIOC_QBUF,&buf);
    }

    for (i = 0; i < 4; i++)
    munmap(buffers[i].start, buffers[i].length);
    free(buffers);
    munmap(pfb, 800*480*4);


    close(fd_video);
    close(fd_fb);

    }

    代码不够完善,因为要结束摄像头按CTRL + C 最后面6行不会执行,这样的话不会执行munmap释放映射,free内存,close文件操作,

    可以用信号signal函数SIGINT (ctrl +c)写个信号函数当按下CTRL + C 时处理最后面6行;

    看代码

    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/ioctl.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/mman.h>
    #include <linux/videodev2.h>
    #include <signal.h>

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

    int fd_fb;
    char* pfb;
    int fd_video;
    char yuyv[640*480*2];
    char rgb[640*480*3];
    struct buffer *buffers;//start, length

    struct buffer
    {
    void* start;
    unsigned int length;
    };

    void mysignal(int signo)
    {
    printf("success ");
    int i;
    for (i = 0; i < 4; i++)
    {
    munmap(buffers[i].start, buffers[i].length);
    }
    free(buffers);
    munmap(pfb, 800*480*4);
    close(fd_video);
    close(fd_fb);
    exit(0);
    }

    void process_image(struct buffer bb)
    {
    int i, j;
    int y0, u0, y1, v0;
    int r, g, b;
    memcpy(yuyv, bb.start, bb.length);
    //保存原始yuyv数据

    //把yuyv转为rgb数据
    for (i = 0; i < 640*480/2 ; i++)
    {
    y0 = yuyv[i*4];
    y1 = yuyv[i*4+2];
    u0 = yuyv[i*4+1];
    v0 = yuyv[i*4+3];

    //第i*2个点
    b = (1.164383 * (y0 - 16) + 2.017232*(u0 - 128));
    if (b > 255) b = 255;
    if (b < 0) b = 0;
    g= 1.164383 * (y0 - 16) - 0.391762*(u0 - 128) - 0.812968*(v0 - 128);
    if (g > 255) g = 255;
    if (g < 0) g = 0;
    r= 1.164383 * (y0 - 16) + 1.596027*(v0 - 128);
    if (r > 255) r = 255;
    if (r < 0) r = 0;
    rgb[i*3*2 + 0] = b;
    rgb[i*3*2 + 1] = g;
    rgb[i*3*2 + 2] = r;

    //第i*2+1个点
    b = (1.164383 * (y1 - 16) + 2.017232*(u0 - 128));
    if (b > 255) b = 255;
    if (b < 0) b = 0;
    g= 1.164383 * (y1 - 16) - 0.391762*(u0 - 128) - 0.812968*(v0 - 128);
    if (g > 255) g = 255;
    if (g < 0) g = 0;
    r= 1.164383 * (y1 - 16) + 1.596027*(v0 - 128);
    if (r > 255) r = 255;
    if (r < 0) r = 0;
    rgb[i*3*2 + 3] = b;
    rgb[i*3*2 + 4] = g;
    rgb[i*3*2 + 5] = r;
    }

    for ( i = 0; i < 480; i++)
    {
    for(j=0; j<80; j++)
    {
    *(pfb+(i*800+j)*4 + 0) = 0x00;
    *(pfb+(i*800+j)*4 + 1) = 0x00;
    *(pfb+(i*800+j)*4 + 2) = 0x00;
    }
    for (j = 80; j < 720; j++)//????????640
    {
    *(pfb+(i*800+j)*4 + 0) = rgb[(i*640+j-80)*3 + 0];
    *(pfb+(i*800+j)*4 + 1) = rgb[(i*640+j-80)*3 + 1];
    *(pfb+(i*800+j)*4 + 2) = rgb[(i*640+j-80)*3 + 2];
    }
    for(j=720; j<800; j++)
    {
    *(pfb+(i*800+j)*4 + 0) = 0x00;
    *(pfb+(i*800+j)*4 + 1) = 0x00;
    *(pfb+(i*800+j)*4 + 2) = 0x00;
    }
    }
    }

    int main()
    {
    //打开摄像头设备
    fd_video = open("/dev/video3", O_RDWR);
    if (fd_video < 0)
    {
    perror("video3 open");
    return fd_video;
    }
    //打开屏幕设备
    fd_fb = open("/dev/fb0", O_RDWR);
    if (fd_fb < 0)
    {
    perror("fb open");
    return fd_fb;
    }
    //设置视频的格式
    struct v4l2_format s_fmt;
    s_fmt.fmt.pix.width = 800;
    s_fmt.fmt.pix.height = 480;
    s_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    //s_fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;//4
    s_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    int flag= ioctl(fd_video,VIDIOC_S_FMT,&s_fmt);
    if(flag != 0)
    {
    printf("set format error ");
    return -1;
    }

    printf("%d %d ",s_fmt.fmt.pix.width,s_fmt.fmt.pix.height);

    //映射
    pfb = (char *)mmap(NULL, 800*480*4, PROT_WRITE|PROT_READ,MAP_SHARED, fd_fb, 0);
    if (pfb == NULL)
    {
    perror ("mmap");
    return -1;
    }
    //此处作用为申请缓冲区
    struct v4l2_requestbuffers req;
    req.count=4;
    req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory=V4L2_MEMORY_MMAP;
    ioctl(fd_video,VIDIOC_REQBUFS,&req);
    //缓冲区与应用程序关联

    //申请4个struct buffer空间
    buffers = (struct buffer*)calloc (req.count, sizeof (struct buffer));
    if (!buffers)
    {
    perror ("Out of memory");
    exit (EXIT_FAILURE);
    }
    unsigned int n_buffers;
    for (n_buffers = 0; n_buffers < req.count; n_buffers++)
    {
    struct v4l2_buffer buf;
    memset(&buf,0,sizeof(buf));
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.index = n_buffers;
    if (-1 == ioctl (fd_video, VIDIOC_QUERYBUF, &buf))
    exit(-1);
    buffers[n_buffers].length = buf.length;
    buffers[n_buffers].start = mmap (NULL,
    buf.length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd_video, buf.m.offset);
    if (MAP_FAILED == buffers[n_buffers].start)
    exit(-1);
    }

    enum v4l2_buf_type type;
    int i;
    for (i = 0; i < 4; ++i)
    {
    struct v4l2_buffer buf;
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.index = i;
    ioctl (fd_video, VIDIOC_QBUF, &buf);
    }
    signal(SIGINT,mysignal);
    while(1)
    {
    //开始捕获图像
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ioctl (fd_video, VIDIOC_STREAMON, &type);


    struct v4l2_buffer buf;
    CLEAR (buf);
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    //取出图像数据
    ioctl (fd_video, VIDIOC_DQBUF, &buf);
    //在LCD屏幕上显示图像
    process_image (buffers[buf.index]); //YUYV
    //告知buf可以使用
    ioctl (fd_video,VIDIOC_QBUF,&buf);
    }
    }

  • 相关阅读:
    CSP2019滚粗记
    [总结] wqs二分学习笔记
    [总结] 圆方树学习笔记
    [CF960G] Bandit Blues
    [总结] 第一类斯特林数
    [EOJ629] 两开花
    [CF286E] Ladies' shop
    [总结] 动态DP学习笔记
    [BZOJ3879] SvT
    [总结] 替罪羊树学习笔记
  • 原文地址:https://www.cnblogs.com/liudehao/p/5795582.html
Copyright © 2011-2022 走看看