V4L2+单色USB摄像头编程实践3:读取MJPG格式图像

news2025/1/12 12:08:41

查看摄像头支持的MJPG格式的分辨率和帧率:

$ v4l2-ctl --list-formats-ext --device /dev/video0
ioctl: VIDIOC_ENUM_FMT
        Type: Video Capture

        [0]: 'MJPG' (Motion-JPEG, compressed)
                Size: Discrete 1280x720      
                        Interval: Discrete 0.008s (120.000 fps)
                        Interval: Discrete 0.017s (60.000 fps)
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                Size: Discrete 640x480
                        Interval: Discrete 0.008s (120.000 fps)
        [1]: 'YUYV' (YUYV 4:2:2)
                Size: Discrete 640x480
                        Interval: Discrete 0.033s (30.000 fps)

由v4l2-ctl命令的执行结果可知,最大支持1280X70,该分辨率最高120帧每秒。

测试结果:

这是

 

测试代码

#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <sys/mman.h>

#include <string.h>
#include <stdint.h>

#define _DEBUG_INFO
#ifdef _DEBUG_INFO
#define DEBUG_INFO(format, ...) printf("%s:%d -- "format"\n" \
,__func__,__LINE__ \
, ##__VA_ARGS__)
#else
#define DEBUG_INFO(format, ...)
#endif

#define _DEBUG_PRINT
#ifdef _DEBUG_PRINT
#define DEBUG_PRINT(format,...)	printf(format,##__VA_ARGS__)	
#else
#define DEBUG_PRINT(format,...)
#endif

// ip addr add 192.168.0.12/24 dev ens33
#define _DEBUG_CORE
#ifdef _DEBUG_CORE
#define DEBUG_CORE(format, ...) printf("%s:%s:%d -- "format"\n" \
,__FILE__,__func__,__LINE__ \
, ##__VA_ARGS__)
#else
#define DEBUG_CORE(format, ...)
#endif

#define DEV_NAME "/dev/video0"
int fd = 0;

