草庐IT

V4L2+QT+USB摄像头实时显示视频(Arm,Linux,window均适用)

爱学习的诸葛铁锤 2023-06-05 原文

写在开头

笔者自从学习了Framebuffer编程和V4L2编程之后,就想实现在LCD屏上显示实时视频

笔者学习过正点I.MX6U Linux C编程中的相关内容,然而原子的例程是针对OV5640摄像头写的,像素格式是RGB

然而USB摄像头大多支持MJPEG或者YUYV格式,如果要在屏幕上显示需要进行格式转换,而转换像素格式是需要处理时间的,自然会影响视频帧率

笔者尝试写过YUYV2RGB888或者YUYV2RGB565,实际跑起来能明显感受到掉帧;
同时,笔者也考虑通过OpenCV来对JPEG或者YUYV进行编解码来显示,然而实际效果也不尽如人意

那有没有办法既能实时显示视频也不用进行图像格式转换还能有用户操作界面?

笔者结合之前所学内容最终决定用QT写界面,V4L2编程来获取实时的帧并将其显示在设计好的界面上

开发环境

  1. 虚拟机Ubuntu 16.04
  2. 编辑器VsCode
  3. 交叉编译工具 arm-linux-gnueabihf
  4. 已制作文件系统,已使能UVC相关驱动
  5. 正点原子ZYNQ7010启明星开发板
  6. USB摄像头淘宝随便买的一个
  7. QT Creator 5.9.6

必备知识

UVC驱动配置可以看我的这一篇: Linux 内核4.14添加UVC配置

QT移植并显示图片可以看我的这一篇: Qt移植正点原子ZYNQ7010-Arm平台显示图片demo,本博客的程序设计也是基于这一篇文章的

V4L2编程入门可以看我的这一篇: V4L2编程之USB摄像头采集jpeg图像

界面布局设计


可以先看一下运行效果,主体部分一共就三个:一个QLabel控件用于显示实时图像,两个QPushButton,一个用于控制视频流的显示,另一个用于解放资源并关闭窗口。

界面很简单,本博客只是实现最基础的功能,后续会在此基础上加控件和其他功能。

程序设计思路

  1. 界面布局初始化
  2. 获得用户输入,打开对应摄像头
  3. 摄像头初始化,包括打印摄像头支持的像素格式、分辨率、帧率
  4. 设置采集格式,包括像素格式、帧率设置
  5. 申请内存空间并建立内存映射,进行出队入队操作
  6. 开启视频流
  7. 设置定时器,等待“开始”按钮信号触发定时器开启
  8. 定时器定时获取一帧图像将其显示在QLabel上
  9. 等待“结束”按钮信号触发释放资源并关闭窗口

关键部分在于789三步,这里稍作解释,不想看的可直接去看源码——我的Github仓库: Linux C编程实战代码
我已经将源码连同V4L2编程的API用户手册一并上传了,欢迎大家学习交流

要在ARM平台跑也是可以的,需要自行移植QT到ARM开发板,移植办法详见我往期博客Qt移植正点原子ZYNQ7010-Arm平台显示图片demo

关键代码分析

获取用户输入赋值给v4l2_dev_init()初始化对应的摄像头
引入QCoreApplication获取用户输入的第二个字段,例如执行 ./Qt_V4l2 /dev/video1,device_parm[1] 的内容即/dev/video1
值得注意的是需要进行一个数据类型转换才能作为open()函数的参数

    QStringList device_parm = QCoreApplication::arguments();
    QString str = device_parm[1];

    v4l2_dev_init(str.toStdString());
    
    ......
    
	int MainWindow::v4l2_dev_init(string device_name){
	    /* 打开摄像头 */
	    v4l2_fd = open(device_name.c_str(),O_RDWR);
	    if(v4l2_fd < 0){
	        printf("open camera failed\n");
	        return -1;
	    }
	    printf("open camera success\n");
	    
	    ......
	    
	 }

