Add methods needed by Transformer to GlUtil

This is to add a step to the Transformer transcoding video pipeline to
copy from a surface to another using OpenGL.

PiperOrigin-RevId: 393391005
This commit is contained in:
kimvde 2021-08-27 19:21:29 +01:00 committed by kim-vde
parent a1ec56a157
commit 75a6908206
3 changed files with 202 additions and 16 deletions

View file

@ -11,6 +11,8 @@
* Deprecate `SimpleExoPlayer.Builder`. Use `ExoPlayer.Builder` instead.
* Fix track selection in `StyledPlayerControlView` when using
`ForwardingPlayer`.
* Remove `ExoPlayerLibraryInfo.GL_ASSERTIONS_ENABLED`. Use
`GlUtil.glAssertionsEnabled` instead.
* Extractors:
* Support TS packets without PTS flag
([#9294](https://github.com/google/ExoPlayer/issues/9294)).

View file

@ -57,9 +57,6 @@ public final class ExoPlayerLibraryInfo {
*/
public static final boolean ASSERTIONS_ENABLED = true;
/** Whether an exception should be thrown in case of an OpenGl error. */
public static final boolean GL_ASSERTIONS_ENABLED = false;
/**
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.TraceUtil}
* trace enabled.

View file

@ -20,13 +20,17 @@ import static android.opengl.GLU.gluErrorString;
import android.content.Context;
import android.content.pm.PackageManager;
import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.text.TextUtils;
import androidx.annotation.DoNotInline;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@ -37,6 +41,17 @@ import javax.microedition.khronos.egl.EGL10;
/** GL utilities. */
public final class GlUtil {
/** Thrown when an OpenGL error occurs and {@link #glAssertionsEnabled} is {@code true}. */
public static final class GlException extends RuntimeException {
/** Creates an instance with the specified error message. */
public GlException(String message) {
super(message);
}
}
/** Thrown when the required EGL version is not supported by the device. */
public static final class UnsupportedEglVersionException extends Exception {}
/**
* GL attribute, which can be attached to a buffer with {@link Attribute#setBuffer(float[], int)}.
*/
@ -185,7 +200,7 @@ public final class GlUtil {
}
if (texId == 0) {
throw new IllegalStateException("call setSamplerTexId before bind");
throw new IllegalStateException("Call setSamplerTexId before bind.");
}
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + unit);
if (type == GLES11Ext.GL_SAMPLER_EXTERNAL_OES) {
@ -193,7 +208,7 @@ public final class GlUtil {
} else if (type == GLES20.GL_SAMPLER_2D) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);
} else {
throw new IllegalStateException("unexpected uniform type: " + type);
throw new IllegalStateException("Unexpected uniform type: " + type);
}
GLES20.glUniform1i(location, unit);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
@ -206,6 +221,9 @@ public final class GlUtil {
}
}
/** Whether to throw a {@link GlException} in case of an OpenGL error. */
public static boolean glAssertionsEnabled = false;
private static final String TAG = "GlUtil";
private static final String EXTENSION_PROTECTED_CONTENT = "EGL_EXT_protected_content";
@ -254,9 +272,38 @@ public final class GlUtil {
return eglExtensions != null && eglExtensions.contains(EXTENSION_SURFACELESS_CONTEXT);
}
/** Returns an initialized default {@link EGLDisplay}. */
@RequiresApi(17)
public static EGLDisplay createEglDisplay() {
return Api17.createEglDisplay();
}
/**
* If there is an OpenGl error, logs the error and if {@link
* ExoPlayerLibraryInfo#GL_ASSERTIONS_ENABLED} is true throws a {@link RuntimeException}.
* Returns a new {@link EGLContext} for the specified {@link EGLDisplay}.
*
* @throws UnsupportedEglVersionException If the device does not support EGL version 2. {@code
* eglDisplay} is terminated before the exception is thrown in this case.
*/
@RequiresApi(17)
public static EGLContext createEglContext(EGLDisplay eglDisplay)
throws UnsupportedEglVersionException {
return Api17.createEglContext(eglDisplay);
}
/**
* Returns a new {@link EGLSurface} wrapping the specified {@code surface}.
*
* @param eglDisplay The {@link EGLDisplay} to attach the surface to.
* @param surface The surface to wrap; must be a surface, surface texture or surface holder.
*/
@RequiresApi(17)
public static EGLSurface getEglSurface(EGLDisplay eglDisplay, Object surface) {
return Api17.getEglSurface(eglDisplay, surface);
}
/**
* If there is an OpenGl error, logs the error and if {@link #glAssertionsEnabled} is true throws
* a {@link GlException}.
*/
public static void checkGlError() {
int lastError = GLES20.GL_NO_ERROR;
@ -265,11 +312,21 @@ public final class GlUtil {
Log.e(TAG, "glError " + gluErrorString(error));
lastError = error;
}
if (ExoPlayerLibraryInfo.GL_ASSERTIONS_ENABLED && lastError != GLES20.GL_NO_ERROR) {
throw new RuntimeException("glError " + gluErrorString(lastError));
if (lastError != GLES20.GL_NO_ERROR) {
throwGlException("glError " + gluErrorString(lastError));
}
}
/**
* Makes the specified {@code surface} the render target, using a viewport of {@code width} by
* {@code height} pixels.
*/
@RequiresApi(17)
public static void focusSurface(
EGLDisplay eglDisplay, EGLContext eglContext, EGLSurface surface, int width, int height) {
Api17.focusSurface(eglDisplay, eglContext, surface, width, height);
}
/**
* Builds a GL shader program from vertex and fragment shader code.
*
@ -303,7 +360,7 @@ public final class GlUtil {
int[] linkStatus = new int[] {GLES20.GL_FALSE};
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES20.GL_TRUE) {
throwGlError("Unable to link shader program: \n" + GLES20.glGetProgramInfoLog(program));
throwGlException("Unable to link shader program: \n" + GLES20.glGetProgramInfoLog(program));
}
checkGlError();
@ -315,7 +372,7 @@ public final class GlUtil {
int[] attributeCount = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_ACTIVE_ATTRIBUTES, attributeCount, 0);
if (attributeCount[0] != 2) {
throw new IllegalStateException("expected two attributes");
throw new IllegalStateException("Expected two attributes.");
}
Attribute[] attributes = new Attribute[attributeCount[0]];
@ -338,6 +395,27 @@ public final class GlUtil {
return uniforms;
}
/**
* Deletes a GL texture.
*
* @param textureId The ID of the texture to delete.
*/
public static void deleteTexture(int textureId) {
int[] textures = new int[] {textureId};
GLES20.glDeleteTextures(1, textures, 0);
checkGlError();
}
/**
* Destroys the {@link EGLContext} identified by the provided {@link EGLDisplay} and {@link
* EGLContext}.
*/
@RequiresApi(17)
public static void destroyEglContext(
@Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) {
Api17.destroyEglContext(eglDisplay, eglContext);
}
/**
* Allocates a FloatBuffer with the given data.
*
@ -385,7 +463,7 @@ public final class GlUtil {
int[] result = new int[] {GLES20.GL_FALSE};
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, result, 0);
if (result[0] != GLES20.GL_TRUE) {
throwGlError(GLES20.glGetShaderInfoLog(shader) + ", source: " + source);
throwGlException(GLES20.glGetShaderInfoLog(shader) + ", source: " + source);
}
GLES20.glAttachShader(program, shader);
@ -393,10 +471,16 @@ public final class GlUtil {
checkGlError();
}
private static void throwGlError(String errorMsg) {
private static void throwGlException(String errorMsg) {
Log.e(TAG, errorMsg);
if (ExoPlayerLibraryInfo.GL_ASSERTIONS_ENABLED) {
throw new RuntimeException(errorMsg);
if (glAssertionsEnabled) {
throw new GlException(errorMsg);
}
}
private static void checkEglException(boolean expression, String errorMessage) {
if (!expression) {
throwGlException(errorMessage);
}
}
@ -409,4 +493,107 @@ public final class GlUtil {
}
return strVal.length;
}
@RequiresApi(17)
private static final class Api17 {
private Api17() {}
@DoNotInline
public static EGLDisplay createEglDisplay() {
EGLDisplay eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
checkEglException(!eglDisplay.equals(EGL14.EGL_NO_DISPLAY), "No EGL display.");
int[] major = new int[1];
int[] minor = new int[1];
if (!EGL14.eglInitialize(eglDisplay, major, 0, minor, 0)) {
throwGlException("Error in eglInitialize.");
}
checkGlError();
return eglDisplay;
}
@DoNotInline
public static EGLContext createEglContext(EGLDisplay eglDisplay)
throws UnsupportedEglVersionException {
int[] contextAttributes = {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE};
EGLContext eglContext =
EGL14.eglCreateContext(
eglDisplay, getEglConfig(eglDisplay), EGL14.EGL_NO_CONTEXT, contextAttributes, 0);
if (eglContext == null) {
EGL14.eglTerminate(eglDisplay);
throw new UnsupportedEglVersionException();
}
checkGlError();
return eglContext;
}
@DoNotInline
public static EGLSurface getEglSurface(EGLDisplay eglDisplay, Object surface) {
return EGL14.eglCreateWindowSurface(
eglDisplay, getEglConfig(eglDisplay), surface, new int[] {EGL14.EGL_NONE}, 0);
}
@DoNotInline
public static void focusSurface(
EGLDisplay eglDisplay, EGLContext eglContext, EGLSurface surface, int width, int height) {
int[] fbos = new int[1];
GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, fbos, 0);
int noFbo = 0;
if (fbos[0] != noFbo) {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, noFbo);
}
EGL14.eglMakeCurrent(eglDisplay, surface, surface, eglContext);
GLES20.glViewport(0, 0, width, height);
}
@DoNotInline
public static void destroyEglContext(
@Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) {
if (eglDisplay == null) {
return;
}
EGL14.eglMakeCurrent(
eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
int error = EGL14.eglGetError();
checkEglException(error == EGL14.EGL_SUCCESS, "Error releasing context: " + error);
if (eglContext != null) {
EGL14.eglDestroyContext(eglDisplay, eglContext);
error = EGL14.eglGetError();
checkEglException(error == EGL14.EGL_SUCCESS, "Error destroying context: " + error);
}
EGL14.eglReleaseThread();
error = EGL14.eglGetError();
checkEglException(error == EGL14.EGL_SUCCESS, "Error releasing thread: " + error);
EGL14.eglTerminate(eglDisplay);
error = EGL14.eglGetError();
checkEglException(error == EGL14.EGL_SUCCESS, "Error terminating display: " + error);
}
@DoNotInline
private static EGLConfig getEglConfig(EGLDisplay eglDisplay) {
int redSize = 8;
int greenSize = 8;
int blueSize = 8;
int alphaSize = 8;
int depthSize = 0;
int stencilSize = 0;
int[] defaultConfiguration =
new int[] {
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_RED_SIZE, redSize,
EGL14.EGL_GREEN_SIZE, greenSize,
EGL14.EGL_BLUE_SIZE, blueSize,
EGL14.EGL_ALPHA_SIZE, alphaSize,
EGL14.EGL_DEPTH_SIZE, depthSize,
EGL14.EGL_STENCIL_SIZE, stencilSize,
EGL14.EGL_NONE
};
int[] configsCount = new int[1];
EGLConfig[] eglConfigs = new EGLConfig[1];
if (!EGL14.eglChooseConfig(
eglDisplay, defaultConfiguration, 0, eglConfigs, 0, 1, configsCount, 0)) {
throwGlException("eglChooseConfig failed.");
}
return eglConfigs[0];
}
}
}