int write_mjpg(char *data,int len,int index)
{
    char name[20];
    snprintf(name,sizeof(name),"%d.mjpg",index);
    int fd = open(name,O_WRONLY | O_CREAT | O_TRUNC,0660);
    if(fd < 0){
        perror("open");
        return 0;
    }
    DEBUG_INFO("open %s ok",name);
    int res = write(fd,data,len);
    DEBUG_INFO("write %s ok,res = %d",name,res);
    close(fd);
    return 0;
}
int main(int argc, char **argv)
{
    int fd = 0;
    int res = 0;
    fd = open(DEV_NAME,O_RDWR);
    if(fd < 0){
        perror("open");
        DEBUG_INFO("open %s fail",DEV_NAME);
        return -1;
    }
    struct v4l2_capability cap;
    res = ioctl(fd,VIDIOC_QUERYCAP,&cap);
    if(res < 0){
        perror("VIDIOC_QUERYCAP:");
        return -1;
    }
    if(!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)){
        DEBUG_INFO("%s is not a camera",DEV_NAME);
        return -1;
    }
    if((cap.capabilities & V4L2_CAP_READWRITE)){
        DEBUG_INFO("V4L2_CAP_READWRITE is ok");
    }else{
        DEBUG_INFO("V4L2_CAP_READWRITE is not ok");
    }
    if((cap.capabilities & V4L2_CAP_STREAMING)){
        DEBUG_INFO("V4L2_CAP_STREAMING is ok");
    }else{
        DEBUG_INFO("V4L2_CAP_STREAMING is not ok");
    }
    DEBUG_INFO("%s is a camera",DEV_NAME);

    struct v4l2_fmtdesc v4l2_fmtdesc;
    
    v4l2_fmtdesc.index = 0;
    struct v4l2_frmsizeenum v4l2_frmsize;
    //枚举支持的像素格式
    while(1){
       v4l2_fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        res = ioctl(fd,VIDIOC_ENUM_FMT,&v4l2_fmtdesc);
        if(res < 0){
            break;
        }
        if(v4l2_fmtdesc.pixelformat == V4L2_PIX_FMT_YUYV){
            DEBUG_INFO("V4L2_PIX_FMT_YUYV");
        }

        if(v4l2_fmtdesc.pixelformat == V4L2_PIX_FMT_MJPEG){
            DEBUG_INFO("V4L2_PIX_FMT_MJPEG");
        } 
        printf("fmt: %s <0x%x> %s\n", v4l2_fmtdesc.description, v4l2_fmtdesc.pixelformat,(char*)&v4l2_fmtdesc.pixelformat);

        v4l2_frmsize.index = 0;
        v4l2_frmsize.pixel_format = v4l2_fmtdesc.pixelformat;
        //枚举指定格式支持的分辨率
        while(1){
            res = ioctl(fd,VIDIOC_ENUM_FRAMESIZES,&v4l2_frmsize);
            if(res < 0){
                break;
            }
            printf("frame_size<%d*%d>\n", v4l2_frmsize.discrete.width, v4l2_frmsize.discrete.height);
            
            struct v4l2_frmivalenum v4l2_frmival;
            v4l2_frmival.index = 0;
            v4l2_frmival.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            v4l2_frmival.pixel_format = v4l2_fmtdesc.pixelformat;
            v4l2_frmival.width = v4l2_frmsize.discrete.width;
            v4l2_frmival.height = v4l2_frmsize.discrete.height;
            //枚举指定格式,分辨率,支持的帧率
            while(1){
                res = ioctl(fd,VIDIOC_ENUM_FRAMEINTERVALS,&v4l2_frmival);
                if(res < 0){
                    break;
                }
                printf("denominator=%d,numerator=%d,Frame interval<%fps> \n", 
                v4l2_frmival.discrete.denominator,
                v4l2_frmival.discrete.numerator,
                 (float)v4l2_frmival.discrete.denominator / (float)v4l2_frmival.discrete.numerator);
                v4l2_frmival.index++;
            }

            v4l2_frmsize.index++;
        }

        v4l2_fmtdesc.index++;
    }
    
    //查询当前的支持的格式
    DEBUG_INFO("获取当前支持的格式");
    struct v4l2_format v4l2_fmt;
    v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    res = ioctl(fd, VIDIOC_G_FMT, &v4l2_fmt);
    if(res < 0){
        perror("VIDIOC_G_FMT:");
        return -1;
    }
    printf("width:%d, height:%d format:%d -- %s,byesperline = %d\n", v4l2_fmt.fmt.pix.width, 
    v4l2_fmt.fmt.pix.height,
    v4l2_fmt.fmt.pix.pixelformat,
    (char*)&v4l2_fmt.fmt.pix.pixelformat,
    v4l2_fmt.fmt.pix.bytesperline);

    printf("field = %d,%s\n",v4l2_fmt.fmt.pix.field,(char*)&v4l2_fmt.fmt.pix.field);
    printf("colorspace = %d,%s\n",v4l2_fmt.fmt.pix.colorspace,(char*)&v4l2_fmt.fmt.pix.colorspace);
    printf("sizeimage = %d,%s\n",v4l2_fmt.fmt.pix.sizeimage,(char*)&v4l2_fmt.fmt.pix.field);

    if(V4L2_COLORSPACE_SRGB == v4l2_fmt.fmt.pix.colorspace){
        DEBUG_INFO("V4L2_COLORSPACE_SRGB");
    }

    //设置图像格式
    struct v4l2_format v4l2_set_fmt;
    v4l2_set_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    v4l2_set_fmt.fmt.pix.width = 1280;
    v4l2_set_fmt.fmt.pix.height = 720;
    v4l2_set_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    res = ioctl(fd,VIDIOC_S_FMT,&v4l2_set_fmt);
    if(res < 0){
        perror("VIDIOC_G_FMT:");
        return -1;
    }
    printf("width:%d, height:%d \n",v4l2_set_fmt.fmt.pix.width,v4l2_set_fmt.fmt.pix.height);

    v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    res = ioctl(fd, VIDIOC_G_FMT, &v4l2_fmt);
    if(res < 0){
        perror("VIDIOC_G_FMT:");
        return -1;
    }
    DEBUG_INFO("width:%d, height:%d format:%d -- %s,byesperline = %d\n", v4l2_fmt.fmt.pix.width, 
    v4l2_fmt.fmt.pix.height,
    v4l2_fmt.fmt.pix.pixelformat,
    (char*)&v4l2_fmt.fmt.pix.pixelformat,
    v4l2_fmt.fmt.pix.bytesperline);

    printf("field = %d,%s\n",v4l2_fmt.fmt.pix.field,(char*)&v4l2_fmt.fmt.pix.field);
    printf("colorspace = %d,%s\n",v4l2_fmt.fmt.pix.colorspace,(char*)&v4l2_fmt.fmt.pix.colorspace);
    printf("sizeimage = %d,%s\n",v4l2_fmt.fmt.pix.sizeimage,(char*)&v4l2_fmt.fmt.pix.field);

    if(V4L2_COLORSPACE_SRGB == v4l2_fmt.fmt.pix.colorspace){
        DEBUG_INFO("V4L2_COLORSPACE_SRGB");
    }

    struct v4l2_streamparm v4l2_streamparm;
    v4l2_streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    res = ioctl(fd,VIDIOC_G_PARM,&v4l2_streamparm);
    if(res < 0){
        perror("VIDIOC_G_PARM:");
        return -1;
    }

    DEBUG_INFO("denominator = %d,numerator = %d\n",
    v4l2_streamparm.parm.capture.timeperframe.denominator,
    v4l2_streamparm.parm.capture.timeperframe.numerator);

    //申请帧缓冲
    struct v4l2_requestbuffers v4l2_reqbuf;
    v4l2_reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    v4l2_reqbuf.count = 3;
    v4l2_reqbuf.memory = V4L2_MEMORY_MMAP;

    res = ioctl(fd,VIDIOC_REQBUFS,&v4l2_reqbuf);
    if(res < 0){
        perror("VIDIOC_REQBUFS:");
        return -1;
    }
