feat: 新增交互界面

chen
cbwu 1 month ago
parent 3e3eb3f1bf
commit 6ac274af2f

@ -6,7 +6,7 @@ project(VideoClient LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 6.5 REQUIRED COMPONENTS Core Widgets OpenGLWidgets Network) find_package(Qt6 6.5 REQUIRED COMPONENTS Core Widgets OpenGLWidgets Network SerialPort)
qt_standard_project_setup() qt_standard_project_setup()

@ -0,0 +1,21 @@
#
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
# Debug
target_link_libraries(VideoClient
PRIVATE
Qt::Core
Qt::Widgets
${CMAKE_CURRENT_LIST_DIR}/QNotify/lib/QNotifyd.lib)
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
# Release
target_link_libraries(VideoClient
PRIVATE
Qt::Core
Qt::Widgets
${CMAKE_CURRENT_LIST_DIR}/QNotify/lib/QNotify.lib)
endif()
target_include_directories(VideoClient
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/QNotify/include
)

@ -6,12 +6,15 @@ qt_add_executable(VideoClient
mainwindow.h mainwindow.h
mainwindow.ui mainwindow.ui
global.h global.cpp global.h global.cpp
communicationsettingdlg.h communicationsettingdlg.cpp communicationsettingdlg.ui
commandwidget.h commandwidget.cpp commandwidget.ui
) )
set(3RDPARTY_DIR ${CMAKE_CURRENT_LIST_DIR}/3rdparty) set(3RDPARTY_DIR ${CMAKE_CURRENT_LIST_DIR}/3rdparty)
set(FFMPEG_DIR ${3RDPARTY_DIR}/ffmpeg) set(FFMPEG_DIR ${3RDPARTY_DIR}/ffmpeg)
add_subdirectory(video) add_subdirectory(video)
add_subdirectory(3rdparty)
target_link_libraries(VideoClient target_link_libraries(VideoClient
PRIVATE PRIVATE
@ -19,6 +22,7 @@ target_link_libraries(VideoClient
Qt::Widgets Qt::Widgets
Qt6::OpenGLWidgets Qt6::OpenGLWidgets
Qt6::Network Qt6::Network
Qt6::SerialPort
video video
) )

@ -0,0 +1,74 @@
#include "commandwidget.h"
#include "ui_commandwidget.h"
CommandWidget::CommandWidget(QWidget *parent)
: QWidget(parent), ui(new Ui::CommandWidget) {
ui->setupUi(this);
ui->stopConnectionToolBtn->setDisabled(true);
udpSocket = new QUdpSocket(this);
connect(&settingDlg, &CommunicationSettingDlg::sendErrorMessage, this,
&CommandWidget::receiveMessageSlots);
}
CommandWidget::~CommandWidget() {
delete ui;
if (udpSocket) udpSocket->deleteLater();
}
void CommandWidget::on_settingToolBtn_clicked() {
settingDlg.exec();
}
// 连接
void CommandWidget::on_startConnectionToolBtn_clicked() {
ui->startConnectionToolBtn->setDisabled(true);
if (!g_networkSettingInfo) return;
int dataSourceType = g_networkSettingInfo->value("DataSource").toInt();
QString groupName = "";
switch (dataSourceType) {
case 0:
groupName = "LLink";
break;
case 1:
groupName = "SATCOM1";
break;
case 2:
groupName = "SATCOM2";
break;
default:
break;
}
m_remoteIP =
g_networkSettingInfo->value(groupName + "/remoteIP").toString();
m_remotePort =
g_networkSettingInfo->value(groupName + "/remotePort").toInt();
int localPort =
g_networkSettingInfo->value(groupName + "/localPort").toInt();
QString localIP =
g_networkSettingInfo->value(groupName + "/localIP").toString();
if (dataSourceType == 0) { // 组播
emit startConnectionSignal(m_remoteIP, m_remotePort);
} else { // 单播
emit startConnectionSignal(localIP, localPort);
}
ui->stopConnectionToolBtn->setDisabled(false);
}
// 断开
void CommandWidget::on_stopConnectionToolBtn_clicked() {
ui->stopConnectionToolBtn->setDisabled(true);
emit stopConnectionSignal();
// ui->stopConnectionToolBtn->setDisabled(false);
ui->startConnectionToolBtn->setDisabled(false);
}
void CommandWidget::receiveMessageSlots(QString message, int type) {
emit sendErrorMessage(message, type);
}

@ -0,0 +1,42 @@
#ifndef COMMANDWIDGET_H
#define COMMANDWIDGET_H
#include <QUdpSocket>
#include <QWidget>
#include "communicationsettingdlg.h"
namespace Ui {
class CommandWidget;
}
class CommandWidget : public QWidget {
Q_OBJECT
public:
explicit CommandWidget(QWidget *parent = nullptr);
~CommandWidget();
QUdpSocket *udpSocket = nullptr;
signals:
void startConnectionSignal(QString ip, int port);
void stopConnectionSignal();
void sendErrorMessage(QString message, int type);
private slots:
void on_settingToolBtn_clicked();
void on_startConnectionToolBtn_clicked();
void on_stopConnectionToolBtn_clicked();
void receiveMessageSlots(QString message, int type);
private:
Ui::CommandWidget *ui;
CommunicationSettingDlg settingDlg;
QString m_remoteIP;
int m_remotePort;
};
#endif // COMMANDWIDGET_H

@ -0,0 +1,240 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CommandWidget</class>
<widget class="QWidget" name="CommandWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>897</width>
<height>105</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</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="bottomMargin">
<number>2</number>
</property>
<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>
<item>
<widget class="QToolButton" name="settingToolBtn">
<property name="text">
<string>通信设置</string>
</property>
<property name="icon">
<iconset>
<normaloff>res/settings.png</normaloff>res/settings.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="startConnectionToolBtn">
<property name="text">
<string>连接</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="stopConnectionToolBtn">
<property name="text">
<string>断开</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout">
<property name="spacing">
<number>12</number>
</property>
<item row="1" column="7">
<widget class="QPushButton" name="pushButton_15">
<property name="text">
<string>4-6图拼接</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="pushButton_8">
<property name="text">
<string>画中画开</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="pushButton_7">
<property name="text">
<string>画中画关</string>
</property>
</widget>
</item>
<item row="0" column="7">
<widget class="QPushButton" name="pushButton_16">
<property name="text">
<string>0-3图拼接</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="pushButton_4">
<property name="text">
<string>俯仰+</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>方位-</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>方位+</string>
</property>
</widget>
</item>
<item row="0" column="6">
<widget class="QPushButton" name="pushButton_14">
<property name="text">
<string>扫描</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QPushButton" name="pushButton_10">
<property name="text">
<string>红外小</string>
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="QPushButton" name="pushButton_11">
<property name="text">
<string>锁定</string>
</property>
</widget>
</item>
<item row="1" column="6">
<widget class="QPushButton" name="pushButton_13">
<property name="text">
<string>吊舱</string>
</property>
</widget>
</item>
<item row="0" column="5">
<widget class="QPushButton" name="pushButton_12">
<property name="text">
<string>复位</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="pushButton_5">
<property name="text">
<string>变焦推远</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="pushButton_6">
<property name="text">
<string>变焦拉近</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QPushButton" name="pushButton_9">
<property name="text">
<string>红外大</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="pushButton_3">
<property name="text">
<string>俯仰-</string>
</property>
</widget>
</item>
<item row="0" column="8">
<widget class="QPushButton" name="pushButton_17">
<property name="text">
<string>图像切换</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<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>
</widget>
<resources/>
<connections/>
</ui>

@ -0,0 +1,120 @@
#include "communicationsettingdlg.h"
#include "ui_communicationsettingdlg.h"
CommunicationSettingDlg::CommunicationSettingDlg(QWidget *parent)
: QDialog(parent), ui(new Ui::CommunicationSettingDlg) {
ui->setupUi(this);
initCombobox();
initNetworkSetting();
}
CommunicationSettingDlg::~CommunicationSettingDlg() {
delete ui;
}
void CommunicationSettingDlg::showEvent(QShowEvent *event) {
setSaveSettingParms();
}
void CommunicationSettingDlg::initCombobox() {
ui->dataSourceCombox->addItem(QStringLiteral("L测控"));
ui->dataSourceCombox->addItem(QStringLiteral("卫通1路"));
ui->dataSourceCombox->addItem(QStringLiteral("卫通2路"));
ui->dataSourceCombox->setCurrentIndex(0);
}
void CommunicationSettingDlg::initNetworkSetting() {
QStringList localIPList;
getLocalIP(localIPList);
ui->localIP->addItems(localIPList);
ui->remoteIPEdit->setText(QStringLiteral("223.0.0.1"));
ui->remotePort->setValue(8080);
ui->localPort->setValue(8082);
}
void CommunicationSettingDlg::setSaveSettingParms() {
if (!g_networkSettingInfo) return;
int dataSourceType = g_networkSettingInfo->value("DataSource").toInt();
ui->dataSourceCombox->setCurrentIndex(dataSourceType);
QString groupName = "";
switch (dataSourceType) {
case 0:
groupName = "LLink";
break;
case 1:
groupName = "SATCOM1";
break;
case 2:
groupName = "SATCOM2";
break;
default:
break;
}
ui->remoteIPEdit->setText(
g_networkSettingInfo->value(groupName + "/remoteIP").toString());
ui->remotePort->setValue(
g_networkSettingInfo->value(groupName + "/remotePort").toInt());
ui->localPort->setValue(
g_networkSettingInfo->value(groupName + "/localPort").toInt());
QString localIP =
g_networkSettingInfo->value(groupName + "/localIP").toString();
for (int i = 0; i < ui->dataSourceCombox->count(); ++i) {
if (localIP == ui->localIP->itemText(i)) {
ui->localIP->setCurrentIndex(i);
}
}
}
// 保存设置
void CommunicationSettingDlg::on_saveSettingBtn_clicked() {
ui->saveSettingBtn->setDisabled(true);
int dataType = ui->dataSourceCombox->currentIndex();
QString remoteIP = ui->remoteIPEdit->text();
if (dataType == 0) { // L链为组播
if (!isMulticastAddress(remoteIP)) {
emit sendErrorMessage("当前IP地址为单播地址请改为组播地址!", 2);
ui->saveSettingBtn->setDisabled(false);
return;
};
} else { // 卫通为单播
if (isMulticastAddress(remoteIP)) {
emit sendErrorMessage("当前IP地址为组播地址请改为单播地址!", 2);
ui->saveSettingBtn->setDisabled(false);
return;
};
}
// 保存配置参数
if (!g_networkSettingInfo) return;
g_networkSettingInfo->setValue("DataSource", dataType);
QString groupName = "";
switch (dataType) {
case 0:
groupName = "LLink";
break;
case 1:
groupName = "SATCOM1";
break;
case 2:
groupName = "SATCOM2";
break;
default:
break;
}
g_networkSettingInfo->setValue(groupName + "/localIP",
ui->localIP->currentText());
g_networkSettingInfo->setValue(groupName + "/localPort",
ui->localPort->value());
g_networkSettingInfo->setValue(groupName + "/remoteIP",
ui->remoteIPEdit->text());
g_networkSettingInfo->setValue(groupName + "/remotePort",
ui->remotePort->value());
ui->saveSettingBtn->setDisabled(false);
emit sendErrorMessage("保存成功!", 1);
}

@ -0,0 +1,41 @@
#ifndef COMMUNICATIONSETTINGDLG_H
#define COMMUNICATIONSETTINGDLG_H
#include <QDialog>
#include <QSettings>
#include <QUdpSocket>
#include "global.h"
namespace Ui {
class CommunicationSettingDlg;
}
class CommunicationSettingDlg : public QDialog {
Q_OBJECT
public:
explicit CommunicationSettingDlg(QWidget *parent = nullptr);
~CommunicationSettingDlg();
protected:
void showEvent(QShowEvent *event);
signals:
void sendErrorMessage(QString message, int type);
private slots:
void on_saveSettingBtn_clicked();
private:
void initCombobox();
void initNetworkSetting();
void setSaveSettingParms();
private:
Ui::CommunicationSettingDlg *ui;
};
#endif // COMMUNICATIONSETTINGDLG_H

@ -0,0 +1,192 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CommunicationSettingDlg</class>
<widget class="QDialog" name="CommunicationSettingDlg">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>307</width>
<height>275</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<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>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout" columnstretch="1,0">
<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>
</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/>
</ui>

@ -1,3 +1,45 @@
#include "global.h" #include "global.h"
QSettings* g_networkSettingInfo;
void getLocalIP(QStringList& localIPList) {
QHostInfo hostInfo = QHostInfo::fromName(QHostInfo::localHostName());
QList<QHostAddress> ipAddressesList;
foreach (QHostAddress address, hostInfo.addresses()) {
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
localIPList.append(address.toString());
}
}
}
void getLocalSerialPorts(QStringList& localCOMList) {
foreach (const QSerialPortInfo& info, QSerialPortInfo::availablePorts()) {
localCOMList.append(info.portName());
}
}
bool isValidIPAddress(const QString& ip) {
QHostAddress address;
return address.setAddress(ip);
}
bool isMulticastAddress(const QString& ip) {
QHostAddress address;
if (!address.setAddress(ip)) {
return false; // 如果不是有效的IP地址直接返回false
}
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
quint32 ip4 = address.toIPv4Address();
return ip4 >= QHostAddress("224.0.0.0").toIPv4Address() &&
ip4 <= QHostAddress("239.255.255.255").toIPv4Address();
} else if (address.protocol() == QAbstractSocket::IPv6Protocol) {
QString ipv6 = address.toString();
return ipv6.startsWith("ff",
Qt::CaseInsensitive); // IPv6组播地址以"ff"开头
}
return false;
}
Global::Global() {} Global::Global() {}

