Compare commits

..

4 Commits

3
.gitignore vendored

@ -80,6 +80,3 @@ CMakeLists.txt.user*
.rcc/
.uic/
/build*/
.vs/
*.json
/out/

@ -13,11 +13,14 @@ qt_add_executable(VideoClient
videoControl.h videoControl.ui videoControl.cpp
wprogressbar.h wprogressbar.cpp
webmapwidget.h webmapwidget.cpp
utils/CUdpSocket.cpp utils/CUdpSocket.h
utils/protocolPodTJ.cpp utils/protocolPodTJ.h
utils/switchbutton.h utils/switchbutton.cpp
joystickcontroller.h joystickcontroller.cpp
directinputjoystick.h directinputjoystick.cpp
)
qt6_add_resources(VideoClient "resources"
PREFIX "/"
FILES "resources/res.qrc"
@ -30,7 +33,7 @@ set(FFMPEG_DIR ${3RDPARTY_DIR}/ffmpeg)
# add_subdirectory(shared)
add_subdirectory(video)
add_subdirectory(3rdparty)
# add_subdirectory(utils)
target_link_libraries(VideoClient
PRIVATE
@ -41,7 +44,11 @@ target_link_libraries(VideoClient
Qt6::Network
Qt6::SerialPort
Qt6::WebEngineWidgets
#Qt6::Gamepad
video
winmm
dinput8
dxguid
# QWindowKit::Widgets
# WidgetFrame
)

@ -1,8 +1,6 @@
#include "commandwidget.h"
#include "ui_commandwidget.h"
#include <QIntValidator>
#include "utils/switchbutton.h"
CommandWidget::CommandWidget(QWidget *parent)
: QWidget(parent), ui(new Ui::CommandWidget) {
@ -15,10 +13,6 @@ CommandWidget::CommandWidget(QWidget *parent)
} else {
qDebug() << "绑定 UDP 套接字失败";
}
//创建自定义套接字
m_CUdpSocket = new CUdpSocket(this);
initmUdpParam();
}
CommandWidget::~CommandWidget() {
@ -84,21 +78,6 @@ void CommandWidget::buttonResponse(quint8 cmd) {
}
}
void CommandWidget::receiveUavTypeIndex(int index)
{
switch (index)
{
case FP98:
ui->cmdStackedWidget->setCurrentWidget(ui->stackedWidgetPage1);
break;
case MEDICAL_UNIVERSITY:
ui->cmdStackedWidget->setCurrentWidget(ui->stackedWidgePage2);
break;
default:
break;
}
}
void CommandWidget::on_pushButton_clicked() {
buttonResponse(0x75);
}
@ -168,465 +147,67 @@ void CommandWidget::on_pushButton_15_clicked() {
}
/** 武汉天进吊舱 */
void CommandWidget::on_pushButton_left_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("7000"),
m_protocolPodTJ.cmdLeft(ui->cmdSlider->value())));
}
void CommandWidget::on_pushButton_left_released()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("7000"),
QByteArray::fromHex("00000000")));
}
void CommandWidget::on_pushButton_right_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("7000"),
m_protocolPodTJ.cmdRight(ui->cmdSlider->value())));
}
void CommandWidget::on_pushButton_right_released()
{
on_pushButton_left_released();
}
void CommandWidget::on_pushButton_up_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("7000"),
m_protocolPodTJ.cmdUp(ui->cmdSlider->value())));
}
void CommandWidget::on_pushButton_up_released()
{
on_pushButton_left_released();
}
void CommandWidget::onButtonChanged(int button, bool pressed)
{
qDebug() << "Button" << button << (pressed ? "Pressed" : "Released");
// switch (button)
// {
// case 0:
// on_pushButton_clicked();
// break;
// case 1:
// on_pushButton_2_clicked();
// break;
// case 2:
// on_pushButton_3_clicked();
// break;
// case 3:
// on_pushButton_clicked();
// break;
// case 4:
// on_pushButton_2_clicked();
// break;
// case 5:
// on_pushButton_3_clicked();
// break;
// case 6:
// on_pushButton_clicked();
// break;
// case 7:
// on_pushButton_2_clicked();
// break;
// case 8:
// on_pushButton_3_clicked();
// break;
// case 9:
// on_pushButton_clicked();
// break;
// case 10:
// on_pushButton_2_clicked();
// break;
// case 11:
// on_pushButton_3_clicked();
// break;
// case 12:
// on_pushButton_clicked();
// break;
// case 13:
// on_pushButton_2_clicked();
// break;
// case 14:
// on_pushButton_3_clicked();
// break;
// }
}
void CommandWidget::onDirectionChanged(int direction){
direction!=42949672?qDebug() << "direction" << direction:qDebug() << "center";
void CommandWidget::on_pushButton_down_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("7000"),
m_protocolPodTJ.cmdDown(ui->cmdSlider->value())));
}
void CommandWidget::on_pushButton_down_released()
{
on_pushButton_left_released();
}
void CommandWidget::on_pushButton_center_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("7100")));
}
void CommandWidget::on_pushButton_guid_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("7200"),
m_protocolPodTJ.digitalGuid(ui->guidLineEdit->text().toInt(), ui->poseLineEdit->text().toInt())));
}
void CommandWidget::on_pushButton_pose_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("7C00"),
m_protocolPodTJ.digitalGuid(ui->guidLineEdit->text().toInt(), ui->poseLineEdit->text().toInt())));
}
void CommandWidget::on_pushButton_directL_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("7A00")));
}
void CommandWidget::on_pushButton_directS_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("7B00")));
}
void CommandWidget::on_pushButton_lookDown_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("7300")));
}
void CommandWidget::on_pushButton_packUp_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("7400")));
}
void CommandWidget::on_pushButton_scanning_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("7900")));
}
void CommandWidget::on_pushButton_close_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("7500")));
}
void CommandWidget::on_comboBox1_activated(int index)
{
if (index >= 0)
{
QByteArray data = ui->comboBox1->currentData().toByteArray();
//qDebug() << data.toHex();
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("2300"),data));
}
}
void CommandWidget::on_comboBox2_activated(int index)
{
if (index >= 0)
{
QByteArray data = ui->comboBox2->currentData().toByteArray();
//qDebug() << data.toHex();
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("2500"), data));
}
}
void CommandWidget::on_pushButton_videoSwitch_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("3100")));
}
void CommandWidget::on_pushButton_video_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("3300")));
}
void CommandWidget::on_pushButton_photo_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("3200")));
}
void CommandWidget::on_pushButton_zoomp_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("4500"),
m_protocolPodTJ.cmdZoom(0x01,ui->comboBox3->currentIndex())));
}
void CommandWidget::on_pushButton_zoomp_released()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("4500")));
}
void CommandWidget::on_pushButton_zoomd_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("4500"),
m_protocolPodTJ.cmdZoom(0x02, ui->comboBox3->currentIndex())));
}
void CommandWidget::on_pushButton_zoomd_released()
{
on_pushButton_zoomp_released();
}
void CommandWidget::on_pushButton_focusP_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("4500"),
m_protocolPodTJ.cmdZoom(0x03, ui->comboBox3->currentIndex())));
}
void CommandWidget::on_pushButton_focusP_released()
{
on_pushButton_zoomp_released();
}
void CommandWidget::on_pushButton_focusD_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("4500"),
m_protocolPodTJ.cmdZoom(0x04, ui->comboBox3->currentIndex())));
}
void CommandWidget::on_pushButton_focusD_released()
{
on_pushButton_zoomp_released();
}
void CommandWidget::on_pushButton_auto_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("4500"),
m_protocolPodTJ.cmdZoom(0x05, 0x00)));
}
void CommandWidget::on_pushButton_auto_released()
{
on_pushButton_zoomp_released();
}
void CommandWidget::on_pushButton_contrastP_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("4100"),
QByteArray::fromHex("01010000")));
}
void CommandWidget::on_pushButton_contrastD_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("4100"),
QByteArray::fromHex("01020000")));
}
void CommandWidget::on_pushButton_lightP_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("4100"),
QByteArray::fromHex("02010000")));
}
void CommandWidget::on_pushButton_lightD_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("4100"),
QByteArray::fromHex("02020000")));
}
void CommandWidget::on_comboBox4_activated(int index)
{
if (index >= 0)
{
QByteArray data = ui->comboBox4->currentData().toByteArray();
//qDebug() << data.toHex();
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("4A00"), data));
}
}
void CommandWidget::on_pushButton_zoomp_2_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("5000"),
QByteArray::fromHex("0F000000")));
}
void CommandWidget::on_pushButton_zoomp_2_released()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("5000"),
QByteArray::fromHex("00000000")));
}
void CommandWidget::on_pushButton_zoomd_2_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("5000"),
QByteArray::fromHex("10000000")));
}
void CommandWidget::on_pushButton_zoomd_2_released()
{
on_pushButton_zoomp_2_released();
}
void CommandWidget::on_pushButton_contrastP_2_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("5A00")));
}
void CommandWidget::on_pushButton_contrastD_2_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("5A00"),
QByteArray::fromHex("01000000")));
}
void CommandWidget::on_pushButton_color_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("5300")));
}
void CommandWidget::on_comboBox5_activated(int index)
{
if (index >= 0)
{
QByteArray data = ui->comboBox5->currentData().toByteArray();
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("5300"), data));
}
}
void CommandWidget::on_pushButton_distance_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("3D00")));
}
void CommandWidget::on_pushButton_distanceC_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("3E00"),
m_protocolPodTJ.cmdLaser(ui->comboBox6->currentIndex() + 1)));
}
void CommandWidget::on_pushButton_distanceS_pressed()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("3F00")));
}
void CommandWidget::on_cmdLineEdit_textChanged(const QString text)
{
qDebug() << text;
if (text.toInt() < 10)//数据小于10不响应
{
return;
}
ui->cmdSlider->setMaximum(text.toInt());
}
void CommandWidget::wheelZoomCtrl(int value)
{
if (value > 0)//鼠标前滚
{
void on_pushButton_zoomp_pressed();
qDebug() << "滚轮变倍+" << value;
}
else if (value < 0)
{
void on_pushButton_zoomd_pressed();
qDebug() << "滚轮变倍-" << value;
}
else if (value == 0)
{
void on_pushButton_zoomp_released();
qDebug() << "停止变倍:" << value;
}
}
void CommandWidget::setPodIPConfig(NetworkIPStruct networkIP, bool isConnect)
{
qDebug() << "ip设置槽函数调用了";
if (!isConnect)
{
m_CUdpSocket->close();
return;
}
if (m_CUdpSocket->close())
{
if (!m_CUdpSocket->bind(networkIP.localIP, networkIP.localPort))
{
qDebug() << "端口号绑定失败";
}
if (!m_CUdpSocket->joinMulticast())
{
qDebug() << "加入组播失败";
}
//绑定目标IP Port
m_CUdpSocket->setTargetInfo(networkIP.remoteIP, networkIP.remotePort);
}
}
void CommandWidget::setMoveParm(QPointF size)
{
int x = std::clamp(static_cast<int>(size.x() / 10), -60, 60);
int y = std::clamp(static_cast<int>(-size.y() / 10), -60, 60);
qDebug() << "缩减之后的屏幕滑动参数==>" << "x:" << x << "y:" << y;
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("7000"),
m_protocolPodTJ.cmdServo(x, y)));
}
void CommandWidget::stopServo()
{
on_pushButton_left_released();
}
void CommandWidget::setTargetTracking(double dir, double pitch)
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("3A00"),
m_protocolPodTJ.targetTracking(static_cast<int>(std::round(dir)), static_cast<int>(std::round(pitch)))));
}
void CommandWidget::setStopTracking()
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("3B00")));
}
/***武汉天进吊舱控制***/
void CommandWidget::initmUdpParam()
{
QIntValidator* validator = new QIntValidator(0, 100, this);
ui->cmdLineEdit->setValidator(validator);
ui->cmdLineEdit->setText("40");
ui->cmdSlider->setValue(20);
//AI开关
connect(ui->switchButton1, &WBSwitchButton::sigSwitchChanged, this, [=](bool onOroff){
if (onOroff) //开关打开
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("9100"),
QByteArray::fromHex("01000000")));
}
else
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("9100")));
}
});
ui->comboBox1->addItem("16 * 16", QByteArray::fromHex("01000000"));
ui->comboBox1->addItem("32 * 32", QByteArray::fromHex("02000000"));
ui->comboBox1->addItem("64 * 64", QByteArray::fromHex("03000000"));
ui->comboBox1->addItem("128 * 128", QByteArray::fromHex("04000000"));
ui->comboBox2->addItem("红外", QByteArray::fromHex("00000000"));
ui->comboBox2->addItem("可见光", QByteArray::fromHex("01000000"));
ui->comboBox2->addItem("上下分屏", QByteArray::fromHex("02000000"));
ui->comboBox2->addItem("画中画1", QByteArray::fromHex("03000000"));
ui->comboBox2->addItem("画中画2", QByteArray::fromHex("04000000"));
ui->comboBox2->addItem("左右分屏", QByteArray::fromHex("05000000"));
ui->comboBox3->setCurrentIndex(4);
//低照度
connect(ui->btn_light, &WBSwitchButton::sigSwitchChanged, this, [=](bool onOroff) {
if (onOroff) //开关打开
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("4B00"),
QByteArray::fromHex("01000000")));
}
else
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("4B00")));
}
});
//电子稳像
connect(ui->btn_stable, &WBSwitchButton::sigSwitchChanged, this, [=](bool onOroff) {
if (onOroff) //开关打开
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("4100"),
QByteArray::fromHex("04010000")));
}
else
{
m_CUdpSocket->sendData(ProtocolPodTJ::encode(QByteArray::fromHex("4100"),
QByteArray::fromHex("04020000")));
}
});
//图像去雾
ui->comboBox4->addItem("", QByteArray::fromHex("00000000"));
ui->comboBox4->addItem("", QByteArray::fromHex("01000000"));
ui->comboBox4->addItem("", QByteArray::fromHex("02000000"));
ui->comboBox4->addItem("", QByteArray::fromHex("03000000"));
//伪彩选择
ui->comboBox5->addItem("白热", QByteArray::fromHex("01000000"));
ui->comboBox5->addItem("黑热", QByteArray::fromHex("01010000"));
ui->comboBox5->addItem("蓝红黄", QByteArray::fromHex("01020000"));
ui->comboBox5->addItem("紫红黄", QByteArray::fromHex("01030000"));
ui->comboBox5->addItem("蓝绿红", QByteArray::fromHex("01040000"));
ui->comboBox5->addItem("彩虹1", QByteArray::fromHex("01050000"));
ui->comboBox5->addItem("彩虹2", QByteArray::fromHex("01060000"));
ui->comboBox5->addItem("黑-红", QByteArray::fromHex("01070000"));
ui->comboBox5->addItem("墨绿-红", QByteArray::fromHex("01080000"));
ui->comboBox5->addItem("蓝绿红-粉", QByteArray::fromHex("01090000"));
ui->comboBox5->addItem("混合色", QByteArray::fromHex("010A0000"));
ui->comboBox5->addItem("红头", QByteArray::fromHex("010B0000"));
}

