摄像头应用编程(三):多平面视频采集

news2025/3/7 5:20:38

文章目录

  • 1、前言
  • 2、环境介绍
  • 3、步骤
  • 4、应用程序编写
  • 5、测试
    • 5.1、编译应用程序
    • 5.2、运行应用程序
  • 6、总结

1、前言

在查看摄像头类型时,大致可以分为两类:Video Capture 和 Video Capture Multiplanar。

本次应用程序主要针对类型为Video Capture Multiplanar的相机。

2、环境介绍

rk3566 + ov8858

3、步骤

应用程序编写主要分为以下几个步骤:

1、打开设备节点。

2、查询设备能力。

3、枚举支持的视频格式。

4、设置格式。

5、请求buffer。

6、buffer入队列。

7、打开相机。

8、buffer出队列。

9、保存为yuv文件。

10、buffer重新入队列。

11、关闭相机。

4、应用程序编写

完整的应用程序如下:


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/types.h>          
#include <linux/videodev2.h>
#include <poll.h>
#include <sys/mman.h>
#include <jpeglib.h>
#include <linux/fb.h>

#define FRAME_SIZE_X    640
#define FRAME_SIZE_Y    480

int main(int argc, char **argv)
{
    int fd;
    int ret;
    void *buffers[VIDEO_MAX_FRAME][VIDEO_MAX_PLANES];
    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    int buffer_cnt;
    struct pollfd fds[1];
    int i;
    int num_planes;
    char filename[32];
    int file_cnt = 0;

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

    /* 1. open /dev/video* */
    fd = open(argv[1], O_RDWR);
    if (fd < 0)
    {
        printf("can not open %s\n", argv[1]);
        goto _err;
    }

    /* 2. query capability */
    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_MPLANE) == 0) 
        {
            fprintf(stderr, "Error opening device %s: video capture mplane not supported.\n", argv[1]);
            goto _ioc_querycap_err;
        }

        if(!(cap.capabilities & V4L2_CAP_STREAMING)) 
        {
            fprintf(stderr, "%s does not support streaming i/o\n", argv[1]);
            goto _ioc_querycap_err;
        }
    }
    else
    {
        printf("can not get capability\n");
        goto _ioc_querycap_err;
    }

    /* 3. enum formt */
    struct v4l2_fmtdesc fmtdesc;
    struct v4l2_frmsizeenum fsenum;
    int fmt_index = 0;
    int frame_index = 0;
    while (1)
    {
        fmtdesc.index = fmt_index;  
        fmtdesc.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;  
        if (0 != ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc))
            break;
        // printf("format %s:\n", fmtdesc.description);

        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("\t%d: %d x %d\n", frame_index, fsenum.discrete.width, fsenum.discrete.height);
            }
            else
            {
                break;
            }

            frame_index++;
        }

        fmt_index++;
    }

    /* 4. set formt */
    struct v4l2_format fmt;
    memset(&fmt, 0, sizeof(struct v4l2_format));

    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    fmt.fmt.pix.width = FRAME_SIZE_X;
    fmt.fmt.pix.height = FRAME_SIZE_Y;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_NV21;
    fmt.fmt.pix.field = V4L2_FIELD_ANY;

    if (0 == ioctl(fd, VIDIOC_S_FMT, &fmt))
    {
        num_planes = fmt.fmt.pix_mp.num_planes;
        printf("the final frame-size has been set : %d x %d\n", fmt.fmt.pix.width, fmt.fmt.pix.height);
    }
    else
    {
        printf("can not set format\n");
        goto _ioc_sfmt_err;
    }
    
    /* 5. require buffer */
    struct v4l2_requestbuffers rb;
    memset(&rb, 0, sizeof(struct v4l2_requestbuffers));

    rb.count = 32;
    rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    rb.memory = V4L2_MEMORY_MMAP;

    if (0 == ioctl(fd, VIDIOC_REQBUFS, &rb))
    {
        buffer_cnt = rb.count;

        for(i = 0; i < rb.count; i++) 
        {
            struct v4l2_buffer buf;
            struct v4l2_plane planes[num_planes];
            memset(&buf, 0, sizeof(struct v4l2_buffer));
            memset(&planes, 0, sizeof(planes));

            buf.index = i;
            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
            buf.memory = V4L2_MEMORY_MMAP;
            buf.m.planes = planes;
            buf.length = num_planes;

            if (0 == ioctl(fd, VIDIOC_QUERYBUF, &buf))
            {
                for (int j = 0; j < num_planes; j++)
                {
                    buffers[i][j] = mmap(NULL, buf.m.planes[j].length, PROT_READ | PROT_WRITE, MAP_SHARED,
                        fd, buf.m.planes[j].m.mem_offset);
                    
                    if (buffers[i][j] == MAP_FAILED) 
                    {
                        printf("Unable to map buffer\n");
                        goto _err;
                    }
                }
            }
            else
            {
                printf("can not query buffer\n");
                goto _err;
            }            
        }
    }
    else
    {
        printf("can not request buffers\n");
        goto _ioc_reqbufs_err;
    }

    /* 6. queue buffer */
    for(i = 0; i < buffer_cnt; ++i) 
    {
        struct v4l2_buffer buf;
        struct v4l2_plane planes[num_planes];
        memset(&buf, 0, sizeof(struct v4l2_buffer));
        memset(&planes, 0, sizeof(planes));

        buf.index = i;
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.m.planes = planes;
        buf.length = num_planes;

        if (0 != ioctl(fd, VIDIOC_QBUF, &buf))
        {
            perror("Unable to queue buffer");
            goto _ioc_qbuf_err;
        }
    }

    /* start camera */
    if (0 != ioctl(fd, VIDIOC_STREAMON, &type))
    {
        printf("Unable to start capture\n");
        goto _err;
    }
    printf("start camera ...\n");

    while (1)
    {
        /* poll */
        memset(fds, 0, sizeof(fds));
        fds[0].fd = fd;
        fds[0].events = POLLIN;
        if (1 == poll(fds, 1, -1))
        {
            /* dequeue buffer */
            struct v4l2_buffer buf;
            struct v4l2_plane planes[num_planes];
            memset(&buf, 0, sizeof(struct v4l2_buffer));
            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
            buf.memory = V4L2_MEMORY_MMAP;
            buf.m.planes = planes;
            buf.length = num_planes;

            if (0 != ioctl(fd, VIDIOC_DQBUF, &buf))
            {
                printf("Unable to dequeue buffer\n");
                goto _ioc_dqbuf_err;
            }
            
            /* save as file */
            sprintf(filename, "video_raw_data_%04d.yuv", 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);
            for (int i = 0; i < num_planes; i++)
                write(fd_file, buffers[buf.index][i], buf.m.planes[i].bytesused);
            close(fd_file);

            /* queue buffer */
            if (0 != ioctl(fd, VIDIOC_QBUF, &buf))
            {
                printf("Unable to queue buffer");
                goto _ioc_qbuf_err;
            }
        }
    }
    
    /* close camera */
    if (0 != ioctl(fd, VIDIOC_STREAMOFF, &type))
    {
        printf("Unable to stop capture\n");
        goto _ioc_streamoff_err;
    }
    close(fd);

    return 0;

