diff --git a/RELEASENOTES.md b/RELEASENOTES.md index fa7db84490..7784360b60 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -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: diff --git a/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java b/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java index e8985473eb..f239f8c6d3 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java @@ -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); } diff --git a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java index 3c4f2fb85f..799cb3e3c5 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java @@ -247,9 +247,8 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor { * *

Using HDR {@code outputColorInfo} requires OpenGL ES 3.0. * - *

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}. + *

If outputting HDR content to a display, {@code EGL_GL_COLORSPACE_BT2020_PQ_EXT} or {@code + * EGL_GL_COLORSPACE_BT2020_HLG_EXT} is required. * *

{@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() diff --git a/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java b/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java index a9870c0086..b543bf7341 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java @@ -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); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java index 0ae30edae5..fdeb2e2345 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java @@ -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(); }