Skip to content

Commit

Permalink
Support YUV 4:4:4 formats
Browse files Browse the repository at this point in the history
  • Loading branch information
ns6089 committed Jun 9, 2024
1 parent ecef382 commit 16e80a7
Show file tree
Hide file tree
Showing 12 changed files with 389 additions and 18 deletions.
7 changes: 6 additions & 1 deletion app/app.pro
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ unix:!macx {
}
win32 {
LIBS += -llibssl -llibcrypto -lSDL2 -lSDL2_ttf -lavcodec -lavutil -lopus -ldxgi -ld3d11
CONFIG += ffmpeg
CONFIG += ffmpeg libplacebo
}
win32:!winrt {
CONFIG += soundio discord-rpc
Expand Down Expand Up @@ -338,6 +338,11 @@ libplacebo {
streaming/video/ffmpeg-renderers/plvk_c.c
HEADERS += \
streaming/video/ffmpeg-renderers/plvk.h

win32 {
INCLUDEPATH += $((VULKAN_SDK))/Include
LIBS += libplacebo.lib
}
}
config_EGL {
message(EGL renderer selected)
Expand Down
4 changes: 4 additions & 0 deletions app/cli/commandlineparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ void StreamCommandLineParser::parse(const QStringList &args, StreamingPreference
parser.addToggleOption("keep-awake", "prevent display sleep while streaming");
parser.addToggleOption("performance-overlay", "show performance overlay");
parser.addToggleOption("hdr", "HDR streaming");
parser.addToggleOption("yuv444", "YUV 4:4:4 sampling, if supported");
parser.addChoiceOption("capture-system-keys", "capture system key combos", m_CaptureSysKeysModeMap.keys());
parser.addChoiceOption("video-codec", "video codec", m_VideoCodecMap.keys());
parser.addChoiceOption("video-decoder", "video decoder", m_VideoDecoderMap.keys());
Expand Down Expand Up @@ -493,6 +494,9 @@ void StreamCommandLineParser::parse(const QStringList &args, StreamingPreference

// Resolve --hdr and --no-hdr options
preferences->enableHdr = parser.getToggleOptionValue("hdr", preferences->enableHdr);

// Resolve --yuv444 and --no-yuv444 options
preferences->enableYUV444 = parser.getToggleOptionValue("yuv444", preferences->enableYUV444);

// Resolve --capture-system-keys option
if (parser.isSet("capture-system-keys")) {
Expand Down
12 changes: 12 additions & 0 deletions app/gui/SettingsView.qml
Original file line number Diff line number Diff line change
Expand Up @@ -1611,6 +1611,18 @@ Flickable {
qsTr("HDR streaming is not supported on this PC.")
}

CheckBox {
id: enableYUV444
width: parent.width
text: qsTr("Enable YUV 4:4:4 (Experimental)")
font.pointSize: 12

checked: StreamingPreferences.enableYUV444
onCheckedChanged: {
StreamingPreferences.enableYUV444 = checked
}
}

CheckBox {
id: enableMdns
width: parent.width
Expand Down
3 changes: 3 additions & 0 deletions app/settings/streamingpreferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#define SER_AUDIOCFG "audiocfg"
#define SER_VIDEOCFG "videocfg"
#define SER_HDR "hdr"
#define SER_YUV444 "yuv444"
#define SER_VIDEODEC "videodec"
#define SER_WINDOWMODE "windowmode"
#define SER_MDNS "mdns"
Expand Down Expand Up @@ -140,6 +141,7 @@ void StreamingPreferences::reload()
swapFaceButtons = settings.value(SER_SWAPFACEBUTTONS, false).toBool();
keepAwake = settings.value(SER_KEEPAWAKE, true).toBool();
enableHdr = settings.value(SER_HDR, false).toBool();
enableYUV444 = settings.value(SER_YUV444, false).toBool();
captureSysKeysMode = static_cast<CaptureSysKeysMode>(settings.value(SER_CAPTURESYSKEYS,
static_cast<int>(CaptureSysKeysMode::CSK_OFF)).toInt());
audioConfig = static_cast<AudioConfig>(settings.value(SER_AUDIOCFG,
Expand Down Expand Up @@ -320,6 +322,7 @@ void StreamingPreferences::save()
settings.setValue(SER_SHOWPERFOVERLAY, showPerformanceOverlay);
settings.setValue(SER_AUDIOCFG, static_cast<int>(audioConfig));
settings.setValue(SER_HDR, enableHdr);
settings.setValue(SER_YUV444, enableYUV444);
settings.setValue(SER_VIDEOCFG, static_cast<int>(videoCodecConfig));
settings.setValue(SER_VIDEODEC, static_cast<int>(videoDecoderSelection));
settings.setValue(SER_WINDOWMODE, static_cast<int>(windowMode));
Expand Down
3 changes: 3 additions & 0 deletions app/settings/streamingpreferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ class StreamingPreferences : public QObject
Q_PROPERTY(AudioConfig audioConfig MEMBER audioConfig NOTIFY audioConfigChanged)
Q_PROPERTY(VideoCodecConfig videoCodecConfig MEMBER videoCodecConfig NOTIFY videoCodecConfigChanged)
Q_PROPERTY(bool enableHdr MEMBER enableHdr NOTIFY enableHdrChanged)
Q_PROPERTY(bool enableYUV444 MEMBER enableYUV444 NOTIFY enableYUV444Changed)
Q_PROPERTY(VideoDecoderSelection videoDecoderSelection MEMBER videoDecoderSelection NOTIFY videoDecoderSelectionChanged)
Q_PROPERTY(WindowMode windowMode MEMBER windowMode NOTIFY windowModeChanged)
Q_PROPERTY(WindowMode recommendedFullScreenMode MEMBER recommendedFullScreenMode CONSTANT)
Expand Down Expand Up @@ -168,6 +169,7 @@ class StreamingPreferences : public QObject
AudioConfig audioConfig;
VideoCodecConfig videoCodecConfig;
bool enableHdr;
bool enableYUV444;
VideoDecoderSelection videoDecoderSelection;
WindowMode windowMode;
WindowMode recommendedFullScreenMode;
Expand All @@ -190,6 +192,7 @@ class StreamingPreferences : public QObject
void audioConfigChanged();
void videoCodecConfigChanged();
void enableHdrChanged();
void enableYUV444Changed();
void videoDecoderSelectionChanged();
void uiDisplayModeChanged();
void windowModeChanged();
Expand Down
157 changes: 145 additions & 12 deletions app/streaming/session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,18 +472,33 @@ bool Session::populateDecoderProperties(SDL_Window* window)
IVideoDecoder* decoder;

int videoFormat;
if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_MAIN10) {
if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_HIGH10_444) {
videoFormat = VIDEO_FORMAT_AV1_HIGH10_444;
}
else if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_MAIN10) {
videoFormat = VIDEO_FORMAT_AV1_MAIN10;
}
else if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_HIGH8_444) {
videoFormat = VIDEO_FORMAT_AV1_HIGH8_444;
}
else if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_MAIN8) {
videoFormat = VIDEO_FORMAT_AV1_MAIN8;
}
else if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_H265_REXT10_444) {
videoFormat = VIDEO_FORMAT_H265_REXT10_444;
}
else if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_H265_MAIN10) {
videoFormat = VIDEO_FORMAT_H265_MAIN10;
}
else if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_H265_REXT8_444) {
videoFormat = VIDEO_FORMAT_H265_REXT8_444;
}
else if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_H265) {
videoFormat = VIDEO_FORMAT_H265;
}
else if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_H264_HIGH8_444) {
videoFormat = VIDEO_FORMAT_H264_HIGH8_444;
}
else {
videoFormat = VIDEO_FORMAT_H264;
}
Expand Down Expand Up @@ -606,15 +621,22 @@ bool Session::initialize()
return false;
}

