ledset增加旧视频协议

This commit is contained in:
gangphon 2023-03-24 18:41:48 +08:00
parent a6c18c0549
commit d3a7cd2207
6 changed files with 156 additions and 60 deletions

View File

@ -9,7 +9,7 @@
pcap_t *pcapSend{0}; pcap_t *pcapSend{0};
PcapReThread *reThd{0}; PcapReThread *reThd{0};
QByteArray getNetDev(QWidget *parent, QByteArray def) { QByteArray getNetDev(QWidget *parent, QByteArray def, bool justReturnIfHave) {
char errbuf[PCAP_ERRBUF_SIZE]; char errbuf[PCAP_ERRBUF_SIZE];
pcap_if_t *devs; pcap_if_t *devs;
if(pcap_findalldevs(&devs, errbuf) == -1) { if(pcap_findalldevs(&devs, errbuf) == -1) {
@ -32,7 +32,7 @@ QByteArray getNetDev(QWidget *parent, QByteArray def) {
for(a=dev->addresses; a; a=a->next) { for(a=dev->addresses; a; a=a->next) {
auto sa_family = a->addr->sa_family; auto sa_family = a->addr->sa_family;
if(sa_family==AF_INET) { if(sa_family==AF_INET) {
if(! def.isEmpty() && dev->name == def) { if(justReturnIfHave && ! def.isEmpty() && dev->name == def) {
pcap_freealldevs(devs); pcap_freealldevs(devs);
table->deleteLater(); table->deleteLater();
return def; return def;
@ -84,7 +84,7 @@ QByteArray getNetDev(QWidget *parent, QByteArray def) {
table->setDefs(); table->setDefs();
table->setSelectionMode(QAbstractItemView::SingleSelection); table->setSelectionMode(QAbstractItemView::SingleSelection);
table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
table->selectRow(0); if(! def.isEmpty()) for(int r=0; r<table->rowCount(); r++) if(table->item(r, "name")->text()==def) table->selectRow(r);
QByteArray name; QByteArray name;
dlg->connect(table, &Table::cellDoubleClicked, dlg, [dlg, table, &name](int row) { dlg->connect(table, &Table::cellDoubleClicked, dlg, [dlg, table, &name](int row) {
name = table->item(row, "name")->text().toLocal8Bit(); name = table->item(row, "name")->text().toLocal8Bit();

View File

@ -10,7 +10,7 @@ extern pcap_t *pcapSend;
extern PcapReThread *reThd; extern PcapReThread *reThd;
QByteArray getNetDev(QWidget *parent, QByteArray); QByteArray getNetDev(QWidget *parent, QByteArray, bool);
int sendMsg(QByteArray, const Resp &resp); int sendMsg(QByteArray, const Resp &resp);
int sendMsgNet(QByteArray, const Resp &resp); int sendMsgNet(QByteArray, const Resp &resp);

View File

@ -131,7 +131,7 @@ MainWin::MainWin() {
auto btnNetSelect = new QPushButton("通讯选择"); auto btnNetSelect = new QPushButton("通讯选择");
btnNetSelect->setMinimumWidth(80); btnNetSelect->setMinimumWidth(80);
connect(btnNetSelect, &QPushButton::clicked, this, [this] { connect(btnNetSelect, &QPushButton::clicked, this, [this] {
auto name = getNetDev(this, ""); auto name = getNetDev(this, net_name, false);
if(name.isEmpty()) return; if(name.isEmpty()) return;
//PCAP_OPENFLAG_DATATX_UDP2它定义了数据传输假如是远程抓包是否用UDP协议来处理。 //PCAP_OPENFLAG_DATATX_UDP2它定义了数据传输假如是远程抓包是否用UDP协议来处理。
//PCAP_OPENFLAG_NOCAPTURE_RPCAP4它定义了远程探测器是否捕获它自己产生的数据包。 //PCAP_OPENFLAG_NOCAPTURE_RPCAP4它定义了远程探测器是否捕获它自己产生的数据包。
@ -168,7 +168,7 @@ MainWin::MainWin() {
QSettings config(QApplication::applicationDirPath()+"/ledset.config", QSettings::IniFormat); QSettings config(QApplication::applicationDirPath()+"/ledset.config", QSettings::IniFormat);
config.beginGroup("GLOBAL");//保存数据 config.beginGroup("GLOBAL");//保存数据
auto name = config.value("net_name").toByteArray(); auto name = config.value("net_name").toByteArray();
name = getNetDev(this, name); name = getNetDev(this, name, true);
if(! name.isEmpty()) { if(! name.isEmpty()) {
char errbuf[PCAP_ERRBUF_SIZE]{'\0'}; char errbuf[PCAP_ERRBUF_SIZE]{'\0'};
auto pcapRe = pcap_open_live(name.data(), 65536, PCAP_OPENFLAG_PROMISCUOUS|4|8|16, 50, errbuf); auto pcapRe = pcap_open_live(name.data(), 65536, PCAP_OPENFLAG_PROMISCUOUS|4|8|16, 50, errbuf);
@ -190,7 +190,7 @@ MainWin::MainWin() {
void MainWin::getCard() { void MainWin::getCard() {
auto msg = QByteArray::fromHex("55 55 01 0D 00 08 FF FF FF FF 00 00 AB CD A0 00 00 00 00 00 38 CB 84 7E 00 00 00 00 00 00 AB CD CD 04 04 46"); auto msg = QByteArray::fromHex("55 55 01 0D 00 08 FF FF FF FF 00 00 AB CD A0 00 00 00 00 00 38 CB 84 7E 00 00 00 00 00 00 AB CD CD 04 04 46");
auto res = sendMsg(msg, {0x10d, [=](const QByteArray data) { auto res = sendMsg(msg, {0x1E0, [=](const QByteArray data) {
if(((u_char)data[10])==0xA0 && data[11]==0 && data[12]==0 && data[13]==0) { if(((u_char)data[10])==0xA0 && data[11]==0 && data[12]==0 && data[13]==0) {
auto rr = 0; auto rr = 0;
table->setRowCount(rr+1); table->setRowCount(rr+1);

View File

@ -21,7 +21,7 @@ void PcapReThread::run() {
std::lock_guard<std::mutex> lock(mtx); std::lock_guard<std::mutex> lock(mtx);
for(int i=0; i<resps.size(); i++) if(resps[i].id==id) { for(int i=0; i<resps.size(); i++) if(resps[i].id==id) {
auto resp = resps.takeAt(i); auto resp = resps.takeAt(i);
emit onMsg(resp, QByteArray((char*)(data+4), header->caplen)); emit onMsg(resp, QByteArray((char*)(data+4), header->caplen-4));
break; break;
} }
} }

View File

@ -61,13 +61,22 @@ VideoWin::VideoWin(pcap_t *pcapRece, pcap_t *pcapSend, QWidget *parent) : BaseWi
fdMsecCapScr->setRange(1, 1000); fdMsecCapScr->setRange(1, 1000);
fdMsecCapScr->setValue(17); fdMsecCapScr->setValue(17);
hBox->addWidget(fdMsecCapScr); hBox->addWidget(fdMsecCapScr);
hBox->addSpacing(20); hBox->addSpacing(10);
fdNoReview = new QCheckBox("关闭显示"); fdNoReview = new QCheckBox("关闭显示");
fdNoReview->setChecked(noReview); fdNoReview->setChecked(noReview);
hBox->addWidget(fdNoReview); hBox->addWidget(fdNoReview);
hBox->addSpacing(20); 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);
hBox->addWidget(new QLabel("接收线程优先级")); hBox->addWidget(new QLabel("接收线程优先级"));
fdYxj = new QSpinBox; fdYxj = new QSpinBox;
@ -135,7 +144,30 @@ VideoWin::VideoWin(pcap_t *pcapRece, pcap_t *pcapSend, QWidget *parent) : BaseWi
connect(thdRece, &QThread::finished, this, [this] { connect(thdRece, &QThread::finished, this, [this] {
thdRece = 0; thdRece = 0;
}); });
connect(thdRece, &VideoRecThread::onMsg, fdCanvas, [this, fdCanvas](QImage img, int lostTimes, int lostPkts) { 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) {
if(noReview) return; if(noReview) return;
fdCanvas->img = img; fdCanvas->img = img;
fdCanvas->update(); fdCanvas->update();
@ -147,16 +179,16 @@ VideoWin::VideoWin(pcap_t *pcapRece, pcap_t *pcapSend, QWidget *parent) : BaseWi
}); });
thdRece->start(QThread::Priority(fdYxj->value()));//HighestPriority //InheritPriority //TimeCriticalPriority thdRece->start(QThread::Priority(fdYxj->value()));//HighestPriority //InheritPriority //TimeCriticalPriority
sendThd = new VideoSendThread(pcapSend); thdSend = new VideoSendThread(pcapSend);
connect(sendThd, &QThread::finished, this, [this] { connect(thdSend, &QThread::finished, this, [this] {
fdEnd->setChecked(true); fdEnd->setChecked(true);
sendThd = 0; thdSend = 0;
}); });
connect(sendThd, &VideoSendThread::onErr, this, [this](QString err) { connect(thdSend, &VideoSendThread::onErr, this, [this](QString err) {
fdEnd->setChecked(true); fdEnd->setChecked(true);
QMessageBox::critical(this, "Error", err); QMessageBox::critical(this, "Error", err);
}); });
sendThd->start(); thdSend->start();
} }
void VideoWin::timerEvent(QTimerEvent *event) { void VideoWin::timerEvent(QTimerEvent *event) {
@ -170,14 +202,14 @@ void VideoWin::timerEvent(QTimerEvent *event) {
auto img = pixmap.toImage(); auto img = pixmap.toImage();
img.convertTo(QImage::Format_RGB888); img.convertTo(QImage::Format_RGB888);
{ {
std::lock_guard<std::mutex> lock(sendThd->mtx); std::lock_guard<std::mutex> lock(thdSend->mtx);
if(sendThd->imgs.size()>2) return; if(thdSend->imgs.size()>2) return;
sendThd->imgs.append(img); thdSend->imgs.append(img);
} }
auto now_epoch = chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now().time_since_epoch()).count(); auto now_epoch = chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now().time_since_epoch()).count();
int dur = now_epoch - last_epoch; int dur = now_epoch - last_epoch;
last_epoch = now_epoch; last_epoch = now_epoch;
fdInfo->setText(info = "帧间隔: "+QString::number(dur)+" 帧缓存: "+QString::number(sendThd ? sendThd->imgs.size() : 0)); fdInfo->setText(info = "帧间隔: "+QString::number(dur)+" 帧缓存: "+QString::number(thdSend ? thdSend->imgs.size() : 0));
} }
} }
@ -197,21 +229,73 @@ void VideoSendThread::run() {
QThread::msleep(15); QThread::msleep(15);
continue; continue;
} }
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);
//发送帧开始指令包
auto queue = pcap_sendqueue_alloc(img.width()*img.height()*4); auto queue = pcap_sendqueue_alloc(img.width()*img.height()*4);
{ 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);
//发送帧开始指令包
struct pcap_pkthdr pktheader; struct pcap_pkthdr pktheader;
pktheader.len = pktheader.caplen = bytes.size(); pktheader.len = pktheader.caplen = bytes.size();
if(pcap_sendqueue_queue(queue, &pktheader, (u_char*)bytes.data()) == -1) { if(pcap_sendqueue_queue(queue, &pktheader, (u_char*)bytes.data()) == -1) {
@ -296,29 +380,34 @@ void VideoRecThread::run() {
if(status==2) return; if(status==2) return;
if(status==1 || res == 0 || noReview) continue; //超时 if(status==1 || res == 0 || noReview) continue; //超时
if(header->caplen<24) continue; if(header->caplen<24) continue;
if(data[0]!=0x55 || data[1]!=0x55) continue; if(useOldProto) {
if(data[2]==0x9a && data[3]==0x3d) { if(data[0]!=0 || data[1]!=0x11 || data[2]!=0x22 || data[3]!=0x33 || data[12]!=0x10 || data[13]!=0x5A) continue;
if(data[17]==0xBB) { emit onMsg(QByteArray((char*)data, header->caplen));
img = QImage(((int)data[24] << 8) + data[25], ((int)data[26] << 8) + data[27], QImage::Format_RGB888); } else {
lastIdx = -1; if(data[0]!=0x55 || data[1]!=0x55) continue;
lostTimes = 0; if(data[2]==0x9a && data[3]==0x3d) {
lostPkts = 0; if(data[17]==0xBB) {
} else if(data[17]==0xCC) emit onMsg(img, lostTimes, lostPkts); img = QImage(((int)data[24] << 8) + data[25], ((int)data[26] << 8) + data[27], QImage::Format_RGB888);
continue; lastIdx = -1;
} lostTimes = 0;
if(data[2]!=0x1 || data[3]!=0x33) continue; lostPkts = 0;
int len = ((int)data[4] << 8) + data[5]; } else if(data[17]==0xCC) emit onImg(img, lostTimes, lostPkts);
int row = ((int)data[14] << 8) + data[15]; continue;
int col = ((int)data[16] << 8) + data[17]; }
quint32 idx = ((quint32)data[24] << 24) + ((quint32)data[25] << 16) + ((quint32)data[26] << 8) + data[27]; if(data[2]!=0x1 || data[3]!=0x33) continue;
//auto epoch = chrono::duration_cast<chrono::microseconds>(chrono::steady_clock::now().time_since_epoch()).count(); int len = ((int)data[4] << 8) + data[5];
memcpy(img.bits()+row*img.bytesPerLine()+col, &data[28], len-4); int row = ((int)data[14] << 8) + data[15];
int diff = idx - lastIdx - 1; int col = ((int)data[16] << 8) + data[17];
lastIdx = idx; quint32 idx = ((quint32)data[24] << 24) + ((quint32)data[25] << 16) + ((quint32)data[26] << 8) + data[27];
if(diff > 0) { //auto epoch = chrono::duration_cast<chrono::microseconds>(chrono::steady_clock::now().time_since_epoch()).count();
//qDebug()<<"丢包"<<diff; memcpy(img.bits()+row*img.bytesPerLine()+col, &data[28], len-4);
lostTimes++; int diff = idx - lastIdx - 1;
lostPkts += diff; lastIdx = idx;
if(diff > 0) {
//qDebug()<<"丢包"<<diff;
lostTimes++;
lostPkts += diff;
}
} }
} }
emit onErr(pcap_geterr(pcap)); emit onErr(pcap_geterr(pcap));

View File

@ -23,6 +23,8 @@ public:
std::atomic<char> status{0}; std::atomic<char> status{0};
QList<QImage> imgs; QList<QImage> imgs;
std::mutex mtx; std::mutex mtx;
quint16 frameIdx{0};
bool useOldProto{false};
protected: protected:
void run(); void run();
signals: signals:
@ -38,10 +40,12 @@ public:
pcap *pcap; pcap *pcap;
std::atomic<char> status{0}; std::atomic<char> status{0};
bool noReview{false}; bool noReview{false};
bool useOldProto{false};
protected: protected:
void run(); void run();
signals: signals:
void onMsg(QImage, int, int); void onMsg(const QByteArray data);
void onImg(QImage, int, int);
void onErr(char *); void onErr(char *);
}; };
@ -59,16 +63,19 @@ public:
~VideoWin() { ~VideoWin() {
if(thdRece) thdRece->status = 2; if(thdRece) thdRece->status = 2;
if(sendThd) sendThd->status = 2; if(thdSend) thdSend->status = 2;
if(timerId) killTimer(timerId); if(timerId) killTimer(timerId);
} }
VideoRecThread *thdRece{0}; VideoRecThread *thdRece{0};
VideoSendThread *sendThd{0}; VideoSendThread *thdSend{0};
QList<QByteArray> imgLines;
int imgWidth{0}, imgHeight{0};
QScreen *screen; QScreen *screen;
int timerId{0}; int timerId{0};
QSpinBox *fdWidth, *fdHeight, *fdMsecCapScr,*fdYxj; QSpinBox *fdWidth, *fdHeight, *fdMsecCapScr,*fdYxj;
QCheckBox *fdNoReview; QCheckBox *fdNoReview;
bool noReview{false}; bool noReview{false};
bool useOldProto{false};
QRadioButton *fdEnd; QRadioButton *fdEnd;
QLabel *fdInfo, *fdLostTimes, *fdLostPkts; QLabel *fdInfo, *fdLostTimes, *fdLostPkts;
QString info; QString info;