2022-10-12 18:17:10 +08:00
|
|
|
#include "videowin.h"
|
2023-05-27 17:43:57 +08:00
|
|
|
#include "gutil/qgui.h"
|
2022-10-12 18:17:10 +08:00
|
|
|
#include "crc.h"
|
|
|
|
#include <QTextEdit>
|
|
|
|
#include <QPushButton>
|
|
|
|
#include <QMessageBox>
|
|
|
|
#include <QDateTime>
|
|
|
|
#include <QTimerEvent>
|
|
|
|
#include <QApplication>
|
|
|
|
#include <QDesktopWidget>
|
|
|
|
#include <QScreen>
|
|
|
|
#include <QDebug>
|
2022-10-14 18:46:16 +08:00
|
|
|
#include <QPainter>
|
2022-10-12 18:17:10 +08:00
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
2022-12-16 15:08:53 +08:00
|
|
|
VideoWin *VideoWin::newIns(QByteArray &name, QWidget *parent) {
|
2022-10-12 18:17:10 +08:00
|
|
|
if(name.isEmpty()) return 0;
|
|
|
|
char errbuf[PCAP_ERRBUF_SIZE]{'\0'};
|
2022-10-14 18:46:16 +08:00
|
|
|
auto pcapRe = pcap_open_live(name.data(), 65536, PCAP_OPENFLAG_PROMISCUOUS, 50, errbuf);
|
2022-10-12 18:17:10 +08:00
|
|
|
if(pcapRe == 0) {
|
|
|
|
QMessageBox::critical(parent, "Error", QString("打开网卡失败")+errbuf);
|
|
|
|
return 0;
|
|
|
|
}
|
2022-10-14 18:46:16 +08:00
|
|
|
auto pcapSend = pcap_open_live(name.data(), 65536, 0, 50, errbuf);
|
2022-10-12 18:17:10 +08:00
|
|
|
if(pcapSend == 0) {
|
|
|
|
QMessageBox::critical(parent, "Error", QString("打开网卡失败")+errbuf);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return new VideoWin(pcapRe, pcapSend, parent);
|
|
|
|
}
|
|
|
|
|
|
|
|
VideoWin::VideoWin(pcap_t *pcapRece, pcap_t *pcapSend, QWidget *parent) : BaseWin{parent} {
|
|
|
|
setWindowModality(Qt::WindowModal);
|
|
|
|
setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
setWindowTitle("视频传输");
|
|
|
|
resize(800, 600);
|
|
|
|
|
|
|
|
auto vBox = new QVBoxLayout(center);
|
|
|
|
vBox->setContentsMargins(0,0,0,0);
|
|
|
|
vBox->setSpacing(3);
|
|
|
|
vBox->addLayout(addBtns(new QHBoxLayout()));
|
|
|
|
|
|
|
|
auto hBox = new HBox(vBox);
|
|
|
|
|
|
|
|
fdWidth = new QSpinBox;
|
|
|
|
fdWidth->setRange(1, 9999);
|
2022-10-14 18:46:16 +08:00
|
|
|
fdWidth->setValue(640);
|
2022-10-12 18:17:10 +08:00
|
|
|
hBox->addWidget(fdWidth);
|
|
|
|
|
|
|
|
hBox->addWidget(new QLabel("x"));
|
|
|
|
|
|
|
|
fdHeight = new QSpinBox;
|
|
|
|
fdHeight->setRange(1, 9999);
|
2022-10-14 18:46:16 +08:00
|
|
|
fdHeight->setValue(360);
|
2022-10-12 18:17:10 +08:00
|
|
|
hBox->addWidget(fdHeight);
|
|
|
|
|
2022-12-16 15:08:53 +08:00
|
|
|
|
|
|
|
hBox->addWidget(new QLabel("截图间隔(ms)"));
|
|
|
|
fdMsecCapScr = new QSpinBox;
|
|
|
|
fdMsecCapScr->setRange(1, 1000);
|
|
|
|
fdMsecCapScr->setValue(17);
|
|
|
|
hBox->addWidget(fdMsecCapScr);
|
2023-03-24 18:41:48 +08:00
|
|
|
hBox->addSpacing(10);
|
2022-12-16 15:08:53 +08:00
|
|
|
|
2022-12-16 16:16:12 +08:00
|
|
|
fdNoReview = new QCheckBox("关闭显示");
|
|
|
|
fdNoReview->setChecked(noReview);
|
|
|
|
hBox->addWidget(fdNoReview);
|
2022-12-16 15:08:53 +08:00
|
|
|
|
2023-03-24 18:41:48 +08:00
|
|
|
hBox->addSpacing(10);
|
|
|
|
auto fdUseOldProtocol = new QCheckBox("用旧协议");
|
|
|
|
connect(fdUseOldProtocol, &QRadioButton::toggled, [=](bool checked) {
|
|
|
|
useOldProto = checked;
|
|
|
|
if(thdSend) thdSend->useOldProto = checked;
|
|
|
|
if(thdRece) thdRece->useOldProto = checked;
|
|
|
|
});
|
|
|
|
hBox->addWidget(fdUseOldProtocol);
|
|
|
|
|
|
|
|
hBox->addSpacing(10);
|
2022-12-16 15:08:53 +08:00
|
|
|
|
|
|
|
hBox->addWidget(new QLabel("接收线程优先级"));
|
|
|
|
fdYxj = new QSpinBox;
|
|
|
|
fdYxj->setRange(0, 7);
|
|
|
|
fdYxj->setValue(6);
|
|
|
|
|
|
|
|
connect(fdYxj, (void(QSpinBox::*)(int))&QSpinBox::valueChanged, [=](int value) {
|
|
|
|
if(thdRece) thdRece->setPriority(QThread::Priority(value));
|
|
|
|
});
|
|
|
|
hBox->addWidget(fdYxj);
|
|
|
|
|
|
|
|
|
2022-10-12 18:17:10 +08:00
|
|
|
hBox->addSpacing(20);
|
|
|
|
|
|
|
|
auto fdStart = new QRadioButton("发送");
|
|
|
|
connect(fdStart, &QRadioButton::toggled, this, [this](bool checked) {
|
|
|
|
if(! checked) return;
|
|
|
|
screen = QGuiApplication::primaryScreen();
|
2022-12-16 15:08:53 +08:00
|
|
|
if(timerId==0) timerId = startTimer(fdMsecCapScr->value());
|
2022-10-12 18:17:10 +08:00
|
|
|
});
|
|
|
|
hBox->addWidget(fdStart);
|
|
|
|
hBox->addSpacing(20);
|
|
|
|
|
|
|
|
fdEnd = new QRadioButton("停止");
|
|
|
|
connect(fdEnd, &QRadioButton::toggled, this, [this](bool checked) {
|
|
|
|
if(! checked) return;
|
|
|
|
if(timerId) {
|
|
|
|
killTimer(timerId);
|
|
|
|
timerId = 0;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
hBox->addWidget(fdEnd);
|
|
|
|
hBox->addSpacing(20);
|
|
|
|
|
|
|
|
fdInfo = new QLabel;
|
|
|
|
fdInfo->setStyleSheet("QLabel{color: #f22;}");
|
|
|
|
hBox->addWidget(fdInfo);
|
|
|
|
hBox->addStretch();
|
|
|
|
|
|
|
|
hBox = new HBox(vBox);
|
|
|
|
|
|
|
|
hBox->addWidget(new QLabel("接收:"));
|
2022-10-14 18:46:16 +08:00
|
|
|
hBox->addSpacing(20);
|
|
|
|
hBox->addWidget(new QLabel("丢包次数: "));
|
|
|
|
hBox->addWidget(fdLostTimes = new QLabel);
|
|
|
|
fdLostTimes->setMinimumWidth(40);
|
|
|
|
hBox->addWidget(new QLabel("丢包数: "));
|
|
|
|
hBox->addWidget(fdLostPkts = new QLabel);
|
|
|
|
fdLostPkts->setMinimumWidth(40);
|
2022-10-12 18:17:10 +08:00
|
|
|
hBox->addStretch();
|
|
|
|
|
2022-10-14 18:46:16 +08:00
|
|
|
auto fdCanvas = new Canvas;
|
|
|
|
vBox->addWidget(fdCanvas, 1);
|
2022-10-12 18:17:10 +08:00
|
|
|
|
2022-12-16 16:16:12 +08:00
|
|
|
connect(fdNoReview, &QRadioButton::toggled, [=](bool checked) {
|
|
|
|
noReview = checked;
|
|
|
|
if(thdRece) thdRece->noReview = checked;
|
|
|
|
if(noReview) {
|
2022-12-16 15:08:53 +08:00
|
|
|
fdCanvas->img = QImage();
|
|
|
|
fdCanvas->update();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-10-12 18:17:10 +08:00
|
|
|
thdRece = new VideoRecThread(pcapRece);
|
|
|
|
connect(thdRece, &QThread::finished, this, [this] {
|
|
|
|
thdRece = 0;
|
|
|
|
});
|
2023-03-24 18:41:48 +08:00
|
|
|
connect(thdRece, &VideoRecThread::onMsg, fdCanvas, [this, fdCanvas](const QByteArray chars) {
|
|
|
|
if(noReview) return;
|
|
|
|
auto data = (uchar *)chars.data();
|
|
|
|
if(data[14]==0x12) {
|
|
|
|
imgLines.append(chars);
|
|
|
|
int i = (data[18]<<8 | data[19]) + 1;
|
|
|
|
if(i > imgHeight) imgHeight = i;
|
|
|
|
i = (data[20]<<8 | data[21]) + (data[22]<<8 | data[23]);
|
|
|
|
if(i > imgWidth) imgWidth = i;
|
|
|
|
} else if(data[14]==0x11 && imgWidth && imgHeight) {
|
|
|
|
//qDebug()<<"imgWidth"<<imgWidth<<"imgHeight"<<imgHeight;
|
|
|
|
QImage img(imgWidth, imgHeight, QImage::Format_RGB888);
|
|
|
|
imgWidth = 0;
|
|
|
|
imgHeight = 0;
|
|
|
|
foreach(auto line, imgLines) {
|
|
|
|
auto data = (uchar *)line.data();
|
|
|
|
memcpy(img.bits()+(data[18]<<8 | data[19])*img.bytesPerLine()+(data[20]<<8 | data[21])*3, data+24, (data[22]<<8 | data[23])*3);
|
|
|
|
}
|
|
|
|
imgLines.clear();
|
|
|
|
fdCanvas->img = img;
|
|
|
|
fdCanvas->update();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
connect(thdRece, &VideoRecThread::onImg, fdCanvas, [this, fdCanvas](QImage img, int lostTimes, int lostPkts) {
|
2022-12-16 16:16:12 +08:00
|
|
|
if(noReview) return;
|
2022-10-14 18:46:16 +08:00
|
|
|
fdCanvas->img = img;
|
|
|
|
fdCanvas->update();
|
|
|
|
if(lostPkts==0) return;
|
|
|
|
this->lostTimes += lostTimes;
|
|
|
|
this->lostPkts += lostPkts;
|
|
|
|
fdLostTimes->setText(QString::number(this->lostTimes));
|
|
|
|
fdLostPkts->setText(QString::number(this->lostPkts));
|
2022-10-12 18:17:10 +08:00
|
|
|
});
|
2022-12-16 15:08:53 +08:00
|
|
|
thdRece->start(QThread::Priority(fdYxj->value()));//HighestPriority //InheritPriority //TimeCriticalPriority
|
2022-10-12 18:17:10 +08:00
|
|
|
|
2023-03-24 18:41:48 +08:00
|
|
|
thdSend = new VideoSendThread(pcapSend);
|
|
|
|
connect(thdSend, &QThread::finished, this, [this] {
|
2022-10-12 18:17:10 +08:00
|
|
|
fdEnd->setChecked(true);
|
2023-03-24 18:41:48 +08:00
|
|
|
thdSend = 0;
|
2022-10-12 18:17:10 +08:00
|
|
|
});
|
2023-03-24 18:41:48 +08:00
|
|
|
connect(thdSend, &VideoSendThread::onErr, this, [this](QString err) {
|
2022-10-12 18:17:10 +08:00
|
|
|
fdEnd->setChecked(true);
|
|
|
|
QMessageBox::critical(this, "Error", err);
|
|
|
|
});
|
2023-03-24 18:41:48 +08:00
|
|
|
thdSend->start();
|
2022-10-12 18:17:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void VideoWin::timerEvent(QTimerEvent *event) {
|
|
|
|
if(event->timerId()!=timerId) BaseWin::timerEvent(event);
|
|
|
|
else {
|
|
|
|
int width = fdWidth->value();
|
|
|
|
int height = fdHeight->value();
|
|
|
|
if(width < 32 || height < 32) return;
|
|
|
|
auto devicePixelRatio = screen->devicePixelRatio();
|
|
|
|
auto pixmap = screen->grabWindow(0, 0, 0, width/devicePixelRatio, height/devicePixelRatio);
|
|
|
|
auto img = pixmap.toImage();
|
|
|
|
img.convertTo(QImage::Format_RGB888);
|
|
|
|
{
|
2023-03-24 18:41:48 +08:00
|
|
|
std::lock_guard<std::mutex> lock(thdSend->mtx);
|
|
|
|
if(thdSend->imgs.size()>2) return;
|
|
|
|
thdSend->imgs.append(img);
|
2022-10-12 18:17:10 +08:00
|
|
|
}
|
|
|
|
auto now_epoch = chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now().time_since_epoch()).count();
|
|
|
|
int dur = now_epoch - last_epoch;
|
|
|
|
last_epoch = now_epoch;
|
2023-03-24 18:41:48 +08:00
|
|
|
fdInfo->setText(info = "帧间隔: "+QString::number(dur)+" 帧缓存: "+QString::number(thdSend ? thdSend->imgs.size() : 0));
|
2022-10-12 18:17:10 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VideoSendThread::VideoSendThread(pcap_t *pcap) : pcap(pcap) {
|
|
|
|
connect(this, &QThread::finished, this, &QThread::deleteLater);
|
|
|
|
}
|
|
|
|
#define MAX_ONCE 1482
|
|
|
|
void VideoSendThread::run() {
|
|
|
|
QImage img;
|
2022-10-14 18:46:16 +08:00
|
|
|
quint32 idx = 0;
|
2022-10-12 18:17:10 +08:00
|
|
|
while(status!=2) {
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(mtx);
|
|
|
|
img = imgs.isEmpty() ? QImage() : imgs.takeFirst();
|
|
|
|
}
|
|
|
|
if(img.isNull()) {
|
|
|
|
QThread::msleep(15);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
auto queue = pcap_sendqueue_alloc(img.width()*img.height()*4);
|
2023-03-24 18:41:48 +08:00
|
|
|
if(useOldProto) {
|
|
|
|
QByteArray bytes;
|
|
|
|
bytes.append("\x00\x11\x22\x33\x44\x55", 6); //目的地址
|
|
|
|
bytes.append("\x00\x11\x22\x33\x44\x55", 6); //源地址
|
|
|
|
bytes.append("\x10\x5A"); //协议标识
|
|
|
|
bytes.append("\x11\0\0\0", 4); //包类型标识 保留 网口标识 接收卡标识
|
|
|
|
bytes.append(frameIdx>>8).append(frameIdx);//帧循环计数
|
|
|
|
frameIdx++;
|
|
|
|
bytes.append(2, 0); //帧保持计数
|
|
|
|
bytes.append("\x0\xff\x0\x0\x0", 5); //自动亮度模式 整屏亮度 实时参数模式 测试模式 保留
|
|
|
|
auto crc32 = crc32_calc((uint8_t*)bytes.data(), bytes.length());
|
|
|
|
bytes.append(crc32>>24).append(crc32>>16).append(crc32>>8).append(crc32);
|
|
|
|
//发送帧开始指令包
|
|
|
|
struct pcap_pkthdr pktheader;
|
|
|
|
pktheader.len = pktheader.caplen = bytes.size();
|
|
|
|
if(pcap_sendqueue_queue(queue, &pktheader, (u_char*)bytes.data()) == -1) {
|
|
|
|
onErr(QString("添加开始失败: ")+pcap_geterr(pcap));
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
auto lineLen = img.width() * 3;
|
|
|
|
int once;
|
|
|
|
if(lineLen > MAX_ONCE) {
|
|
|
|
int cnt = (lineLen + MAX_ONCE - 1) / MAX_ONCE;
|
|
|
|
once = (lineLen + cnt - 1) / cnt;
|
|
|
|
} else once = lineLen;
|
|
|
|
auto bytesPerLine = img.bytesPerLine();
|
|
|
|
auto bits = img.constBits();
|
|
|
|
//按行发送图像数据包
|
|
|
|
for(int i=0; i<img.height(); i++) for(int j=0; j<lineLen; j+=once) {
|
|
|
|
int dataLen = lineLen - j;
|
|
|
|
if(dataLen > once) dataLen = once;
|
|
|
|
bytes.clear();
|
|
|
|
bytes.append("\x00\x11\x22\x33\x44\x55", 6); //目的地址
|
|
|
|
bytes.append("\x00\x11\x22\x33\x44\x55", 6); //源地址
|
|
|
|
bytes.append("\x10\x5A"); //协议标识
|
|
|
|
bytes.append("\x12\0\0\0", 4); //包类型标识 保留 网口标识 接收卡标识
|
|
|
|
bytes.append(i>>8).append(i).append(j/3>>8).append(j/3); //行列起始位置
|
|
|
|
bytes.append(dataLen/3>>8).append(dataLen/3); //包像素计数
|
|
|
|
bytes.append((const char*)bits + i * bytesPerLine + j, dataLen); //图像数据
|
|
|
|
crc32 = crc32_calc((uint8_t*)bytes.data(), bytes.length());
|
|
|
|
bytes.append(crc32>>24).append(crc32>>16).append(crc32>>8).append(crc32);
|
|
|
|
pktheader.len = pktheader.caplen = bytes.size();
|
|
|
|
if(pcap_sendqueue_queue(queue, &pktheader, (u_char*)bytes.data()) == -1) {
|
|
|
|
onErr(QString("添加数据失败: ")+pcap_geterr(pcap));
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
u_int res;
|
|
|
|
if((res = pcap_sendqueue_transmit(pcap, queue, 0)) < queue->len) {
|
|
|
|
onErr(QString::asprintf("发送包出错: %s. 仅发了 %d / %d bytes", pcap_geterr(pcap), res, queue->len));
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
QByteArray bytes;
|
|
|
|
bytes.append("\x55\x55\x9a\x3d"); //前导, 版本号, 服务类型
|
|
|
|
bytes.append("\0\x4", 2); //数据长度
|
|
|
|
bytes.append(4, '\xff'); //目的地址
|
|
|
|
bytes.append(4, 0); //源地址
|
|
|
|
bytes.append("\x35\0\xc\xbb", 4); //内存指针
|
|
|
|
bytes.append(2, 0); //应答填充项
|
|
|
|
auto crc32 = crc32_calc((uint8_t*)bytes.data()+2, bytes.length()-2);
|
|
|
|
bytes.append(crc32>>24).append(crc32>>16).append(crc32>>8).append(crc32);
|
|
|
|
bytes.append(img.width()>>8).append(img.width()).append(img.height()>>8).append(img.height());
|
|
|
|
crc32 = crc32_calc((uint8_t*)bytes.data()+bytes.length()-4, 4);
|
|
|
|
bytes.append(crc32>>24).append(crc32>>16).append(crc32>>8).append(crc32);
|
|
|
|
//发送帧开始指令包
|
2022-10-12 18:17:10 +08:00
|
|
|
struct pcap_pkthdr pktheader;
|
|
|
|
pktheader.len = pktheader.caplen = bytes.size();
|
|
|
|
if(pcap_sendqueue_queue(queue, &pktheader, (u_char*)bytes.data()) == -1) {
|
|
|
|
onErr(QString("添加开始失败: ")+pcap_geterr(pcap));
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
auto lineLen = img.width() * 3;
|
|
|
|
int once;
|
|
|
|
if(lineLen > MAX_ONCE) {
|
|
|
|
int cnt = (lineLen + MAX_ONCE - 1) / MAX_ONCE;
|
|
|
|
once = (lineLen + cnt - 1) / cnt;
|
|
|
|
} else once = lineLen;
|
|
|
|
auto bytesPerLine = img.bytesPerLine();
|
|
|
|
auto bits = img.constBits();
|
2022-10-14 18:46:16 +08:00
|
|
|
idx = 0;
|
2022-12-16 15:08:53 +08:00
|
|
|
//按行发送图像数据包
|
2022-10-14 18:46:16 +08:00
|
|
|
for(int i=0; i<img.height(); i++) for(int j=0; j<lineLen; j+=once) {
|
2022-10-12 18:17:10 +08:00
|
|
|
int dataLen = lineLen - j;
|
|
|
|
if(dataLen > once) dataLen = once;
|
|
|
|
dataLen += 4;
|
|
|
|
bytes.clear();
|
|
|
|
bytes.append("\x55\x55\x1\x33"); //前导, 版本号, 服务类型
|
|
|
|
bytes.append(dataLen>>8).append(dataLen); //数据长度
|
|
|
|
bytes.append(4, '\xff'); //目的地址
|
|
|
|
bytes.append(4, 0); //源地址
|
|
|
|
bytes.append(i>>8).append(i).append(j>>8).append(j); //内存指针
|
|
|
|
bytes.append(2, 0); //应答填充项
|
|
|
|
crc32 = crc32_calc((uint8_t*)bytes.data()+2, bytes.length()-2);
|
|
|
|
bytes.append(crc32>>24).append(crc32>>16).append(crc32>>8).append(crc32);
|
2022-12-16 15:08:53 +08:00
|
|
|
bytes.append(idx>>24).append(idx>>16).append(idx>>8).append(idx);//包序号
|
2022-10-14 18:46:16 +08:00
|
|
|
idx++;
|
2022-10-12 18:17:10 +08:00
|
|
|
bytes.append((const char*)bits + i * bytesPerLine + j, dataLen - 4);
|
|
|
|
crc32 = crc32_calc((uint8_t*)bytes.data()+bytes.length()-dataLen, dataLen);
|
|
|
|
bytes.append(crc32>>24).append(crc32>>16).append(crc32>>8).append(crc32);
|
|
|
|
pktheader.len = pktheader.caplen = bytes.size();
|
|
|
|
if(pcap_sendqueue_queue(queue, &pktheader, (u_char*)bytes.data()) == -1) {
|
|
|
|
onErr(QString("添加数据失败: ")+pcap_geterr(pcap));
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bytes.clear();
|
|
|
|
bytes.append("\x55\x55\x9a\x3d"); //前导, 版本号, 服务类型
|
|
|
|
bytes.append(2, 0); //数据长度
|
|
|
|
bytes.append(4, '\xff'); //目的地址
|
|
|
|
bytes.append(4, 0); //源地址
|
|
|
|
bytes.append("\x35\0\xc\xcc", 4); //内存指针
|
|
|
|
bytes.append(2, 0); //应答填充项
|
|
|
|
crc32 = crc32_calc((uint8_t*)bytes.data()+2, bytes.length()-2);
|
|
|
|
bytes.append(crc32>>24).append(crc32>>16).append(crc32>>8).append(crc32);
|
|
|
|
pktheader.len = pktheader.caplen = bytes.size();
|
2022-12-16 15:08:53 +08:00
|
|
|
//发送帧结束指令包
|
2022-10-12 18:17:10 +08:00
|
|
|
if(pcap_sendqueue_queue(queue, &pktheader, (u_char*)bytes.data()) == -1) {
|
|
|
|
onErr(QString("添加结束失败: ")+pcap_geterr(pcap));
|
|
|
|
goto end;
|
|
|
|
}
|
2022-10-14 18:46:16 +08:00
|
|
|
u_int res;
|
2022-10-12 18:17:10 +08:00
|
|
|
if((res = pcap_sendqueue_transmit(pcap, queue, 0)) < queue->len) {
|
2022-10-14 18:46:16 +08:00
|
|
|
onErr(QString::asprintf("发送包出错: %s. 仅发了 %d / %d bytes", pcap_geterr(pcap), res, queue->len));
|
2022-10-12 18:17:10 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end:
|
|
|
|
pcap_sendqueue_destroy(queue);
|
|
|
|
}
|
|
|
|
}
|
2022-10-14 18:46:16 +08:00
|
|
|
/* Callback function invoked by libpcap for every incoming packet */
|
2022-12-16 15:08:53 +08:00
|
|
|
//void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data) {
|
2022-10-14 18:46:16 +08:00
|
|
|
|
2022-12-16 15:08:53 +08:00
|
|
|
//}
|
2022-10-14 18:46:16 +08:00
|
|
|
|
2022-10-12 18:17:10 +08:00
|
|
|
VideoRecThread::VideoRecThread(pcap_t *pcap) : pcap(pcap) {
|
|
|
|
connect(this, &QThread::finished, this, &QThread::deleteLater);
|
|
|
|
}
|
|
|
|
void VideoRecThread::run() {
|
|
|
|
pcap_pkthdr *header;
|
|
|
|
const u_char *data;
|
|
|
|
QImage img;
|
|
|
|
int res;
|
2022-10-14 18:46:16 +08:00
|
|
|
int lastIdx = -1;
|
|
|
|
int lostTimes{0}, lostPkts{0};
|
2022-10-12 18:17:10 +08:00
|
|
|
while((res = pcap_next_ex(pcap, &header, &data)) >= 0) {
|
|
|
|
if(status==2) return;
|
2022-12-16 16:16:12 +08:00
|
|
|
if(status==1 || res == 0 || noReview) continue; //超时
|
2022-10-12 18:17:10 +08:00
|
|
|
if(header->caplen<24) continue;
|
2023-03-24 18:41:48 +08:00
|
|
|
if(useOldProto) {
|
|
|
|
if(data[0]!=0 || data[1]!=0x11 || data[2]!=0x22 || data[3]!=0x33 || data[12]!=0x10 || data[13]!=0x5A) continue;
|
|
|
|
emit onMsg(QByteArray((char*)data, header->caplen));
|
|
|
|
} else {
|
|
|
|
if(data[0]!=0x55 || data[1]!=0x55) continue;
|
|
|
|
if(data[2]==0x9a && data[3]==0x3d) {
|
|
|
|
if(data[17]==0xBB) {
|
|
|
|
img = QImage(((int)data[24] << 8) + data[25], ((int)data[26] << 8) + data[27], QImage::Format_RGB888);
|
|
|
|
lastIdx = -1;
|
|
|
|
lostTimes = 0;
|
|
|
|
lostPkts = 0;
|
|
|
|
} else if(data[17]==0xCC) emit onImg(img, lostTimes, lostPkts);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(data[2]!=0x1 || data[3]!=0x33) continue;
|
|
|
|
int len = ((int)data[4] << 8) + data[5];
|
|
|
|
int row = ((int)data[14] << 8) + data[15];
|
|
|
|
int col = ((int)data[16] << 8) + data[17];
|
|
|
|
quint32 idx = ((quint32)data[24] << 24) + ((quint32)data[25] << 16) + ((quint32)data[26] << 8) + data[27];
|
|
|
|
//auto epoch = chrono::duration_cast<chrono::microseconds>(chrono::steady_clock::now().time_since_epoch()).count();
|
|
|
|
memcpy(img.bits()+row*img.bytesPerLine()+col, &data[28], len-4);
|
|
|
|
int diff = idx - lastIdx - 1;
|
|
|
|
lastIdx = idx;
|
|
|
|
if(diff > 0) {
|
|
|
|
//qDebug()<<"丢包"<<diff;
|
|
|
|
lostTimes++;
|
|
|
|
lostPkts += diff;
|
|
|
|
}
|
2022-10-14 18:46:16 +08:00
|
|
|
}
|
2022-10-12 18:17:10 +08:00
|
|
|
}
|
|
|
|
emit onErr(pcap_geterr(pcap));
|
|
|
|
}
|
2022-10-14 18:46:16 +08:00
|
|
|
|
2022-12-16 15:08:53 +08:00
|
|
|
void Canvas::paintEvent(QPaintEvent *) {
|
2022-10-14 18:46:16 +08:00
|
|
|
QPainter painter(this);
|
|
|
|
painter.drawImage(0, 0, img);
|
|
|
|
}
|