LiInitializeStreamConfiguration(&m_StreamConfig);
m_StreamConfig.width = m_Preferences->width;
m_StreamConfig.height = m_Preferences->height;

int x, y, width, height;
getWindowDimensions(x, y, width, height);

// Create a hidden window to use for decoder initialization tests
SDL_Window* testWindow = SDL_CreateWindow("", 0, 0, 1280, 720,
SDL_Window* testWindow = SDL_CreateWindow("", x, y, width, height,
SDL_WINDOW_HIDDEN | StreamUtils::getPlatformWindowFlags());
if (!testWindow) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create test window with platform flags: %s",
SDL_GetError());

testWindow = SDL_CreateWindow("", 0, 0, 1280, 720, SDL_WINDOW_HIDDEN);
testWindow = SDL_CreateWindow("", x, y, width, height, SDL_WINDOW_HIDDEN);
if (!testWindow) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create window for hardware decode test: %s",
Expand All @@ -630,9 +652,6 @@ bool Session::initialize()
LiInitializeVideoCallbacks(&m_VideoCallbacks);
m_VideoCallbacks.setup = drSetup;

LiInitializeStreamConfiguration(&m_StreamConfig);
m_StreamConfig.width = m_Preferences->width;
m_StreamConfig.height = m_Preferences->height;
m_StreamConfig.fps = m_Preferences->fps;
m_StreamConfig.bitrate = m_Preferences->bitrateKbps;