@ -6,8 +6,6 @@
#include "communicationsettingdlg.h"
#include "global.h"
#include "utils/CUdpSocket.h"
#include "utils/protocolPodTJ.h"
typedef struct struProtocalKB {
quint8 head[2]; // 帧头0x
@ -73,12 +71,6 @@ const quint16 CRC8TAB[256] = {
0x0082, 0x00B3, 0x00E0, 0x00D1, 0x0046, 0x0077, 0x0024, 0x0015, 0x003B,
0x000A, 0x0059, 0x0068, 0x00FF, 0x00CE, 0x009D, 0x00AC};
enum uav_type
{
FP98=0,
MEDICAL_UNIVERSITY=5
};
namespace Ui {
class CommandWidget;
}
@ -130,126 +122,24 @@ private slots:
void on_pushButton_15_clicked();
/*武汉天进吊舱控制*/
void on_pushButton_left_pressed();
void on_pushButton_left_released();
void on_pushButton_right_pressed();
void on_pushButton_right_released();
void on_pushButton_up_pressed();
void on_pushButton_up_released();
void on_pushButton_down_pressed();
void on_pushButton_down_released();
void on_pushButton_center_pressed();
//姿态指引
void on_pushButton_guid_pressed();
void on_pushButton_pose_pressed();
//航向锁定
void on_pushButton_directL_pressed();
//航向随动
void on_pushButton_directS_pressed();
//垂直下视
void on_pushButton_lookDown_pressed();
//镜头收藏
void on_pushButton_packUp_pressed();
//扫描
void on_pushButton_scanning_pressed();
//关伺服
void on_pushButton_close_pressed();
//设置检测模板
void on_comboBox1_activated(int index);
//显示模式
void on_comboBox2_activated(int index);
//视频切换
void on_pushButton_videoSwitch_pressed();
//开始录像
void on_pushButton_video_pressed();
//拍照
void on_pushButton_photo_pressed();
//可见光变倍+
void on_pushButton_zoomp_pressed();
void on_pushButton_zoomp_released();
//可见光变倍-
void on_pushButton_zoomd_pressed();
void on_pushButton_zoomd_released();
//可见光变焦+
void on_pushButton_focusP_pressed();
void on_pushButton_focusP_released();
//可见光变焦-
void on_pushButton_focusD_pressed();
void on_pushButton_focusD_released();
//自动对焦
void on_pushButton_auto_pressed();
void on_pushButton_auto_released();
//对比度
void on_pushButton_contrastP_pressed();
void on_pushButton_contrastD_pressed();
//亮度
void on_pushButton_lightP_pressed();
void on_pushButton_lightD_pressed();
//图像去雾
void on_comboBox4_activated(int index);
//红外变倍+
void on_pushButton_zoomp_2_pressed();
void on_pushButton_zoomp_2_released();
//红外变倍-
void on_pushButton_zoomd_2_pressed();
void on_pushButton_zoomd_2_released();
//对比度
void on_pushButton_contrastP_2_pressed();
void on_pushButton_contrastD_2_pressed();
//伪彩循环切换
void on_pushButton_color_pressed();
//伪彩选择
void on_comboBox5_activated(int index);
//单次测距
void on_pushButton_distance_pressed();
//连续测距
void on_pushButton_distanceC_pressed();
//停止测距
void on_pushButton_distanceS_pressed();
void on_cmdLineEdit_textChanged(const QString text);
public slots:
void onButtonChanged(int button, bool pressed);
void onDirectionChanged(int dirction);
private:
Ui::CommandWidget *ui;
QUdpSocket *m_commandUdpSocket = nullptr;
QString m_remoteIP;
int m_remotePort;
CUdpSocket* m_CUdpSocket = nullptr;
ProtocolPodTJ m_protocolPodTJ;
unsigned char getCrc(quint8 *data, quint8 length);
ProtocalKB EncodeCommandCmd(quint8 cmd);
bool writeBufferToClient(const QByteArray &data);
void buttonResponse(quint8 cmd);
void initmUdpParam();
public slots:
void receiveUavTypeIndex(int index);
//设置通信IP及Port 连接udp
void setPodIPConfig(NetworkIPStruct networkIP, bool isConnect);
public:
//设置窗口传递参数
void setMoveParm(QPointF size);
//伺服停止
void stopServo();
//目标跟踪
void setTargetTracking(double dir, double pitch);
//停止跟踪
void setStopTracking();
//滚轮控制变倍
void wheelZoomCtrl(int value);
};
#endif // COMMANDWIDGET_H

File diff suppressed because it is too large Load Diff