_ioc_qbuf_err:
_ioc_reqbufs_err:
_ioc_sfmt_err:
_ioc_querycap_err:
_ioc_streamoff_err:
_ioc_dqbuf_err:
_err:
    return -1;
}

5、测试

5.1、编译应用程序

如果你使用的buildroot系统,需要交叉编译。

这里测试所使用的板子跑的是ubuntu,执行如下命令直接编译:

sudo gcc -o mipi_to_yuv mipi_to_yuv.c 

5.2、运行应用程序

sudo ./mipi_to_yuv /dev/video0

在windows下使用yuv播放器打开任意一张图片:

6、总结

参考文章:

Camera | 4.瑞芯微平台MIPI摄像头应用程序编写_瑞芯微-CSDN专栏

源码gitee仓库:

仓库主页:
https://gitee.com/cattle_l/v4l2_app.git
直接拉取:
git clone https://gitee.com/cattle_l/v4l2_app.git

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2310873.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【GoTeams】-2:项目基础搭建(下)

本文目录 1. 回顾2. Zap日志3. 配置4. 引入gprc梳理gRPC思路优雅关闭gRPC 1. 回顾 上篇文章我们进行了路由搭建&#xff0c;引入了redis&#xff0c;现在来看看对应的效果。 首先先把前端跑起来&#xff0c;然后点击注册获取验证码。 再看看控制台输出和redis是否已经有记录&…

