2026-10-28 20:48:12 +08:00
|
|
|
// Moonlight 会话(外部进程)
|
|
|
|
|
//
|
2026-10-30 14:42:11 +08:00
|
|
|
// 以 QProcess 参数数组直启 moonlight-qt(不拼 shell 命令)。
|
|
|
|
|
// 进程启动、串流就绪和终止是不同事件,调用方据此控制过场和状态机。
|
2026-10-28 20:48:12 +08:00
|
|
|
|
|
|
|
|
#ifndef KIOSK_SHELL_MOONLIGHT_SESSION_H
|
|
|
|
|
#define KIOSK_SHELL_MOONLIGHT_SESSION_H
|
|
|
|
|
|
2026-10-30 14:42:11 +08:00
|
|
|
#include <QByteArray>
|
2026-10-28 20:48:12 +08:00
|
|
|
#include <QProcess>
|
|
|
|
|
#include <QString>
|
|
|
|
|
#include <functional>
|
|
|
|
|
|
|
|
|
|
#include "desktop_session.h"
|
|
|
|
|
|
2026-10-30 14:42:11 +08:00
|
|
|
class QTimer;
|
|
|
|
|
|
2026-10-28 20:48:12 +08:00
|
|
|
namespace vdi {
|
|
|
|
|
|
2026-10-30 14:42:11 +08:00
|
|
|
class MoonlightOutputParser {
|
|
|
|
|
public:
|
|
|
|
|
void reset();
|
|
|
|
|
void append(const QByteArray& output);
|
|
|
|
|
bool isReady() const;
|
|
|
|
|
bool hasTestDecode() const { return hasTestDecode_; }
|
|
|
|
|
bool hasRenderer() const { return hasRenderer_; }
|
|
|
|
|
bool hasTerminalError() const { return hasTerminalError_; }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QByteArray buffer_;
|
|
|
|
|
bool hasTestDecode_ = false;
|
|
|
|
|
bool hasRenderer_ = false;
|
|
|
|
|
bool hasTerminalError_ = false;
|
|
|
|
|
};
|
|
|
|
|
|
2026-10-28 20:48:12 +08:00
|
|
|
class MoonlightSession : public QObject {
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
struct Options {
|
|
|
|
|
bool vsync = true;
|
|
|
|
|
bool framePacing = true;
|
|
|
|
|
bool performanceOverlay = false;
|
|
|
|
|
bool audioOnHost = true;
|
|
|
|
|
QString videoCodec = QStringLiteral("H.264");
|
2026-10-30 14:42:11 +08:00
|
|
|
QString decoder = QStringLiteral("software");
|
2026-10-28 20:48:12 +08:00
|
|
|
};
|
|
|
|
|
|
2026-10-30 14:42:11 +08:00
|
|
|
enum class EventType {
|
|
|
|
|
kStarted,
|
|
|
|
|
kStreamReady,
|
|
|
|
|
kFailed,
|
|
|
|
|
kFinished,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct Event {
|
|
|
|
|
EventType type;
|
|
|
|
|
QString message;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using EventCallback = std::function<void(const Event& event)>;
|
2026-10-28 20:48:12 +08:00
|
|
|
|
|
|
|
|
explicit MoonlightSession(QObject* parent = nullptr);
|
|
|
|
|
~MoonlightSession() override;
|
|
|
|
|
|
|
|
|
|
void setOptions(const Options& options);
|
2026-10-30 14:42:11 +08:00
|
|
|
bool start(const ConnectionTicket& ticket, EventCallback callback);
|
2026-10-28 20:48:12 +08:00
|
|
|
void stop();
|
|
|
|
|
bool isRunning() const;
|
2026-10-29 17:37:38 +08:00
|
|
|
qint64 processId() const;
|
2026-10-30 14:42:11 +08:00
|
|
|
bool activateStreamWindow();
|
2026-10-29 17:37:38 +08:00
|
|
|
|
2026-10-28 20:48:12 +08:00
|
|
|
private slots:
|
2026-10-30 14:42:11 +08:00
|
|
|
void onProcessStarted();
|
2026-10-28 20:48:12 +08:00
|
|
|
void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
|
|
|
|
void onProcessError(QProcess::ProcessError error);
|
2026-10-30 14:42:11 +08:00
|
|
|
void onProcessOutput();
|
2026-10-28 20:48:12 +08:00
|
|
|
|
|
|
|
|
private:
|
2026-10-30 14:42:11 +08:00
|
|
|
void emitEvent(EventType type, const QString& message);
|
|
|
|
|
void emitTerminalEvent(EventType type, const QString& message);
|
|
|
|
|
void scheduleReadyEvent();
|
|
|
|
|
|
2026-10-28 20:48:12 +08:00
|
|
|
QProcess* process_ = nullptr;
|
2026-10-30 14:42:11 +08:00
|
|
|
QTimer* readyTimer_ = nullptr;
|
|
|
|
|
QTimer* killTimer_ = nullptr;
|
2026-10-28 20:48:12 +08:00
|
|
|
Options options_;
|
2026-10-30 14:42:11 +08:00
|
|
|
EventCallback callback_;
|
|
|
|
|
MoonlightOutputParser outputParser_;
|
|
|
|
|
QString logPath_;
|
|
|
|
|
bool stopRequested_ = false;
|
|
|
|
|
bool terminalEventDelivered_ = false;
|
|
|
|
|
bool readyEventDelivered_ = false;
|
2026-10-28 20:48:12 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace vdi
|
|
|
|
|
|
|
|
|
|
#endif // KIOSK_SHELL_MOONLIGHT_SESSION_H
|