@ -18,90 +18,6 @@ CommunicationSettingDlg::~CommunicationSettingDlg() {
void CommunicationSettingDlg::showEvent(QShowEvent *event) {
setSaveSettingParms();
setPodIniSetting();
}
bool CommunicationSettingDlg::isValidIP()
{
QHostAddress addrLocal(ui->podLocalIP->currentText());
QHostAddress addrremote(ui->podRemoteIPEdit->text());
if (addrLocal.isNull() || addrLocal.protocol() != QAbstractSocket::IPv4Protocol)
{
emit sendErrorMessage("本地IP格式错误!", 2);
return false;
}
if (addrremote.isNull() || addrremote.protocol() != QAbstractSocket::IPv4Protocol)
{
emit sendErrorMessage("远端IP格式错误!", 2);
return false;
}
if (ui->podLocalPort->value() == 0 || ui->podRemotePort->value() == 0)
{
emit sendErrorMessage("请输入正确的端口号!", 2);
return false;
}
return true;
}
void CommunicationSettingDlg::savePodIniSetting()
{
g_networkSettingInfo->setValue("PodSetting/localIP",
ui->podLocalIP->currentText());
g_networkSettingInfo->setValue("PodSetting/remoteIP",
ui->podRemoteIPEdit->text());
g_networkSettingInfo->setValue("PodSetting/localPort",
ui->podLocalPort->value());
g_networkSettingInfo->setValue("PodSetting/remotePort",
ui->podRemotePort->value());
}
void CommunicationSettingDlg::setPodIniSetting()
{
ui->podRemoteIPEdit->setText(
g_networkSettingInfo->value("PodSetting/remoteIP", "192.168.1.160").toString());
ui->podRemotePort->setValue(
g_networkSettingInfo->value("PodSetting/remotePort", 10000).toInt());
ui->podLocalPort->setValue(
g_networkSettingInfo->value("PodSetting/localPort", 10001).toInt());
QString localIP =
g_networkSettingInfo->value("PodSetting/localIP").toString();
for (int i = 0; i < ui->podLocalIP->count(); ++i) {
if (localIP == ui->podLocalIP->itemText(i)) {
ui->podLocalIP->setCurrentIndex(i);
}
}
}
void CommunicationSettingDlg::on_podConnectBtn_clicked()
{
if (!isValidIP()) { return; }
savePodIniSetting();
NetworkIPStruct networkIP;
if (!_commIsConnect)//断开时点击连接
{
_commIsConnect = true;//状态为连接
networkIP.localIP = ui->podLocalIP->currentText();
networkIP.remoteIP = ui->podRemoteIPEdit->text();
networkIP.localPort = ui->podLocalPort->value();
networkIP.remotePort = ui->podRemotePort->value();
emit sendPodData(networkIP, _commIsConnect);
ui->podConnectBtn->setText("断开");
}
else
{
_commIsConnect = false;//状态为连接
emit sendPodData(networkIP, _commIsConnect);
ui->podConnectBtn->setText("连接");
}
}
void CommunicationSettingDlg::initCombobox() {
@ -115,14 +31,10 @@ void CommunicationSettingDlg::initNetworkSetting() {
QStringList localIPList;
getLocalIP(localIPList);
ui->localIP->addItems(localIPList);
ui->podLocalIP->addItems(localIPList);
ui->remoteIPEdit->setText(QStringLiteral("223.0.0.1"));
ui->remotePort->setValue(8080);
ui->localPort->setValue(8082);
ui->podRemotePort->setValue(10000);
ui->podLocalPort->setValue(10001);
}
void CommunicationSettingDlg::setSaveSettingParms() {
@ -152,7 +64,7 @@ void CommunicationSettingDlg::setSaveSettingParms() {
QString localIP =
g_networkSettingInfo->value(groupName + "/localIP").toString();
for (int i = 0; i < ui->localIP->count(); ++i) {
for (int i = 0; i < ui->dataSourceCombox->count(); ++i) {
if (localIP == ui->localIP->itemText(i)) {
ui->localIP->setCurrentIndex(i);
}

@ -23,11 +23,9 @@ protected:
signals:
void sendErrorMessage(QString message, int type);
void sendPodData(NetworkIPStruct networkIP, bool isConnect);
private slots:
void on_saveSettingBtn_clicked();
void on_podConnectBtn_clicked();
private:
void initCombobox();
@ -36,14 +34,8 @@ private:
void setSaveSettingParms();
bool isValidIP();
void savePodIniSetting();
void setPodIniSetting();
private:
Ui::CommunicationSettingDlg *ui;
bool _commIsConnect{ false };
};
#endif // COMMUNICATIONSETTINGDLG_H

@ -6,450 +6,192 @@
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>350</height>
<width>307</width>
<height>275</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>600</width>
<height>350</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>600</width>
<height>350</height>
</size>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<widget class="QGroupBox" name="groupBox">
<property name="geometry">
<rect>
<x>20</x>
<y>10</y>
<width>551</width>
<height>171</height>
</rect>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
<property name="rightMargin">
<number>4</number>
</property>
<property name="title">
<string>网络设置</string>
<property name="bottomMargin">
<number>4</number>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout" columnstretch="1,0,0,0,0,0">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="title">
<string>网络设置</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<item row="1" column="4">
<widget class="QSpinBox" name="remotePort">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="maximum">
<number>65534</number>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QSpinBox" name="localPort">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="maximum">
<number>65534</number>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="remoteIPEdit"/>
</item>
<item row="0" column="0">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_4">
<property name="maximumSize">
<size>
<width>150</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>数据来源:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_2">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>网络IP</string>
</property>
</widget>
</item>
<item row="0" column="2" colspan="3">
<widget class="QComboBox" name="dataSourceCombox"/>
</item>
<item row="1" column="3">
<widget class="QLabel" name="label_12">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>网络端口:</string>
</property>
</widget>
</item>
<item row="1" column="5" rowspan="2">
<widget class="QWidget" name="widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="saveSettingBtn">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>保存设置</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>本地IP</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QLabel" name="label_13">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>本地端口:</string>
</property>
</widget>
</item>
<item row="0" column="5">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>100</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="2">
<widget class="QComboBox" name="localIP"/>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout" columnstretch="1,2">
<property name="leftMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="horizontalSpacing">
<number>7</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>本地IP</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="localIP"/>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="remoteIPEdit"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>网络IP</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>数据来源:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="dataSourceCombox"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_12">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>网络端口:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="remotePort">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="maximum">
<number>65534</number>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_13">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>本地端口:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="localPort">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="maximum">
<number>65534</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QGroupBox" name="groupBox_2">
<property name="geometry">
<rect>
<x>20</x>
<y>190</y>
<width>551</width>
<height>131</height>
</rect>
</property>
<property name="title">
<string>吊舱网络设置</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="4">
<widget class="QSpinBox" name="podRemotePort">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="maximum">
<number>65534</number>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLabel" name="label_14">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>本地端口:</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="podRemoteIPEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="5" rowspan="2">
<widget class="QWidget" name="widget_2" native="true">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="podConnectBtn">
<property name="text">
<string>连接</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="2">
<widget class="QComboBox" name="podLocalIP"/>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_3">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>本地IP</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QSpinBox" name="podLocalPort">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="maximum">
<number>65534</number>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="label_15">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>远端端口:</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_5">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>远端P</string>
</property>
</widget>
</item>
<item row="0" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="saveSettingBtn">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>保存设置</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>

@ -0,0 +1,164 @@
#include "DirectInputJoystick.h"
#include <QDebug>
DirectInputJoystick::DirectInputJoystick(QObject *parent) : QObject(parent) {
// 确保 COM 库在主线程初始化
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
}
DirectInputJoystick::~DirectInputJoystick() {
cleanup();
CoUninitialize();
}
bool DirectInputJoystick::initialize() {
HRESULT hr = DirectInput8Create(
GetModuleHandle(nullptr),
DIRECTINPUT_VERSION,
IID_IDirectInput8,
(VOID**)&m_pDirectInput,
nullptr
);
if (FAILED(hr)) {
emit errorOccurred("无法创建 DirectInput 对象");
return false;
}
// 枚举所有摇杆设备
hr = m_pDirectInput->EnumDevices(
DI8DEVCLASS_GAMECTRL,
enumJoysticksCallback,
this,
DIEDFL_ATTACHEDONLY
);
return SUCCEEDED(hr);
}
BOOL CALLBACK DirectInputJoystick::enumJoysticksCallback(const DIDEVICEINSTANCE* instance, VOID* context) {
DirectInputJoystick* joystick = static_cast<DirectInputJoystick*>(context);
return joystick->setupDevice();
}
bool DirectInputJoystick::setupDevice() {
if (!m_pDirectInput) return false;
// 创建设备
HRESULT hr = m_pDirectInput->CreateDevice(
GUID_Joystick,
&m_pJoystick,
nullptr
);
if (FAILED(hr)) {
emit errorOccurred("无法创建摇杆设备");
return false;
}
// 设置数据格式
hr = m_pJoystick->SetDataFormat(&c_dfDIJoystick);
if (FAILED(hr)) {
emit errorOccurred("设置数据格式失败");
return false;
}
// 设置协作级别
hr = m_pJoystick->SetCooperativeLevel(
nullptr, // 无窗口句柄
DISCL_BACKGROUND | DISCL_NONEXCLUSIVE
);
// 枚举轴并设置范围
hr = m_pJoystick->EnumObjects(enumAxesCallback, this, DIDFT_AXIS);
if (FAILED(hr)) {
emit errorOccurred("枚举摇杆轴失败");
return false;
}
// 创建事件对象
m_hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
hr = m_pJoystick->SetEventNotification(m_hEvent);
if (FAILED(hr)) {
emit errorOccurred("设置事件通知失败");
return false;
}
return true;
}
BOOL CALLBACK DirectInputJoystick::enumAxesCallback(const DIDEVICEOBJECTINSTANCE* doi, VOID* context) {
DirectInputJoystick* joystick = static_cast<DirectInputJoystick*>(context);
DIPROPRANGE diprg;
diprg.diph.dwSize = sizeof(DIPROPRANGE);
diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
diprg.diph.dwHow = DIPH_BYID;
diprg.diph.dwObj = doi->dwType;
diprg.lMin = -1000;
diprg.lMax = 1000;
HRESULT hr = joystick->m_pJoystick->SetProperty(DIPROP_RANGE, &diprg.diph);
return SUCCEEDED(hr);
}
void DirectInputJoystick::startListening() {
if (!m_pJoystick || m_isRunning) return;
m_isRunning = true;
m_workerThread = QThread::create([this]() {
m_pJoystick->Acquire();
while (m_isRunning) {
WaitForSingleObject(m_hEvent, INFINITE);
DIJOYSTATE state;
HRESULT hr = m_pJoystick->GetDeviceState(sizeof(DIJOYSTATE), &state);
if (SUCCEEDED(hr)) {
// 处理轴数据
double x = state.lX / 1000.0;
double y = state.lY / 1000.0;
double z = state.lZ / 1000.0;
emit axisChanged(0, x);
emit axisChanged(1, y);
emit axisChanged(2, z);
// 处理按钮
for (int i = 0; i < 32; ++i) {
if(lastState.rgbButtons[i]!=state.rgbButtons[i])
{
bool pressed = (state.rgbButtons[i] & 0x80) != 0;
emit buttonChanged(i, pressed);
}
}
if(lastState.rgdwPOV[0]!=state.rgdwPOV[0])
{
emit directionChanged(state.rgdwPOV[0]/100);
}
}
lastState = state;
}
});
connect(m_workerThread, &QThread::finished, m_workerThread, &QThread::deleteLater);
m_workerThread->start();
}
void DirectInputJoystick::cleanup() {
m_isRunning = false;
if (m_hEvent) {
CloseHandle(m_hEvent);
m_hEvent = nullptr;
}
if (m_pJoystick) {
m_pJoystick->Unacquire();
m_pJoystick->Release();
m_pJoystick = nullptr;
}
if (m_pDirectInput) {
m_pDirectInput->Release();
m_pDirectInput = nullptr;
}
}

@ -0,0 +1,47 @@
#ifndef DIRECTINPUTJOYSTICK_H
#define DIRECTINPUTJOYSTICK_H
#include <QObject>
#include <windows.h>
#include <dinput.h>
#include <QThread>
class DirectInputJoystick : public QObject
{
Q_OBJECT
public:
explicit DirectInputJoystick(QObject *parent = nullptr);
~DirectInputJoystick();
bool initialize();
void startListening();
signals:
// 轴变化信号axis: 0-X, 1-Y, 2-Z; value: -1.0 ~ 1.0
void axisChanged(int axis, double value);
// 按钮状态变化button: 按钮编号从0开始pressed: 是否按下)
void buttonChanged(int button, bool pressed);
// 方向按钮状态变化direction: 按钮编号上下左右pressed: 是否按下)
void directionChanged(int direction);
// 错误信号
void errorOccurred(const QString &message);
private:
bool setupDevice();
void cleanup();
static BOOL CALLBACK enumAxesCallback(const DIDEVICEOBJECTINSTANCE* doi, VOID* context);
static BOOL CALLBACK enumJoysticksCallback(const DIDEVICEINSTANCE* instance, VOID* context);
DIJOYSTATE lastState;
LPDIRECTINPUT8 m_pDirectInput = nullptr;
LPDIRECTINPUTDEVICE8 m_pJoystick = nullptr;
HANDLE m_hEvent = nullptr;
QThread *m_workerThread;
bool m_isRunning = false;
};
#endif // DIRECTINPUTJOYSTICK_H

@ -0,0 +1,106 @@
#include "joystickcontroller.h"
#include <QDebug>
JoystickController::JoystickController(int joystickId, QObject *parent)
: QObject{parent}, m_joystickId(joystickId), m_isConnected(false)
{
// 初始化定时器
m_timer = new QTimer(this);
connect(m_timer, &QTimer::timeout, this, &JoystickController::pollJoystick);
// 检测设备是否存在
UINT numDevs = joyGetNumDevs();
if (numDevs == 0) {
emit errorOccurred("系统未检测到任何摇杆设备");
return;
}
// 获取设备能力
MMRESULT capsResult = joyGetDevCaps(m_joystickId, &m_joyCaps, sizeof(JOYCAPS));
if (capsResult != JOYERR_NOERROR) {
emit errorOccurred(QString("无法获取摇杆设备能力,错误代码: %1").arg(capsResult));
return;
}
m_isConnected = true;
qDebug() << "摇杆设备初始化成功,名称:" << QString::fromWCharArray(m_joyCaps.szPname);
}
JoystickController::~JoystickController()
{
stopPolling();
}
bool JoystickController::isConnected() const
{
return m_isConnected;
}
void JoystickController::startPolling(int intervalMs)
{
if (m_isConnected) {
m_timer->start(intervalMs);
}
}
void JoystickController::stopPolling()
{
m_timer->stop();
}
void JoystickController::pollJoystick()
{
JOYINFOEX joyInfo;
joyInfo.dwSize = sizeof(JOYINFOEX);
joyInfo.dwFlags = JOY_RETURNALL;
MMRESULT result = joyGetPosEx(m_joystickId, &joyInfo);
if (result != JOYERR_NOERROR) {
emit errorOccurred(QString("读取摇杆数据失败,错误代码: %1").arg(result));
m_isConnected = false;
stopPolling();
return;
}
// 处理轴数据
normalizeAxisValues(joyInfo);
// 处理按钮状态
for (int i = 0; i < m_joyCaps.wNumButtons; ++i) {
bool isPressed = (joyInfo.dwButtons & (1 << i)) != 0;
emit buttonChanged(i + 1, isPressed); // 按钮ID从1开始
}
}
void JoystickController::normalizeAxisValues(const JOYINFOEX &joyInfo)
{
// X/Y轴归一化处理
double x = (joyInfo.dwXpos - 32768) / 32768.0;
double y = (joyInfo.dwYpos - 32768) / 32768.0;
emit axisChanged(1, x); // 假设轴1为X轴
emit axisChanged(2, y); // 轴2为Y轴
// 其他轴如Z轴、Rudder等
if (m_joyCaps.wCaps & JOYCAPS_HASZ) {
double z = (joyInfo.dwZpos - 32768) / 32768.0;
emit axisChanged(3, z);
}
}

@ -0,0 +1,41 @@
#ifndef JOYSTICKCONTROLLER_H
#define JOYSTICKCONTROLLER_H
#include <QObject>
#include <QTimer>
#include <windows.h>
#include <mmsystem.h>
class JoystickController : public QObject
{
Q_OBJECT
public:
explicit JoystickController(int joystickId = JOYSTICKID1, QObject *parent = nullptr);
~JoystickController();
bool isConnected() const;
void startPolling(int intervalMs = 50);
void stopPolling();
signals:
// 摇杆轴变化信号(范围:-1.0 ~ 1.0
void axisChanged(int axis, double value);
// 按钮状态变化信号按钮ID, 是否按下)
void buttonChanged(int button, bool pressed);
// 错误信号
void errorOccurred(const QString &message);
private slots:
void pollJoystick();
private:
void normalizeAxisValues(const JOYINFOEX &joyInfo);
QTimer *m_timer;
int m_joystickId;
bool m_isConnected;
JOYCAPS m_joyCaps;
};
#endif // JOYSTICKCONTROLLER_H

@ -32,6 +32,7 @@ MainWindow::MainWindow(QWidget *parent)
qDebug() << "Open file fail " << Qt::endl;
}
// QList<QString> list;
// list.append("rtmp://liteavapp.qcloud.com/live/liteavdemoplayerstreamid");
// list.append("rtsp://182.92.130.23/nmyj/video");
@ -59,10 +60,10 @@ MainWindow::MainWindow(QWidget *parent)
initChangeVideoLayoutConnection();
initHideButton();
initWidget();
//initJoyStick();
initDtJoyStick();
setSavedVideoDir();
initPodParam();
// ui->stackedWidget->setCurrentIndex(1);
// ui->videoWidget1->play(list.at(0));
// ui->videoWidget2->play(list.at(0));
@ -126,7 +127,6 @@ void MainWindow::initSignalConnection() {
connect(ui->videoControlWidget, &VideoControl::stopVideoSignal, this,
&MainWindow::closeVideoSlot);
connect(&streamAddrSettingsDlg, &StreamAddrSettingsDlg::sendUavTypeIndex, ui->commandWidget, &CommandWidget::receiveUavTypeIndex);
}
@ -137,8 +137,6 @@ void MainWindow::initNotifyMessageConnection() {
&MainWindow::showMessageSlots);
connect(ui->commandWidget, &CommandWidget::sendErrorMessage, this,
&MainWindow::showMessageSlots);
connect(&settingDlg, &CommunicationSettingDlg::sendErrorMessage, this,
&MainWindow::showMessageSlots);
}
void MainWindow::initChangeVideoLayoutConnection() {
@ -148,6 +146,38 @@ void MainWindow::initChangeVideoLayoutConnection() {
});
}
void MainWindow::onAxisChanged(int axis, double value)
{
qDebug() << "Axis" << axis << "Value:" << value;
}
// void MainWindow::initJoyStick()
// {
// m_joystick = new JoystickController(JOYSTICKID1,this);
// connect(m_joystick, &JoystickController::axisChanged, this, &MainWindow::onAxisChanged);
// connect(m_joystick, &JoystickController::buttonChanged, this, &MainWindow::onButtonChanged);
// if (m_joystick->isConnected()) {
// m_joystick->startPolling();
// }
// }
void MainWindow::initDtJoyStick()
{
m_dtjoystick = new DirectInputJoystick(this);
if (m_dtjoystick->initialize()) {
connect(m_dtjoystick, &DirectInputJoystick::buttonChanged, ui->commandWidget, &CommandWidget::onButtonChanged);
connect(m_dtjoystick, &DirectInputJoystick::directionChanged, ui->commandWidget, &CommandWidget::onDirectionChanged);
// connect(m_dtjoystick, &DirectInputJoystick::buttonChanged, this, &MainWindow::onButtonChanged);
m_dtjoystick->startListening();
}
}
void MainWindow::initNotifyManager() {
m_notifyManager = new NotifyManager(this, this);
m_notifyManager->setMaxCount(5);
@ -326,11 +356,6 @@ void MainWindow::initHideButton() {
&MainWindow::hideCommandWidgetSlot);
}
void MainWindow::initWidget()
{
ui->commandWidget->receiveUavTypeIndex(g_networkSettingInfo->value("NetworkStreamSettings/uavType").toInt());
}
void MainWindow::showSettingDlgSlot() {
settingDlg.exec();
}
@ -480,7 +505,10 @@ void MainWindow::pushStreamSlot(bool bPush) {
} else {
pushStreamIP = g_pushStreamInfoStruct.pushStreamIP;
}
ui->videoWidget->pushStream(pushStreamIP);
QStringList pushStreamIPs;
pushStreamIPs.append(pushStreamIP);
// pushStreamIPs.append("rtmp://182.92.130.23/videoclient/fp98C_1_gcs");
ui->videoWidget->pushStream(pushStreamIPs);
} else {
ui->videoWidget->stopPushStream();
// ui->videoWidget1->stopPushStream();
@ -518,161 +546,3 @@ void MainWindow::showMap3DSlot() {
m_mapWidget->show();
}
}
void MainWindow::initPodParam()
{
_wheelStopTime = new QTimer(this);
_wheelStopTime->setInterval(300);
_wheelStopTime->setSingleShot(true);
firstLocation = new QLabel(this);
firstLocation->setPixmap(QPixmap(":/images/c.png"));
firstLocation->setScaledContents(true);
firstLocation->resize(30, 30);
firstLocation->setStyleSheet("border: none; background: transparent;");
firstLocation->hide();
lastLocation = new QLabel(this);
lastLocation->setPixmap(QPixmap(":/images/circle.png"));
lastLocation->setScaledContents(true);
lastLocation->resize(25, 25);
lastLocation->setStyleSheet("border: none; background: transparent;");
lastLocation->hide();
connect(&settingDlg, &CommunicationSettingDlg::sendPodData, ui->commandWidget, &CommandWidget::setPodIPConfig);
connect(_wheelStopTime, &QTimer::timeout, this, [this]() {
ui->commandWidget->wheelZoomCtrl(0);
qDebug() << "滚轮停止了";
});
}
void MainWindow::mousePressEvent(QMouseEvent* event)
{
if (ui->videoWidget->underMouse())
{
if (event->button() == Qt::LeftButton)
{
//记录鼠标按下位置
_dragStartPositon = event->pos();
//QPointF b = event->globalPosition();
qDebug() << "局部坐标" << "x" << _dragStartPositon.x() << "y" << _dragStartPositon.y();
//qDebug() << "全局坐标" << "x" << b.x() << "y" << b.y();
_dragFlag = true;
}
else if (event->button() == Qt::RightButton)
{
//鼠标右键取消目标跟踪
ui->commandWidget->setStopTracking();
qDebug() << "取消跟踪";
}
}
}
void MainWindow::mouseMoveEvent(QMouseEvent* event)
{
if (_dragFlag)//点击并且移动
{
QPointF diff = event->pos() - _dragStartPositon;
qDebug() << "x:" << diff.x() << "y:" << diff.y();
ui->commandWidget->setMoveParm(diff);
if (qAbs(diff.x()) > 5 || qAbs(diff.y()) > 5)//像素点超过5ptx才算移动否则为跟踪
{
_moveFlag = true;
firstLocation->move((_dragStartPositon - QPoint(firstLocation->width() / 2, firstLocation->height() / 2)).toPoint());
lastLocation->move(event->pos() - QPoint(lastLocation->width() / 2, lastLocation->height() / 2));
firstLocation->show();
lastLocation->show();
}
QPointF widgetPos = mapFromGlobal(event->globalPosition());
//如果移动的时候超过了画面显示区域,则自动退出伺服控制
if (!ui->videoWidget->rect().contains(widgetPos.toPoint()))
{
_moveFlag = false;
_dragFlag = false;
ui->commandWidget->stopServo();
firstLocation->hide();
lastLocation->hide();
}
}
}
void MainWindow::mouseReleaseEvent(QMouseEvent* event)
{
if (ui->videoWidget->underMouse())
{
if (event->button() == Qt::LeftButton && _moveFlag)
{
_moveFlag = false;
ui->commandWidget->stopServo();
firstLocation->hide();
lastLocation->hide();
}
else if (event->button() == Qt::LeftButton && _dragFlag)
{
// 目标跟踪
QPointF sPoint1 = event->globalPosition();
QPointF widgetPoint = ui->videoWidget->mapFromGlobal(sPoint1);
double a = ui->videoWidget->width();
double b = ui->videoWidget->height();
double dir = 1920 / a * widgetPoint.rx() - 960;
double pitch = -(1080 / b * widgetPoint.ry() - 540);
ui->commandWidget->setTargetTracking(dir, pitch);
qDebug() << "=========>目标跟踪" << " dir:" << dir << " pitch:" << pitch << " a:" << a << " b:" << b;
}
}
}
void MainWindow::wheelEvent(QWheelEvent* event)
{
if (ui->videoWidget->underMouse())
{
// 获取滚轮的滚动方向和角度增量
_delta = event->angleDelta().y();
QPointF currentLocation = event->position();
// qDebug() << "当前位置:" << currentLocation;
if (_delta > 0)
{
// 向上滚动
qDebug() << "鼠标滚轮向上滚动" << " 步长:" << _delta;
// if(wheelSub->isVisible())
// {
// wheelSub->hide();
// }
// wheelPlus->move((currentLocation - QPoint(wheelPlus->width()/2, wheelPlus->height()/2)).toPoint());
// wheelPlus->show();
ui->commandWidget->wheelZoomCtrl(_delta);
}
else if (_delta < 0)
{
// 向下滚动
// if(wheelPlus->isVisible())
// {
// wheelPlus->hide();
// }
// wheelSub->move((currentLocation - QPoint(wheelSub->width()/2, wheelSub->height()/2)).toPoint());
// wheelSub->show();
ui->commandWidget->wheelZoomCtrl(_delta);
qDebug() << "鼠标滚轮事向下滚动" << " 步长:" << _delta;
}
if (!_wheelStopTime->isActive())
{
_wheelStopTime->start();
}
else
{
_wheelStopTime->stop();
_wheelStopTime->start();
}
}
QWidget::wheelEvent(event);
}

@ -16,13 +16,14 @@
#include "videoControl.h"
#include "videowidget.h"
#include "webmapwidget.h"
//#include "joystickapi.h"
#include "joystickcontroller.h"
#include "DirectInputJoystick.h"
#ifdef Q_OS_WIN
#include "windows.h"
#include "windowsx.h"
#pragma comment(lib, "user32.lib")
#endif
#include <QLabel>
namespace QWK {
class WidgetWindowAgent;
@ -55,7 +56,6 @@ protected:
signals:
void themeChanged();
void sendErrorMessage(QString message, int type);
void wheelSendData(int value);
private:
void initSignalConnection();
@ -70,7 +70,14 @@ private:
void setSavedVideoDir();
void initHideButton();
void initWidget();
// void initJoyStick();
void initDtJoyStick();
private slots:
void onAxisChanged(int axis, double value);
private slots:
void showSettingDlgSlot();
@ -95,23 +102,8 @@ private:
QToolButton *hideCommandWidgetBtn = nullptr;
WebMapWidget *m_mapWidget = nullptr;
// JoystickController *m_joystick = nullptr;
DirectInputJoystick *m_dtjoystick;
//吊舱人机交互变量
int _delta{0}; //滚轮滚动量
QTimer* _wheelStopTime;
QPointF _dragStartPositon; //记录滑动操作当前点击的局部坐标
bool _dragFlag{false}; //记录滑动初始标志
bool _moveFlag{false}; //记录正在滑动中的标志
QLabel* firstLocation;
QLabel* lastLocation;
void initPodParam();
public:
void mousePressEvent(QMouseEvent* event) override; // 事件过滤器
void mouseMoveEvent(QMouseEvent* event) override; // 事件过滤器
void mouseReleaseEvent(QMouseEvent* event) override;
protected:
void wheelEvent(QWheelEvent* event) override;
};
#endif // MAINWINDOW_H

@ -105,14 +105,7 @@
</layout>
</item>
<item>
<widget class="CommandWidget" name="commandWidget" native="true">
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
</widget>
<widget class="CommandWidget" name="commandWidget" native="true"/>
</item>
</layout>
</widget>
@ -122,7 +115,7 @@
<x>0</x>
<y>0</y>
<width>1388</width>
<height>33</height>
<height>24</height>
</rect>
</property>
</widget>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

@ -37,7 +37,5 @@
<file>images/play1.75X.png</file>
<file>images/play1X.png</file>
<file>images/play2X.png</file>
<file>images/c.png</file>
<file>images/circle.png</file>
</qresource>
</RCC>

@ -14,7 +14,6 @@ StreamAddrSettingsDlg::StreamAddrSettingsDlg(QWidget *parent)
uavTypeList.append("FP981C");
uavTypeList.append("FP981CS");
uavTypeList.append("FP981A");
uavTypeList.append("四医大");
ui->uavTypeCombox->insertItems(0, uavTypeList);
//
ui->pushStreamIPCombox->insertItem(0, QStringLiteral("航天飞鹏服务器"));
@ -158,11 +157,3 @@ void StreamAddrSettingsDlg::on_pushStreamBtn_clicked() {
ui->pushStreamBtn->setDisabled(false);
}
void StreamAddrSettingsDlg::on_uavTypeCombox_activated(int index)
{
qDebug() << index;
g_networkSettingInfo->setValue("NetworkStreamSettings/uavType",
ui->uavTypeCombox->currentIndex());
emit sendUavTypeIndex(index);
}

@ -22,7 +22,6 @@ protected:
signals:
void startPullStreamSignal(bool bPull);
void startPushStreamSignal(bool bPush);
void sendUavTypeIndex(int index);
private slots:
void on_pushStreamIPCombox_currentIndexChanged(int index);
@ -33,8 +32,6 @@ private slots:
void on_pushStreamBtn_clicked();
void on_uavTypeCombox_activated(int index);
private:
void initSavedParms();
void saveParms();

@ -1,107 +0,0 @@
#include "CUdpSocket.h"
#include "../global.h"
CUdpSocket::CUdpSocket(QObject* parent) : QObject(parent)
{
//创建UDP套接字内存空间
m_sock = new QUdpSocket;
// 连接数据读取信号槽
connect(m_sock, &QUdpSocket::readyRead, this, &CUdpSocket::on_readyReadData);
}
CUdpSocket::~CUdpSocket()
{
//离开所有加入的组播组
if (m_sock->state() == QAbstractSocket::BoundState) {
m_sock->leaveMulticastGroup(m_hostAddr);
}
// 释放UDP套接字内存空间
m_sock->close();
delete m_sock;
}
bool CUdpSocket::bind(QString ip, ushort port)
{
// 返回绑定函数返回值
m_localIP = ip;
m_localAddr = QHostAddress(ip);
m_localPort = port;
if (!m_sock->bind(QHostAddress(ip), port))
{
qDebug() << "Bind failed:" << m_sock->errorString();
isSocketBind = false;
return false;
}
isSocketBind = true;
return true;
}
void CUdpSocket::sendData(QByteArray data)
{
// 发送传入的数据到指定的信息的位置
if (isSocketBind)
{
m_sock->writeDatagram(data, m_hostAddr, m_port);
}
}
void CUdpSocket::setTargetInfo(QString ip, quint16 port)
{
//存储传入的IP和端口号
m_hostAddr = QHostAddress(ip);
m_port = port;
}
bool CUdpSocket::close()
{
//离开所有加入的组播组
if (m_sock->state() == QAbstractSocket::BoundState) {
m_sock->leaveMulticastGroup(m_hostAddr);
}
m_sock->close();
if (m_sock->state() == QUdpSocket::UnconnectedState) {
qDebug() << "Socket 已成功关闭";
return true;
}
qDebug() << "Socket 关闭失败,当前状态:" << m_sock->state();
return false;
}
bool CUdpSocket::joinMulticast()
{
bool a = isMulticastAddress(m_localIP);
if (m_localIP.isNull() || !isMulticastAddress(m_localIP)) { return false; }
if (!m_sock->joinMulticastGroup(m_localAddr)) {
qDebug() << "Join multicast group failed:" << m_sock->errorString();
return false;
}
return true;
}
QPair<QString, quint16> CUdpSocket::getSenderIPSetting()
{
return qMakePair(m_localIP, m_localPort);
}
void CUdpSocket::on_readyReadData()
{
//通过函数判断当前是否有等待读取的数据并循环获取
while (m_sock->hasPendingDatagrams())
{
//创建数据存储容器,并设置长度为将接收的数据长度
QByteArray data;
data.resize(m_sock->pendingDatagramSize());
//读取数据并保存信息发送者的地址和ip(方便发送时指定发送位置)
m_sock->readDatagram(data.data(), data.size(), &senderIP, &senderPort);
//发送接收数据的信号
emit recvDataSignal(data);
}
}

@ -1,53 +0,0 @@
#ifndef CUDPSOCKET_H
#define CUDPSOCKET_H
#include <QObject>
#include <QUdpSocket>
#include <QHostAddress>
#include <QPair>
class CUdpSocket : public QObject
{ Q_OBJECT
public:
explicit CUdpSocket(QObject* parent = nullptr);
~CUdpSocket();
//绑定本机的ip和端口号信息
bool bind(QString ip, ushort port);
//通过该函数发送数据
void sendData(QByteArray data);
//设置目标主机的ip和端口号
void setTargetInfo(QString ip, quint16 port);
//关闭socket
bool close();
//加入组播
bool joinMulticast();
//获取发送者的IP及端口号
QPair<QString, quint16> getSenderIPSetting();
signals:
//通过该信号传递接收到的数据
void recvDataSignal(QString data);
public slots:
//读取数据的槽函数
void on_readyReadData();
private:
QUdpSocket* m_sock{nullptr}; //UDP套接字指针
QHostAddress m_hostAddr; //保存目标的地址对象
quint16 m_port{0}; //保存目标的端口号(类型一致)
QString m_localIP{""}; //本地IP
QHostAddress m_localAddr; //保存本地的地址对象
quint16 m_localPort{0}; //保存本地的端口号(类型一致)
QHostAddress senderIP; //发送者IP
quint16 senderPort{0}; //发送者端口号
bool isSocketBind{false}; //数据绑定成功or失败
};
#endif

@ -1,123 +0,0 @@
#include "protocolPodTJ.h"
QByteArray ProtocolPodTJ::encode(const QByteArray& cmd, const QByteArray& loadData)
{
QByteArray buffer;
// 帧头
buffer.append(0xFB);
buffer.append(0x2C);
buffer.append(0xAA);
// 帧长指令2字节 + 数据)
quint8 length = 2 + loadData.size();
buffer.append(length);
// 指令(高位在前)
buffer.append(cmd);
// 数据体
buffer.append(loadData);
// 校验(从帧长开始 到最后一个数据字节)
quint8 xorSum = calculateXOR(buffer, 3, buffer.size() - 1);
buffer.append(xorSum);
return buffer;
}
QByteArray ProtocolPodTJ::cmdLeft(uint16_t sliderValue)
{
QByteArray param(4, 0x00);
//小端在前
param[0] = -sliderValue & 0xFF;
param[1] = ((-sliderValue >> 8) & 0xFF);
return param;
}
QByteArray ProtocolPodTJ::cmdRight(uint16_t sliderValue)
{
QByteArray param(4, 0x00);
//小端在前
param[0] = sliderValue & 0xFF;
param[1] = ((sliderValue >> 8) & 0xFF);
return param;
}
QByteArray ProtocolPodTJ::cmdUp(uint16_t sliderValue)
{
QByteArray param(4, 0x00);
//小端在前
param[2] = sliderValue & 0xFF;
param[3]= ((sliderValue >> 8) & 0xFF);
return param;
}
QByteArray ProtocolPodTJ::cmdDown(uint16_t sliderValue)
{
QByteArray param(4, 0x00);
//小端在前
param[2] = -sliderValue & 0xFF;
param[3] = ((-sliderValue >> 8) & 0xFF);
return param;
}
QByteArray ProtocolPodTJ::cmdZoom(uint8_t model, uint8_t speed)
{
QByteArray param(4, 0x00);
//小端在前
param[0] = model;
param[1] = speed;
return param;
}
QByteArray ProtocolPodTJ::cmdLaser(uint8_t fre)
{
QByteArray param(4, 0x00);
//小端在前
param[0] = fre;
return param;
}
QByteArray ProtocolPodTJ::digitalGuid(uint8_t directionAngle, uint8_t pitchAngle)
{
QByteArray param(4, 0x00);
//小端在前
param[0] = (directionAngle * 100) & 0xFF;
param[1] = (((directionAngle * 100) >> 8) & 0xFF);
param[2] = (pitchAngle * 100) & 0xFF;
param[3] = (((pitchAngle * 100) >> 8) & 0xFF);
return param;
}
QByteArray ProtocolPodTJ::cmdServo(int pointX, int pointY)
{
QByteArray param(4, 0x00);
//小端在前
param[0] = pointX & 0xFF;
param[1] = ((pointX >> 8) & 0xFF);
param[2] = pointY & 0xFF;
param[3] = ((pointY >> 8) & 0xFF);
return param;
}
QByteArray ProtocolPodTJ::targetTracking(uint16_t dir, uint16_t pitch)
{
QByteArray param(4, 0x00);
//小端在前
param[0] = dir & 0xFF;
param[1] = ((dir >> 8) & 0xFF);
param[2] = pitch & 0xFF;
param[3] = ((pitch >> 8) & 0xFF);
return param;
}
//数据校验
quint8 ProtocolPodTJ::calculateXOR(const QByteArray& data, int begin, int end)
{
quint8 xorVal = 0;
for (int i = begin; i <= end; ++i) {
xorVal ^= static_cast<quint8>(data[i]);
}
return xorVal;
}

@ -1,28 +0,0 @@
#include <QByteArray>
class ProtocolPodTJ
{
public:
static QByteArray encode(const QByteArray& cmd, const QByteArray& loadData = QByteArray::fromHex("00000000"));
public:
//控制指令
QByteArray cmdLeft(uint16_t sliderValue);
QByteArray cmdRight(uint16_t sliderValue);
QByteArray cmdUp(uint16_t sliderValue);
QByteArray cmdDown(uint16_t sliderValue);
QByteArray cmdZoom(uint8_t model, uint8_t speed);
QByteArray cmdLaser(uint8_t fre);
//数字引导
QByteArray digitalGuid(uint8_t directionAngle, uint8_t pitchAngle);
//伺服控制
QByteArray cmdServo(int pointX, int pointY);
//目标跟踪
QByteArray targetTracking(uint16_t dir, uint16_t pitch);
private:
static quint8 calculateXOR(const QByteArray& data, int begin, int end);
};

@ -1,201 +0,0 @@
#include "switchbutton.h"
#include <QDebug>
WBSwitchButton::WBSwitchButton(QWidget* parent)
: QWidget{ parent }
{
}
bool WBSwitchButton::getSwitch() {
return mOnOff;
}
void WBSwitchButton::setSwitch(bool onoff) {
if (mWaitSigModel) return;
/// 状态切换
mOnOff = onoff;
/// 发送信号
sigSwitchChanged(mOnOff);
/// 动画-背景颜色
QPropertyAnimation* colorAnimation = new QPropertyAnimation(this, "pBackColor");
colorAnimation->setDuration(mAnimationPeriod);
colorAnimation->setStartValue(mBackColor);
colorAnimation->setEndValue(mOnOff ? mBackOnColor : mBackOffColor);
colorAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped); //停止后删除
/// 动画-开关按钮位置
QVariantAnimation* posAnimation = new QVariantAnimation(this);
posAnimation->setDuration(mAnimationPeriod);
posAnimation->setStartValue(mButtonRect.topLeft());
posAnimation->setEndValue(mOnOff ? mRightPos : mLeftPos);
connect(posAnimation, &QPropertyAnimation::valueChanged, [=](const QVariant& value) {
mButtonRect.moveTo(value.toPointF());
update();
});
posAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped); //停止后删除
}
void WBSwitchButton::setSwitchForWaitModel(bool onoff)
{
if (!mWaitSigModel) return;
if (mOnOff == onoff) {
/// 表示值未改变先运行按钮位置动画
QVariantAnimation* posAnimation = new QVariantAnimation(this);
posAnimation->setDuration(mAnimationPeriod);
posAnimation->setStartValue(mOnOff ? mLeftPos : mRightPos);
posAnimation->setEndValue(mOnOff ? mRightPos : mLeftPos);
connect(posAnimation, &QVariantAnimation::valueChanged, [=](const QVariant& value) {
mButtonRect.moveTo(value.toPointF());
update();
});
posAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped); //停止后删除
return;
}
/// 状态切换
mOnOff = onoff;
/// 发送信号
sigSwitchChanged(mOnOff);
/// 后运行背景颜色动画
QPropertyAnimation* colorAnimation = new QPropertyAnimation(this, "pBackColor");
colorAnimation->setDuration(mAnimationPeriod);
colorAnimation->setStartValue(mBackColor);
colorAnimation->setEndValue(mOnOff ? mBackOnColor : mBackOffColor);
colorAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped); //停止后删除
connect(colorAnimation, &QPropertyAnimation::valueChanged, [=](const QVariant& value) {
update();
});
}
void WBSwitchButton::setEnabled(bool enable) {
QWidget::setEnabled(enable);
mEnable = enable;
emit sigEnableChanged(mEnable);
update();
}
bool WBSwitchButton::getEnabled()
{
return mEnable;
}
void WBSwitchButton::setAnimationPeriod(int period) {
mAnimationPeriod = period;
}
void WBSwitchButton::setPrecisionClick(bool flag) {
mPrecisionClickFlagh = flag;
}
void WBSwitchButton::setWaitModel(bool flag)
{
mWaitSigModel = flag;
}
void WBSwitchButton::setButtonColor(QColor color) {
mButtonColor = color;
update();
}
void WBSwitchButton::setBackOnColor(QColor color) {
mBackOnColor = color;
update();
}
void WBSwitchButton::setBackOffColor(QColor color) {
mBackOffColor = color;
update();
}
void WBSwitchButton::setEdgeColor(QColor color) {
mEdgeColor = color;
update();
}
void WBSwitchButton::paintEvent(QPaintEvent* event) {
Q_UNUSED(event)
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setPen(Qt::NoPen);
/// 绘制边缘颜色
QPainterPath path;
path.addRect(this->rect());
path.addRoundedRect(this->rect(), mRadius, mRadius);
path.setFillRule(Qt::OddEvenFill);
painter.setBrush(mEdgeColor);
painter.drawPath(path);
/// 绘制背景颜色
painter.setBrush(mBackColor);
painter.drawRoundedRect(this->rect(), mRadius, mRadius);
/// 绘制圆形按钮
painter.setBrush(mButtonColor);
painter.drawEllipse(mButtonRect);
/// 绘制按钮阴影
painter.setBrush(Qt::NoBrush);
QColor color(Qt::black);
int count = (this->height() - mButtonRect.height()) / 2;
float stepColor = (0.15 - 0.0) / count;
for (int i = mButtonRect.height() / 2 + 1; i < this->height() / 2; i++) {
color.setAlphaF(0.15 - stepColor * (i - mButtonRect.height() / 2));
painter.setPen(color);
painter.drawEllipse(mButtonRect.center(), i, i);
}
/// 失能显示,添加一层蒙层
if (!mEnable) {
QColor disable(Qt::black);
disable.setAlphaF(0.5);
painter.setBrush(disable);
painter.drawRoundedRect(this->rect(), mRadius, mRadius);
}
}
void WBSwitchButton::resizeEvent(QResizeEvent* event) {
Q_UNUSED(event)
/// 更新按钮大小、圆角大小、动画两个位置
int size = qMin(this->width(), this->height());
mRadius = size / 2;
float width = size * 3 / 4;
float border = (size - width) / 2;
mLeftPos = QPoint(border, border);
mRightPos = QPoint(this->width() - border - width, border);
mButtonRect.setWidth(width);
mButtonRect.setHeight(width);
mButtonRect.moveTo(mOnOff ? mRightPos : mLeftPos);
mBackColor = mOnOff ? mBackOnColor : mBackOffColor;
update();
}
void WBSwitchButton::mouseReleaseEvent(QMouseEvent* event) {
if (mWaitSigModel) {
/// 先运行按钮位置动画
QVariantAnimation* posAnimation = new QVariantAnimation(this);
posAnimation->setDuration(mAnimationPeriod);
posAnimation->setStartValue(mOnOff ? mRightPos : mLeftPos);
posAnimation->setEndValue(mOnOff ? mLeftPos : mRightPos);
connect(posAnimation, &QVariantAnimation::valueChanged, [=](const QVariant& value) {
mButtonRect.moveTo(value.toPointF());
update();
});
posAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped); //停止后删除
return;
}
if (!mEnable) return;
if (mButtonRect.contains(event->pos()) || !mPrecisionClickFlagh) {
setSwitch(!mOnOff);
}
}
void WBSwitchButton::enterEvent(QEvent* event) {
Q_UNUSED(event)
mHover = true;
}
void WBSwitchButton::leaveEvent(QEvent* event) {
Q_UNUSED(event)
mHover = false;
}