02-双指针-A-B 数对

题目 链接&#xff1a;P1102 A-B 数对 - 洛谷 思路 问题场景想象 我们可以把这个问题想象成在一个排队的队伍里找符合特定身高差的人对。给定的数列里的每个数就好比队伍里每个人的身高&#xff0c;而差值 C 就是我们要找的身高差。我们的目标是找出队伍里所有身高差恰好是 …

2025年Cursor最新安装使用教程

Cursor安装教程 一、Cursor下载二、Cursor安装三、Cursor编辑器快捷键(1) 基础编辑快捷键(2) 导航快捷键(3) 其他常用快捷键 一、Cursor下载 Cursor官方网站&#xff08;https://www.cursor.com/ &#xff09; 根据自己电脑操作系统选择对应安装包 二、Cursor安装 下载完成后…

快速部署:在虚拟机上安装 CentOS 7 的详细步骤

CentOS是一个开源的基于Red Hat Enterprise Linux (RHEL) 的Linux发行版&#xff0c;它的主要目的是提供一个与RHEL相似的操作系统但不包含RHEL的商业支持和服务&#xff0c;完全免费。主要面向那些希望在企业环境中使用稳定、可靠的Linux系统但又不想支付RHEL许可证费用的用户…

【有啥问啥】深入浅出:大模型应用工具 Ollama 技术详解

深入浅出&#xff1a;大模型应用工具 Ollama 技术详解 引言 近年来&#xff0c;大型模型&#xff08;Large Models&#xff0c;LLMs&#xff09;技术突飞猛进&#xff0c;在自然语言处理、计算机视觉、语音识别等领域展现出强大的能力。然而&#xff0c;部署和运行这些庞大的…

利用opencv_python(pdf2image、poppler)将pdf每页转为图片

1、安装依赖pdf2image pip install pdf2image 运行.py报错&#xff0c;因为缺少了poppler支持。 2、安装pdf2image的依赖poppler 以上命令直接报错。 改为手工下载&#xff1a; github: Releases oschwartz10612/poppler-windows GitHub 百度网盘&#xff1a; 百度网盘…

大数据测试总结

总结测试要点&#xff1a; 参考产品文档&#xff0c;技术文档梳理以下内容 需求来源 业务方应用场景 数据源&#xff0c;数据格转&#xff0c;数据产出&#xff0c;数据呈现方式&#xff08;数据消亡史&#xff09;&#xff0c;数据量级&#xff08;增量&#xff0c;全量&am…

Redis面试常见问题——集群方案

Redis集群方案 在Redis中提供的集群方案总共有三种 主从复制 哨兵模式 分片集群 主从复制 单节点Redis的并发能力是有上限的&#xff0c;要进一步提高Redis的并发能力&#xff0c;就需要搭建主从集群&#xff0c;实现读写分离。 主从数据同步原理 单节点Redis的并发能力是有…

Qt:day4

一、作业 1&#xff1a;实现绘图的时候&#xff0c;颜色的随时调整&#xff1b; 2&#xff1a;追加橡皮擦功能&#xff1b; 3&#xff1a;配合键盘事件&#xff0c;实现功能&#xff1b; 当键盘按 ctrlz 的时候&#xff0c;撤销最后一次绘图。 【Headers / widget.h】&#xff…

vue3之echarts仪表盘

