vdi-cloud/terminal/kiosk-shell/src/moonlight_session.cpp
songyiming 66f0cacb32 修复:QProcess::finished 重载消歧;文档:串流失败根因(双 IP ARP 混乱)与延迟 A/B 结论
- moonlight_session.cpp:QOverload 显式指定 finished 重载,修复 ARM64 编译
- moonlight.conf:vsync/frame-pacing 默认改回开启(A/B 实测关闭无改善)
- docs:补充 10-29 登录流程验收、重启验收、Monitor 恢复、双 IP 根因与 A/B 数据
- tools:新增 deploy_kiosk.sh 一键部署脚本与语法自检
2026-10-29 11:58:32 +08:00

142 lines
4.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "moonlight_session.h"
#include <QDate>
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <QTextStream>
namespace vdi {
MoonlightSession::MoonlightSession(QObject* parent) : QObject(parent) {}
MoonlightSession::~MoonlightSession() {
stop();
}
void MoonlightSession::setOptions(const Options& options) {
options_ = options;
}
bool MoonlightSession::isRunning() const {
return process_ && process_->state() != QProcess::NotRunning;
}
bool MoonlightSession::start(const ConnectionTicket& ticket, StateCallback callback) {
if (isRunning()) {
return false;
}
callback_ = std::move(callback);
// moonlight-qt 需要 X11 会话环境;由 kiosk-shell 继承当前 DISPLAY/XAUTHORITY
process_ = new QProcess(this);
process_->setProgram(QStringLiteral("moonlight-qt"));
process_->setProcessChannelMode(QProcess::MergedChannels);
QStringList args;
args << QStringLiteral("stream")
<< ticket.endpoint
<< ticket.app
<< QStringLiteral("--resolution")
<< QStringLiteral("%1x%2").arg(ticket.width).arg(ticket.height)
<< QStringLiteral("--fps") << QString::number(ticket.fps)
<< QStringLiteral("--bitrate") << QString::number(20000)
<< QStringLiteral("--display-mode") << QStringLiteral("fullscreen")
<< QStringLiteral("--video-codec") << options_.videoCodec
<< QStringLiteral("--video-decoder") << options_.decoder
<< QStringLiteral("--absolute-mouse")
<< QStringLiteral("--keep-awake");
if (options_.vsync) {
args << QStringLiteral("--vsync");
} else {
args << QStringLiteral("--no-vsync");
}
if (options_.framePacing) {
args << QStringLiteral("--frame-pacing");
} else {
args << QStringLiteral("--no-frame-pacing");
}
if (options_.performanceOverlay) {
args << QStringLiteral("--performance-overlay");
}
if (options_.audioOnHost) {
args << QStringLiteral("--audio-on-host");
} else {
args << QStringLiteral("--no-audio-on-host");
}
process_->setArguments(args);
// 日志追加到 moonlight-yyyyMMdd.log,便于现场排查
const QString logDir =
QDir::homePath() + QStringLiteral("/vdi-terminal/logs");
QDir().mkpath(logDir);
const QString logPath = logDir + QStringLiteral("/moonlight-%1.log")
.arg(QDate::currentDate().toString(QStringLiteral("yyyyMMdd")));
// QProcess::finished 在 Qt5 里有两个重载((int) 已废弃),必须用 QOverload 消歧
connect(process_,
QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this,
&MoonlightSession::onProcessFinished);
connect(process_, &QProcess::errorOccurred, this, &MoonlightSession::onProcessError);
connect(process_, &QProcess::readyReadStandardOutput, this, [this, logPath]() {
QFile log(logPath);
if (log.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
QTextStream(&log) << QString::fromUtf8(process_->readAllStandardOutput());
}
});
if (callback_) {
callback_(true, QStringLiteral("串流进程已启动"));
}
process_->start();
return true;
}
void MoonlightSession::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) {
QString message;
if (exitStatus == QProcess::CrashExit) {
message = QStringLiteral("串流异常退出 (code=%1)").arg(exitCode);
} else {
message = QStringLiteral("串流已结束 (code=%1)").arg(exitCode);
}
process_->deleteLater();
process_ = nullptr;
if (callback_) {
callback_(false, message);
}
}
void MoonlightSession::onProcessError(QProcess::ProcessError error) {
QString message;
switch (error) {
case QProcess::FailedToStart:
message = QStringLiteral("无法启动 moonlight-qt,请检查安装");
break;
case QProcess::Crashed:
return; // 由 finished(CrashExit) 统一上报
default:
message = QStringLiteral("串流进程错误 (%1)").arg(static_cast<int>(error));
break;
}
if (callback_) {
callback_(false, message);
}
}
void MoonlightSession::stop() {
if (!process_) {
return;
}
QProcess* proc = process_;
process_ = nullptr; // 先断开自身引用,避免 finished 回调再触发状态迁移
disconnect(proc, nullptr, this, nullptr);
proc->terminate();
if (!proc->waitForFinished(2000)) {
proc->kill();
proc->waitForFinished(1000);
}
proc->deleteLater();
}
} // namespace vdi