mirror of
https://github.com/moonlight-stream/moonlight-qt
synced 2024-11-10 05:34:17 +00:00
6d023c2dfa
We need to destroy the SDL window before we can be sure that Qt can draw to the screen if we used KMSDRM.
285 lines
7.5 KiB
C++
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;
|
|
};
|