#pragma once #include #include #include #include #include "settings/streamingpreferences.h" #include "input/input.h" #include "video/decoder.h" #include "audio/renderers/renderer.h" #include "video/overlaymanager.h" class SupportedVideoFormatList : public QList { public: operator int() const { int value = 0; for (const int & v : *this) { value |= v; } return value; } void removeByMask(int mask) { int i = 0; while (i < this->length()) { if (this->value(i) & mask) { this->removeAt(i); } else { i++; } } } void deprioritizeByMask(int mask) { QList deprioritizedList; int i = 0; while (i < this->length()) { if (this->value(i) & mask) { deprioritizedList.append(this->takeAt(i)); } else { i++; } } this->append(std::move(deprioritizedList)); } int maskByServerCodecModes(int serverCodecModes) { int mask = 0; const QMap mapping = { {SCM_H264, VIDEO_FORMAT_H264}, {SCM_H264_HIGH8_444, VIDEO_FORMAT_H264_HIGH8_444}, {SCM_HEVC, VIDEO_FORMAT_H265}, {SCM_HEVC_MAIN10, VIDEO_FORMAT_H265_MAIN10}, {SCM_HEVC_REXT8_444, VIDEO_FORMAT_H265_REXT8_444}, {SCM_HEVC_REXT10_444, VIDEO_FORMAT_H265_REXT10_444}, {SCM_AV1_MAIN8, VIDEO_FORMAT_AV1_MAIN8}, {SCM_AV1_MAIN10, VIDEO_FORMAT_AV1_MAIN10}, {SCM_AV1_HIGH8_444, VIDEO_FORMAT_AV1_HIGH8_444}, {SCM_AV1_HIGH10_444, VIDEO_FORMAT_AV1_HIGH10_444}, }; for (QMap::const_iterator it = mapping.cbegin(); it != mapping.cend(); ++it) { if (serverCodecModes & it.key()) { mask |= it.value(); serverCodecModes &= ~it.key(); } } // Make sure nobody forgets to update this for new SCM values SDL_assert(serverCodecModes == 0); int val = *this; return val & mask; } }; class Session : public QObject { Q_OBJECT friend class SdlInputHandler; friend class DeferredSessionCleanupTask; friend class AsyncConnectionStartThread; friend class ExecThread; public: explicit Session(NvComputer* computer, NvApp& app, StreamingPreferences *preferences = nullptr); // NB: This may not get destroyed for a long time! Don't put any cleanup here. // Use Session::exec() or DeferredSessionCleanupTask instead. virtual ~Session() {}; Q_INVOKABLE void exec(QWindow* qtWindow); static void getDecoderInfo(SDL_Window* window, bool& isHardwareAccelerated, bool& isFullScreenOnly, bool& isHdrSupported, QSize& maxResolution); static Session* get() { return s_ActiveSession; } Overlay::OverlayManager& getOverlayManager() { return m_OverlayManager; } void flushWindowEvents(); signals: void stageStarting(QString stage); void stageFailed(QString stage, int errorCode, QString failingPorts); void connectionStarted(); void displayLaunchError(QString text); void displayLaunchWarning(QString text); void quitStarting(); void sessionFinished(int portTestResult); // Emitted after sessionFinished() when the session is ready to be destroyed void readyForDeletion(); private: void execInternal(); bool initialize(); bool startConnectionAsync(); bool validateLaunch(SDL_Window* testWindow); void emitLaunchWarning(QString text); bool populateDecoderProperties(SDL_Window* window); IAudioRenderer* createAudioRenderer(const POPUS_MULTISTREAM_CONFIGURATION opusConfig); bool initializeAudioRenderer(); bool testAudio(int audioConfiguration); int getAudioRendererCapabilities(int audioConfiguration); void getWindowDimensions(int& x, int& y, int& width, int& height); void toggleFullscreen(); void notifyMouseEmulationMode(bool enabled); void updateOptimalWindowDisplayMode(); enum class DecoderAvailability { None, Software, Hardware }; static DecoderAvailability getDecoderAvailability(SDL_Window* window, StreamingPreferences::VideoDecoderSelection vds, int videoFormat, int width, int height, int frameRate); static bool chooseDecoder(StreamingPreferences::VideoDecoderSelection vds, SDL_Window* window, int videoFormat, int width, int height, int frameRate, bool enableVsync, bool enableFramePacing, bool testOnly, IVideoDecoder*& chosenDecoder); static void clStageStarting(int stage); static void clStageFailed(int stage, int errorCode); static void clConnectionTerminated(int errorCode); static void clLogMessage(const char* format, ...); static void clRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor); static void clConnectionStatusUpdate(int connectionStatus); static void clSetHdrMode(bool enabled); static void clRumbleTriggers(uint16_t controllerNumber, uint16_t leftTrigger, uint16_t rightTrigger); static void clSetMotionEventState(uint16_t controllerNumber, uint8_t motionType, uint16_t reportRateHz); static void clSetControllerLED(uint16_t controllerNumber, uint8_t r, uint8_t g, uint8_t b); static int arInit(int audioConfiguration, const POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* arContext, int arFlags); static void arCleanup(); static void arDecodeAndPlaySample(char* sampleData, int sampleLength); static int drSetup(int videoFormat, int width, int height, int frameRate, void*, int); static void drCleanup(); static int drSubmitDecodeUnit(PDECODE_UNIT du); StreamingPreferences* m_Preferences; bool m_IsFullScreen; SupportedVideoFormatList m_SupportedVideoFormats; // Sorted in order of descending priority STREAM_CONFIGURATION m_StreamConfig; DECODER_RENDERER_CALLBACKS m_VideoCallbacks; AUDIO_RENDERER_CALLBACKS m_AudioCallbacks; NvComputer* m_Computer; NvApp m_App; SDL_Window* m_Window; IVideoDecoder* m_VideoDecoder; SDL_SpinLock m_DecoderLock; bool m_AudioDisabled; bool m_AudioMuted; Uint32 m_FullScreenFlag; QWindow* m_QtWindow; bool m_ThreadedExec; bool m_UnexpectedTermination; SdlInputHandler* m_InputHandler; int m_MouseEmulationRefCount; int m_FlushingWindowEventsRef; QList m_LaunchWarnings; bool m_AsyncConnectionSuccess; int m_PortTestResults; int m_ActiveVideoFormat; int m_ActiveVideoWidth; int m_ActiveVideoHeight; int m_ActiveVideoFrameRate; OpusMSDecoder* m_OpusDecoder; IAudioRenderer* m_AudioRenderer; OPUS_MULTISTREAM_CONFIGURATION m_ActiveAudioConfig; OPUS_MULTISTREAM_CONFIGURATION m_OriginalAudioConfig; int m_AudioSampleCount; Uint32 m_DropAudioEndTime; Overlay::OverlayManager m_OverlayManager; static CONNECTION_LISTENER_CALLBACKS k_ConnCallbacks; static Session* s_ActiveSession; static QSemaphore s_ActiveSessionSemaphore; };