小R科技-WIFI机器人网·机器人创意工作室

 找回密码
 立即注册
查看: 6561|回复: 1

QT上位机MJPG视频采集程序分析

[复制链接]
发表于 2021-2-5 14:56:45 | 显示全部楼层 |阅读模式
本帖最后由 猫先森大仙 于 2021-2-5 15:04 编辑

一、先来看看什么是mjpg-streamer视频流:

      mjpg-streamer是一个开源的视频服务器,通过摄像头采集数据,放到内存中,再通过socket把视频数据发送出去,最终在web端显示视频数据。mjpg-streamer把采集数据、socket发送数据封装成了两个动态库,一个称作输入插件,一个称作输出插件。
     如果我们想做一些跟视频传输相关的项目,完全可以利用mjpg-streamer作为视频数据来源,而不用再关心底层驱动如何实现,驱动视频数据如何读取。mjpg-streamer自带压缩算法,可以把采集的原始数据压缩成jpg格式的图像数据,从而方便传输。

      以上是网上搜到的说法,不是很懂,但是说了两点①:通过SOCKET传输    ②:传输的是一幅幅mjpg图像。
1.、SOCKET
     
                                               图1.
              图2
     理解这幅图,在tcp/ip协议中,tcp通过三次握手建立起一个tcp的链接就行,具体知识自行百度或者查资料。
对应到程序里面,IP就是你连接设备的网络地址,port就是这台设备上的哪个对象。好比送信,IP地址是你们小区的地址,PORT就是你家的门牌号。再来看程序里面,IP地址就是树莓派或者WiFi模块的地址,port就是他们创建对象的端口,比如视频流传输使用8080端口, 看上图2客户端,你得创建个socket类,然后连接模块,地址192.168.1.1,端口8080,然后连接成功就可以发送和接收数据了
2.mjpg协议  
关于mjpg-streamer的流程介绍网上有很多,我们只是分析下它是如何输出数据的,因为只有知道如何输出数据,才能写对应的代码去接收数据。
主要代码集中在 httpd.c 文件中。
下面我们来看树莓派或WiFi模块中的代码:
客户端想要获取服务器的视频数据,先要向服务器发起请求,简单点理解,就是先得告诉服务器,是要获取视频流,还是获取一张图片。

于是,在成功连接服务器后,我们要做的第一步:

向服务器发送字符串 "GET /?action=stream"
为了保证数据的安全性,mjpg-streamer加上了用户名和密码,当然,是在启动服务器的时候,通过参数来决定是否要加验证。
从代码的 898 行可以看出,如果客户端发送的数据小于 2 个字节,就是跳出循环,代码继续向下走。所以得到了第二步:
向服务器发送任意小于两字节的字符串。
接着程序走到了 929 行,开始调用函数 send_stream。
send_streamer主要是向客户端返回数据,只要搞清楚返回哪些数据,那么我们的客户端程序基本就写出来了。

首先返回头部信息,这一部分没有什么有用的信息,所以我们直接接收后忽略就好。
接下来进入 376 行开始死循环。


分别向客户端发送了三个数据:
头部信息:包含了一帧数据的大小;
帧数据:我们真正想要得到的数据;
尾部信息。
好了,一帧mjpg流数据大概是这样:
content-type”+“content-length+0XFF  +0xD8+byte1+byte2+.......+byteN+0xFF+0xd9;
其中图像数据为:0XFF  +0xD8+byte1+byte2+.......+byteN+0xFF+0xd9;

虽然是分为三次发送,但是因为使用的是TCP协议,传输的过程中可能会分包或者粘包,所以接收数据的时候,并不是接收三次那么简单。有可能第一次收到的数据既包含了头部信息,又包含了帧数据(粘包);有可能第二次接收数据的时候,只收到了部分帧数据(分包)。
二、代码实现
总结一下,如果想实现客户端的视频采集,需要完成下面的步骤:
      1.构造QTcpSocket:
      mysocket_video = new QTcpSocket;
      2.连接服务器端
     mysocket_video->connectToHost(ui->lineEdit_IP->text(),ui->lineEdit_Port->text().toInt());//连接服务器端     if(!mysocket_video->waitForConnected(30000))     {         qDebug()<<tr("error!");     }     else     {         qDebug()<<tr("success!");
     }
  • 3.向服务器发送请求,即发送字符串“GET /?action=stream”,之后向服务器发送任意两个字节的数据,合在一起就是;
  • QString  req22= "GET /?action=stream\n\n"; //0x0a 0x0a mysocket_video->write(req22.toLatin1(),strlen(req22.toLatin1()));
  • 4.利用定时器,循环接收服务器返回的图像数据;
  •   构造函数中初始化定时器:
  • timer_crema = new QTimer;  timer_crema->start(20);
  • 打开视频按钮,增加槽:
  • connect(timer_crema,SIGNAL(timeout()),this,SLOT(socket_Read_data()));
  • 接收图像流:
  • QByteArray Buffer;//原始视频图片流缓冲
  • Buffer=mysocket_video->readAll();  //即content-type”+“content-length+0XFF  +0xD8+byte1+byte2+.......+byteN+0xFF+0xd9;
  • 接收服务器返回的头部信息、帧数据、尾部信息。
       循环接收的时候,最好能够根据头部信息和尾部信息来确定帧数据,做到万无一失。
     5.利用头部信息、帧数据、尾部信息进行图像提取,这个就考验提取算法了
      QByteArray jpg_buf;//采集到的图片数组 ,即0XFF  +0xD8+byte1+byte2+.......+byteN+0xFF+0xd9;
     6 .显示图像:
      PICTURE.loadFromData(jpg_buf);//图片数据向QPixmap PICTURE传递
   ui->CREMA_Show->setPixmap(PICTURE);//显示  7.到此为止,一个图像显示上位机就好了,水平有限,适合初学者,大神轻喷。

这个程序对30万像素640*480的摄像头运行没问题,但是对1280*760这样的摄像头就会很卡,而且报错:Corrupt JPEG data: premature end of data segment,希望大神看看啥原因。

上位机

上位机
ae061eb3d4514314b9d6b6143aa6fe8c.jpg
ae061eb3d4514314b9d6b6143aa6fe8c.jpg

评分

参与人数 1金钱 +3 收起 理由
liuviking + 3

查看全部评分

回复

使用道具 举报

发表于 2021-2-6 00:44:11 | 显示全部楼层
如果对算法过程要求不是很高,其实可以直接调用现成的视频显示组件,比如ffmpeg,传个URL就可以了,解码都不用自己搞的。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

新品特惠推荐上一条 /2 下一条

QQ|QQ技术咨询1|QQ技术咨询2|商务合作微信1:xiaorgeek001|商务合作微信2:XiaoRGEEK|诚聘英才|Archiver|手机版|小R科技-WIFI机器人网·机器人创意工作室 ( 粤ICP备15000788号-6 )

GMT+8, 2024-4-19 15:03 , Processed in 1.085028 second(s), 22 queries .

Powered by XiaoR GEEK X3.4

© 2014-2021 XiaoR GEEK

快速回复 返回顶部 返回列表