Effect: Add EGL_EXT_gl_colorspace_bt2020_hlg support, on API 34+

Confirmed that the HLG extension displays properly on Pixel 7 Pro, API 34 and Samsung S24, API 34. On these devices, this fixes the washed out HLG preview issue on API 34, where the PQ ext had a washed out look, and where the HLG ext is supported (this bug didn't occur on API 33, only 34).

More info on manual tests done to sanity-check:
* Test cases: Transformer debug SurfaceView and ExoPlayer.setVideoEffects
* Test inputs: HLG and PQ
* Test devices: Pixel 7 Pro (API 33 & 34), Samsung Galaxy S24 (API 34)
* Added debugging: Logging colorInfo used in GlUtil.createEglSurface

No regressions were seen. HLG extension is used more in API 34, and behavior stays the same on API 33. Only human-visible change without logging is that HLG content looks better on API 34, for Samsung S24 and Pixel 7 Pro.

PiperOrigin-RevId: 616131192
This commit is contained in:
huangdarwin 2024-03-15 08:03:34 -07:00 committed by Copybara-Service
parent c0ffc94d15
commit b126bae93d
5 changed files with 45 additions and 32 deletions

View File

@ -73,6 +73,9 @@
* Support multiple speed changes within the same `EditedMediaItem` or
`Composition` in `SpeedChangeEffect`.
* Support for HLG and PQ output from ultra HDR bitmap input.
* Add support for EGL_GL_COLORSPACE_BT2020_HLG_EXT, which improves HLG
surface output in ExoPlayer.setVideoEffect and Transformer's Debug
SurfaceView.
* Muxers:
* IMA extension:
* Session:

View File

@ -96,6 +96,7 @@ public final class GlUtil {
private static final String EXTENSION_YUV_TARGET = "GL_EXT_YUV_target";
// https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_gl_colorspace_bt2020_linear.txt
private static final String EXTENSION_COLORSPACE_BT2020_PQ = "EGL_EXT_gl_colorspace_bt2020_pq";
private static final String EXTENSION_COLORSPACE_BT2020_HLG = "EGL_EXT_gl_colorspace_bt2020_hlg";
// https://registry.khronos.org/EGL/extensions/KHR/EGL_KHR_gl_colorspace.txt
private static final int EGL_GL_COLORSPACE_KHR = 0x309D;
// https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_gl_colorspace_bt2020_linear.txt
@ -104,6 +105,11 @@ public final class GlUtil {
new int[] {
EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_BT2020_PQ_EXT, EGL14.EGL_NONE, EGL14.EGL_NONE
};
private static final int EGL_GL_COLORSPACE_BT2020_HLG_EXT = 0x3540;
private static final int[] EGL_WINDOW_SURFACE_ATTRIBUTES_BT2020_HLG =
new int[] {
EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_BT2020_HLG_EXT, EGL14.EGL_NONE, EGL14.EGL_NONE
};
private static final int[] EGL_WINDOW_SURFACE_ATTRIBUTES_NONE = new int[] {EGL14.EGL_NONE};
/** Class only contains static methods. */
@ -221,7 +227,14 @@ public final class GlUtil {
/** Returns whether {@link #EXTENSION_COLORSPACE_BT2020_PQ} is supported. */
public static boolean isBt2020PqExtensionSupported() {
return isExtensionSupported(EXTENSION_COLORSPACE_BT2020_PQ);
// On API<33, the system cannot display PQ content correctly regardless of whether BT2020 PQ
// GL extension is supported. Context: http://b/252537203#comment5.
return Util.SDK_INT >= 33 && isExtensionSupported(EXTENSION_COLORSPACE_BT2020_PQ);
}
/** Returns whether {@link #EXTENSION_COLORSPACE_BT2020_HLG} is supported. */
public static boolean isBt2020HlgExtensionSupported() {
return isExtensionSupported(EXTENSION_COLORSPACE_BT2020_HLG);
}
/** Returns an initialized default {@link EGLDisplay}. */
@ -301,7 +314,7 @@ public final class GlUtil {
* @param surface The surface to wrap; must be a surface, surface texture or surface holder.
* @param colorTransfer The {@linkplain C.ColorTransfer color transfer characteristics} to which
* the {@code surface} is configured. The only accepted values are {@link
* C#COLOR_TRANSFER_SDR}, {@link C#COLOR_TRANSFER_HLG} and {@link C#COLOR_TRANSFER_ST2084}.
* C#COLOR_TRANSFER_SDR}, {@link C#COLOR_TRANSFER_HLG}, and {@link C#COLOR_TRANSFER_ST2084}.
* @param isEncoderInputSurface Whether the {@code surface} is the input surface of an encoder.
*/
public static EGLSurface createEglSurface(
@ -315,22 +328,28 @@ public final class GlUtil {
if (colorTransfer == C.COLOR_TRANSFER_SDR || colorTransfer == C.COLOR_TRANSFER_GAMMA_2_2) {
configAttributes = EGL_CONFIG_ATTRIBUTES_RGBA_8888;
windowAttributes = EGL_WINDOW_SURFACE_ATTRIBUTES_NONE;
} else if (colorTransfer == C.COLOR_TRANSFER_ST2084) {
} else if (colorTransfer == C.COLOR_TRANSFER_HLG || colorTransfer == C.COLOR_TRANSFER_ST2084) {
configAttributes = EGL_CONFIG_ATTRIBUTES_RGBA_1010102;
// !isEncoderInputSurface means outputting to a display surface. HDR display surfaces
// require EGL_GL_COLORSPACE_BT2020_PQ_EXT or EGL_GL_COLORSPACE_BT2020_HLG_EXT.
if (isEncoderInputSurface) {
// Outputting BT2020 PQ with EGL_WINDOW_SURFACE_ATTRIBUTES_BT2020_PQ to an encoder causes
// the encoder to incorrectly switch to full range color, even if the encoder is configured
// with limited range color, because EGL_WINDOW_SURFACE_ATTRIBUTES_BT2020_PQ sets full range
// color output, and GL windowAttributes overrides encoder settings.
// Outputting BT2020 PQ or HLG with EGL_WINDOW_SURFACE_ATTRIBUTES_BT2020_PQ to an encoder
// causes the encoder to incorrectly switch to full range color, even if the encoder is
// configured with limited range color, because EGL_WINDOW_SURFACE_ATTRIBUTES_BT2020_PQ sets
// full range color output, and GL windowAttributes overrides encoder settings.
windowAttributes = EGL_WINDOW_SURFACE_ATTRIBUTES_NONE;
} else {
// TODO(b/262259999): HDR10 PQ content looks dark on the screen.
} else if (colorTransfer == C.COLOR_TRANSFER_ST2084) {
if (!isBt2020PqExtensionSupported()) {
throw new GlException("BT.2020 PQ OpenGL output isn't supported.");
}
// TODO(b/262259999): HDR10 PQ content looks dark on the screen, on API 33.
windowAttributes = EGL_WINDOW_SURFACE_ATTRIBUTES_BT2020_PQ;
} else {
if (!isBt2020HlgExtensionSupported()) {
throw new GlException("BT.2020 HLG OpenGL output isn't supported.");
}
windowAttributes = EGL_WINDOW_SURFACE_ATTRIBUTES_BT2020_HLG;
}
} else if (colorTransfer == C.COLOR_TRANSFER_HLG) {
checkArgument(isEncoderInputSurface, "Outputting HLG to the screen is not supported.");
configAttributes = EGL_CONFIG_ATTRIBUTES_RGBA_1010102;
windowAttributes = EGL_WINDOW_SURFACE_ATTRIBUTES_NONE;
} else {
throw new IllegalArgumentException("Unsupported color transfer: " + colorTransfer);
}

View File

@ -247,9 +247,8 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
*
* <p>Using HDR {@code outputColorInfo} requires OpenGL ES 3.0.
*
* <p>If outputting HDR content to a display, {@code EGL_GL_COLORSPACE_BT2020_PQ_EXT} is
* required, and {@link ColorInfo#colorTransfer outputColorInfo.colorTransfer} must be {@link
* C#COLOR_TRANSFER_ST2084}.
* <p>If outputting HDR content to a display, {@code EGL_GL_COLORSPACE_BT2020_PQ_EXT} or {@code
* EGL_GL_COLORSPACE_BT2020_HLG_EXT} is required.
*
* <p>{@code outputColorInfo}'s {@link ColorInfo#colorRange} values are currently ignored, in
* favor of {@link C#COLOR_RANGE_FULL}.
@ -678,18 +677,6 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
EGLContext eglContext =
createFocusedEglContextWithFallback(glObjectsProvider, eglDisplay, configAttributes);
// Not renderFramesAutomatically means outputting to a display surface. HDR display surfaces
// require the BT2020 PQ GL extension.
if (!renderFramesAutomatically && ColorInfo.isTransferHdr(outputColorInfo)) {
// Display hardware supports PQ only.
checkArgument(outputColorInfo.colorTransfer == C.COLOR_TRANSFER_ST2084);
if (SDK_INT < 33 || !GlUtil.isBt2020PqExtensionSupported()) {
GlUtil.destroyEglContext(eglDisplay, eglContext);
// On API<33, the system cannot display PQ content correctly regardless of whether BT2020 PQ
// GL extension is supported.
throw new VideoFrameProcessingException("BT.2020 PQ OpenGL output isn't supported.");
}
}
ColorInfo linearColorInfo =
outputColorInfo
.buildUpon()

View File

@ -588,9 +588,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@C.ColorTransfer int outputColorTransfer) {
this.eglDisplay = eglDisplay;
this.eglContext = eglContext;
// Screen output supports only BT.2020 PQ (ST2084) for HDR.
// PQ SurfaceView output is supported from API 33, but HLG output is supported from API 34.
// Therefore, convert HLG to PQ below API 34, so that HLG input can be displayed properly on
// API 33.
this.outputColorTransfer =
outputColorTransfer == C.COLOR_TRANSFER_HLG
outputColorTransfer == C.COLOR_TRANSFER_HLG && Util.SDK_INT < 34
? C.COLOR_TRANSFER_ST2084
: outputColorTransfer;
surfaceView.getHolder().addCallback(this);

View File

@ -252,8 +252,10 @@ public final class CompositingVideoSinkProvider
ColorInfo inputColorInfo = getAdjustedInputColorInfo(sourceFormat.colorInfo);
ColorInfo outputColorInfo = inputColorInfo;
if (inputColorInfo.colorTransfer == C.COLOR_TRANSFER_HLG) {
// SurfaceView only supports BT2020 PQ input. Therefore, convert HLG to PQ.
if (inputColorInfo.colorTransfer == C.COLOR_TRANSFER_HLG && Util.SDK_INT < 34) {
// PQ SurfaceView output is supported from API 33, but HLG output is supported from API 34.
// Therefore, convert HLG to PQ below API 34, so that HLG input can be displayed properly on
// API 33.
outputColorInfo =
inputColorInfo.buildUpon().setColorTransfer(C.COLOR_TRANSFER_ST2084).build();
}