Expand Down Expand Up @@ -712,14 +731,31 @@ bool Session::initialize()
#endif

// TODO: Determine if HEVC is better depending on the decoder
if (m_Preferences->enableHdr && isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
VIDEO_FORMAT_H265_MAIN10,
m_StreamConfig.width,
m_StreamConfig.height,
m_StreamConfig.fps)) {
if (m_Preferences->enableHdr &&
m_Preferences->enableYUV444 && isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
VIDEO_FORMAT_H265_REXT10_444,
m_StreamConfig.width,
m_StreamConfig.height,
m_StreamConfig.fps)) {
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H265 | VIDEO_FORMAT_H265_MAIN10 | VIDEO_FORMAT_H265_REXT8_444 | VIDEO_FORMAT_H265_REXT10_444;
}
else if (m_Preferences->enableHdr && isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
VIDEO_FORMAT_H265_MAIN10,
m_StreamConfig.width,
m_StreamConfig.height,
m_StreamConfig.fps)) {
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H265 | VIDEO_FORMAT_H265_MAIN10;
}
else if (m_Preferences->enableYUV444 && isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
VIDEO_FORMAT_H265_REXT8_444,
m_StreamConfig.width,
m_StreamConfig.height,
m_StreamConfig.fps)) {
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H265 | VIDEO_FORMAT_H265_REXT8_444;
}
else if (isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
VIDEO_FORMAT_H265,
Expand Down Expand Up @@ -747,18 +783,33 @@ bool Session::initialize()
#endif
break;
case StreamingPreferences::VCC_FORCE_H264:
if (m_Preferences->enableYUV444) {
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H264_HIGH8_444;
}
break;
case StreamingPreferences::VCC_FORCE_HEVC:
case StreamingPreferences::VCC_FORCE_HEVC_HDR_DEPRECATED:
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H265;
if (m_Preferences->enableHdr) {
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H265_MAIN10;
if (m_Preferences->enableYUV444) {
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H265_REXT10_444;
}
}
if (m_Preferences->enableYUV444) {
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H265_REXT8_444;
}
break;
case StreamingPreferences::VCC_FORCE_AV1:
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_AV1_MAIN8;
if (m_Preferences->enableHdr) {
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_AV1_MAIN10;
if (m_Preferences->enableYUV444) {
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_AV1_HIGH10_444;
}
}
if (m_Preferences->enableYUV444) {
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_AV1_HIGH8_444;
}

// We'll try to fall back to HEVC first if AV1 fails. We'd rather not fall back
Expand All @@ -769,6 +820,12 @@ bool Session::initialize()
if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_MAIN10) {
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H265_MAIN10;
}
if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_HIGH8_444) {
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H265_REXT8_444;
}
if (m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_HIGH10_444) {
m_StreamConfig.supportedVideoFormats |= VIDEO_FORMAT_H265_REXT10_444;
}
break;
}

