功能:完善2K串流、显示恢复与输入优化

This commit is contained in:
songyiming committed 2026-11-03 13:43:50 +08:00
1 parent e47b01beec
commit 938ec97cce
95 files changed
+5788 -72

No files matched your search

+98 -25
View File
@@ -14,7 +14,11 @@ constexpr int kReadyStableMs = 750;
constexpr int kTerminateGraceMs = 2000;
bool isTestDecodeLine(const QByteArray& line) {
return line.toLower().contains("test decode successful");
const QByteArray lower = line.toLower();
// Hardware decoders use a separate test context. Upstream logs this only
// after that context has successfully decoded and validated a test frame.
return lower.contains("test decode successful") ||
lower.contains("not reusing test decoder for ");
}
bool isRendererLine(const QByteArray& line) {
@@ -105,6 +109,66 @@ void MoonlightSession::setOptions(const Options& options) {
options_ = options;
}
MoonlightSession::Options MoonlightSession::optionsFromEnvironment(
const QProcessEnvironment& environment) {
Options options;
const QString profile = environment.value(QStringLiteral("VDI_STREAM_PROFILE"));
if (profile == QStringLiteral("low-latency")) {
options.profile = profile;
options.vsync = false;
options.framePacing = false;
} else if (profile == QStringLiteral("diagnostic")) {
options.profile = profile;
options.performanceOverlay = true;
}
// Statistics must be independently selectable for a fair profile A/B test.
if (environment.contains(QStringLiteral("VDI_STREAM_STATS"))) {
options.performanceOverlay = environment.value(QStringLiteral("VDI_STREAM_STATS")) == QStringLiteral("1");
}
const QString decoder = environment.value(QStringLiteral("VDI_MOONLIGHT_DECODER"));
if (QStringList{QStringLiteral("software"), QStringLiteral("hardware"),
QStringLiteral("auto")}.contains(decoder)) {
options.decoder = decoder;
}
const QString codec = environment.value(QStringLiteral("VDI_MOONLIGHT_CODEC"));
if (QStringList{QStringLiteral("H.264"), QStringLiteral("HEVC"),
QStringLiteral("AV1"), QStringLiteral("auto")}.contains(codec)) {
options.videoCodec = codec;
}
const QString program = environment.value(QStringLiteral("VDI_MOONLIGHT_PROGRAM"));
if (!program.isEmpty()) {
options.program = program;
}
return options;
}
QStringList MoonlightSession::buildStreamArguments(const ConnectionTicket& ticket,
const Options& options) {
const int bitrateKbps = ticket.width >= 2560 ? 30000 : 20000;
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(bitrateKbps)
<< QStringLiteral("--display-mode") << QStringLiteral("fullscreen")
<< QStringLiteral("--video-codec") << options.videoCodec
<< QStringLiteral("--video-decoder") << options.decoder
<< QStringLiteral("--absolute-mouse")
<< QStringLiteral("--keep-awake")
<< (options.vsync ? QStringLiteral("--vsync") : QStringLiteral("--no-vsync"))
<< (options.framePacing ? QStringLiteral("--frame-pacing")
: QStringLiteral("--no-frame-pacing"));
if (options.performanceOverlay) {
args << QStringLiteral("--performance-overlay");
}
args << (options.audioOnHost ? QStringLiteral("--audio-on-host")
: QStringLiteral("--no-audio-on-host"));
return args;
}
bool MoonlightSession::isRunning() const {
return process_->state() != QProcess::NotRunning;
}
@@ -156,34 +220,26 @@ bool MoonlightSession::start(const ConnectionTicket& ticket, EventCallback callb
readyTimer_->stop();
killTimer_->stop();
process_->setProgram(QStringLiteral("moonlight-qt"));
process_->setProgram(options_.program);
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
environment.insert(QStringLiteral("GL_IS_SLOW"), QStringLiteral("0"));
environment.insert(QStringLiteral("SDL_RENDER_DRIVER"), QStringLiteral("opengles2"));
environment.insert(QStringLiteral("SDL_RENDER_DRIVER"),
environment.value(QStringLiteral("VDI_SDL_RENDER_DRIVER"), QStringLiteral("opengles2")));
// Xorg下绕过桌面合成器;Wayland/KMS部署时可通过SDL_VIDEODRIVER切换,
// 不在客户端里硬编码会话类型,避免当前Xorg镜像无法启动。
environment.insert(QStringLiteral("SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR"),
environment.value(QStringLiteral("VDI_SDL_BYPASS_COMPOSITOR"), QStringLiteral("1")));
const QString forceEgl = qEnvironmentVariable("VDI_SDL_FORCE_EGL");
if (!forceEgl.isEmpty()) {
environment.insert(QStringLiteral("SDL_VIDEO_X11_FORCE_EGL"), forceEgl);
}
const QString sdlVideoDriver = qEnvironmentVariable("VDI_SDL_VIDEODRIVER");
if (!sdlVideoDriver.isEmpty()) {
environment.insert(QStringLiteral("SDL_VIDEODRIVER"), sdlVideoDriver);
}
process_->setProcessEnvironment(environment);
const int bitrateKbps = ticket.width >= 2560 ? 30000 : 20000;
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(bitrateKbps)
<< QStringLiteral("--display-mode") << QStringLiteral("fullscreen")
<< QStringLiteral("--video-codec") << options_.videoCodec
<< QStringLiteral("--video-decoder") << options_.decoder
<< QStringLiteral("--absolute-mouse")
<< QStringLiteral("--keep-awake");
args << (options_.vsync ? QStringLiteral("--vsync") : QStringLiteral("--no-vsync"));
args << (options_.framePacing ? QStringLiteral("--frame-pacing")
: QStringLiteral("--no-frame-pacing"));
if (options_.performanceOverlay) {
args << QStringLiteral("--performance-overlay");
}
args << (options_.audioOnHost ? QStringLiteral("--audio-on-host")
: QStringLiteral("--no-audio-on-host"));
const QStringList args = buildStreamArguments(ticket, options_);
process_->setArguments(args);
const QString logDir = QDir::homePath() + QStringLiteral("/vdi-terminal/logs");
@@ -191,6 +247,23 @@ bool MoonlightSession::start(const ConnectionTicket& ticket, EventCallback callb
logPath_ = logDir + QStringLiteral("/moonlight-%1.log")
.arg(QDate::currentDate().toString(QStringLiteral("yyyyMMdd")));
// 把最终生效的环境和参数写入日志,避免只看Moonlight叠图时无法判断
// 产品入口到底使用了哪一套呈现策略。
QFile launchLog(logPath_);
if (launchLog.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
QTextStream stream(&launchLog);
stream << "\n[VDI launch]\n"
<< "profile=" << options_.profile << "\n"
<< "program=" << options_.program << "\n"
<< "decoder=" << options_.decoder << "\n"
<< "codec=" << options_.videoCodec << "\n"
<< "vsync=" << (options_.vsync ? "1" : "0") << "\n"
<< "frame-pacing=" << (options_.framePacing ? "1" : "0") << "\n"
<< "renderer=" << environment.value(QStringLiteral("SDL_RENDER_DRIVER")) << "\n"
<< "video-driver=" << environment.value(QStringLiteral("SDL_VIDEODRIVER")) << "\n"
<< "args=" << args.join(QLatin1Char(' ')) << "\n";
}
process_->start();
return true;
}