功能:完善2K串流、显示恢复与输入优化
This commit is contained in:
1 parent
e47b01beec
commit
938ec97cce
95 files changed
+5788
-72
No files matched your search
@@ -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;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user