diff --git a/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java b/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java index 735abf4cfa..793e1fd493 100644 --- a/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java +++ b/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java @@ -29,6 +29,7 @@ import android.opengl.GLUtils; import androidx.media3.common.C; import androidx.media3.common.util.GlProgram; import androidx.media3.common.util.GlUtil; +import androidx.media3.common.util.Log; import java.io.IOException; import java.util.Locale; import javax.microedition.khronos.opengles.GL10; @@ -41,6 +42,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /* package */ final class BitmapOverlayVideoProcessor implements VideoProcessingGLSurfaceView.VideoProcessor { + private static final String TAG = "BitmapOverlayVP"; private static final int OVERLAY_WIDTH = 512; private static final int OVERLAY_HEIGHT = 256; @@ -85,6 +87,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /* fragmentShaderFilePath= */ "bitmap_overlay_video_processor_fragment.glsl"); } catch (IOException e) { throw new IllegalStateException(e); + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to initialize the shader program", e); + return; } program.setBufferAttribute( "aFramePosition", @@ -119,7 +124,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; GLES20.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]); GLUtils.texSubImage2D( GL10.GL_TEXTURE_2D, /* level= */ 0, /* xoffset= */ 0, /* yoffset= */ 0, overlayBitmap); - GlUtil.checkGlError(); + try { + GlUtil.checkGlError(); + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to populate the texture", e); + } // Run the shader program. GlProgram program = checkNotNull(this.program); @@ -128,16 +137,28 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; program.setFloatUniform("uScaleX", bitmapScaleX); program.setFloatUniform("uScaleY", bitmapScaleY); program.setFloatsUniform("uTexTransform", transformMatrix); - program.bindAttributesAndUniforms(); + try { + program.bindAttributesAndUniforms(); + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to update the shader program", e); + } GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4); - GlUtil.checkGlError(); + try { + GlUtil.checkGlError(); + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to draw a frame", e); + } } @Override public void release() { if (program != null) { - program.delete(); + try { + program.delete(); + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to delete the shader program", e); + } } } } diff --git a/demos/gl/src/main/java/androidx/media3/demo/gl/VideoProcessingGLSurfaceView.java b/demos/gl/src/main/java/androidx/media3/demo/gl/VideoProcessingGLSurfaceView.java index 9c95122d4b..c7c0aba58a 100644 --- a/demos/gl/src/main/java/androidx/media3/demo/gl/VideoProcessingGLSurfaceView.java +++ b/demos/gl/src/main/java/androidx/media3/demo/gl/VideoProcessingGLSurfaceView.java @@ -28,6 +28,7 @@ import androidx.media3.common.C; import androidx.media3.common.Format; import androidx.media3.common.util.Assertions; import androidx.media3.common.util.GlUtil; +import androidx.media3.common.util.Log; import androidx.media3.common.util.TimedValueQueue; import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.exoplayer.video.VideoFrameMetadataListener; @@ -70,6 +71,7 @@ public final class VideoProcessingGLSurfaceView extends GLSurfaceView { } private static final int EGL_PROTECTED_CONTENT_EXT = 0x32C0; + private static final String TAG = "VPGlSurfaceView"; private final VideoRenderer renderer; private final Handler mainHandler; @@ -239,7 +241,11 @@ public final class VideoProcessingGLSurfaceView extends GLSurfaceView { @Override public synchronized void onSurfaceCreated(GL10 gl, EGLConfig config) { - texture = GlUtil.createExternalTexture(); + try { + texture = GlUtil.createExternalTexture(); + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to create an external texture", e); + } surfaceTexture = new SurfaceTexture(texture); surfaceTexture.setOnFrameAvailableListener( surfaceTexture -> { diff --git a/demos/transformer/src/main/java/androidx/media3/demo/transformer/BitmapOverlayProcessor.java b/demos/transformer/src/main/java/androidx/media3/demo/transformer/BitmapOverlayProcessor.java index b9c0efac37..7e0b0f5112 100644 --- a/demos/transformer/src/main/java/androidx/media3/demo/transformer/BitmapOverlayProcessor.java +++ b/demos/transformer/src/main/java/androidx/media3/demo/transformer/BitmapOverlayProcessor.java @@ -45,9 +45,6 @@ import java.util.Locale; // TODO(b/227625365): Delete this class and use a texture processor from the Transformer library, // once overlaying a bitmap and text is supported in Transformer. /* package */ final class BitmapOverlayProcessor extends SingleFrameGlTextureProcessor { - static { - GlUtil.glAssertionsEnabled = true; - } private static final String VERTEX_SHADER_PATH = "vertex_shader_copy_es2.glsl"; private static final String FRAGMENT_SHADER_PATH = "fragment_shader_bitmap_overlay_es2.glsl"; @@ -67,9 +64,9 @@ import java.util.Locale; /** * Creates a new instance. * - * @throws IOException If a problem occurs while reading shader files. + * @throws FrameProcessingException If a problem occurs while reading shader files. */ - public BitmapOverlayProcessor(Context context) throws IOException { + public BitmapOverlayProcessor(Context context) throws FrameProcessingException { paint = new Paint(); paint.setTextSize(64); paint.setAntiAlias(true); @@ -87,10 +84,14 @@ import java.util.Locale; } catch (PackageManager.NameNotFoundException e) { throw new IllegalStateException(e); } - bitmapTexId = GlUtil.createTexture(BITMAP_WIDTH_HEIGHT, BITMAP_WIDTH_HEIGHT); - GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, /* level= */ 0, overlayBitmap, /* border= */ 0); + try { + bitmapTexId = GlUtil.createTexture(BITMAP_WIDTH_HEIGHT, BITMAP_WIDTH_HEIGHT); + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, /* level= */ 0, overlayBitmap, /* border= */ 0); - glProgram = new GlProgram(context, VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH); + glProgram = new GlProgram(context, VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH); + } catch (GlUtil.GlException | IOException e) { + throw new FrameProcessingException(e); + } // Draw the frame on the entire normalized device coordinate space, from -1 to 1, for x and y. glProgram.setBufferAttribute( "aFramePosition", @@ -141,15 +142,19 @@ import java.util.Locale; GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4); GlUtil.checkGlError(); } catch (GlUtil.GlException e) { - throw new FrameProcessingException(e); + throw new FrameProcessingException(e, presentationTimeUs); } } @Override - public void release() { + public void release() throws FrameProcessingException { super.release(); if (glProgram != null) { - glProgram.delete(); + try { + glProgram.delete(); + } catch (GlUtil.GlException e) { + throw new FrameProcessingException(e); + } } } diff --git a/demos/transformer/src/main/java/androidx/media3/demo/transformer/PeriodicVignetteProcessor.java b/demos/transformer/src/main/java/androidx/media3/demo/transformer/PeriodicVignetteProcessor.java index fb00e5f888..9ae281b37b 100644 --- a/demos/transformer/src/main/java/androidx/media3/demo/transformer/PeriodicVignetteProcessor.java +++ b/demos/transformer/src/main/java/androidx/media3/demo/transformer/PeriodicVignetteProcessor.java @@ -31,9 +31,6 @@ import java.io.IOException; * darker the further they are away from the frame center. */ /* package */ final class PeriodicVignetteProcessor extends SingleFrameGlTextureProcessor { - static { - GlUtil.glAssertionsEnabled = true; - } private static final String VERTEX_SHADER_PATH = "vertex_shader_copy_es2.glsl"; private static final String FRAGMENT_SHADER_PATH = "fragment_shader_vignette_es2.glsl"; @@ -60,7 +57,7 @@ import java.io.IOException; * @param minInnerRadius The lower bound of the radius that is unaffected by the effect. * @param maxInnerRadius The upper bound of the radius that is unaffected by the effect. * @param outerRadius The radius after which all pixels are black. - * @throws IOException If a problem occurs while reading shader files. + * @throws FrameProcessingException If a problem occurs while reading shader files. */ public PeriodicVignetteProcessor( Context context, @@ -69,12 +66,16 @@ import java.io.IOException; float minInnerRadius, float maxInnerRadius, float outerRadius) - throws IOException { + throws FrameProcessingException { checkArgument(minInnerRadius <= maxInnerRadius); checkArgument(maxInnerRadius <= outerRadius); this.minInnerRadius = minInnerRadius; this.deltaInnerRadius = maxInnerRadius - minInnerRadius; - glProgram = new GlProgram(context, VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH); + try { + glProgram = new GlProgram(context, VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH); + } catch (IOException | GlUtil.GlException e) { + throw new FrameProcessingException(e); + } glProgram.setFloatsUniform("uCenter", new float[] {centerX, centerY}); glProgram.setFloatsUniform("uOuterRadius", new float[] {outerRadius}); // Draw the frame on the entire normalized device coordinate space, from -1 to 1, for x and y. @@ -102,15 +103,19 @@ import java.io.IOException; // The four-vertex triangle strip forms a quad. GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4); } catch (GlUtil.GlException e) { - throw new FrameProcessingException(e); + throw new FrameProcessingException(e, presentationTimeUs); } } @Override - public void release() { + public void release() throws FrameProcessingException { super.release(); if (glProgram != null) { - glProgram.delete(); + try { + glProgram.delete(); + } catch (GlUtil.GlException e) { + throw new FrameProcessingException(e); + } } } } diff --git a/demos/transformer/src/withMediaPipe/java/androidx/media3/demo/transformer/MediaPipeProcessor.java b/demos/transformer/src/withMediaPipe/java/androidx/media3/demo/transformer/MediaPipeProcessor.java index 7d6ddfae5b..2ceb8cf12f 100644 --- a/demos/transformer/src/withMediaPipe/java/androidx/media3/demo/transformer/MediaPipeProcessor.java +++ b/demos/transformer/src/withMediaPipe/java/androidx/media3/demo/transformer/MediaPipeProcessor.java @@ -79,12 +79,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * @param graphName Name of a MediaPipe graph asset to load. * @param inputStreamName Name of the input video stream in the graph. * @param outputStreamName Name of the input video stream in the graph. - * @throws IOException If a problem occurs while reading shader files or initializing MediaPipe - * resources. + * @throws FrameProcessingException If a problem occurs while reading shader files or initializing + * MediaPipe resources. */ public MediaPipeProcessor( Context context, String graphName, String inputStreamName, String outputStreamName) - throws IOException { + throws FrameProcessingException { checkState(LOADER.isAvailable()); frameProcessorConditionVariable = new ConditionVariable(); @@ -104,7 +104,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; frameProcessorPendingError = error; frameProcessorConditionVariable.open(); }); - glProgram = new GlProgram(context, COPY_VERTEX_SHADER_NAME, COPY_FRAGMENT_SHADER_NAME); + try { + glProgram = new GlProgram(context, COPY_VERTEX_SHADER_NAME, COPY_FRAGMENT_SHADER_NAME); + } catch (IOException | GlUtil.GlException e) { + throw new FrameProcessingException(e); + } } @Override @@ -152,14 +156,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4); GlUtil.checkGlError(); } catch (GlUtil.GlException e) { - throw new FrameProcessingException(e); + throw new FrameProcessingException(e, presentationTimeUs); } finally { checkStateNotNull(outputFrame).release(); } } @Override - public void release() { + public void release() throws FrameProcessingException { super.release(); checkStateNotNull(frameProcessor).close(); } diff --git a/libraries/common/src/main/java/androidx/media3/common/util/EGLSurfaceTexture.java b/libraries/common/src/main/java/androidx/media3/common/util/EGLSurfaceTexture.java index ee76ed476b..d764882e2e 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/EGLSurfaceTexture.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/EGLSurfaceTexture.java @@ -79,13 +79,6 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL private static final int EGL_PROTECTED_CONTENT_EXT = 0x32C0; - /** A runtime exception to be thrown if some EGL operations failed. */ - public static final class GlException extends RuntimeException { - private GlException(String msg) { - super(msg); - } - } - private final Handler handler; private final int[] textureIdHolder; @Nullable private final TextureImageListener callback; @@ -125,7 +118,7 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL * * @param secureMode The {@link SecureMode} to be used for EGL surface. */ - public void init(@SecureMode int secureMode) { + public void init(@SecureMode int secureMode) throws GlUtil.GlException { display = getDefaultDisplay(); EGLConfig config = chooseEGLConfig(display); context = createEGLContext(display, config, secureMode); @@ -206,22 +199,18 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL } } - private static EGLDisplay getDefaultDisplay() { + private static EGLDisplay getDefaultDisplay() throws GlUtil.GlException { EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); - if (display == null) { - throw new GlException("eglGetDisplay failed"); - } + GlUtil.checkGlException(display != null, "eglGetDisplay failed"); int[] version = new int[2]; boolean eglInitialized = EGL14.eglInitialize(display, version, /* majorOffset= */ 0, version, /* minorOffset= */ 1); - if (!eglInitialized) { - throw new GlException("eglInitialize failed"); - } + GlUtil.checkGlException(eglInitialized, "eglInitialize failed"); return display; } - private static EGLConfig chooseEGLConfig(EGLDisplay display) { + private static EGLConfig chooseEGLConfig(EGLDisplay display) throws GlUtil.GlException { EGLConfig[] configs = new EGLConfig[1]; int[] numConfigs = new int[1]; boolean success = @@ -234,18 +223,17 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL /* config_size= */ 1, numConfigs, /* num_configOffset= */ 0); - if (!success || numConfigs[0] <= 0 || configs[0] == null) { - throw new GlException( - Util.formatInvariant( - /* format= */ "eglChooseConfig failed: success=%b, numConfigs[0]=%d, configs[0]=%s", - success, numConfigs[0], configs[0])); - } + GlUtil.checkGlException( + success && numConfigs[0] > 0 && configs[0] != null, + Util.formatInvariant( + /* format= */ "eglChooseConfig failed: success=%b, numConfigs[0]=%d, configs[0]=%s", + success, numConfigs[0], configs[0])); return configs[0]; } private static EGLContext createEGLContext( - EGLDisplay display, EGLConfig config, @SecureMode int secureMode) { + EGLDisplay display, EGLConfig config, @SecureMode int secureMode) throws GlUtil.GlException { int[] glAttributes; if (secureMode == SECURE_MODE_NONE) { glAttributes = new int[] {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE}; @@ -262,14 +250,13 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL EGLContext context = EGL14.eglCreateContext( display, config, android.opengl.EGL14.EGL_NO_CONTEXT, glAttributes, 0); - if (context == null) { - throw new GlException("eglCreateContext failed"); - } + GlUtil.checkGlException(context != null, "eglCreateContext failed"); return context; } private static EGLSurface createEGLSurface( - EGLDisplay display, EGLConfig config, EGLContext context, @SecureMode int secureMode) { + EGLDisplay display, EGLConfig config, EGLContext context, @SecureMode int secureMode) + throws GlUtil.GlException { EGLSurface surface; if (secureMode == SECURE_MODE_SURFACELESS_CONTEXT) { surface = EGL14.EGL_NO_SURFACE; @@ -297,20 +284,16 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL }; } surface = EGL14.eglCreatePbufferSurface(display, config, pbufferAttributes, /* offset= */ 0); - if (surface == null) { - throw new GlException("eglCreatePbufferSurface failed"); - } + GlUtil.checkGlException(surface != null, "eglCreatePbufferSurface failed"); } boolean eglMadeCurrent = EGL14.eglMakeCurrent(display, /* draw= */ surface, /* read= */ surface, context); - if (!eglMadeCurrent) { - throw new GlException("eglMakeCurrent failed"); - } + GlUtil.checkGlException(eglMadeCurrent, "eglMakeCurrent failed"); return surface; } - private static void generateTextureIds(int[] textureIdHolder) { + private static void generateTextureIds(int[] textureIdHolder) throws GlUtil.GlException { GLES20.glGenTextures(/* n= */ 1, textureIdHolder, /* offset= */ 0); GlUtil.checkGlError(); } diff --git a/libraries/common/src/main/java/androidx/media3/common/util/GlProgram.java b/libraries/common/src/main/java/androidx/media3/common/util/GlProgram.java index da410f921e..63976b776d 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/GlProgram.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/GlProgram.java @@ -54,7 +54,7 @@ public final class GlProgram { * @throws IOException When failing to read shader files. */ public GlProgram(Context context, String vertexShaderFilePath, String fragmentShaderFilePath) - throws IOException { + throws IOException, GlUtil.GlException { this( GlUtil.loadAsset(context, vertexShaderFilePath), GlUtil.loadAsset(context, fragmentShaderFilePath)); @@ -69,7 +69,7 @@ public final class GlProgram { * @param vertexShaderGlsl The vertex shader program. * @param fragmentShaderGlsl The fragment shader program. */ - public GlProgram(String vertexShaderGlsl, String fragmentShaderGlsl) { + public GlProgram(String vertexShaderGlsl, String fragmentShaderGlsl) throws GlUtil.GlException { programId = GLES20.glCreateProgram(); GlUtil.checkGlError(); @@ -81,10 +81,9 @@ public final class GlProgram { GLES20.glLinkProgram(programId); int[] linkStatus = new int[] {GLES20.GL_FALSE}; GLES20.glGetProgramiv(programId, GLES20.GL_LINK_STATUS, linkStatus, /* offset= */ 0); - if (linkStatus[0] != GLES20.GL_TRUE) { - GlUtil.throwGlException( - "Unable to link shader program: \n" + GLES20.glGetProgramInfoLog(programId)); - } + GlUtil.checkGlException( + linkStatus[0] == GLES20.GL_TRUE, + "Unable to link shader program: \n" + GLES20.glGetProgramInfoLog(programId)); GLES20.glUseProgram(programId); attributeByName = new HashMap<>(); int[] attributeCount = new int[1]; @@ -107,16 +106,15 @@ public final class GlProgram { GlUtil.checkGlError(); } - private static void addShader(int programId, int type, String glsl) { + private static void addShader(int programId, int type, String glsl) throws GlUtil.GlException { int shader = GLES20.glCreateShader(type); GLES20.glShaderSource(shader, glsl); GLES20.glCompileShader(shader); int[] result = new int[] {GLES20.GL_FALSE}; GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, result, /* offset= */ 0); - if (result[0] != GLES20.GL_TRUE) { - GlUtil.throwGlException(GLES20.glGetShaderInfoLog(shader) + ", source: " + glsl); - } + GlUtil.checkGlException( + result[0] == GLES20.GL_TRUE, GLES20.glGetShaderInfoLog(shader) + ", source: " + glsl); GLES20.glAttachShader(programId, shader); GLES20.glDeleteShader(shader); @@ -146,13 +144,13 @@ public final class GlProgram { * *