@ -1,6 +1,38 @@
#ifndef GLOBAL_H #ifndef GLOBAL_H
#define GLOBAL_H #define GLOBAL_H
#include <QHostAddress>
#include <QHostInfo>
#include <QList>
#include <QNetworkInterface>
#include <QSerialPortInfo>
#include <QSettings>
#include <QStringList>
extern QSettings* g_networkSettingInfo;
extern void getLocalIP(QStringList& localIPList);
extern void getLocalSerialPorts(QStringList& localCOMList);
extern bool isValidIPAddress(const QString& ip);
extern bool isMulticastAddress(const QString& ip);
struct NetworkIPStruct {
QString localIP;
QString remoteIP;
uint localPort;
uint remotePort;
};
// enum NotificationType {
// NOTIFICATION_INFORMATION = 0,
// NOTIFICATION_SUCCESS = 1,
// NOTIFICATION_ERROR = 2,
// NOTIFICATION_WARNING = 3
// };
class Global { class Global {
public: public:
Global(); Global();

@ -7,18 +7,61 @@
MainWindow::MainWindow(QWidget* parent) MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent), ui(new Ui::MainWindow) { : QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this); ui->setupUi(this);
QList<QString> list;
list.append("rtmp://liteavapp.qcloud.com/live/liteavdemoplayerstreamid"); // QList<QString> list;
list.append("rtsp://182.92.130.23/nmyj/video"); // list.append("rtmp://liteavapp.qcloud.com/live/liteavdemoplayerstreamid");
list.append("rtmp://182.92.130.23/nmyj/video"); // list.append("rtsp://182.92.130.23/nmyj/video");
list.append("rtmp://ns8.indexforce.com/home/mystream"); // list.append("rtmp://182.92.130.23/nmyj/video");
// ui->videoWidget->udpPlay("172.10.1.199", 13001); // list.append("rtmp://ns8.indexforce.com/home/mystream");
ui->videoWidget->setVedioSaveFileDirPath("./video"); // // ui->videoWidget->udpPlay("172.10.1.199", 13001);
// ui->videoWidget->setVedioSaveFileDirPath("./video");
ui->videoWidget->play(list.at(1));
// ui->videoWidget->play(list.at(1));
// ui->videoWidget->pushStream("udp://172.10.1.199:8009"); // ui->videoWidget->pushStream("udp://172.10.1.199:8009");
QString urlFile = "networkSettingInfo.ini";
QFile file(urlFile);
if (!file.exists()) {
file.open(QIODevice::WriteOnly);
file.close();
}
g_networkSettingInfo =
new QSettings("networkSettingInfo.ini", QSettings::IniFormat);
initSignalConnection();
initNotifyManager();
initNotifyMessageConnection();
} }
MainWindow::~MainWindow() { MainWindow::~MainWindow() {
delete ui; delete ui;
} }
void MainWindow::initSignalConnection() {
connect(ui->commandWidget, &CommandWidget::startConnectionSignal,
ui->videoWidget, &VideoWidget::udpPlay, Qt::UniqueConnection);
connect(ui->commandWidget, &CommandWidget::stopConnectionSignal,
ui->videoWidget, &VideoWidget::stopPlay, Qt::UniqueConnection);
}
void MainWindow::initNotifyMessageConnection() {
connect(ui->videoWidget, &VideoWidget::sendErrorMessageSignal, this,
&MainWindow::showMessageSlots);
connect(ui->commandWidget, &CommandWidget::sendErrorMessage, this,
&MainWindow::showMessageSlots);
}
void MainWindow::initNotifyManager() {
m_notifyManager = new NotifyManager(this, this);
m_notifyManager->setMaxCount(5);
m_notifyManager->setDisplayTime(2000);
m_notifyManager->setNotifyWndSize(400, 60);
}
void MainWindow::showMessageSlots(QString message, int type) {
if (m_notifyManager) {
m_notifyManager->notify(message, "", type, 3000);
}
}

@ -1,23 +1,35 @@
#ifndef MAINWINDOW_H #ifndef MAINWINDOW_H
#define MAINWINDOW_H #define MAINWINDOW_H
#include <QDebug>
#include <QMainWindow> #include <QMainWindow>
#include "NotifyManager.h"
#include "commandwidget.h"
#include "global.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
namespace Ui { namespace Ui {
class MainWindow; class MainWindow;
} }
QT_END_NAMESPACE QT_END_NAMESPACE
class MainWindow : public QMainWindow class MainWindow : public QMainWindow {
{
Q_OBJECT Q_OBJECT
public: public:
MainWindow(QWidget *parent = nullptr); MainWindow(QWidget *parent = nullptr);
~MainWindow(); ~MainWindow();
private:
void initSignalConnection();
void initNotifyMessageConnection();
void initNotifyManager();
void showMessageSlots(QString message, int type);
private: private:
Ui::MainWindow *ui; Ui::MainWindow *ui;
NotifyManager *m_notifyManager = nullptr;
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

@ -14,7 +14,7 @@
<string>MainWindow</string> <string>MainWindow</string>
</property> </property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QVBoxLayout" name="verticalLayout" stretch="6,1">
<property name="leftMargin"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
@ -30,6 +30,9 @@
<item> <item>
<widget class="VideoWidget" name="videoWidget"/> <widget class="VideoWidget" name="videoWidget"/>
</item> </item>
<item>
<widget class="CommandWidget" name="commandWidget" native="true"/>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QMenuBar" name="menubar"> <widget class="QMenuBar" name="menubar">
@ -49,6 +52,12 @@
<extends>QOpenGLWidget</extends> <extends>QOpenGLWidget</extends>
<header location="global">videowidget.h</header> <header location="global">videowidget.h</header>
</customwidget> </customwidget>
<customwidget>
<class>CommandWidget</class>
<extends>QWidget</extends>
<header>commandwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

@ -0,0 +1,261 @@
/************************QWidget窗体设置****************************/
QWidget{
background-color: rgb(50, 50, 50);
}
/************************QPushButton按钮设置****************************/
/*hover样式*/
QPushButton:hover{
background-color: rgb(155,155,155);
}
/*pressed样式*/
QPushButton:pressed{
background-color: rgb(180,238,180);
}
QPushButton{
background-color:rgb(50,50,50);
color:rgb(255,255,255);
border-radius: 2px;
border: 1px groove gray;
border-style: outset;
font: 10pt "微软雅黑";
}
/**************************QLineEdit编辑框设置*************************/
QLineEdit{
color:rgb(255,255,255);
border-radius: 2px;
border: 1px groove gray;
border-style: outset;
font: 10pt "微软雅黑";
}
/***************************QComboBox下拉框设置************************/
QComboBox
{
border-radius:3px;
background-color:rgba(60,60,60,200);
font: 75 10pt "微软雅黑";
color:rgb(255,255,255);
border:0px ;
padding-top: 2px;
padding-left: 2px;
border-width: 0.5px;
border-style: solid;
border-color: rgb(255,255,255);
}
QComboBox:disabled
{
background-color:rgba(50,50,50,200);
font: 75 10pt "微软雅黑";
color:rgb(160,160,160);
}
QComboBox:hover
{
background-color:rgba(45,45,45,200);
border:1px solid rgb(31,156,220) ;
}
/*点击combox的样式*/
QComboBox:on
{
border-radius:3px;
background-color:rgba(35,35,35,100);
font: 75 10pt "微软雅黑";
color:rgb(255,255,255);
border:1px solid rgb(31,156,220) ;
}
/*下拉框的样式*/
QComboBox QAbstractItemView
{
outline: 0px solid gray; /*取消选中虚线*/
border: 1px solid rgb(31,156,220);
font: 75 10pt "微软雅黑";
color: rgb(255,255,255);
background-color: rgb(45,45,45);
selection-background-color: rgb(90,90,90);
}
/*选中每一项高度*/
QComboBox QAbstractItemView::item
{
height: 25px;
}
/*选中每一项的字体颜色和背景颜色*/
QComboBox QAbstractItemView::item:selected
{
color: rgb(31,163,246);
background-color: rgb(90,90,90);
}
/**********************设置TabWidget中的样式*****************/
TabWidget::tab
{
border:1px solid rgb(31,156,220);
}
TabWidget::pane
{
border-top:3px;
/*border:none;*/
}
/**********************设置TabWidget中tab_x的样式*****************/
#tab_1.QWidget{
background-color: rgb(50, 50, 50);
}
#tab_2.QWidget{
background-color: rgb(50, 50, 50);
}
#tab_3.QWidget{
background-color: rgb(50, 50, 50);
}
#tab_4.QWidget{
background-color: rgb(50, 50, 50);
}
#tab_5.QWidget{
background-color: rgb(50, 50, 50);
}
#tab_6.QWidget{
background-color: rgb(50, 50, 50);
}
/************************QTabBar设置************************/
QTabBar::tab{
background-color: #0B0E11;
font-family:"微软雅黑"; /*设置tab中的文本的字体*/
font-size:12pt;
color:rgb(255,255,255); /*设置tab中的文本的颜色*/
border-top-left-radius: 5px; /*设置tab的边框的圆角左上圆角*/
border-top-right-radius: 5px; /*设置tab的边框的圆角右上圆角*/
min-width: 12px;
padding: 5px;
background-color: rgb(50, 50, 50);
}
/*设置TabWidget中QTabBar的tab被选中时的样式*/
QTabBar::tab:selected{
background-color: rgb(204, 229, 255);
color: rgb(50, 50, 50);
}
/*设置TabWidget中鼠标悬浮在QTabBar的tab上但未选中该Tab的样式*/
QTabBar::tab:hover:!selected {
background-color: rgb(224, 224, 224);
color: rgb(50, 50, 50);
}
/*设置TabWidget的边框的样式*/
QTabWidget::pane {
border: 1px solid rgb(108, 117, 125);
}
/*当打开多个tab右侧出现点击后可以向前向后的按钮的样式*/
QTabBar QToolButton {
border: none;
color: rgb(255, 206, 6);
background-color: #0b0e11;
}
QTabBar QToolButton:hover {
background-color: rgb(50, 50, 50);
}
/****************************QLabel设置************************/
QLabel{
color: rgb(255, 255, 255);
font-family:"微软雅黑";
font-size:10.5pt;
}
/***************************QSpinBox设置***********************/
QSpinBox{
border-radius:1px;
background-color:rgba(60,60,60,200);
font: 10pt "微软雅黑";
color:rgb(255,255,255);
border-width: 0.5px;
border-style: solid;
border-color: rgb(255,255,255);
}
/***************************QGroupBox设置***********************/
QGroupBox{
border:1px solid rgb(255,255,255);
border-radius:5px;
margin-top:2ex;/*文字在方框中位置的偏离度*/
color:rgb(153,255,153);
font-family:"微软雅黑";
font-size:12pt;
/*font:bold 12px;*/
}
QGroupBox::title
{
subcontrol-origin:margin;
subcontrol-position:top center;
right:5px;
left:0px;
padding:0 1px 0 1px;
}
/***************************QRadioButton设置***********************/
QRadioButton{
color: rgb(255, 255, 255);
font-family:"微软雅黑";
font-size:10pt;
}
QRadioButton::indicator {
width: 12px;
height: 12px;
border-radius: 7px; /* 让其看起来更圆 */
border: 1px solid #5A5A5A; /* 边框颜色 */
}
/* 如果没有合适的图片,可以使用伪元素模拟 */
QRadioButton::indicator:checked::enabled {
background-color: rgb(27,217,40);
image: none; /* 移除图片,使用纯色 */
}
/***************************QFrame***********************/
QFrame#frameGDDCPage
{
border:1px solid rgb(0,255,0);
}
/***************************QTableView***********************/
QTableView
{
color: rgb(255, 255, 255);
font: 10pt "Microsoft YaHei UI";
border:none;
background-color: rgba(50, 50, 50, 50); /*rgba(150, 223, 155, 50)*/
selection-background-color: rgba(96, 96, 96, 0); /*选中区域的背景色,设置成透明96,96,96*/
}
QTableWidget::item {
font-size: 10pt;
color: #34495E;
height: 30px;
width: 80px;
}
/*设置单元格背景颜色*/
QTableWidget::item:selected
{
background-color:#CCFFE5;
}
/*设置表头背景颜色、角落颜色*/
QHeaderView::section,QTableCornerButton:section
{
padding:3px;
margin:0px;
color:rgb(0, 255, 0);
border:1px solid #242424;
border-left-width:0px;
border-right-width:1px;
border-top-width:0px;
border-bottom-width:1px;
background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 rgb(128, 128, 128),stop:1 #525252);
}
QHeaderView::section:checked
{
background-color: rgb(11, 11, 11);
}
/*设置表格中的水平线和垂直线*/
QTableWidget::horizontalHeader
{
border-bottom: 1px solid #DADADA;
}
QTableWidget::verticalHeader
{
border-right: 1px solid #DADADA;
}
QDoubleSpinBox
{
color:white;
}

@ -0,0 +1,8 @@
/*hover样式*/
QPushButton:hover{
background-color: rgb(95,107,117);
}
/*pressed样式*/
QPushButton:pressed{
background-color: rgb(54,54,54);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 872 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 837 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 854 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 760 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@ -0,0 +1,12 @@
<!DOCTYPE RCC>
<RCC version="1.0"/>
/*************************MainWindow*****************/
#MainWindow
{
background-color: rgba(131,175,155 100%);
}
#pushButton:hover { color: red } //
#pushButton_9:enabled { color: blue } //
#pushButton_10:enabled { color: red } //

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

@ -0,0 +1,17 @@
<!DOCTYPE RCC>
<RCC version="1.0"/>
QPushButton_9 {
font: 15px;
min-width:2em;
font-family:黑体;
padding: 10px;
border-radius: 5px;
}
QPushButton_9:hover {
background-color: #2E3648;
color: #2E3648;
}
QPushButton_9:pressed, QPushButton:checked {
border: 1px solid #2E3648;
color: #2E3648;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 951 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

@ -9,7 +9,7 @@ ReadStream::ReadStream(QObject *parent) : QObject{parent} {
ReadStream::~ReadStream() { ReadStream::~ReadStream() {
if (udpSocket) { if (udpSocket) {
udpSocket->close(); udpSocket->abort();
udpSocket->deleteLater(); udpSocket->deleteLater();
} }
} }
@ -116,12 +116,16 @@ bool ReadStream::setUDPStreamDecoder(DecodeStream *decodeStreamer) {
return false; return false;
} }
bool ReadStream::setStreamSaver(SaveStream *streamSaver) { bool ReadStream::setStreamSaver(SaveStream *streamSaver, bool isUDP) {
if (streamSaver) { if (streamSaver) {
m_streamSaver = streamSaver; m_streamSaver = streamSaver;
m_queueManager.clearSaveQueue(); m_queueManager.clearSaveQueue();
m_saveStreamFlag = if (isUDP) {
m_streamSaver->init(m_formatContext, &m_queueManager, m_videoIndex); m_saveStreamFlag = m_streamSaver->initUDP(&m_queueManager);
} else {
m_saveStreamFlag = m_streamSaver->init(
m_formatContext, &m_queueManager, m_videoIndex);
}
return m_saveStreamFlag; return m_saveStreamFlag;
} else { } else {
m_saveStreamFlag = false; m_saveStreamFlag = false;
@ -146,6 +150,7 @@ void ReadStream::close() {
// QMutexLocker locker(&m_mutex); // QMutexLocker locker(&m_mutex);
m_start = false; m_start = false;
m_end = true; m_end = true;
emit closeUDPConnectionSignal();
// if (m_streamDecoder) { // if (m_streamDecoder) {
// m_streamDecoder->close(); // m_streamDecoder->close();
// } // }
@ -157,14 +162,28 @@ void ReadStream::close() {
// } // }
} }
void ReadStream::setUDPParms(QString ip, int port) {
m_UDPIP = ip;
m_UDPPort = port;
}
bool ReadStream::initSocket(QString ip, int port) { bool ReadStream::initSocket(QString ip, int port) {
qDebug() << "initSocket:" << QThread::currentThreadId();
if (udpSocket == nullptr) { if (udpSocket == nullptr) {
udpSocket = new QUdpSocket(this); udpSocket = new QUdpSocket();
} }
if (ip.isEmpty()) { if (ip.isEmpty()) {
return udpSocket->bind(QHostAddress::Any, port); return udpSocket->bind(QHostAddress::Any, port);
} else { } else {
if (!isMulticastAddress(ip)) { // 单播
return udpSocket->bind(QHostAddress(ip), port); return udpSocket->bind(QHostAddress(ip), port);
} else { // 组播
if (udpSocket->bind(QHostAddress::AnyIPv4, port,
QUdpSocket::ShareAddress)) {
return udpSocket->joinMulticastGroup(QHostAddress(ip));
}
}
} }
} }
@ -236,6 +255,11 @@ void ReadStream::startPullUDPStream() {
// parser = av_parser_init(AV_CODEC_ID_H264); // parser = av_parser_init(AV_CODEC_ID_H264);
// codec_ctx = avcodec_alloc_context3(NULL); // codec_ctx = avcodec_alloc_context3(NULL);
if (!initSocket(m_UDPIP, m_UDPPort)) {
emit sendErrorMessageSignal("UDP绑定失败!", 2);
return;
}
const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264); const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264);
codec_ctx = avcodec_alloc_context3(codec); codec_ctx = avcodec_alloc_context3(codec);
codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
@ -251,6 +275,15 @@ void ReadStream::startPullUDPStream() {
} }
} }
void ReadStream::closeUDPConnectionSLot() {
qDebug() << "closeUDPConnectionSLot";
if (udpSocket) {
udpSocket->abort();
delete udpSocket;
udpSocket = nullptr;
}
}
void ReadStream::initFFmpeg() { void ReadStream::initFFmpeg() {
avformat_network_init(); avformat_network_init();
// m_formatContext = avformat_alloc_context(); // m_formatContext = avformat_alloc_context();
@ -372,15 +405,34 @@ bool ReadStream::isValidAVPacket(AVPacket *pkt) {
return true; return true;
} }
bool ReadStream::isMulticastAddress(const QString &ip) {
QHostAddress address;
if (!address.setAddress(ip)) {
return false; // 如果不是有效的IP地址直接返回false
}
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
quint32 ip4 = address.toIPv4Address();
return ip4 >= QHostAddress("224.0.0.0").toIPv4Address() &&
ip4 <= QHostAddress("239.255.255.255").toIPv4Address();
} else if (address.protocol() == QAbstractSocket::IPv6Protocol) {
QString ipv6 = address.toString();
return ipv6.startsWith("ff",
Qt::CaseInsensitive); // IPv6组播地址以"ff"开头
}
return false;
}
void ReadStream::udpDataReceivedSlot() { void ReadStream::udpDataReceivedSlot() {
// qDebug() << "udpreceive:" << QThread::currentThreadId();
while (udpSocket->hasPendingDatagrams()) { while (udpSocket->hasPendingDatagrams()) {
QByteArray datagram; QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize()); datagram.resize(udpSocket->pendingDatagramSize());
udpSocket->readDatagram(datagram.data(), datagram.size()); udpSocket->readDatagram(datagram.data(), datagram.size());
// if (static_cast<uint8_t>(datagram.at(0)) == 0xEB && if (static_cast<uint8_t>(datagram.at(0)) == 0xEB &&
// static_cast<uint8_t>(datagram[1]) == 0x90 && static_cast<uint8_t>(datagram[1]) == 0x90 &&
// static_cast<uint8_t>(datagram[4]) == 0xD5) { static_cast<uint8_t>(datagram[4]) == 0xD5) {
if (1) {
// QByteArray tmp = datagram.sliced(2, 4); // QByteArray tmp = datagram.sliced(2, 4);
// uint16_t crc = // uint16_t crc =
// getCRC16Sum(reinterpret_cast<uint8_t*>(tmp.data()), // getCRC16Sum(reinterpret_cast<uint8_t*>(tmp.data()),
@ -390,14 +442,14 @@ void ReadStream::udpDataReceivedSlot() {
// if (crc != rawcrc) continue; // if (crc != rawcrc) continue;
// int videoDataLen = static_cast<int>(datagram.at(5) & 0xFF); // int videoDataLen = static_cast<int>(datagram.at(5) & 0xFF);
// int videoDataLen = 248; int videoDataLen = 248;
// QByteArray videoData = datagram.sliced(8, videoDataLen); QByteArray videoData = datagram.sliced(8, videoDataLen);
// uint8_t *inBuf = (uint8_t *)(videoData.data());
int videoDataLen = datagram.size();
QByteArray videoData = datagram.data();
uint8_t *inBuf = (uint8_t *)(videoData.data()); uint8_t *inBuf = (uint8_t *)(videoData.data());
// int videoDataLen = datagram.size();
// QByteArray videoData = datagram.data();
// uint8_t *inBuf = (uint8_t *)(videoData.data());
// if (videoDataLen > 0) { // if (videoDataLen > 0) {
// AVPacket packet; // AVPacket packet;
// av_init_packet(&packet); // av_init_packet(&packet);

@ -23,19 +23,22 @@ public:
bool openFile(const QString &url); bool openFile(const QString &url);
bool setStreamDecoder(DecodeStream *decodeStreamer); bool setStreamDecoder(DecodeStream *decodeStreamer);
bool setUDPStreamDecoder(DecodeStream *decodeStreamer); bool setUDPStreamDecoder(DecodeStream *decodeStreamer);
bool setStreamSaver(SaveStream *streamSaver); bool setStreamSaver(SaveStream *streamSaver, bool isUDP = false);
bool setStreamPusher(PushStream *streamPusher); // 开启推流 bool setStreamPusher(PushStream *streamPusher); // 开启推流
void close(); void close();
void setUDPParms(QString ip, int port);
bool initSocket(QString ip, int port); bool initSocket(QString ip, int port);
public slots: public slots:
void startPullStream(); void startPullStream();
void startPullUDPStream(); void startPullUDPStream();
void closeUDPConnectionSLot();
signals: signals:
// void startDecodeSignal(); // void startDecodeSignal();
void startPullStreamSignal(); void startPullStreamSignal();
void startPullUDPStreamSignal(); void startPullUDPStreamSignal();
void sendErrorMessageSignal(QString message, int type); void sendErrorMessageSignal(QString message, int type);
void closeUDPConnectionSignal();
private: private:
void initFFmpeg(); // 初始化ffmpeg库整个程序中只需加载一次 void initFFmpeg(); // 初始化ffmpeg库整个程序中只需加载一次
@ -54,6 +57,8 @@ private:
bool isValidAVPacket(AVPacket *pkt); bool isValidAVPacket(AVPacket *pkt);
bool isMulticastAddress(const QString &ip);
void udpDataReceivedSlot(); void udpDataReceivedSlot();
private: private:
@ -85,6 +90,8 @@ private:
private: private:
QUdpSocket *udpSocket = nullptr; QUdpSocket *udpSocket = nullptr;
QString m_UDPIP;
int m_UDPPort;
AVCodecParserContext *parser = nullptr; AVCodecParserContext *parser = nullptr;
AVCodecContext *codec_ctx = nullptr; AVCodecContext *codec_ctx = nullptr;
}; };

@ -16,6 +16,15 @@ bool SaveStream::init(AVFormatContext *formatContext,
return m_start; return m_start;
} }
bool SaveStream::initUDP(AVPacketQueueManager *queueManager) {
m_queueManager = queueManager;
m_start = openFile(true);
if (!m_start) {
free();
}
return m_start;
}
void SaveStream::setSaveFileDirPath(QString fileDirPath) { void SaveStream::setSaveFileDirPath(QString fileDirPath) {
m_outputDirPath = fileDirPath; m_outputDirPath = fileDirPath;
} }
@ -118,7 +127,68 @@ void SaveStream::startSaveStream() {
emit sendErrorMessageSignal("视频保存结束!", 1); emit sendErrorMessageSignal("视频保存结束!", 1);
} }
bool SaveStream::openFile() { void SaveStream::startSaveUDPStream() {
qDebug() << "SaveStreamThreadID:" << QThread::currentThreadId();
if (!m_start) {
return;
}
int frameIndex = 0;
int64_t pts = 0;
int64_t dts = 0;
int64_t dts_last = 0;
int64_t first_pts = 0;
int64_t last_pts = 0;
while (m_start || !m_queueManager->isEmptySaveQueue()) {
if (m_queueManager->isEmptySaveQueue()) {
QThread::usleep(1000);
continue;
}
AVPacket *inputPacket = m_queueManager->dequeueSavePacket();
if (inputPacket) {
// 由于保存的m_formatContextSave只创建了一个视频流而读取到的图像的流索引不一定为0可能会出现错误【Invalid
// packet stream index: 1】
// 所以这里需要将stream_index指定为和m_formatContextSave中视频流索引相同因为就一个流所以直接设置为0
inputPacket->stream_index = 0;
inputPacket->pos = -1;
inputPacket->pts = pts;
inputPacket->dts = dts;
// PTS & DTS 递增,假设帧率 30FPS
pts += 3000; // 90000 / 30 = 3000
dts += 3000;
int ret = av_interleaved_write_frame(
m_formatContextSave,
inputPacket); // 将数据包写入输出媒体文件
if (ret < 0) {
qDebug() << "save packet error!";
} else {
}
// av_packet_unref(inputPacket);
inputPacket = nullptr;
frameIndex++;
} else {
QThread::usleep(1000);
}
}
// 写入文件尾
if (m_formatContextSave && m_writeHeader) {
av_write_trailer(m_formatContextSave);
// if (m_formatContextSave->duration == 0) {
// m_formatContextSave->duration =
// (m_lastPts - m_firstPts) *
// av_q2d(m_formatContextSave->streams[0]->time_base);
// }
m_writeHeader = false;
}
free();
qDebug() << "视频保存结束!";
emit sendErrorMessageSignal("视频保存结束!", 1);
}
bool SaveStream::openFile(bool isUDP) {
// QMutexLocker locker(&m_mutex); // QMutexLocker locker(&m_mutex);
QDir dir; QDir dir;
if (!dir.exists(m_outputDirPath)) { if (!dir.exists(m_outputDirPath)) {
@ -144,7 +214,14 @@ bool SaveStream::openFile() {
qWarning() << "Create New Stream Error"; qWarning() << "Create New Stream Error";
return false; return false;
} }
if (isUDP) {
m_videoStreamOut->codecpar->codec_id = AV_CODEC_ID_H264;
m_videoStreamOut->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
m_videoStreamOut->codecpar->format = AV_PIX_FMT_YUV420P;
m_videoStreamOut->codecpar->width = 1920; // 确保设置正确的分辨率
m_videoStreamOut->codecpar->height = 1080;
m_videoStreamOut->time_base = {1, 90000}; // 设置时间基准
} else {
// 拷贝一些参数给codecpar赋值(这里使用编码器上下文进行赋值) // 拷贝一些参数给codecpar赋值(这里使用编码器上下文进行赋值)
ret = avcodec_parameters_copy( ret = avcodec_parameters_copy(
m_videoStreamOut->codecpar, m_videoStreamOut->codecpar,
@ -155,6 +232,7 @@ bool SaveStream::openFile() {
qWarning() << "avcodec_parameters_from_context Failed"; qWarning() << "avcodec_parameters_from_context Failed";
return false; return false;
} }
}
m_videoStreamOut->codecpar->codec_tag = 0; m_videoStreamOut->codecpar->codec_tag = 0;
av_dump_format(m_formatContextSave, 0, strName.toStdString().data(), 1); av_dump_format(m_formatContextSave, 0, strName.toStdString().data(), 1);

@ -17,6 +17,7 @@ public:
explicit SaveStream(QObject *parent = nullptr); explicit SaveStream(QObject *parent = nullptr);
bool init(AVFormatContext *formatContext, bool init(AVFormatContext *formatContext,
AVPacketQueueManager *queueManager, int videoIndex); AVPacketQueueManager *queueManager, int videoIndex);
bool initUDP(AVPacketQueueManager *queueManager);
/** /**
* @brief * @brief
* @param fileDirPath * @param fileDirPath
@ -25,12 +26,13 @@ public:
void close(); void close();
public slots: public slots:
void startSaveStream(); void startSaveStream();
void startSaveUDPStream();
signals: signals:
void startSaveStreamSignal(); void startSaveStreamSignal();
void sendErrorMessageSignal(QString message, int type); void sendErrorMessageSignal(QString message, int type);
private: private:
bool openFile(); bool openFile(bool isUDP = false);
void free(); void free();
bool isValidAVPacket(AVPacket *pkt); bool isValidAVPacket(AVPacket *pkt);

@ -14,6 +14,7 @@ VideoWidget::VideoWidget(QWidget *parent, Qt::WindowFlags f)
// 初始化视图大小由于Shader里面有YUV转RGB的代码会初始化显示为绿色这里通过将视图大小设置为0避免显示绿色背景 // 初始化视图大小由于Shader里面有YUV转RGB的代码会初始化显示为绿色这里通过将视图大小设置为0避免显示绿色背景
m_pos = QPointF(0, 0); m_pos = QPointF(0, 0);
m_zoomSize = QSize(0, 0); m_zoomSize = QSize(0, 0);
setVedioSaveFileDirPath("./video");
} }
#endif #endif
@ -131,9 +132,11 @@ void VideoWidget::stopPlay() {
if (!m_pushFlag) { if (!m_pushFlag) {
if (readStreamer) { if (readStreamer) {
readStreamer->close(); readStreamer->close();
QThread::msleep(100);
} }
readStreamThread.quit(); readStreamThread.quit();
readStreamThread.wait(); readStreamThread.wait();
m_pullFlag = false; m_pullFlag = false;
if (saveStreamer) { if (saveStreamer) {
@ -610,19 +613,22 @@ bool VideoWidget::pullUDPStream(QString ip, int port) {
connect(readStreamer, &ReadStream::sendErrorMessageSignal, this, connect(readStreamer, &ReadStream::sendErrorMessageSignal, this,
&VideoWidget::receiveErrorMessage, Qt::UniqueConnection); &VideoWidget::receiveErrorMessage, Qt::UniqueConnection);
} }
if (readStreamer->initSocket(ip, port)) { connect(readStreamer, &ReadStream::closeUDPConnectionSignal, readStreamer,
&ReadStream::closeUDPConnectionSLot, Qt::UniqueConnection);
// if (readStreamer->initSocket(ip, port)) {
// 保存线程 // 保存线程
if (!saveStreamer) { if (!saveStreamer) {
saveStreamer = new SaveStream; saveStreamer = new SaveStream;
connect(saveStreamer, &SaveStream::startSaveStreamSignal, connect(saveStreamer, &SaveStream::startSaveStreamSignal, saveStreamer,
saveStreamer, &SaveStream::startSaveStream, &SaveStream::startSaveUDPStream, Qt::UniqueConnection);
Qt::UniqueConnection);
} }
saveStreamer->setSaveFileDirPath(m_videoSaveDirPath); saveStreamer->setSaveFileDirPath(m_videoSaveDirPath);
saveStreamer->moveToThread(&saveStreamThread); saveStreamer->moveToThread(&saveStreamThread);
saveStreamThread.start(); saveStreamThread.start();
readStreamer->setStreamSaver(saveStreamer); readStreamer->setStreamSaver(saveStreamer, true);
readStreamer->setUDPParms(ip, port);
qDebug() << "UIThreadID:" << QThread::currentThreadId(); qDebug() << "UIThreadID:" << QThread::currentThreadId();
readStreamer->moveToThread(&readStreamThread); readStreamer->moveToThread(&readStreamThread);
@ -630,8 +636,8 @@ bool VideoWidget::pullUDPStream(QString ip, int port) {
emit readStreamer->startPullUDPStreamSignal(); emit readStreamer->startPullUDPStreamSignal();
return true; return true;
} // }
return false; // return false;
} }
void VideoWidget::receiveErrorMessage(QString message, int type) { void VideoWidget::receiveErrorMessage(QString message, int type) {

@ -46,7 +46,7 @@ public:
bool play(const QString &url); bool play(const QString &url);
bool udpPlay(QString ip, int port); bool udpPlay(QString ip, int port);
void stopPlay(); void stopPlay();
void stopUDPPlay(); // void stopUDPPlay();
bool pushStream(const QString &url); bool pushStream(const QString &url);
void stopPushStream(); void stopPushStream();
void setPullURL(const QString &url); void setPullURL(const QString &url);
@ -116,6 +116,7 @@ private:
signals: signals:
void startPullSignal(); void startPullSignal();
void sendErrorMessageSignal(QString message, int type); void sendErrorMessageSignal(QString message, int type);
void closeUDPConnectionSignal();
public: public:
QSizeF getCurImgSize(); QSizeF getCurImgSize();

Loading…
Cancel
Save