@ -1,70 +0,0 @@
#ifndef WBSWITCHBUTTON_H
#define WBSWITCHBUTTON_H
#include <QWidget>
#include <QPropertyAnimation>
#include <QPainterPath>
#include <QPainter>
#include <QRadialGradient>
#include <QMouseEvent>
///
/// \brief 基础控件-Switch开关按钮
///
class WBSwitchButton : public QWidget
{
Q_OBJECT
public:
Q_PROPERTY(QColor pBackColor MEMBER mBackColor) //新增背景颜色属性,用于动画
explicit WBSwitchButton(QWidget* parent = nullptr);
bool getSwitch(); /// 获取开关状态
public slots:
void setSwitch(bool onoff); /// 设置开关状态,default:0
void setEnabled(bool enable); /// 设置使能状态,default:1
bool getEnabled(); /// 获取使能状态
void setAnimationPeriod(int period); /// 设置切换状态周期
void setPrecisionClick(bool flag); /// 设置精确点击,即只有点中按钮的时候才开关
void setWaitModel(bool flag); /// 设置等待模式点击后不会主动切换开关需要setSwitch
void setSwitchForWaitModel(bool onoff); /// 设置开关状态,default:0
void setButtonColor(QColor color); /// 设置开关(圆形按钮)颜色
void setBackOnColor(QColor color); /// 设置背景颜色-开
void setBackOffColor(QColor color); /// 设置背景颜色-关
void setEdgeColor(QColor color); /// 设置边缘颜色,默认透明
signals:
void sigEnableChanged(bool enable); /// 使能状态变化信号
void sigSwitchChanged(bool onoff); /// 开关状态变化信号
protected:
void paintEvent(QPaintEvent* event);
void resizeEvent(QResizeEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
private:
bool mOnOff{ 0 }; //开关状态
bool mEnable{ 1 }; //使能状态
bool mPrecisionClickFlagh{ 0 }; //精确点击标志位
bool mWaitSigModel{ 0 }; //等待模式,点击后按钮位置会进行切换,但是颜色需要等待外部信号变动
bool mAnimationOnOff{ 1 }; //动画开关default1
bool mHover{ 0 };
QColor mButtonColor{ Qt::white }; //开关(圆形按钮)颜色
QColor mBackColor{ Qt::red };
QColor mEdgeColor{ Qt::transparent }; //边缘颜色
QRectF mButtonRect; //开关按钮rect
int mRadius{ 8 }; // 开关外观边缘圆角
int mAnimationPeriod{ 200 }; //动画周期
QPointF mRightPos; // 动画位置-开
QPointF mLeftPos; // 动画位置-关
QColor mBackOnColor{ Qt::green }; //背景颜色-开
QColor mBackOffColor{ Qt::darkGray }; //背景颜色-关
};
#endif // WBSWITCHBUTTON_H

@ -95,3 +95,63 @@ bool AVPacketQueueManager::isEmptySeekQueue() {
QMutexLocker locker(&m_seekMutex);
return m_seekQueue.isEmpty();
}
void AVPacketQueueManager::waitForStreamDecoderClosed()
{
if(!m_isDecodeEnd){
m_waitCloseStreamDecoderMutex.lock();
qDebug()<<"waitForStreamDecoderClosed0";
m_condition.wait(&m_waitCloseStreamDecoderMutex);
m_waitCloseStreamDecoderMutex.unlock();
qDebug()<<"waitForStreamDecoderClosed1";
}
}
void AVPacketQueueManager::waitForStreamSaverClosed()
{
if(!m_isStreamSaverEnd){
m_waitCloseStreamSaverMutex.lock();
qDebug()<<"waitForStreamSaverClosed0";
m_condition.wait(&m_waitCloseStreamSaverMutex);
m_waitCloseStreamSaverMutex.unlock();
qDebug()<<"waitForStreamSaverClosed1";
}
}
void AVPacketQueueManager::waitForStreamPusherClosed()
{
if(!m_isStreamPusherEnd){
m_waitCloseStreamPusherMutex.lock();
qDebug()<<"waitForStreamPusherClosed0";
m_condition.wait(&m_waitCloseStreamPusherMutex);
m_waitCloseStreamPusherMutex.unlock();
qDebug()<<"waitForStreamPusherClosed1";
}
}
void AVPacketQueueManager::wakeStreamDecoder()
{
m_waitCloseStreamDecoderMutex.lock();
qDebug()<<"wakeStreamDecoder0";
m_condition.wakeAll();
m_waitCloseStreamDecoderMutex.unlock();
qDebug()<<"wakeStreamDecoder1";
}
void AVPacketQueueManager::wakeStreamSaver()
{
m_waitCloseStreamSaverMutex.lock();
qDebug()<<"wakeStreamSaver0";
m_condition.wakeAll();
m_waitCloseStreamSaverMutex.unlock();
qDebug()<<"wakeStreamSaver1";
}
void AVPacketQueueManager::wakeStreamPusher()
{
m_waitCloseStreamPusherMutex.lock();
m_condition.wakeAll();
qDebug()<<"wakeStreamPusher0";
m_waitCloseStreamPusherMutex.unlock();
qDebug()<<"wakeStreamPusher1";
}

@ -3,6 +3,7 @@
#include <QMutex>
#include <QQueue>
#include <QWaitCondition>
#include "ffmpeginclude.h"
@ -29,11 +30,22 @@ public:
bool isEmptySaveQueue();
bool isEmptySeekQueue();
void waitForStreamDecoderClosed();
void waitForStreamSaverClosed();
void waitForStreamPusherClosed();
void wakeStreamDecoder();
void wakeStreamSaver();
void wakeStreamPusher();
public:
std::atomic<bool> m_isSeeking{false}; // 是否正在跳转
std::atomic<bool> m_isReadEnd{false};
std::atomic<bool> m_isDecodeEnd{false};
std::atomic<bool> m_isStreamSaverEnd{false};
std::atomic<bool> m_isStreamPusherEnd{false};
std::atomic<bool> m_isPullReconnect{false};
std::atomic<bool> m_isPushReconnect{false};
private:
int QUEUECAPACITY = 100;
QQueue<AVPacket*> m_decodeQueue;
@ -44,6 +56,11 @@ private:
QMutex m_saveMutex; // 共享的互斥锁
QMutex m_pushMutex; // 共享的互斥锁
QMutex m_seekMutex; // 共享的互斥锁
private:
QWaitCondition m_condition;
QMutex m_waitCloseStreamDecoderMutex;
QMutex m_waitCloseStreamSaverMutex;
QMutex m_waitCloseStreamPusherMutex;
};
#endif // AVPACKETQUEUEMANAGER_H

