ledset增加视频传输, ledok修改指定ip

This commit is contained in:
gangphon 2022-10-12 18:17:10 +08:00
parent 7626902534
commit caf3ef0add
9 changed files with 2386 additions and 72 deletions

View File

@ -22,11 +22,11 @@ DevicePanel::DevicePanel(QSettings &settings, QWidget *parent) : QWidget(parent)
pal.setBrush(QPalette::Window, QColor(0xeeeeee));
setPalette(pal);
auto vBox = new QVBoxLayout(this);
auto vBox = new VBox(this);
vBox->setContentsMargins(0, 0, 0, 0);
vBox->setSpacing(0);
auto hBox = new QHBoxLayout();
auto hBox = new HBox(vBox);
hBox->setContentsMargins(6, 2, 6, 2);
hBox->setSpacing(2);
@ -68,9 +68,6 @@ DevicePanel::DevicePanel(QSettings &settings, QWidget *parent) : QWidget(parent)
bnSpecifyIP->setGeometry(0, 0, areaFlash->width(), areaFlash->height());
bnSpecifyIP->setItemDelegate(new QStyledItemDelegate);
connect(bnSpecifyIP, &QComboBox::textActivated, this, [this] {
if(specifyIPDlg==0) {
}
mUdpTimer.stop();
specifyIPDlg->exec();
mUdpTimer.start(30000);
@ -111,8 +108,6 @@ QPushButton:hover {background-color: #08b;}
hBox->addWidget(areaFlash);
vBox->addLayout(hBox);
mHBox = new QHBoxLayout();
mDeviceTable = new LoQTreeWidget(this);
@ -190,66 +185,59 @@ QPushButton:hover {background-color: #08b;}
connect(mDeviceTable, SIGNAL(sigCheckStateChanged(int)), this, SLOT(onCheckStateChanged(int)));
mDeviceTable->setSelectionMode(QAbstractItemView::SingleSelection);
//开始轮训查找局域网内的控制卡
sendGetInfo();
mUdpTimer.start(30000);
{
specifyIPDlg = new QDialog(this);
specifyIPDlg->setWindowTitle(tr("Specify IP"));
auto vBox = new VBox(specifyIPDlg);
vBox->addStretch();
auto label = new QLabel(tr("Specify IP list"));
label->setAlignment(Qt::AlignCenter);
vBox->addWidget(label);
auto ipstr = settings.value("SpecifyIP").toString();
fdIP = new QTextEdit(ipstr);
fdIP->setPlaceholderText("192.168.1.1\n192.168.1.2\n ...");
fdIP->setMinimumWidth(160);
vBox->addWidget(fdIP);
auto hBox = new HBox(vBox);
hBox->addStretch();
auto bnSearch = new QPushButton(tr("Search"));
connect(bnSearch, &QPushButton::clicked, this, [this] {
QString ipsStr = fdIP->toPlainText();
if(ipsStr.isEmpty()) {
QMessageBox::warning(gMainWin, tr("Attention"), tr("Please input IP address!"));
return;
}
auto ips = ipsStr.split("\n", Qt::SkipEmptyParts);
if(ips.isEmpty()) {
QMessageBox::warning(gMainWin, tr("Attention"), tr("Please input IP address!"));
return;
}
QByteArray data = QJsonDocument({{"action", "getInfo"}}).toJson(QJsonDocument::Compact);
foreach(auto ip, ips) {
if(gDevicePanel->mUdpSocket.writeDatagram(data, QHostAddress(ip), 22222) != data.length()) qDebug() << "Specify IP write Failed." << ip;
}
specifyIPDlg->accept();
});
hBox->addWidget(bnSearch);
auto btnCancel = new QPushButton(tr("Cancel"));
connect(btnCancel, &QPushButton::clicked, specifyIPDlg, &QDialog::reject);
hBox->addWidget(btnCancel);
hBox->addStretch();
if(! ipstr.isEmpty()) {
auto ips = ipstr.split("\n", Qt::SkipEmptyParts);
QByteArray data = QJsonDocument({{"action", "getInfo"}}).toJson(QJsonDocument::Compact);
foreach(auto ip, ips) {
if(gDevicePanel->mUdpSocket.writeDatagram(data, QHostAddress(ip), 22222) != data.length()) qDebug() << "Specify IP write Failed." << ip;
}
}
}
mDeviceTable->hideColumn(0);
mDeviceTable->fdIsSelAll->hide();
specifyIPDlg = new QDialog(this);
specifyIPDlg->setWindowTitle(tr("Specify IP"));
vBox = new VBox(specifyIPDlg);
vBox->addStretch();
label = new QLabel(tr("Specify IP list"));
label->setAlignment(Qt::AlignCenter);
vBox->addWidget(label);
fdIP = new QTextEdit;
auto ipstr = settings.value("SpecifyIP").toString();
if(! ipstr.isEmpty()) fdIP->setText(ipstr);
fdIP->setPlaceholderText("192.168.1.1\n192.168.1.2\n ...");
fdIP->setMinimumWidth(160);
vBox->addWidget(fdIP);
hBox = new HBox(vBox);
hBox->addStretch();
auto bnSearch = new QPushButton(tr("Search"));
connect(bnSearch, &QPushButton::clicked, this, [this] {
QString ipsStr = fdIP->toPlainText();
if(ipsStr.isEmpty()) {
QMessageBox::warning(gMainWin, tr("Attention"), tr("Please input IP address!"));
return;
}
auto ips = ipsStr.split("\n", Qt::SkipEmptyParts);
if(ips.isEmpty()) {
QMessageBox::warning(gMainWin, tr("Attention"), tr("Please input IP address!"));
return;
}
QByteArray data = QJsonDocument({{"action", "getInfo"}}).toJson(QJsonDocument::Compact);
foreach(auto ip, ips) {
if(gDevicePanel->mUdpSocket.writeDatagram(data, QHostAddress(ip), 22222) != data.length()) qDebug() << "Specify IP write Failed." << ip;
}
specifyIPDlg->accept();
});
hBox->addWidget(bnSearch);
auto btnCancel = new QPushButton(tr("Cancel"));
connect(btnCancel, &QPushButton::clicked, specifyIPDlg, &QDialog::reject);
hBox->addWidget(btnCancel);
hBox->addStretch();
transUi();
sendGetInfo();
mUdpTimer.start(30000);
}
DevicePanel::~DevicePanel() {
@ -276,6 +264,13 @@ void DevicePanel::sendGetInfo() {
if(mUdpSocket.writeDatagram(data, broadcast, 22222) != data.length()) qDebug() << "getInfo writeDatagram Failed." << ip.toString() << "->" << broadcast.toString();
}
}
auto ipstr = fdIP->toPlainText();
if(! ipstr.isEmpty()) {
auto ips = ipstr.split("\n", Qt::SkipEmptyParts);
foreach(auto ip, ips) {
if(gDevicePanel->mUdpSocket.writeDatagram(data, QHostAddress(ip), 22222) != data.length()) qDebug() << "Specify IP write Failed." << ip;
}
}
}
void DevicePanel::ShowDevicesInfoOrControlWidget(int index){

View File

@ -399,8 +399,8 @@ MainWindow::~MainWindow() {
settings.setValue("MainIsMax", isMaximized());
settings.setValue("PlayPos", gPlayPos);
if(mDevicePanel->fdIP) {
auto ips = mDevicePanel->fdIP->toPlainText();
if(! ips.isEmpty()) settings.setValue("SpecifyIP", ips);
auto ipstr = mDevicePanel->fdIP->toPlainText();
if(! ipstr.isEmpty()) settings.setValue("SpecifyIP", ipstr);
}
if(m_pTimerSendResoreIpOneKey!=nullptr) {
if(m_pTimerSendResoreIpOneKey->isActive()) m_pTimerSendResoreIpOneKey->stop();

1827
ledset/crc.c Normal file

File diff suppressed because it is too large Load Diff

51
ledset/crc.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef CRC_H
#define CRC_H
#ifdef __cplusplus
extern "C"{
#endif
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
//void crc8_GenerateTable();
//void crc8_rohc_GenerateTable();
//void crc4_itu_GenerateTable();
//void crc5_itu_GenerateTable();
uint8_t crc4_itu_calc (uint8_t *data, uint32_t length);
uint8_t crc5_epc_calc (uint8_t *data, uint32_t length);
uint8_t crc5_itu_calc (uint8_t *data, uint32_t length);
uint8_t crc5_usb_calc (uint8_t *data, uint32_t length);
uint8_t crc6_itu_calc (uint8_t *data, uint32_t length);
uint8_t crc7_mmc_calc (uint8_t *data, uint32_t length);
uint8_t crc8_Calc (uint8_t *data, uint32_t length);
uint8_t crc8_itu_calc (uint8_t *data, uint32_t length);
uint8_t crc8_rohc_Calc (uint8_t *data, uint32_t length);
uint8_t crc8_maxim_calc (uint8_t *data, uint32_t length);
uint16_t crc16_ibm_calc (uint8_t *data, uint32_t length);
uint16_t crc16_maxim_calc (uint8_t *data, uint32_t length);
uint16_t crc16_usb_calc (uint8_t *data, uint32_t length);
uint16_t crc16_modbus_calc (uint8_t *data, uint32_t length);
uint16_t crc16_ccitt_calc (uint8_t *data, uint32_t length);
uint16_t crc16_ccitt_false_calc (uint8_t *data, uint32_t length);
uint16_t crc16_x25_calc (uint8_t *data, uint32_t length);
uint16_t crc16_xmodem_calc (uint8_t *data, uint32_t length);
uint16_t crc16_dnp_calc (uint8_t *data, uint32_t length);
uint32_t crc32_calc (uint8_t *data, uint32_t length);
uint32_t crc32_mpeg_2_calc (uint8_t *data, uint32_t length);
#ifdef __cplusplus
}
#endif
#endif // CRC_H

View File

@ -27,6 +27,7 @@ win32 {
SOURCES += \
basewin.cpp \
brightwin.cpp \
crc.c \
expertscreenconnwin.cpp \
expertsmartpointsetwin.cpp \
expertwin.cpp \
@ -37,11 +38,13 @@ SOURCES += \
pcapthread.cpp \
pcapwin.cpp \
screenunit.cpp \
table.cpp
table.cpp \
videowin.cpp
HEADERS += \
basewin.h \
brightwin.h \
crc.h \
expertscreenconnwin.h \
expertsmartpointsetwin.h \
expertwin.h \
@ -51,7 +54,8 @@ HEADERS += \
pcapthread.h \
pcapwin.h \
screenunit.h \
table.h
table.h \
videowin.h
TRANSLATIONS += \
translations/app_en.ts

View File

@ -3,6 +3,7 @@
#include "expertwin.h"
#include "brightwin.h"
#include "pcapwin.h"
#include "videowin.h"
#include "gqt.h"
#include <QLabel>
#include <QPushButton>
@ -12,6 +13,8 @@
#include <QPainter>
#include <QPainterPath>
#include <QTimer>
#include <QMessageBox>
#include <QDebug>
class ImgBtn : public QToolButton {
public:
@ -104,9 +107,10 @@ MainWin::MainWin() {
auto ins = PcapWin::newIns(this);
if(ins) ins->show();
});
btnImg = addImg(imgsBar, QPixmap(":/imgs/idea.png").scaledToWidth(128, Qt::SmoothTransformation), "创意显示");
btnImg = addImg(imgsBar, QPixmap(":/imgs/idea.png").scaledToWidth(128, Qt::SmoothTransformation), "视频传输");
connect(btnImg, &ImgBtn::clicked, this, [=](){
//qDebug()<<"geometry"<<win->geometry()<<"screen"<<win->screen();
auto ins = VideoWin::newIns(this);
ins->show();
});
vBox->addLayout(imgsBar);

View File

@ -23,9 +23,7 @@ class CapThread : public QThread {
Q_OBJECT
public:
explicit CapThread(pcap *pcap, QObject *parent = nullptr);
void setPacketList(QList<Packet> *p){p->clear();packet_list=p;}
pcap *pcap{0};
QList<Packet> *packet_list;
Packet packet;
protected:
void run();

360
ledset/videowin.cpp Normal file
View File

@ -0,0 +1,360 @@
#include "videowin.h"
#include "table.h"
#include "gqt.h"
#include "crc.h"
#include <QTextEdit>
#include <QPushButton>
#include <QMessageBox>
#include <QDateTime>
#include <QTimerEvent>
#include <QApplication>
#include <QDesktopWidget>
#include <QScreen>
#include <QDebug>
using namespace std;
QByteArray getNetDev(QWidget *parent) {
char errbuf[PCAP_ERRBUF_SIZE];
pcap_if_t *devs;
if(pcap_findalldevs(&devs, errbuf) == -1) {
QMessageBox::critical(parent, "Error", QString("寻找网卡失败")+errbuf);
return QByteArray();
}
auto table = new Table{
{"name", "网卡名称", 400},
{"desc", "网卡描述", 300},
{"loopback","Loopback"},
{"ip","IP"},
{"netmask","Netmask"},
{"broadaddr","Broad Addr"},
{"dstaddr","Dst Addr"}
};
pcap_if_t *device = 0;
for(pcap_if_t *dev = devs; dev; dev = dev->next) {
pcap_addr_t *a;
for(a=dev->addresses; a; a=a->next) {
auto sa_family = a->addr->sa_family;
if(sa_family==AF_INET) {
auto rr = table->rowCount();
table->setRowCount(rr+1);
table->setValue(rr, "name", dev->name);
table->setValue(rr, "desc", dev->description);
table->setValue(rr, "loopback", (dev->flags & PCAP_IF_LOOPBACK)?"True":"False");
auto ip = (u_char *) &((sockaddr_in *) a->addr)->sin_addr.s_addr;
table->setValue(rr, "ip", QString::asprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]));
if(a->netmask) {
ip = (u_char *) &((sockaddr_in *) a->netmask)->sin_addr.s_addr;
table->setValue(rr, "netmask", QString::asprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]));
}
if(a->broadaddr) {
ip = (u_char *) &((sockaddr_in *) a->broadaddr)->sin_addr.s_addr;
table->setValue(rr, "broadaddr", QString::asprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]));
}
if(a->dstaddr) {
ip = (u_char *) &((sockaddr_in *) a->dstaddr)->sin_addr.s_addr;
table->setValue(rr, "dstaddr", QString::asprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]));
}
if(device==0) device = dev;
}
}
}
pcap_freealldevs(devs);
auto rowCnt = table->rowCount();
if(rowCnt==0) {
table->deleteLater();
QMessageBox::critical(parent, "Error", "没找到 internet 设备");
return QByteArray();
} else if(rowCnt==1) {
table->deleteLater();
return table->item(0, "name")->text().toLocal8Bit();
} else {
auto dlg = new BaseDlg(parent);
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->setWindowTitle("选择网卡");
dlg->resize(1200, 300);
auto vBox = new QVBoxLayout(dlg->center);
vBox->setContentsMargins(0,0,0,0);
vBox->setSpacing(3);
vBox->addSpacing(30);
table->setDefs();
table->setSelectionMode(QAbstractItemView::SingleSelection);
table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
table->selectRow(0);
QByteArray name;
dlg->connect(table, &Table::cellDoubleClicked, dlg, [dlg, table, &name](int row) {
name = table->item(row, "name")->text().toLocal8Bit();
dlg->accept();
});
vBox->addWidget(table);
auto hBox = new QHBoxLayout;
hBox->addStretch();
auto btnOk = new QPushButton("确定");
btnOk->setMinimumWidth(80);
dlg->connect(btnOk, &QPushButton::clicked, dlg, [dlg, table, &name] {
auto sels = table->selectedRanges();
if(sels.isEmpty()) {
QMessageBox::warning(dlg, "Warning", "请选择网卡");
return;
}
name = table->item(sels[0].topRow(), "name")->text().toLocal8Bit();
dlg->accept();
});
hBox->addWidget(btnOk);
hBox->addStretch();
auto btnClose = new QPushButton("关闭");
btnClose->setMinimumWidth(80);
dlg->connect(btnClose, &QPushButton::clicked, dlg, &QDialog::reject);
hBox->addWidget(btnClose);
hBox->addStretch();
vBox->addLayout(hBox);
dlg->exec();
return name;
}
}
VideoWin *VideoWin::newIns(QWidget *parent) {
auto name = getNetDev(parent);
if(name.isEmpty()) return 0;
char errbuf[PCAP_ERRBUF_SIZE]{'\0'};
auto pcapRe = pcap_open(name.data(), 65535, PCAP_OPENFLAG_PROMISCUOUS | PCAP_OPENFLAG_MAX_RESPONSIVENESS, 15, 0, errbuf);
if(pcapRe == 0) {
QMessageBox::critical(parent, "Error", QString("打开网卡失败")+errbuf);
return 0;
}
auto pcapSend = pcap_open(name.data(), 65535, 0, 15, 0, 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);
fdWidth->setValue(320);
hBox->addWidget(fdWidth);
hBox->addWidget(new QLabel("x"));
fdHeight = new QSpinBox;
fdHeight->setRange(1, 9999);
fdHeight->setValue(180);
hBox->addWidget(fdHeight);
hBox->addSpacing(20);
auto fdStart = new QRadioButton("发送");
connect(fdStart, &QRadioButton::toggled, this, [this](bool checked) {
if(! checked) return;
screen = QGuiApplication::primaryScreen();
if(timerId==0) timerId = startTimer(17);
});
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("接收:"));
hBox->addStretch();
auto fdReceive = new QLabel;
vBox->addWidget(fdReceive, 1);
thdRece = new VideoRecThread(pcapRece);
connect(thdRece, &QThread::finished, this, [this] {
thdRece = 0;
});
connect(thdRece, &VideoRecThread::onMsg, fdReceive, [fdReceive](QImage img) {
fdReceive->setPixmap(QPixmap::fromImage(img));
});
thdRece->start();
sendThd = new VideoSendThread(pcapSend);
connect(sendThd, &QThread::finished, this, [this] {
fdEnd->setChecked(true);
sendThd = 0;
});
connect(sendThd, &VideoSendThread::onErr, this, [this](QString err) {
fdEnd->setChecked(true);
QMessageBox::critical(this, "Error", err);
});
sendThd->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);
{
std::lock_guard<std::mutex> lock(sendThd->mtx);
if(sendThd->imgs.size()>2) return;
sendThd->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;
fdInfo->setText(info = "帧间隔: "+QString::number(dur)+" 帧缓存: "+QString::number(sendThd ? sendThd->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;
while(status!=2) {
{
std::lock_guard<std::mutex> lock(mtx);
img = imgs.isEmpty() ? QImage() : imgs.takeFirst();
}
if(img.isNull()) {
QThread::msleep(15);
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);
{
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(uint i=0; i<img.height(); i++) for(uint 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);
bytes.append(i>>24).append(i>>16).append(i>>8).append(i);
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();
if(pcap_sendqueue_queue(queue, &pktheader, (u_char*)bytes.data()) == -1) {
onErr(QString("添加结束失败: ")+pcap_geterr(pcap));
goto end;
}
int res;
if((res = pcap_sendqueue_transmit(pcap, queue, 0)) < queue->len) {
onErr(QString::asprintf("发送包出错: %s. 仅发了 %d bytes", pcap_geterr(pcap), res));
goto end;
}
}
end:
pcap_sendqueue_destroy(queue);
}
}
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;
while((res = pcap_next_ex(pcap, &header, &data)) >= 0) {
if(status==2) return;
if(status==1 || res == 0) continue; //超时
if(header->caplen<24) continue;
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);
else if(data[17]==0xCC) emit onMsg(img);
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];
memcpy(img.bits()+row*img.bytesPerLine()+col, &data[28], len-4);
}
emit onErr(pcap_geterr(pcap));
}