Call this in the rendering loop to switch between different programs. */ - public void use() { + public void use() throws GlUtil.GlException { GLES20.glUseProgram(programId); GlUtil.checkGlError(); } /** Deletes the program. Deleted programs cannot be used again. */ - public void delete() { + public void delete() throws GlUtil.GlException { GLES20.glDeleteProgram(programId); GlUtil.checkGlError(); } @@ -161,7 +159,7 @@ public final class GlProgram { * Returns the location of an {@link Attribute}, which has been enabled as a vertex attribute * array. */ - public int getAttributeArrayLocationAndEnable(String attributeName) { + public int getAttributeArrayLocationAndEnable(String attributeName) throws GlUtil.GlException { int location = getAttributeLocation(attributeName); GLES20.glEnableVertexAttribArray(location); GlUtil.checkGlError(); @@ -196,7 +194,7 @@ public final class GlProgram { } /** Binds all attributes and uniforms in the program. */ - public void bindAttributesAndUniforms() { + public void bindAttributesAndUniforms() throws GlUtil.GlException { for (Attribute attribute : attributes) { attribute.bind(); } @@ -277,7 +275,7 @@ public final class GlProgram { * *

Should be called before each drawing call. */ - public void bind() { + public void bind() throws GlUtil.GlException { Buffer buffer = checkNotNull(this.buffer, "call setBuffer before bind"); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, /* buffer= */ 0); GLES20.glVertexAttribPointer( @@ -363,7 +361,7 @@ public final class GlProgram { * *

Should be called before each drawing call. */ - public void bind() { + public void bind() throws GlUtil.GlException { switch (type) { case GLES20.GL_FLOAT: GLES20.glUniform1fv(location, /* count= */ 1, value, /* offset= */ 0); 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 485f2db986..44f4e648fa 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 @@ -43,19 +43,14 @@ import javax.microedition.khronos.egl.EGL10; @UnstableApi public final class GlUtil { - /** Thrown when an OpenGL error occurs and {@link #glAssertionsEnabled} is {@code true}. */ - public static final class GlException extends RuntimeException { + /** Thrown when an OpenGL error occurs. */ + public static final class GlException extends Exception { /** Creates an instance with the specified error message. */ public GlException(String message) { super(message); } } - // TODO(b/231937416): Consider removing this flag, enabling assertions by default, and making - // GlException checked. - /** Whether to throw a {@link GlException} in case of an OpenGL error. */ - public static boolean glAssertionsEnabled = false; - /** Number of elements in a 3d homogeneous coordinate vector describing a vertex. */ public static final int HOMOGENEOUS_COORDINATE_VECTOR_SIZE = 4; @@ -184,13 +179,13 @@ public final class GlUtil { /** Returns an initialized default {@link EGLDisplay}. */ @RequiresApi(17) - public static EGLDisplay createEglDisplay() { + public static EGLDisplay createEglDisplay() throws GlException { return Api17.createEglDisplay(); } /** Returns a new {@link EGLContext} for the specified {@link EGLDisplay}. */ @RequiresApi(17) - public static EGLContext createEglContext(EGLDisplay eglDisplay) { + public static EGLContext createEglContext(EGLDisplay eglDisplay) throws GlException { return Api17.createEglContext(eglDisplay, /* version= */ 2, EGL_CONFIG_ATTRIBUTES_RGBA_8888); } @@ -199,7 +194,8 @@ public final class GlUtil { * RGBA 1010102 config. */ @RequiresApi(17) - public static EGLContext createEglContextEs3Rgba1010102(EGLDisplay eglDisplay) { + public static EGLContext createEglContextEs3Rgba1010102(EGLDisplay eglDisplay) + throws GlException { return Api17.createEglContext(eglDisplay, /* version= */ 3, EGL_CONFIG_ATTRIBUTES_RGBA_1010102); } @@ -210,7 +206,7 @@ public final class GlUtil { * @param surface The surface to wrap; must be a surface, surface texture or surface holder. */ @RequiresApi(17) - public static EGLSurface getEglSurface(EGLDisplay eglDisplay, Object surface) { + public static EGLSurface getEglSurface(EGLDisplay eglDisplay, Object surface) throws GlException { return Api17.getEglSurface( eglDisplay, surface, EGL_CONFIG_ATTRIBUTES_RGBA_8888, EGL_WINDOW_SURFACE_ATTRIBUTES_NONE); } @@ -223,7 +219,8 @@ public final class GlUtil { * @param surface The surface to wrap; must be a surface, surface texture or surface holder. */ @RequiresApi(17) - public static EGLSurface getEglSurfaceBt2020Pq(EGLDisplay eglDisplay, Object surface) { + public static EGLSurface getEglSurfaceBt2020Pq(EGLDisplay eglDisplay, Object surface) + throws GlException { return Api17.getEglSurface( eglDisplay, surface, @@ -239,7 +236,8 @@ public final class GlUtil { * @param height The height of the pixel buffer. */ @RequiresApi(17) - private static EGLSurface createPbufferSurface(EGLDisplay eglDisplay, int width, int height) { + private static EGLSurface createPbufferSurface(EGLDisplay eglDisplay, int width, int height) + throws GlException { int[] pbufferAttributes = new int[] { EGL14.EGL_WIDTH, width, @@ -258,7 +256,7 @@ public final class GlUtil { * @return {@link EGL14#EGL_NO_SURFACE} if supported and a 1x1 pixel buffer surface otherwise. */ @RequiresApi(17) - public static EGLSurface createPlaceholderEglSurface(EGLDisplay eglDisplay) { + public static EGLSurface createPlaceholderEglSurface(EGLDisplay eglDisplay) throws GlException { return isSurfacelessContextExtensionSupported() ? EGL14.EGL_NO_SURFACE : createPbufferSurface(eglDisplay, /* width= */ 1, /* height= */ 1); @@ -271,7 +269,8 @@ public final class GlUtil { * @param eglDisplay The {@link EGLDisplay} to attach the surface to. */ @RequiresApi(17) - public static void focusPlaceholderEglSurface(EGLContext eglContext, EGLDisplay eglDisplay) { + public static void focusPlaceholderEglSurface(EGLContext eglContext, EGLDisplay eglDisplay) + throws GlException { EGLSurface eglSurface = createPbufferSurface(eglDisplay, /* width= */ 1, /* height= */ 1); focusEglSurface(eglDisplay, eglContext, eglSurface, /* width= */ 1, /* height= */ 1); } @@ -285,7 +284,7 @@ public final class GlUtil { */ @RequiresApi(17) public static void focusPlaceholderEglSurfaceBt2020Pq( - EGLContext eglContext, EGLDisplay eglDisplay) { + EGLContext eglContext, EGLDisplay eglDisplay) throws GlException { int[] pbufferAttributes = new int[] { EGL14.EGL_WIDTH, @@ -303,10 +302,10 @@ public final class GlUtil { } /** - * If there is an OpenGl error, logs the error and if {@link #glAssertionsEnabled} is true throws - * a {@link GlException}. + * Logs all OpenGL errors that occurred since this method was last called and throws a {@link + * GlException} for the last error. */ - public static void checkGlError() { + public static void checkGlError() throws GlException { int lastError = GLES20.GL_NO_ERROR; int error; while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { @@ -314,7 +313,7 @@ public final class GlUtil { lastError = error; } if (lastError != GLES20.GL_NO_ERROR) { - throwGlException("glError: " + gluErrorString(lastError)); + throw new GlException("glError: " + gluErrorString(lastError)); } } @@ -325,7 +324,7 @@ public final class GlUtil { * @param height The height for a texture. * @throws GlException If the texture width or height is invalid. */ - public static void assertValidTextureSize(int width, int height) { + public static void assertValidTextureSize(int width, int height) throws GlException { // TODO(b/201293185): Consider handling adjustments for sizes > GL_MAX_TEXTURE_SIZE // (ex. downscaling appropriately) in a texture processor instead of asserting incorrect // values. @@ -336,15 +335,16 @@ public final class GlUtil { GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSizeBuffer, 0); int maxTextureSize = maxTextureSizeBuffer[0]; if (width < 0 || height < 0) { - throwGlException("width or height is less than 0"); + throw new GlException("width or height is less than 0"); } if (width > maxTextureSize || height > maxTextureSize) { - throwGlException("width or height is greater than GL_MAX_TEXTURE_SIZE " + maxTextureSize); + throw new GlException( + "width or height is greater than GL_MAX_TEXTURE_SIZE " + maxTextureSize); } } /** Fills the pixels in the current output render target with (r=0, g=0, b=0, a=0). */ - public static void clearOutputFrame() { + public static void clearOutputFrame() throws GlException { GLES20.glClearColor(/* red= */ 0, /* green= */ 0, /* blue= */ 0, /* alpha= */ 0); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); GlUtil.checkGlError(); @@ -356,7 +356,8 @@ public final class GlUtil { */ @RequiresApi(17) public static void focusEglSurface( - EGLDisplay eglDisplay, EGLContext eglContext, EGLSurface eglSurface, int width, int height) { + EGLDisplay eglDisplay, EGLContext eglContext, EGLSurface eglSurface, int width, int height) + throws GlException { Api17.focusRenderTarget( eglDisplay, eglContext, eglSurface, /* framebuffer= */ 0, width, height); } @@ -372,7 +373,8 @@ public final class GlUtil { EGLSurface eglSurface, int framebuffer, int width, - int height) { + int height) + throws GlException { Api17.focusRenderTarget(eglDisplay, eglContext, eglSurface, framebuffer, width, height); } @@ -388,7 +390,8 @@ public final class GlUtil { * @param height The viewport height, in pixels. */ @RequiresApi(17) - public static void focusFramebufferUsingCurrentContext(int framebuffer, int width, int height) { + public static void focusFramebufferUsingCurrentContext(int framebuffer, int width, int height) + throws GlException { Api17.focusFramebufferUsingCurrentContext(framebuffer, width, height); } @@ -397,7 +400,7 @@ public final class GlUtil { * * @param textureId The ID of the texture to delete. */ - public static void deleteTexture(int textureId) { + public static void deleteTexture(int textureId) throws GlException { GLES20.glDeleteTextures(/* n= */ 1, new int[] {textureId}, /* offset= */ 0); checkGlError(); } @@ -408,7 +411,7 @@ public final class GlUtil { */ @RequiresApi(17) public static void destroyEglContext( - @Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) { + @Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) throws GlException { Api17.destroyEglContext(eglDisplay, eglContext); } @@ -453,7 +456,7 @@ public final class GlUtil { * Creates a GL_TEXTURE_EXTERNAL_OES with default configuration of GL_LINEAR filtering and * GL_CLAMP_TO_EDGE wrapping. */ - public static int createExternalTexture() { + public static int createExternalTexture() throws GlException { int texId = generateTexture(); bindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId); return texId; @@ -465,7 +468,7 @@ public final class GlUtil { * @param width of the new texture in pixels * @param height of the new texture in pixels */ - public static int createTexture(int width, int height) { + public static int createTexture(int width, int height) throws GlException { assertValidTextureSize(width, height); int texId = generateTexture(); bindTexture(GLES20.GL_TEXTURE_2D, texId); @@ -485,8 +488,8 @@ public final class GlUtil { } /** Returns a new GL texture identifier. */ - private static int generateTexture() { - checkEglException( + private static int generateTexture() throws GlException { + checkGlException( !Util.areEqual(EGL14.eglGetCurrentContext(), EGL14.EGL_NO_CONTEXT), "No current context"); int[] texId = new int[1]; @@ -504,7 +507,7 @@ public final class GlUtil { * GLES20#GL_TEXTURE_2D} for a two-dimensional texture or {@link * GLES11Ext#GL_TEXTURE_EXTERNAL_OES} for an external texture. */ - public static void bindTexture(int textureTarget, int texId) { + public static void bindTexture(int textureTarget, int texId) throws GlException { GLES20.glBindTexture(textureTarget, texId); checkGlError(); GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); @@ -522,8 +525,8 @@ public final class GlUtil { * * @param texId The identifier of the texture to attach to the framebuffer. */ - public static int createFboForTexture(int texId) { - checkEglException( + public static int createFboForTexture(int texId) throws GlException { + checkGlException( !Util.areEqual(EGL14.eglGetCurrentContext(), EGL14.EGL_NO_CONTEXT), "No current context"); int[] fboId = new int[1]; @@ -537,23 +540,19 @@ public final class GlUtil { return fboId[0]; } - /* package */ static void throwGlException(String errorMsg) { - if (glAssertionsEnabled) { - throw new GlException(errorMsg); - } else { - Log.e(TAG, errorMsg); - } - } - - private static void checkEglException(boolean expression, String errorMessage) { + /** + * Throws a {@link GlException} with the given message if {@code expression} evaluates to {@code + * false}. + */ + public static void checkGlException(boolean expression, String errorMessage) throws GlException { if (!expression) { - throwGlException(errorMessage); + throw new GlException(errorMessage); } } - private static void checkEglException(String errorMessage) { + private static void checkEglException(String errorMessage) throws GlException { int error = EGL14.eglGetError(); - checkEglException(error == EGL14.EGL_SUCCESS, errorMessage + ", error code: " + error); + checkGlException(error == EGL14.EGL_SUCCESS, errorMessage + ", error code: " + error); } @RequiresApi(17) @@ -561,24 +560,24 @@ public final class GlUtil { private Api17() {} @DoNotInline - public static EGLDisplay createEglDisplay() { + public static EGLDisplay createEglDisplay() throws GlException { EGLDisplay eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); - checkEglException(!eglDisplay.equals(EGL14.EGL_NO_DISPLAY), "No EGL display."); - if (!EGL14.eglInitialize( - eglDisplay, - /* unusedMajor */ new int[1], - /* majorOffset= */ 0, - /* unusedMinor */ new int[1], - /* minorOffset= */ 0)) { - throwGlException("Error in eglInitialize."); - } + checkGlException(!eglDisplay.equals(EGL14.EGL_NO_DISPLAY), "No EGL display."); + checkGlException( + EGL14.eglInitialize( + eglDisplay, + /* unusedMajor */ new int[1], + /* majorOffset= */ 0, + /* unusedMinor */ new int[1], + /* minorOffset= */ 0), + "Error in eglInitialize."); checkGlError(); return eglDisplay; } @DoNotInline public static EGLContext createEglContext( - EGLDisplay eglDisplay, int version, int[] configAttributes) { + EGLDisplay eglDisplay, int version, int[] configAttributes) throws GlException { int[] contextAttributes = {EGL14.EGL_CONTEXT_CLIENT_VERSION, version, EGL14.EGL_NONE}; EGLContext eglContext = EGL14.eglCreateContext( @@ -589,7 +588,7 @@ public final class GlUtil { /* offset= */ 0); if (eglContext == null) { EGL14.eglTerminate(eglDisplay); - throwGlException( + throw new GlException( "eglCreateContext() failed to create a valid context. The device may not support EGL" + " version " + version); @@ -603,7 +602,8 @@ public final class GlUtil { EGLDisplay eglDisplay, Object surface, int[] configAttributes, - int[] windowSurfaceAttributes) { + int[] windowSurfaceAttributes) + throws GlException { EGLSurface eglSurface = EGL14.eglCreateWindowSurface( eglDisplay, @@ -617,7 +617,7 @@ public final class GlUtil { @DoNotInline public static EGLSurface createEglPbufferSurface( - EGLDisplay eglDisplay, int[] configAttributes, int[] pbufferAttributes) { + EGLDisplay eglDisplay, int[] configAttributes, int[] pbufferAttributes) throws GlException { EGLSurface eglSurface = EGL14.eglCreatePbufferSurface( eglDisplay, @@ -635,15 +635,17 @@ public final class GlUtil { EGLSurface eglSurface, int framebuffer, int width, - int height) { + int height) + throws GlException { EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext); checkEglException("Error making context current"); focusFramebufferUsingCurrentContext(framebuffer, width, height); } @DoNotInline - public static void focusFramebufferUsingCurrentContext(int framebuffer, int width, int height) { - checkEglException( + public static void focusFramebufferUsingCurrentContext(int framebuffer, int width, int height) + throws GlException { + checkGlException( !Util.areEqual(EGL14.eglGetCurrentContext(), EGL14.EGL_NO_CONTEXT), "No current context"); int[] boundFramebuffer = new int[1]; @@ -658,7 +660,7 @@ public final class GlUtil { @DoNotInline public static void destroyEglContext( - @Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) { + @Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) throws GlException { if (eglDisplay == null) { return; } @@ -676,7 +678,8 @@ public final class GlUtil { } @DoNotInline - private static EGLConfig getEglConfig(EGLDisplay eglDisplay, int[] attributes) { + private static EGLConfig getEglConfig(EGLDisplay eglDisplay, int[] attributes) + throws GlException { EGLConfig[] eglConfigs = new EGLConfig[1]; if (!EGL14.eglChooseConfig( eglDisplay, @@ -687,7 +690,7 @@ public final class GlUtil { /* config_size= */ 1, /* unusedNumConfig */ new int[1], /* num_configOffset= */ 0)) { - throwGlException("eglChooseConfig failed."); + throw new GlException("eglChooseConfig failed."); } return eglConfigs[0]; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/PlaceholderSurface.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/PlaceholderSurface.java index e9d1955435..5ee73d52f6 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/PlaceholderSurface.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/PlaceholderSurface.java @@ -179,6 +179,9 @@ public final class PlaceholderSurface extends Surface { } catch (RuntimeException e) { Log.e(TAG, "Failed to initialize placeholder surface", e); initException = e; + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to initialize placeholder surface", e); + initException = new IllegalStateException(e); } catch (Error e) { Log.e(TAG, "Failed to initialize placeholder surface", e); initError = e; @@ -202,7 +205,7 @@ public final class PlaceholderSurface extends Surface { } } - private void initInternal(@SecureMode int secureMode) { + private void initInternal(@SecureMode int secureMode) throws GlUtil.GlException { Assertions.checkNotNull(eglSurfaceTexture); eglSurfaceTexture.init(secureMode); this.surface = diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoDecoderGLSurfaceView.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoDecoderGLSurfaceView.java index 66e5ad93fb..9ffbf37ae4 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoDecoderGLSurfaceView.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoDecoderGLSurfaceView.java @@ -21,6 +21,7 @@ import android.content.Context; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.util.AttributeSet; +import android.util.Log; import androidx.annotation.Nullable; import androidx.media3.common.util.Assertions; import androidx.media3.common.util.GlProgram; @@ -48,6 +49,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; public final class VideoDecoderGLSurfaceView extends GLSurfaceView implements VideoDecoderOutputBufferRenderer { + private static final String TAG = "VideoDecoderGLSV"; + private final Renderer renderer; /** @@ -172,22 +175,26 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView @Override public void onSurfaceCreated(GL10 unused, EGLConfig config) { - program = new GlProgram(VERTEX_SHADER, FRAGMENT_SHADER); - int posLocation = program.getAttributeArrayLocationAndEnable("in_pos"); - GLES20.glVertexAttribPointer( - posLocation, - 2, - GLES20.GL_FLOAT, - /* normalized= */ false, - /* stride= */ 0, - TEXTURE_VERTICES); - texLocations[0] = program.getAttributeArrayLocationAndEnable("in_tc_y"); - texLocations[1] = program.getAttributeArrayLocationAndEnable("in_tc_u"); - texLocations[2] = program.getAttributeArrayLocationAndEnable("in_tc_v"); - colorMatrixLocation = program.getUniformLocation("mColorConversion"); - GlUtil.checkGlError(); - setupTextures(); - GlUtil.checkGlError(); + try { + program = new GlProgram(VERTEX_SHADER, FRAGMENT_SHADER); + int posLocation = program.getAttributeArrayLocationAndEnable("in_pos"); + GLES20.glVertexAttribPointer( + posLocation, + 2, + GLES20.GL_FLOAT, + /* normalized= */ false, + /* stride= */ 0, + TEXTURE_VERTICES); + texLocations[0] = program.getAttributeArrayLocationAndEnable("in_tc_y"); + texLocations[1] = program.getAttributeArrayLocationAndEnable("in_tc_u"); + texLocations[2] = program.getAttributeArrayLocationAndEnable("in_tc_v"); + colorMatrixLocation = program.getUniformLocation("mColorConversion"); + GlUtil.checkGlError(); + setupTextures(); + GlUtil.checkGlError(); + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to set up the textures and program", e); + } } @Override @@ -284,7 +291,11 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4); - GlUtil.checkGlError(); + try { + GlUtil.checkGlError(); + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to draw a frame", e); + } } public void setOutputBuffer(VideoDecoderOutputBuffer outputBuffer) { @@ -300,13 +311,17 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView @RequiresNonNull("program") private void setupTextures() { - GLES20.glGenTextures(/* n= */ 3, yuvTextures, /* offset= */ 0); - for (int i = 0; i < 3; i++) { - GLES20.glUniform1i(program.getUniformLocation(TEXTURE_UNIFORMS[i]), i); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i); - GlUtil.bindTexture(GLES20.GL_TEXTURE_2D, yuvTextures[i]); + try { + GLES20.glGenTextures(/* n= */ 3, yuvTextures, /* offset= */ 0); + for (int i = 0; i < 3; i++) { + GLES20.glUniform1i(program.getUniformLocation(TEXTURE_UNIFORMS[i]), i); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i); + GlUtil.bindTexture(GLES20.GL_TEXTURE_2D, yuvTextures[i]); + } + GlUtil.checkGlError(); + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to set up the textures", e); } - GlUtil.checkGlError(); } } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionRenderer.java index 2503733f36..5c808a8890 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionRenderer.java @@ -19,6 +19,7 @@ import static androidx.media3.common.util.GlUtil.checkGlError; import android.opengl.GLES11Ext; import android.opengl.GLES20; +import android.util.Log; import androidx.annotation.Nullable; import androidx.media3.common.C; import androidx.media3.common.util.GlProgram; @@ -45,6 +46,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; && rightMesh.getSubMesh(0).textureId == Projection.SubMesh.VIDEO_TEXTURE_ID; } + private static final String TAG = "ProjectionRenderer"; + // Basic vertex & fragment shaders to render a mesh with 3D position & 2D texture data. private static final String VERTEX_SHADER = "uniform mat4 uMvpMatrix;\n" @@ -115,12 +118,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** Initializes of the GL components. */ public void init() { - program = new GlProgram(VERTEX_SHADER, FRAGMENT_SHADER); - mvpMatrixHandle = program.getUniformLocation("uMvpMatrix"); - uTexMatrixHandle = program.getUniformLocation("uTexMatrix"); - positionHandle = program.getAttributeArrayLocationAndEnable("aPosition"); - texCoordsHandle = program.getAttributeArrayLocationAndEnable("aTexCoords"); - textureHandle = program.getUniformLocation("uTexture"); + try { + program = new GlProgram(VERTEX_SHADER, FRAGMENT_SHADER); + mvpMatrixHandle = program.getUniformLocation("uMvpMatrix"); + uTexMatrixHandle = program.getUniformLocation("uTexMatrix"); + positionHandle = program.getAttributeArrayLocationAndEnable("aPosition"); + texCoordsHandle = program.getAttributeArrayLocationAndEnable("aTexCoords"); + textureHandle = program.getUniformLocation("uTexture"); + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to initialize the program", e); + } } /** @@ -154,7 +161,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId); GLES20.glUniform1i(textureHandle, 0); - checkGlError(); + try { + checkGlError(); + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to bind uniforms", e); + } // Load position data. GLES20.glVertexAttribPointer( @@ -164,7 +175,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; false, Projection.POSITION_COORDS_PER_VERTEX * C.BYTES_PER_FLOAT, meshData.vertexBuffer); - checkGlError(); + try { + checkGlError(); + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to load position data", e); + } // Load texture data. GLES20.glVertexAttribPointer( @@ -174,17 +189,29 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; false, Projection.TEXTURE_COORDS_PER_VERTEX * C.BYTES_PER_FLOAT, meshData.textureBuffer); - checkGlError(); + try { + checkGlError(); + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to load texture data", e); + } // Render. GLES20.glDrawArrays(meshData.drawMode, /* first= */ 0, meshData.vertexCount); - checkGlError(); + try { + checkGlError(); + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to render", e); + } } /** Cleans up GL resources. */ public void shutdown() { if (program != null) { - program.delete(); + try { + program.delete(); + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to delete the shader program", e); + } } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/SceneRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/SceneRenderer.java index b2828a465e..5d262acd3e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/SceneRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/SceneRenderer.java @@ -26,6 +26,7 @@ import androidx.media3.common.C; import androidx.media3.common.Format; import androidx.media3.common.util.Assertions; import androidx.media3.common.util.GlUtil; +import androidx.media3.common.util.Log; import androidx.media3.common.util.TimedValueQueue; import androidx.media3.exoplayer.video.VideoFrameMetadataListener; import java.util.Arrays; @@ -36,6 +37,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /* package */ final class SceneRenderer implements VideoFrameMetadataListener, CameraMotionListener { + private static final String TAG = "SceneRenderer"; + private final AtomicBoolean frameAvailable; private final AtomicBoolean resetRotationAtNextFrame; private final ProjectionRenderer projectionRenderer; @@ -83,14 +86,19 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** Initializes the renderer. */ public SurfaceTexture init() { - // Set the background frame color. This is only visible if the display mesh isn't a full sphere. - GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); - checkGlError(); + try { + // Set the background frame color. This is only visible if the display mesh isn't a full + // sphere. + GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); + checkGlError(); - projectionRenderer.init(); - checkGlError(); + projectionRenderer.init(); + checkGlError(); - textureId = GlUtil.createExternalTexture(); + textureId = GlUtil.createExternalTexture(); + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to initialize the renderer", e); + } surfaceTexture = new SurfaceTexture(textureId); surfaceTexture.setOnFrameAvailableListener(surfaceTexture -> frameAvailable.set(true)); return surfaceTexture; @@ -107,11 +115,19 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; // glClear isn't strictly necessary when rendering fully spherical panoramas, but it can improve // performance on tiled renderers by causing the GPU to discard previous data. GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); - checkGlError(); + try { + checkGlError(); + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to draw a frame", e); + } if (frameAvailable.compareAndSet(true, false)) { Assertions.checkNotNull(surfaceTexture).updateTexImage(); - checkGlError(); + try { + checkGlError(); + } catch (GlUtil.GlException e) { + Log.e(TAG, "Failed to draw a frame", e); + } if (resetRotationAtNextFrame.compareAndSet(true, false)) { Matrix.setIdentityM(rotationMatrix, 0); } diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/BitmapTestUtil.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/BitmapTestUtil.java index fda61200d1..2f38910bd8 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/BitmapTestUtil.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/BitmapTestUtil.java @@ -186,7 +186,8 @@ public class BitmapTestUtil { * @param height The height of the pixel rectangle to read. * @return A {@link Bitmap} with the framebuffer's values. */ - public static Bitmap createArgb8888BitmapFromCurrentGlFramebuffer(int width, int height) { + public static Bitmap createArgb8888BitmapFromCurrentGlFramebuffer(int width, int height) + throws GlUtil.GlException { ByteBuffer rgba8888Buffer = ByteBuffer.allocateDirect(width * height * 4); GLES20.glReadPixels( 0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, rgba8888Buffer); @@ -206,7 +207,7 @@ public class BitmapTestUtil { * @param bitmap A {@link Bitmap}. * @return The identifier of the newly created texture. */ - public static int createGlTextureFromBitmap(Bitmap bitmap) { + public static int createGlTextureFromBitmap(Bitmap bitmap) throws GlUtil.GlException { int texId = GlUtil.createTexture(bitmap.getWidth(), bitmap.getHeight()); // Put the flipped bitmap in the OpenGL texture as the bitmap's positive y-axis points down // while OpenGL's positive y-axis points up. diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/CropPixelTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/CropPixelTest.java index f4830f1ab1..b0de6571fb 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/CropPixelTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/CropPixelTest.java @@ -52,13 +52,9 @@ public final class CropPixelTest { public static final String CROP_LARGER_PNG_ASSET_PATH = "media/bitmap/sample_mp4_first_frame/crop_larger.png"; - static { - GlUtil.glAssertionsEnabled = true; - } - - private final Context context = getApplicationContext(); - private final EGLDisplay eglDisplay = GlUtil.createEglDisplay(); - private final EGLContext eglContext = GlUtil.createEglContext(eglDisplay); + private Context context = getApplicationContext(); + private @MonotonicNonNull EGLDisplay eglDisplay; + private @MonotonicNonNull EGLContext eglContext; private @MonotonicNonNull SingleFrameGlTextureProcessor cropTextureProcessor; private @MonotonicNonNull EGLSurface placeholderEglSurface; private int inputTexId; @@ -67,7 +63,9 @@ public final class CropPixelTest { private int inputHeight; @Before - public void createTextures() throws IOException { + public void createGlObjects() throws IOException, GlUtil.GlException { + eglDisplay = GlUtil.createEglDisplay(); + eglContext = GlUtil.createEglContext(eglDisplay); Bitmap inputBitmap = BitmapTestUtil.readBitmap(ORIGINAL_PNG_ASSET_PATH); inputWidth = inputBitmap.getWidth(); inputHeight = inputBitmap.getHeight(); @@ -77,11 +75,13 @@ public final class CropPixelTest { } @After - public void release() { + public void release() throws GlUtil.GlException, FrameProcessingException { if (cropTextureProcessor != null) { cropTextureProcessor.release(); } - GlUtil.destroyEglContext(eglDisplay, eglContext); + if (eglContext != null && eglDisplay != null) { + GlUtil.destroyEglContext(eglDisplay, eglContext); + } } @Test @@ -156,12 +156,12 @@ public final class CropPixelTest { assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE); } - private void setupOutputTexture(int outputWidth, int outputHeight) { + private void setupOutputTexture(int outputWidth, int outputHeight) throws GlUtil.GlException { outputTexId = GlUtil.createTexture(outputWidth, outputHeight); int frameBuffer = GlUtil.createFboForTexture(outputTexId); GlUtil.focusFramebuffer( - eglDisplay, - eglContext, + checkNotNull(eglDisplay), + checkNotNull(eglContext), checkNotNull(placeholderEglSurface), frameBuffer, outputWidth, diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/MatrixTransformationProcessorPixelTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/MatrixTransformationProcessorPixelTest.java index f25c09a16b..3255c093cb 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/MatrixTransformationProcessorPixelTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/MatrixTransformationProcessorPixelTest.java @@ -53,13 +53,9 @@ public final class MatrixTransformationProcessorPixelTest { public static final String ROTATE_90_PNG_ASSET_PATH = "media/bitmap/sample_mp4_first_frame/rotate90.png"; - static { - GlUtil.glAssertionsEnabled = true; - } - private final Context context = getApplicationContext(); - private final EGLDisplay eglDisplay = GlUtil.createEglDisplay(); - private final EGLContext eglContext = GlUtil.createEglContext(eglDisplay); + private @MonotonicNonNull EGLDisplay eglDisplay; + private @MonotonicNonNull EGLContext eglContext; private @MonotonicNonNull SingleFrameGlTextureProcessor matrixTransformationFrameProcessor; private int inputTexId; private int outputTexId; @@ -67,7 +63,9 @@ public final class MatrixTransformationProcessorPixelTest { private int height; @Before - public void createTextures() throws IOException { + public void createGlObjects() throws IOException, GlUtil.GlException { + eglDisplay = GlUtil.createEglDisplay(); + eglContext = GlUtil.createEglContext(eglDisplay); Bitmap inputBitmap = BitmapTestUtil.readBitmap(ORIGINAL_PNG_ASSET_PATH); width = inputBitmap.getWidth(); height = inputBitmap.getHeight(); @@ -81,11 +79,13 @@ public final class MatrixTransformationProcessorPixelTest { } @After - public void release() { + public void release() throws GlUtil.GlException, FrameProcessingException { if (matrixTransformationFrameProcessor != null) { matrixTransformationFrameProcessor.release(); } - GlUtil.destroyEglContext(eglDisplay, eglContext); + if (eglContext != null && eglDisplay != null) { + GlUtil.destroyEglContext(eglDisplay, eglContext); + } } @Test diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/PresentationPixelTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/PresentationPixelTest.java index 9465cd631c..867aed28ff 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/PresentationPixelTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/PresentationPixelTest.java @@ -60,13 +60,9 @@ public final class PresentationPixelTest { public static final String ASPECT_RATIO_STRETCH_TO_FIT_WIDE_PNG_ASSET_PATH = "media/bitmap/sample_mp4_first_frame/aspect_ratio_stretch_to_fit_wide.png"; - static { - GlUtil.glAssertionsEnabled = true; - } - - private final Context context = getApplicationContext(); - private final EGLDisplay eglDisplay = GlUtil.createEglDisplay(); - private final EGLContext eglContext = GlUtil.createEglContext(eglDisplay); + private Context context = getApplicationContext(); + private @MonotonicNonNull EGLDisplay eglDisplay; + private @MonotonicNonNull EGLContext eglContext; private @MonotonicNonNull SingleFrameGlTextureProcessor presentationTextureProcessor; private @MonotonicNonNull EGLSurface placeholderEglSurface; private int inputTexId; @@ -75,7 +71,9 @@ public final class PresentationPixelTest { private int inputHeight; @Before - public void createTextures() throws IOException { + public void createGlObjects() throws IOException, GlUtil.GlException { + eglDisplay = GlUtil.createEglDisplay(); + eglContext = GlUtil.createEglContext(eglDisplay); Bitmap inputBitmap = BitmapTestUtil.readBitmap(ORIGINAL_PNG_ASSET_PATH); inputWidth = inputBitmap.getWidth(); inputHeight = inputBitmap.getHeight(); @@ -85,11 +83,13 @@ public final class PresentationPixelTest { } @After - public void release() { + public void release() throws GlUtil.GlException, FrameProcessingException { if (presentationTextureProcessor != null) { presentationTextureProcessor.release(); } - GlUtil.destroyEglContext(eglDisplay, eglContext); + if (eglContext != null && eglDisplay != null) { + GlUtil.destroyEglContext(eglDisplay, eglContext); + } } @Test @@ -282,12 +282,12 @@ public final class PresentationPixelTest { assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE); } - private void setupOutputTexture(int outputWidth, int outputHeight) { + private void setupOutputTexture(int outputWidth, int outputHeight) throws GlUtil.GlException { outputTexId = GlUtil.createTexture(outputWidth, outputHeight); int frameBuffer = GlUtil.createFboForTexture(outputTexId); GlUtil.focusFramebuffer( - eglDisplay, - eglContext, + checkNotNull(eglDisplay), + checkNotNull(eglContext), checkNotNull(placeholderEglSurface), frameBuffer, outputWidth, diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Crop.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Crop.java index 511a5cf8c9..20f83f105a 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Crop.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Crop.java @@ -32,10 +32,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @UnstableApi public final class Crop implements MatrixTransformation { - static { - GlUtil.glAssertionsEnabled = true; - } - private final float left; private final float right; private final float bottom; diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderCompatibilityTransformation.java b/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderCompatibilityTransformation.java index d12e1b5f82..05ff98fe0a 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderCompatibilityTransformation.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderCompatibilityTransformation.java @@ -23,7 +23,6 @@ import android.graphics.Matrix; import android.util.Size; import androidx.media3.common.C; import androidx.media3.common.Format; -import androidx.media3.common.util.GlUtil; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** @@ -38,10 +37,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; // TODO(b/218488308): Allow reconfiguration of the output size, as encoders may not support the // requested output resolution. - static { - GlUtil.glAssertionsEnabled = true; - } - private int outputRotationDegrees; private @MonotonicNonNull Matrix transformationMatrix; diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/ExternalTextureProcessor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/ExternalTextureProcessor.java index 823b883668..693f5533bc 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/ExternalTextureProcessor.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/ExternalTextureProcessor.java @@ -28,10 +28,6 @@ import java.io.IOException; /** Copies frames from an external texture and applies color transformations for HDR if needed. */ /* package */ class ExternalTextureProcessor extends SingleFrameGlTextureProcessor { - static { - GlUtil.glAssertionsEnabled = true; - } - private static final String VERTEX_SHADER_TEX_TRANSFORM_PATH = "shaders/vertex_shader_tex_transform_es2.glsl"; private static final String VERTEX_SHADER_TEX_TRANSFORM_ES3_PATH = @@ -54,10 +50,10 @@ import java.io.IOException; * Creates a new instance. * * @param enableExperimentalHdrEditing Whether to attempt to process the input as an HDR signal. - * @throws IOException If a problem occurs while reading shader files. + * @throws FrameProcessingException If a problem occurs while reading shader files. */ public ExternalTextureProcessor(Context context, boolean enableExperimentalHdrEditing) - throws IOException { + throws FrameProcessingException { String vertexShaderFilePath = enableExperimentalHdrEditing ? VERTEX_SHADER_TEX_TRANSFORM_ES3_PATH @@ -66,7 +62,11 @@ import java.io.IOException; enableExperimentalHdrEditing ? FRAGMENT_SHADER_COPY_EXTERNAL_YUV_ES3_PATH : FRAGMENT_SHADER_COPY_EXTERNAL_PATH; - glProgram = new GlProgram(context, vertexShaderFilePath, fragmentShaderFilePath); + try { + glProgram = new GlProgram(context, vertexShaderFilePath, fragmentShaderFilePath); + } catch (IOException | GlUtil.GlException e) { + throw new FrameProcessingException(e); + } // Draw the frame on the entire normalized device coordinate space, from -1 to 1, for x and y. glProgram.setBufferAttribute( "aFramePosition", @@ -109,15 +109,19 @@ import java.io.IOException; GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4); GlUtil.checkGlError(); } catch (GlUtil.GlException e) { - throw new FrameProcessingException(e); + throw new FrameProcessingException(e, presentationTimeUs); } } @Override - public void release() { + public void release() throws FrameProcessingException { super.release(); if (glProgram != null) { - glProgram.delete(); + try { + glProgram.delete(); + } catch (GlUtil.GlException e) { + throw new FrameProcessingException(e); + } } } } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameProcessingException.java b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameProcessingException.java index 6d413fd38a..1d1d5d47ed 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameProcessingException.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameProcessingException.java @@ -22,6 +22,26 @@ import androidx.media3.common.util.UnstableApi; @UnstableApi public final class FrameProcessingException extends Exception { + /** + * Wraps the given exception in a {@code FrameProcessingException} if it is not already a {@code + * FrameProcessingException} and returns the exception otherwise. + */ + public static FrameProcessingException from(Exception exception) { + return from(exception, /* presentationTimeUs= */ C.TIME_UNSET); + } + + /** + * Wraps the given exception in a {@code FrameProcessingException} with the given timestamp if it + * is not already a {@code FrameProcessingException} and returns the exception otherwise. + */ + public static FrameProcessingException from(Exception exception, long presentationTimeUs) { + if (exception instanceof FrameProcessingException) { + return (FrameProcessingException) exception; + } else { + return new FrameProcessingException(exception, presentationTimeUs); + } + } + /** * The microsecond timestamp of the frame being processed while the exception occurred or {@link * C#TIME_UNSET} if unknown. diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameProcessorChain.java b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameProcessorChain.java index c4ded83fd9..0b2bea5a34 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameProcessorChain.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameProcessorChain.java @@ -41,7 +41,6 @@ import androidx.media3.common.util.GlUtil; import androidx.media3.common.util.Log; import androidx.media3.common.util.Util; import com.google.common.collect.ImmutableList; -import java.io.IOException; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; @@ -65,10 +64,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; // TODO(b/227625423): Factor out FrameProcessor interface and rename this class to GlFrameProcessor. /* package */ final class FrameProcessorChain { - static { - GlUtil.glAssertionsEnabled = true; - } - /** * Listener for asynchronous frame processing events. * @@ -150,7 +145,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; List effects, boolean enableExperimentalHdrEditing, ExecutorService singleThreadExecutorService) - throws IOException { + throws GlUtil.GlException, FrameProcessingException { checkState(Thread.currentThread().getName().equals(THREAD_NAME)); EGLDisplay eglDisplay = GlUtil.createEglDisplay(); @@ -204,7 +199,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ExternalTextureProcessor externalTextureProcessor, float pixelWidthHeightRatio, List effects) - throws IOException { + throws FrameProcessingException { ImmutableList.Builder textureProcessors = new ImmutableList.Builder().add(externalTextureProcessor); @@ -532,22 +527,19 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; int finalInputTexId = inputTexId; debugSurfaceViewWrapper.maybeRenderToSurfaceView( () -> { - GlUtil.clearOutputFrame(); try { + GlUtil.clearOutputFrame(); getLast(textureProcessors).drawFrame(finalInputTexId, finalPresentationTimeUs); - } catch (FrameProcessingException e) { + } catch (GlUtil.GlException | FrameProcessingException e) { Log.d(TAG, "Error rendering to debug preview", e); } }); } checkState(pendingFrameCount.getAndDecrement() > 0); - } catch (FrameProcessingException | RuntimeException e) { + } catch (FrameProcessingException | GlUtil.GlException | RuntimeException e) { if (!stopProcessing.getAndSet(true)) { - listener.onFrameProcessingError( - e instanceof FrameProcessingException - ? (FrameProcessingException) e - : new FrameProcessingException(e, presentationTimeUs)); + listener.onFrameProcessingError(FrameProcessingException.from(e, presentationTimeUs)); } } } @@ -565,8 +557,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; textureProcessors.get(i).release(); } GlUtil.destroyEglContext(eglDisplay, eglContext); - } catch (RuntimeException e) { - listener.onFrameProcessingError(new FrameProcessingException(e)); + } catch (FrameProcessingException | GlUtil.GlException | RuntimeException e) { + listener.onFrameProcessingError(FrameProcessingException.from(e)); } } @@ -600,7 +592,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * otherwise. */ @WorkerThread - public synchronized void maybeRenderToSurfaceView(Runnable renderRunnable) { + public synchronized void maybeRenderToSurfaceView(Runnable renderRunnable) + throws GlUtil.GlException { if (surface == null) { return; } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/GlEffect.java b/libraries/transformer/src/main/java/androidx/media3/transformer/GlEffect.java index fa64cb780d..10a587a026 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/GlEffect.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/GlEffect.java @@ -17,7 +17,6 @@ package androidx.media3.transformer; import android.content.Context; import androidx.media3.common.util.UnstableApi; -import java.io.IOException; /** * Interface for a video frame effect with a {@link SingleFrameGlTextureProcessor} implementation. @@ -31,5 +30,6 @@ public interface GlEffect { /** Returns a {@link SingleFrameGlTextureProcessor} that applies the effect. */ // TODO(b/227625423): use GlTextureProcessor here once this interface exists. - SingleFrameGlTextureProcessor toGlTextureProcessor(Context context) throws IOException; + SingleFrameGlTextureProcessor toGlTextureProcessor(Context context) + throws FrameProcessingException; } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/GlMatrixTransformation.java b/libraries/transformer/src/main/java/androidx/media3/transformer/GlMatrixTransformation.java index b4b91e6087..0ecdc48247 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/GlMatrixTransformation.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/GlMatrixTransformation.java @@ -19,7 +19,6 @@ import android.content.Context; import android.opengl.Matrix; import android.util.Size; import androidx.media3.common.util.UnstableApi; -import java.io.IOException; /** * Specifies a 4x4 transformation {@link Matrix} to apply in the vertex shader for each frame. @@ -52,7 +51,8 @@ public interface GlMatrixTransformation extends GlEffect { float[] getGlMatrixArray(long presentationTimeUs); @Override - default SingleFrameGlTextureProcessor toGlTextureProcessor(Context context) throws IOException { + default SingleFrameGlTextureProcessor toGlTextureProcessor(Context context) + throws FrameProcessingException { return new MatrixTransformationProcessor(context, this); } } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/GlTextureProcessor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/GlTextureProcessor.java index 3aae70d280..6021ed1be4 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/GlTextureProcessor.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/GlTextureProcessor.java @@ -113,6 +113,10 @@ public interface GlTextureProcessor { /** Notifies the texture processor that no further input frames will become available. */ void signalEndOfInputStream(); - /** Releases all resources. */ - void release(); + /** + * Releases all resources. + * + * @throws FrameProcessingException If an error occurs while releasing resources. + */ + void release() throws FrameProcessingException; } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/MatrixTransformationProcessor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/MatrixTransformationProcessor.java index 8c40191dd9..542f79c45e 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/MatrixTransformationProcessor.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/MatrixTransformationProcessor.java @@ -43,10 +43,6 @@ import java.util.Arrays; @SuppressWarnings("FunctionalInterfaceClash") // b/228192298 /* package */ final class MatrixTransformationProcessor extends SingleFrameGlTextureProcessor { - static { - GlUtil.glAssertionsEnabled = true; - } - private static final String VERTEX_SHADER_TRANSFORMATION_PATH = "shaders/vertex_shader_transformation_es2.glsl"; private static final String FRAGMENT_SHADER_PATH = "shaders/fragment_shader_copy_es2.glsl"; @@ -90,10 +86,10 @@ import java.util.Arrays; * @param context The {@link Context}. * @param matrixTransformation A {@link MatrixTransformation} that specifies the transformation * matrix to use for each frame. - * @throws IOException If a problem occurs while reading shader files. + * @throws FrameProcessingException If a problem occurs while reading shader files. */ public MatrixTransformationProcessor(Context context, MatrixTransformation matrixTransformation) - throws IOException { + throws FrameProcessingException { this(context, ImmutableList.of(matrixTransformation)); } @@ -103,10 +99,10 @@ import java.util.Arrays; * @param context The {@link Context}. * @param matrixTransformation A {@link GlMatrixTransformation} that specifies the transformation * matrix to use for each frame. - * @throws IOException If a problem occurs while reading shader files. + * @throws FrameProcessingException If a problem occurs while reading shader files. */ public MatrixTransformationProcessor(Context context, GlMatrixTransformation matrixTransformation) - throws IOException { + throws FrameProcessingException { this(context, ImmutableList.of(matrixTransformation)); } @@ -116,11 +112,11 @@ import java.util.Arrays; * @param context The {@link Context}. * @param matrixTransformations The {@link GlMatrixTransformation GlMatrixTransformations} to * apply to each frame in order. - * @throws IOException If a problem occurs while reading shader files. + * @throws FrameProcessingException If a problem occurs while reading shader files. */ public MatrixTransformationProcessor( Context context, ImmutableList matrixTransformations) - throws IOException { + throws FrameProcessingException { this.matrixTransformations = matrixTransformations; transformationMatrixCache = new float[matrixTransformations.size()][16]; @@ -128,7 +124,11 @@ import java.util.Arrays; tempResultMatrix = new float[16]; Matrix.setIdentityM(compositeTransformationMatrix, /* smOffset= */ 0); visiblePolygon = NDC_SQUARE; - glProgram = new GlProgram(context, VERTEX_SHADER_TRANSFORMATION_PATH, FRAGMENT_SHADER_PATH); + try { + glProgram = new GlProgram(context, VERTEX_SHADER_TRANSFORMATION_PATH, FRAGMENT_SHADER_PATH); + } catch (IOException | GlUtil.GlException e) { + throw new FrameProcessingException(e); + } } @Override @@ -170,9 +170,15 @@ import java.util.Arrays; } @Override - public void release() { + public void release() throws FrameProcessingException { super.release(); - glProgram.delete(); + if (glProgram != null) { + try { + glProgram.delete(); + } catch (GlUtil.GlException e) { + throw new FrameProcessingException(e); + } + } } /** diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Presentation.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Presentation.java index 102af6ac7c..167e83bf0f 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Presentation.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Presentation.java @@ -24,7 +24,6 @@ import android.graphics.Matrix; import android.util.Size; import androidx.annotation.IntDef; import androidx.media3.common.C; -import androidx.media3.common.util.GlUtil; import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -164,10 +163,6 @@ public final class Presentation implements MatrixTransformation { } } - static { - GlUtil.glAssertionsEnabled = true; - } - private final int requestedHeightPixels; private final float requestedAspectRatio; private final @Layout int layout; diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/ScaleToFitTransformation.java b/libraries/transformer/src/main/java/androidx/media3/transformer/ScaleToFitTransformation.java index 12fb085727..cf69a25896 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/ScaleToFitTransformation.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/ScaleToFitTransformation.java @@ -85,10 +85,6 @@ public final class ScaleToFitTransformation implements MatrixTransformation { } } - static { - GlUtil.glAssertionsEnabled = true; - } - private final Matrix transformationMatrix; private @MonotonicNonNull Matrix adjustedTransformationMatrix; diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/SingleFrameGlTextureProcessor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/SingleFrameGlTextureProcessor.java index 5ec5522ddd..84f05c815b 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/SingleFrameGlTextureProcessor.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/SingleFrameGlTextureProcessor.java @@ -96,7 +96,7 @@ public abstract class SingleFrameGlTextureProcessor implements GlTextureProcesso listener.onInputFrameProcessed(inputTexture); listener.onOutputFrameAvailable(outputTexture, presentationTimeUs); } - } catch (FrameProcessingException | RuntimeException e) { + } catch (FrameProcessingException | GlUtil.GlException | RuntimeException e) { if (listener != null) { listener.onFrameProcessingError( e instanceof FrameProcessingException @@ -108,7 +108,7 @@ public abstract class SingleFrameGlTextureProcessor implements GlTextureProcesso } @EnsuresNonNull("outputTexture") - private void configureOutputTexture(int inputWidth, int inputHeight) { + private void configureOutputTexture(int inputWidth, int inputHeight) throws GlUtil.GlException { this.inputWidth = inputWidth; this.inputHeight = inputHeight; Size outputSize = configure(inputWidth, inputHeight); @@ -139,9 +139,13 @@ public abstract class SingleFrameGlTextureProcessor implements GlTextureProcesso @Override @CallSuper - public void release() { + public void release() throws FrameProcessingException { if (outputTexture != null) { - GlUtil.deleteTexture(outputTexture.texId); + try { + GlUtil.deleteTexture(outputTexture.texId); + } catch (GlUtil.GlException e) { + throw new FrameProcessingException(e); + } } } }