#define REQUEST_BUF_COUNT  3
    void *frm_base[REQUEST_BUF_COUNT];
    struct v4l2_buffer v4l2_buf;
    v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    v4l2_buf.memory = V4L2_MEMORY_MMAP;
    for(v4l2_buf.index = 0;v4l2_buf.index < REQUEST_BUF_COUNT;v4l2_buf.index++){
        res = ioctl(fd,VIDIOC_QUERYBUF,&v4l2_buf);
        if(res < 0){
            perror("VIDIOC_G_PARM:");
            return -1;
        }
        printf("bytesused = %d,",v4l2_buf.bytesused);
        printf("offset = %d,",v4l2_buf.m.offset);
        printf("length = %d,",v4l2_buf.length);
        printf("index = %d,",v4l2_buf.index);
        printf("memory = %d\n",v4l2_buf.memory);

        frm_base[v4l2_buf.index] = mmap(NULL,v4l2_buf.length,PROT_READ | PROT_WRITE,MAP_SHARED,fd,v4l2_buf.m.offset);
        if(MAP_FAILED == frm_base[v4l2_buf.index]){
            perror("mmap");
            return -1;
        }
        DEBUG_INFO("mmap %d ok",v4l2_buf.index);
        // v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        // v4l2_buf.memory = V4L2_MEMORY_MMAP;

        // res = ioctl(fd,VIDIOC_QBUF,&v4l2_buf);
        // if(res < 0){
        //     perror("VIDIOC_QBUF");
        //     return -1;
        // }
        // DEBUG_INFO("push %d ok",v4l2_buf.index);
    }
    struct v4l2_buffer v4l2_push_buf;
    v4l2_push_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    v4l2_push_buf.memory = V4L2_MEMORY_MMAP;
    for(v4l2_push_buf.index = 0;v4l2_push_buf.index < REQUEST_BUF_COUNT;v4l2_push_buf.index++){
        res = ioctl(fd,VIDIOC_QBUF,&v4l2_push_buf);
        if(res < 0){
            perror("VIDIOC_QBUF");
            return -1;
        }
        DEBUG_INFO("push %d ok",v4l2_push_buf.index);
    }
    enum v4l2_buf_type v4l2_buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    res = ioctl(fd,VIDIOC_STREAMON,&v4l2_buf_type);
    if(res < 0){
        perror("VIDIOC_STREAMON");
        return -1;
    }   
    DEBUG_INFO("VIDIOC_STREAMON ok");

    struct v4l2_buffer v4l2_process_buf;
    v4l2_process_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    v4l2_process_buf.memory = V4L2_MEMORY_MMAP;
    // v4l2_process_buf.length = 614400;
    int i = 0;
    DEBUG_INFO("开始读图");
    // char *grey_buf = malloc(614400);
    // if(grey_buf == NULL){
    //     DEBUG_INFO("malloc error");
    //     return -1;
    // }
    for(i = 0;i < REQUEST_BUF_COUNT;i++){
        // v4l2_process_buf.m.offset = 614400 * v4l2_process_buf.index;
        res = ioctl(fd,VIDIOC_DQBUF,&v4l2_process_buf);
        if(res < 0){
            perror("VIDIOC_DQBUF");
            DEBUG_INFO("res = %d,i = %d",res,i);
            goto end;
        }
        printf("出队后:");
        printf("bytesused = %d,",v4l2_process_buf.bytesused);
        printf("offset = %d,",v4l2_process_buf.m.offset);
        printf("length = %d,",v4l2_process_buf.length);
        printf("index = %d,",v4l2_process_buf.index);
        printf("memory = %d\n",v4l2_process_buf.memory);
        //保存MJPG
        write_mjpg(frm_base[v4l2_process_buf.index],v4l2_process_buf.bytesused,v4l2_process_buf.index);
        
        res = ioctl(fd,VIDIOC_QBUF,&v4l2_process_buf);
        if(res < 0){
            perror("VIDIOC_QBUF");
            DEBUG_INFO("res = %d,index = %d",res,v4l2_process_buf.index);
            goto end;
        }

        printf("入队后:");
        printf("bytesused = %d,",v4l2_process_buf.bytesused);
        printf("offset = %d,",v4l2_process_buf.m.offset);
        printf("length = %d,",v4l2_process_buf.length);
        printf("index = %d,",v4l2_process_buf.index);
        printf("memory = %d\n",v4l2_process_buf.memory);
    }
    DEBUG_INFO("process ok");
