mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Add a method to disallow VFP destroying shared eglContext
Previously in MultiInputVideoGraph, each VFP would destroy the shared eglContext, such that the same eglContext object is destroyed multiple times. Adding a flag to disallow this. The alternative being we could add a flag on the VFP constructor, but I think that is too subscriptive (meaning if we later might want to add another boolean to control another GL behaviour, multiple booleans would make the class less reason-able), and would incur a lot of code changes at places. PiperOrigin-RevId: 660354367
This commit is contained in:
parent
a087f82fa8
commit
8f8e48731e
@ -77,6 +77,7 @@
|
|||||||
such that output is closer to expected.
|
such that output is closer to expected.
|
||||||
* Speed up `DefaultVideoFrameProcessor.queueInputBitmap()`. As a result,
|
* Speed up `DefaultVideoFrameProcessor.queueInputBitmap()`. As a result,
|
||||||
exporting images to videos with `Transformer` is faster.
|
exporting images to videos with `Transformer` is faster.
|
||||||
|
* Add a `release()` method to `GlObjectsProvider`.
|
||||||
* Muxers:
|
* Muxers:
|
||||||
* IMA extension:
|
* IMA extension:
|
||||||
* Fix bug where clearing the playlist may cause an
|
* Fix bug where clearing the playlist may cause an
|
||||||
|
@ -81,4 +81,11 @@ public interface GlObjectsProvider {
|
|||||||
* @throws GlException If an error occurs during creation.
|
* @throws GlException If an error occurs during creation.
|
||||||
*/
|
*/
|
||||||
GlTextureInfo createBuffersForTexture(int texId, int width, int height) throws GlException;
|
GlTextureInfo createBuffersForTexture(int texId, int width, int height) throws GlException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases the created objects.
|
||||||
|
*
|
||||||
|
* @param eglDisplay The {@link EGLDisplay} to release the objects for.
|
||||||
|
*/
|
||||||
|
void release(EGLDisplay eglDisplay) throws GlException;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.effect;
|
package androidx.media3.effect;
|
||||||
|
|
||||||
|
import static androidx.media3.common.util.GlUtil.destroyEglContext;
|
||||||
|
|
||||||
import android.opengl.EGL14;
|
import android.opengl.EGL14;
|
||||||
import android.opengl.EGLContext;
|
import android.opengl.EGLContext;
|
||||||
import android.opengl.EGLDisplay;
|
import android.opengl.EGLDisplay;
|
||||||
@ -25,6 +27,8 @@ import androidx.media3.common.GlObjectsProvider;
|
|||||||
import androidx.media3.common.GlTextureInfo;
|
import androidx.media3.common.GlTextureInfo;
|
||||||
import androidx.media3.common.util.GlUtil;
|
import androidx.media3.common.util.GlUtil;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
// TODO(b/261820382): Add tests for sharing context.
|
// TODO(b/261820382): Add tests for sharing context.
|
||||||
/**
|
/**
|
||||||
@ -38,6 +42,7 @@ import androidx.media3.common.util.UnstableApi;
|
|||||||
public final class DefaultGlObjectsProvider implements GlObjectsProvider {
|
public final class DefaultGlObjectsProvider implements GlObjectsProvider {
|
||||||
|
|
||||||
private final EGLContext sharedEglContext;
|
private final EGLContext sharedEglContext;
|
||||||
|
private final List<EGLContext> createdEglContexts;
|
||||||
|
|
||||||
/** Creates an instance with no shared EGL context. */
|
/** Creates an instance with no shared EGL context. */
|
||||||
public DefaultGlObjectsProvider() {
|
public DefaultGlObjectsProvider() {
|
||||||
@ -51,12 +56,16 @@ public final class DefaultGlObjectsProvider implements GlObjectsProvider {
|
|||||||
*/
|
*/
|
||||||
public DefaultGlObjectsProvider(@Nullable EGLContext sharedEglContext) {
|
public DefaultGlObjectsProvider(@Nullable EGLContext sharedEglContext) {
|
||||||
this.sharedEglContext = sharedEglContext != null ? sharedEglContext : EGL14.EGL_NO_CONTEXT;
|
this.sharedEglContext = sharedEglContext != null ? sharedEglContext : EGL14.EGL_NO_CONTEXT;
|
||||||
|
createdEglContexts = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EGLContext createEglContext(
|
public EGLContext createEglContext(
|
||||||
EGLDisplay eglDisplay, int openGlVersion, int[] configAttributes) throws GlUtil.GlException {
|
EGLDisplay eglDisplay, int openGlVersion, int[] configAttributes) throws GlUtil.GlException {
|
||||||
return GlUtil.createEglContext(sharedEglContext, eglDisplay, openGlVersion, configAttributes);
|
EGLContext eglContext =
|
||||||
|
GlUtil.createEglContext(sharedEglContext, eglDisplay, openGlVersion, configAttributes);
|
||||||
|
createdEglContexts.add(eglContext);
|
||||||
|
return eglContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -81,4 +90,11 @@ public final class DefaultGlObjectsProvider implements GlObjectsProvider {
|
|||||||
int fboId = GlUtil.createFboForTexture(texId);
|
int fboId = GlUtil.createFboForTexture(texId);
|
||||||
return new GlTextureInfo(texId, fboId, /* rboId= */ C.INDEX_UNSET, width, height);
|
return new GlTextureInfo(texId, fboId, /* rboId= */ C.INDEX_UNSET, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void release(EGLDisplay eglDisplay) throws GlUtil.GlException {
|
||||||
|
for (int i = 0; i < createdEglContexts.size(); i++) {
|
||||||
|
destroyEglContext(eglDisplay, createdEglContexts.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -420,9 +420,9 @@ public final class DefaultVideoCompositor implements VideoCompositor {
|
|||||||
Log.e(TAG, "Error releasing GL resources", e);
|
Log.e(TAG, "Error releasing GL resources", e);
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
glObjectsProvider.release(checkNotNull(eglDisplay));
|
||||||
} catch (GlUtil.GlException e) {
|
} catch (GlUtil.GlException e) {
|
||||||
Log.e(TAG, "Error releasing GL context", e);
|
Log.e(TAG, "Error releasing GL objects", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import static androidx.media3.common.util.Assertions.checkArgument;
|
|||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static androidx.media3.common.util.Assertions.checkState;
|
import static androidx.media3.common.util.Assertions.checkState;
|
||||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||||
|
import static androidx.media3.common.util.GlUtil.getDefaultEglDisplay;
|
||||||
import static androidx.media3.common.util.Util.SDK_INT;
|
import static androidx.media3.common.util.Util.SDK_INT;
|
||||||
import static androidx.media3.effect.DebugTraceUtil.COMPONENT_VFP;
|
import static androidx.media3.effect.DebugTraceUtil.COMPONENT_VFP;
|
||||||
import static androidx.media3.effect.DebugTraceUtil.EVENT_RECEIVE_END_OF_ALL_INPUT;
|
import static androidx.media3.effect.DebugTraceUtil.EVENT_RECEIVE_END_OF_ALL_INPUT;
|
||||||
@ -445,7 +446,6 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
|||||||
private final Context context;
|
private final Context context;
|
||||||
private final GlObjectsProvider glObjectsProvider;
|
private final GlObjectsProvider glObjectsProvider;
|
||||||
private final EGLDisplay eglDisplay;
|
private final EGLDisplay eglDisplay;
|
||||||
private final EGLContext eglContext;
|
|
||||||
private final InputSwitcher inputSwitcher;
|
private final InputSwitcher inputSwitcher;
|
||||||
private final VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor;
|
private final VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor;
|
||||||
private final VideoFrameProcessor.Listener listener;
|
private final VideoFrameProcessor.Listener listener;
|
||||||
@ -483,10 +483,9 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
|||||||
Context context,
|
Context context,
|
||||||
GlObjectsProvider glObjectsProvider,
|
GlObjectsProvider glObjectsProvider,
|
||||||
EGLDisplay eglDisplay,
|
EGLDisplay eglDisplay,
|
||||||
EGLContext eglContext,
|
|
||||||
InputSwitcher inputSwitcher,
|
InputSwitcher inputSwitcher,
|
||||||
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor,
|
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor,
|
||||||
VideoFrameProcessor.Listener listener,
|
Listener listener,
|
||||||
Executor listenerExecutor,
|
Executor listenerExecutor,
|
||||||
FinalShaderProgramWrapper finalShaderProgramWrapper,
|
FinalShaderProgramWrapper finalShaderProgramWrapper,
|
||||||
boolean renderFramesAutomatically,
|
boolean renderFramesAutomatically,
|
||||||
@ -494,7 +493,6 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
|||||||
this.context = context;
|
this.context = context;
|
||||||
this.glObjectsProvider = glObjectsProvider;
|
this.glObjectsProvider = glObjectsProvider;
|
||||||
this.eglDisplay = eglDisplay;
|
this.eglDisplay = eglDisplay;
|
||||||
this.eglContext = eglContext;
|
|
||||||
this.inputSwitcher = inputSwitcher;
|
this.inputSwitcher = inputSwitcher;
|
||||||
this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor;
|
this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
@ -820,7 +818,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
|||||||
boolean experimentalAdjustSurfaceTextureTransformationMatrix,
|
boolean experimentalAdjustSurfaceTextureTransformationMatrix,
|
||||||
boolean experimentalRepeatInputBitmapWithoutResampling)
|
boolean experimentalRepeatInputBitmapWithoutResampling)
|
||||||
throws GlUtil.GlException, VideoFrameProcessingException {
|
throws GlUtil.GlException, VideoFrameProcessingException {
|
||||||
EGLDisplay eglDisplay = GlUtil.getDefaultEglDisplay();
|
EGLDisplay eglDisplay = getDefaultEglDisplay();
|
||||||
int[] configAttributes =
|
int[] configAttributes =
|
||||||
ColorInfo.isTransferHdr(outputColorInfo)
|
ColorInfo.isTransferHdr(outputColorInfo)
|
||||||
? GlUtil.EGL_CONFIG_ATTRIBUTES_RGBA_1010102
|
? GlUtil.EGL_CONFIG_ATTRIBUTES_RGBA_1010102
|
||||||
@ -873,7 +871,6 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
|||||||
context,
|
context,
|
||||||
glObjectsProvider,
|
glObjectsProvider,
|
||||||
eglDisplay,
|
eglDisplay,
|
||||||
eglContextAndPlaceholderSurface.first,
|
|
||||||
inputSwitcher,
|
inputSwitcher,
|
||||||
videoFrameProcessingTaskExecutor,
|
videoFrameProcessingTaskExecutor,
|
||||||
listener,
|
listener,
|
||||||
@ -1106,9 +1103,9 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
glObjectsProvider.release(eglDisplay);
|
||||||
} catch (GlUtil.GlException e) {
|
} catch (GlUtil.GlException e) {
|
||||||
Log.e(TAG, "Error releasing GL context", e);
|
Log.e(TAG, "Error releasing GL objects", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@ import static androidx.media3.common.util.Assertions.checkArgument;
|
|||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static androidx.media3.common.util.Assertions.checkState;
|
import static androidx.media3.common.util.Assertions.checkState;
|
||||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||||
|
import static androidx.media3.common.util.GlUtil.destroyEglContext;
|
||||||
|
import static androidx.media3.common.util.GlUtil.getDefaultEglDisplay;
|
||||||
import static androidx.media3.common.util.Util.contains;
|
import static androidx.media3.common.util.Util.contains;
|
||||||
import static androidx.media3.common.util.Util.newSingleThreadScheduledExecutor;
|
import static androidx.media3.common.util.Util.newSingleThreadScheduledExecutor;
|
||||||
import static androidx.media3.effect.DebugTraceUtil.COMPONENT_COMPOSITOR;
|
import static androidx.media3.effect.DebugTraceUtil.COMPONENT_COMPOSITOR;
|
||||||
@ -47,6 +49,8 @@ import androidx.media3.common.VideoFrameProcessingException;
|
|||||||
import androidx.media3.common.VideoFrameProcessor;
|
import androidx.media3.common.VideoFrameProcessor;
|
||||||
import androidx.media3.common.VideoGraph;
|
import androidx.media3.common.VideoGraph;
|
||||||
import androidx.media3.common.util.GlUtil;
|
import androidx.media3.common.util.GlUtil;
|
||||||
|
import androidx.media3.common.util.GlUtil.GlException;
|
||||||
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
@ -61,6 +65,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public abstract class MultipleInputVideoGraph implements VideoGraph {
|
public abstract class MultipleInputVideoGraph implements VideoGraph {
|
||||||
|
|
||||||
|
private static final String TAG = "MultiInputVG";
|
||||||
private static final String SHARED_EXECUTOR_NAME = "Effect:MultipleInputVideoGraph:Thread";
|
private static final String SHARED_EXECUTOR_NAME = "Effect:MultipleInputVideoGraph:Thread";
|
||||||
|
|
||||||
private static final long RELEASE_WAIT_TIME_MS = 1_000;
|
private static final long RELEASE_WAIT_TIME_MS = 1_000;
|
||||||
@ -70,7 +75,7 @@ public abstract class MultipleInputVideoGraph implements VideoGraph {
|
|||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
private final ColorInfo outputColorInfo;
|
private final ColorInfo outputColorInfo;
|
||||||
private final GlObjectsProvider glObjectsProvider;
|
private final SingleContextGlObjectsProvider glObjectsProvider;
|
||||||
private final DebugViewProvider debugViewProvider;
|
private final DebugViewProvider debugViewProvider;
|
||||||
private final VideoGraph.Listener listener;
|
private final VideoGraph.Listener listener;
|
||||||
private final Executor listenerExecutor;
|
private final Executor listenerExecutor;
|
||||||
@ -307,6 +312,15 @@ public abstract class MultipleInputVideoGraph implements VideoGraph {
|
|||||||
compositionVideoFrameProcessor = null;
|
compositionVideoFrameProcessor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// The eglContext is not released by any of the frame processors.
|
||||||
|
if (glObjectsProvider.singleEglContext != null) {
|
||||||
|
destroyEglContext(getDefaultEglDisplay(), glObjectsProvider.singleEglContext);
|
||||||
|
}
|
||||||
|
} catch (GlUtil.GlException e) {
|
||||||
|
Log.e(TAG, "Error releasing GL context", e);
|
||||||
|
}
|
||||||
|
|
||||||
sharedExecutorService.shutdown();
|
sharedExecutorService.shutdown();
|
||||||
try {
|
try {
|
||||||
sharedExecutorService.awaitTermination(RELEASE_WAIT_TIME_MS, MILLISECONDS);
|
sharedExecutorService.awaitTermination(RELEASE_WAIT_TIME_MS, MILLISECONDS);
|
||||||
@ -472,8 +486,7 @@ public abstract class MultipleInputVideoGraph implements VideoGraph {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EGLContext createEglContext(
|
public EGLContext createEglContext(
|
||||||
EGLDisplay eglDisplay, int openGlVersion, int[] configAttributes)
|
EGLDisplay eglDisplay, int openGlVersion, int[] configAttributes) throws GlException {
|
||||||
throws GlUtil.GlException {
|
|
||||||
if (singleEglContext == null) {
|
if (singleEglContext == null) {
|
||||||
singleEglContext =
|
singleEglContext =
|
||||||
glObjectsProvider.createEglContext(eglDisplay, openGlVersion, configAttributes);
|
glObjectsProvider.createEglContext(eglDisplay, openGlVersion, configAttributes);
|
||||||
@ -487,21 +500,26 @@ public abstract class MultipleInputVideoGraph implements VideoGraph {
|
|||||||
Object surface,
|
Object surface,
|
||||||
@C.ColorTransfer int colorTransfer,
|
@C.ColorTransfer int colorTransfer,
|
||||||
boolean isEncoderInputSurface)
|
boolean isEncoderInputSurface)
|
||||||
throws GlUtil.GlException {
|
throws GlException {
|
||||||
return glObjectsProvider.createEglSurface(
|
return glObjectsProvider.createEglSurface(
|
||||||
eglDisplay, surface, colorTransfer, isEncoderInputSurface);
|
eglDisplay, surface, colorTransfer, isEncoderInputSurface);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EGLSurface createFocusedPlaceholderEglSurface(
|
public EGLSurface createFocusedPlaceholderEglSurface(
|
||||||
EGLContext eglContext, EGLDisplay eglDisplay) throws GlUtil.GlException {
|
EGLContext eglContext, EGLDisplay eglDisplay) throws GlException {
|
||||||
return glObjectsProvider.createFocusedPlaceholderEglSurface(eglContext, eglDisplay);
|
return glObjectsProvider.createFocusedPlaceholderEglSurface(eglContext, eglDisplay);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GlTextureInfo createBuffersForTexture(int texId, int width, int height)
|
public GlTextureInfo createBuffersForTexture(int texId, int width, int height)
|
||||||
throws GlUtil.GlException {
|
throws GlException {
|
||||||
return glObjectsProvider.createBuffersForTexture(texId, width, height);
|
return glObjectsProvider.createBuffersForTexture(texId, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void release(EGLDisplay eglDisplay) {
|
||||||
|
// The eglContext is released in the VideoGraph after all VideoFrameProcessors are released.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user