V4L2应用程序开发(2)

参考资料:韦东山第三期
 
 
V4L2获取数据的操作流程如下:
  • 打开设备
  • ioctl VIDIOC_QUERYCAP:Query Capbility,查询能力
  • 枚举格式、设置格式
  • ioctl VIDIOC_REQBUFS:申请buffer
  • ioctl VIDIOC_QUERYBUF和mmap:查询buffer信息、映射
  • ioctl VIDIOC_QBUF:把buffer放入"空闲链表"
  • ioctl VIDIOC_STREAMON:启动摄像头
  • 这里是一个循环:使用poll/select监测buffer,然后从"完成链表"中取出buffer,处理后再放入"空闲链表"
    • poll/select
    • ioctl VIDIOC_DQBUF:从"完成链表"中取出buffer
    • 处理:前面使用mmap映射了每个buffer的地址,把这个buffer的数据存为文件
    • ioclt VIDIOC_QBUF:把buffer放入"空闲链表"
  • ioctl VIDIOC_STREAMOFF:停止摄像头
 

获取数据demo如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/time.h>
#include <stdint.h>
#include <pthread.h>
#include <linux/videodev2.h>
int stop = 1;
int* thread(void *arg) {
    if(getchar() == 'x')
    stop = 0;
    return NULL;
}

int main(int argc, char* argv[])
{
    //获取当前时间,将其保存到cur_time中去
    struct timeval tv;
    gettimeofday(&tv,NULL);                  //获取1970-1-1到现在的时间结果保存到tv中
    uint64_t sec=tv.tv_sec;
    uint64_t min=tv.tv_sec/60;
    struct tm cur_tm;                        //保存转换后的时间结果
    localtime_r((time_t*)&sec,&cur_tm);
    char cur_time[24];                       //cur_time为本次生成图片的名字
    snprintf(cur_time,24,"%d%02d%02d-%02d:%02d:%02d.avi",cur_tm.tm_year+1900,cur_tm.tm_mon+1,cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min,cur_tm.tm_sec);
    int type;
    int pfd;
    pthread_t thid;
    pthread_create(&thid, NULL, (void *)thread, NULL);

    //1、打开设备
    int fd = open("/dev/video0", O_RDWR);
    if(-1 == fd) {
        perror("打开摄像头失败");
    }

    //2、获取采集格式    
    struct v4l2_fmtdesc v4fmt;
    v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    v4fmt.index = 0;
    int re = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
    if(re < 0) {
        perror("获取失败");
        goto over;
    }
    printf("description=%s\n",v4fmt.description);
    unsigned char *p = (unsigned char *)&v4fmt.pixelformat;
    printf("pixelformat=%c%c%c%c\n",p[0],p[1],p[2],p[3]);
    //printf("reserved=%d\n",v4fmt.res将帧缓冲区出队erved[0]);
    

    //3、设置采集格式
    struct v4l2_format vfmt;
    vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;        //摄像头采集功能
    vfmt.fmt.pix.width = 640;                       //采集的长度
    vfmt.fmt.pix.height = 480;                      //采集的宽度
    vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;  //设置视频采集格式
    int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);
    if(ret < 0) {
        perror("设置采集格式失败");
        goto over;
    }

    /*
    memset(&vfmt, 0, sizeof(vfmt));
    vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ret = ioctl(fd, VIDIOC_G_FMT, &vfmt);
    if(ret < 0) {
        perror("获取失败格式");
    }
    unsigned char *p = (unsigned char *)&vfmt.pixelformat;
    printf("pixelformat=%c%c%c%c\n",p[0],p[1],p[2],p[3]);
    */

    //4、申请帧缓冲队列
    struct v4l2_requestbuffers reqbuf;
    reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.count = 4;                               //申请4个缓冲区
    reqbuf.memory = V4L2_MEMORY_MMAP;               //映射方式为内存映射
    ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuf);       //申请buffer
    if(ret < 0) {
        perror("申请内存缓冲区失败");
        goto over;
    }

    //5、映射缓存区到用户空间
    unsigned char *mptr[4];                         //保存映射后用户空间的首地址
    unsigned int size[4];
    struct v4l2_buffer mapbuf;
    mapbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    for(int i = 0; i < 4; i++) {
        mapbuf.index = i;
        ret = ioctl(fd, VIDIOC_QUERYBUF, &mapbuf);  //从内核空间查询一个做映射
        if(ret < 0) {
            perror("查询内核空间队列失败");
            goto over;
        }
        mptr[i] = (unsigned char *)mmap(NULL, mapbuf.length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, mapbuf.m.offset);
        printf("offset size is %d\n",mapbuf.m.offset);
        size[i] = mapbuf.length;                    //释放映射的时候要知道其缓存区的大小
        //通知内核映射完,buffer放到空闲链表
        ret = ioctl(fd, VIDIOC_QBUF, &mapbuf);
        if(ret < 0) {
            perror("放回失败");
            goto over;
        }
    }

    pfd = open(cur_time,O_WRONLY|O_CREAT|O_APPEND,0644);
    if(-1 == pfd) {
        perror("打开图片失败");
        goto over;
    }

    //6、开始采集数据
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ret = ioctl(fd, VIDIOC_STREAMON, &type);
    if(ret  < 0) {
            perror("开始失败");
            goto over;
    }

    while(stop == 1) {
        //从队列中提取一帧数据  
        struct v4l2_buffer readbuf;
        readbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ret = ioctl(fd, VIDIOC_DQBUF, &readbuf);
        if(ret < 0) {
            perror("读取数据失败");
            goto over;
        }
        //将读取到的数据写入文件中

        ret = write(pfd,mptr[readbuf.index], readbuf.length);
        printf("readbuf.index = %d\n",readbuf.index);
        if(-1 == ret) {
            perror("写入图片失败");
            goto over;
        }
        //告诉内核采集完毕    
        ret = ioctl(fd, VIDIOC_QBUF, &readbuf);
        if(ret < 0) {
            perror("通知失败");
            goto over;
        }
        usleep(30000);
    }

    //8、停止采集
    ret = ioctl(fd, VIDIOC_STREAMOFF, &type);
    if(ret < 0) {
        perror("停止采集失败");
        goto over;
    }

    // 9、做结束工作 释放关闭等
