螺栓视频流和检测,接下来调优存储
This commit is contained in:
parent
92ef1af588
commit
818aa764fb
@ -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") {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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<bool> m_bConnected{false};
|
||||
std::atomic<bool> m_bRunning{false};
|
||||
std::atomic<bool> m_liveStreamMode{false};
|
||||
std::atomic<bool> m_rtspReceiving{false};
|
||||
std::atomic<bool> m_rtspFirstFrameReported{false};
|
||||
QTimer* m_pReconnectTimer{nullptr};
|
||||
|
||||
@ -11,15 +11,18 @@
|
||||
#include <QThread>
|
||||
#include <QUrl>
|
||||
#include <QtGlobal>
|
||||
#include <algorithm>
|
||||
|
||||
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<double>(b.x) * scaleX),
|
||||
qRound(static_cast<double>(b.y) * scaleY),
|
||||
qRound(static_cast<double>(b.width) * scaleX),
|
||||
qRound(static_cast<double>(b.height) * scaleY));
|
||||
if (b.width <= 0 || b.height <= 0)
|
||||
continue;
|
||||
const int x1 = qRound(static_cast<double>(b.x) * scaleX);
|
||||
const int y1 = qRound(static_cast<double>(b.y) * scaleY);
|
||||
int x2 = qRound(static_cast<double>(b.x + b.width) * scaleX) - 1;
|
||||
int y2 = qRound(static_cast<double>(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;
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
<Display drawBoxes="true"
|
||||
drawScores="true"
|
||||
drawClassId="true"
|
||||
boxThickness="1"
|
||||
boxThickness="2"
|
||||
maxResultListItems="200" />
|
||||
|
||||
</DroneScrewCtrlConfig>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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<int>(frame.distances.size());
|
||||
if (targetIndex >= 0 && targetIndex < total)
|
||||
{
|
||||
const CtrlDetectionDistance& d = frame.distances[static_cast<size_t>(targetIndex)];
|
||||
return QString::number(d.toId);
|
||||
}
|
||||
return QStringLiteral("-");
|
||||
}
|
||||
}
|
||||
|
||||
ResultItem::ResultItem(QWidget* parent)
|
||||
@ -112,11 +123,7 @@ void ResultItem::setResultData(int targetIndex,
|
||||
.arg(static_cast<int>(frame.boxes.size())));
|
||||
else if (mode == DisplayMode::Distance)
|
||||
{
|
||||
const int total = static_cast<int>(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"));
|
||||
|
||||
@ -1323,6 +1323,7 @@ int DroneScrewServerPresenter::initRtspPusher(unsigned int width, unsigned int h
|
||||
|
||||
void DroneScrewServerPresenter::releaseRtspPusher()
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<std::mutex> pusherLock(m_rtspPusherMutex);
|
||||
if (!m_pPusher)
|
||||
return ERR_CODE(DRONESCREW_ERR_RTSP_INIT);
|
||||
return m_pPusher->Start();
|
||||
}
|
||||
|
||||
void DroneScrewServerPresenter::resetRtspPushState()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<std::mutex> 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<int>(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<int>(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<int>(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<int>(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<std::mutex> 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<int>(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<std::mutex> 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<size_t>(img.width) * static_cast<size_t>(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<size_t>(outWidth) *
|
||||
static_cast<size_t>(outHeight);
|
||||
@ -3236,27 +3321,39 @@ int DroneScrewServerPresenter::pushRtspFrame(const MvsImageData& img)
|
||||
const int64_t ptsUs = static_cast<int64_t>(
|
||||
(static_cast<double>(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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<int> m_detectPipelineMode{0}; // 0=precision, 1=distance
|
||||
std::atomic<int> m_activeTriggerFps{2};
|
||||
std::atomic<int64_t> m_rtspFrameCounter{0}; // 相对帧计数(推流启动时重置)
|
||||
std::mutex m_rtspPusherMutex;
|
||||
std::mutex m_rtspPushMutex;
|
||||
std::atomic<int64_t> m_rtspPushedFrameCounter{0};
|
||||
std::atomic<int> m_rtspLastPushRet{0};
|
||||
std::atomic<unsigned long long> m_rtspLastPushFrameId{0};
|
||||
std::vector<unsigned char> m_rtspFrameBuffer;
|
||||
std::vector<unsigned int> m_rtspScaleX;
|
||||
std::vector<unsigned int> m_rtspScaleY;
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
BIN
App/DroneScrewbolt/result.png
Normal file
BIN
App/DroneScrewbolt/result.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
@ -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
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -686,13 +686,9 @@ int WDRemoteReceiver::StartWork(int timeoutMs)
|
||||
int WDRemoteReceiver::StartWork(WDRemoteWorkMode mode, int timeoutMs)
|
||||
{
|
||||
std::lock_guard<std::mutex> 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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user