@ -1,6 +1,9 @@
#include "decodestream.h"
DecodeStream::DecodeStream(QObject *parent) : QObject{parent}, m_playSpeed{1} {}
DecodeStream::DecodeStream(QObject *parent) : QObject{parent}, m_playSpeed{1} {
connect(this,&DecodeStream::initStreamDecoderSignal,this,&DecodeStream::init,Qt::BlockingQueuedConnection);
connect(this,&DecodeStream::initUDPStreamDecoderSignal,this,&DecodeStream::initUDPDecodeStream,Qt::BlockingQueuedConnection);
}
bool DecodeStream::init(AVPacketQueueManager *queueManager,
AVFormatContext *formatContext, int videoIndex) {
@ -22,13 +25,14 @@ bool DecodeStream::init(AVPacketQueueManager *queueManager,
if (m_fps <= 0) {
m_fps = 30.0; // 默认帧率
}
return initDecoder(formatContext, videoIndex);
initStatus = initDecoder(formatContext, videoIndex);
return initStatus;
}
bool DecodeStream::init(AVPacketQueueManager *queueManager) {
bool DecodeStream::initUDPDecodeStream(AVPacketQueueManager *queueManager) {
m_queueManager = queueManager;
return initUDPDecoder();
initStatus = initUDPDecoder();
return initStatus;
}
// 视频解码线程任务
@ -142,9 +146,12 @@ void DecodeStream::startDecode() {
}
free();
m_queueManager->m_isDecodeEnd = true;
m_queueManager->wakeStreamDecoder();
qDebug() << "Decoding Thread End!";
emit decodeEndSignal();
emit sendErrorMessageSignal("视频解码结束!", 1);
}
void DecodeStream::changePlaySpeedSlot(double speed) {

@ -18,7 +18,7 @@ public:
DecodeStream(QObject *parent = nullptr);
bool init(AVPacketQueueManager *queueManager,
AVFormatContext *formatContext, int videoIndex);
bool init(AVPacketQueueManager *queueManager);
bool initUDPDecodeStream(AVPacketQueueManager *queueManager);
void close();
AVCodecContext *getCodecContext();
AVCodecParserContext *getParserContext();
@ -31,7 +31,11 @@ signals:
void sendErrorMessageSignal(QString message, int type);
void updateVideoCurrentTime(int currentTime, int duration);
void decodeEndSignal();
void initUDPStreamDecoderSignal(AVPacketQueueManager *queueManager);
void initStreamDecoderSignal(AVPacketQueueManager *queueManager,
AVFormatContext *formatContext, int videoIndex);
public:
bool initStatus = false;
private:
bool initObject(); // 初始化对象
bool initDecoder(AVFormatContext *inputFormatContext,
@ -44,7 +48,7 @@ private:
bool isValidAVPacket(AVPacket *pkt);
private:
bool m_start = true;
std::atomic<bool>m_start = true;
int m_videoIndex = 0;
AVFormatContext *m_formatContext = nullptr;
AVCodecContext *m_codecContext = nullptr; // 解码器上下文

@ -22,7 +22,6 @@ extern "C" {
#include <QElapsedTimer>
#include <QEventLoop>
// #include "global.h"
#define PRINT_LOG 1
#define ERROR_LEN 1024 // 异常信息数组长度

@ -1,17 +1,32 @@
#include "pushstream.h"
PushStream::PushStream(QObject *parent) : QObject{parent} {}
PushStream::PushStream(QObject *parent) : QObject{parent} {
connect(this,&PushStream::initStreamPusherSignal,this,&PushStream::init,Qt::BlockingQueuedConnection);
}
void PushStream::setRemoteIP(QString url) {
m_pushStreamIP = url;
void PushStream::setRemoteIP(QStringList& urls) {
m_pushStreamIPs = urls;
}
bool PushStream::init(AVFormatContext *inputFormatCtx,
AVPacketQueueManager *queueManager) {
qDebug() << "PushStream::init ThreadID:" << QThread::currentThreadId();
// m_pusherQueue = queue;
m_queueManager = queueManager;
m_inputFormatCtx = inputFormatCtx;
m_start = openNetworkStream(inputFormatCtx);
// m_start = openNetworkStream(inputFormatCtx);
bool ss = false;
for (int i = 0; i < m_pushStreamIPs.size(); ++i) {
AVFormatContext* outputformatCtx = nullptr;
ss = openNetworkStream(inputFormatCtx,outputformatCtx,m_pushStreamIPs.at(i));
m_outputFormatCtxArray.append(outputformatCtx);
m_initStatus.append(ss);
if(ss){
m_start = ss;
}
}
initStatus = m_start;
return m_start;
}
@ -22,36 +37,37 @@ void PushStream::close() {
// qDebug() << "*******m_end0:" << m_end;
}
bool PushStream::openNetworkStream(AVFormatContext *inputFormatCtx) {
if (m_pushStreamIP.isEmpty()) return false;
bool PushStream::openNetworkStream(AVFormatContext *&inputFormatCtx,AVFormatContext *&outputFormatCtx,QString pushStreamIP) {
if (pushStreamIP.isEmpty()) return false;
if (!inputFormatCtx) return false;
// 初始化网络输出流
// QString m_pushStreamIP = "rtsp://182.92.130.23/app/stream999";
QString format_name = "flv";
if (m_pushStreamIP.left(3) == "udp" || m_pushStreamIP.left(3) == "UDP")
if (pushStreamIP.left(3) == "udp" || pushStreamIP.left(3) == "UDP")
format_name = "mpegts";
int ret = avformat_alloc_output_context2(
&m_outputFormatCtx, NULL, format_name.toStdString().data(),
m_pushStreamIP.toUtf8().constData());
&outputFormatCtx, NULL, format_name.toStdString().data(),
pushStreamIP.toUtf8().constData());
if (ret < 0) {
showError(ret);
free();
free(outputFormatCtx);
qDebug() << "Could not create output context.";
return false;
}
AVStream *m_ostream = nullptr;
// 复制流信息
for (unsigned int i = 0; i < inputFormatCtx->nb_streams; ++i) {
// AVStream *stream = inputFormatCtx->streams[i];
if (inputFormatCtx->streams[i]->codecpar->codec_type ==
AVMEDIA_TYPE_VIDEO) {
m_istream = inputFormatCtx->streams[i];
m_ostream = avformat_new_stream(m_outputFormatCtx, nullptr);
m_ostream = avformat_new_stream(outputFormatCtx, nullptr);
if (!m_ostream) {
qDebug() << "Failed allocating output stream.\n";
free();
free(outputFormatCtx);
return false;
}
// 复制编解码器参数
@ -59,7 +75,7 @@ bool PushStream::openNetworkStream(AVFormatContext *inputFormatCtx) {
m_istream->codecpar);
if (ret < 0) {
showError(ret);
free();
free(outputFormatCtx);
qWarning() << "avcodec_parameters_from_context Failed";
return false;
}
@ -71,25 +87,25 @@ bool PushStream::openNetworkStream(AVFormatContext *inputFormatCtx) {
m_inputTimeBase = m_istream->time_base;
m_inputFrameRate = m_istream->r_frame_rate;
m_outputTimeBase = m_istream->time_base;
// 打开输出文件
if (!(m_outputFormatCtx->flags & AVFMT_NOFILE)) {
if (ret = avio_open(&m_outputFormatCtx->pb,
m_pushStreamIP.toUtf8().constData(),
if (!(outputFormatCtx->flags & AVFMT_NOFILE)) {
if (ret = avio_open(&outputFormatCtx->pb,
pushStreamIP.toUtf8().constData(),
AVIO_FLAG_WRITE) < 0) {
showError(ret);
free();
free(outputFormatCtx);
qDebug() << "Could not open output file.\n";
return false;
}
}
// 写入头文件
ret = avformat_write_header(m_outputFormatCtx, NULL);
ret = avformat_write_header(outputFormatCtx, NULL);
if (ret < 0) {
showError(ret);
free();
free(outputFormatCtx);
qDebug() << "Error occurred when write_header into output file.\n";
return false;
}
@ -97,64 +113,89 @@ bool PushStream::openNetworkStream(AVFormatContext *inputFormatCtx) {
// m_InitStatus = true;
// startTime = av_gettime_relative();
m_outputTimeBase = m_ostream->time_base;
m_bwriteHeader = true;
m_firstPts = AV_NOPTS_VALUE;
return true;
}
int PushStream::reconnect(int ret) {
int PushStream::reconnect(int ret,int id) {
if(!m_queueManager->m_isPullReconnect) return 0;
if (ret == -10053 || ret == -10054) {
m_queueManager->m_isPushReconnect = true;
m_end = false;
// qDebug() << "m_end:" << m_end;
AVFormatContext* outputFormatCtx = m_outputFormatCtxArray.at(id);
QString message;
for (int nRetryCount = 0; nRetryCount < MAXCONNECT; ++nRetryCount) {
if(m_queueManager->m_isPullReconnect) {
break;
}
// 关闭输出
if (m_outputFormatCtx &&
!(m_outputFormatCtx->flags & AVFMT_NOFILE)) {
avio_close(m_outputFormatCtx->pb);
if (outputFormatCtx &&
!(outputFormatCtx->flags & AVFMT_NOFILE)) {
avio_close(outputFormatCtx->pb);
}
ret =
avio_open(&m_outputFormatCtx->pb,
m_pushStreamIP.toUtf8().constData(), AVIO_FLAG_WRITE);
avio_open(&outputFormatCtx->pb,
m_pushStreamIPs[id].toUtf8().constData(), AVIO_FLAG_WRITE);
if (ret < 0) {
showError(ret);
qDebug() << "Failed to reconnect"
<< QString::number(nRetryCount + 1);
QString str =
QString("网络中断,尝试重连第%1次!").arg(nRetryCount + 1);
emit sendErrorMessageSignal(str, 3);
message =
QString("推流地址%1网络中断尝试重连第%2次!").arg(id+1,nRetryCount + 1);
emit sendErrorMessageSignal(message, 3);
if (m_end) break;
// av_usleep(5 * 1000000);
continue;
}
// Try to reconnect
ret = avformat_write_header(m_outputFormatCtx, nullptr);
ret = avformat_write_header(outputFormatCtx, nullptr);
if (ret < 0) {
m_initStatus[id] = false;
showError(ret);
// free();
qDebug() << "Failed to reconnect"
<< QString::number(nRetryCount + 1);
QString str =
QString("网络中断,尝试重连第%1次!").arg(nRetryCount + 1);
emit sendErrorMessageSignal(str, 3);
message =
QString("推流地址%1网络中断尝试重连第%2次!").arg(id+1,nRetryCount + 1);
emit sendErrorMessageSignal(message, 3);
if (m_end) break;
// nRetryCount++;
// av_usleep(5 * 1000000);
} else {
m_initStatus[id] = true;
m_start = true;
m_firstPts = AV_NOPTS_VALUE;
m_frm_cnt = 0;
m_bwriteHeader = true;
emit sendErrorMessageSignal("重连成功!", 1);
message =
QString("推流地址%1重连成功!").arg(id+1);
emit sendErrorMessageSignal(message, 1);
m_queueManager->clearPushQueue();
qDebug() << "重连成功!";
qDebug() << message;
m_queueManager->m_isPushReconnect = false;
return ret;
}
if (m_end) break;
}
m_start = false;
for (int i = 0; i < m_initStatus.size(); ++i) {
if(m_initStatus.at(i)){
m_start = true;
break;
}
}
m_bwriteHeader = false;
emit sendErrorMessageSignal("重连失败,推流停止!", 2);
message =
QString("推流地址%1重连失败推流停止!").arg(id+1);
emit sendErrorMessageSignal(message, 2);
m_queueManager->m_isPushReconnect = false;
return -1;
}
return 0;
@ -163,6 +204,7 @@ int PushStream::reconnect(int ret) {
void PushStream::pushStream(int64_t startTime) {
qDebug() << "PushStreamThreadID:" << QThread::currentThreadId();
// m_startTime = startTime;
m_queueManager->m_isStreamPusherEnd = false;
while (m_start) {
AVPacket *inputPacket = m_queueManager->dequeuePushPacket();
if (inputPacket) {
@ -213,7 +255,7 @@ void PushStream::pushStream(int64_t startTime) {
// sleepMsec(40);
} else {
if (delay < -100000) {
qDebug() << "delay:" << delay;
qDebug() << "push delay:" << delay;
// 滞后100ms以上丢弃非重要帧
if (!(inputPacket->flags & AV_PKT_FLAG_KEY)) {
av_packet_unref(inputPacket);
@ -238,42 +280,79 @@ void PushStream::pushStream(int64_t startTime) {
// 向推流服务器推送流数据
m_frm_cnt++;
int ret =
av_interleaved_write_frame(m_outputFormatCtx, inputPacket);
if (ret < 0) {
av_packet_unref(inputPacket);
showError(ret);
// if (ret == -10053) {
// qDebug() << "网络不稳定";
// }
if (reconnect(ret) < 0) {
break;
};
continue;
int ret;
for (int i = 0; i < m_outputFormatCtxArray.size(); ++i) {
if(!m_initStatus.at(i)) continue;
AVFormatContext* outputFormatCtx = m_outputFormatCtxArray.at(i);
if(outputFormatCtx!=nullptr){
AVPacket* clonePacket = nullptr;
if(i!= m_outputFormatCtxArray.size()-1){
clonePacket = av_packet_clone(inputPacket);
}else{
clonePacket = inputPacket;
}
ret =
av_interleaved_write_frame(outputFormatCtx, clonePacket);
if (ret < 0) {
av_packet_unref(clonePacket);
showError(ret);
// if (ret == -10053) {
// qDebug() << "网络不稳定";
// }
if (reconnect(ret,i) < 0) {
// break;
};
continue;
}
// 数据包写入成功现在可以释放pkt
av_packet_unref(clonePacket);
av_packet_free(&clonePacket);
}
}
// 数据包写入成功现在可以释放pkt
av_packet_unref(inputPacket);
av_packet_free(&inputPacket);
} else {
// QThread::usleep(1000);
av_usleep(1000);
}
}
if (m_bwriteHeader) av_write_trailer(m_outputFormatCtx);
if (m_bwriteHeader) {
for (auto& outputFormatCtx : m_outputFormatCtxArray) {
if(outputFormatCtx!=nullptr){
av_write_trailer(outputFormatCtx);
}
}
}
free();
m_queueManager->m_isStreamPusherEnd = true;
m_queueManager->wakeStreamPusher();
qDebug() << "Push Stream End!";
emit sendErrorMessageSignal("推流结束!", 1);
}
void PushStream::free() {
m_start = false;
for (auto &outputFormatCtx : m_outputFormatCtxArray) {
// 关闭输出
if (outputFormatCtx && !(outputFormatCtx->flags & AVFMT_NOFILE)) {
avio_close(outputFormatCtx->pb);
}
if (outputFormatCtx) {
avformat_free_context(outputFormatCtx);
outputFormatCtx = nullptr;
}
}
m_outputFormatCtxArray.clear();
}
void PushStream::free(AVFormatContext *&outputFormatCtx)
{
// 关闭输出
if (m_outputFormatCtx && !(m_outputFormatCtx->flags & AVFMT_NOFILE)) {
avio_close(m_outputFormatCtx->pb);
if (outputFormatCtx && !(outputFormatCtx->flags & AVFMT_NOFILE)) {
avio_close(outputFormatCtx->pb);
}
if (m_outputFormatCtx) {
avformat_free_context(m_outputFormatCtx);
m_outputFormatCtx = nullptr;
if (outputFormatCtx) {
avformat_free_context(outputFormatCtx);
outputFormatCtx = nullptr;
}
}

@ -17,30 +17,36 @@ public:
* @brief
* @param url
*/
void setRemoteIP(QString url);
bool init(AVFormatContext *inputFormatCtx,
AVPacketQueueManager *queueManager);
void setRemoteIP(QStringList& urls);
void close();
public slots:
void pushStream(int64_t startTime);
bool init(AVFormatContext *inputFormatCtx,
AVPacketQueueManager *queueManager);
signals:
void startPushStreamSignal(int64_t startTime);
void sendErrorMessageSignal(QString message, int type);
void initStreamPusherSignal(AVFormatContext *inputFormatCtx,AVPacketQueueManager *queueManager);
private:
bool openNetworkStream(AVFormatContext *inputFormatCtx);
int reconnect(int ret);
bool openNetworkStream(AVFormatContext *&inputFormatCtx,AVFormatContext *&outputFormatCtx,QString pushStreamIP);
int reconnect(int ret,int id=0);
void free();
void free(AVFormatContext *&outputFormatCtx);
public:
bool initStatus = false;
private:
// QQueue<AVPacket *> *m_pusherQueue = nullptr;
AVFormatContext *m_inputFormatCtx = nullptr; //
AVFormatContext *m_outputFormatCtx = NULL; //
AVFormatContext *m_outputFormatCtx = nullptr; //
AVFormatContext *m_outputFormatCtx2 = nullptr; //推流2
QVector<AVFormatContext *>m_outputFormatCtxArray;
AVStream *m_istream = nullptr;
AVStream *m_ostream = nullptr;
// AVStream *m_ostream = nullptr;
bool m_bwriteHeader = false;
int m_videoIndex = -1;
QString m_pushStreamIP; // 推流地址
QStringList m_pushStreamIPs; // 推流地址
std::atomic<bool> m_start = false;
std::atomic<bool> m_end = false;
int64_t m_startTime;
@ -54,6 +60,8 @@ private:
AVRational m_inputTimeBase;
AVRational m_inputFrameRate;
AVRational m_outputTimeBase;
QVector<bool> m_initStatus;
};
#endif // PUSHSTREAM_H

@ -105,8 +105,10 @@ bool ReadStream::setStreamDecoder(DecodeStream *decodeStreamer) {
// QMutexLocker locker(&m_mutex);
m_streamDecoder = decodeStreamer;
m_queueManager.clearDecodeQueue();
m_decodeStreamFlag = m_streamDecoder->init(
&m_queueManager, m_formatContext, m_videoIndex);
// m_decodeStreamFlag = m_streamDecoder->init(
// &m_queueManager, m_formatContext, m_videoIndex);
m_streamDecoder->initStreamDecoderSignal(&m_queueManager, m_formatContext, m_videoIndex);
m_decodeStreamFlag = m_streamDecoder->initStatus;
if (m_decodeStreamFlag) emit decodeStreamer->startDecodeSignal();
return m_decodeStreamFlag;
} else {
@ -120,9 +122,11 @@ bool ReadStream::setUDPStreamDecoder(DecodeStream *decodeStreamer) {
// QMutexLocker locker(&m_mutex);
m_streamDecoder = decodeStreamer;
m_queueManager.clearDecodeQueue();
m_decodeStreamFlag = m_streamDecoder->init(&m_queueManager);
// m_decodeStreamFlag = m_streamDecoder->init(&m_queueManager);
// codec_ctx = m_streamDecoder->getCodecContext();
// parser = m_streamDecoder->getParserContext();
m_streamDecoder->initUDPStreamDecoderSignal(&m_queueManager);
m_decodeStreamFlag = m_streamDecoder->initStatus;
if (m_decodeStreamFlag) emit decodeStreamer->startDecodeSignal();
return m_decodeStreamFlag;
} else {
@ -138,8 +142,10 @@ bool ReadStream::setStreamSaver(SaveStream *streamSaver, bool isUDP) {
if (isUDP) {
m_saveStreamFlag = m_streamSaver->initUDP(&m_queueManager);
} else {
m_saveStreamFlag = m_streamSaver->init(
m_formatContext, &m_queueManager, m_videoIndex);
// m_saveStreamFlag = m_streamSaver->init(
// m_formatContext, &m_queueManager, m_videoIndex);
emit m_streamSaver->initStreamSaverSignal(m_formatContext, &m_queueManager, m_videoIndex);
m_saveStreamFlag = m_streamSaver->initStatus;
}
return m_saveStreamFlag;
} else {
@ -149,10 +155,13 @@ bool ReadStream::setStreamSaver(SaveStream *streamSaver, bool isUDP) {
}
bool ReadStream::setStreamPusher(PushStream *streamPusher) {
qDebug() << "setStreamPusher ThreadID:" << QThread::currentThreadId();
if (streamPusher) {
m_streamPusher = streamPusher;
m_queueManager.clearPushQueue();
m_pushStreamFlag = streamPusher->init(m_formatContext, &m_queueManager);
emit streamPusher->initStreamPusherSignal(m_formatContext, &m_queueManager);
// m_pushStreamFlag = streamPusher->init(m_formatContext, &m_queueManager);
m_pushStreamFlag = m_streamPusher->initStatus;
if (m_pushStreamFlag) streamPusher->startPushStreamSignal(0);
return m_pushStreamFlag;
} else {
@ -200,6 +209,7 @@ bool ReadStream::initSocket(QString ip, int port) {
}
}
}
}
bool ReadStream::initSavedRawFile(QString fileDir, QString uavName) {
@ -471,21 +481,25 @@ void ReadStream::free() {
}
bool ReadStream::reconnect() {
m_queueManager.m_isPullReconnect = true;
m_end = false;
if (m_streamDecoder) {
m_streamDecoder->close();
m_queueManager.waitForStreamDecoderClosed();
}
if (m_streamSaver) {
m_streamSaver->close();
m_queueManager.waitForStreamSaverClosed();
}
if (m_streamPusher) {
m_streamPusher->close();
m_queueManager.waitForStreamPusherClosed();
}
qDebug()<<"all closed!";
free();
for (int i = 0; i < MAXRECONNECT; ++i) {
m_start = openFile(m_pullURL);
if (m_start) {
emit sendErrorMessageSignal("重连成功!", 1);
if (m_streamDecoder) {
setStreamDecoder(m_streamDecoder);
}
@ -495,6 +509,8 @@ bool ReadStream::reconnect() {
if (m_streamPusher) {
setStreamPusher(m_streamPusher);
}
m_queueManager.m_isPullReconnect = false;
emit sendErrorMessageSignal("重连成功!", 1);
return true;
} else {
qDebug() << "reconnect failed:" << QString::number(i + 1);
@ -506,6 +522,7 @@ bool ReadStream::reconnect() {
if (m_end) break;
}
emit sendErrorMessageSignal("重连失败!", 2);
m_queueManager.m_isPullReconnect = false;
return false;
}

@ -12,7 +12,6 @@
#include "avpacketqueuemanager.h"
#include "decodestream.h"
#include "ffmpeginclude.h"
#include "pushstream.h"
#include "savestream.h"

@ -1,6 +1,8 @@
#include "savestream.h"
SaveStream::SaveStream(QObject *parent) : QObject{parent} {}
SaveStream::SaveStream(QObject *parent) : QObject{parent} {
connect(this,&SaveStream::initStreamSaverSignal,this,&SaveStream::init,Qt::BlockingQueuedConnection);
}
bool SaveStream::init(AVFormatContext *formatContext,
AVPacketQueueManager *queueManager, int videoIndex) {
@ -13,6 +15,7 @@ bool SaveStream::init(AVFormatContext *formatContext,
if (!m_start) {
free();
}
initStatus = m_start;
return m_start;
}
@ -37,8 +40,10 @@ void SaveStream::close() {
void SaveStream::startSaveStream() {
qDebug() << "SaveStreamThreadID:" << QThread::currentThreadId();
if (!m_start) {
m_queueManager->m_isStreamSaverEnd = true;
return;
}
m_queueManager->m_isStreamSaverEnd = false;
int frameIndex = 0;
int64_t dts = 0;
int64_t dts_last = 0;
@ -101,16 +106,6 @@ void SaveStream::startSaveStream() {
}
}
// AVStream *out_stream = m_formatContextSave->streams[0];
// out_stream->start_time = 0;
// m_formatContextSave->start_time = 0;
// int64_t duration_pts = last_pts - first_pts;
// // duration_pts = av_rescale(duration_pts, 1, AV_TIME_BASE);
// m_formatContextSave->duration = duration_pts;
// AVStream *stream = m_formatContextSave->streams[0];
// stream->duration = duration_pts;
// 写入文件尾
if (m_formatContextSave && m_writeHeader) {
av_write_trailer(m_formatContextSave);
@ -123,6 +118,8 @@ void SaveStream::startSaveStream() {
}
free();
m_queueManager->m_isStreamSaverEnd = true;
m_queueManager->wakeStreamSaver();
qDebug() << "Save Video End";
emit sendErrorMessageSignal("视频保存结束!", 1);
}
@ -130,8 +127,11 @@ void SaveStream::startSaveStream() {
void SaveStream::startSaveUDPStream() {
qDebug() << "SaveStreamThreadID:" << QThread::currentThreadId();
if (!m_start) {
m_queueManager->m_isStreamSaverEnd = true;
qDebug()<<"StreamSaver direct return ";
return;
}
m_queueManager->m_isStreamSaverEnd = false;
int frameIndex = 0;
int64_t pts = 0;
int64_t dts = 0;
@ -184,8 +184,11 @@ void SaveStream::startSaveUDPStream() {
}
free();
m_queueManager->m_isStreamSaverEnd = true;
m_queueManager->wakeStreamSaver();
qDebug() << "视频保存结束!";
emit sendErrorMessageSignal("视频保存结束!", 1);
}
bool SaveStream::openFile(bool isUDP) {

@ -30,12 +30,16 @@ public slots:
signals:
void startSaveStreamSignal();
void sendErrorMessageSignal(QString message, int type);
void initStreamSaverSignal(AVFormatContext *formatContext,
AVPacketQueueManager *queueManager, int videoIndex);
private:
bool openFile(bool isUDP = false);
void free();
bool isValidAVPacket(AVPacket *pkt);
public:
bool initStatus = false;
private:
AVFormatContext *m_formatContextSave = nullptr; // 封装上下文
AVFormatContext *m_inputFormatContext = nullptr;

@ -171,8 +171,8 @@ void VideoWidget::stopPlay() {
}
// 推流
bool VideoWidget::pushStream(const QString &url) {
if (url.isEmpty()) {
bool VideoWidget::pushStream(QStringList &urls) {
if (urls.isEmpty()) {
return false;
} else {
// 先拉流
@ -192,7 +192,7 @@ bool VideoWidget::pushStream(const QString &url) {
connect(streamPusher, &PushStream::sendErrorMessageSignal, this,
&VideoWidget::receiveErrorMessage, Qt::UniqueConnection);
}
streamPusher->setRemoteIP(url);
streamPusher->setRemoteIP(urls);
streamPusher->moveToThread(&pushStreamThread);
pushStreamThread.start();
@ -203,7 +203,7 @@ bool VideoWidget::pushStream(const QString &url) {
return false;
}
m_pushURL = url;
m_pushURLs = urls;
m_pushFlag = true;
return true;
}
@ -230,8 +230,8 @@ void VideoWidget::setPullURL(const QString &url) {
m_pullURL = url;
}
void VideoWidget::setPushURL(const QString &url) {
m_pushURL = url;
void VideoWidget::setPushURL(QStringList &urls) {
m_pushURLs = urls;
}
void VideoWidget::setVedioSaveFileDirPath(const QString &dirPath) {

@ -46,10 +46,10 @@ public:
bool play(const QString &url, bool bSave = true);
bool udpPlay(QString ip, int port);
void stopPlay();
bool pushStream(const QString &url);
bool pushStream(QStringList &urls);
void stopPushStream();
void setPullURL(const QString &url);
void setPushURL(const QString &url);
void setPushURL(QStringList &urls);
void setVedioSaveFileDirPath(const QString &dirPath);
bool getPlayStatus();
double getVideoDuration();
@ -98,7 +98,7 @@ private:
private:
QString m_pullURL;
QString m_pushURL;
QStringList m_pushURLs;
bool m_playFlag = false;
bool m_pullFlag = false;
bool m_pushFlag = false;

Loading…
Cancel
Save