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 1360abf4e0..c32b5edf05 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 @@ -366,11 +366,23 @@ public final class GlUtil { return eglSurface; } + /** + * Returns the {@link EGL14#EGL_CONTEXT_CLIENT_VERSION} of the current context. + * + *
Returns {@code 0} if no {@link EGLContext} {@linkplain #createFocusedPlaceholderEglSurface + * is focused}. + */ + @RequiresApi(17) + public static long getContextMajorVersion() throws GlException { + return Api17.getContextMajorVersion(); + } + /** * Returns a newly created sync object and inserts it into the GL command stream. * - *
Returns {@code 0} if the operation failed or the {@link EGLContext} version is less than - * 3.0. + *
Returns {@code 0} if the operation failed, no {@link EGLContext} {@linkplain
+ * #createFocusedPlaceholderEglSurface is focused}, or the focused {@link EGLContext} version is
+ * less than 3.0.
*/
@RequiresApi(17)
public static long createGlSyncFence() throws GlException {
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 c129d95647..e445877310 100644
--- a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java
+++ b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java
@@ -19,6 +19,7 @@ import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.common.util.Assertions.checkStateNotNull;
+import static androidx.media3.common.util.Util.SDK_INT;
import static androidx.media3.effect.DebugTraceUtil.EVENT_VFP_RECEIVE_END_OF_INPUT;
import static androidx.media3.effect.DebugTraceUtil.EVENT_VFP_REGISTER_NEW_INPUT_STREAM;
import static androidx.media3.effect.DebugTraceUtil.EVENT_VFP_SIGNAL_ENDED;
@@ -637,18 +638,20 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
ColorInfo.isTransferHdr(outputColorInfo)
? GlUtil.EGL_CONFIG_ATTRIBUTES_RGBA_1010102
: GlUtil.EGL_CONFIG_ATTRIBUTES_RGBA_8888;
- int openGlVersion =
- ColorInfo.isTransferHdr(inputColorInfo) || ColorInfo.isTransferHdr(outputColorInfo) ? 3 : 2;
EGLContext eglContext =
- glObjectsProvider.createEglContext(eglDisplay, openGlVersion, configAttributes);
- glObjectsProvider.createFocusedPlaceholderEglSurface(eglContext, eglDisplay);
+ createFocusedEglContextWithFallback(glObjectsProvider, eglDisplay, configAttributes);
+ if ((ColorInfo.isTransferHdr(inputColorInfo) || ColorInfo.isTransferHdr(outputColorInfo))
+ && GlUtil.getContextMajorVersion() != 3) {
+ throw new VideoFrameProcessingException(
+ "OpenGL ES 3.0 context support is required for HDR input or output.");
+ }
// 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 (Util.SDK_INT < 33 || !GlUtil.isBt2020PqExtensionSupported()) {
+ 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.
@@ -882,6 +885,43 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
}
}
+ /** Creates an OpenGL ES 3.0 context if possible, and an OpenGL ES 2.0 context otherwise. */
+ private static EGLContext createFocusedEglContextWithFallback(
+ GlObjectsProvider glObjectsProvider, EGLDisplay eglDisplay, int[] configAttributes)
+ throws GlUtil.GlException {
+ if (SDK_INT < 29) {
+ return createFocusedEglContext(
+ glObjectsProvider, eglDisplay, /* openGlVersion= */ 2, configAttributes);
+ }
+
+ try {
+ return createFocusedEglContext(
+ glObjectsProvider, eglDisplay, /* openGlVersion= */ 3, configAttributes);
+ } catch (GlUtil.GlException e) {
+ return createFocusedEglContext(
+ glObjectsProvider, eglDisplay, /* openGlVersion= */ 2, configAttributes);
+ }
+ }
+
+ /**
+ * Creates an {@link EGLContext} and focus it using a {@linkplain
+ * GlObjectsProvider#createFocusedPlaceholderEglSurface placeholder EGL Surface}.
+ */
+ private static EGLContext createFocusedEglContext(
+ GlObjectsProvider glObjectsProvider,
+ EGLDisplay eglDisplay,
+ int openGlVersion,
+ int[] configAttributes)
+ throws GlUtil.GlException {
+ EGLContext eglContext =
+ glObjectsProvider.createEglContext(eglDisplay, openGlVersion, configAttributes);
+ // Some OpenGL ES 3.0 contexts returned from createEglContext may throw EGL_BAD_MATCH when being
+ // used to createFocusedPlaceHolderEglSurface, despite GL documentation suggesting the contexts,
+ // if successfully created, are valid. Check early whether the context is really valid.
+ glObjectsProvider.createFocusedPlaceholderEglSurface(eglContext, eglDisplay);
+ return eglContext;
+ }
+
private static final class InputStreamInfo {
public final @InputType int inputType;
public final List