end:
    res = ioctl(fd,VIDIOC_STREAMOFF,&v4l2_buf_type);
    if(res < 0){
        perror("VIDIOC_STREAMOFF");
        return -1;
    }   
    DEBUG_INFO("VIDIOC_STREAMOFF ok");


  
    DEBUG_INFO("open %s ok",DEV_NAME);
    close(fd);
    return 0;
};

 小结

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

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

相关文章

敏捷研发管理软件及敏捷管理流程

Scrum中非常强调公开、透明、直接有效的沟通&#xff0c;这也是“可视化的管理工具”在敏捷开发中如此重要的原因之一。通过“可视化的管理工具”让所有人直观的看到需求&#xff0c;故事&#xff0c;任务之间的流转状态&#xff0c;可以使团队成员更加快速适应敏捷开发流程。 …

Java cc链2 分析

环境 cc4 <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-collections4 --> <dependency><groupId>org.apache.commons</groupId><artifactId>commons-collections4</artifactId><version>4.0</version&…

损失函数介绍

用softmax&#xff0c;就可以将一个输出值转换到概率取值的一个范围。 交叉熵损失CrossEntropyLoss 第一个参数weight&#xff0c; 各类别的loss设置权值&#xff0c; 如果类别不均衡的时候这个参数很有必要了&#xff0c;加了之后损失函数变成这样&#xff1a; 第二个参数ign…

8年测试经验之谈 —— 什么是全链路压测?

随着互联网技术的发展和普及&#xff0c;越来越多的互联网公司开始重视性能压测&#xff0c;并将其纳入软件开发和测试的流程中。 阿里巴巴在2014 年双11 大促活动保障背景下提出了全链路压测技术&#xff0c;能更好的保障系统可用性和稳定性。 什么是全链路压测&#xff1f; …

湘潭大学 湘大 XTU 1251 Colombian Number 题解(非常详细)

参考文章 1.XTUOJ-1251-Colombian Number 链接 1251 题面 题目描述 对于正整数n,不存在整数k,使得n等于k加上k的数码累加和&#xff0c;我们称这样的数是哥伦比亚数或者自我数。 比如 11就不是一个哥伦比亚数&#xff0c;因为10加上10的数码累加和1等于11;而20则是一个哥伦…

uniapp 开发微信小程序使用echart的dataZoom属性缩放功能不生效!bug记录!

在本项目中使用的是这个echart库 在项目中添加了dataZoom配置项但是不生效&#xff0c;突然想到微信小程序代码大小的限制&#xff0c;之前的echarts.js是定制的&#xff0c;有可能没有加dataZoom组件。故重新定制echarts.js。之前用的echarts版本是5.0.0&#xff0c;这次也是…

容灾设备系统组成,容灾备份系统组成包括哪些

随着信息技术的快速发展&#xff0c;企业对数据的需求越来越大&#xff0c;数据已经成为企业的核心财产。但是&#xff0c;数据安全性和完整性面临巨大挑战。在这种环境下&#xff0c;容灾备份系统应运而生&#xff0c;成为保证企业数据安全的关键因素。下面我们就详细介绍容灾…

IDEA启动Tomcat两个端口的方式 使用nginx进行反向代理 JMeter测试分布式情况下synchronized锁失效

目录 引出IDEA启动Tomcat两个端口的方式1.编辑配置2.添加新的端口-Dserver.port80833.service里面管理4.启动后进行测试 使用nginx进行反向代理反向代理多个端口运行日志查看启动关闭重启 分布式情况下synchronized失效synchronized锁代码启动tomcat两个端口nginx反向代理JMete…

慕课网 Go工程师 第三周 package和gomodules章节

