diff --git a/app/backend/computermanager.cpp b/app/backend/computermanager.cpp index 6b89d036..a6ef73d9 100644 --- a/app/backend/computermanager.cpp +++ b/app/backend/computermanager.cpp @@ -10,7 +10,6 @@ #define SER_NAME "hostname" #define SER_UUID "uuid" #define SER_MAC "mac" -#define SER_CODECSUPP "codecsupport" #define SER_LOCALADDR "localaddress" #define SER_REMOTEADDR "remoteaddress" #define SER_MANUALADDR "manualaddress" @@ -25,7 +24,6 @@ NvComputer::NvComputer(QSettings& settings) this->name = settings.value(SER_NAME).toString(); this->uuid = settings.value(SER_UUID).toString(); this->macAddress = settings.value(SER_MAC).toByteArray(); - this->serverCodecModeSupport = settings.value(SER_CODECSUPP).toInt(); this->localAddress = settings.value(SER_LOCALADDR).toString(); this->remoteAddress = settings.value(SER_REMOTEADDR).toString(); this->manualAddress = settings.value(SER_MANUALADDR).toString(); @@ -61,7 +59,6 @@ NvComputer::serialize(QSettings& settings) settings.setValue(SER_NAME, name); settings.setValue(SER_UUID, uuid); settings.setValue(SER_MAC, macAddress); - settings.setValue(SER_CODECSUPP, serverCodecModeSupport); settings.setValue(SER_LOCALADDR, localAddress); settings.setValue(SER_REMOTEADDR, remoteAddress); settings.setValue(SER_MANUALADDR, manualAddress); @@ -112,6 +109,21 @@ NvComputer::NvComputer(QString address, QString serverInfo) this->serverCodecModeSupport = 0; } + QString maxLumaPixelsHEVC = NvHTTP::getXmlString(serverInfo, "MaxLumaPixelsHEVC"); + if (!maxLumaPixelsHEVC.isNull()) { + this->maxLumaPixelsHEVC = maxLumaPixelsHEVC.toInt(); + } + else { + this->maxLumaPixelsHEVC = 0; + } + + this->displayModes = NvHTTP::getDisplayModeList(serverInfo); + std::stable_sort(this->displayModes.begin(), this->displayModes.end(), + [](const NvDisplayMode& mode1, const NvDisplayMode& mode2) { + return mode1.width * mode1.height * mode1.refreshRate < + mode2.width * mode2.height * mode2.refreshRate; + }); + this->localAddress = NvHTTP::getXmlString(serverInfo, "LocalIP"); this->remoteAddress = NvHTTP::getXmlString(serverInfo, "ExternalIP"); this->pairState = NvHTTP::getXmlString(serverInfo, "PairStatus") == "1" ? @@ -249,7 +261,9 @@ bool NvComputer::update(NvComputer& that) ASSIGN_IF_CHANGED(state); ASSIGN_IF_CHANGED(gfeVersion); ASSIGN_IF_CHANGED(appVersion); + ASSIGN_IF_CHANGED(maxLumaPixelsHEVC); ASSIGN_IF_CHANGED_AND_NONEMPTY(appList); + ASSIGN_IF_CHANGED_AND_NONEMPTY(displayModes); return changed; } diff --git a/app/backend/computermanager.h b/app/backend/computermanager.h index 8efc6a68..e9fdc0e0 100644 --- a/app/backend/computermanager.h +++ b/app/backend/computermanager.h @@ -58,6 +58,9 @@ public: int currentGameId; QString gfeVersion; QString appVersion; + QVector displayModes; + int maxLumaPixelsHEVC; + int serverCodecModeSupport; // Persisted traits QString localAddress; @@ -66,7 +69,6 @@ public: QByteArray macAddress; QString name; QString uuid; - int serverCodecModeSupport; QVector appList; // Synchronization diff --git a/app/backend/nvhttp.cpp b/app/backend/nvhttp.cpp index ebcc9248..a3d42af2 100644 --- a/app/backend/nvhttp.cpp +++ b/app/backend/nvhttp.cpp @@ -182,6 +182,33 @@ NvHTTP::quitApp() } } +QVector +NvHTTP::getDisplayModeList(QString serverInfo) +{ + QXmlStreamReader xmlReader(serverInfo); + QVector modes; + + while (!xmlReader.atEnd()) { + while (xmlReader.readNextStartElement()) { + QStringRef name = xmlReader.name(); + if (xmlReader.name() == "DisplayMode") { + modes.append(NvDisplayMode()); + } + else if (xmlReader.name() == "Width") { + modes.last().width = xmlReader.readElementText().toInt(); + } + else if (xmlReader.name() == "Height") { + modes.last().height = xmlReader.readElementText().toInt(); + } + else if (xmlReader.name() == "RefreshRate") { + modes.last().refreshRate = xmlReader.readElementText().toInt(); + } + } + } + + return modes; +} + QVector NvHTTP::getAppList() { diff --git a/app/backend/nvhttp.h b/app/backend/nvhttp.h index 4730d66b..dfb0ef20 100644 --- a/app/backend/nvhttp.h +++ b/app/backend/nvhttp.h @@ -27,6 +27,21 @@ public: Q_DECLARE_METATYPE(NvApp) +class NvDisplayMode +{ +public: + bool operator==(const NvDisplayMode& other) const + { + return width == other.width && + height == other.height && + refreshRate == other.refreshRate; + } + + int width; + int height; + int refreshRate; +}; + class GfeHttpResponseException : public std::exception { public: @@ -117,6 +132,10 @@ public: QImage getBoxArt(int appId); + static + QVector + getDisplayModeList(QString serverInfo); + QUrl m_BaseUrlHttp; QUrl m_BaseUrlHttps; private: diff --git a/app/settings/streamingpreferences.h b/app/settings/streamingpreferences.h index 41d9fb87..eba37971 100644 --- a/app/settings/streamingpreferences.h +++ b/app/settings/streamingpreferences.h @@ -41,9 +41,9 @@ public: }; Q_ENUM(VideoDecoderSelection) - Q_PROPERTY(int width MEMBER width NOTIFY resolutionOrFpsChanged) - Q_PROPERTY(int height MEMBER height NOTIFY resolutionOrFpsChanged) - Q_PROPERTY(int fps MEMBER fps NOTIFY resolutionOrFpsChanged) + Q_PROPERTY(int width MEMBER width NOTIFY displayModeChanged) + Q_PROPERTY(int height MEMBER height NOTIFY displayModeChanged) + Q_PROPERTY(int fps MEMBER fps NOTIFY displayModeChanged) Q_PROPERTY(int bitrateKbps MEMBER bitrateKbps NOTIFY bitrateChanged) Q_PROPERTY(bool fullScreen MEMBER fullScreen NOTIFY fullScreenChanged) Q_PROPERTY(bool gameOptimizations MEMBER gameOptimizations NOTIFY gameOptimizationsChanged) @@ -67,7 +67,7 @@ public: VideoDecoderSelection videoDecoderSelection; signals: - void resolutionOrFpsChanged(); + void displayModeChanged(); void bitrateChanged(); void fullScreenChanged(); void gameOptimizationsChanged(); diff --git a/app/streaming/session.cpp b/app/streaming/session.cpp index 40b60bb8..890d9e7a 100644 --- a/app/streaming/session.cpp +++ b/app/streaming/session.cpp @@ -136,9 +136,20 @@ Session::Session(NvComputer* computer, NvApp& app) bool Session::validateLaunch() { - NvHTTP http(m_Computer->activeAddress); QStringList warningList; + if (m_StreamConfig.supportsHevc) { + if (m_Preferences.videoCodecConfig == StreamingPreferences::VCC_FORCE_HEVC || + m_Preferences.videoCodecConfig == StreamingPreferences::VCC_FORCE_HEVC_HDR) { + if (m_Computer->maxLumaPixelsHEVC == 0) { + emit displayLaunchWarning("Your host PC GPU doesn't support HEVC. " + "A GeForce GTX 900-series (Maxwell) or later GPU is required for HEVC streaming."); + } + } + + // TODO: Validate HEVC support based on decoder caps + } + if (m_StreamConfig.enableHdr) { // Turn HDR back off unless all criteria are met. m_StreamConfig.enableHdr = false; @@ -161,16 +172,22 @@ bool Session::validateLaunch() } if (m_StreamConfig.width >= 3840) { - if (m_StreamConfig.fps >= 60) { - // TODO: Validate 4K60 support based on serverinfo - } - else { - // TODO: Validate 4K30 support based on serverinfo - } - } + // Only allow 4K on GFE 3.x+ + if (m_Computer->gfeVersion.isNull() || m_Computer->gfeVersion.startsWith("2.")) { + emit displayLaunchWarning("GeForce Experience 3.0 or higher is required for 4K streaming."); - if (m_StreamConfig.supportsHevc) { - // TODO: Validate HEVC support based on decoder caps + m_StreamConfig.width = 1920; + m_StreamConfig.height = 1080; + } + // This list is sorted from least to greatest + else if (m_Computer->displayModes.last().width < 3840 || + (m_Computer->displayModes.last().refreshRate < 60 && m_StreamConfig.fps >= 60)) { + emit displayLaunchWarning("Your host PC GPU doesn't support 4K streaming. " + "A GeForce GTX 900-series (Maxwell) or later GPU is required for 4K streaming."); + + m_StreamConfig.width = 1920; + m_StreamConfig.height = 1080; + } } // Always allow the launch to proceed for now