两个按钮的信号连接代码

    //点击开始按钮,打开定时器
    connect(pushButton[0],SIGNAL(clicked()),this,SLOT(timer_start()));

    //每隔固定的时间显示一帧
    timer = new QTimer();
    connect(timer, SIGNAL(timeout()), this, SLOT(video_show()));

    /* 按钮窗口关闭,先释放设备,再关闭窗口 */
    connect(pushButton[1],SIGNAL(clicked()),this,SLOT(v4l2_device_release()));
    connect(pushButton[1],SIGNAL(clicked()),this,SLOT(close()));

定时器控制帧率,每个33ms触发video_show()显示一帧数据,一秒钟正好显示30帧(笔者的摄像头最大支持30fps,故如此设置)

/* 定时器控制帧 */
void MainWindow::timer_start(){
    // 1000/33约等于30,也就是每一秒显示30帧
    timer->start(33);
}

初始化select()来进行I/O端口复用

    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(v4l2_fd,&fds);

    //设置等待时间为2s
    struct timeval tv;
    tv.tv_sec = 2;
    tv.tv_usec = 0;
    select(v4l2_fd+1,&fds,NULL,NULL,&tv);

此段代码摘自V4L2 API手册最后的示例代码中,相关pdf文档已上传到github

获取一帧图像的数据,见于v4l2_get_one_frame(FrameBuffer *framebuf)
首先从出队队列中取一帧视频数据的视频缓冲区,再将该帧数据拷贝到frame中

    if(0 > ioctl(v4l2_fd,VIDIOC_DQBUF,&one_buf)){
        printf("VIDIOC_DQBUF failed!\n");
        return -1;
    }
	// bytesused 表示buf中已经使用的字节数
	    memcpy(framebuf->buf,(char *)buffer_infos[one_buf.index].start,one_buf.bytesused);
	    framebuf->length = one_buf.bytesused;

Qt 提供了四个用于处理图像数据的类,而 QPixmap 正是为在屏幕上显示图像而设计和优化
最后将获取保存在frame中的数据用QPixmap转换就可以显示了

     FrameBuffer frame;
     QPixmap pix;

     //获取一帧显示
     v4l2_get_one_frame(&frame);
     pix.loadFromData(frame.buf, frame.length);
     pix.scaled(displayLabel->width(),displayLabel->height(),Qt::KeepAspectRatio);
     displayLabel->setPixmap(pix);

结束语

本博客的程序已上传到github中,往期博客有关Linux C编程的代码也一并上传,需要的自行下载
地址:https://github.com/Huge-Hammer/Linux-C-Coding

后续会继续更新,通过程序设计来实现拍照截图、图像处理、视频流的存储和推流等功能,慢慢完善了

我是爱学习的诸葛铁锤,觉得有帮助的话记得点个赞再走吧,wakuwaku!

