1、在Pro文件中添加 QT += network,未添加时提示找不到QtcpSocket 和QtcpServer类的头文件。
2、创建socket,连接web sever。
//tcpclient.h
#ifndef TCPCLIENT_H
#define TCPCLIENT_H
#include <QTcpSocket>
#include <QHostAddress>
class TcpClient : public QTcpSocket
{
Q_OBJECT
public:
TcpClient();
~TcpClient();
QTcpSocket *socket;
private:
QHostAddress serverIP;
unsigned short int port = 81; //esp32-cam 视频流端口
QString ip = "***.***.***.***"; //ESP32-cam 分配到的IP
};
#endif // TCPCLIENT_H
//tcpclient.c
#include "tcpclient.h"
TcpClient::TcpClient()
{
socket = new QTcpSocket;
serverIP.setAddress(ip);
this->socket->connectToHost(serverIP,port);
}
3、发送get请求。
void mainwindow::sendHttpRequest()
{
Client->socket->write("GET /stream HTTP/1.1\r\nHost: 192.168.1.1\r\nConnection: Keep-Alive\r\nuser-agent: 1601\r\n\r\n", 90);
// qDebug() << tr("成功发送 GET 请求 ");
}
4、解析视频流(JPEG)。
void mainwindow::dataReceived()
{
//只需要处理jpg_stream的http协议
// HTTP/1.1 200 OK
//Content-disposition: inline; filename=capture.jpg
// Content-length: 24800
// Content-type: image/jpeg
QByteArray buff = Client->socket->readAll();
QString tmpStr(buff);
QString tmp2Str = "HTTP/1.1";
//字符串操作
if (tmpStr.indexOf(tmp2Str) == 0)
{
/* 判断http请求视频数据是否成功
* HTTP/1.1 200 OK
* Content-type: multipart/x-mixed-replace;
* boundary=123456789000000000000987654321
*/
//如果是200表示接收正确
if (tmpStr.mid(9, 3) == "200")
{
//获得Content-type
int tmp = tmpStr.indexOf("Content-Type");
if (tmp > 0)
{
//得到的是Content-type的类型
if (tmpStr.mid(tmp + 14, tmpStr.indexOf(";", tmp) - (tmp + 14)) == "multipart/x-mixed-replace")
{
//得到boundary的值
int tmp2 = tmpStr.indexOf("boundary");
//保存待用
m_HttpBoundary = tmpStr.mid(tmp2 + 9, tmpStr.indexOf("\r\n", tmp2) - (tmp2 + 9));
m_bflagHttpStream = true; //设置jpg_stream标志为真
}
else
{
//不是jpg_stream
m_bflagHttpStream = false;
}
}
}
else
{
qDebug()<< "received data error" ;
return;
}
}
if (m_bflagHttpStream)//是否是JPEG stream
{
//进入stream流
//1.检测 Content-Type: image/jpeg 作为一帧数据的开头
//2.检测分隔符 “123456789” 作为一帧数据结束
//3.提取图片数据
if (stream_head_start)
{
auto ContentType_info = tmpStr.indexOf("Content-Type");
if (ContentType_info == -1)// not find Content-Type
{
return;
}
auto boundary_info = tmpStr.indexOf("123456789", ContentType_info);
if (boundary_info >= 0)
{
//qDebug() << "1.boundary_info :" << boundary_info;
m_JpgBuff.append(buff.mid(ContentType_info, boundary_info - ContentType_info + 1));
stream_head_start = false;
analysis_imgdata = true;
}
else
{
m_JpgBuff.append(buff.mid(ContentType_info));
stream_head_start = false;
}
}
else
{
auto boundary_info = tmpStr.indexOf("123456789");
if (boundary_info >= 0)
{
m_JpgBuff.append(buff.left(boundary_info));
stream_head_start = false;
analysis_imgdata = true;
}
else
{
m_JpgBuff.append(buff);
stream_head_start = false;
}
}
if (analysis_imgdata)
{
analysis_imgdata = false;
stream_head_start = true;
QString imgdata2Qstring(m_JpgBuff);
int tmp = imgdata2Qstring.indexOf("Content-Length");
if (tmp >= 0)
{
int tmp1 = imgdata2Qstring.indexOf("\r\n", tmp);
//得到转换后的个数
m_length = 0;
m_length = imgdata2Qstring.mid(tmp + 16, tmp1 - (tmp + 16 - 1)).toInt();
QPixmap pixmap;
auto error = pixmap.loadFromData(m_JpgBuff.mid(tmp1 + 12, m_length));
if (!error)
{
m_JpgBuff.clear();
m_bflagBodyEnd = false;
return;
}
m_JpgBuff.clear();
QPixmap pps = pixmap.scaled(ui.label_display->width(), ui.label_display->height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
ui.label_display->setPixmap(pps);
emit emitImgdata(pixmap.toImage());
}
}
}
}
5、opencv 显示解析后的jpeg图片
void mainwindow::image_signal(QImage img)
{
cv::Mat mat = QImage2cvMat(img);
if (!mat.data)
qDebug() << "mat图像错误";
cv::imshow("show",mat);
}