Go包的引入&#xff1a; 包名前面加匿名&#xff0c;只引入但不使用&#xff0c;如果对应包有init函数&#xff0c;会执行init函数&#xff08;初始化操作&#xff09; 包名前面加. 把这个包的结构体和方法导入当前包&#xff0c;慎用&#xff0c;你不知道当前包和被引入的包用…

顺序表详解|顺序表常见错误并调试分析

前言&#xff1a; 今天我们开始学习基础的数据结构——顺序表&#xff0c;数据结构就是将数据在内存存储起来&#xff0c;在内存管理数据。 一、线性表 1、线性表&#xff08;Linear list&#xff09;是n个具有相同特性的数据元素的有限序列&#xff0c;线性表是一种在实际中广…

美国访问学者签证好办吗?

近年来&#xff0c;随着国际交流与合作的不断深入&#xff0c;许多人对于美国访问学者签证的办理情况产生了浓厚的兴趣。那么&#xff0c;美国访问学者签证到底好办吗&#xff1f;让知识人网小编带您一起了解一下。 首先&#xff0c;美国作为世界上的科研、教育和创新中心之一&…

springBoot的启动

自动配置注解的逻辑: 在启动类的基础上,导入了springboot的大量自动配置类,以至于自己不用关心配置实现过程(约定大于配置) 大量自动配置类是如何导入的? Spring提供了一个SpringFactories功能(SPI: service provider interface ),读取固定文件META-INF/spring.factories,按照…

keepalived双机热备,keepalived+lvs(DR)

本节主要学习了keepalivedlvs的作用和配置方法主要配置调度器和web节点&#xff0c;还有keepalived的双击热备&#xff0c;主要内容有概述&#xff0c;安装&#xff0c;功能模块&#xff0c;配置双击热备&#xff0c;验证方法&#xff0c;双击热备的脑裂现象和VIP无法通信。 目…

❤ windows 安装后台java开发环境JDK 、MySQL 、Redis

❤ windows 安装后台java开发环境 1、windows 安装 JDK. 下载地址&#xff1a; http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 1、下载安装 官网点击下载安装 网盘 jdk安装包 链接&#xff1a;https://pan.baidu.com/s/1sdxA6B…

Axure设计之日期选择器(年月选择)

在系统中&#xff0c;日期选择器经常会用到&#xff0c;包括日历日期的选择、日期时间的选择和日期范围的选择&#xff0c;一般是下拉列表的形式进行选择。Axure没有自带的日期选择器&#xff0c;下面教大家如何在Axure中制作真实日期选择&#xff08;年月选择&#xff09;效果…

首席执行官Adam Selipsky解读“亚马逊云科技的技术产品差异化”

迄今为止&#xff0c;亚马逊云科技已经参与了21世纪几乎所有的大型计算变革&#xff0c;亚马逊云科技是一个很传奇的故事&#xff0c;它始于大约20年前的一项实验&#xff0c;当时亚马逊试图出售其过剩的服务器。人们确实对此表示怀疑。为什么在线书店试图销售云服务&#xff1…

js逆向实战之某书protobuf反序列化

什么是Protobuf&#xff1f; \qquad Protobuf&#xff08;Protocol Buffer&#xff09;是 Google 开发的一套数据存储传输协议&#xff0c;作用就是将数据进行序列化后再传输&#xff0c;Protobuf 编码是二进制的&#xff0c;它不是可读的&#xff0c;也不容易手动修改&#xf…

抓包工具Charles的安装及代理设置(Windows浏览器代理、安卓代理)

1、下载Charles 官网地址&#xff1a;https://www.charlesproxy.com/download/&#xff0c;下载对应的安装包。安装完成后按照以下步骤进行代理配置。 2、配置Charles证书 按照以下截图步骤进行配置即可。 3、Charles代理设置 这里的端口号根据自己情况设置&#xff0c;这里…

17.4 【Linux】systemctl 针对 timer 的配置文件

有时候&#xff0c;某些服务你想要定期执行&#xff0c;或者是开机后执行&#xff0c;或者是什么服务启动多久后执行等等的。在过去&#xff0c;我们大概都是使用 crond 这个服务来定期处理&#xff0c; 不过&#xff0c;既然现在有一直常驻在内存当中的 systemd 这个好用的东西…

visio三维格式、三维旋转导出图模糊解决方案

问题描述 visio中元素经过三维格式或三维旋转&#xff08;也可能包括其他的特殊操作&#xff09;后&#xff0c;导出.emf格式的图会模糊&#xff0c;如下图所示。 解决方案 借助其他软件 在ppt中将对应的元素旋转后再导入visio&#xff0c;此时对于visio来说该元素相当于一…