Linux--V4L2应用程序开发(二)获取数据

news/2024/7/8 2:20:53 标签: linux, 运维, 服务器

一、采集数据流程

申请buffer用来放置摄像头数据

  • ioctl VIDIOC_REQBUFS:申请buffer,APP可以申请很多个buffer,但是驱动程序不一定能申请到

  • ioctl VIDIOC_QUERYBUF和mmap:查询buffer信息、映射

    • 如果申请到了N个buffer,这个ioctl就应该执行N次

    • 执行mmap后,APP就可以直接读写这些buffer

  • ioctl VIDIOC_QBUF:把buffer放入"空闲链表"

    • 如果申请到了N个buffer,这个ioctl就应该执行N次

获取数据

  • ioctl VIDIOC_STREAMON:启动摄像头

存储数据

  • 这里是一个循环:使用poll/select监测buffer,然后从"完成链表"中取出buffer,处理后再放入"空闲链表"

    • poll/select

    • ioctl VIDIOC_DQBUF:从"完成链表"中取出buffer

    • 处理:前面使用mmap映射了每个buffer的地址,处理时就可以直接使用地址来访问buffer

    • ioclt VIDIOC_QBUF:把buffer放入"空闲链表"

  • ioctl VIDIOC_STREAMOFF:停止摄像头

二、代码如下:


    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;  

    /*申请buffer*/
    if(0 == ioctl(fd, VIDIOC_REQBUFS,&rb))
    {
        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))/*查询申请到buf是否成功*/
            {
                bufs[i] = mmap(0, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED,fd, buf.m.offset);/*申请成功后,mmap这些buffer*/
                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 ");
    }

    /*把所有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("Uable to queue buffer");
            return -1;
        }
        
    }
    printf("queue buffers ok\n");


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

    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 creat file :%s \n", filename);
            return -1;
        }
        write(fd_file, bufs[buf.index], buf.bytesused);
        close(fd_file);
        /*把buffer放入队列*/
        if(0 != ioctl(fd, VIDIOC_QBUF,&buf))
        {
            perror("Uable to queue buffer");
            return -1;
        }       
        }

    }

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

输出结果为:将每一帧数据采集为jpg格式保存在当前目录下。

三、代码知识点补充

1、mmap()

bufs[i] = mmap(0, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED,fd, buf.m.offset);

        mmap 函数用于将一个文件或设备(如视频设备)的内容映射到进程的地址空间。这样,可以通过指针直接访问文件或设备的内容,而不需要使用系统调用,如 readwrite,从而提高了访问效率。

参数解释

  • addr: 指定映射的起始地址。通常设为 0NULL,表示由内核决定映射区域的起始地址。
  • length: 要映射的文件部分的长度。这里是 buf.length,表示需要映射的缓冲区的大小。
  • prot: 映射区域的保护方式。可以是以下几个值的组合:
    • PROT_READ:页内容可以被读取。
    • PROT_WRITE:页内容可以被写入。
    • PROT_EXEC:页内容可以被执行。
  • flags: 映射对象的类型、映射选项和页是否可以共享等。常用值有:
    • MAP_SHARED:映射区内的写入数据会写回到原文件,同时对其他映射到该文件的进程可见。
    • MAP_PRIVATE:写入数据会产生一个写时拷贝(copy-on-write),对其他映射到该文件的进程不可见。
  • fd: 要映射到内存的文件描述符。这里是视频设备文件描述符 fd
  • offset: 文件映射的起始位置。通常是缓冲区的偏移量,这里是 buf.m.offset

返回值

mmap 成功时返回映射区的指针,失败时返回 MAP_FAILED,并设置 errno 以指示错误。

2、perror()

        perror 函数是 C 标准库中的一个函数,用于输出描述最近一次函数调用发生错误的错误信息。它会根据 errno 的值输出对应的错误消息。errno 是一个全局变量,用于记录最近一次系统调用或库函数调用错误的错误码。

        perror 函数根据 errno 的值,查找并输出对应的错误消息。错误消息通常来自系统定义的一系列错误描述字符串。errno 由最近一次出错的系统调用或标准库函数设置。

  • errno: errno 是由库函数和系统调用设置的全局变量,表示上一次操作的错误码。每个错误码对应一个错误消息。
  • 错误消息查找perror 根据当前 errno 的值,在系统预定义的错误消息列表中查找并输出相应的错误消息。

3、poll()函数

        外部阻塞式,内部监视多路 I/O,系统调用 poll()select()函数很相似,但函数接口有所不同。在 select()函数中,我们提供三个 fd_set 集 合,在每个集合中添加我们关心的文件描述符;而在 poll()函数中,则需要构造一个 struct pollfd 类型的数组,每个数组元素指定一个文件描述符以及我们对该文件描述符所关心的条件(数据可读、可写或异常情况)。