75
ledset/videowin.h Normal file
View File

@ -0,0 +1,75 @@
#ifndef VIDEOWIN_H
#define VIDEOWIN_H
#include "basewin.h"
#define HAVE_REMOTE
#include <winsock2.h>
#include "pcap.h"
#include <QThread>
#include <QSpinBox>
#include <QLabel>
#include <QRadioButton>
#include "Win32-Extensions.h"
#pragma comment(lib, "wpcap.lib")
#pragma comment(lib, "Ws2_32.lib")
QByteArray getNetDev(QWidget *parent);
class VideoSendThread : public QThread {
Q_OBJECT
public:
explicit VideoSendThread(pcap *pcap);
~VideoSendThread() {
pcap_close(pcap);
}
pcap *pcap;
std::atomic<char> status{0};
QList<QImage> imgs;
std::mutex mtx;
protected:
void run();
signals:
void onErr(QString);
};
class VideoRecThread : public QThread {
Q_OBJECT
public:
explicit VideoRecThread(pcap *pcap);
~VideoRecThread() {
pcap_close(pcap);
}
pcap *pcap;
std::atomic<char> status{0};
protected:
void run();
signals:
void onMsg(QImage);
void onErr(char *);
};
class VideoWin : public BaseWin {
Q_OBJECT
public:
static VideoWin *newIns(QWidget *);
explicit VideoWin(pcap *, pcap *, QWidget *parent = nullptr);
~VideoWin() {
if(thdRece) thdRece->status = 2;
if(sendThd) sendThd->status = 2;
if(timerId) killTimer(timerId);
}
VideoRecThread *thdRece{0};
VideoSendThread *sendThd{0};
QScreen *screen;
int timerId{0};
QSpinBox *fdWidth, *fdHeight;
QRadioButton *fdEnd;
QLabel *fdInfo;
QString info;
qint64 last_epoch{0};
protected:
void timerEvent(QTimerEvent *event) override;
};
#endif // VIDEOWIN_H