SDL: Make use of the HDR light range when playing HDR video

From 202886f87392fc3e964aae5a3ec99eed1d99acf7 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 16 Feb 2024 20:05:11 -0800
Subject: [PATCH] Make use of the HDR light range when playing HDR video

---
 test/testffmpeg.c | 69 ++++++++++++++++++++++-------------------------
 1 file changed, 32 insertions(+), 37 deletions(-)

diff --git a/test/testffmpeg.c b/test/testffmpeg.c
index d1b8e0901a81..b5babd53862a 100644
--- a/test/testffmpeg.c
+++ b/test/testffmpeg.c
@@ -99,28 +99,51 @@ static int done;
 
 /* This function isn't platform specific, but we haven't hooked up HDR video support on other platforms yet */
 #if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_APPLE)
-static void GetDisplayHDRProperties(SDL_bool *HDR_display, float *SDR_white_level)
+static void GetDisplayHDRProperties(SDL_bool *HDR_display, float *SDR_white_level, float *HDR_white_level)
 {
     SDL_PropertiesID props;
 
+    *HDR_display = SDL_FALSE;
+    *SDR_white_level = SDR_DISPLAY_WHITE_LEVEL;
+    *HDR_white_level = SDR_DISPLAY_WHITE_LEVEL;
+
     props = SDL_GetRendererProperties(renderer);
     if (SDL_GetNumberProperty(props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB) != SDL_COLORSPACE_SCRGB) {
         /* We're not displaying in HDR, use the SDR white level */
-        *HDR_display = SDL_FALSE;
-        *SDR_white_level = SDR_DISPLAY_WHITE_LEVEL;
         return;
     }
 
     props = SDL_GetDisplayProperties(SDL_GetDisplayForWindow(window));
     if (!SDL_GetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE)) {
         /* HDR is not enabled on the display */
-        *HDR_display = SDL_FALSE;
-        *SDR_white_level = SDR_DISPLAY_WHITE_LEVEL;
         return;
     }
 
     *HDR_display = SDL_TRUE;
     *SDR_white_level = SDL_GetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT, DEFAULT_SDR_WHITE_LEVEL);
+    *HDR_white_level = SDL_GetFloatProperty(props, SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT, DEFAULT_HDR_WHITE_LEVEL);
+}
+
+static void UpdateVideoColorScale(SDL_bool HDR_video)
+{
+    SDL_bool HDR_display = SDL_FALSE;
+    float SDR_white_level, HDR_white_level, video_white_level;
+
+    GetDisplayHDRProperties(&HDR_display, &SDR_white_level, &HDR_white_level);
+
+    if (HDR_video) {
+        video_white_level = DEFAULT_HDR_WHITE_LEVEL;
+    } else {
+        video_white_level = SDR_DISPLAY_WHITE_LEVEL;
+    }
+
+    if (HDR_display && HDR_video) {
+        /* Scale the HDR range of the video to the HDR range of the display */
+        SDL_SetRenderColorScale(renderer, HDR_white_level / video_white_level);
+    } else {
+        /* Scale the range of the video to the SDR range of the display */
+        SDL_SetRenderColorScale(renderer, SDR_white_level / video_white_level);
+    }
 }
 #endif /* SDL_PLATFORM_WIN32 || SDL_PLATFORM_APPLE */
 
@@ -675,10 +698,8 @@ static SDL_bool GetTextureForD3D11Frame(AVFrame *frame, SDL_Texture **texture)
         SDL_QueryTexture(*texture, NULL, NULL, &texture_width, &texture_height);
     }
     if (!*texture || (UINT)texture_width != desc.Width || (UINT)texture_height != desc.Height) {
-        float SDR_white_level, video_white_level;
-        SDL_bool HDR_display = SDL_FALSE;
-        SDL_bool HDR_video = SDL_FALSE;
         Uint32 format;
+        SDL_bool HDR_video = SDL_FALSE;
 
         switch (desc.Format) {
         case DXGI_FORMAT_NV12:
@@ -701,8 +722,6 @@ static SDL_bool GetTextureForD3D11Frame(AVFrame *frame, SDL_Texture **texture)
             SDL_DestroyTexture(*texture);
         }
 
-        GetDisplayHDRProperties(&HDR_display, &SDR_white_level);
-
         SDL_PropertiesID props = SDL_CreateProperties();
         SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, GetFrameColorspace(frame));
         SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format);
@@ -715,16 +734,7 @@ static SDL_bool GetTextureForD3D11Frame(AVFrame *frame, SDL_Texture **texture)
             return SDL_FALSE;
         }
 
-        if (HDR_video != HDR_display) {
-            if (HDR_display) {
-                video_white_level = SDR_DISPLAY_WHITE_LEVEL;
-            } else {
-                video_white_level = DEFAULT_HDR_WHITE_LEVEL;
-            }
-            SDL_SetRenderColorScale(renderer, SDR_white_level / video_white_level);
-        } else {
-            SDL_SetRenderColorScale(renderer, 1.0f);
-        }
+        UpdateVideoColorScale(HDR_video);
     }
 
     ID3D11Resource *dx11_resource = SDL_GetProperty(SDL_GetTextureProperties(*texture), SDL_PROP_TEXTURE_D3D11_TEXTURE_POINTER, NULL);
@@ -749,8 +759,6 @@ static SDL_bool GetTextureForVideoToolboxFrame(AVFrame *frame, SDL_Texture **tex
     size_t nPixelBufferHeight = CVPixelBufferGetHeightOfPlane(pPixelBuffer, 0);
     SDL_PropertiesID props;
     Uint32 format;
-    float SDR_white_level, video_white_level;
-    SDL_bool HDR_display = SDL_FALSE;
     SDL_bool HDR_video = SDL_FALSE;
 
     switch (nPixelBufferType) {
@@ -778,8 +786,6 @@ static SDL_bool GetTextureForVideoToolboxFrame(AVFrame *frame, SDL_Texture **tex
         SDL_DestroyTexture(*texture);
     }
 
-    GetDisplayHDRProperties(&HDR_display, &SDR_white_level);
-
     props = SDL_CreateProperties();
     SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, GetFrameColorspace(frame));
     SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format);
@@ -793,19 +799,8 @@ static SDL_bool GetTextureForVideoToolboxFrame(AVFrame *frame, SDL_Texture **tex
         return SDL_FALSE;
     }
 
-    if (HDR_video != HDR_display) {
-        if (HDR_display) {
-            video_white_level = SDR_DISPLAY_WHITE_LEVEL;
-        } else {
-            video_white_level = DEFAULT_HDR_WHITE_LEVEL;
-        }
-        SDL_SetRenderColorScale(renderer, SDR_white_level / video_white_level);
-    } else if (HDR_display) {
-        /* Apple platforms already scale up the brightness of content so we need to scale it down by the same amount for HDR video */
-        SDL_SetRenderColorScale(renderer, SDR_white_level / DEFAULT_SDR_WHITE_LEVEL);
-    } else {
-        SDL_SetRenderColorScale(renderer, 1.0f);
-    }
+    UpdateVideoColorScale(HDR_video);
+
     return SDL_TRUE;
 #else
     return SDL_FALSE;