qt/ledset/videowin.cpp

421 lines
17 KiB
C++
Raw Normal View History

#include "videowin.h"
2023-05-27 17:43:57 +08:00
#include "gutil/qgui.h"
#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>
using namespace std;
2022-12-16 15:08:53 +08:00
VideoWin *VideoWin::newIns(QByteArray &name, QWidget *parent) {
if(name.isEmpty()) return 0;
2023-06-13 18:00:19 +08:00
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);
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);
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);
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);
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);
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());
});
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);
hBox->addStretch();
2022-10-14 18:46:16 +08:00
auto fdCanvas = new Canvas;
vBox->addWidget(fdCanvas, 1);
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();
}
});
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();
2023-07-28 14:48:41 +08:00
if(data[16]==0x12) {
2023-03-24 18:41:48 +08:00
imgLines.append(chars);
2023-07-28 14:48:41 +08:00
int i = (data[20]<<8 | data[21]) + 1;
2023-03-24 18:41:48 +08:00
if(i > imgHeight) imgHeight = i;
2023-07-28 14:48:41 +08:00
i = (data[22]<<8 | data[23]) + (data[24]<<8 | data[25]);
2023-03-24 18:41:48 +08:00
if(i > imgWidth) imgWidth = i;
2023-07-28 14:48:41 +08:00
} else if(data[16]==0x11 && imgWidth && imgHeight) {
2023-03-24 18:41:48 +08:00
//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();
2023-07-28 14:48:41 +08:00
memcpy(img.bits()+(data[20]<<8 | data[21])*img.bytesPerLine()+(data[22]<<8 | data[23])*3, data+26, (data[24]<<8 | data[25])*3);
2023-03-24 18:41:48 +08:00
}
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-12-16 15:08:53 +08:00
thdRece->start(QThread::Priority(fdYxj->value()));//HighestPriority //InheritPriority //TimeCriticalPriority
2023-03-24 18:41:48 +08:00
thdSend = new VideoSendThread(pcapSend);
connect(thdSend, &QThread::finished, this, [this] {
fdEnd->setChecked(true);
2023-03-24 18:41:48 +08:00
thdSend = 0;
});
2023-03-24 18:41:48 +08:00
connect(thdSend, &VideoSendThread::onErr, this, [this](QString err) {
fdEnd->setChecked(true);
QMessageBox::critical(this, "Error", err);
});
2023-03-24 18:41:48 +08:00
thdSend->start();
}
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);
}
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));
}
}
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;
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;
2023-07-28 14:48:41 +08:00
bytes.append("\x55\x55");
2023-03-24 18:41:48 +08:00
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();
2023-07-28 14:48:41 +08:00
bytes.append("\x55\x55");
2023-03-24 18:41:48 +08:00
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);
//发送帧开始指令包
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) {
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++;
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
//发送帧结束指令包
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;
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));
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
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};
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; //超时
if(header->caplen<24) continue;
2023-03-24 18:41:48 +08:00
if(useOldProto) {
2023-07-28 14:48:41 +08:00
if(data[0]!=0x55 || data[1]!=0x55 || data[14]!=0x10 || data[15]!=0x5A) continue;
2023-03-24 18:41:48 +08:00
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
}
}
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);
}