有关V4L2+QT+USB摄像头实时显示视频(Arm,Linux,window均适用)的更多相关文章

  1. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  2. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  3. ruby - inverse_of 是否适用于 has_many? - 2

    当我使用has_one时,它​​工作得很好,但在has_many上却不行。在这里您可以看到object_id不同,因为它运行了另一个SQL来再次获取它。ruby-1.9.2-p290:001>e=Employee.create(name:'rafael',active:false)ruby-1.9.2-p290:002>b=Badge.create(number:1,employee:e)ruby-1.9.2-p290:003>a=Address.create(street:"123MarketSt",city:"SanDiego",employee:e)ruby-1.9.2-p290

  4. Vscode+Cmake配置并运行opencv环境(Windows和Ubuntu大同小异) - 2

    之前在培训新生的时候,windows环境下配置opencv环境一直教的都是网上主流的vsstudio配置属性表,但是这个似乎对新生来说难度略高(虽然个人觉得完全是他们自己的问题),加之暑假之后对cmake实在是爱不释手,且这样配置确实十分简单(其实都不需要配置),故斗胆妄言vscode下配置CV之法。其实极为简单,图比较多所以很长。如果你看此文还配不好,你应该思考一下是不是自己的问题。闲话少说,直接开始。0.CMkae简介有的人到大二了都不知道cmake是什么,我不说是谁。CMake是一个开源免费并且跨平台的构建工具,可以用简单的语句来描述所有平台的编译过程。它能够根据当前所在平台输出对应的m

  5. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  6. ruby - 如何在 Ruby 中执行 Windows CLI 命令? - 2

    我在目录“C:\DocumentsandSettings\test.exe”中有一个文件,但是当我用单引号编写命令时`C:\DocumentsandSettings\test.exe(我无法在此框中显示),用于在Ruby中执行命令,我无法这样做,我收到的错误是找不到文件或目录。我尝试用“//”和“\”替换“\”,但似乎没有任何效果。我也使用过系统、IO.popen和exec命令,但所有的努力都是徒劳的。exec命令还使程序退出,这是我不想发生的。提前致谢。 最佳答案 反引号环境就像双引号,所以反斜杠用于转义。此外,Ruby会将空格解

  7. 【Linux操作系统】——网络配置与SSH远程 - 2

    Linux操作系统——网络配置与SSH远程安装完VMware与系统后,需要进行网络配置。第一个目标为进行SSH连接,可以从本机到VMware进行文件传送,首先需要进行网络配置。1.下载远程软件首先需要先下载安装一款远程软件:FinalShell或者xhell7FinalShellxhell7FinalShell下载:Windows下载http://www.hostbuf.com/downloads/finalshell_install.exemacOS下载http://www.hostbuf.com/downloads/finalshell_install.pkg2.配置CentOS网络安装好

  8. Linux磁盘分区中物理卷(PV)、卷组(VG)、逻辑卷(LV)创建和(LVM)管理 - 2

    文章目录一基础定义二创建逻辑卷2-1准备物理设备2-2创建物理卷2-3创建卷组2-4创建逻辑卷2-5创建文件系统并挂载文件三扩展卷组和缩减卷组3-1准备物理设备3-2创建物理卷3-3扩展卷组3-4查看卷组的详细信息以验证3-5缩减卷组四扩展逻辑卷4-1检查卷组是否有可用的空间4-2扩展逻辑卷4-3扩展文件系统五删除逻辑卷5-1备份数据5-2卸载文件系统5-3删除逻辑卷5-4删除卷组5-5删除物理卷六LVM逻辑卷缩容6-1缩容注意事项6-2标准缩容步骤一基础定义LVM,LogicalVolumeManger,逻辑卷管理,Linux磁盘分区管理的一种机制,建立在硬盘和分区上的一个逻辑层,提高磁盘分

  9. ruby - 如何使用适用于 ruby​​ 的 aws sdk 创建 route53 记录集? - 2

    EC2会在实例停止然后重新启动时为其提供新的IP地址,因此我需要能够自动管理route53记录集,以便我可以一致地访问内容。遗憾的是,sdk的route53部分的文档远不如ec2的文档那么健壮(可以理解),所以我有点卡住了。到目前为止,从我所看到的情况来看,似乎change_resource_record_sets(link)是可行的方法,但我对:chages需要什么感到困惑>因为它提到了一个Change对象,但没有提供指向所述对象描述的链接。这是我的代码目前的样子:r53.client.change_resource_record_sets(:hosted_zone_id=>'MY_

  10. 适用于Web开发的Python还是Ruby? - 2

    Asitcurrentlystands,thisquestionisnotagoodfitforourQ&Aformat.Weexpectanswerstobesupportedbyfacts,references,orexpertise,butthisquestionwilllikelysolicitdebate,arguments,polling,orextendeddiscussion.Ifyoufeelthatthisquestioncanbeimprovedandpossiblyreopened,visitthehelpcenter提供指导。11年前关闭。我是一位精通HTML

随机推荐