Expand Down Expand Up @@ -988,6 +1045,82 @@ bool Session::validateLaunch(SDL_Window* testWindow)
}
}

if (m_Preferences->enableYUV444) {
switch (m_Preferences->videoCodecConfig) {
case StreamingPreferences::VCC_AUTO:
// Auto was already checked during init
break;

case StreamingPreferences::VCC_FORCE_H264:
if ((m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_H264_HIGH8_444) &&
(!(m_Computer->serverCodecModeSupport & SCM_H264_HIGH8_444) ||
!isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
VIDEO_FORMAT_H264_HIGH8_444,
m_StreamConfig.width,
m_StreamConfig.height,
m_StreamConfig.fps))) {
m_StreamConfig.supportedVideoFormats &= ~VIDEO_FORMAT_H264_HIGH8_444;
}
break;

case StreamingPreferences::VCC_FORCE_HEVC:
case StreamingPreferences::VCC_FORCE_HEVC_HDR_DEPRECATED:
if ((m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_H265_REXT10_444) &&
(!(m_Computer->serverCodecModeSupport & SCM_HEVC_REXT10_444) ||
!isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
VIDEO_FORMAT_H265_REXT10_444,
m_StreamConfig.width,
m_StreamConfig.height,
m_StreamConfig.fps))) {
m_StreamConfig.supportedVideoFormats &= ~VIDEO_FORMAT_H265_REXT10_444;
}
// Skip 8-bit check if we passed 10-bit check
if (!(m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_H265_REXT10_444) &&
(m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_H265_REXT8_444) &&
(!(m_Computer->serverCodecModeSupport & SCM_HEVC_REXT8_444) ||
!isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
VIDEO_FORMAT_H265_REXT8_444,
m_StreamConfig.width,
m_StreamConfig.height,
m_StreamConfig.fps))) {
m_StreamConfig.supportedVideoFormats &= ~VIDEO_FORMAT_H265_REXT8_444;
}
break;

case StreamingPreferences::VCC_FORCE_AV1:
if ((m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_HIGH10_444) &&
(!(m_Computer->serverCodecModeSupport & SCM_AV1_HIGH10_444) ||
!isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
VIDEO_FORMAT_AV1_HIGH10_444,
m_StreamConfig.width,
m_StreamConfig.height,
m_StreamConfig.fps))) {
m_StreamConfig.supportedVideoFormats &= ~VIDEO_FORMAT_AV1_HIGH10_444;
}
// Skip 8-bit check if we passed 10-bit check
if (!(m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_HIGH10_444) &&
(m_StreamConfig.supportedVideoFormats & VIDEO_FORMAT_AV1_HIGH8_444) &&
(!(m_Computer->serverCodecModeSupport & SCM_AV1_HIGH8_444) ||
!isHardwareDecodeAvailable(testWindow,
m_Preferences->videoDecoderSelection,
VIDEO_FORMAT_AV1_HIGH8_444,
m_StreamConfig.width,
m_StreamConfig.height,
m_StreamConfig.fps))) {
m_StreamConfig.supportedVideoFormats &= ~VIDEO_FORMAT_AV1_HIGH8_444;
}
break;

default:
SDL_assert(false);
break;
}
}

if (m_StreamConfig.width >= 3840) {
// Only allow 4K on GFE 3.x+
if (m_Computer->gfeVersion.isEmpty() || m_Computer->gfeVersion.startsWith("2.")) {
Expand Down
Loading

0 comments on commit 16e80a7

Please sign in to comment.