over:
    close(fd);
    close(pfd);
    for(int i = 0; i < 4; i++) {
        printf("%d\n",size[i]);
        munmap(mptr[i],size[i]);
    }
    return 0;
}
 

亮度控制demo:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <linux/types.h>          /* for videodev2.h */
#include <linux/videodev2.h>
#include <poll.h>
#include <sys/mman.h>
#include <pthread.h>

/* ./video_test </dev/video0> */

static void *thread_brightness_control (void *args)        // 创建线程控制亮度,在./video_test /dev/video0的时候终端输入字符,控制亮度信息
{
    int fd = (int)args;

    unsigned char c;
    int brightness;
    int delta;
    
    struct v4l2_queryctrl   qctrl;
    memset(&qctrl, 0, sizeof(qctrl));
    qctrl.id = V4L2_CID_BRIGHTNESS; // V4L2_CID_BASE+0;
    if (0 != ioctl(fd, VIDIOC_QUERYCTRL, &qctrl))
    {
        printf("can not query brightness\n");
        return NULL;
    }

    printf("brightness min = %d, max = %d\n", qctrl.minimum, qctrl.maximum);
    delta = (qctrl.maximum - qctrl.minimum) / 10;
        
    struct v4l2_control ctl;
    ctl.id = V4L2_CID_BRIGHTNESS; // V4L2_CID_BASE+0;
    ioctl(fd, VIDIOC_G_CTRL, &ctl);

    while (1)
    {
        c = getchar();
        if (c == 'u' || c == 'U')
        {
            ctl.value += delta;
        }
        else if (c == 'd' || c == 'D')
        {
            ctl.value -= delta;
        }
        if (ctl.value > qctrl.maximum)
            ctl.value = qctrl.maximum;
        if (ctl.value < qctrl.minimum)
            ctl.value = qctrl.minimum;

        ioctl(fd, VIDIOC_S_CTRL, &ctl);
    }
    return NULL;
}

