Compositor: Split out OpenGL drawing logic into inner class.
Organize logic a bit by splitting logic about how we draw using OpenGL onto a texture, out from the larger class, which has lots of logic discussing how we select frames and streams. No functional change intended, but a few method calls are shuffled around to simplify things. PiperOrigin-RevId: 565426225
This commit is contained in:
parent
16b0ea850f
commit
c77b8c8479
@ -78,16 +78,13 @@ public final class DefaultVideoCompositor implements VideoCompositor {
|
||||
|
||||
private static final String THREAD_NAME = "Effect:DefaultVideoCompositor:GlThread";
|
||||
private static final String TAG = "DefaultVideoCompositor";
|
||||
private static final String VERTEX_SHADER_PATH = "shaders/vertex_shader_transformation_es2.glsl";
|
||||
private static final String FRAGMENT_SHADER_PATH = "shaders/fragment_shader_alpha_scale_es2.glsl";
|
||||
private static final int PRIMARY_INPUT_ID = 0;
|
||||
|
||||
private final Context context;
|
||||
private final VideoCompositor.Listener listener;
|
||||
private final GlTextureProducer.Listener textureOutputListener;
|
||||
private final GlObjectsProvider glObjectsProvider;
|
||||
private final VideoCompositorSettings settings;
|
||||
private final OverlayMatrixProvider overlayMatrixProvider;
|
||||
private final CompositorGlProgram compositorGlProgram;
|
||||
private final VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor;
|
||||
|
||||
@GuardedBy("this")
|
||||
@ -105,7 +102,6 @@ public final class DefaultVideoCompositor implements VideoCompositor {
|
||||
// Only used on the GL Thread.
|
||||
private @MonotonicNonNull EGLContext eglContext;
|
||||
private @MonotonicNonNull EGLDisplay eglDisplay;
|
||||
private @MonotonicNonNull GlProgram glProgram;
|
||||
private @MonotonicNonNull EGLSurface placeholderEglSurface;
|
||||
|
||||
/**
|
||||
@ -122,12 +118,11 @@ public final class DefaultVideoCompositor implements VideoCompositor {
|
||||
VideoCompositor.Listener listener,
|
||||
GlTextureProducer.Listener textureOutputListener,
|
||||
@IntRange(from = 1) int textureOutputCapacity) {
|
||||
this.context = context;
|
||||
this.listener = listener;
|
||||
this.textureOutputListener = textureOutputListener;
|
||||
this.glObjectsProvider = glObjectsProvider;
|
||||
this.settings = settings;
|
||||
this.overlayMatrixProvider = new OverlayMatrixProvider();
|
||||
this.compositorGlProgram = new CompositorGlProgram(context);
|
||||
|
||||
inputSources = new ArrayList<>();
|
||||
outputTexturePool =
|
||||
@ -214,7 +209,8 @@ public final class DefaultVideoCompositor implements VideoCompositor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
public synchronized void release() {
|
||||
checkState(allInputsEnded);
|
||||
try {
|
||||
videoFrameProcessingTaskExecutor.release(/* releaseTask= */ this::releaseGlObjects);
|
||||
} catch (InterruptedException e) {
|
||||
@ -296,8 +292,6 @@ public final class DefaultVideoCompositor implements VideoCompositor {
|
||||
return;
|
||||
}
|
||||
|
||||
ensureGlProgramConfigured();
|
||||
|
||||
InputFrameInfo primaryInputFrame = framesToComposite.get(PRIMARY_INPUT_ID);
|
||||
|
||||
ImmutableList.Builder<Size> inputSizes = new ImmutableList.Builder<>();
|
||||
@ -313,7 +307,7 @@ public final class DefaultVideoCompositor implements VideoCompositor {
|
||||
long outputPresentationTimestampUs = primaryInputFrame.presentationTimeUs;
|
||||
outputTextureTimestamps.add(outputPresentationTimestampUs);
|
||||
|
||||
drawFrame(framesToComposite, outputTexture);
|
||||
compositorGlProgram.drawFrame(framesToComposite, outputTexture);
|
||||
long syncObject = GlUtil.createGlSyncFence();
|
||||
syncObjects.add(syncObject);
|
||||
textureOutputListener.onTextureRendered(
|
||||
@ -403,27 +397,55 @@ public final class DefaultVideoCompositor implements VideoCompositor {
|
||||
maybeComposite();
|
||||
}
|
||||
|
||||
private void ensureGlProgramConfigured()
|
||||
throws VideoFrameProcessingException, GlUtil.GlException {
|
||||
if (glProgram != null) {
|
||||
return;
|
||||
}
|
||||
private void releaseGlObjects() {
|
||||
try {
|
||||
glProgram = new GlProgram(context, VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH);
|
||||
glProgram.setBufferAttribute(
|
||||
"aFramePosition",
|
||||
GlUtil.getNormalizedCoordinateBounds(),
|
||||
GlUtil.HOMOGENEOUS_COORDINATE_VECTOR_SIZE);
|
||||
glProgram.setFloatsUniform("uTexTransformationMatrix", GlUtil.create4x4IdentityMatrix());
|
||||
} catch (IOException e) {
|
||||
throw new VideoFrameProcessingException(e);
|
||||
compositorGlProgram.release();
|
||||
outputTexturePool.deleteAllTextures();
|
||||
GlUtil.destroyEglSurface(eglDisplay, placeholderEglSurface);
|
||||
} catch (GlUtil.GlException e) {
|
||||
Log.e(TAG, "Error releasing GL resources", e);
|
||||
} finally {
|
||||
try {
|
||||
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
||||
} catch (GlUtil.GlException e) {
|
||||
Log.e(TAG, "Error releasing GL context", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper for a {@link GlProgram}, that draws multiple input {@link InputFrameInfo}s onto one
|
||||
* output {@link GlTextureInfo}.
|
||||
*
|
||||
* <p>All methods must be called on a GL thread, unless otherwise stated.
|
||||
*/
|
||||
private static final class CompositorGlProgram {
|
||||
private static final String TAG = "CompositorGlProgram";
|
||||
private static final String VERTEX_SHADER_PATH =
|
||||
"shaders/vertex_shader_transformation_es2.glsl";
|
||||
private static final String FRAGMENT_SHADER_PATH =
|
||||
"shaders/fragment_shader_alpha_scale_es2.glsl";
|
||||
|
||||
private final Context context;
|
||||
private final OverlayMatrixProvider overlayMatrixProvider;
|
||||
private @MonotonicNonNull GlProgram glProgram;
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
*
|
||||
* <p>May be called on any thread.
|
||||
*/
|
||||
public CompositorGlProgram(Context context) {
|
||||
this.context = context;
|
||||
this.overlayMatrixProvider = new OverlayMatrixProvider();
|
||||
}
|
||||
|
||||
/** Draws {@link InputFrameInfo}s onto an output {@link GlTextureInfo}. */
|
||||
// Enhanced for-loops are discouraged in media3.effect due to short-lived allocations.
|
||||
@SuppressWarnings("ListReverse")
|
||||
private void drawFrame(List<InputFrameInfo> framesToComposite, GlTextureInfo outputTexture)
|
||||
throws GlUtil.GlException {
|
||||
public void drawFrame(List<InputFrameInfo> framesToComposite, GlTextureInfo outputTexture)
|
||||
throws GlUtil.GlException, VideoFrameProcessingException {
|
||||
ensureConfigured();
|
||||
GlUtil.focusFramebufferUsingCurrentContext(
|
||||
outputTexture.fboId, outputTexture.width, outputTexture.height);
|
||||
overlayMatrixProvider.configure(new Size(outputTexture.width, outputTexture.height));
|
||||
@ -450,10 +472,35 @@ public final class DefaultVideoCompositor implements VideoCompositor {
|
||||
}
|
||||
|
||||
GLES20.glDisable(GLES20.GL_BLEND);
|
||||
|
||||
GlUtil.checkGlError();
|
||||
}
|
||||
|
||||
public void release() {
|
||||
try {
|
||||
if (glProgram != null) {
|
||||
glProgram.delete();
|
||||
}
|
||||
} catch (GlUtil.GlException e) {
|
||||
Log.e(TAG, "Error releasing GL Program", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureConfigured() throws VideoFrameProcessingException, GlUtil.GlException {
|
||||
if (glProgram != null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
glProgram = new GlProgram(context, VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH);
|
||||
glProgram.setBufferAttribute(
|
||||
"aFramePosition",
|
||||
GlUtil.getNormalizedCoordinateBounds(),
|
||||
GlUtil.HOMOGENEOUS_COORDINATE_VECTOR_SIZE);
|
||||
glProgram.setFloatsUniform("uTexTransformationMatrix", GlUtil.create4x4IdentityMatrix());
|
||||
} catch (IOException e) {
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void blendOntoFocusedTexture(InputFrameInfo inputFrameInfo) throws GlUtil.GlException {
|
||||
GlProgram glProgram = checkNotNull(this.glProgram);
|
||||
GlTextureInfo inputTexture = inputFrameInfo.texture;
|
||||
@ -470,24 +517,6 @@ public final class DefaultVideoCompositor implements VideoCompositor {
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
||||
GlUtil.checkGlError();
|
||||
}
|
||||
|
||||
private synchronized void releaseGlObjects() {
|
||||
try {
|
||||
checkState(allInputsEnded);
|
||||
outputTexturePool.deleteAllTextures();
|
||||
GlUtil.destroyEglSurface(eglDisplay, placeholderEglSurface);
|
||||
if (glProgram != null) {
|
||||
glProgram.delete();
|
||||
}
|
||||
} catch (GlUtil.GlException e) {
|
||||
Log.e(TAG, "Error releasing GL resources", e);
|
||||
} finally {
|
||||
try {
|
||||
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
||||
} catch (GlUtil.GlException e) {
|
||||
Log.e(TAG, "Error releasing GL context", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds information on an input source. */
|
||||
|
Loading…
x
Reference in New Issue
Block a user