From 818aa764fb1c3abf1e25ce731e549601ef44585f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=B0=E4=BB=94?= Date: Tue, 30 Jun 2026 11:12:25 +0800 Subject: [PATCH] =?UTF-8?q?=E8=9E=BA=E6=A0=93=E8=A7=86=E9=A2=91=E6=B5=81?= =?UTF-8?q?=E5=92=8C=E6=A3=80=E6=B5=8B=EF=BC=8C=E6=8E=A5=E4=B8=8B=E6=9D=A5?= =?UTF-8?q?=E8=B0=83=E4=BC=98=E5=AD=98=E5=82=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App/App.pro | 4 + App/BeltTearing/BeltTearing.pro | 19 +- .../Presenter/Inc/DroneScrewCtrlPresenter.h | 5 +- .../Presenter/Src/DroneScrewCtrlPresenter.cpp | 175 +++++++++----- .../DroneScrewCtrlApp/config/config.xml | 2 +- .../DroneScrewCtrlApp/mainwindow.cpp | 13 +- .../DroneScrewCtrlApp/resultitem.cpp | 17 +- .../DroneScrewServerPresenter.cpp | 225 +++++++++++++----- .../DroneScrewServerPresenter.h | 8 + .../DroneScrewZmqProtocol.cpp | 2 +- App/DroneScrewbolt/result.png | Bin 0 -> 67023 bytes App/WorkpieceProject/WorkpieceProject.pro | 24 +- Module/FFMediaStream/Src/CVrFFMediaPusher.cpp | 11 +- Module/Module.pro | 60 +++-- .../WDRemoteReceiver/Src/WDRemoteReceiver.cpp | 7 - 15 files changed, 406 insertions(+), 166 deletions(-) create mode 100644 App/DroneScrewbolt/result.png diff --git a/App/App.pro b/App/App.pro index fb378196..f1c27549 100644 --- a/App/App.pro +++ b/App/App.pro @@ -45,6 +45,10 @@ isEmpty(TARGET_APP) { SUBDIRS += BinocularMark/BinocularMark.pro } else:equals(TARGET_APP, "WorkpieceProject") { SUBDIRS += WorkpieceProject/WorkpieceProject.pro + } else:equals(TARGET_APP, "WorkpiecePosition") { + SUBDIRS += WorkpieceProject/WorkpieceProject.pro + } else:equals(TARGET_APP, "WorkpieceSplice") { + SUBDIRS += WorkpieceProject/WorkpieceProject.pro } else:equals(TARGET_APP, "TunnelChannel") { SUBDIRS += TunnelChannel/TunnelChannel.pro } else:equals(TARGET_APP, "WheelMeasure") { diff --git a/App/BeltTearing/BeltTearing.pro b/App/BeltTearing/BeltTearing.pro index 18cc223e..6a35d762 100644 --- a/App/BeltTearing/BeltTearing.pro +++ b/App/BeltTearing/BeltTearing.pro @@ -1,6 +1,17 @@ TEMPLATE = subdirs -# 撕裂项目 -SUBDIRS += BeltTearingConfig/BeltTearingConfig.pro -SUBDIRS += BeltTearingApp/BeltTearingApp.pro -SUBDIRS += BeltTearingServer/BeltTearingServer.pro +BeltTearingConfig.file = BeltTearingConfig/BeltTearingConfig.pro +BeltTearingApp.file = BeltTearingApp/BeltTearingApp.pro +BeltTearingServer.file = BeltTearingServer/BeltTearingServer.pro + +SUBDIRS += BeltTearingConfig + +equals(TARGET_APP, "BeltTearing") { + SUBDIRS += BeltTearingServer + BeltTearingServer.depends = BeltTearingConfig +} else { + SUBDIRS += BeltTearingApp + SUBDIRS += BeltTearingServer + BeltTearingApp.depends = BeltTearingConfig + BeltTearingServer.depends = BeltTearingConfig +} diff --git a/App/DroneScrewbolt/DroneScrewCtrlApp/Presenter/Inc/DroneScrewCtrlPresenter.h b/App/DroneScrewbolt/DroneScrewCtrlApp/Presenter/Inc/DroneScrewCtrlPresenter.h index bd06b753..ccc14bdd 100644 --- a/App/DroneScrewbolt/DroneScrewCtrlApp/Presenter/Inc/DroneScrewCtrlPresenter.h +++ b/App/DroneScrewbolt/DroneScrewCtrlApp/Presenter/Inc/DroneScrewCtrlPresenter.h @@ -80,8 +80,8 @@ private slots: private: // RTSP 拉流回调 void onRtspFrame(const VrFFPulledFrame& frame); - bool initPuller(const QString& streamUrl); - bool startPullerOnce(const QString& streamUrl); + bool initPuller(const QString& streamUrl, bool quiet = false); + bool startPullerOnce(const QString& streamUrl, bool quiet = false); bool waitForRtspFirstFrame(int timeoutMs); bool startPullerWithRetry(const QString& streamUrl); void releasePuller(); @@ -128,6 +128,7 @@ private: std::atomic m_bConnected{false}; std::atomic m_bRunning{false}; + std::atomic m_liveStreamMode{false}; std::atomic m_rtspReceiving{false}; std::atomic m_rtspFirstFrameReported{false}; QTimer* m_pReconnectTimer{nullptr}; diff --git a/App/DroneScrewbolt/DroneScrewCtrlApp/Presenter/Src/DroneScrewCtrlPresenter.cpp b/App/DroneScrewbolt/DroneScrewCtrlApp/Presenter/Src/DroneScrewCtrlPresenter.cpp index 65dd8106..64243407 100644 --- a/App/DroneScrewbolt/DroneScrewCtrlApp/Presenter/Src/DroneScrewCtrlPresenter.cpp +++ b/App/DroneScrewbolt/DroneScrewCtrlApp/Presenter/Src/DroneScrewCtrlPresenter.cpp @@ -11,15 +11,18 @@ #include #include #include +#include namespace { constexpr int kDefaultRtspPullPort = 8554; constexpr int kLegacyRtmpPublishPort = 1935; -constexpr unsigned int kRtspOpenTimeoutMs = 5000; -constexpr int kRtspPullMaxAttempts = 5; +constexpr unsigned int kRtspOpenTimeoutMs = 10000; +constexpr int kRtspPullMaxAttempts = 20; constexpr int kRtspPullRetryDelayMs = 500; -constexpr int kRtspFirstFrameWaitMs = 5000; +constexpr int kRtspFirstFrameWaitMs = 10000; +constexpr int kControlStartTimeoutMs = 30000; +constexpr int kControlStopTimeoutMs = 30000; constexpr size_t kMaxPendingMatchFrames = 30; constexpr qint64 kMaxPendingMatchAgeMs = 15000; @@ -124,8 +127,9 @@ QSize overlayCoordinateSize(const CtrlDetectionFrame& frame, const QImage& img, { if (sourceSize.width() > 0 && sourceSize.height() > 0) return sourceSize; - return QSize(frame.imageWidth > 0 ? frame.imageWidth : img.width(), - frame.imageHeight > 0 ? frame.imageHeight : img.height()); + if (frame.imageWidth > 0 && frame.imageHeight > 0) + return QSize(frame.imageWidth, frame.imageHeight); + return img.size(); } qint64 nowMs() @@ -261,6 +265,7 @@ void DroneScrewCtrlPresenter::DeinitApp() { if (m_pReconnectTimer && m_pReconnectTimer->isActive()) m_pReconnectTimer->stop(); + m_liveStreamMode = false; releasePuller(); if (m_pRemote) @@ -307,20 +312,22 @@ QString DroneScrewCtrlPresenter::resolveStreamUrl(const QString& streamUrl) cons return normalizeStreamUrl(streamUrl, m_cfg); } -bool DroneScrewCtrlPresenter::initPuller(const QString& streamUrl) +bool DroneScrewCtrlPresenter::initPuller(const QString& streamUrl, bool quiet) { releasePuller(); const QString url = resolveStreamUrl(streamUrl); if (url.isEmpty()) { - emit statusMessage(QStringLiteral("Server 未返回拉流地址")); + if (!quiet) + emit statusMessage(QStringLiteral("Server 未返回拉流地址")); return false; } if (!IVrFFMediaPuller::CreateObject(&m_pPuller) || !m_pPuller) { - emit statusMessage(QStringLiteral("创建拉流器失败")); + if (!quiet) + emit statusMessage(QStringLiteral("创建拉流器失败")); return false; } @@ -339,7 +346,8 @@ bool DroneScrewCtrlPresenter::initPuller(const QString& streamUrl) const int ret = m_pPuller->Init(pc); if (ret != 0) { - emit statusMessage(QStringLiteral("打开拉流地址失败 ret=%1: %2").arg(ret).arg(url)); + if (!quiet) + emit statusMessage(QStringLiteral("打开拉流地址失败 ret=%1: %2").arg(ret).arg(url)); releasePuller(); return false; } @@ -347,19 +355,23 @@ bool DroneScrewCtrlPresenter::initPuller(const QString& streamUrl) return true; } -bool DroneScrewCtrlPresenter::startPullerOnce(const QString& streamUrl) +bool DroneScrewCtrlPresenter::startPullerOnce(const QString& streamUrl, bool quiet) { const QString url = resolveStreamUrl(streamUrl); if (url.isEmpty()) { - emit statusMessage(QStringLiteral("Server 未返回拉流地址,配置也无法生成地址")); + if (!quiet) + emit statusMessage(QStringLiteral("Server 未返回拉流地址,配置也无法生成地址")); return false; } m_streamUrl = url; - emit statusMessage(QStringLiteral("正在打开拉流: %1").arg(url)); - QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 20); - if (initPuller(url)) + if (!quiet) + { + emit statusMessage(QStringLiteral("正在打开拉流: %1").arg(url)); + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 20); + } + if (initPuller(url, quiet)) { m_rtspFirstFrameReported = false; m_rtspReceiving = true; @@ -367,15 +379,18 @@ bool DroneScrewCtrlPresenter::startPullerOnce(const QString& streamUrl) if (startRet == 0) { m_streamUrl = url; - emit statusMessage(QStringLiteral("拉流器已启动: %1").arg(url)); + if (!quiet) + emit statusMessage(QStringLiteral("拉流器已启动: %1").arg(url)); return true; } m_rtspReceiving = false; - emit statusMessage(QStringLiteral("启动拉流器失败 ret=%1: %2").arg(startRet).arg(url)); + if (!quiet) + emit statusMessage(QStringLiteral("启动拉流器失败 ret=%1: %2").arg(startRet).arg(url)); releasePuller(); } - emit statusMessage(QStringLiteral("启动拉流失败: %1").arg(url)); + if (!quiet) + emit statusMessage(QStringLiteral("启动拉流失败: %1").arg(url)); return false; } @@ -405,33 +420,24 @@ bool DroneScrewCtrlPresenter::startPullerWithRetry(const QString& streamUrl) } m_streamUrl = url; + emit statusMessage(QStringLiteral("正在拉流: %1").arg(url)); + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 20); for (int attempt = 1; attempt <= kRtspPullMaxAttempts; ++attempt) { - emit statusMessage(QStringLiteral("正在拉流(%1/%2): %3") - .arg(attempt) - .arg(kRtspPullMaxAttempts) - .arg(url)); - QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 20); + const bool started = startPullerOnce(url, true); - if (startPullerOnce(url) && waitForRtspFirstFrame(kRtspFirstFrameWaitMs)) + if (started && waitForRtspFirstFrame(kRtspFirstFrameWaitMs)) return true; - emit statusMessage(QStringLiteral("本地 RTSP 未收到首帧(%1/%2): %3") - .arg(attempt) - .arg(kRtspPullMaxAttempts) - .arg(url)); releasePuller(); if (attempt < kRtspPullMaxAttempts) { - emit statusMessage(QStringLiteral("本地 RTSP 拉流失败,准备重试(%1/%2): %3") - .arg(attempt + 1) - .arg(kRtspPullMaxAttempts) - .arg(url)); processEventsForMs(kRtspPullRetryDelayMs); } } + emit statusMessage(QStringLiteral("本地 RTSP 拉流未收到首帧,Server 推流保持运行: %1").arg(url)); return false; } @@ -445,12 +451,15 @@ bool DroneScrewCtrlPresenter::StartLiveStream() if (!m_pRemote) return false; releasePuller(); clearCachedDisplayState(); - m_pRemote->StopWork(WDRemoteWorkMode::Detection); + m_liveStreamMode = false; emit statusMessage(QStringLiteral("正在请求服务端启动距离检测...")); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 20); - bool ok = m_pRemote->StartWork(WDRemoteWorkMode::LiveStream) == 0; + const int startRet = m_pRemote->StartWork(WDRemoteWorkMode::LiveStream, + kControlStartTimeoutMs); + bool ok = (startRet == 0); if (ok) { + m_liveStreamMode = true; emit statusMessage(QStringLiteral("服务端已启动,正在获取拉流地址...")); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 20); WDRemoteServerInfo info = m_pRemote->GetServerInfo(); @@ -463,60 +472,85 @@ bool DroneScrewCtrlPresenter::StartLiveStream() const bool pullOk = startPullerWithRetry(m_streamUrl); if (!pullOk) { - emit statusMessage(QStringLiteral("服务端已开流,但本地 RTSP 多次拉流未启动: %1").arg(m_streamUrl)); + releasePuller(); + emit statusMessage(QStringLiteral("Server 已开流,本地暂未拉到图像: %1").arg(m_streamUrl)); return true; } emit statusMessage(QStringLiteral("Server 已开流,RTSP 已出图: %1").arg(m_streamUrl)); } + if (!ok) + { + emit statusMessage(QStringLiteral("Server 启动距离检测失败 ret=%1").arg(startRet)); + m_liveStreamMode = false; + } return ok; } bool DroneScrewCtrlPresenter::StopLiveStream() { if (!m_pRemote) return false; + m_liveStreamMode = false; // 停止 RTSP 拉流 releasePuller(); - bool ok = m_pRemote->StopWork(WDRemoteWorkMode::LiveStream) == 0; + const int stopRet = m_pRemote->StopWork(WDRemoteWorkMode::LiveStream, + kControlStopTimeoutMs); + bool ok = (stopRet == 0); if (ok) emit statusMessage(QStringLiteral("Server 已关流")); + else emit statusMessage(QStringLiteral("Server 关流失败 ret=%1").arg(stopRet)); return ok; } bool DroneScrewCtrlPresenter::StartDetection() { if (!m_pRemote) return false; + m_liveStreamMode = false; // 检测模式下停止 RTSP 拉流(改用 ZMQ 原始图像通道) releasePuller(); clearCachedDisplayState(); - m_pRemote->StopWork(WDRemoteWorkMode::LiveStream); // 使用单目传输模式:仅接收左目图像,减少网络带宽消耗 m_pRemote->SetDetectMode("mono"); - bool ok = m_pRemote->StartWork(WDRemoteWorkMode::Detection) == 0; + const int startRet = m_pRemote->StartWork(WDRemoteWorkMode::Detection, + kControlStartTimeoutMs); + bool ok = (startRet == 0); if (ok) emit statusMessage(QStringLiteral("Server 已开始检测(单目传输)")); + else emit statusMessage(QStringLiteral("Server 启动精准检测失败 ret=%1").arg(startRet)); return ok; } bool DroneScrewCtrlPresenter::StopDetection() { if (!m_pRemote) return false; - bool ok = m_pRemote->StopWork(WDRemoteWorkMode::Detection) == 0; + const int stopRet = m_pRemote->StopWork(WDRemoteWorkMode::Detection, + kControlStopTimeoutMs); + bool ok = (stopRet == 0); if (ok) emit statusMessage(QStringLiteral("Server 已停止检测")); + else emit statusMessage(QStringLiteral("Server 停止检测失败 ret=%1").arg(stopRet)); return ok; } bool DroneScrewCtrlPresenter::StopAll() { if (!m_pRemote) return false; + m_liveStreamMode = false; // 停止 RTSP 拉流 releasePuller(); clearCachedDisplayState(); // 两种模式都停掉,保证服务端回到空闲(幂等) - m_pRemote->StopWork(WDRemoteWorkMode::Detection, 1000); - m_pRemote->StopWork(WDRemoteWorkMode::LiveStream, 1000); - emit statusMessage(QStringLiteral("Server 已停止(实时/检测)")); - return true; + const int streamRet = m_pRemote->StopWork(WDRemoteWorkMode::LiveStream, + kControlStopTimeoutMs); + const int detectRet = m_pRemote->StopWork(WDRemoteWorkMode::Detection, + kControlStopTimeoutMs); + const bool ok = (streamRet == 0 && detectRet == 0); + if (ok) + emit statusMessage(QStringLiteral("Server 已停止(实时/检测)")); + else + emit statusMessage(QStringLiteral("Server 停止失败 streamRet=%1 detectRet=%2") + .arg(streamRet) + .arg(detectRet)); + return ok; } bool DroneScrewCtrlPresenter::SetServerExposure(double exposureTime) @@ -548,7 +582,7 @@ bool DroneScrewCtrlPresenter::PushAlgoParams(const DroneScrewAlgoUiParams& param void DroneScrewCtrlPresenter::onRemoteDetection(const WDRemoteDetectionFrame& f) { CtrlDetectionFrame frame = toCtrlFrame(f); - if (m_rtspReceiving.load()) + if (m_liveStreamMode.load() || m_rtspReceiving.load()) { { QMutexLocker lk(&m_lastResultMutex); @@ -579,7 +613,15 @@ void DroneScrewCtrlPresenter::onRemoteDetection(const WDRemoteDetectionFrame& f) } if (!hasMatchedImage) + { + { + QMutexLocker lk(&m_lastResultMutex); + m_lastResult = frame; + m_bHasResult = true; + } + emit detectionResultReady(frame); return; + } { QMutexLocker lk(&m_lastResultMutex); @@ -595,7 +637,7 @@ void DroneScrewCtrlPresenter::onRemoteRawImage(const WDRemoteBinocularRawImage& // 检测模式下板一通过 ZMQ 原始图通道发来左目图像(此时 RTSP 已关闭)。 // 转为 QImage,走与 RTSP 相同的叠加+显示通路(onOverlayFrameReady 会叠加检测框)。 if (!img.leftData || img.leftWidth <= 0 || img.leftHeight <= 0) return; - if (m_rtspReceiving.load()) return; + if (m_liveStreamMode.load() || m_rtspReceiving.load()) return; QImage q; switch (img.leftPixelFormat) @@ -649,7 +691,9 @@ void DroneScrewCtrlPresenter::onRemoteRawImage(const WDRemoteBinocularRawImage& } if (!hasMatchedResult) + { return; + } { QMutexLocker lk(&m_lastResultMutex); @@ -757,9 +801,21 @@ void DroneScrewCtrlPresenter::onRtspFrame(const VrFFPulledFrame& frame) return; } + CtrlDetectionFrame latestResult; + bool hasLatestResult = false; + if (m_liveStreamMode.load()) + { + QMutexLocker lk(&m_lastResultMutex); + if (m_bHasResult) + { + latestResult = m_lastResult; + hasLatestResult = true; + } + } + if (!m_rtspFirstFrameReported.exchange(true)) emit statusMessage(QStringLiteral("RTSP 已出图")); - emit displayFrameReady(img, img.size(), -1, true, CtrlDetectionFrame{}, false); + emit displayFrameReady(img, img.size(), -1, true, latestResult, hasLatestResult); } void DroneScrewCtrlPresenter::onOverlayFrameReady(const QImage& img, @@ -770,9 +826,16 @@ void DroneScrewCtrlPresenter::onOverlayFrameReady(const QImage& img, bool hasResult) { QImage frame = img; - if (hasResult && !rtspFrame) + if (hasResult && !m_liveStreamMode.load()) { - overlayDetectionBoxes(frame, result, sourceSize); + QSize overlaySourceSize = sourceSize; + if (rtspFrame) + { + overlaySourceSize = (result.imageWidth > 0 && result.imageHeight > 0) + ? QSize(result.imageWidth, result.imageHeight) + : img.size(); + } + overlayDetectionBoxes(frame, result, overlaySourceSize); } { @@ -839,9 +902,10 @@ void DroneScrewCtrlPresenter::overlayDetectionBoxes(QImage& img, if (!m_cfg.display.drawBoxes || frame.boxes.empty()) return; QPainter p(&img); - p.setRenderHint(QPainter::Antialiasing, true); - const int thickness = m_cfg.display.boxThickness > 0 ? m_cfg.display.boxThickness : 1; + p.setRenderHint(QPainter::Antialiasing, false); + const int thickness = std::max(2, m_cfg.display.boxThickness); QPen pen(QColor(0, 0, 0), thickness); + pen.setJoinStyle(Qt::MiterJoin); p.setPen(pen); QFont f = p.font(); f.setPointSize(28); @@ -864,10 +928,15 @@ void DroneScrewCtrlPresenter::overlayDetectionBoxes(QImage& img, for (size_t i = 0; i < frame.boxes.size(); ++i) { const auto& b = frame.boxes[i]; - QRect r(qRound(static_cast(b.x) * scaleX), - qRound(static_cast(b.y) * scaleY), - qRound(static_cast(b.width) * scaleX), - qRound(static_cast(b.height) * scaleY)); + if (b.width <= 0 || b.height <= 0) + continue; + const int x1 = qRound(static_cast(b.x) * scaleX); + const int y1 = qRound(static_cast(b.y) * scaleY); + int x2 = qRound(static_cast(b.x + b.width) * scaleX) - 1; + int y2 = qRound(static_cast(b.y + b.height) * scaleY) - 1; + if (x2 < x1) x2 = x1; + if (y2 < y1) y2 = y1; + QRect r(QPoint(x1, y1), QPoint(x2, y2)); r = r.normalized().intersected(imageRect); if (r.isEmpty()) continue; diff --git a/App/DroneScrewbolt/DroneScrewCtrlApp/config/config.xml b/App/DroneScrewbolt/DroneScrewCtrlApp/config/config.xml index e5eb90bb..f17d7cf9 100644 --- a/App/DroneScrewbolt/DroneScrewCtrlApp/config/config.xml +++ b/App/DroneScrewbolt/DroneScrewCtrlApp/config/config.xml @@ -25,7 +25,7 @@ diff --git a/App/DroneScrewbolt/DroneScrewCtrlApp/mainwindow.cpp b/App/DroneScrewbolt/DroneScrewCtrlApp/mainwindow.cpp index bd57bcd7..77de336b 100644 --- a/App/DroneScrewbolt/DroneScrewCtrlApp/mainwindow.cpp +++ b/App/DroneScrewbolt/DroneScrewCtrlApp/mainwindow.cpp @@ -556,9 +556,16 @@ void MainWindow::on_btn_start_clicked() ui->btn_start->setEnabled(false); ui->btn_detect_start->setEnabled(false); QApplication::processEvents(); - if (m_pPresenter) m_pPresenter->StopAll(); - appendLog(QStringLiteral("已请求 Server 停止")); - applyUiState(UiState::Idle); + const bool ok = m_pPresenter && m_pPresenter->StopAll(); + if (ok) + { + appendLog(QStringLiteral("已请求 Server 停止")); + applyUiState(UiState::Idle); + } + else + { + appendLog(QStringLiteral("Server 停止失败,状态未切换")); + } ui->btn_start->setEnabled(true); ui->btn_detect_start->setEnabled(true); } diff --git a/App/DroneScrewbolt/DroneScrewCtrlApp/resultitem.cpp b/App/DroneScrewbolt/DroneScrewCtrlApp/resultitem.cpp index bb9b2bef..687f73cf 100644 --- a/App/DroneScrewbolt/DroneScrewCtrlApp/resultitem.cpp +++ b/App/DroneScrewbolt/DroneScrewCtrlApp/resultitem.cpp @@ -51,6 +51,17 @@ QString distanceText(const CtrlDetectionFrame& frame, int targetIndex) ? QStringLiteral("未测到距离") : distanceParts.join(" "); } + +QString distanceIndexText(const CtrlDetectionFrame& frame, int targetIndex) +{ + const int total = static_cast(frame.distances.size()); + if (targetIndex >= 0 && targetIndex < total) + { + const CtrlDetectionDistance& d = frame.distances[static_cast(targetIndex)]; + return QString::number(d.toId); + } + return QStringLiteral("-"); +} } ResultItem::ResultItem(QWidget* parent) @@ -112,11 +123,7 @@ void ResultItem::setResultData(int targetIndex, .arg(static_cast(frame.boxes.size()))); else if (mode == DisplayMode::Distance) { - const int total = static_cast(frame.distances.size()); - if (targetIndex >= 0 && targetIndex < total) - ui->label_id->setText(QString("%1/%2").arg(targetIndex + 1).arg(total)); - else - ui->label_id->setText(QString("%1/%1").arg(total)); + ui->label_id->setText(distanceIndexText(frame, targetIndex)); } else ui->label_id->setText(QStringLiteral("0/0")); diff --git a/App/DroneScrewbolt/DroneScrewServer/DroneScrewServerPresenter.cpp b/App/DroneScrewbolt/DroneScrewServer/DroneScrewServerPresenter.cpp index 2d5be50b..e264f07a 100644 --- a/App/DroneScrewbolt/DroneScrewServer/DroneScrewServerPresenter.cpp +++ b/App/DroneScrewbolt/DroneScrewServer/DroneScrewServerPresenter.cpp @@ -1323,6 +1323,7 @@ int DroneScrewServerPresenter::initRtspPusher(unsigned int width, unsigned int h void DroneScrewServerPresenter::releaseRtspPusher() { + std::lock_guard pusherLock(m_rtspPusherMutex); m_bRtspStarted = false; if (m_pPusher) { @@ -1343,6 +1344,67 @@ void DroneScrewServerPresenter::releaseRtspPusher() m_rtspUvInitialized = false; m_rtspRgaScaleDisabled = false; m_rtspRgaScaleLogged = false; + resetRtspPushState(); +} + +int DroneScrewServerPresenter::startRtspPusher() +{ + std::lock_guard pusherLock(m_rtspPusherMutex); + if (!m_pPusher) + return ERR_CODE(DRONESCREW_ERR_RTSP_INIT); + return m_pPusher->Start(); +} + +void DroneScrewServerPresenter::resetRtspPushState() +{ + { + std::lock_guard lk(m_rtspPushMutex); + m_rtspPushedFrameCounter = 0; + m_rtspLastPushRet = 0; + m_rtspLastPushFrameId = 0; + } +} + +void DroneScrewServerPresenter::recordRtspPushResult(int ret, unsigned long long frameId) +{ + bool logFirstSuccess = false; + bool logFirstFailure = false; + int64_t pushedCount = 0; + const bool pendingEncoderOutput = (ret == ERR_CODE(DEV_RESULT_EMPTY)); + { + std::lock_guard lk(m_rtspPushMutex); + pushedCount = m_rtspPushedFrameCounter.load(); + const int previousRet = m_rtspLastPushRet.load(); + m_rtspLastPushFrameId = frameId; + if (ret == 0) + { + m_rtspLastPushRet = 0; + ++m_rtspPushedFrameCounter; + logFirstSuccess = (pushedCount == 0); + } + else if (!pendingEncoderOutput) + { + m_rtspLastPushRet = ret; + logFirstFailure = (!pendingEncoderOutput && + pushedCount == 0 && + previousRet != ret); + } + else if (previousRet == 0) + { + m_rtspLastPushRet = ret; + } + } + + if (logFirstSuccess) + { + LOG_INFO("[RTMP] first frame pushed frame=%llu pull=%s\n", + frameId, m_rtspAdvertiseUrl.toStdString().c_str()); + } + else if (logFirstFailure) + { + LOG_WARN("[RTMP] push failed before first frame ret=%d frame=%llu\n", + ret, frameId); + } } void DroneScrewServerPresenter::leftCameraCallback(const MvsImageData& img) @@ -1406,12 +1468,12 @@ void DroneScrewServerPresenter::leftCameraCallback(const MvsImageData& img) // 直播相机配置为左目时,收到左目立即推流(不等右目)。 if (isLiveStreamCameraRole("left") && - m_pPusher && m_bRtspStarted.load() && img.pData) + m_bRtspStarted.load() && img.pData) { const int rtspRet = pushRtspFrame(img); if (rtspRet == 0) ++rtspPushCnt; - else + else if (rtspRet != ERR_CODE(DEV_RESULT_EMPTY)) ++rtspFailCnt; } @@ -1443,47 +1505,49 @@ void DroneScrewServerPresenter::rightCameraCallback(const MvsImageData& img) { try { - QMutexLocker lk(&m_frameMutex); - - // 检查是否需要重新分配内存 - if (m_rightImageData.dataSize != img.dataSize) { - if (m_rightImageData.pData != nullptr) - { - delete[] m_rightImageData.pData; - m_rightImageData.pData = nullptr; - LOG_DEBUG("[CAM] right realloc: old=%zu new=%zu frame=%llu\n", - m_rightImageData.dataSize, img.dataSize, img.frameID); - } + QMutexLocker lk(&m_frameMutex); - if (img.dataSize > 0) + // 检查是否需要重新分配内存 + if (m_rightImageData.dataSize != img.dataSize) { - m_rightImageData.pData = new (std::nothrow) unsigned char[img.dataSize]; - if (!m_rightImageData.pData) + if (m_rightImageData.pData != nullptr) { - m_rightImageData.dataSize = 0; - m_bRightImageReady = false; - LOG_ERROR("[CAM] right alloc failed size=%zu frame=%llu\n", - img.dataSize, img.frameID); - return; + delete[] m_rightImageData.pData; + m_rightImageData.pData = nullptr; + LOG_DEBUG("[CAM] right realloc: old=%zu new=%zu frame=%llu\n", + m_rightImageData.dataSize, img.dataSize, img.frameID); + } + + if (img.dataSize > 0) + { + m_rightImageData.pData = new (std::nothrow) unsigned char[img.dataSize]; + if (!m_rightImageData.pData) + { + m_rightImageData.dataSize = 0; + m_bRightImageReady = false; + LOG_ERROR("[CAM] right alloc failed size=%zu frame=%llu\n", + img.dataSize, img.frameID); + return; + } } } + + // 复制图像数据 + m_rightImageData.width = img.width; + m_rightImageData.height = img.height; + m_rightImageData.dataSize = img.dataSize; + m_rightImageData.pixelFormat = img.pixelFormat; + m_rightImageData.frameID = img.frameID; + m_rightImageData.timestamp = img.timestamp; + + if (m_rightImageData.pData != nullptr && img.pData != nullptr) + { + memcpy(m_rightImageData.pData, img.pData, img.dataSize); + } + + m_bRightImageReady = true; } - - // 复制图像数据 - m_rightImageData.width = img.width; - m_rightImageData.height = img.height; - m_rightImageData.dataSize = img.dataSize; - m_rightImageData.pixelFormat = img.pixelFormat; - m_rightImageData.frameID = img.frameID; - m_rightImageData.timestamp = img.timestamp; - - if (m_rightImageData.pData != nullptr && img.pData != nullptr) - { - memcpy(m_rightImageData.pData, img.pData, img.dataSize); - } - - m_bRightImageReady = true; static bool rightFirst = true; if (rightFirst) { std::ostringstream oss; @@ -1496,12 +1560,12 @@ void DroneScrewServerPresenter::rightCameraCallback(const MvsImageData& img) static int rtspFailCnt = 0; if (isLiveStreamCameraRole("right") && - m_pPusher && m_bRtspStarted.load() && img.pData) + m_bRtspStarted.load() && img.pData) { const int rtspRet = pushRtspFrame(img); if (rtspRet == 0) ++rtspPushCnt; - else + else if (rtspRet != ERR_CODE(DEV_RESULT_EMPTY)) ++rtspFailCnt; } @@ -1711,10 +1775,10 @@ int DroneScrewServerPresenter::startDetectionWork() } // 停止 RTSP 推流(检测和推流互斥) - if (m_pPusher && m_bRtspStarted.load()) + if (m_pPusher) { LOG_DEBUG("[DETECT] stopping RTSP pusher (detection mode)\n"); - m_pPusher->Stop(); + releaseRtspPusher(); } m_bRtspStarted = false; resetFrameReadyFlags(); @@ -1746,12 +1810,12 @@ int DroneScrewServerPresenter::stopDetectionWork() return 0; } - m_bIsDetecting = false; stopGpioTriggerLoop(); m_bThreadExit = true; if (m_detectThread.joinable()) m_detectThread.join(); stopImageSaveThread(); + m_bIsDetecting = false; m_detectPipelineMode = kDetectPipelinePrecision; m_activeTriggerFps = static_cast(kPrecisionFrameRate); @@ -1768,6 +1832,8 @@ int DroneScrewServerPresenter::startLiveStream() return 0; } + releaseRtspPusher(); + if (m_bIsDetecting.load()) { LOG_WARN("[LIVE] start rejected: detection is running\n"); @@ -1802,6 +1868,9 @@ int DroneScrewServerPresenter::startLiveStream() if (acqRet != 0) { emit statusChanged(QStringLiteral("实时传图相机采集启动失败")); + releaseRtspPusher(); + m_detectPipelineMode = kDetectPipelinePrecision; + m_activeTriggerFps = static_cast(kPrecisionFrameRate); return acqRet; } @@ -1815,20 +1884,25 @@ int DroneScrewServerPresenter::startLiveStream() LOG_ERROR("[LIVE] RTSP pusher init failed ret=%d\n", ret); stopLiveCameras(); releaseRtspPusher(); + m_detectPipelineMode = kDetectPipelinePrecision; + m_activeTriggerFps = static_cast(kPrecisionFrameRate); return ERR_CODE(DRONESCREW_ERR_RTSP_INIT); } - ret = m_pPusher->Start(); + ret = startRtspPusher(); if (ret != 0) { LOG_ERROR("[LIVE] RTSP pusher start failed ret=%d\n", ret); m_bRtspStarted = false; stopLiveCameras(); releaseRtspPusher(); + m_detectPipelineMode = kDetectPipelinePrecision; + m_activeTriggerFps = static_cast(kPrecisionFrameRate); return ERR_CODE(DRONESCREW_ERR_RTSP_START); } m_rtspFrameCounter = 0; // 重置帧计数器 + resetRtspPushState(); m_bRtspStarted = true; m_bLiveStreaming = true; m_bThreadExit = false; @@ -1851,7 +1925,6 @@ int DroneScrewServerPresenter::stopLiveStream() releaseRtspPusher(); else m_bRtspStarted = false; - stopImageSaveThread(); m_rtspFrameCounter = 0; // 重置计数器,防止下次开流时 DTS 残留 return 0; } @@ -2255,11 +2328,6 @@ QString DroneScrewServerPresenter::imageSaveModeName() const if (m_detectPipelineMode.load() == kDetectPipelineDistance) return QStringLiteral("distance"); - const std::string mode = detectMode(); - if (mode == "mono") - return QStringLiteral("precision_mono"); - if (mode == "binocular") - return QStringLiteral("precision_binocular"); return QStringLiteral("precision"); } @@ -2267,9 +2335,13 @@ void DroneScrewServerPresenter::startImageSaveThread(const QString& modeName) { stopImageSaveThread(); - const QString dateDir = QDateTime::currentDateTime().toString(QStringLiteral("yyyyMMdd")); + const QDateTime now = QDateTime::currentDateTime(); + const QString dateDir = now.toString(QStringLiteral("yyyyMMdd")); + const QString sessionName = QStringLiteral("%1_%2") + .arg(modeName, + now.toString(QStringLiteral("HHmmss_zzz"))); const QString sessionDir = - QDir(QDir(QString::fromLatin1(kImageSaveRootDir)).filePath(dateDir)).filePath(modeName); + QDir(QDir(QString::fromLatin1(kImageSaveRootDir)).filePath(dateDir)).filePath(sessionName); if (!QDir().mkpath(sessionDir)) { LOG_WARN("[SAVE] create image save dir failed: %s\n", @@ -2294,9 +2366,9 @@ void DroneScrewServerPresenter::stopImageSaveThread() const bool shouldJoin = m_imageSaveThread.joinable(); { std::lock_guard lk(m_imageSaveMutex); + m_imageSaveQueue.clear(); if (!shouldJoin) { - m_imageSaveQueue.clear(); m_imageSaveSessionDir.clear(); m_imageSaveThreadExit = false; return; @@ -2397,12 +2469,11 @@ void DroneScrewServerPresenter::imageSaveThreadFunc() return m_imageSaveThreadExit.load() || !m_imageSaveQueue.empty(); }); + if (m_imageSaveThreadExit.load()) + break; + if (m_imageSaveQueue.empty()) - { - if (m_imageSaveThreadExit.load()) - break; continue; - } job = std::move(m_imageSaveQueue.front()); m_imageSaveQueue.pop_front(); @@ -2422,7 +2493,7 @@ void DroneScrewServerPresenter::imageSaveThreadFunc() static_cast(img.width), QImage::Format_Grayscale8); const QString filePath = QDir(job.dirPath).filePath( - QStringLiteral("%1Image_%2.png").arg(prefix).arg(job.index)); + QStringLiteral("%1_%2Image.png").arg(job.index).arg(prefix)); if (!q.save(filePath, "PNG")) { LOG_WARN("[SAVE] image save failed: %s\n", @@ -3142,20 +3213,34 @@ int DroneScrewServerPresenter::pushRtspFrame(const MvsImageData& img) { try { + std::lock_guard pusherLock(m_rtspPusherMutex); if (!m_pPusher) return ERR_CODE(DRONESCREW_ERR_RTSP_INIT); if (!m_bLiveStreaming.load() || !m_bRtspStarted.load()) return 0; if (!img.pData || img.dataSize == 0 || img.width == 0 || img.height == 0) - return ERR_CODE(DATA_ERR_INVALID); + { + const int ret = ERR_CODE(DATA_ERR_INVALID); + recordRtspPushResult(ret, img.frameID); + return ret; + } const unsigned int outWidth = m_rtspWidth > 0 ? m_rtspWidth : img.width; const unsigned int outHeight = m_rtspHeight > 0 ? m_rtspHeight : img.height; if (outWidth == 0 || outHeight == 0) - return ERR_CODE(DATA_ERR_INVALID); + { + const int ret = ERR_CODE(DATA_ERR_INVALID); + recordRtspPushResult(ret, img.frameID); + return ret; + } if (img.pixelFormat == kMvsPixelTypeMono8) { const size_t monoSize = static_cast(img.width) * static_cast(img.height); - if (img.dataSize < monoSize) return ERR_CODE(DATA_ERR_LEN); + if (img.dataSize < monoSize) + { + const int ret = ERR_CODE(DATA_ERR_LEN); + recordRtspPushResult(ret, img.frameID); + return ret; + } const size_t ySize = static_cast(outWidth) * static_cast(outHeight); @@ -3236,27 +3321,39 @@ int DroneScrewServerPresenter::pushRtspFrame(const MvsImageData& img) const int64_t ptsUs = static_cast( (static_cast(frameIdx) / streamFps) * 1000000.0 ); - return m_pPusher->PushFrame(m_rtspFrameBuffer.data(), m_rtspFrameBuffer.size(), ptsUs); + const int ret = m_pPusher->PushFrame(m_rtspFrameBuffer.data(), + m_rtspFrameBuffer.size(), + ptsUs); + recordRtspPushResult(ret, img.frameID); + return ret; } - return ERR_CODE(DATA_ERR_INVALID); + const int ret = ERR_CODE(DATA_ERR_INVALID); + recordRtspPushResult(ret, img.frameID); + return ret; } catch (const std::bad_alloc& e) { LOG_ERROR("[RTSP] push memory allocation failed frame=%llu src=%ux%u: %s\n", img.frameID, img.width, img.height, e.what()); - return ERR_CODE(DATA_ERR_MEM); + const int ret = ERR_CODE(DATA_ERR_MEM); + recordRtspPushResult(ret, img.frameID); + return ret; } catch (const std::exception& e) { LOG_ERROR("[RTSP] push exception frame=%llu src=%ux%u: %s\n", img.frameID, img.width, img.height, e.what()); - return ERR_CODE(DATA_ERR_INVALID); + const int ret = ERR_CODE(DATA_ERR_INVALID); + recordRtspPushResult(ret, img.frameID); + return ret; } catch (...) { LOG_ERROR("[RTSP] push unknown exception frame=%llu src=%ux%u\n", img.frameID, img.width, img.height); - return ERR_CODE(DATA_ERR_INVALID); + const int ret = ERR_CODE(DATA_ERR_INVALID); + recordRtspPushResult(ret, img.frameID); + return ret; } } diff --git a/App/DroneScrewbolt/DroneScrewServer/DroneScrewServerPresenter.h b/App/DroneScrewbolt/DroneScrewServer/DroneScrewServerPresenter.h index 90decb2f..0cb507fe 100644 --- a/App/DroneScrewbolt/DroneScrewServer/DroneScrewServerPresenter.h +++ b/App/DroneScrewbolt/DroneScrewServer/DroneScrewServerPresenter.h @@ -210,8 +210,11 @@ private: int prepareLiveStreamAcquisition(); void resetFrameReadyFlags(); int initRtspPusher(unsigned int width, unsigned int height, unsigned int fps); + int startRtspPusher(); void releaseRtspPusher(); int pushRtspFrame(const MvsImageData& img); + void resetRtspPushState(); + void recordRtspPushResult(int ret, unsigned long long frameId); bool tryRgaScaleMonoToY(const MvsImageData& img, unsigned char* dstY, unsigned int outWidth, @@ -308,6 +311,11 @@ private: std::atomic m_detectPipelineMode{0}; // 0=precision, 1=distance std::atomic m_activeTriggerFps{2}; std::atomic m_rtspFrameCounter{0}; // 相对帧计数(推流启动时重置) + std::mutex m_rtspPusherMutex; + std::mutex m_rtspPushMutex; + std::atomic m_rtspPushedFrameCounter{0}; + std::atomic m_rtspLastPushRet{0}; + std::atomic m_rtspLastPushFrameId{0}; std::vector m_rtspFrameBuffer; std::vector m_rtspScaleX; std::vector m_rtspScaleY; diff --git a/App/DroneScrewbolt/DroneScrewServer/DroneScrewZmqProtocol.cpp b/App/DroneScrewbolt/DroneScrewServer/DroneScrewZmqProtocol.cpp index b4f317fa..fb4cb906 100644 --- a/App/DroneScrewbolt/DroneScrewServer/DroneScrewZmqProtocol.cpp +++ b/App/DroneScrewbolt/DroneScrewServer/DroneScrewZmqProtocol.cpp @@ -25,7 +25,7 @@ namespace { constexpr int kRawImageZlibLevel = 9; -constexpr bool kUseFixedPrecisionMonoRawMjpeg = false; +constexpr bool kUseFixedPrecisionMonoRawMjpeg = true; constexpr int kFixedPrecisionMonoRawMjpegQuality = 25; constexpr int kFixedPrecisionMonoRawScaleDiv = 4; diff --git a/App/DroneScrewbolt/result.png b/App/DroneScrewbolt/result.png new file mode 100644 index 0000000000000000000000000000000000000000..ad904261a72b7a7cec8f70733f53cfa50683cbb0 GIT binary patch literal 67023 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 LNkvXXu0mjf08?*m literal 0 HcmV?d00001 diff --git a/App/WorkpieceProject/WorkpieceProject.pro b/App/WorkpieceProject/WorkpieceProject.pro index 8d4334e0..06982dc7 100644 --- a/App/WorkpieceProject/WorkpieceProject.pro +++ b/App/WorkpieceProject/WorkpieceProject.pro @@ -1,9 +1,21 @@ # WorkpieceProject.pro -# 工件项目总工程文件 - TEMPLATE = subdirs -SUBDIRS += \ - WorkpieceProjectConfig/WorkpieceProjectConfig.pro \ - WorkpiecePositionApp/WorkpiecePositionApp.pro \ - WorkpieceSpliceApp/WorkpieceSpliceApp.pro +WorkpieceProjectConfig.file = WorkpieceProjectConfig/WorkpieceProjectConfig.pro +WorkpiecePositionApp.file = WorkpiecePositionApp/WorkpiecePositionApp.pro +WorkpieceSpliceApp.file = WorkpieceSpliceApp/WorkpieceSpliceApp.pro + +SUBDIRS += WorkpieceProjectConfig + +equals(TARGET_APP, "WorkpiecePosition") { + SUBDIRS += WorkpiecePositionApp + WorkpiecePositionApp.depends = WorkpieceProjectConfig +} else:equals(TARGET_APP, "WorkpieceSplice") { + SUBDIRS += WorkpieceSpliceApp + WorkpieceSpliceApp.depends = WorkpieceProjectConfig +} else { + SUBDIRS += WorkpiecePositionApp + SUBDIRS += WorkpieceSpliceApp + WorkpiecePositionApp.depends = WorkpieceProjectConfig + WorkpieceSpliceApp.depends = WorkpieceProjectConfig +} diff --git a/Module/FFMediaStream/Src/CVrFFMediaPusher.cpp b/Module/FFMediaStream/Src/CVrFFMediaPusher.cpp index d295ec4e..2ff4d776 100644 --- a/Module/FFMediaStream/Src/CVrFFMediaPusher.cpp +++ b/Module/FFMediaStream/Src/CVrFFMediaPusher.cpp @@ -585,7 +585,7 @@ int CVrFFMediaPusher::PushFrame(const void* data, size_t size, int64_t ptsUs) mpp_packet_deinit(&packet); mpp_buffer_put(pktBuf); LOG_DEBUG("FFMediaPusher: encode produced no packet (warmup)\n"); - return SUCCESS; // 跳过,不算失败 + return ERR_CODE(DEV_RESULT_EMPTY); } MppBuffer encodedBuf = mpp_packet_get_buffer(encodedPacket); @@ -618,7 +618,7 @@ int CVrFFMediaPusher::PushFrame(const void* data, size_t size, int64_t ptsUs) { releaseEncodedPacket(); LOG_DEBUG("FFMediaPusher: encode produced empty packet data\n"); - return SUCCESS; // 跳过,不算失败 + return ERR_CODE(DEV_RESULT_EMPTY); } // 4) 写入 RTSP(独立 AVPacket,pts rescale 到 muxer 时基) @@ -678,8 +678,7 @@ int CVrFFMediaPusher::PushFrame(const void* data, size_t size, int64_t ptsUs) // 微秒 → muxer 实际时基 av_packet_rescale_ts(avpkt, AVRational{1, 1000000}, m_fmtCtx->streams[0]->time_base); - // Single video stream: write directly to avoid interleave buffering latency. - aret = av_write_frame(m_fmtCtx, avpkt); + aret = av_interleaved_write_frame(m_fmtCtx, avpkt); // RTMP 推流需要强制刷新缓冲区,否则数据会积压不发送 if (aret >= 0 && m_fmtCtx->pb) @@ -689,7 +688,7 @@ int CVrFFMediaPusher::PushFrame(const void* data, size_t size, int64_t ptsUs) } else { - aret = 0; // 已停止,丢弃本帧 + aret = AVERROR_EOF; // 已停止,丢弃本帧 } } @@ -697,7 +696,7 @@ int CVrFFMediaPusher::PushFrame(const void* data, size_t size, int64_t ptsUs) if (aret < 0) { - LOG_DEBUG("FFMediaPusher: av_write_frame fail %d\n", aret); + LOG_DEBUG("FFMediaPusher: av_interleaved_write_frame fail %d\n", aret); return ERR_CODE(DEV_CTRL_ERR); } diff --git a/Module/Module.pro b/Module/Module.pro index 57a1d69f..798ace13 100644 --- a/Module/Module.pro +++ b/Module/Module.pro @@ -3,27 +3,59 @@ TEMPLATE = subdirs -SUBDIRS += \ - ShareMem \ - BinocularMarkReceiver \ - ModbusTCPServer \ - ModbusTCPClient \ - AuthModule \ - HandEyeCalib \ - ChessboardDetector \ - FFMediaStream \ - ZeroMQClient \ - ZeroMQServer \ - ZeroMQPubSub \ - WDRemoteReceiver +isEmpty(TARGET_APP) { + SUBDIRS += \ + ShareMem \ + BinocularMarkReceiver \ + ModbusTCPServer \ + ModbusTCPClient \ + AuthModule \ + HandEyeCalib \ + ChessboardDetector \ + FFMediaStream \ + ZeroMQClient \ + ZeroMQServer \ + ZeroMQPubSub \ + WDRemoteReceiver +} else { + SUBDIRS += \ + ShareMem \ + BinocularMarkReceiver \ + ModbusTCPServer \ + AuthModule + + contains(TARGET_APP, "^(BagThreadPosition|HoleDetection|StatorPosition|WorkpieceHole)$") { + SUBDIRS += ModbusTCPClient + } + + contains(TARGET_APP, "^(ScrewPosition|WorkpieceHole|DiscHolePose|TireHolePose|RodAndBarPosition|RodWeldSeam)$") { + SUBDIRS += HandEyeCalib + } + + contains(TARGET_APP, "^(DroneScrewServer|DroneScrewCtrlApp|DroneScrewbolt)$") { + SUBDIRS += FFMediaStream ZeroMQPubSub + } + + contains(TARGET_APP, "^(DroneScrewServer|DroneScrewbolt)$") { + SUBDIRS += ZeroMQServer + } + + contains(TARGET_APP, "^(DroneScrewCtrlApp|DroneScrewbolt)$") { + SUBDIRS += ZeroMQClient WDRemoteReceiver + } +} # 设置依赖关系 # 注意:SUBDIRS 必须用短名(目录名)注册,qmake 会自动查找 子目录/同名.pro, # 这样 .depends 才能生效。若写成 "WDRemoteReceiver/WDRemoteReceiver.pro" 路径形式, # 该依赖不会被 qmake 关联,make -j 并行编译时会出现 "cannot find -lZeroMQClient"。 # WDRemoteReceiver 是动态库,链接 ZeroMQClient / ZeroMQPubSub 静态库,必须先构建它们。 -WDRemoteReceiver.depends = ZeroMQClient ZeroMQPubSub +contains(SUBDIRS, WDRemoteReceiver) { + WDRemoteReceiver.depends = ZeroMQClient ZeroMQPubSub AuthModule +} win32-msvc { SUBDIRS += CloudShow } + +SUBDIRS = $$unique(SUBDIRS) diff --git a/Module/WDRemoteReceiver/Src/WDRemoteReceiver.cpp b/Module/WDRemoteReceiver/Src/WDRemoteReceiver.cpp index 2eabf045..6433c0ce 100644 --- a/Module/WDRemoteReceiver/Src/WDRemoteReceiver.cpp +++ b/Module/WDRemoteReceiver/Src/WDRemoteReceiver.cpp @@ -686,13 +686,9 @@ int WDRemoteReceiver::StartWork(int timeoutMs) int WDRemoteReceiver::StartWork(WDRemoteWorkMode mode, int timeoutMs) { std::lock_guard stateLock(m_mtxWorkState); - const WorkState oldState = m_workState; if (mode == WDRemoteWorkMode::LiveStream) { - if (oldState == WorkState::LiveStream) - return 0; - Json::Value stopReq; stopReq["cmd"] = "stop"; Json::FastWriter w; @@ -734,9 +730,6 @@ int WDRemoteReceiver::StartWork(WDRemoteWorkMode mode, int timeoutMs) return 0; } - if (oldState == WorkState::Detection) - return 0; - Json::Value stopStreamReq; stopStreamReq["cmd"] = "stop_stream"; Json::FastWriter w;