esp32-cam摄像头jpg_stream的http协议,及QT测试软件
esp32-cam用的固件是ESP32_CAMERA_QR 具体参考:
电脑连接WiFi摄像头并显示图像(基于安信可esp32-cam)_一瓶可乐zc的博客-CSDN博客
exe可执行程序下载:
https://download.csdn.net/download/gws09876/11961819
点击连接之后要等待一会儿,保证连接成功之后再点击播放
源码下载地址:
https://download.csdn.net/download/gws09876/13689687
esp32_cam必须要下载官方提供的代码,可能会有更改(原理就是去连esp32_cam,向他发送请求)
Qt的http 好像不支持 multipart/x-mixed-replace 类型播放,于是只好自己根据esp32-cam 的摄像头发送的响应头和报文进行解析,
就是将/jpg_stream 的,通过TCP自己写,/jpg 使用QT自带的Http写
解析esp32-cam 的jpg_stream代码
void MainWindow::slotNetRead()
{
//只需要处理jpg_stream的http协议
///
/// HTTP/1.1 200 OK
///Content-disposition: inline; filename=capture.jpg
/// Content-length: 24800
/// Content-type: image/jpeg
///
QByteArray buff = m_pTcp->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");//到\r\n或;
if(tmp> 0)
{
//得到的是Content-type的类型
//qDebug()<<tmpStr.mid(tmp+14,tmpStr.indexOf(";",tmp)-(tmp+14));
if(tmpStr.mid(tmp+14,tmpStr.indexOf(";",tmp)-(tmp+14)) == "multipart/x-mixed-replace")
{
//如果是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标志为真
//qDebug()<<m_HttpBoundary ;
}
else
{
//不是jpg_stream
m_bflagHttpStream = false;
}
}
}
}
//Content-disposition
///记录下boundary的值作为判断jpeg图片结束的标志,
/*
*Content-length: 26400
*Content-type: image/jpg
*/
// qDebug()<<tmpStr;
if( m_bflagHttpStream)
{
int tmp3 = tmpStr.indexOf("Content-length");
if(tmp3>= 0)
{
//得到转换后的个数
m_length = 0;
m_length = tmpStr.mid(tmp3+16,tmpStr.indexOf("\r\n",tmp3)-(tmp3+16)).toInt();
//int ty = tmpStr.indexOf("\r\n\r\n");
//qDebug()<<m_length;
m_bflagBodyStart = true;
m_JpgBuff.clear();
m_JpgBuff.append(buff.mid(tmp3+46+4));
// qDebug()<<m_JpgBuff.toHex();
// qDebug()<<m_JpgBuff.size();
}
else
{
if(m_bflagBodyStart)
{
///根据Content-length的值将后面的图片数据放入缓冲区
int ttt = m_length - m_JpgBuff.size();
if(ttt>0 && ttt< buff.size())
{
//部分添加
m_JpgBuff.append(buff.left(ttt));
m_bflagBodyEnd = true;
m_bflagBodyStart = false;
}
else if(ttt > buff.size())
{
//全部添加
m_JpgBuff.append(buff);
}
}
}
// qDebug()<<buff.size();
}
buff.clear();
if(m_bflagBodyEnd)
{
#if 0
//保存图片以测试提取的数据是否正确,
const QString tfileName = "JJJ.jpg";
QFile tmpfile;
tmpfile.setFileName(tfileName);
tmpfile.open(QIODevice::WriteOnly);
if (!tmpfile.isOpen())
{
qDebug() << "file is not open";return;
}
if (!tmpfile.isWritable())
{
qDebug() << "file write disable";
return;
}
//写文件
//tmpfile.write(m_JpgBuff);
//关闭文件
tmpfile.close();
#endif
QPixmap pixmap;
pixmap.loadFromData(m_JpgBuff);
QPixmap pps = pixmap.scaled(ui->label_display->width(),ui->label_display->height(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation);
ui->label_display->setPixmap(pps);
m_JpgBuff.clear();
m_bflagBodyEnd = false;
}
}