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

文章目录

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、应用程序编写

完整的应用程序如下:

c 复制代码
#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,执行如下命令直接编译:

shell 复制代码
sudo gcc -o mipi_to_yuv mipi_to_yuv.c 

5.2、运行应用程序

shell 复制代码
sudo ./mipi_to_yuv /dev/video0

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

6、总结

参考文章:

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

源码gitee仓库:

shell 复制代码
仓库主页:
https://gitee.com/cattle_l/v4l2_app.git
直接拉取:
git clone https://gitee.com/cattle_l/v4l2_app.git
相关推荐
科技小E7 小时前
WebRTC实时音视频通话技术EasyRTC嵌入式音视频通信SDK,助力智慧物流打造实时高效的物流管理体系
人工智能·音视频
Icoolkj11 小时前
可灵 AI:开启 AI 视频创作新时代
人工智能·音视频
SuperW20 小时前
视频编码原理讲解一:VCL层和NAL层的讲解
音视频
Panesle1 天前
HunyuanCustom:文生视频框架论文速读
人工智能·算法·音视频·文生视频
程序员JerrySUN2 天前
驱动开发硬核特训 · Day 30(下篇): 深入解析 lm48100q I2C 音频编解码器驱动模型(基于 i.MX8MP)
linux·驱动开发·架构·音视频
读心悦2 天前
5000字总结 HTML5 中的音频和视频,关羽标签、属性、API 和最佳实践
前端·音视频·html5
东风西巷2 天前
BLURRR剪辑软件免费版:创意剪辑,轻松上手,打造个性视频
android·智能手机·音视频·生活·软件需求
weixin_446260852 天前
视觉革命来袭!ComfyUI-LTXVideo 让视频创作更高效
人工智能·音视频
拧螺丝专业户2 天前
外网访问内网海康威视监控视频的方案:WebRTC + Coturn 搭建
音视频·webrtc·监控视频
追随远方2 天前
Android平台FFmpeg音视频开发深度指南
android·ffmpeg·音视频