• v4l2程序实例


    #include <stdio.h>
    #include <string.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sys/mman.h>
    #include <assert.h>
    #include <linux/videodev2.h>
     
     
    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)
    {
    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));
    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;
     
    printf("n_buffer = %d ",n_buffer);
     
    user_buf = calloc(reqbuf.count,sizeof(*user_buf));
    if(user_buf == NULL){
    fprintf(stderr,"Out of memory ");
    exit(EXIT_FAILURE);
    }
     
    //将内核缓冲区映射到用户进程空间
    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))
    {
    perror("Fail to ioctl : VIDIOC_QUERYBUF");
    exit(EXIT_FAILURE);
    }
     
    user_buf[i].length = buf.length;
    user_buf[i].start = 
    mmap(
    NULL,/*start anywhere*/
    buf.length,
    PROT_READ | PROT_WRITE,
    MAP_SHARED,
    fd,buf.m.offset
    );
    if(MAP_FAILED == user_buf[i].start)
    {
    perror("Fail to mmap");
    exit(EXIT_FAILURE);
    }
    }
     
    return 0;
    }
     
    //初始化视频设备
    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' ",
    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 ");
    exit(EXIT_FAILURE);
     
    }
     
    //判断是否支持视频流形式
    if(!(cap.capabilities & V4L2_CAP_STREAMING))
    {
    printf("The Current device does not support streaming i/o ");
    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;
    }
     
    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))
    {
    perror("Fail to ioctl 'VIDIOC_QBUF'");
    exit(EXIT_FAILURE);
    }
    }
     
    //开始采集数据
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if(-1 == ioctl(fd,VIDIOC_STREAMON,&type))
    {
    printf("i = %d. ",i);
    perror("Fail to ioctl 'VIDIOC_STREAMON'");
    exit(EXIT_FAILURE);
    }
     
    return 0;
    }
     
    //将采集好的数据放到文件中
    int process_image(void *addr,int length)
    {
    FILE *fp;
    static int num = 0;
    char picture_name[20];
     
    sprintf(picture_name,"picture%d.jpg",num ++);
     
    if((fp = fopen(picture_name,"w")) == NULL)
    {
    perror("Fail to fopen");
    exit(EXIT_FAILURE);
    }
     
    fwrite(addr,length,1,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))
    {
    perror("Fail to ioctl 'VIDIOC_DQBUF'");
    exit(EXIT_FAILURE);
    }
     
    assert(buf.index < n_buffer);
    //读取进程空间的数据到一个文件中
    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)
    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 ");
    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;
    }

    注意:每个摄像头采集出来的数据格式可能不一样,一般支持MJPEG,JPEG格式的摄像头,采集出来的数据可以直接放在文件中,然后用图片查看器显示。
  • 相关阅读:
    Linux 环境下umount, 报 device is busy 的问题分析与解决方法
    WScript与CScript的区别
    20170803上课笔记
    20170802上课随笔
    20170801上课笔记
    20170731上课笔记
    20170729上课笔记
    20170727上课笔记
    20170726上课笔记
    20170725上课笔记
  • 原文地址:https://www.cnblogs.com/cainiaoaixuexi/p/3290437.html
Copyright © 2020-2023  润新知