Make GlUtil.GlException checked and remove flag to disable it.
Transformer always enabled glAssertionsEnabled, so there should be no functional change. ExoPlayer previously disabled glAssertionsEnabled, so GlUtil logged GlExceptions instead of throwing them. The GlExceptions are now caught and logged by the callers so that there should also be no functional change overall. This change also replaces EGLSurfaceTexture#GlException with GlUtil#GlException. PiperOrigin-RevId: 453963741
This commit is contained in:
parent
e2f0fd7673
commit
cc1f32d094
@ -29,6 +29,7 @@ import android.opengl.GLUtils;
|
|||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.util.GlProgram;
|
import androidx.media3.common.util.GlProgram;
|
||||||
import androidx.media3.common.util.GlUtil;
|
import androidx.media3.common.util.GlUtil;
|
||||||
|
import androidx.media3.common.util.Log;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import javax.microedition.khronos.opengles.GL10;
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
@ -41,6 +42,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
/* package */ final class BitmapOverlayVideoProcessor
|
/* package */ final class BitmapOverlayVideoProcessor
|
||||||
implements VideoProcessingGLSurfaceView.VideoProcessor {
|
implements VideoProcessingGLSurfaceView.VideoProcessor {
|
||||||
|
|
||||||
|
private static final String TAG = "BitmapOverlayVP";
|
||||||
private static final int OVERLAY_WIDTH = 512;
|
private static final int OVERLAY_WIDTH = 512;
|
||||||
private static final int OVERLAY_HEIGHT = 256;
|
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");
|
/* fragmentShaderFilePath= */ "bitmap_overlay_video_processor_fragment.glsl");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
Log.e(TAG, "Failed to initialize the shader program", e);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
program.setBufferAttribute(
|
program.setBufferAttribute(
|
||||||
"aFramePosition",
|
"aFramePosition",
|
||||||
@ -119,7 +124,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
GLES20.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
|
GLES20.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
|
||||||
GLUtils.texSubImage2D(
|
GLUtils.texSubImage2D(
|
||||||
GL10.GL_TEXTURE_2D, /* level= */ 0, /* xoffset= */ 0, /* yoffset= */ 0, overlayBitmap);
|
GL10.GL_TEXTURE_2D, /* level= */ 0, /* xoffset= */ 0, /* yoffset= */ 0, overlayBitmap);
|
||||||
|
try {
|
||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
Log.e(TAG, "Failed to populate the texture", e);
|
||||||
|
}
|
||||||
|
|
||||||
// Run the shader program.
|
// Run the shader program.
|
||||||
GlProgram program = checkNotNull(this.program);
|
GlProgram program = checkNotNull(this.program);
|
||||||
@ -128,16 +137,28 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
program.setFloatUniform("uScaleX", bitmapScaleX);
|
program.setFloatUniform("uScaleX", bitmapScaleX);
|
||||||
program.setFloatUniform("uScaleY", bitmapScaleY);
|
program.setFloatUniform("uScaleY", bitmapScaleY);
|
||||||
program.setFloatsUniform("uTexTransform", transformMatrix);
|
program.setFloatsUniform("uTexTransform", transformMatrix);
|
||||||
|
try {
|
||||||
program.bindAttributesAndUniforms();
|
program.bindAttributesAndUniforms();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
Log.e(TAG, "Failed to update the shader program", e);
|
||||||
|
}
|
||||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
||||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
||||||
|
try {
|
||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
Log.e(TAG, "Failed to draw a frame", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void release() {
|
public void release() {
|
||||||
if (program != null) {
|
if (program != null) {
|
||||||
|
try {
|
||||||
program.delete();
|
program.delete();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
Log.e(TAG, "Failed to delete the shader program", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.GlUtil;
|
import androidx.media3.common.util.GlUtil;
|
||||||
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.TimedValueQueue;
|
import androidx.media3.common.util.TimedValueQueue;
|
||||||
import androidx.media3.exoplayer.ExoPlayer;
|
import androidx.media3.exoplayer.ExoPlayer;
|
||||||
import androidx.media3.exoplayer.video.VideoFrameMetadataListener;
|
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 int EGL_PROTECTED_CONTENT_EXT = 0x32C0;
|
||||||
|
private static final String TAG = "VPGlSurfaceView";
|
||||||
|
|
||||||
private final VideoRenderer renderer;
|
private final VideoRenderer renderer;
|
||||||
private final Handler mainHandler;
|
private final Handler mainHandler;
|
||||||
@ -239,7 +241,11 @@ public final class VideoProcessingGLSurfaceView extends GLSurfaceView {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
public synchronized void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||||||
|
try {
|
||||||
texture = GlUtil.createExternalTexture();
|
texture = GlUtil.createExternalTexture();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
Log.e(TAG, "Failed to create an external texture", e);
|
||||||
|
}
|
||||||
surfaceTexture = new SurfaceTexture(texture);
|
surfaceTexture = new SurfaceTexture(texture);
|
||||||
surfaceTexture.setOnFrameAvailableListener(
|
surfaceTexture.setOnFrameAvailableListener(
|
||||||
surfaceTexture -> {
|
surfaceTexture -> {
|
||||||
|
@ -45,9 +45,6 @@ import java.util.Locale;
|
|||||||
// TODO(b/227625365): Delete this class and use a texture processor from the Transformer library,
|
// 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.
|
// once overlaying a bitmap and text is supported in Transformer.
|
||||||
/* package */ final class BitmapOverlayProcessor extends SingleFrameGlTextureProcessor {
|
/* 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 VERTEX_SHADER_PATH = "vertex_shader_copy_es2.glsl";
|
||||||
private static final String FRAGMENT_SHADER_PATH = "fragment_shader_bitmap_overlay_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.
|
* 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 = new Paint();
|
||||||
paint.setTextSize(64);
|
paint.setTextSize(64);
|
||||||
paint.setAntiAlias(true);
|
paint.setAntiAlias(true);
|
||||||
@ -87,10 +84,14 @@ import java.util.Locale;
|
|||||||
} catch (PackageManager.NameNotFoundException e) {
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
bitmapTexId = GlUtil.createTexture(BITMAP_WIDTH_HEIGHT, BITMAP_WIDTH_HEIGHT);
|
bitmapTexId = GlUtil.createTexture(BITMAP_WIDTH_HEIGHT, BITMAP_WIDTH_HEIGHT);
|
||||||
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, /* level= */ 0, overlayBitmap, /* border= */ 0);
|
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.
|
// Draw the frame on the entire normalized device coordinate space, from -1 to 1, for x and y.
|
||||||
glProgram.setBufferAttribute(
|
glProgram.setBufferAttribute(
|
||||||
"aFramePosition",
|
"aFramePosition",
|
||||||
@ -141,15 +142,19 @@ import java.util.Locale;
|
|||||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
} catch (GlUtil.GlException e) {
|
} catch (GlUtil.GlException e) {
|
||||||
throw new FrameProcessingException(e);
|
throw new FrameProcessingException(e, presentationTimeUs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void release() {
|
public void release() throws FrameProcessingException {
|
||||||
super.release();
|
super.release();
|
||||||
if (glProgram != null) {
|
if (glProgram != null) {
|
||||||
|
try {
|
||||||
glProgram.delete();
|
glProgram.delete();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
throw new FrameProcessingException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,9 +31,6 @@ import java.io.IOException;
|
|||||||
* darker the further they are away from the frame center.
|
* darker the further they are away from the frame center.
|
||||||
*/
|
*/
|
||||||
/* package */ final class PeriodicVignetteProcessor extends SingleFrameGlTextureProcessor {
|
/* 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 VERTEX_SHADER_PATH = "vertex_shader_copy_es2.glsl";
|
||||||
private static final String FRAGMENT_SHADER_PATH = "fragment_shader_vignette_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 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 maxInnerRadius The upper bound of the radius that is unaffected by the effect.
|
||||||
* @param outerRadius The radius after which all pixels are black.
|
* @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(
|
public PeriodicVignetteProcessor(
|
||||||
Context context,
|
Context context,
|
||||||
@ -69,12 +66,16 @@ import java.io.IOException;
|
|||||||
float minInnerRadius,
|
float minInnerRadius,
|
||||||
float maxInnerRadius,
|
float maxInnerRadius,
|
||||||
float outerRadius)
|
float outerRadius)
|
||||||
throws IOException {
|
throws FrameProcessingException {
|
||||||
checkArgument(minInnerRadius <= maxInnerRadius);
|
checkArgument(minInnerRadius <= maxInnerRadius);
|
||||||
checkArgument(maxInnerRadius <= outerRadius);
|
checkArgument(maxInnerRadius <= outerRadius);
|
||||||
this.minInnerRadius = minInnerRadius;
|
this.minInnerRadius = minInnerRadius;
|
||||||
this.deltaInnerRadius = maxInnerRadius - minInnerRadius;
|
this.deltaInnerRadius = maxInnerRadius - minInnerRadius;
|
||||||
|
try {
|
||||||
glProgram = new GlProgram(context, VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH);
|
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("uCenter", new float[] {centerX, centerY});
|
||||||
glProgram.setFloatsUniform("uOuterRadius", new float[] {outerRadius});
|
glProgram.setFloatsUniform("uOuterRadius", new float[] {outerRadius});
|
||||||
// Draw the frame on the entire normalized device coordinate space, from -1 to 1, for x and y.
|
// 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.
|
// The four-vertex triangle strip forms a quad.
|
||||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
||||||
} catch (GlUtil.GlException e) {
|
} catch (GlUtil.GlException e) {
|
||||||
throw new FrameProcessingException(e);
|
throw new FrameProcessingException(e, presentationTimeUs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void release() {
|
public void release() throws FrameProcessingException {
|
||||||
super.release();
|
super.release();
|
||||||
if (glProgram != null) {
|
if (glProgram != null) {
|
||||||
|
try {
|
||||||
glProgram.delete();
|
glProgram.delete();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
throw new FrameProcessingException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,12 +79,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
* @param graphName Name of a MediaPipe graph asset to load.
|
* @param graphName Name of a MediaPipe graph asset to load.
|
||||||
* @param inputStreamName Name of the input video stream in the graph.
|
* @param inputStreamName Name of the input video stream in the graph.
|
||||||
* @param outputStreamName 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
|
* @throws FrameProcessingException If a problem occurs while reading shader files or initializing
|
||||||
* resources.
|
* MediaPipe resources.
|
||||||
*/
|
*/
|
||||||
public MediaPipeProcessor(
|
public MediaPipeProcessor(
|
||||||
Context context, String graphName, String inputStreamName, String outputStreamName)
|
Context context, String graphName, String inputStreamName, String outputStreamName)
|
||||||
throws IOException {
|
throws FrameProcessingException {
|
||||||
checkState(LOADER.isAvailable());
|
checkState(LOADER.isAvailable());
|
||||||
|
|
||||||
frameProcessorConditionVariable = new ConditionVariable();
|
frameProcessorConditionVariable = new ConditionVariable();
|
||||||
@ -104,7 +104,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
frameProcessorPendingError = error;
|
frameProcessorPendingError = error;
|
||||||
frameProcessorConditionVariable.open();
|
frameProcessorConditionVariable.open();
|
||||||
});
|
});
|
||||||
|
try {
|
||||||
glProgram = new GlProgram(context, COPY_VERTEX_SHADER_NAME, COPY_FRAGMENT_SHADER_NAME);
|
glProgram = new GlProgram(context, COPY_VERTEX_SHADER_NAME, COPY_FRAGMENT_SHADER_NAME);
|
||||||
|
} catch (IOException | GlUtil.GlException e) {
|
||||||
|
throw new FrameProcessingException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -152,14 +156,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
} catch (GlUtil.GlException e) {
|
} catch (GlUtil.GlException e) {
|
||||||
throw new FrameProcessingException(e);
|
throw new FrameProcessingException(e, presentationTimeUs);
|
||||||
} finally {
|
} finally {
|
||||||
checkStateNotNull(outputFrame).release();
|
checkStateNotNull(outputFrame).release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void release() {
|
public void release() throws FrameProcessingException {
|
||||||
super.release();
|
super.release();
|
||||||
checkStateNotNull(frameProcessor).close();
|
checkStateNotNull(frameProcessor).close();
|
||||||
}
|
}
|
||||||
|
@ -79,13 +79,6 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL
|
|||||||
|
|
||||||
private static final int EGL_PROTECTED_CONTENT_EXT = 0x32C0;
|
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 Handler handler;
|
||||||
private final int[] textureIdHolder;
|
private final int[] textureIdHolder;
|
||||||
@Nullable private final TextureImageListener callback;
|
@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.
|
* @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();
|
display = getDefaultDisplay();
|
||||||
EGLConfig config = chooseEGLConfig(display);
|
EGLConfig config = chooseEGLConfig(display);
|
||||||
context = createEGLContext(display, config, secureMode);
|
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);
|
EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
||||||
if (display == null) {
|
GlUtil.checkGlException(display != null, "eglGetDisplay failed");
|
||||||
throw new GlException("eglGetDisplay failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
int[] version = new int[2];
|
int[] version = new int[2];
|
||||||
boolean eglInitialized =
|
boolean eglInitialized =
|
||||||
EGL14.eglInitialize(display, version, /* majorOffset= */ 0, version, /* minorOffset= */ 1);
|
EGL14.eglInitialize(display, version, /* majorOffset= */ 0, version, /* minorOffset= */ 1);
|
||||||
if (!eglInitialized) {
|
GlUtil.checkGlException(eglInitialized, "eglInitialize failed");
|
||||||
throw new GlException("eglInitialize failed");
|
|
||||||
}
|
|
||||||
return display;
|
return display;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EGLConfig chooseEGLConfig(EGLDisplay display) {
|
private static EGLConfig chooseEGLConfig(EGLDisplay display) throws GlUtil.GlException {
|
||||||
EGLConfig[] configs = new EGLConfig[1];
|
EGLConfig[] configs = new EGLConfig[1];
|
||||||
int[] numConfigs = new int[1];
|
int[] numConfigs = new int[1];
|
||||||
boolean success =
|
boolean success =
|
||||||
@ -234,18 +223,17 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL
|
|||||||
/* config_size= */ 1,
|
/* config_size= */ 1,
|
||||||
numConfigs,
|
numConfigs,
|
||||||
/* num_configOffset= */ 0);
|
/* num_configOffset= */ 0);
|
||||||
if (!success || numConfigs[0] <= 0 || configs[0] == null) {
|
GlUtil.checkGlException(
|
||||||
throw new GlException(
|
success && numConfigs[0] > 0 && configs[0] != null,
|
||||||
Util.formatInvariant(
|
Util.formatInvariant(
|
||||||
/* format= */ "eglChooseConfig failed: success=%b, numConfigs[0]=%d, configs[0]=%s",
|
/* format= */ "eglChooseConfig failed: success=%b, numConfigs[0]=%d, configs[0]=%s",
|
||||||
success, numConfigs[0], configs[0]));
|
success, numConfigs[0], configs[0]));
|
||||||
}
|
|
||||||
|
|
||||||
return configs[0];
|
return configs[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EGLContext createEGLContext(
|
private static EGLContext createEGLContext(
|
||||||
EGLDisplay display, EGLConfig config, @SecureMode int secureMode) {
|
EGLDisplay display, EGLConfig config, @SecureMode int secureMode) throws GlUtil.GlException {
|
||||||
int[] glAttributes;
|
int[] glAttributes;
|
||||||
if (secureMode == SECURE_MODE_NONE) {
|
if (secureMode == SECURE_MODE_NONE) {
|
||||||
glAttributes = new int[] {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_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 =
|
EGLContext context =
|
||||||
EGL14.eglCreateContext(
|
EGL14.eglCreateContext(
|
||||||
display, config, android.opengl.EGL14.EGL_NO_CONTEXT, glAttributes, 0);
|
display, config, android.opengl.EGL14.EGL_NO_CONTEXT, glAttributes, 0);
|
||||||
if (context == null) {
|
GlUtil.checkGlException(context != null, "eglCreateContext failed");
|
||||||
throw new GlException("eglCreateContext failed");
|
|
||||||
}
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EGLSurface createEGLSurface(
|
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;
|
EGLSurface surface;
|
||||||
if (secureMode == SECURE_MODE_SURFACELESS_CONTEXT) {
|
if (secureMode == SECURE_MODE_SURFACELESS_CONTEXT) {
|
||||||
surface = EGL14.EGL_NO_SURFACE;
|
surface = EGL14.EGL_NO_SURFACE;
|
||||||
@ -297,20 +284,16 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
surface = EGL14.eglCreatePbufferSurface(display, config, pbufferAttributes, /* offset= */ 0);
|
surface = EGL14.eglCreatePbufferSurface(display, config, pbufferAttributes, /* offset= */ 0);
|
||||||
if (surface == null) {
|
GlUtil.checkGlException(surface != null, "eglCreatePbufferSurface failed");
|
||||||
throw new GlException("eglCreatePbufferSurface failed");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean eglMadeCurrent =
|
boolean eglMadeCurrent =
|
||||||
EGL14.eglMakeCurrent(display, /* draw= */ surface, /* read= */ surface, context);
|
EGL14.eglMakeCurrent(display, /* draw= */ surface, /* read= */ surface, context);
|
||||||
if (!eglMadeCurrent) {
|
GlUtil.checkGlException(eglMadeCurrent, "eglMakeCurrent failed");
|
||||||
throw new GlException("eglMakeCurrent failed");
|
|
||||||
}
|
|
||||||
return surface;
|
return surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void generateTextureIds(int[] textureIdHolder) {
|
private static void generateTextureIds(int[] textureIdHolder) throws GlUtil.GlException {
|
||||||
GLES20.glGenTextures(/* n= */ 1, textureIdHolder, /* offset= */ 0);
|
GLES20.glGenTextures(/* n= */ 1, textureIdHolder, /* offset= */ 0);
|
||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ public final class GlProgram {
|
|||||||
* @throws IOException When failing to read shader files.
|
* @throws IOException When failing to read shader files.
|
||||||
*/
|
*/
|
||||||
public GlProgram(Context context, String vertexShaderFilePath, String fragmentShaderFilePath)
|
public GlProgram(Context context, String vertexShaderFilePath, String fragmentShaderFilePath)
|
||||||
throws IOException {
|
throws IOException, GlUtil.GlException {
|
||||||
this(
|
this(
|
||||||
GlUtil.loadAsset(context, vertexShaderFilePath),
|
GlUtil.loadAsset(context, vertexShaderFilePath),
|
||||||
GlUtil.loadAsset(context, fragmentShaderFilePath));
|
GlUtil.loadAsset(context, fragmentShaderFilePath));
|
||||||
@ -69,7 +69,7 @@ public final class GlProgram {
|
|||||||
* @param vertexShaderGlsl The vertex shader program.
|
* @param vertexShaderGlsl The vertex shader program.
|
||||||
* @param fragmentShaderGlsl The fragment 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();
|
programId = GLES20.glCreateProgram();
|
||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
|
|
||||||
@ -81,10 +81,9 @@ public final class GlProgram {
|
|||||||
GLES20.glLinkProgram(programId);
|
GLES20.glLinkProgram(programId);
|
||||||
int[] linkStatus = new int[] {GLES20.GL_FALSE};
|
int[] linkStatus = new int[] {GLES20.GL_FALSE};
|
||||||
GLES20.glGetProgramiv(programId, GLES20.GL_LINK_STATUS, linkStatus, /* offset= */ 0);
|
GLES20.glGetProgramiv(programId, GLES20.GL_LINK_STATUS, linkStatus, /* offset= */ 0);
|
||||||
if (linkStatus[0] != GLES20.GL_TRUE) {
|
GlUtil.checkGlException(
|
||||||
GlUtil.throwGlException(
|
linkStatus[0] == GLES20.GL_TRUE,
|
||||||
"Unable to link shader program: \n" + GLES20.glGetProgramInfoLog(programId));
|
"Unable to link shader program: \n" + GLES20.glGetProgramInfoLog(programId));
|
||||||
}
|
|
||||||
GLES20.glUseProgram(programId);
|
GLES20.glUseProgram(programId);
|
||||||
attributeByName = new HashMap<>();
|
attributeByName = new HashMap<>();
|
||||||
int[] attributeCount = new int[1];
|
int[] attributeCount = new int[1];
|
||||||
@ -107,16 +106,15 @@ public final class GlProgram {
|
|||||||
GlUtil.checkGlError();
|
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);
|
int shader = GLES20.glCreateShader(type);
|
||||||
GLES20.glShaderSource(shader, glsl);
|
GLES20.glShaderSource(shader, glsl);
|
||||||
GLES20.glCompileShader(shader);
|
GLES20.glCompileShader(shader);
|
||||||
|
|
||||||
int[] result = new int[] {GLES20.GL_FALSE};
|
int[] result = new int[] {GLES20.GL_FALSE};
|
||||||
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, result, /* offset= */ 0);
|
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, result, /* offset= */ 0);
|
||||||
if (result[0] != GLES20.GL_TRUE) {
|
GlUtil.checkGlException(
|
||||||
GlUtil.throwGlException(GLES20.glGetShaderInfoLog(shader) + ", source: " + glsl);
|
result[0] == GLES20.GL_TRUE, GLES20.glGetShaderInfoLog(shader) + ", source: " + glsl);
|
||||||
}
|
|
||||||
|
|
||||||
GLES20.glAttachShader(programId, shader);
|
GLES20.glAttachShader(programId, shader);
|
||||||
GLES20.glDeleteShader(shader);
|
GLES20.glDeleteShader(shader);
|
||||||
@ -146,13 +144,13 @@ public final class GlProgram {
|
|||||||
*
|
*
|
||||||
* <p>Call this in the rendering loop to switch between different programs.
|
* <p>Call this in the rendering loop to switch between different programs.
|
||||||
*/
|
*/
|
||||||
public void use() {
|
public void use() throws GlUtil.GlException {
|
||||||
GLES20.glUseProgram(programId);
|
GLES20.glUseProgram(programId);
|
||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Deletes the program. Deleted programs cannot be used again. */
|
/** Deletes the program. Deleted programs cannot be used again. */
|
||||||
public void delete() {
|
public void delete() throws GlUtil.GlException {
|
||||||
GLES20.glDeleteProgram(programId);
|
GLES20.glDeleteProgram(programId);
|
||||||
GlUtil.checkGlError();
|
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
|
* Returns the location of an {@link Attribute}, which has been enabled as a vertex attribute
|
||||||
* array.
|
* array.
|
||||||
*/
|
*/
|
||||||
public int getAttributeArrayLocationAndEnable(String attributeName) {
|
public int getAttributeArrayLocationAndEnable(String attributeName) throws GlUtil.GlException {
|
||||||
int location = getAttributeLocation(attributeName);
|
int location = getAttributeLocation(attributeName);
|
||||||
GLES20.glEnableVertexAttribArray(location);
|
GLES20.glEnableVertexAttribArray(location);
|
||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
@ -196,7 +194,7 @@ public final class GlProgram {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Binds all attributes and uniforms in the program. */
|
/** Binds all attributes and uniforms in the program. */
|
||||||
public void bindAttributesAndUniforms() {
|
public void bindAttributesAndUniforms() throws GlUtil.GlException {
|
||||||
for (Attribute attribute : attributes) {
|
for (Attribute attribute : attributes) {
|
||||||
attribute.bind();
|
attribute.bind();
|
||||||
}
|
}
|
||||||
@ -277,7 +275,7 @@ public final class GlProgram {
|
|||||||
*
|
*
|
||||||
* <p>Should be called before each drawing call.
|
* <p>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");
|
Buffer buffer = checkNotNull(this.buffer, "call setBuffer before bind");
|
||||||
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, /* buffer= */ 0);
|
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, /* buffer= */ 0);
|
||||||
GLES20.glVertexAttribPointer(
|
GLES20.glVertexAttribPointer(
|
||||||
@ -363,7 +361,7 @@ public final class GlProgram {
|
|||||||
*
|
*
|
||||||
* <p>Should be called before each drawing call.
|
* <p>Should be called before each drawing call.
|
||||||
*/
|
*/
|
||||||
public void bind() {
|
public void bind() throws GlUtil.GlException {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case GLES20.GL_FLOAT:
|
case GLES20.GL_FLOAT:
|
||||||
GLES20.glUniform1fv(location, /* count= */ 1, value, /* offset= */ 0);
|
GLES20.glUniform1fv(location, /* count= */ 1, value, /* offset= */ 0);
|
||||||
|
@ -43,19 +43,14 @@ import javax.microedition.khronos.egl.EGL10;
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class GlUtil {
|
public final class GlUtil {
|
||||||
|
|
||||||
/** Thrown when an OpenGL error occurs and {@link #glAssertionsEnabled} is {@code true}. */
|
/** Thrown when an OpenGL error occurs. */
|
||||||
public static final class GlException extends RuntimeException {
|
public static final class GlException extends Exception {
|
||||||
/** Creates an instance with the specified error message. */
|
/** Creates an instance with the specified error message. */
|
||||||
public GlException(String message) {
|
public GlException(String message) {
|
||||||
super(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. */
|
/** Number of elements in a 3d homogeneous coordinate vector describing a vertex. */
|
||||||
public static final int HOMOGENEOUS_COORDINATE_VECTOR_SIZE = 4;
|
public static final int HOMOGENEOUS_COORDINATE_VECTOR_SIZE = 4;
|
||||||
|
|
||||||
@ -184,13 +179,13 @@ public final class GlUtil {
|
|||||||
|
|
||||||
/** Returns an initialized default {@link EGLDisplay}. */
|
/** Returns an initialized default {@link EGLDisplay}. */
|
||||||
@RequiresApi(17)
|
@RequiresApi(17)
|
||||||
public static EGLDisplay createEglDisplay() {
|
public static EGLDisplay createEglDisplay() throws GlException {
|
||||||
return Api17.createEglDisplay();
|
return Api17.createEglDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a new {@link EGLContext} for the specified {@link EGLDisplay}. */
|
/** Returns a new {@link EGLContext} for the specified {@link EGLDisplay}. */
|
||||||
@RequiresApi(17)
|
@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);
|
return Api17.createEglContext(eglDisplay, /* version= */ 2, EGL_CONFIG_ATTRIBUTES_RGBA_8888);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +194,8 @@ public final class GlUtil {
|
|||||||
* RGBA 1010102 config.
|
* RGBA 1010102 config.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
@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);
|
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.
|
* @param surface The surface to wrap; must be a surface, surface texture or surface holder.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
@RequiresApi(17)
|
||||||
public static EGLSurface getEglSurface(EGLDisplay eglDisplay, Object surface) {
|
public static EGLSurface getEglSurface(EGLDisplay eglDisplay, Object surface) throws GlException {
|
||||||
return Api17.getEglSurface(
|
return Api17.getEglSurface(
|
||||||
eglDisplay, surface, EGL_CONFIG_ATTRIBUTES_RGBA_8888, EGL_WINDOW_SURFACE_ATTRIBUTES_NONE);
|
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.
|
* @param surface The surface to wrap; must be a surface, surface texture or surface holder.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
@RequiresApi(17)
|
||||||
public static EGLSurface getEglSurfaceBt2020Pq(EGLDisplay eglDisplay, Object surface) {
|
public static EGLSurface getEglSurfaceBt2020Pq(EGLDisplay eglDisplay, Object surface)
|
||||||
|
throws GlException {
|
||||||
return Api17.getEglSurface(
|
return Api17.getEglSurface(
|
||||||
eglDisplay,
|
eglDisplay,
|
||||||
surface,
|
surface,
|
||||||
@ -239,7 +236,8 @@ public final class GlUtil {
|
|||||||
* @param height The height of the pixel buffer.
|
* @param height The height of the pixel buffer.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
@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 =
|
int[] pbufferAttributes =
|
||||||
new int[] {
|
new int[] {
|
||||||
EGL14.EGL_WIDTH, width,
|
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.
|
* @return {@link EGL14#EGL_NO_SURFACE} if supported and a 1x1 pixel buffer surface otherwise.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
@RequiresApi(17)
|
||||||
public static EGLSurface createPlaceholderEglSurface(EGLDisplay eglDisplay) {
|
public static EGLSurface createPlaceholderEglSurface(EGLDisplay eglDisplay) throws GlException {
|
||||||
return isSurfacelessContextExtensionSupported()
|
return isSurfacelessContextExtensionSupported()
|
||||||
? EGL14.EGL_NO_SURFACE
|
? EGL14.EGL_NO_SURFACE
|
||||||
: createPbufferSurface(eglDisplay, /* width= */ 1, /* height= */ 1);
|
: createPbufferSurface(eglDisplay, /* width= */ 1, /* height= */ 1);
|
||||||
@ -271,7 +269,8 @@ public final class GlUtil {
|
|||||||
* @param eglDisplay The {@link EGLDisplay} to attach the surface to.
|
* @param eglDisplay The {@link EGLDisplay} to attach the surface to.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
@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);
|
EGLSurface eglSurface = createPbufferSurface(eglDisplay, /* width= */ 1, /* height= */ 1);
|
||||||
focusEglSurface(eglDisplay, eglContext, eglSurface, /* width= */ 1, /* height= */ 1);
|
focusEglSurface(eglDisplay, eglContext, eglSurface, /* width= */ 1, /* height= */ 1);
|
||||||
}
|
}
|
||||||
@ -285,7 +284,7 @@ public final class GlUtil {
|
|||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
@RequiresApi(17)
|
||||||
public static void focusPlaceholderEglSurfaceBt2020Pq(
|
public static void focusPlaceholderEglSurfaceBt2020Pq(
|
||||||
EGLContext eglContext, EGLDisplay eglDisplay) {
|
EGLContext eglContext, EGLDisplay eglDisplay) throws GlException {
|
||||||
int[] pbufferAttributes =
|
int[] pbufferAttributes =
|
||||||
new int[] {
|
new int[] {
|
||||||
EGL14.EGL_WIDTH,
|
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
|
* Logs all OpenGL errors that occurred since this method was last called and throws a {@link
|
||||||
* a {@link GlException}.
|
* GlException} for the last error.
|
||||||
*/
|
*/
|
||||||
public static void checkGlError() {
|
public static void checkGlError() throws GlException {
|
||||||
int lastError = GLES20.GL_NO_ERROR;
|
int lastError = GLES20.GL_NO_ERROR;
|
||||||
int error;
|
int error;
|
||||||
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
|
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
|
||||||
@ -314,7 +313,7 @@ public final class GlUtil {
|
|||||||
lastError = error;
|
lastError = error;
|
||||||
}
|
}
|
||||||
if (lastError != GLES20.GL_NO_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.
|
* @param height The height for a texture.
|
||||||
* @throws GlException If the texture width or height is invalid.
|
* @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
|
// TODO(b/201293185): Consider handling adjustments for sizes > GL_MAX_TEXTURE_SIZE
|
||||||
// (ex. downscaling appropriately) in a texture processor instead of asserting incorrect
|
// (ex. downscaling appropriately) in a texture processor instead of asserting incorrect
|
||||||
// values.
|
// values.
|
||||||
@ -336,15 +335,16 @@ public final class GlUtil {
|
|||||||
GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSizeBuffer, 0);
|
GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSizeBuffer, 0);
|
||||||
int maxTextureSize = maxTextureSizeBuffer[0];
|
int maxTextureSize = maxTextureSizeBuffer[0];
|
||||||
if (width < 0 || height < 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) {
|
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). */
|
/** 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.glClearColor(/* red= */ 0, /* green= */ 0, /* blue= */ 0, /* alpha= */ 0);
|
||||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
@ -356,7 +356,8 @@ public final class GlUtil {
|
|||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
@RequiresApi(17)
|
||||||
public static void focusEglSurface(
|
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(
|
Api17.focusRenderTarget(
|
||||||
eglDisplay, eglContext, eglSurface, /* framebuffer= */ 0, width, height);
|
eglDisplay, eglContext, eglSurface, /* framebuffer= */ 0, width, height);
|
||||||
}
|
}
|
||||||
@ -372,7 +373,8 @@ public final class GlUtil {
|
|||||||
EGLSurface eglSurface,
|
EGLSurface eglSurface,
|
||||||
int framebuffer,
|
int framebuffer,
|
||||||
int width,
|
int width,
|
||||||
int height) {
|
int height)
|
||||||
|
throws GlException {
|
||||||
Api17.focusRenderTarget(eglDisplay, eglContext, eglSurface, framebuffer, width, height);
|
Api17.focusRenderTarget(eglDisplay, eglContext, eglSurface, framebuffer, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,7 +390,8 @@ public final class GlUtil {
|
|||||||
* @param height The viewport height, in pixels.
|
* @param height The viewport height, in pixels.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
@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);
|
Api17.focusFramebufferUsingCurrentContext(framebuffer, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,7 +400,7 @@ public final class GlUtil {
|
|||||||
*
|
*
|
||||||
* @param textureId The ID of the texture to delete.
|
* @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);
|
GLES20.glDeleteTextures(/* n= */ 1, new int[] {textureId}, /* offset= */ 0);
|
||||||
checkGlError();
|
checkGlError();
|
||||||
}
|
}
|
||||||
@ -408,7 +411,7 @@ public final class GlUtil {
|
|||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
@RequiresApi(17)
|
||||||
public static void destroyEglContext(
|
public static void destroyEglContext(
|
||||||
@Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) {
|
@Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) throws GlException {
|
||||||
Api17.destroyEglContext(eglDisplay, eglContext);
|
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
|
* Creates a GL_TEXTURE_EXTERNAL_OES with default configuration of GL_LINEAR filtering and
|
||||||
* GL_CLAMP_TO_EDGE wrapping.
|
* GL_CLAMP_TO_EDGE wrapping.
|
||||||
*/
|
*/
|
||||||
public static int createExternalTexture() {
|
public static int createExternalTexture() throws GlException {
|
||||||
int texId = generateTexture();
|
int texId = generateTexture();
|
||||||
bindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId);
|
bindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId);
|
||||||
return texId;
|
return texId;
|
||||||
@ -465,7 +468,7 @@ public final class GlUtil {
|
|||||||
* @param width of the new texture in pixels
|
* @param width of the new texture in pixels
|
||||||
* @param height 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);
|
assertValidTextureSize(width, height);
|
||||||
int texId = generateTexture();
|
int texId = generateTexture();
|
||||||
bindTexture(GLES20.GL_TEXTURE_2D, texId);
|
bindTexture(GLES20.GL_TEXTURE_2D, texId);
|
||||||
@ -485,8 +488,8 @@ public final class GlUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a new GL texture identifier. */
|
/** Returns a new GL texture identifier. */
|
||||||
private static int generateTexture() {
|
private static int generateTexture() throws GlException {
|
||||||
checkEglException(
|
checkGlException(
|
||||||
!Util.areEqual(EGL14.eglGetCurrentContext(), EGL14.EGL_NO_CONTEXT), "No current context");
|
!Util.areEqual(EGL14.eglGetCurrentContext(), EGL14.EGL_NO_CONTEXT), "No current context");
|
||||||
|
|
||||||
int[] texId = new int[1];
|
int[] texId = new int[1];
|
||||||
@ -504,7 +507,7 @@ public final class GlUtil {
|
|||||||
* GLES20#GL_TEXTURE_2D} for a two-dimensional texture or {@link
|
* GLES20#GL_TEXTURE_2D} for a two-dimensional texture or {@link
|
||||||
* GLES11Ext#GL_TEXTURE_EXTERNAL_OES} for an external texture.
|
* 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);
|
GLES20.glBindTexture(textureTarget, texId);
|
||||||
checkGlError();
|
checkGlError();
|
||||||
GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
|
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.
|
* @param texId The identifier of the texture to attach to the framebuffer.
|
||||||
*/
|
*/
|
||||||
public static int createFboForTexture(int texId) {
|
public static int createFboForTexture(int texId) throws GlException {
|
||||||
checkEglException(
|
checkGlException(
|
||||||
!Util.areEqual(EGL14.eglGetCurrentContext(), EGL14.EGL_NO_CONTEXT), "No current context");
|
!Util.areEqual(EGL14.eglGetCurrentContext(), EGL14.EGL_NO_CONTEXT), "No current context");
|
||||||
|
|
||||||
int[] fboId = new int[1];
|
int[] fboId = new int[1];
|
||||||
@ -537,23 +540,19 @@ public final class GlUtil {
|
|||||||
return fboId[0];
|
return fboId[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ static void throwGlException(String errorMsg) {
|
/**
|
||||||
if (glAssertionsEnabled) {
|
* Throws a {@link GlException} with the given message if {@code expression} evaluates to {@code
|
||||||
throw new GlException(errorMsg);
|
* false}.
|
||||||
} else {
|
*/
|
||||||
Log.e(TAG, errorMsg);
|
public static void checkGlException(boolean expression, String errorMessage) throws GlException {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkEglException(boolean expression, String errorMessage) {
|
|
||||||
if (!expression) {
|
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();
|
int error = EGL14.eglGetError();
|
||||||
checkEglException(error == EGL14.EGL_SUCCESS, errorMessage + ", error code: " + error);
|
checkGlException(error == EGL14.EGL_SUCCESS, errorMessage + ", error code: " + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(17)
|
@RequiresApi(17)
|
||||||
@ -561,24 +560,24 @@ public final class GlUtil {
|
|||||||
private Api17() {}
|
private Api17() {}
|
||||||
|
|
||||||
@DoNotInline
|
@DoNotInline
|
||||||
public static EGLDisplay createEglDisplay() {
|
public static EGLDisplay createEglDisplay() throws GlException {
|
||||||
EGLDisplay eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
EGLDisplay eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
||||||
checkEglException(!eglDisplay.equals(EGL14.EGL_NO_DISPLAY), "No EGL display.");
|
checkGlException(!eglDisplay.equals(EGL14.EGL_NO_DISPLAY), "No EGL display.");
|
||||||
if (!EGL14.eglInitialize(
|
checkGlException(
|
||||||
|
EGL14.eglInitialize(
|
||||||
eglDisplay,
|
eglDisplay,
|
||||||
/* unusedMajor */ new int[1],
|
/* unusedMajor */ new int[1],
|
||||||
/* majorOffset= */ 0,
|
/* majorOffset= */ 0,
|
||||||
/* unusedMinor */ new int[1],
|
/* unusedMinor */ new int[1],
|
||||||
/* minorOffset= */ 0)) {
|
/* minorOffset= */ 0),
|
||||||
throwGlException("Error in eglInitialize.");
|
"Error in eglInitialize.");
|
||||||
}
|
|
||||||
checkGlError();
|
checkGlError();
|
||||||
return eglDisplay;
|
return eglDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
@DoNotInline
|
@DoNotInline
|
||||||
public static EGLContext createEglContext(
|
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};
|
int[] contextAttributes = {EGL14.EGL_CONTEXT_CLIENT_VERSION, version, EGL14.EGL_NONE};
|
||||||
EGLContext eglContext =
|
EGLContext eglContext =
|
||||||
EGL14.eglCreateContext(
|
EGL14.eglCreateContext(
|
||||||
@ -589,7 +588,7 @@ public final class GlUtil {
|
|||||||
/* offset= */ 0);
|
/* offset= */ 0);
|
||||||
if (eglContext == null) {
|
if (eglContext == null) {
|
||||||
EGL14.eglTerminate(eglDisplay);
|
EGL14.eglTerminate(eglDisplay);
|
||||||
throwGlException(
|
throw new GlException(
|
||||||
"eglCreateContext() failed to create a valid context. The device may not support EGL"
|
"eglCreateContext() failed to create a valid context. The device may not support EGL"
|
||||||
+ " version "
|
+ " version "
|
||||||
+ version);
|
+ version);
|
||||||
@ -603,7 +602,8 @@ public final class GlUtil {
|
|||||||
EGLDisplay eglDisplay,
|
EGLDisplay eglDisplay,
|
||||||
Object surface,
|
Object surface,
|
||||||
int[] configAttributes,
|
int[] configAttributes,
|
||||||
int[] windowSurfaceAttributes) {
|
int[] windowSurfaceAttributes)
|
||||||
|
throws GlException {
|
||||||
EGLSurface eglSurface =
|
EGLSurface eglSurface =
|
||||||
EGL14.eglCreateWindowSurface(
|
EGL14.eglCreateWindowSurface(
|
||||||
eglDisplay,
|
eglDisplay,
|
||||||
@ -617,7 +617,7 @@ public final class GlUtil {
|
|||||||
|
|
||||||
@DoNotInline
|
@DoNotInline
|
||||||
public static EGLSurface createEglPbufferSurface(
|
public static EGLSurface createEglPbufferSurface(
|
||||||
EGLDisplay eglDisplay, int[] configAttributes, int[] pbufferAttributes) {
|
EGLDisplay eglDisplay, int[] configAttributes, int[] pbufferAttributes) throws GlException {
|
||||||
EGLSurface eglSurface =
|
EGLSurface eglSurface =
|
||||||
EGL14.eglCreatePbufferSurface(
|
EGL14.eglCreatePbufferSurface(
|
||||||
eglDisplay,
|
eglDisplay,
|
||||||
@ -635,15 +635,17 @@ public final class GlUtil {
|
|||||||
EGLSurface eglSurface,
|
EGLSurface eglSurface,
|
||||||
int framebuffer,
|
int framebuffer,
|
||||||
int width,
|
int width,
|
||||||
int height) {
|
int height)
|
||||||
|
throws GlException {
|
||||||
EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
|
EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
|
||||||
checkEglException("Error making context current");
|
checkEglException("Error making context current");
|
||||||
focusFramebufferUsingCurrentContext(framebuffer, width, height);
|
focusFramebufferUsingCurrentContext(framebuffer, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DoNotInline
|
@DoNotInline
|
||||||
public static void focusFramebufferUsingCurrentContext(int framebuffer, int width, int height) {
|
public static void focusFramebufferUsingCurrentContext(int framebuffer, int width, int height)
|
||||||
checkEglException(
|
throws GlException {
|
||||||
|
checkGlException(
|
||||||
!Util.areEqual(EGL14.eglGetCurrentContext(), EGL14.EGL_NO_CONTEXT), "No current context");
|
!Util.areEqual(EGL14.eglGetCurrentContext(), EGL14.EGL_NO_CONTEXT), "No current context");
|
||||||
|
|
||||||
int[] boundFramebuffer = new int[1];
|
int[] boundFramebuffer = new int[1];
|
||||||
@ -658,7 +660,7 @@ public final class GlUtil {
|
|||||||
|
|
||||||
@DoNotInline
|
@DoNotInline
|
||||||
public static void destroyEglContext(
|
public static void destroyEglContext(
|
||||||
@Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) {
|
@Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) throws GlException {
|
||||||
if (eglDisplay == null) {
|
if (eglDisplay == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -676,7 +678,8 @@ public final class GlUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@DoNotInline
|
@DoNotInline
|
||||||
private static EGLConfig getEglConfig(EGLDisplay eglDisplay, int[] attributes) {
|
private static EGLConfig getEglConfig(EGLDisplay eglDisplay, int[] attributes)
|
||||||
|
throws GlException {
|
||||||
EGLConfig[] eglConfigs = new EGLConfig[1];
|
EGLConfig[] eglConfigs = new EGLConfig[1];
|
||||||
if (!EGL14.eglChooseConfig(
|
if (!EGL14.eglChooseConfig(
|
||||||
eglDisplay,
|
eglDisplay,
|
||||||
@ -687,7 +690,7 @@ public final class GlUtil {
|
|||||||
/* config_size= */ 1,
|
/* config_size= */ 1,
|
||||||
/* unusedNumConfig */ new int[1],
|
/* unusedNumConfig */ new int[1],
|
||||||
/* num_configOffset= */ 0)) {
|
/* num_configOffset= */ 0)) {
|
||||||
throwGlException("eglChooseConfig failed.");
|
throw new GlException("eglChooseConfig failed.");
|
||||||
}
|
}
|
||||||
return eglConfigs[0];
|
return eglConfigs[0];
|
||||||
}
|
}
|
||||||
|
@ -179,6 +179,9 @@ public final class PlaceholderSurface extends Surface {
|
|||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
Log.e(TAG, "Failed to initialize placeholder surface", e);
|
Log.e(TAG, "Failed to initialize placeholder surface", e);
|
||||||
initException = e;
|
initException = e;
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
Log.e(TAG, "Failed to initialize placeholder surface", e);
|
||||||
|
initException = new IllegalStateException(e);
|
||||||
} catch (Error e) {
|
} catch (Error e) {
|
||||||
Log.e(TAG, "Failed to initialize placeholder surface", e);
|
Log.e(TAG, "Failed to initialize placeholder surface", e);
|
||||||
initError = 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);
|
Assertions.checkNotNull(eglSurfaceTexture);
|
||||||
eglSurfaceTexture.init(secureMode);
|
eglSurfaceTexture.init(secureMode);
|
||||||
this.surface =
|
this.surface =
|
||||||
|
@ -21,6 +21,7 @@ import android.content.Context;
|
|||||||
import android.opengl.GLES20;
|
import android.opengl.GLES20;
|
||||||
import android.opengl.GLSurfaceView;
|
import android.opengl.GLSurfaceView;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.GlProgram;
|
import androidx.media3.common.util.GlProgram;
|
||||||
@ -48,6 +49,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
public final class VideoDecoderGLSurfaceView extends GLSurfaceView
|
public final class VideoDecoderGLSurfaceView extends GLSurfaceView
|
||||||
implements VideoDecoderOutputBufferRenderer {
|
implements VideoDecoderOutputBufferRenderer {
|
||||||
|
|
||||||
|
private static final String TAG = "VideoDecoderGLSV";
|
||||||
|
|
||||||
private final Renderer renderer;
|
private final Renderer renderer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -172,6 +175,7 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
|
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
|
||||||
|
try {
|
||||||
program = new GlProgram(VERTEX_SHADER, FRAGMENT_SHADER);
|
program = new GlProgram(VERTEX_SHADER, FRAGMENT_SHADER);
|
||||||
int posLocation = program.getAttributeArrayLocationAndEnable("in_pos");
|
int posLocation = program.getAttributeArrayLocationAndEnable("in_pos");
|
||||||
GLES20.glVertexAttribPointer(
|
GLES20.glVertexAttribPointer(
|
||||||
@ -188,6 +192,9 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView
|
|||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
setupTextures();
|
setupTextures();
|
||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
Log.e(TAG, "Failed to set up the textures and program", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -284,7 +291,11 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView
|
|||||||
|
|
||||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
||||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
||||||
|
try {
|
||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
Log.e(TAG, "Failed to draw a frame", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOutputBuffer(VideoDecoderOutputBuffer outputBuffer) {
|
public void setOutputBuffer(VideoDecoderOutputBuffer outputBuffer) {
|
||||||
@ -300,6 +311,7 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView
|
|||||||
|
|
||||||
@RequiresNonNull("program")
|
@RequiresNonNull("program")
|
||||||
private void setupTextures() {
|
private void setupTextures() {
|
||||||
|
try {
|
||||||
GLES20.glGenTextures(/* n= */ 3, yuvTextures, /* offset= */ 0);
|
GLES20.glGenTextures(/* n= */ 3, yuvTextures, /* offset= */ 0);
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
GLES20.glUniform1i(program.getUniformLocation(TEXTURE_UNIFORMS[i]), i);
|
GLES20.glUniform1i(program.getUniformLocation(TEXTURE_UNIFORMS[i]), i);
|
||||||
@ -307,6 +319,9 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView
|
|||||||
GlUtil.bindTexture(GLES20.GL_TEXTURE_2D, yuvTextures[i]);
|
GlUtil.bindTexture(GLES20.GL_TEXTURE_2D, yuvTextures[i]);
|
||||||
}
|
}
|
||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
Log.e(TAG, "Failed to set up the textures", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import static androidx.media3.common.util.GlUtil.checkGlError;
|
|||||||
|
|
||||||
import android.opengl.GLES11Ext;
|
import android.opengl.GLES11Ext;
|
||||||
import android.opengl.GLES20;
|
import android.opengl.GLES20;
|
||||||
|
import android.util.Log;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.util.GlProgram;
|
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;
|
&& 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.
|
// Basic vertex & fragment shaders to render a mesh with 3D position & 2D texture data.
|
||||||
private static final String VERTEX_SHADER =
|
private static final String VERTEX_SHADER =
|
||||||
"uniform mat4 uMvpMatrix;\n"
|
"uniform mat4 uMvpMatrix;\n"
|
||||||
@ -115,12 +118,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
/** Initializes of the GL components. */
|
/** Initializes of the GL components. */
|
||||||
public void init() {
|
public void init() {
|
||||||
|
try {
|
||||||
program = new GlProgram(VERTEX_SHADER, FRAGMENT_SHADER);
|
program = new GlProgram(VERTEX_SHADER, FRAGMENT_SHADER);
|
||||||
mvpMatrixHandle = program.getUniformLocation("uMvpMatrix");
|
mvpMatrixHandle = program.getUniformLocation("uMvpMatrix");
|
||||||
uTexMatrixHandle = program.getUniformLocation("uTexMatrix");
|
uTexMatrixHandle = program.getUniformLocation("uTexMatrix");
|
||||||
positionHandle = program.getAttributeArrayLocationAndEnable("aPosition");
|
positionHandle = program.getAttributeArrayLocationAndEnable("aPosition");
|
||||||
texCoordsHandle = program.getAttributeArrayLocationAndEnable("aTexCoords");
|
texCoordsHandle = program.getAttributeArrayLocationAndEnable("aTexCoords");
|
||||||
textureHandle = program.getUniformLocation("uTexture");
|
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.glActiveTexture(GLES20.GL_TEXTURE0);
|
||||||
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
|
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
|
||||||
GLES20.glUniform1i(textureHandle, 0);
|
GLES20.glUniform1i(textureHandle, 0);
|
||||||
|
try {
|
||||||
checkGlError();
|
checkGlError();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
Log.e(TAG, "Failed to bind uniforms", e);
|
||||||
|
}
|
||||||
|
|
||||||
// Load position data.
|
// Load position data.
|
||||||
GLES20.glVertexAttribPointer(
|
GLES20.glVertexAttribPointer(
|
||||||
@ -164,7 +175,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
false,
|
false,
|
||||||
Projection.POSITION_COORDS_PER_VERTEX * C.BYTES_PER_FLOAT,
|
Projection.POSITION_COORDS_PER_VERTEX * C.BYTES_PER_FLOAT,
|
||||||
meshData.vertexBuffer);
|
meshData.vertexBuffer);
|
||||||
|
try {
|
||||||
checkGlError();
|
checkGlError();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
Log.e(TAG, "Failed to load position data", e);
|
||||||
|
}
|
||||||
|
|
||||||
// Load texture data.
|
// Load texture data.
|
||||||
GLES20.glVertexAttribPointer(
|
GLES20.glVertexAttribPointer(
|
||||||
@ -174,17 +189,29 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
false,
|
false,
|
||||||
Projection.TEXTURE_COORDS_PER_VERTEX * C.BYTES_PER_FLOAT,
|
Projection.TEXTURE_COORDS_PER_VERTEX * C.BYTES_PER_FLOAT,
|
||||||
meshData.textureBuffer);
|
meshData.textureBuffer);
|
||||||
|
try {
|
||||||
checkGlError();
|
checkGlError();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
Log.e(TAG, "Failed to load texture data", e);
|
||||||
|
}
|
||||||
|
|
||||||
// Render.
|
// Render.
|
||||||
GLES20.glDrawArrays(meshData.drawMode, /* first= */ 0, meshData.vertexCount);
|
GLES20.glDrawArrays(meshData.drawMode, /* first= */ 0, meshData.vertexCount);
|
||||||
|
try {
|
||||||
checkGlError();
|
checkGlError();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
Log.e(TAG, "Failed to render", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Cleans up GL resources. */
|
/** Cleans up GL resources. */
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
if (program != null) {
|
if (program != null) {
|
||||||
|
try {
|
||||||
program.delete();
|
program.delete();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
Log.e(TAG, "Failed to delete the shader program", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.GlUtil;
|
import androidx.media3.common.util.GlUtil;
|
||||||
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.TimedValueQueue;
|
import androidx.media3.common.util.TimedValueQueue;
|
||||||
import androidx.media3.exoplayer.video.VideoFrameMetadataListener;
|
import androidx.media3.exoplayer.video.VideoFrameMetadataListener;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -36,6 +37,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
/* package */ final class SceneRenderer
|
/* package */ final class SceneRenderer
|
||||||
implements VideoFrameMetadataListener, CameraMotionListener {
|
implements VideoFrameMetadataListener, CameraMotionListener {
|
||||||
|
|
||||||
|
private static final String TAG = "SceneRenderer";
|
||||||
|
|
||||||
private final AtomicBoolean frameAvailable;
|
private final AtomicBoolean frameAvailable;
|
||||||
private final AtomicBoolean resetRotationAtNextFrame;
|
private final AtomicBoolean resetRotationAtNextFrame;
|
||||||
private final ProjectionRenderer projectionRenderer;
|
private final ProjectionRenderer projectionRenderer;
|
||||||
@ -83,7 +86,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
/** Initializes the renderer. */
|
/** Initializes the renderer. */
|
||||||
public SurfaceTexture init() {
|
public SurfaceTexture init() {
|
||||||
// Set the background frame color. This is only visible if the display mesh isn't a full sphere.
|
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);
|
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
|
||||||
checkGlError();
|
checkGlError();
|
||||||
|
|
||||||
@ -91,6 +96,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
checkGlError();
|
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 = new SurfaceTexture(textureId);
|
||||||
surfaceTexture.setOnFrameAvailableListener(surfaceTexture -> frameAvailable.set(true));
|
surfaceTexture.setOnFrameAvailableListener(surfaceTexture -> frameAvailable.set(true));
|
||||||
return surfaceTexture;
|
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
|
// 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.
|
// performance on tiled renderers by causing the GPU to discard previous data.
|
||||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
||||||
|
try {
|
||||||
checkGlError();
|
checkGlError();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
Log.e(TAG, "Failed to draw a frame", e);
|
||||||
|
}
|
||||||
|
|
||||||
if (frameAvailable.compareAndSet(true, false)) {
|
if (frameAvailable.compareAndSet(true, false)) {
|
||||||
Assertions.checkNotNull(surfaceTexture).updateTexImage();
|
Assertions.checkNotNull(surfaceTexture).updateTexImage();
|
||||||
|
try {
|
||||||
checkGlError();
|
checkGlError();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
Log.e(TAG, "Failed to draw a frame", e);
|
||||||
|
}
|
||||||
if (resetRotationAtNextFrame.compareAndSet(true, false)) {
|
if (resetRotationAtNextFrame.compareAndSet(true, false)) {
|
||||||
Matrix.setIdentityM(rotationMatrix, 0);
|
Matrix.setIdentityM(rotationMatrix, 0);
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,8 @@ public class BitmapTestUtil {
|
|||||||
* @param height The height of the pixel rectangle to read.
|
* @param height The height of the pixel rectangle to read.
|
||||||
* @return A {@link Bitmap} with the framebuffer's values.
|
* @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);
|
ByteBuffer rgba8888Buffer = ByteBuffer.allocateDirect(width * height * 4);
|
||||||
GLES20.glReadPixels(
|
GLES20.glReadPixels(
|
||||||
0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, rgba8888Buffer);
|
0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, rgba8888Buffer);
|
||||||
@ -206,7 +207,7 @@ public class BitmapTestUtil {
|
|||||||
* @param bitmap A {@link Bitmap}.
|
* @param bitmap A {@link Bitmap}.
|
||||||
* @return The identifier of the newly created texture.
|
* @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());
|
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
|
// 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.
|
// while OpenGL's positive y-axis points up.
|
||||||
|
@ -52,13 +52,9 @@ public final class CropPixelTest {
|
|||||||
public static final String CROP_LARGER_PNG_ASSET_PATH =
|
public static final String CROP_LARGER_PNG_ASSET_PATH =
|
||||||
"media/bitmap/sample_mp4_first_frame/crop_larger.png";
|
"media/bitmap/sample_mp4_first_frame/crop_larger.png";
|
||||||
|
|
||||||
static {
|
private Context context = getApplicationContext();
|
||||||
GlUtil.glAssertionsEnabled = true;
|
private @MonotonicNonNull EGLDisplay eglDisplay;
|
||||||
}
|
private @MonotonicNonNull EGLContext eglContext;
|
||||||
|
|
||||||
private final Context context = getApplicationContext();
|
|
||||||
private final EGLDisplay eglDisplay = GlUtil.createEglDisplay();
|
|
||||||
private final EGLContext eglContext = GlUtil.createEglContext(eglDisplay);
|
|
||||||
private @MonotonicNonNull SingleFrameGlTextureProcessor cropTextureProcessor;
|
private @MonotonicNonNull SingleFrameGlTextureProcessor cropTextureProcessor;
|
||||||
private @MonotonicNonNull EGLSurface placeholderEglSurface;
|
private @MonotonicNonNull EGLSurface placeholderEglSurface;
|
||||||
private int inputTexId;
|
private int inputTexId;
|
||||||
@ -67,7 +63,9 @@ public final class CropPixelTest {
|
|||||||
private int inputHeight;
|
private int inputHeight;
|
||||||
|
|
||||||
@Before
|
@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);
|
Bitmap inputBitmap = BitmapTestUtil.readBitmap(ORIGINAL_PNG_ASSET_PATH);
|
||||||
inputWidth = inputBitmap.getWidth();
|
inputWidth = inputBitmap.getWidth();
|
||||||
inputHeight = inputBitmap.getHeight();
|
inputHeight = inputBitmap.getHeight();
|
||||||
@ -77,12 +75,14 @@ public final class CropPixelTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void release() {
|
public void release() throws GlUtil.GlException, FrameProcessingException {
|
||||||
if (cropTextureProcessor != null) {
|
if (cropTextureProcessor != null) {
|
||||||
cropTextureProcessor.release();
|
cropTextureProcessor.release();
|
||||||
}
|
}
|
||||||
|
if (eglContext != null && eglDisplay != null) {
|
||||||
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void drawFrame_noEdits_producesExpectedOutput() throws Exception {
|
public void drawFrame_noEdits_producesExpectedOutput() throws Exception {
|
||||||
@ -156,12 +156,12 @@ public final class CropPixelTest {
|
|||||||
assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
|
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);
|
outputTexId = GlUtil.createTexture(outputWidth, outputHeight);
|
||||||
int frameBuffer = GlUtil.createFboForTexture(outputTexId);
|
int frameBuffer = GlUtil.createFboForTexture(outputTexId);
|
||||||
GlUtil.focusFramebuffer(
|
GlUtil.focusFramebuffer(
|
||||||
eglDisplay,
|
checkNotNull(eglDisplay),
|
||||||
eglContext,
|
checkNotNull(eglContext),
|
||||||
checkNotNull(placeholderEglSurface),
|
checkNotNull(placeholderEglSurface),
|
||||||
frameBuffer,
|
frameBuffer,
|
||||||
outputWidth,
|
outputWidth,
|
||||||
|
@ -53,13 +53,9 @@ public final class MatrixTransformationProcessorPixelTest {
|
|||||||
public static final String ROTATE_90_PNG_ASSET_PATH =
|
public static final String ROTATE_90_PNG_ASSET_PATH =
|
||||||
"media/bitmap/sample_mp4_first_frame/rotate90.png";
|
"media/bitmap/sample_mp4_first_frame/rotate90.png";
|
||||||
|
|
||||||
static {
|
|
||||||
GlUtil.glAssertionsEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Context context = getApplicationContext();
|
private final Context context = getApplicationContext();
|
||||||
private final EGLDisplay eglDisplay = GlUtil.createEglDisplay();
|
private @MonotonicNonNull EGLDisplay eglDisplay;
|
||||||
private final EGLContext eglContext = GlUtil.createEglContext(eglDisplay);
|
private @MonotonicNonNull EGLContext eglContext;
|
||||||
private @MonotonicNonNull SingleFrameGlTextureProcessor matrixTransformationFrameProcessor;
|
private @MonotonicNonNull SingleFrameGlTextureProcessor matrixTransformationFrameProcessor;
|
||||||
private int inputTexId;
|
private int inputTexId;
|
||||||
private int outputTexId;
|
private int outputTexId;
|
||||||
@ -67,7 +63,9 @@ public final class MatrixTransformationProcessorPixelTest {
|
|||||||
private int height;
|
private int height;
|
||||||
|
|
||||||
@Before
|
@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);
|
Bitmap inputBitmap = BitmapTestUtil.readBitmap(ORIGINAL_PNG_ASSET_PATH);
|
||||||
width = inputBitmap.getWidth();
|
width = inputBitmap.getWidth();
|
||||||
height = inputBitmap.getHeight();
|
height = inputBitmap.getHeight();
|
||||||
@ -81,12 +79,14 @@ public final class MatrixTransformationProcessorPixelTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void release() {
|
public void release() throws GlUtil.GlException, FrameProcessingException {
|
||||||
if (matrixTransformationFrameProcessor != null) {
|
if (matrixTransformationFrameProcessor != null) {
|
||||||
matrixTransformationFrameProcessor.release();
|
matrixTransformationFrameProcessor.release();
|
||||||
}
|
}
|
||||||
|
if (eglContext != null && eglDisplay != null) {
|
||||||
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void drawFrame_noEdits_producesExpectedOutput() throws Exception {
|
public void drawFrame_noEdits_producesExpectedOutput() throws Exception {
|
||||||
|
@ -60,13 +60,9 @@ public final class PresentationPixelTest {
|
|||||||
public static final String ASPECT_RATIO_STRETCH_TO_FIT_WIDE_PNG_ASSET_PATH =
|
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";
|
"media/bitmap/sample_mp4_first_frame/aspect_ratio_stretch_to_fit_wide.png";
|
||||||
|
|
||||||
static {
|
private Context context = getApplicationContext();
|
||||||
GlUtil.glAssertionsEnabled = true;
|
private @MonotonicNonNull EGLDisplay eglDisplay;
|
||||||
}
|
private @MonotonicNonNull EGLContext eglContext;
|
||||||
|
|
||||||
private final Context context = getApplicationContext();
|
|
||||||
private final EGLDisplay eglDisplay = GlUtil.createEglDisplay();
|
|
||||||
private final EGLContext eglContext = GlUtil.createEglContext(eglDisplay);
|
|
||||||
private @MonotonicNonNull SingleFrameGlTextureProcessor presentationTextureProcessor;
|
private @MonotonicNonNull SingleFrameGlTextureProcessor presentationTextureProcessor;
|
||||||
private @MonotonicNonNull EGLSurface placeholderEglSurface;
|
private @MonotonicNonNull EGLSurface placeholderEglSurface;
|
||||||
private int inputTexId;
|
private int inputTexId;
|
||||||
@ -75,7 +71,9 @@ public final class PresentationPixelTest {
|
|||||||
private int inputHeight;
|
private int inputHeight;
|
||||||
|
|
||||||
@Before
|
@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);
|
Bitmap inputBitmap = BitmapTestUtil.readBitmap(ORIGINAL_PNG_ASSET_PATH);
|
||||||
inputWidth = inputBitmap.getWidth();
|
inputWidth = inputBitmap.getWidth();
|
||||||
inputHeight = inputBitmap.getHeight();
|
inputHeight = inputBitmap.getHeight();
|
||||||
@ -85,12 +83,14 @@ public final class PresentationPixelTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void release() {
|
public void release() throws GlUtil.GlException, FrameProcessingException {
|
||||||
if (presentationTextureProcessor != null) {
|
if (presentationTextureProcessor != null) {
|
||||||
presentationTextureProcessor.release();
|
presentationTextureProcessor.release();
|
||||||
}
|
}
|
||||||
|
if (eglContext != null && eglDisplay != null) {
|
||||||
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void drawFrame_noEdits_producesExpectedOutput() throws Exception {
|
public void drawFrame_noEdits_producesExpectedOutput() throws Exception {
|
||||||
@ -282,12 +282,12 @@ public final class PresentationPixelTest {
|
|||||||
assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
|
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);
|
outputTexId = GlUtil.createTexture(outputWidth, outputHeight);
|
||||||
int frameBuffer = GlUtil.createFboForTexture(outputTexId);
|
int frameBuffer = GlUtil.createFboForTexture(outputTexId);
|
||||||
GlUtil.focusFramebuffer(
|
GlUtil.focusFramebuffer(
|
||||||
eglDisplay,
|
checkNotNull(eglDisplay),
|
||||||
eglContext,
|
checkNotNull(eglContext),
|
||||||
checkNotNull(placeholderEglSurface),
|
checkNotNull(placeholderEglSurface),
|
||||||
frameBuffer,
|
frameBuffer,
|
||||||
outputWidth,
|
outputWidth,
|
||||||
|
@ -32,10 +32,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class Crop implements MatrixTransformation {
|
public final class Crop implements MatrixTransformation {
|
||||||
|
|
||||||
static {
|
|
||||||
GlUtil.glAssertionsEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final float left;
|
private final float left;
|
||||||
private final float right;
|
private final float right;
|
||||||
private final float bottom;
|
private final float bottom;
|
||||||
|
@ -23,7 +23,6 @@ import android.graphics.Matrix;
|
|||||||
import android.util.Size;
|
import android.util.Size;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.util.GlUtil;
|
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
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
|
// TODO(b/218488308): Allow reconfiguration of the output size, as encoders may not support the
|
||||||
// requested output resolution.
|
// requested output resolution.
|
||||||
|
|
||||||
static {
|
|
||||||
GlUtil.glAssertionsEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int outputRotationDegrees;
|
private int outputRotationDegrees;
|
||||||
private @MonotonicNonNull Matrix transformationMatrix;
|
private @MonotonicNonNull Matrix transformationMatrix;
|
||||||
|
|
||||||
|
@ -28,10 +28,6 @@ import java.io.IOException;
|
|||||||
/** Copies frames from an external texture and applies color transformations for HDR if needed. */
|
/** Copies frames from an external texture and applies color transformations for HDR if needed. */
|
||||||
/* package */ class ExternalTextureProcessor extends SingleFrameGlTextureProcessor {
|
/* package */ class ExternalTextureProcessor extends SingleFrameGlTextureProcessor {
|
||||||
|
|
||||||
static {
|
|
||||||
GlUtil.glAssertionsEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String VERTEX_SHADER_TEX_TRANSFORM_PATH =
|
private static final String VERTEX_SHADER_TEX_TRANSFORM_PATH =
|
||||||
"shaders/vertex_shader_tex_transform_es2.glsl";
|
"shaders/vertex_shader_tex_transform_es2.glsl";
|
||||||
private static final String VERTEX_SHADER_TEX_TRANSFORM_ES3_PATH =
|
private static final String VERTEX_SHADER_TEX_TRANSFORM_ES3_PATH =
|
||||||
@ -54,10 +50,10 @@ import java.io.IOException;
|
|||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
*
|
*
|
||||||
* @param enableExperimentalHdrEditing Whether to attempt to process the input as an HDR signal.
|
* @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)
|
public ExternalTextureProcessor(Context context, boolean enableExperimentalHdrEditing)
|
||||||
throws IOException {
|
throws FrameProcessingException {
|
||||||
String vertexShaderFilePath =
|
String vertexShaderFilePath =
|
||||||
enableExperimentalHdrEditing
|
enableExperimentalHdrEditing
|
||||||
? VERTEX_SHADER_TEX_TRANSFORM_ES3_PATH
|
? VERTEX_SHADER_TEX_TRANSFORM_ES3_PATH
|
||||||
@ -66,7 +62,11 @@ import java.io.IOException;
|
|||||||
enableExperimentalHdrEditing
|
enableExperimentalHdrEditing
|
||||||
? FRAGMENT_SHADER_COPY_EXTERNAL_YUV_ES3_PATH
|
? FRAGMENT_SHADER_COPY_EXTERNAL_YUV_ES3_PATH
|
||||||
: FRAGMENT_SHADER_COPY_EXTERNAL_PATH;
|
: FRAGMENT_SHADER_COPY_EXTERNAL_PATH;
|
||||||
|
try {
|
||||||
glProgram = new GlProgram(context, vertexShaderFilePath, fragmentShaderFilePath);
|
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.
|
// Draw the frame on the entire normalized device coordinate space, from -1 to 1, for x and y.
|
||||||
glProgram.setBufferAttribute(
|
glProgram.setBufferAttribute(
|
||||||
"aFramePosition",
|
"aFramePosition",
|
||||||
@ -109,15 +109,19 @@ import java.io.IOException;
|
|||||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
} catch (GlUtil.GlException e) {
|
} catch (GlUtil.GlException e) {
|
||||||
throw new FrameProcessingException(e);
|
throw new FrameProcessingException(e, presentationTimeUs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void release() {
|
public void release() throws FrameProcessingException {
|
||||||
super.release();
|
super.release();
|
||||||
if (glProgram != null) {
|
if (glProgram != null) {
|
||||||
|
try {
|
||||||
glProgram.delete();
|
glProgram.delete();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
throw new FrameProcessingException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,26 @@ import androidx.media3.common.util.UnstableApi;
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class FrameProcessingException extends Exception {
|
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
|
* The microsecond timestamp of the frame being processed while the exception occurred or {@link
|
||||||
* C#TIME_UNSET} if unknown.
|
* C#TIME_UNSET} if unknown.
|
||||||
|
@ -41,7 +41,6 @@ import androidx.media3.common.util.GlUtil;
|
|||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.ExecutionException;
|
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.
|
// TODO(b/227625423): Factor out FrameProcessor interface and rename this class to GlFrameProcessor.
|
||||||
/* package */ final class FrameProcessorChain {
|
/* package */ final class FrameProcessorChain {
|
||||||
|
|
||||||
static {
|
|
||||||
GlUtil.glAssertionsEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener for asynchronous frame processing events.
|
* Listener for asynchronous frame processing events.
|
||||||
*
|
*
|
||||||
@ -150,7 +145,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
List<GlEffect> effects,
|
List<GlEffect> effects,
|
||||||
boolean enableExperimentalHdrEditing,
|
boolean enableExperimentalHdrEditing,
|
||||||
ExecutorService singleThreadExecutorService)
|
ExecutorService singleThreadExecutorService)
|
||||||
throws IOException {
|
throws GlUtil.GlException, FrameProcessingException {
|
||||||
checkState(Thread.currentThread().getName().equals(THREAD_NAME));
|
checkState(Thread.currentThread().getName().equals(THREAD_NAME));
|
||||||
|
|
||||||
EGLDisplay eglDisplay = GlUtil.createEglDisplay();
|
EGLDisplay eglDisplay = GlUtil.createEglDisplay();
|
||||||
@ -204,7 +199,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
ExternalTextureProcessor externalTextureProcessor,
|
ExternalTextureProcessor externalTextureProcessor,
|
||||||
float pixelWidthHeightRatio,
|
float pixelWidthHeightRatio,
|
||||||
List<GlEffect> effects)
|
List<GlEffect> effects)
|
||||||
throws IOException {
|
throws FrameProcessingException {
|
||||||
ImmutableList.Builder<SingleFrameGlTextureProcessor> textureProcessors =
|
ImmutableList.Builder<SingleFrameGlTextureProcessor> textureProcessors =
|
||||||
new ImmutableList.Builder<SingleFrameGlTextureProcessor>().add(externalTextureProcessor);
|
new ImmutableList.Builder<SingleFrameGlTextureProcessor>().add(externalTextureProcessor);
|
||||||
|
|
||||||
@ -532,22 +527,19 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
int finalInputTexId = inputTexId;
|
int finalInputTexId = inputTexId;
|
||||||
debugSurfaceViewWrapper.maybeRenderToSurfaceView(
|
debugSurfaceViewWrapper.maybeRenderToSurfaceView(
|
||||||
() -> {
|
() -> {
|
||||||
GlUtil.clearOutputFrame();
|
|
||||||
try {
|
try {
|
||||||
|
GlUtil.clearOutputFrame();
|
||||||
getLast(textureProcessors).drawFrame(finalInputTexId, finalPresentationTimeUs);
|
getLast(textureProcessors).drawFrame(finalInputTexId, finalPresentationTimeUs);
|
||||||
} catch (FrameProcessingException e) {
|
} catch (GlUtil.GlException | FrameProcessingException e) {
|
||||||
Log.d(TAG, "Error rendering to debug preview", e);
|
Log.d(TAG, "Error rendering to debug preview", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
checkState(pendingFrameCount.getAndDecrement() > 0);
|
checkState(pendingFrameCount.getAndDecrement() > 0);
|
||||||
} catch (FrameProcessingException | RuntimeException e) {
|
} catch (FrameProcessingException | GlUtil.GlException | RuntimeException e) {
|
||||||
if (!stopProcessing.getAndSet(true)) {
|
if (!stopProcessing.getAndSet(true)) {
|
||||||
listener.onFrameProcessingError(
|
listener.onFrameProcessingError(FrameProcessingException.from(e, presentationTimeUs));
|
||||||
e instanceof FrameProcessingException
|
|
||||||
? (FrameProcessingException) e
|
|
||||||
: new FrameProcessingException(e, presentationTimeUs));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -565,8 +557,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
textureProcessors.get(i).release();
|
textureProcessors.get(i).release();
|
||||||
}
|
}
|
||||||
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
||||||
} catch (RuntimeException e) {
|
} catch (FrameProcessingException | GlUtil.GlException | RuntimeException e) {
|
||||||
listener.onFrameProcessingError(new FrameProcessingException(e));
|
listener.onFrameProcessingError(FrameProcessingException.from(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -600,7 +592,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public synchronized void maybeRenderToSurfaceView(Runnable renderRunnable) {
|
public synchronized void maybeRenderToSurfaceView(Runnable renderRunnable)
|
||||||
|
throws GlUtil.GlException {
|
||||||
if (surface == null) {
|
if (surface == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ package androidx.media3.transformer;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for a video frame effect with a {@link SingleFrameGlTextureProcessor} implementation.
|
* 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. */
|
/** Returns a {@link SingleFrameGlTextureProcessor} that applies the effect. */
|
||||||
// TODO(b/227625423): use GlTextureProcessor here once this interface exists.
|
// TODO(b/227625423): use GlTextureProcessor here once this interface exists.
|
||||||
SingleFrameGlTextureProcessor toGlTextureProcessor(Context context) throws IOException;
|
SingleFrameGlTextureProcessor toGlTextureProcessor(Context context)
|
||||||
|
throws FrameProcessingException;
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ import android.content.Context;
|
|||||||
import android.opengl.Matrix;
|
import android.opengl.Matrix;
|
||||||
import android.util.Size;
|
import android.util.Size;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
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.
|
* 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);
|
float[] getGlMatrixArray(long presentationTimeUs);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default SingleFrameGlTextureProcessor toGlTextureProcessor(Context context) throws IOException {
|
default SingleFrameGlTextureProcessor toGlTextureProcessor(Context context)
|
||||||
|
throws FrameProcessingException {
|
||||||
return new MatrixTransformationProcessor(context, this);
|
return new MatrixTransformationProcessor(context, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,10 @@ public interface GlTextureProcessor {
|
|||||||
/** Notifies the texture processor that no further input frames will become available. */
|
/** Notifies the texture processor that no further input frames will become available. */
|
||||||
void signalEndOfInputStream();
|
void signalEndOfInputStream();
|
||||||
|
|
||||||
/** Releases all resources. */
|
/**
|
||||||
void release();
|
* Releases all resources.
|
||||||
|
*
|
||||||
|
* @throws FrameProcessingException If an error occurs while releasing resources.
|
||||||
|
*/
|
||||||
|
void release() throws FrameProcessingException;
|
||||||
}
|
}
|
||||||
|
@ -43,10 +43,6 @@ import java.util.Arrays;
|
|||||||
@SuppressWarnings("FunctionalInterfaceClash") // b/228192298
|
@SuppressWarnings("FunctionalInterfaceClash") // b/228192298
|
||||||
/* package */ final class MatrixTransformationProcessor extends SingleFrameGlTextureProcessor {
|
/* package */ final class MatrixTransformationProcessor extends SingleFrameGlTextureProcessor {
|
||||||
|
|
||||||
static {
|
|
||||||
GlUtil.glAssertionsEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String VERTEX_SHADER_TRANSFORMATION_PATH =
|
private static final String VERTEX_SHADER_TRANSFORMATION_PATH =
|
||||||
"shaders/vertex_shader_transformation_es2.glsl";
|
"shaders/vertex_shader_transformation_es2.glsl";
|
||||||
private static final String FRAGMENT_SHADER_PATH = "shaders/fragment_shader_copy_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 context The {@link Context}.
|
||||||
* @param matrixTransformation A {@link MatrixTransformation} that specifies the transformation
|
* @param matrixTransformation A {@link MatrixTransformation} that specifies the transformation
|
||||||
* matrix to use for each frame.
|
* 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)
|
public MatrixTransformationProcessor(Context context, MatrixTransformation matrixTransformation)
|
||||||
throws IOException {
|
throws FrameProcessingException {
|
||||||
this(context, ImmutableList.of(matrixTransformation));
|
this(context, ImmutableList.of(matrixTransformation));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,10 +99,10 @@ import java.util.Arrays;
|
|||||||
* @param context The {@link Context}.
|
* @param context The {@link Context}.
|
||||||
* @param matrixTransformation A {@link GlMatrixTransformation} that specifies the transformation
|
* @param matrixTransformation A {@link GlMatrixTransformation} that specifies the transformation
|
||||||
* matrix to use for each frame.
|
* 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)
|
public MatrixTransformationProcessor(Context context, GlMatrixTransformation matrixTransformation)
|
||||||
throws IOException {
|
throws FrameProcessingException {
|
||||||
this(context, ImmutableList.of(matrixTransformation));
|
this(context, ImmutableList.of(matrixTransformation));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,11 +112,11 @@ import java.util.Arrays;
|
|||||||
* @param context The {@link Context}.
|
* @param context The {@link Context}.
|
||||||
* @param matrixTransformations The {@link GlMatrixTransformation GlMatrixTransformations} to
|
* @param matrixTransformations The {@link GlMatrixTransformation GlMatrixTransformations} to
|
||||||
* apply to each frame in order.
|
* 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(
|
public MatrixTransformationProcessor(
|
||||||
Context context, ImmutableList<GlMatrixTransformation> matrixTransformations)
|
Context context, ImmutableList<GlMatrixTransformation> matrixTransformations)
|
||||||
throws IOException {
|
throws FrameProcessingException {
|
||||||
this.matrixTransformations = matrixTransformations;
|
this.matrixTransformations = matrixTransformations;
|
||||||
|
|
||||||
transformationMatrixCache = new float[matrixTransformations.size()][16];
|
transformationMatrixCache = new float[matrixTransformations.size()][16];
|
||||||
@ -128,7 +124,11 @@ import java.util.Arrays;
|
|||||||
tempResultMatrix = new float[16];
|
tempResultMatrix = new float[16];
|
||||||
Matrix.setIdentityM(compositeTransformationMatrix, /* smOffset= */ 0);
|
Matrix.setIdentityM(compositeTransformationMatrix, /* smOffset= */ 0);
|
||||||
visiblePolygon = NDC_SQUARE;
|
visiblePolygon = NDC_SQUARE;
|
||||||
|
try {
|
||||||
glProgram = new GlProgram(context, VERTEX_SHADER_TRANSFORMATION_PATH, FRAGMENT_SHADER_PATH);
|
glProgram = new GlProgram(context, VERTEX_SHADER_TRANSFORMATION_PATH, FRAGMENT_SHADER_PATH);
|
||||||
|
} catch (IOException | GlUtil.GlException e) {
|
||||||
|
throw new FrameProcessingException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -170,9 +170,15 @@ import java.util.Arrays;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void release() {
|
public void release() throws FrameProcessingException {
|
||||||
super.release();
|
super.release();
|
||||||
|
if (glProgram != null) {
|
||||||
|
try {
|
||||||
glProgram.delete();
|
glProgram.delete();
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
throw new FrameProcessingException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,7 +24,6 @@ import android.graphics.Matrix;
|
|||||||
import android.util.Size;
|
import android.util.Size;
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.util.GlUtil;
|
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
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 int requestedHeightPixels;
|
||||||
private final float requestedAspectRatio;
|
private final float requestedAspectRatio;
|
||||||
private final @Layout int layout;
|
private final @Layout int layout;
|
||||||
|
@ -85,10 +85,6 @@ public final class ScaleToFitTransformation implements MatrixTransformation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
|
||||||
GlUtil.glAssertionsEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Matrix transformationMatrix;
|
private final Matrix transformationMatrix;
|
||||||
private @MonotonicNonNull Matrix adjustedTransformationMatrix;
|
private @MonotonicNonNull Matrix adjustedTransformationMatrix;
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ public abstract class SingleFrameGlTextureProcessor implements GlTextureProcesso
|
|||||||
listener.onInputFrameProcessed(inputTexture);
|
listener.onInputFrameProcessed(inputTexture);
|
||||||
listener.onOutputFrameAvailable(outputTexture, presentationTimeUs);
|
listener.onOutputFrameAvailable(outputTexture, presentationTimeUs);
|
||||||
}
|
}
|
||||||
} catch (FrameProcessingException | RuntimeException e) {
|
} catch (FrameProcessingException | GlUtil.GlException | RuntimeException e) {
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
listener.onFrameProcessingError(
|
listener.onFrameProcessingError(
|
||||||
e instanceof FrameProcessingException
|
e instanceof FrameProcessingException
|
||||||
@ -108,7 +108,7 @@ public abstract class SingleFrameGlTextureProcessor implements GlTextureProcesso
|
|||||||
}
|
}
|
||||||
|
|
||||||
@EnsuresNonNull("outputTexture")
|
@EnsuresNonNull("outputTexture")
|
||||||
private void configureOutputTexture(int inputWidth, int inputHeight) {
|
private void configureOutputTexture(int inputWidth, int inputHeight) throws GlUtil.GlException {
|
||||||
this.inputWidth = inputWidth;
|
this.inputWidth = inputWidth;
|
||||||
this.inputHeight = inputHeight;
|
this.inputHeight = inputHeight;
|
||||||
Size outputSize = configure(inputWidth, inputHeight);
|
Size outputSize = configure(inputWidth, inputHeight);
|
||||||
@ -139,9 +139,13 @@ public abstract class SingleFrameGlTextureProcessor implements GlTextureProcesso
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@CallSuper
|
@CallSuper
|
||||||
public void release() {
|
public void release() throws FrameProcessingException {
|
||||||
if (outputTexture != null) {
|
if (outputTexture != null) {
|
||||||
|
try {
|
||||||
GlUtil.deleteTexture(outputTexture.texId);
|
GlUtil.deleteTexture(outputTexture.texId);
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
throw new FrameProcessingException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user