树莓派学习专题<11>:使用V4L2驱动获取摄像头数据--启动/停止数据流,数据捕获,缓存释放

树莓派学习专题<11>:使用V4L2驱动获取摄像头数据--启动/停止数据流,数据捕获,缓存释放

  • [1. 启动和停止数据流](#1. 启动和停止数据流)
  • [2. 捕获数据](#2. 捕获数据)
  • [3. 释放缓存](#3. 释放缓存)

1. 启动和停止数据流

使用命令 VIDIOC_STREAMON 启动摄像头数据流,使用命令 VIDIOC_STREAMOFF 停止摄像头数据流。

这两条命令的定义是:

c 复制代码
#define VIDIOC_STREAMON		 _IOW('V', 18, int)
#define VIDIOC_STREAMOFF	 _IOW('V', 19, int)

这两条命令都需要一个 enum v4l2_buf_type 类型的参数作为入参数。参数内容固定填写 V4L2_BUF_TYPE_VIDEO_CAPTURE

代码如下:

c 复制代码
/*******************************************************************************
- Function    : __StartStream
- Description : 本函数启动数据接收。
- Input       : VOID
- Output      : NULL
- Return      : VOID
- Others      :
*******************************************************************************/
void __StartStream(void)
{
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    
    if(-1 == ioctl(g_iFDVideo, VIDIOC_STREAMON, &type))
    {
        printf("Start stream failed.\n") ;
        exit(-1) ;
    }

    return ;
}


/*******************************************************************************
- Function    : __StopStream
- Description : 本函数停止数据接收。
- Input       : VOID
- Output      : NULL
- Return      : VOID
- Others      :
*******************************************************************************/
void __StopStream(void)
{
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    
    if(-1 == ioctl(g_iFDVideo, VIDIOC_STREAMOFF, &type))
    {
        printf("Stop stream failed.\n") ;
        exit(-1) ;
    }

    return ;
}

2. 捕获数据

代码如下:

c 复制代码
/*******************************************************************************
- Function    : __ReadFrame
- Description : 本函数读取摄像头数据并写入到文件。
- Input       : VOID
- Output      : NULL
- Return      : VOID
- Others      :
*******************************************************************************/
void __ReadFrame(void)
{
    struct v4l2_buffer stBuf;

    memset(&stBuf, 0, sizeof(stBuf)) ;

    stBuf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    stBuf.memory = V4L2_MEMORY_MMAP;
    
    if(-1 == ioctl(g_iFDVideo, VIDIOC_DQBUF, &stBuf))
    {
        printf("De-queue buffer failed.\n") ;
        exit(-1) ;
    }

#if 1 == WRITE_TO_FILE
    if(-1 == write(g_iFDStore, g_pstBufDesc[stBuf.index].pvBufPtr, stBuf.bytesused))
    {
        printf("Write camera data failed.\n") ;
        exit(-1) ;
    }
#endif
    
    if(-1 == ioctl(g_iFDVideo, VIDIOC_QBUF, &stBuf))
    {
        printf("Re-Queue buffer failed.\n") ;
        exit(-1) ;
    }
    
    return ;
}

/*******************************************************************************
- Function    : __CaptureFrame
- Description : 本函数捕获帧数据。
- Input       : VOID
- Output      : NULL
- Return      : VOID
- Others      :
*******************************************************************************/
void __CaptureFrame(void)
{
    struct timeval stTimeVal ;
    fd_set         stFDRead ;
    int            iRetVal ;
    
    stTimeVal.tv_usec = 0 ;
    stTimeVal.tv_sec  = 2 ; 
    
    FD_ZERO(&stFDRead) ;
    FD_SET(g_iFDVideo, &stFDRead) ;

    iRetVal = select(g_iFDVideo + 1, &stFDRead, NULL, NULL, &stTimeVal) ;
    
    if(-1 == iRetVal)
    {
        perror("select") ;
        exit(-1) ;
    }
    else if(0 == iRetVal)
    {
        printf("timeout.\n") ;
        exit(-1) ;
    }
    
    __ReadFrame() ;

    return ;
}

主函数中,首先调用函数 __StartStream 来启动摄像头输出数据的接收。而后调用函数 __CaptureFrame 来捕获一帧数据。使用 select 函数监视摄像头文件描述符的读操作。一旦监视到,则调用 __ReadFrame 函数来获取数据。

对于V4L2驱动,使用 VIDIOC_DQBUF命令来从缓存队列中取出一块就绪的缓存。该命令的定义如下:

c 复制代码
#define VIDIOC_DQBUF		_IOWR('V', 17, struct v4l2_buffer)

命令需要一个struct v4l2_buffer 类型的参数。

结构体中,index 字段指示了当前是那一块缓冲。bytesused 字段指示了缓冲中有多少有效数据量。对于非编码格式,如YUV或RGB系列格式,应该是整个缓冲都用了。使用index 字段可以索引到对应缓存块在用户空间中的地址,从而获取数据。

从队列中取出缓存块后,可对缓存块中数据进行处理,例如写入文件,或者编码等。操作完毕后,还需要将缓存块重新放入到队列中。该操作使用 VIDIOC_QBUF 命令执行。

c 复制代码
#define VIDIOC_QBUF		_IOWR('V', 15, struct v4l2_buffer)

3. 释放缓存

在使用 VIDIOC_STREAMOFF 命令停止摄像头数据流后,退出程序之前,还需要释放缓存。

c 复制代码
/*******************************************************************************
- Function    : __UnmapBuffer
- Description : 本函数撤除缓冲区映射。
- Input       : VOID
- Output      : NULL
- Return      : VOID
- Others      :
*******************************************************************************/
void __UnmapBuffer(void)
{
    int iLoop ;
    
    for(iLoop = 0 ; iLoop < 4 ; iLoop++) 
    {
        munmap(g_pstBufDesc[iLoop].pvBufPtr, g_pstBufDesc[iLoop].szBufSize) ;
    }
    
    free(g_pstBufDesc) ;

    return ;
}

这里包括解除 mmap 映射关系,以及释放描述符。


相关推荐
小弥儿7 小时前
GitHub今日热榜 | 2026-09-18:腾讯双项目上榜
学习·开源
知识分享小能手9 小时前
深度学习学习教程,从入门到精通,深度生成模型 —— 知识点详解与代码实现(20)
人工智能·深度学习·学习
m4Rk_9 小时前
【论文阅读】Agent 记忆机制(74):CompassMem——从相似度检索走向事件图上的记忆导航
论文阅读·人工智能·学习·开源·github
小李不想当小白9 小时前
DMA直接存储器存取(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·分享
2601_9499506311 小时前
练题簿在线练题全流程:从资料导入到模拟考试与错题复盘
学习·考研·小程序·刷题·小程序推荐
别动我齐刘海11 小时前
ROS2 Jazzy + C++ 实战路线——基础学习2
c++·人工智能·vscode·python·学习·机器学习·机器人
水云桐程序员13 小时前
量子力学概论,量子力学概念总结
笔记·科技·学习·量子计算
三克的油15 小时前
java-学习1
java·开发语言·学习
j7~15 小时前
【C++微服务项目开发脚手架】(接口篇一)gflags + gtest + spdlog 接口学习笔记
c++·学习·gtest·项目开发·spdlog·gflags·c++项目微服务开发脚手架
彧azz15 小时前
图的最短路径:Dijkstra与Floyd算法
数据结构·笔记·学习