moonlight-qt/app/streaming/session.h
Cameron Gutman 6d023c2dfa Defer launch warnings until after launch validation
We need to destroy the SDL window before we can be sure that Qt
can draw to the screen if we used KMSDRM.
2024-09-23 21:48:43 -05:00

285 lines
7.5 KiB
C++

#pragma once
#include <QSemaphore>
#include <QWindow>
#include <Limelight.h>
#include <opus_multistream.h>
#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<int>
{
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<int> 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<int, int> 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<int, int>::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<QString> 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;
};