Add Sunshine protocol extension for passing HDR metadata

This commit is contained in:
Cameron Gutman 2023-01-16 21:06:33 -06:00
parent c9a5cea93e
commit 79b5ef0e1e
2 changed files with 56 additions and 2 deletions

View File

@ -50,6 +50,7 @@ static bool stopping;
static bool disconnectPending;
static bool encryptedControlStream;
static bool hdrEnabled;
static SS_HDR_METADATA hdrMetadata;
static int intervalGoodFrameCount;
static int intervalTotalFrameCount;
@ -275,6 +276,7 @@ int initializeControlStream(void) {
encryptionCtx = PltCreateCryptoContext();
decryptionCtx = PltCreateCryptoContext();
hdrEnabled = false;
memset(&hdrMetadata, 0, sizeof(hdrMetadata));
return 0;
}
@ -813,9 +815,24 @@ static void controlReceiveThreadFunc(void* context) {
BbInitializeWrappedBuffer(&bb, (char*)ctlHdr, sizeof(*ctlHdr), packetLength - sizeof(*ctlHdr), BYTE_ORDER_LITTLE);
// FIXME: There are 7 additional bytes that appear to always be all zeros. What do they mean?
// Is there some way that GFE tells us the HDR mastering metadata (NV_HDR_COLOR_DATA) set by the game?
BbGet8(&bb, &enableByte);
if (IS_SUNSHINE()) {
// Zero the metadata buffer to properly handle older servers if we have to add new fields
memset(&hdrMetadata, 0, sizeof(hdrMetadata));
// Sunshine sends HDR metadata in this message too
for (int i = 0; i < 3; i++) {
BbGet16(&bb, &hdrMetadata.displayPrimaries[i].x);
BbGet16(&bb, &hdrMetadata.displayPrimaries[i].y);
}
BbGet16(&bb, &hdrMetadata.whitePoint.x);
BbGet16(&bb, &hdrMetadata.whitePoint.y);
BbGet16(&bb, &hdrMetadata.maxDisplayLuminance);
BbGet16(&bb, &hdrMetadata.minDisplayLuminance);
BbGet16(&bb, &hdrMetadata.maxContentLightLevel);
BbGet16(&bb, &hdrMetadata.maxFrameAverageLightLevel);
BbGet16(&bb, &hdrMetadata.maxFullFrameLuminance);
}
hdrEnabled = (enableByte != 0);
ListenerCallbacks.setHdrMode(hdrEnabled);
@ -1447,3 +1464,12 @@ int startControlStream(void) {
bool LiGetCurrentHostDisplayHdrMode(void) {
return hdrEnabled;
}
bool LiGetHdrMetadata(PSS_HDR_METADATA metadata) {
if (!IS_SUNSHINE() || !hdrEnabled) {
return false;
}
*metadata = hdrMetadata;
return true;
}

View File

@ -705,6 +705,34 @@ void LiCompleteVideoFrame(VIDEO_FRAME_HANDLE handle, int drStatus);
// See ConnListenerSetHdrMode() for more details.
bool LiGetCurrentHostDisplayHdrMode(void);
typedef struct _SS_HDR_METADATA {
// RGB order
struct {
uint16_t x; // Normalized to 50,000
uint16_t y; // Normalized to 50,000
} displayPrimaries[3];
struct {
uint16_t x; // Normalized to 50,000
uint16_t y; // Normalized to 50,000
} whitePoint;
uint16_t maxDisplayLuminance; // Nits
uint16_t minDisplayLuminance; // 1/10000th of a nit
// These are content-specific values which may not be available for all hosts.
uint16_t maxContentLightLevel; // Nits
uint16_t maxFrameAverageLightLevel; // Nits
// These are display-specific values which may not be available for all hosts.
uint16_t maxFullFrameLuminance; // Nits
} SS_HDR_METADATA, *PSS_HDR_METADATA;
// This function populates the provided mastering metadata struct with the HDR metadata
// from the host PC's monitor and content (if available). It is only valid to call this
// function when HDR mode is active on the host. This is a Sunshine protocol extension.
bool LiGetHdrMetadata(PSS_HDR_METADATA metadata);
// This function requests an IDR frame from the host. Typically this is done using DR_NEED_IDR, but clients
// processing frames asynchronously may need to reset their decoder state even after returning DR_OK for
// the prior frame. Rather than wait for a new frame and return DR_NEED_IDR for that one, they can just