int main(int argc, char **argv)
{
    int fd;
    struct v4l2_fmtdesc fmtdesc;
    struct v4l2_frmsizeenum fsenum;
    int fmt_index = 0;
    int frame_index = 0;
    int i;
    void *bufs[32];
    int buf_cnt;
    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    struct pollfd fds[1];
    char filename[32];
    int file_cnt = 0;

    if (argc != 2)
    {
        printf("Usage: %s </dev/videoX>, print format detail for video device\n", argv[0]);
        return -1;
    }

    /* open */
    fd = open(argv[1], O_RDWR);
    if (fd < 0)
    {
        printf("can not open %s\n", argv[1]);
        return -1;
    }

    /* 查询能力 */
    struct v4l2_capability cap;
    memset(&cap, 0, sizeof(struct v4l2_capability));
    
    if (0 == ioctl(fd, VIDIOC_QUERYCAP, &cap))
    {        
        if((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {
            fprintf(stderr, "Error opening device %s: video capture not supported.\n",
                    argv[1]);
            return -1;
        }
        
        if(!(cap.capabilities & V4L2_CAP_STREAMING)) {
            fprintf(stderr, "%s does not support streaming i/o\n", argv[1]);
            return -1;
        }
    }
    else
    {
        printf("can not get capability\n");
        return -1;
    }

    while (1)
    {
        /* 枚举格式 */
        fmtdesc.index = fmt_index;  // 比如从0开始
        fmtdesc.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;  // 指定type为"捕获"
        if (0 != ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc))
            break;

        frame_index = 0;
        while (1)
        {
            /* 枚举这种格式所支持的帧大小 */
            memset(&fsenum, 0, sizeof(struct v4l2_frmsizeenum));
            fsenum.pixel_format = fmtdesc.pixelformat;
            fsenum.index = frame_index;

            if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &fsenum) == 0)
            {
                printf("format %s,%d, framesize %d: %d x %d\n", fmtdesc.description, fmtdesc.pixelformat, frame_index, fsenum.discrete.width, fsenum.discrete.height);
            }
            else
            {
                break;
            }

            frame_index++;
        }

        fmt_index++;
    }


    /* 设置格式 */
    struct v4l2_format fmt;
    memset(&fmt, 0, sizeof(struct v4l2_format));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = 1024;
    fmt.fmt.pix.height = 768;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    fmt.fmt.pix.field = V4L2_FIELD_ANY;
    if (0 == ioctl(fd, VIDIOC_S_FMT, &fmt))
    {
        printf("set format ok: %d x %d\n", fmt.fmt.pix.width, fmt.fmt.pix.height);
    }
    else
    {
        printf("can not set format\n");
        return -1;
    }

    /*
     * 申请buffer
     */
    struct v4l2_requestbuffers rb;
    memset(&rb, 0, sizeof(struct v4l2_requestbuffers));
    rb.count = 32;
    rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    rb.memory = V4L2_MEMORY_MMAP;

    if (0 == ioctl(fd, VIDIOC_REQBUFS, &rb))
    {
        /* 申请成功后, mmap这些buffer */
        buf_cnt = rb.count;
        for(i = 0; i < rb.count; i++) {
            struct v4l2_buffer buf;
            memset(&buf, 0, sizeof(struct v4l2_buffer));
            buf.index = i;
            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            buf.memory = V4L2_MEMORY_MMAP;
            if (0 == ioctl(fd, VIDIOC_QUERYBUF, &buf))
            {
                /* mmap */
                bufs[i] = mmap(0 /* start anywhere */ ,
                                  buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
                                  buf.m.offset);
                if(bufs[i] == MAP_FAILED) {
                    perror("Unable to map buffer");
                    return -1;
                }
            }
            else
            {
                printf("can not query buffer\n");
                return -1;
            }            
        }

        printf("map %d buffers ok\n", buf_cnt);
        
    }
    else
    {
        printf("can not request buffers\n");
        return -1;
    }

    /* 把所有buffer放入"空闲链表" */
    for(i = 0; i < buf_cnt; ++i) {
        struct v4l2_buffer buf;
        memset(&buf, 0, sizeof(struct v4l2_buffer));
        buf.index = i;
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        if (0 != ioctl(fd, VIDIOC_QBUF, &buf))
        {
            perror("Unable to queue buffer");
            return -1;
        }
    }
    printf("queue buffers ok\n");

    /* 启动摄像头 */
    if (0 != ioctl(fd, VIDIOC_STREAMON, &type))
    {
        perror("Unable to start capture");
        return -1;
    }
    printf("start capture ok\n");


    /* 创建线程用来控制亮度 */
    pthread_t thread;
    pthread_create(&thread, NULL, thread_brightness_control, (void *)fd);
   

    while (1)
    {
        /* poll */
        memset(fds, 0, sizeof(fds));
        fds[0].fd = fd;
        fds[0].events = POLLIN;
        if (1 == poll(fds, 1, -1))
        {
            /* 把buffer取出队列 */
            struct v4l2_buffer buf;
            memset(&buf, 0, sizeof(struct v4l2_buffer));
            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            buf.memory = V4L2_MEMORY_MMAP;
            
            if (0 != ioctl(fd, VIDIOC_DQBUF, &buf))
            {
                perror("Unable to dequeue buffer");
                return -1;
            }
            
            /* 把buffer的数据存为文件 */
            sprintf(filename, "video_raw_data_%04d.jpg", file_cnt++);
            int fd_file = open(filename, O_RDWR | O_CREAT, 0666);
            if (fd_file < 0)
            {
                printf("can not create file : %s\n", filename);
            }
            printf("capture to %s\n", filename);
            write(fd_file, bufs[buf.index], buf.bytesused);
            close(fd_file);

            /* 把buffer放入队列 */
            if (0 != ioctl(fd, VIDIOC_QBUF, &buf))
            {
                perror("Unable to queue buffer");
                return -1;
            }
        }
    }

    if (0 != ioctl(fd, VIDIOC_STREAMOFF, &type))
    {
        perror("Unable to stop capture");
        return -1;
    }
    printf("stop capture ok\n");
    close(fd);

    return 0;
}

 

 
posted @ 2024-03-26 17:33  lethe1203  阅读(32)  评论(0编辑  收藏  举报