mirror of
https://github.com/samsonjs/media.git
synced 2026-04-05 11:15:46 +00:00
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:
parent
a1ec56a157
commit
75a6908206
3 changed files with 202 additions and 16 deletions
|
|
@ -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)).
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue