新增大窗口播放视频按键,窗口大小根据解码视频分辨率自动调整,关掉大窗口回到原来位置播放视频

This commit is contained in:
钟富强 2024-09-05 09:25:01 +08:00
parent 4bd2d7eab5
commit 9a49eab4af
13 changed files with 2228 additions and 92 deletions

View File

@ -4,6 +4,7 @@
FFmpegDecoder::FFmpegDecoder(QObject* parent) :
QThread(parent),
videoLabel(nullptr),
resolutionEdit(nullptr),
abort(false),
restart(false),
formatContext(nullptr),
@ -33,32 +34,49 @@ FFmpegDecoder::~FFmpegDecoder()
void FFmpegDecoder::processVideo(int itemIndex)
{
#if 1
int width = 720;
int height = 1280;
#elif 1
int width = 640;
int height = 480;
QLabel* originalLabel = this->videoLabel;
if ((FocusWindowWidth != 0) && (FocusWindowHeight != 0)) {
qDebug() << "------ processVideo";
FocusWindowDialog* dialog = nullptr;
if (FocusWindowWidth * 16 == FocusWindowHeight * 9) {
dialog = new FocusWindowDialog(nullptr, QSize(540, 960));
}
else if (FocusWindowWidth * 9 == FocusWindowHeight * 16) {
dialog = new FocusWindowDialog(nullptr, QSize(960, 540));
}
else if (FocusWindowWidth * 4 == FocusWindowHeight * 3) {
dialog = new FocusWindowDialog(nullptr, QSize(480, 640));
}
else if (FocusWindowWidth * 3 == FocusWindowHeight * 4) {
dialog = new FocusWindowDialog(nullptr, QSize(640, 480));
}
else {
qDebug() << "------ Other scaled resolutions use 480x640";
dialog = new FocusWindowDialog(nullptr, QSize(480, 640));
}
#if 0
// 将视频显示的 QLabel 切换为对话框内的 videoDisplayLabel
this->videoLabel = dialog->videoDisplayLabel;
if ((dialog->exec() == QDialog::Accepted) || (dialog->exec() == QDialog::Rejected)) {
this->videoLabel = originalLabel;
}
#else
int width = 480;
int height = 640;
mutex.lock();
this->videoLabelTemp = dialog->videoDisplayLabel; // 更新临时标签
this->videoLabelChanged = true; // 设置标志位,表示标签已更改
mutex.unlock();
if ((dialog->exec() == QDialog::Accepted) || (dialog->exec() == QDialog::Rejected)) {
mutex.lock();
this->videoLabelTemp = originalLabel; // 还原回原始标签
this->videoLabelChanged = true; // 设置标志位
mutex.unlock();
}
#endif
qDebug() << "------ processVideo";
if (width * 16 == height * 9) {
FocusWindowDialog* dialog = new FocusWindowDialog(nullptr, QSize(540, 960));
dialog->exec();
delete dialog;
}
else if (width * 9 == height * 16) {
FocusWindowDialog* dialog = new FocusWindowDialog(nullptr, QSize(960, 540));
dialog->exec();
}
else if (width * 4 == height * 3) {
FocusWindowDialog* dialog = new FocusWindowDialog(nullptr, QSize(480, 640));
dialog->exec();
}
else if (width * 3 == height * 4) {
FocusWindowDialog* dialog = new FocusWindowDialog(nullptr, QSize(640, 480));
dialog->exec();
else {
qDebug() << "------ Please wait for the video to be decoded and rendered before clicking";
}
}
@ -94,7 +112,7 @@ void FFmpegDecoder::run()
{
QFile file(filePath);
qint64 fileSize = 0;
QLabel* currentVideoLabel = videoLabel;
while (!isInterruptionRequested()) {
mutex.lock();
while (!restart && !abort) {
@ -105,7 +123,7 @@ void FFmpegDecoder::run()
qDebug() << "Decoder thread aborting";
break;
}
QLabel* currentVideoLabel = videoLabel;
/*QLabel* currentVideoLabel = videoLabel;*/
QSize labelSize = currentVideoLabel->size();
mutex.unlock();
@ -153,9 +171,18 @@ void FFmpegDecoder::run()
qWarning() << "Error during decoding";
break;
}
mutex.lock();
if (videoLabelChanged) {
currentVideoLabel = videoLabelTemp; // 更新 currentVideoLabel
videoLabelChanged = false; // 重置标志位
labelSize = currentVideoLabel->size();
}
mutex.unlock();
QImage img = avFrameToQImage(frame);
QImage scaledImage = img.scaled(labelSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
currentVideoLabel->setPixmap(QPixmap::fromImage(scaledImage));
//currentVideoLabel->setPixmap(QPixmap::fromImage(scaledImage));
QMetaObject::invokeMethod(currentVideoLabel, "setPixmap", Qt::QueuedConnection, Q_ARG(QPixmap, QPixmap::fromImage(scaledImage)));
QThread::msleep(10); // Simulate 25 FPS frame rate
}
}
@ -266,12 +293,18 @@ QImage FFmpegDecoder::avFrameToQImage(AVFrame* frame)
{
int width = frame->width;
int height = frame->height;
// 这里注意切换镜头后是否改变分辨率去改变 isGotResolution
if (!isGotResolution && (width != 0) && (height != 0)) {
isGotResolution = true;
FocusWindowWidth = width;
FocusWindowHeight = height;
}
QString resolutionText = QString::number(width) + " x " + QString::number(height);
resolutionEdit->setText(resolutionText);
//resolutionEdit->setText(resolutionText);
QMetaObject::invokeMethod(resolutionEdit, "setText", Qt::QueuedConnection, Q_ARG(QString, resolutionText));
//qDebug() << "H264 video resolution: Width =" << frame->width << ", Height =" << frame->height;
AVPixelFormat pixFmt = (AVPixelFormat)frame->format;
if (!swsContext) {
swsContext = sws_getContext(width, height, pixFmt, width, height, AV_PIX_FMT_RGB24, SWS_BILINEAR, nullptr, nullptr, nullptr);
if (!swsContext) {

View File

@ -56,9 +56,12 @@ private:
QWaitCondition condition;
QString filePath;
QLabel* videoLabel;
QLabel* videoLabelTemp; // 临时存储新标签
QLineEdit* resolutionEdit;
bool videoLabelChanged = false;
bool abort;
bool restart;
bool isGotResolution = false;
AVFormatContext* formatContext;
AVCodecContext* codecContext;
@ -66,9 +69,10 @@ private:
AVPacket* packet;
SwsContext* swsContext;
int videoStreamIndex;
int FocusWindowWidth = 0;
int FocusWindowHeight = 0;
RingBuffer* ringBuffer;
//QLabel* singleFrameLabel; // 用于解码单帧的标签
};
#endif // FFMPEGDECODER_H

View File

@ -1,3 +1,4 @@
// FocusWindow.h
#include <QDialog>
#include <QVBoxLayout>
#include <QLabel>
@ -8,12 +9,13 @@ class FocusWindowDialog : public QDialog
Q_OBJECT
public:
QLabel* videoDisplayLabel;
FocusWindowDialog(QWidget* parent = nullptr, const QSize& labelSize = QSize(480, 640)) : QDialog(parent)
{
QVBoxLayout* layout = new QVBoxLayout(this);
QLabel* label = new QLabel("1111 This is a new window opened by clicking the new button.", this);
label->setFixedSize(labelSize); // 设置QLabel的固定大小
layout->addWidget(label);
videoDisplayLabel = new QLabel(this);
videoDisplayLabel->setFixedSize(labelSize); // 设置QLabel的固定大小
layout->addWidget(videoDisplayLabel);
QPushButton* closeButton = new QPushButton("Close", this);
layout->addWidget(closeButton);

View File

@ -45,14 +45,10 @@ ClientHandler::ClientHandler(QJsonArray frontBoardOneClickTest, QJsonArray front
void ClientHandler::initialize(QTcpSocket* socket)
{
this->socket = socket;
// 连接信号槽,将其放在同一线程中处理
connect(socket, &QTcpSocket::readyRead, this, &ClientHandler::onDataReceived);
connect(socket, &QTcpSocket::readyRead, this, &ClientHandler::onDataReceived);
connect(socket, &QTcpSocket::disconnected, this, &ClientHandler::onDisconnected);
// 其他的 socket 操作都将在这个线程中进行
}
#endif
ClientHandler::~ClientHandler()

View File

@ -118,6 +118,7 @@ signals:
void startReadTimer(int client_Id);
void stopReadTimer(int client_Id);
void sendDataToSomeClient(int client_Id, const QByteArray& data);
void openFocusWindowRequested(int itemIndex);
public slots:
#if !TEST_TCP_MOVE_TO_MAIN

View File

@ -21,7 +21,6 @@ DataHandler::DataHandler(QLabel* videoLabel, QLineEdit* VideoResolutionEdit, QSi
backBoardUuidJson(backBoardUuidJson), getPicJson(getPicJson), getVideoJson(getVideoJson),
buffer(new QByteArray()), picBuffer(new QByteArray()),
ffmpegDecoder(new FFmpegDecoder()) // 初始化FFmpeg解码器
{
ffmpegDecoder->initialize(); // 初始化解码器
clearAllRecvData();
@ -46,7 +45,6 @@ DataHandler::~DataHandler()
void DataHandler::handleOpenFocusWindow(int itemIndex)
{
if (ffmpegDecoder) {
qDebug() << "------- handleOpenFocusWindow";
ffmpegDecoder->processVideo(itemIndex); // 调用 FFmpegDecoder 的处理函数
}
}
@ -77,7 +75,6 @@ void DataHandler::showVideo(const QString& client, const QByteArray& valData)
return;
}
}
if (!valData.isEmpty()) {
file.write(valData);
file.flush(); // 刷新文件缓冲区
@ -111,9 +108,9 @@ void DataHandler::clearAllRecvData() {
}
// 处理接收到的数据
void DataHandler::handleData(const QString& client, const QByteArray& recvData, int msg_id,
int currentRecvItemIndex, int currentRecvFuncItemIndex,
const QString& itemData, const QString& funcItemData, const QJsonArray& jsonArray, int itemJsonIndex)
void DataHandler::handleData(const QString& client, const QByteArray& recvData, int msg_id,
int currentRecvItemIndex, int currentRecvFuncItemIndex, const QString& itemData,
const QString& funcItemData, const QJsonArray& jsonArray, int itemJsonIndex)
{
//qDebug() << "Data received from" << client << ":" << recvData;
#if 0
@ -158,10 +155,8 @@ void DataHandler::handleData(const QString& client, const QByteArray& recvData,
//QByteArray data = buffer->mid(10, dataSize);
QString hexString = QString::fromUtf8(data.toHex().data());
QByteArray actual_data = hexStringToByteArray(hexString);
//qDebug() << "---Received hexString:" << hexString;
buffer->remove(0, totalSize); // 移除已处理的数据
if(msg_id != 0x0040 && msg_id != START_VIDEO && msg_id != GET_IMG) {
//if (msg_id != 0x0040) {
qDebug() << "--- msg_id:" << msg_id;
qDebug() << "--- clientLastMsgId.value(client, 0):" << clientLastMsgId.value(client, 0);
// 同一个client仅当 msg_id 不连续为 0x11/0x21 或第一次处理时才执行 emit statusUpdated
@ -186,7 +181,6 @@ void DataHandler::handleData(const QString& client, const QByteArray& recvData,
}
}
void DataHandler::initializeMsgIdToCmdMap() {
msgIdToCmdMap[GET_FRONT_V851_VERSION] = "GET_FRONT_V851_VERSION";
msgIdToCmdMap[GET_FRONT_MCU_VERSION] = "GET_FRONT_MCU_VERSION";

View File

@ -40,12 +40,11 @@ public:
QJsonArray backBoardDevInfoJson, QJsonArray backBoardUuidJson,
QJsonArray getPicJson, QJsonArray getVideoJson, QObject* parent = nullptr);
~DataHandler();
void handleOpenFocusWindow(int itemIndex);
public slots:
void handleData(const QString& client, const QByteArray& data, int msg_id, int currentRecvItemIndex,
int currentRecvFuncItemIndex, const QString& itemData, const QString& funcItemData,
const QJsonArray& jsonArray, int itemJsonIndex);
void handleOpenFocusWindow(int itemIndex);
signals:
void statusUpdated(const QString& clientAddress, int currentItemIndex, int currentFuncItemIndex,
bool success, const QString& currentItemLabel, const QString& currentFuncItemLabel,

View File

@ -96,26 +96,20 @@ MainWidget::MainWidget(QWidget* parent) :
frontBoardDevInfoJson, frontBoardLicenseJson,
backBoardOneClickTest, backBoardTest, backBoardFuncConfig, backBoardDevInfoJson,
backBoardUuidJson, getPicJson, getVideoJson, clientId, isBackBoardOrAllBoard, nullptr);
/*
* QObject::moveToThread: Cannot move objects with a parent
* 线线
*/
// 一定要先取消父对象
socket->setParent(nullptr);
// 将 socket 移动到新的线程中
socket->moveToThread(thread);
// 将 socket 传递到 handler
handler->initialize(socket);
#endif
// 将 ClientHandler 移到线程池中的线程
handler->moveToThread(thread);
// 创建 DataHandler 对象并连接信号
DataHandler* dataHandler = new DataHandler(videoLabel, VideoResolutionEdit, leftLens_imageLabel->size(), &devInfoLineEdits,
frontBoardOneClickTest, frontBoardTest, frontBoardFuncConfig,
frontBoardDevInfoJson, frontBoardLicenseJson,
backBoardOneClickTest, backBoardTest, backBoardFuncConfig, backBoardDevInfoJson,
backBoardUuidJson, getPicJson, getVideoJson, nullptr);
// 将 DataHandler 移到线程池中的线程
// 将 ClientHandler 和 DataHandler 移到线程池中的线程
handler->moveToThread(thread);
dataHandler->moveToThread(thread);
#if TEST_TCP_MOVE_TO_MAIN
// 将sendData信号连接到主线程中的槽上
@ -403,7 +397,7 @@ void MainWidget::setupUI()
// 前板
QGroupBox* frontDeviceInfoGroupBox = createFrontDeviceInfoGroupBox();
QGroupBox* frontLicenseGroupBox = createLicenseGroupBox();
QGroupBox* frontLicenseGroupBox = createLicenseGroupBox();
QHBoxLayout* frontGroupBoxLayout = new QHBoxLayout;
frontGroupBoxLayout->addWidget(frontDeviceInfoGroupBox, 1);
@ -411,20 +405,20 @@ void MainWidget::setupUI()
QTabWidget* frontTabWidget = new QTabWidget(this);
frontTabWidget->setFixedSize(900, 315);
QWidget* frontFunctionTestTab = createFunctionTestTab(frontBoardTest, "frontBoardTest");
QWidget* frontFunctionTestTab = createFunctionTestTab(frontBoardTest, "frontBoardTest");
QWidget* frontFunctionConfigTab = createFunctionConfigTab(frontBoardFuncConfig, "frontBoardFuncConfig");
frontTabWidget->addTab(frontFunctionTestTab, "前板测试项");
frontTabWidget->addTab(frontFunctionTestTab, "前板测试项");
frontTabWidget->addTab(frontFunctionConfigTab, "前板配置项");
QTabWidget* mainTabWidget = new QTabWidget(this);
QWidget* frontDevAreaTab = new QWidget(this);
QVBoxLayout* frontDevLayout = new QVBoxLayout(frontDevAreaTab);
QTabWidget* mainTabWidget = new QTabWidget(this);
QWidget* frontDevAreaTab = new QWidget(this);
QVBoxLayout* frontDevLayout = new QVBoxLayout(frontDevAreaTab);
frontDevLayout->addLayout(frontGroupBoxLayout);
frontDevLayout->addWidget(frontTabWidget, 2, Qt::AlignTop | Qt::AlignLeft);
mainTabWidget->addTab(frontDevAreaTab, "前 板");
// 后板
QGroupBox* backDeviceInfoGroupBox1 = createBackDeviceInfoGroupBox();
QGroupBox* backDeviceInfoGroupBox1 = createBackDeviceInfoGroupBox();
QGroupBox* BackConnectServerGroupBox = createBackConnectServerGroupBox();
QHBoxLayout* backGroupBoxLayout = new QHBoxLayout;
@ -433,25 +427,25 @@ void MainWidget::setupUI()
QTabWidget* backTabWidget = new QTabWidget(this);
backTabWidget->setFixedSize(900, 315);
QWidget* backFunctionTestTab = createFunctionTestTab(backBoardTest, "backBoardTest");
QWidget* backFunctionTestTab = createFunctionTestTab(backBoardTest, "backBoardTest");
QWidget* backFunctionConfigTab = createFunctionConfigTab(backBoardFuncConfig, "backBoardFuncConfig");
backTabWidget->addTab(backFunctionTestTab, "后板测试项");
backTabWidget->addTab(backFunctionTestTab, "后板测试项");
backTabWidget->addTab(backFunctionConfigTab, "后板配置项");
QWidget* backDevAreaTab = new QWidget(this);
QVBoxLayout* backDevLayout = new QVBoxLayout(backDevAreaTab);
QWidget* backDevAreaTab = new QWidget(this);
QVBoxLayout* backDevLayout = new QVBoxLayout(backDevAreaTab);
backDevLayout->addLayout(backGroupBoxLayout);
backDevLayout->addWidget(backTabWidget, 2, Qt::AlignTop | Qt::AlignLeft);
mainTabWidget->addTab(backDevAreaTab, "后 板");
// 整机
QWidget* allDevAreaTab = new QWidget(this);
QVBoxLayout* allDevLayout = new QVBoxLayout(allDevAreaTab);
QWidget* allDevAreaTab = new QWidget(this);
QVBoxLayout* allDevLayout = new QVBoxLayout(allDevAreaTab);
mainTabWidget->addTab(allDevAreaTab, "整 机");
QTabWidget* tabWidget_media = new QTabWidget(this);
QWidget* imageDisplayTab = createImageDisplayTab();
QWidget* videoDisplayTab = createVideoDisplayTab();
QWidget* imageDisplayTab = createImageDisplayTab();
QWidget* videoDisplayTab = createVideoDisplayTab();
tabWidget_media->addTab(imageDisplayTab, "图像显示区");
tabWidget_media->addTab(videoDisplayTab, "视频显示区");
@ -464,15 +458,15 @@ void MainWidget::setupUI()
mainLayout->addLayout(rightVerticalLayout, 3);
setLayout(mainLayout);
setWindowTitle("SL100 工厂产测工具 - V0.0.4");
setWindowTitle("SL100 工厂产测工具 - V0.0.5");
resize(1340, 900);
connect(startServerButton, &QPushButton::clicked, this, &MainWidget::onStartServerClicked);
connect(sendAllButton, &QPushButton::clicked, this, &MainWidget::onSendAllClicked);
connect(statusListWidget, &QListWidget::itemChanged, this, &MainWidget::scrollToBottom);
connect(selectFileButton, &QPushButton::clicked, this, &MainWidget::onSelectFileButtonClicked);
connect(clearLogButton, &QPushButton::clicked, this, &MainWidget::onclearLogButtonClicked);
connect(saveCheckBox, &QCheckBox::stateChanged, this, &MainWidget::onSaveCheckBoxStateChanged);
connect(startServerButton, &QPushButton::clicked, this, &MainWidget::onStartServerClicked);
connect(sendAllButton, &QPushButton::clicked, this, &MainWidget::onSendAllClicked);
connect(statusListWidget, &QListWidget::itemChanged, this, &MainWidget::scrollToBottom);
connect(selectFileButton, &QPushButton::clicked, this, &MainWidget::onSelectFileButtonClicked);
connect(clearLogButton, &QPushButton::clicked, this, &MainWidget::onclearLogButtonClicked);
connect(saveCheckBox, &QCheckBox::stateChanged, this, &MainWidget::onSaveCheckBoxStateChanged);
}
// 服务器响应
@ -1290,7 +1284,11 @@ void MainWidget::onOpenFocusWindowClicked()
if (itemIndex == FOCUS_WINDOWS_BUTTON) {
for (ClientHandler* handler : clients) {
if (handler->getClientId() == handler->preVideoClientId) {
emit openFocusWindowRequested(itemIndex); // 发送信号
DataHandler* dataHandler = dataHandlers[handler->getClientId()];
if (dataHandler) {
dataHandler->handleOpenFocusWindow(itemIndex);
break;
}
}
}

View File

@ -153,7 +153,7 @@ private:
void onDisconnectClient(int clientId);
void setupTimerForThreadPoolInfo();
void sendDataToClient(int clientId, const QByteArray& data);
//void FactoryToolSendPostFactoryInfotoHttpServer();
// 与服务器的接口
void FactoryToolSendPostTestToHttpServer();
void FactoryToolSendPostLicenseToHttpServer(const QString& hardware_info, const QString& license_info);
void FactoryToolSendPostComfirmToHttpServer(const QString& mac_addr, const QString& uuid);

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

File diff suppressed because one or more lines are too long

View File

@ -3,17 +3,13 @@
遇到问题:
2024.09.01
1. 运行报错QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
问题分析:主线程创建了 QTcpSocket这个QTcpSocket的读写操作必须在主线程中执行不能将QTcpSocket对象
通过新建的对象传参后到新建对象中对QTcpSocket进行读写操作所有的QTcpSocket 的操作都在与它关
联的线程中执行。
问题分析:主线程创建了 QTcpSocket这个QTcpSocket的读写操作必须在主线程中执行不能将QTcpSocket对象通过新建的对象传参后到新建对象中对QTcpSocket进行读写操作所有的QTcpSocket 的操作都在与它关联的线程中执行。
问题解决1. 在主线程中创建 QTcpSocket 并获取连接:
继续在主线程中使用 server->nextPendingConnection() 来获取客户端连接。
2. 将 QTcpSocket 移动到 ClientHandler 所属的新线程:
在将 QTcpSocket 传递给 ClientHandler 之前,将 QTcpSocket 移动到新线程中,确保它在新线程
中被使用。
这里注意不能将有父对象的QTcpSocket 移动到新线程中,因为父对象负责管理子对象的生命周期,
如果子对象被移动到其他线程,可能会导致线程安全问题或不一致的对象状态。
在将 QTcpSocket 传递给 ClientHandler 之前,将 QTcpSocket 移动到新线程中,确保它在新线程中被使用。
这里注意不能将有父对象的QTcpSocket 移动到新线程中,因为父对象负责管理子对象的生命周期,如果子对象被移动到其他线程,可能会导致线程安全问题或不一致的对象状态。
运行报错QObject::moveToThread: Cannot move objects with a parent
解决方法:如果 QTcpSocket 已经有父对象,你需要先将其父对象设置为 nullptr再移动到新的线程。