vue3之echarts仪表盘 效果如下&#xff1a; 版本 "echarts": "^5.5.1" 核心代码&#xff1a; <template><div ref"chartRef" class"circle"></div> </template> <script lang"ts" setup>…

将PDF转为Word的在线工具

参考视频&#xff1a;外文翻译 文章目录 一、迅捷PDF转换器二、Smallpdf 一、迅捷PDF转换器 二、Smallpdf

MWC 2025|紫光展锐联手美格智能发布5G通信模组SRM812

在2025年世界移动通信大会&#xff08;MWC 2025&#xff09;期间&#xff0c;紫光展锐携手美格智能正式推出了基于紫光展锐V620平台的第二代5G Sub6G R16模组SRM812&#xff0c;以超高性价比方案&#xff0c;全面赋能合作伙伴&#xff0c;加速5G规模化应用在各垂直领域的全面落…

前端基础之ajax

vue-cli配置代理服务器解决跨域问题 我们可以使用一个代理服务器8080&#xff0c;Vue项目8080发送请求向代理服务器8080发送请求&#xff0c;再由在理服务器转发给后端服务器 首先需要在vue.config.js中配置代理服务器 const { defineConfig } require(vue/cli-service) modul…

【无标题】FrmImport

文章目录 前言一、问题描述二、解决方案三、软件开发&#xff08;源码&#xff09;四、项目展示五、资源链接 前言 我能抽象出整个世界&#xff0c;但是我不能抽象你。 想让你成为私有常量&#xff0c;这样外部函数就无法访问你。 又想让你成为全局常量&#xff0c;这样在我的…

IP-Guard软件设置P2P升级功能

日常使用IP-Guard软件遇到客户端升级&#xff0c;需要从服务器下载升级包&#xff0c;为了让快速升级&#xff0c;可以配置参数&#xff0c;具体设置见下图&#xff1a; 控制台—策略—定制配置—新增 关键字&#xff1a;obt_dislble_p2p2 内容&#xff1a;2

【Mac】git使用再学习

目录 前言 如何使用github建立自己的代码库 第一步&#xff1a;建立本地git与远程github的联系 生成密钥 将密钥加入github 第二步&#xff1a;创建github仓库并clone到本地 第三步&#xff1a;上传文件 常见的git命令 git commit git branch git merge/git rebase …

java后端开发day27--常用API(二)正则表达式爬虫

&#xff08;以下内容全部来自上述课程&#xff09; 1.正则表达式&#xff08;regex&#xff09; 可以校验字符串是否满足一定的规则&#xff0c;并用来校验数据格式的合法性。 1.作用 校验字符串是否满足规则在一段文本中查找满足要求的内容 2.内容定义 ps&#xff1a;一…

【TCP/IP协议栈】【传输层】端口号、套接字、多路复用/分解、网络字节序

参考资料&#xff1a; 前言&#xff1a; 总结&#xff1a; 【计算机网络】套接字&#xff08;应用层和传输层之间的接口&#xff09; 套接字是一个通用的通信接口抽象不仅限于TCP/IP协议族作为应用层和传输层之间的桥梁支持多种通信方式和协议族 套接字定义 在 TCP 或者 UDP…

【漫话机器学习系列】120.参数化建模(Parametric Modeling)

参数化建模&#xff08;Parametric Modeling&#xff09;详解 1. 引言 在数据建模和机器学习中&#xff0c;参数化建模&#xff08;Parametric Modeling&#xff09;是一种广泛应用的建模方法。它通过假设一个函数形式来表达变量之间的关系&#xff0c;并估算该函数的参数&am…

Web3 的未来:去中心化如何重塑互联网

Web3 的未来&#xff1a;去中心化如何重塑互联网 在这个信息爆炸的时代&#xff0c;我们正站在一个新的技术革命的门槛上——Web3。Web3 不仅仅是一个技术术语&#xff0c;它代表了一种全新的互联网理念&#xff0c;即去中心化。这种理念正在逐步改变我们对互联网的使用方式和…