首页 > 编程语言 >V4L2应用程序开发(2)

V4L2应用程序开发(2)

时间:2024-03-26 17:48:19浏览次数:26  
标签:buffer buf 程序开发 ioctl VIDIOC fd 应用 V4L2

参考资料:韦东山第三期     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;
}

 

 

标签:buffer,buf,程序开发,ioctl,VIDIOC,fd,应用,V4L2
From: https://www.cnblogs.com/lethe1203/p/18097172

相关文章

  • 【Python程序开发】2024年安徽省职业院校技能大赛中职组“Python程序开发”赛项规程
    2023年安徽省职业院校技能大赛(中职组)“Python程序开发”竞赛规程一、赛项名称(一)赛项名称Python程序开发赛项归属产业类型电子信息产业赛项归属专业大类组别专业大类专业类专业代码专业名称中职电子与信息大类电子信息类710101电子信息技术中职电子与信息大类计算机类7......
  • Linux V4l2简单使用
    V4L2:VideoforLinuxtwo,缩写Video4Linux2,是Linux内核中的一个框架,提供了一套用于视频设备驱动程序开发的API。它是一个开放的、通用的、模块化的视频设备驱动程序框架,允许Linux操作系统和应用程序与各种视频设备(如摄像头、视频采集卡等)进行交互。V4L2提供了通用的API,使......
  • V4L2应用程序开发(1)
    参考资料:韦东山第三期 v4l2应用程序开发分为两个部分,数据采集流程和控制流程两个部分 数据采集流程:分为空闲链表和完成链表 驱动程序周而复始地做如下事情:从硬件采集到数据把"空闲链表"取出buffer,把数据存入buffer把含有数据的buffer放入"完成链表"APP也会周而......
  • 无人机反制:低空目标监测预警打击系统之应用场景【知语云智能科技】
    随着科技的不断发展,低空目标监测预警打击系统已成为现代军事与民用安全领域的重要组成部分。知语云智能科技在这一领域深耕多年,凭借其先进的技术实力和创新能力,为众多应用场景提供了高效、精准的解决方案。以下,将详细探讨低空目标监测预警打击系统在20种不同场景中的应用。军......
  • Java面试题:请解释Java中的集合框架?并详细说明各个集合的应用场景
    Java中的集合框架(CollectionFramework)是一组用来存储和管理对象的类和接口的集合,它为开发中常见的数据结构和算法提供了一种统一的、可重用的实现。Java集合框架的主要目的是提供一种灵活、可扩展的方式来存储和操作对象集合,而无需关心底层数据的存储细节。Java集合框架主......
  • 【附源码】Node.js毕业设计个人健康信息记录移动应用app(Express)
    本系统(程序+源码)带文档lw万字以上  文末可获取本课题的源码和程序系统程序文件列表系统的选题背景和意义选题背景:随着科技的进步和互联网的普及,移动应用已经成为人们日常生活中不可或缺的一部分。在健康管理领域,个人健康信息记录移动应用APP的开发和应用也日益受到关注......
  • 打车代驾app软件小程序开发定制
       打车代驾APP软件和小程序软件功能开发,除了基本的功能外还需要添加一些自己的想法,还有按照用户的使用习惯添加功能。   智能调度系统:打车软件会按照市场规律调整,例如用户需求、司机位置和交通状况,实现智能派单,提高效率。   语音导航:为司机提供语音导航功能,方便......
  • 探索计算机视觉的前沿:研究方向与应用前景
    引言计算机视觉作为人工智能领域的一个重要分支,近年来取得了显著的进展。它旨在使计算机能够从图像或多维数据中理解和解释视觉信息。随着深度学习技术的突破,计算机视觉的应用范围不断扩大,从自动驾驶到医疗诊断,其影响力日益增强。本文将探讨计算机视觉的几个主要研究方向及其......
  • C# 退出应用程序
    常见的有如下几种退出的方式this.Close关闭当前窗口如果我们操作的对象是Form时可以采用这种方式退出对应的Form,若不是主窗体的话,是无法退出程序的另外若有托管线程(非主线程),也无法干净地退出privatevoidbtnClose_Click(objectsender,EventArgse){this.close();......
  • 专注驱动器芯片,包括MASTERGAN1TR、VNQ860SP、VNH9013YTR、VN5160STR用于汽车应用
    1、MASTERGAN1TR——半桥驱动器通用DMOS31QFN输出配置:半桥应用:通用接口:-负载类型:容性,感性技术:DMOS导通电阻(典型值):150毫欧电流-输出/通道:10A电流-峰值输出:17A电压-供电:4.75V~9.5V电压-负载:600V(最大)工作温度:-40°C~150°C(TJ)特性:自举电路故障保护:超温,UVLO安装类型......