poll 函数的内部轮询主要发生在有多个文件描述符时,它需要检查每一个文件描述符的状态。虽然这种检查会有一定的 CPU 开销,但相比纯轮询而言,开销要小得多,因为在大多数时间里,poll 函数处于阻塞状态,只有在事件发生时才会进行一次所有文件描述符状态的检查,已确定哪些文件描述符上有时间发生。

struct pollfd {
    int fd;         /* 文件描述符 */
    short events;   /* 要监视的事件 */
    short revents;  /* 发生的事件 */
};

4、memset()函数

       函数原型: void *memset(void *s, int c, size_t n);

memset 函数将内存块 s 的前 n 个字节设置为值 c。具体来说,它会把每个字节都设置为 c 的值(取 unsigned char 的值)。通常用于以下情况:

  • 初始化数组或结构体。
  • 重置缓冲区。
  • 为数据结构分配内存后进行清零。

四、遇到的问题

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

在if语句后误加了;分号,导致if语句判断没有执行,而perror会一直执行

我是用Windows上的vscode通过ssh链接Ubuntu开发的,VScode没有报错且交叉编译也通过了,所以执行后一直报错Uable to start capture: Invalid argument


http://www.niftyadmin.cn/n/5536266.html

相关文章

边界无限陈佩文:红蓝对抗安全演练常态化的各方分析

虽然常态化演练尚未正式开始&#xff0c;但我们仍然希望对各方的表现进行一些分析和预测&#xff0c;以辅助我们对市场的判断和决策。同时&#xff0c;也希望通过这些初步的见解&#xff0c;抛砖引玉&#xff0c;引发更多有价值的讨论和观点。 “船停在码头是最安全的&#xf…

table = collections.defaultdict(list)申请的字典的类型是什么?

当你使用 collections.defaultdict(list) 来申请一个字典时&#xff0c;这个字典的类型是 defaultdict&#xff0c;但是其行为和表现方式在某些方面与普通的字典&#xff08;dict&#xff09;相似&#xff0c;主要区别在于它如何处理缺失的键。 defaultdict 是 Python 标准库 …

人工智能系列-numpy(一)

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” Numpy是python语言的一个拓展程序库&#xff0c;支持大量的维度数组与矩阵计算&#xff0c;此外也针对数组运算提供大量的数学函数库 NumPy支持的数据类型比Python内置的类型要…

金融科技在反洗钱领域的创新应用及案例

随着金融科技的迅猛发展&#xff0c;其在反洗钱领域的应用也日益广泛和深入。这些创新应用不仅提高了反洗钱工作的效率和准确性&#xff0c;还为金融机构应对日益复杂的洗钱活动提供了有力支持。下面将详细介绍一些金融科技在反洗钱领域的创新应用案例。 一、AI大模型与知识图谱…

同步时钟:北斗/GPS卫星、电信基站、NTP以太网校时方式的区别

同步时钟是保证各设备时间统一的重要装置&#xff0c;广泛应用于电力、通信、金融、学校、医院、地铁等多个领域。目前&#xff0c;常用的同步时钟方式包括&#xff1a;北斗/GPS卫星、电信基站、NTP以太网等。 下面跟着小编来看一下这些校时方式及他们的区别吧。 1. 北斗/GP…

Three.js机器人与星系动态场景(二):强化三维空间认识

在上篇博客中介绍了如何快速利用react搭建three.js平台&#xff0c;并实现3D模型的可视化。本文将在上一篇的基础上强化坐标系的概念。引入AxesHelper辅助工具及文本绘制工具&#xff0c;带你快速理解camer、坐标系、position、可视区域。 Three.js机器人与星系动态场景&#x…

GPT-5 一年半后发布?我们可能所受影响与应用领域

前言&#xff1a; IT之家6月22日消息&#xff0c;在美国达特茅斯工程学院周四公布的采访中&#xff0c;OpenAI首席技术官米拉穆拉蒂被问及GPT-5是否会在明年发布&#xff0c;给出了肯定答案并表示将在一年半后发布。此外&#xff0c;穆拉蒂在采访中还把GPT-4到GPT-5的飞跃描述…

小白 | 华为云docker设置镜像加速器

一、操作场景 通过docker pull命令下载镜像中心的公有镜像时&#xff0c;往往会因为网络原因而需要很长时间&#xff0c;甚至可能因超时而下载失败。为此&#xff0c;容器镜像服务提供了镜像下载加速功能&#xff0c;帮助您获得更快的下载体验。 二、约束与限制 构建镜像的客…