Pass GlObjectsProvider
to methods
By passing this class where it's needed, implementations don't need to store it in a field (reducing boilerplate) and it's clearer that it can't be unset when needed. PiperOrigin-RevId: 542823522
This commit is contained in:
parent
c2d8051662
commit
90c8f642af
@ -161,10 +161,8 @@ import java.util.concurrent.Future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) {}
|
||||
|
||||
@Override
|
||||
public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
public void queueInputFrame(
|
||||
GlObjectsProvider glObjectsProvider, GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
AppTextureFrame appTextureFrame =
|
||||
new AppTextureFrame(
|
||||
inputTexture.getTexId(), inputTexture.getWidth(), inputTexture.getHeight());
|
||||
|
@ -22,7 +22,6 @@ import android.opengl.EGLDisplay;
|
||||
import android.opengl.EGLSurface;
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.GlUtil.GlException;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
@ -30,45 +29,6 @@ import androidx.media3.common.util.UnstableApi;
|
||||
/** Provider to customize the creation and maintenance of GL objects. */
|
||||
@UnstableApi
|
||||
public interface GlObjectsProvider {
|
||||
/**
|
||||
* @deprecated Please use {@code DefaultGlObjectsProvider} in {@code androidx.media3.effect}.
|
||||
*/
|
||||
@Deprecated
|
||||
GlObjectsProvider DEFAULT =
|
||||
new GlObjectsProvider() {
|
||||
@Override
|
||||
@RequiresApi(17)
|
||||
public EGLContext createEglContext(
|
||||
EGLDisplay eglDisplay, int openGlVersion, int[] configAttributes) throws GlException {
|
||||
return GlUtil.createEglContext(
|
||||
EGL14.EGL_NO_CONTEXT, eglDisplay, openGlVersion, configAttributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RequiresApi(17)
|
||||
public EGLSurface createEglSurface(
|
||||
EGLDisplay eglDisplay,
|
||||
Object surface,
|
||||
@C.ColorTransfer int colorTransfer,
|
||||
boolean isEncoderInputSurface)
|
||||
throws GlException {
|
||||
return GlUtil.createEglSurface(eglDisplay, surface, colorTransfer, isEncoderInputSurface);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RequiresApi(17)
|
||||
public EGLSurface createFocusedPlaceholderEglSurface(
|
||||
EGLContext eglContext, EGLDisplay eglDisplay) throws GlException {
|
||||
return GlUtil.createFocusedPlaceholderEglSurface(eglContext, eglDisplay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GlTextureInfo createBuffersForTexture(int texId, int width, int height)
|
||||
throws GlException {
|
||||
int fboId = GlUtil.createFboForTexture(texId);
|
||||
return new GlTextureInfo(texId, fboId, /* rboId= */ C.INDEX_UNSET, width, height);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new {@link EGLContext} for the specified {@link EGLDisplay}.
|
||||
|
@ -412,10 +412,8 @@ public final class DefaultVideoFrameProcessorVideoFrameRenderingTest {
|
||||
public void setErrorListener(Executor executor, ErrorListener errorListener) {}
|
||||
|
||||
@Override
|
||||
public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) {}
|
||||
|
||||
@Override
|
||||
public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
public void queueInputFrame(
|
||||
GlObjectsProvider glObjectsProvider, GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
// No input is queued in these tests. The BlankFrameProducer is used to produce frames.
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
@ -15,8 +15,6 @@
|
||||
*/
|
||||
package androidx.media3.effect;
|
||||
|
||||
import static androidx.media3.common.util.Assertions.checkState;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.media3.common.GlObjectsProvider;
|
||||
import androidx.media3.common.GlTextureInfo;
|
||||
@ -49,7 +47,6 @@ public abstract class BaseGlShaderProgram implements GlShaderProgram {
|
||||
private OutputListener outputListener;
|
||||
private ErrorListener errorListener;
|
||||
private Executor errorListenerExecutor;
|
||||
private boolean frameProcessingStarted;
|
||||
|
||||
/**
|
||||
* Creates a {@code BaseGlShaderProgram} instance.
|
||||
@ -119,20 +116,12 @@ public abstract class BaseGlShaderProgram implements GlShaderProgram {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) {
|
||||
checkState(
|
||||
!frameProcessingStarted,
|
||||
"The GlObjectsProvider cannot be set after frame processing has started.");
|
||||
outputTexturePool.setGlObjectsProvider(glObjectsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
public void queueInputFrame(
|
||||
GlObjectsProvider glObjectsProvider, GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
try {
|
||||
Size outputTextureSize = configure(inputTexture.getWidth(), inputTexture.getHeight());
|
||||
outputTexturePool.ensureConfigured(
|
||||
outputTextureSize.getWidth(), outputTextureSize.getHeight());
|
||||
frameProcessingStarted = true;
|
||||
glObjectsProvider, outputTextureSize.getWidth(), outputTextureSize.getHeight());
|
||||
|
||||
// Focus on the next free buffer.
|
||||
GlTextureInfo outputTexture = outputTexturePool.useTexture();
|
||||
@ -152,21 +141,18 @@ public abstract class BaseGlShaderProgram implements GlShaderProgram {
|
||||
|
||||
@Override
|
||||
public void releaseOutputFrame(GlTextureInfo outputTexture) {
|
||||
frameProcessingStarted = true;
|
||||
outputTexturePool.freeTexture(outputTexture);
|
||||
inputListener.onReadyToAcceptInputFrame();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void signalEndOfCurrentInputStream() {
|
||||
frameProcessingStarted = true;
|
||||
outputListener.onCurrentOutputStreamEnded();
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
public void flush() {
|
||||
frameProcessingStarted = true;
|
||||
outputTexturePool.freeAllTextures();
|
||||
inputListener.onFlush();
|
||||
for (int i = 0; i < outputTexturePool.capacity(); i++) {
|
||||
@ -177,7 +163,6 @@ public abstract class BaseGlShaderProgram implements GlShaderProgram {
|
||||
@Override
|
||||
@CallSuper
|
||||
public void release() throws VideoFrameProcessingException {
|
||||
frameProcessingStarted = true;
|
||||
try {
|
||||
outputTexturePool.deleteAllTextures();
|
||||
} catch (GlUtil.GlException e) {
|
||||
|
@ -25,6 +25,7 @@ import android.opengl.GLUtils;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.FrameInfo;
|
||||
import androidx.media3.common.GlObjectsProvider;
|
||||
import androidx.media3.common.GlTextureInfo;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
@ -47,6 +48,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
"Unsupported Image Configuration: No more than 8 bits of precision should be used for each"
|
||||
+ " RGB channel.";
|
||||
|
||||
private final GlObjectsProvider glObjectsProvider;
|
||||
private final GlShaderProgram shaderProgram;
|
||||
private final VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor;
|
||||
// The queue holds all bitmaps with one or more frames pending to be sent downstream.
|
||||
@ -62,14 +64,17 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param glObjectsProvider The {@link GlObjectsProvider} for using EGL and GLES.
|
||||
* @param shaderProgram The {@link GlShaderProgram} for which this {@code BitmapTextureManager}
|
||||
* will be set as the {@link GlShaderProgram.InputListener}.
|
||||
* @param videoFrameProcessingTaskExecutor The {@link VideoFrameProcessingTaskExecutor} that the
|
||||
* methods of this class run on.
|
||||
*/
|
||||
public BitmapTextureManager(
|
||||
GlObjectsProvider glObjectsProvider,
|
||||
GlShaderProgram shaderProgram,
|
||||
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor) {
|
||||
this.glObjectsProvider = glObjectsProvider;
|
||||
this.shaderProgram = shaderProgram;
|
||||
this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor;
|
||||
pendingBitmaps = new LinkedBlockingQueue<>();
|
||||
@ -187,7 +192,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
framesToQueueForCurrentBitmap--;
|
||||
downstreamShaderProgramCapacity--;
|
||||
shaderProgram.queueInputFrame(
|
||||
checkNotNull(currentGlTextureInfo), round(currentPresentationTimeUs));
|
||||
glObjectsProvider, checkNotNull(currentGlTextureInfo), round(currentPresentationTimeUs));
|
||||
currentPresentationTimeUs += currentBitmapInfo.frameDurationUs;
|
||||
|
||||
if (framesToQueueForCurrentBitmap == 0) {
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package androidx.media3.effect;
|
||||
|
||||
import androidx.media3.common.GlObjectsProvider;
|
||||
import androidx.media3.common.GlTextureInfo;
|
||||
import androidx.media3.effect.GlShaderProgram.InputListener;
|
||||
import androidx.media3.effect.GlShaderProgram.OutputListener;
|
||||
@ -35,6 +36,7 @@ import androidx.media3.effect.GlShaderProgram.OutputListener;
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param glObjectsProvider The {@link GlObjectsProvider} for using EGL and GLES.
|
||||
* @param producingGlShaderProgram The {@link GlShaderProgram} for which this listener will be set
|
||||
* as {@link OutputListener}.
|
||||
* @param consumingGlShaderProgram The {@link GlShaderProgram} for which this listener will be set
|
||||
@ -45,12 +47,14 @@ import androidx.media3.effect.GlShaderProgram.OutputListener;
|
||||
* releasing the {@link VideoFrameProcessingTaskExecutor}.
|
||||
*/
|
||||
public ChainingGlShaderProgramListener(
|
||||
GlObjectsProvider glObjectsProvider,
|
||||
GlShaderProgram producingGlShaderProgram,
|
||||
GlShaderProgram consumingGlShaderProgram,
|
||||
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor) {
|
||||
this.producingGlShaderProgram = producingGlShaderProgram;
|
||||
frameConsumptionManager =
|
||||
new FrameConsumptionManager(consumingGlShaderProgram, videoFrameProcessingTaskExecutor);
|
||||
new FrameConsumptionManager(
|
||||
glObjectsProvider, consumingGlShaderProgram, videoFrameProcessingTaskExecutor);
|
||||
this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor;
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,6 @@ import androidx.media3.common.util.Size;
|
||||
*/
|
||||
/* package */ final class DefaultFrameDroppingShaderProgram extends FrameCacheGlShaderProgram {
|
||||
|
||||
private final GlObjectsProvider glObjectsProvider;
|
||||
private final boolean useHdr;
|
||||
private final long targetFrameDeltaUs;
|
||||
|
||||
@ -69,25 +68,25 @@ import androidx.media3.common.util.Size;
|
||||
this.targetFrameDeltaUs = (long) (C.MICROS_PER_SECOND / targetFrameRate);
|
||||
lastQueuedPresentationTimeUs = C.TIME_UNSET;
|
||||
previousPresentationTimeUs = C.TIME_UNSET;
|
||||
glObjectsProvider = new DefaultGlObjectsProvider(/* sharedEglContext= */ null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
public void queueInputFrame(
|
||||
GlObjectsProvider glObjectsProvider, GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
framesReceived++;
|
||||
if (framesReceived == 1) {
|
||||
copyTextureToPreviousFrame(inputTexture, presentationTimeUs);
|
||||
queuePreviousFrame();
|
||||
copyTextureToPreviousFrame(glObjectsProvider, inputTexture, presentationTimeUs);
|
||||
queuePreviousFrame(glObjectsProvider);
|
||||
getInputListener().onInputFrameProcessed(inputTexture);
|
||||
getInputListener().onReadyToAcceptInputFrame();
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldQueuePreviousFrame(presentationTimeUs)) {
|
||||
queuePreviousFrame();
|
||||
queuePreviousFrame(glObjectsProvider);
|
||||
}
|
||||
|
||||
copyTextureToPreviousFrame(inputTexture, presentationTimeUs);
|
||||
copyTextureToPreviousFrame(glObjectsProvider, inputTexture, presentationTimeUs);
|
||||
getInputListener().onInputFrameProcessed(inputTexture);
|
||||
getInputListener().onReadyToAcceptInputFrame();
|
||||
}
|
||||
@ -129,7 +128,8 @@ import androidx.media3.common.util.Size;
|
||||
framesReceived = 0;
|
||||
}
|
||||
|
||||
private void copyTextureToPreviousFrame(GlTextureInfo newTexture, long presentationTimeUs) {
|
||||
private void copyTextureToPreviousFrame(
|
||||
GlObjectsProvider glObjectsProvider, GlTextureInfo newTexture, long presentationTimeUs) {
|
||||
try {
|
||||
if (previousTexture == null) {
|
||||
int texId = GlUtil.createTexture(newTexture.getWidth(), newTexture.getHeight(), useHdr);
|
||||
@ -171,12 +171,12 @@ import androidx.media3.common.util.Size;
|
||||
< abs(currentFrameTimeDeltaUs - targetFrameDeltaUs);
|
||||
}
|
||||
|
||||
private void queuePreviousFrame() {
|
||||
private void queuePreviousFrame(GlObjectsProvider glObjectsProvider) {
|
||||
try {
|
||||
GlTextureInfo previousTexture = checkNotNull(this.previousTexture);
|
||||
Size outputTextureSize = configure(previousTexture.getWidth(), previousTexture.getHeight());
|
||||
outputTexturePool.ensureConfigured(
|
||||
outputTextureSize.getWidth(), outputTextureSize.getHeight());
|
||||
glObjectsProvider, outputTextureSize.getWidth(), outputTextureSize.getHeight());
|
||||
|
||||
// Focus on the next free buffer.
|
||||
GlTextureInfo outputTexture = outputTexturePool.useTexture();
|
||||
|
@ -39,10 +39,15 @@ public final class DefaultGlObjectsProvider implements GlObjectsProvider {
|
||||
|
||||
private final EGLContext sharedEglContext;
|
||||
|
||||
/** Creates an instance with no shared EGL context. */
|
||||
public DefaultGlObjectsProvider() {
|
||||
this(/* sharedEglContext= */ null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
* Creates an instance with the specified shared EGL context.
|
||||
*
|
||||
* @param sharedEglContext The {@link EGLContext} with which to share data.
|
||||
* @param sharedEglContext The context with which to share data, or {@code null} if none.
|
||||
*/
|
||||
public DefaultGlObjectsProvider(@Nullable EGLContext sharedEglContext) {
|
||||
this.sharedEglContext = sharedEglContext != null ? sharedEglContext : EGL14.EGL_NO_CONTEXT;
|
||||
|
@ -102,7 +102,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
/** Creates an instance. */
|
||||
public Builder() {
|
||||
enableColorTransfers = true;
|
||||
glObjectsProvider = GlObjectsProvider.DEFAULT;
|
||||
glObjectsProvider = new DefaultGlObjectsProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,7 +119,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
/**
|
||||
* Sets the {@link GlObjectsProvider}.
|
||||
*
|
||||
* <p>The default value is {@link GlObjectsProvider#DEFAULT}.
|
||||
* <p>The default value is a {@link DefaultGlObjectsProvider}.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setGlObjectsProvider(GlObjectsProvider glObjectsProvider) {
|
||||
@ -285,6 +285,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
private static final long RELEASE_WAIT_TIME_MS = 500;
|
||||
|
||||
private final Context context;
|
||||
private final GlObjectsProvider glObjectsProvider;
|
||||
private final EGLDisplay eglDisplay;
|
||||
private final EGLContext eglContext;
|
||||
private final InputSwitcher inputSwitcher;
|
||||
@ -303,7 +304,6 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
private final List<Effect> activeEffects;
|
||||
private final Object lock;
|
||||
private final ColorInfo outputColorInfo;
|
||||
private final GlObjectsProvider glObjectsProvider;
|
||||
|
||||
// CountDownLatch to wait for the current input stream to finish processing.
|
||||
private volatile @MonotonicNonNull CountDownLatch latch;
|
||||
@ -313,6 +313,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
|
||||
private DefaultVideoFrameProcessor(
|
||||
Context context,
|
||||
GlObjectsProvider glObjectsProvider,
|
||||
EGLDisplay eglDisplay,
|
||||
EGLContext eglContext,
|
||||
InputSwitcher inputSwitcher,
|
||||
@ -322,9 +323,9 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
ImmutableList<GlShaderProgram> intermediateGlShaderPrograms,
|
||||
FinalShaderProgramWrapper finalShaderProgramWrapper,
|
||||
boolean renderFramesAutomatically,
|
||||
ColorInfo outputColorInfo,
|
||||
GlObjectsProvider glObjectsProvider) {
|
||||
ColorInfo outputColorInfo) {
|
||||
this.context = context;
|
||||
this.glObjectsProvider = glObjectsProvider;
|
||||
this.eglDisplay = eglDisplay;
|
||||
this.eglContext = eglContext;
|
||||
this.inputSwitcher = inputSwitcher;
|
||||
@ -335,7 +336,6 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
this.activeEffects = new ArrayList<>();
|
||||
this.lock = new Object();
|
||||
this.outputColorInfo = outputColorInfo;
|
||||
this.glObjectsProvider = glObjectsProvider;
|
||||
this.finalShaderProgramWrapper = finalShaderProgramWrapper;
|
||||
finalShaderProgramWrapper.setOnInputStreamProcessedListener(
|
||||
() -> {
|
||||
@ -461,8 +461,8 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
inputSwitcher.setDownstreamShaderProgram(
|
||||
getFirst(
|
||||
intermediateGlShaderPrograms, /* defaultValue= */ finalShaderProgramWrapper));
|
||||
setGlObjectProviderOnShaderPrograms(intermediateGlShaderPrograms, glObjectsProvider);
|
||||
chainShaderProgramsWithListeners(
|
||||
glObjectsProvider,
|
||||
intermediateGlShaderPrograms,
|
||||
finalShaderProgramWrapper,
|
||||
videoFrameProcessingTaskExecutor,
|
||||
@ -521,7 +521,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
!renderFramesAutomatically,
|
||||
"Calling this method is not allowed when renderFramesAutomatically is enabled");
|
||||
videoFrameProcessingTaskExecutor.submitWithHighPriority(
|
||||
() -> finalShaderProgramWrapper.renderOutputFrame(renderTimeNs));
|
||||
() -> finalShaderProgramWrapper.renderOutputFrame(glObjectsProvider, renderTimeNs));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -658,7 +658,6 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
videoFrameProcessingTaskExecutor,
|
||||
videoFrameProcessorListenerExecutor,
|
||||
listener,
|
||||
glObjectsProvider,
|
||||
textureOutputListener,
|
||||
textureOutputCapacity);
|
||||
|
||||
@ -681,8 +680,8 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
inputSwitcher.setDownstreamShaderProgram(
|
||||
getFirst(intermediateGlShaderPrograms, /* defaultValue= */ finalShaderProgramWrapper));
|
||||
|
||||
setGlObjectProviderOnShaderPrograms(intermediateGlShaderPrograms, glObjectsProvider);
|
||||
chainShaderProgramsWithListeners(
|
||||
glObjectsProvider,
|
||||
intermediateGlShaderPrograms,
|
||||
finalShaderProgramWrapper,
|
||||
videoFrameProcessingTaskExecutor,
|
||||
@ -691,6 +690,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
|
||||
return new DefaultVideoFrameProcessor(
|
||||
context,
|
||||
glObjectsProvider,
|
||||
eglDisplay,
|
||||
eglContext,
|
||||
inputSwitcher,
|
||||
@ -700,8 +700,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
intermediateGlShaderPrograms,
|
||||
finalShaderProgramWrapper,
|
||||
renderFramesAutomatically,
|
||||
outputColorInfo,
|
||||
glObjectsProvider);
|
||||
outputColorInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -766,20 +765,12 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
return shaderProgramListBuilder.build();
|
||||
}
|
||||
|
||||
/** Sets the {@link GlObjectsProvider} on all of the {@linkplain GlShaderProgram}s provided. */
|
||||
private static void setGlObjectProviderOnShaderPrograms(
|
||||
List<GlShaderProgram> shaderPrograms, GlObjectsProvider glObjectsProvider) {
|
||||
for (int i = 0; i < shaderPrograms.size(); i++) {
|
||||
GlShaderProgram shaderProgram = shaderPrograms.get(i);
|
||||
shaderProgram.setGlObjectsProvider(glObjectsProvider);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Chains the given {@link GlShaderProgram} instances using {@link
|
||||
* ChainingGlShaderProgramListener} instances.
|
||||
*/
|
||||
private static void chainShaderProgramsWithListeners(
|
||||
GlObjectsProvider glObjectsProvider,
|
||||
List<GlShaderProgram> shaderPrograms,
|
||||
FinalShaderProgramWrapper finalShaderProgramWrapper,
|
||||
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor,
|
||||
@ -792,7 +783,10 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
GlShaderProgram consumingGlShaderProgram = shaderProgramsToChain.get(i + 1);
|
||||
ChainingGlShaderProgramListener chainingGlShaderProgramListener =
|
||||
new ChainingGlShaderProgramListener(
|
||||
producingGlShaderProgram, consumingGlShaderProgram, videoFrameProcessingTaskExecutor);
|
||||
glObjectsProvider,
|
||||
producingGlShaderProgram,
|
||||
consumingGlShaderProgram,
|
||||
videoFrameProcessingTaskExecutor);
|
||||
producingGlShaderProgram.setOutputListener(chainingGlShaderProgramListener);
|
||||
producingGlShaderProgram.setErrorListener(
|
||||
videoFrameProcessorListenerExecutor, videoFrameProcessorListener::onError);
|
||||
|
@ -23,6 +23,7 @@ import android.view.Surface;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.FrameInfo;
|
||||
import androidx.media3.common.GlObjectsProvider;
|
||||
import androidx.media3.common.GlTextureInfo;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
@ -55,6 +56,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
private static final long SURFACE_TEXTURE_TIMEOUT_MS =
|
||||
Util.DEVICE.contains("emulator") ? 10_000 : 500;
|
||||
|
||||
private final GlObjectsProvider glObjectsProvider;
|
||||
private final VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor;
|
||||
private final ExternalShaderProgram externalShaderProgram;
|
||||
private final int externalTexId;
|
||||
@ -90,6 +92,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param glObjectsProvider The {@link GlObjectsProvider} for using EGL and GLES.
|
||||
* @param externalShaderProgram The {@link ExternalShaderProgram} for which this {@code
|
||||
* ExternalTextureManager} will be set as the {@link InputListener}.
|
||||
* @param videoFrameProcessingTaskExecutor The {@link VideoFrameProcessingTaskExecutor}.
|
||||
@ -98,9 +101,11 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
// The onFrameAvailableListener will not be invoked until the constructor returns.
|
||||
@SuppressWarnings("nullness:method.invocation.invalid")
|
||||
public ExternalTextureManager(
|
||||
GlObjectsProvider glObjectsProvider,
|
||||
ExternalShaderProgram externalShaderProgram,
|
||||
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor)
|
||||
throws VideoFrameProcessingException {
|
||||
this.glObjectsProvider = glObjectsProvider;
|
||||
this.externalShaderProgram = externalShaderProgram;
|
||||
this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor;
|
||||
try {
|
||||
@ -314,6 +319,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
// Correct the presentation time so that GlShaderPrograms don't see the stream offset.
|
||||
long presentationTimeUs = (frameTimeNs / 1000) + offsetToAddUs;
|
||||
externalShaderProgram.queueInputFrame(
|
||||
glObjectsProvider,
|
||||
new GlTextureInfo(
|
||||
externalTexId,
|
||||
/* fboId= */ C.INDEX_UNSET,
|
||||
|
@ -101,12 +101,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
private int outputHeight;
|
||||
@Nullable private DefaultShaderProgram defaultShaderProgram;
|
||||
@Nullable private SurfaceViewWrapper debugSurfaceViewWrapper;
|
||||
private GlObjectsProvider glObjectsProvider;
|
||||
private InputListener inputListener;
|
||||
private @MonotonicNonNull Size outputSizeBeforeSurfaceTransformation;
|
||||
@Nullable private SurfaceView debugSurfaceView;
|
||||
@Nullable private OnInputStreamProcessedListener onInputStreamProcessedListener;
|
||||
private boolean frameProcessingStarted;
|
||||
private boolean matrixTransformationsChanged;
|
||||
|
||||
@GuardedBy("this")
|
||||
@ -132,7 +130,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor,
|
||||
Executor videoFrameProcessorListenerExecutor,
|
||||
VideoFrameProcessor.Listener videoFrameProcessorListener,
|
||||
GlObjectsProvider glObjectsProvider,
|
||||
@Nullable DefaultVideoFrameProcessor.TextureOutputListener textureOutputListener,
|
||||
int textureOutputCapacity) {
|
||||
this.context = context;
|
||||
@ -147,7 +144,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor;
|
||||
this.videoFrameProcessorListenerExecutor = videoFrameProcessorListenerExecutor;
|
||||
this.videoFrameProcessorListener = videoFrameProcessorListener;
|
||||
this.glObjectsProvider = glObjectsProvider;
|
||||
this.textureOutputListener = textureOutputListener;
|
||||
|
||||
inputListener = new InputListener() {};
|
||||
@ -158,15 +154,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
outputTextureTimestamps = new ArrayDeque<>(textureOutputCapacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) {
|
||||
checkState(
|
||||
!frameProcessingStarted,
|
||||
"The GlObjectsProvider cannot be set after frame processing has started.");
|
||||
this.glObjectsProvider = glObjectsProvider;
|
||||
outputTexturePool.setGlObjectsProvider(glObjectsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputListener(InputListener inputListener) {
|
||||
this.inputListener = inputListener;
|
||||
@ -192,27 +179,33 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
@Override
|
||||
public void signalEndOfCurrentInputStream() {
|
||||
frameProcessingStarted = true;
|
||||
checkNotNull(onInputStreamProcessedListener).onInputStreamProcessed();
|
||||
}
|
||||
|
||||
// Methods that must be called on the GL thread.
|
||||
|
||||
@Override
|
||||
public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
frameProcessingStarted = true;
|
||||
public void queueInputFrame(
|
||||
GlObjectsProvider glObjectsProvider, GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
videoFrameProcessorListenerExecutor.execute(
|
||||
() -> videoFrameProcessorListener.onOutputFrameAvailableForRendering(presentationTimeUs));
|
||||
if (textureOutputListener == null) {
|
||||
if (renderFramesAutomatically) {
|
||||
renderFrame(
|
||||
inputTexture, presentationTimeUs, /* renderTimeNs= */ presentationTimeUs * 1000);
|
||||
glObjectsProvider,
|
||||
inputTexture,
|
||||
presentationTimeUs,
|
||||
/* renderTimeNs= */ presentationTimeUs * 1000);
|
||||
} else {
|
||||
availableFrames.add(Pair.create(inputTexture, presentationTimeUs));
|
||||
}
|
||||
} else {
|
||||
checkState(outputTexturePool.freeTextureCount() > 0);
|
||||
renderFrame(inputTexture, presentationTimeUs, /* renderTimeNs= */ presentationTimeUs * 1000);
|
||||
renderFrame(
|
||||
glObjectsProvider,
|
||||
inputTexture,
|
||||
presentationTimeUs,
|
||||
/* renderTimeNs= */ presentationTimeUs * 1000);
|
||||
}
|
||||
maybeOnReadyToAcceptInputFrame();
|
||||
}
|
||||
@ -254,7 +247,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
frameProcessingStarted = true;
|
||||
// Drops all frames that aren't rendered yet.
|
||||
availableFrames.clear();
|
||||
if (defaultShaderProgram != null) {
|
||||
@ -277,14 +269,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
}
|
||||
}
|
||||
|
||||
public void renderOutputFrame(long renderTimeNs) {
|
||||
public void renderOutputFrame(GlObjectsProvider glObjectsProvider, long renderTimeNs) {
|
||||
if (textureOutputListener != null) {
|
||||
return;
|
||||
}
|
||||
frameProcessingStarted = true;
|
||||
checkState(!renderFramesAutomatically);
|
||||
Pair<GlTextureInfo, Long> oldestAvailableFrame = availableFrames.remove();
|
||||
renderFrame(
|
||||
glObjectsProvider,
|
||||
/* inputTexture= */ oldestAvailableFrame.first,
|
||||
/* presentationTimeUs= */ oldestAvailableFrame.second,
|
||||
renderTimeNs);
|
||||
@ -326,10 +318,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
}
|
||||
|
||||
private synchronized void renderFrame(
|
||||
GlTextureInfo inputTexture, long presentationTimeUs, long renderTimeNs) {
|
||||
GlObjectsProvider glObjectsProvider,
|
||||
GlTextureInfo inputTexture,
|
||||
long presentationTimeUs,
|
||||
long renderTimeNs) {
|
||||
try {
|
||||
if (renderTimeNs == VideoFrameProcessor.DROP_OUTPUT_FRAME
|
||||
|| !ensureConfigured(inputTexture.getWidth(), inputTexture.getHeight())) {
|
||||
|| !ensureConfigured(
|
||||
glObjectsProvider, inputTexture.getWidth(), inputTexture.getHeight())) {
|
||||
inputListener.onInputFrameProcessed(inputTexture);
|
||||
return; // Drop frames when requested, or there is no output surface and output texture.
|
||||
}
|
||||
@ -345,7 +341,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
VideoFrameProcessingException.from(e, presentationTimeUs)));
|
||||
}
|
||||
if (debugSurfaceViewWrapper != null && defaultShaderProgram != null) {
|
||||
renderFrameToDebugSurface(inputTexture, presentationTimeUs);
|
||||
renderFrameToDebugSurface(glObjectsProvider, inputTexture, presentationTimeUs);
|
||||
}
|
||||
|
||||
inputListener.onInputFrameProcessed(inputTexture);
|
||||
@ -399,7 +395,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
*
|
||||
* <p>Returns {@code false} if {@code outputSurfaceInfo} is unset.
|
||||
*/
|
||||
private synchronized boolean ensureConfigured(int inputWidth, int inputHeight)
|
||||
private synchronized boolean ensureConfigured(
|
||||
GlObjectsProvider glObjectsProvider, int inputWidth, int inputHeight)
|
||||
throws VideoFrameProcessingException, GlUtil.GlException {
|
||||
// Clear extra or outdated resources.
|
||||
boolean inputSizeChanged =
|
||||
@ -455,7 +452,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
/* isEncoderInputSurface= */ renderFramesAutomatically);
|
||||
}
|
||||
if (textureOutputListener != null) {
|
||||
outputTexturePool.ensureConfigured(outputWidth, outputHeight);
|
||||
outputTexturePool.ensureConfigured(glObjectsProvider, outputWidth, outputHeight);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -522,7 +519,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
return defaultShaderProgram;
|
||||
}
|
||||
|
||||
private void renderFrameToDebugSurface(GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
private void renderFrameToDebugSurface(
|
||||
GlObjectsProvider glObjectsProvider, GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
DefaultShaderProgram defaultShaderProgram = checkNotNull(this.defaultShaderProgram);
|
||||
SurfaceViewWrapper debugSurfaceViewWrapper = checkNotNull(this.debugSurfaceViewWrapper);
|
||||
try {
|
||||
|
@ -19,6 +19,7 @@ import android.util.Pair;
|
||||
import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.GlObjectsProvider;
|
||||
import androidx.media3.common.GlTextureInfo;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import java.util.ArrayDeque;
|
||||
@ -32,6 +33,8 @@ import java.util.Queue;
|
||||
*/
|
||||
|
||||
/* package */ final class FrameConsumptionManager implements GlShaderProgram.InputListener {
|
||||
|
||||
private final GlObjectsProvider glObjectsProvider;
|
||||
private final GlShaderProgram consumingGlShaderProgram;
|
||||
private final VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor;
|
||||
|
||||
@ -44,12 +47,15 @@ import java.util.Queue;
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param glObjectsProvider The {@link GlObjectsProvider} for using EGL and GLES.
|
||||
* @param consumingGlShaderProgram The {@link GlShaderProgram} that frames are queued to.
|
||||
* @param videoFrameProcessingTaskExecutor The {@link VideoFrameProcessingTaskExecutor}.
|
||||
*/
|
||||
public FrameConsumptionManager(
|
||||
GlObjectsProvider glObjectsProvider,
|
||||
GlShaderProgram consumingGlShaderProgram,
|
||||
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor) {
|
||||
this.glObjectsProvider = glObjectsProvider;
|
||||
this.consumingGlShaderProgram = consumingGlShaderProgram;
|
||||
this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor;
|
||||
availableFrames = new ArrayDeque<>();
|
||||
@ -66,6 +72,7 @@ import java.util.Queue;
|
||||
videoFrameProcessingTaskExecutor.submit(
|
||||
() ->
|
||||
consumingGlShaderProgram.queueInputFrame(
|
||||
glObjectsProvider,
|
||||
/* inputTexture= */ pendingFrame.first,
|
||||
/* presentationTimeUs= */ pendingFrame.second));
|
||||
@Nullable Pair<GlTextureInfo, Long> nextPendingFrame = availableFrames.peek();
|
||||
@ -82,15 +89,15 @@ import java.util.Queue;
|
||||
availableFrames.clear();
|
||||
}
|
||||
|
||||
public synchronized void queueInputFrame(GlTextureInfo texture, long presentationTimeUs) {
|
||||
public synchronized void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
if (consumingGlShaderProgramInputCapacity > 0) {
|
||||
videoFrameProcessingTaskExecutor.submit(
|
||||
() ->
|
||||
consumingGlShaderProgram.queueInputFrame(
|
||||
/* inputTexture= */ texture, presentationTimeUs));
|
||||
glObjectsProvider, inputTexture, presentationTimeUs));
|
||||
consumingGlShaderProgramInputCapacity--;
|
||||
} else {
|
||||
availableFrames.add(Pair.create(texture, presentationTimeUs));
|
||||
availableFrames.add(Pair.create(inputTexture, presentationTimeUs));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,9 +25,9 @@ import java.util.concurrent.Executor;
|
||||
* Processes frames from one OpenGL 2D texture to another.
|
||||
*
|
||||
* <p>The {@code GlShaderProgram} consumes input frames it accepts via {@link
|
||||
* #queueInputFrame(GlTextureInfo, long)} and surrenders each texture back to the caller via its
|
||||
* {@linkplain InputListener#onInputFrameProcessed(GlTextureInfo) listener} once the texture's
|
||||
* contents have been processed.
|
||||
* #queueInputFrame(GlObjectsProvider, GlTextureInfo, long)} and surrenders each texture back to the
|
||||
* caller via its {@linkplain InputListener#onInputFrameProcessed(GlTextureInfo) listener} once the
|
||||
* texture's contents have been processed.
|
||||
*
|
||||
* <p>The {@code GlShaderProgram} produces output frames asynchronously and notifies its owner when
|
||||
* they are available via its {@linkplain OutputListener#onOutputFrameAvailable(GlTextureInfo, long)
|
||||
@ -57,8 +57,8 @@ public interface GlShaderProgram {
|
||||
/**
|
||||
* Called when the {@link GlShaderProgram} is ready to accept another input frame.
|
||||
*
|
||||
* <p>For each time this method is called, {@link #queueInputFrame(GlTextureInfo, long)} can be
|
||||
* called once.
|
||||
* <p>For each time this method is called, {@link #queueInputFrame(GlObjectsProvider,
|
||||
* GlTextureInfo, long)} can be called once.
|
||||
*/
|
||||
default void onReadyToAcceptInputFrame() {}
|
||||
|
||||
@ -69,7 +69,7 @@ public interface GlShaderProgram {
|
||||
* #onReadyToAcceptInputFrame ready to accept another input frame} when this method is called.
|
||||
*
|
||||
* @param inputTexture The {@link GlTextureInfo} that was used to {@linkplain
|
||||
* #queueInputFrame(GlTextureInfo, long) queue} the input frame.
|
||||
* #queueInputFrame(GlObjectsProvider, GlTextureInfo, long) queue} the input frame.
|
||||
*/
|
||||
default void onInputFrameProcessed(GlTextureInfo inputTexture) {}
|
||||
|
||||
@ -149,13 +149,6 @@ public interface GlShaderProgram {
|
||||
*/
|
||||
void setErrorListener(Executor executor, ErrorListener errorListener);
|
||||
|
||||
/**
|
||||
* Sets the {@link GlObjectsProvider}.
|
||||
*
|
||||
* <p>This method should not be called after any of the frame processing methods.
|
||||
*/
|
||||
void setGlObjectsProvider(GlObjectsProvider glObjectsProvider);
|
||||
|
||||
/**
|
||||
* Processes an input frame if possible.
|
||||
*
|
||||
@ -166,10 +159,12 @@ public interface GlShaderProgram {
|
||||
* <p>This method must only be called when the {@code GlShaderProgram} can {@linkplain
|
||||
* InputListener#onReadyToAcceptInputFrame() accept an input frame}.
|
||||
*
|
||||
* @param glObjectsProvider The {@link GlObjectsProvider} for using EGL and GLES.
|
||||
* @param inputTexture A {@link GlTextureInfo} describing the texture containing the input frame.
|
||||
* @param presentationTimeUs The presentation timestamp of the input frame, in microseconds.
|
||||
*/
|
||||
void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs);
|
||||
void queueInputFrame(
|
||||
GlObjectsProvider glObjectsProvider, GlTextureInfo inputTexture, long presentationTimeUs);
|
||||
|
||||
/**
|
||||
* Notifies the {@code GlShaderProgram} that the frame on the given output texture is no longer
|
||||
|
@ -92,9 +92,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
inputColorInfo,
|
||||
outputColorInfo,
|
||||
enableColorTransfers);
|
||||
samplingShaderProgram.setGlObjectsProvider(glObjectsProvider);
|
||||
textureManager =
|
||||
new ExternalTextureManager(samplingShaderProgram, videoFrameProcessingTaskExecutor);
|
||||
new ExternalTextureManager(
|
||||
glObjectsProvider, samplingShaderProgram, videoFrameProcessingTaskExecutor);
|
||||
inputs.put(inputType, new Input(textureManager, samplingShaderProgram));
|
||||
break;
|
||||
case VideoFrameProcessor.INPUT_TYPE_BITMAP:
|
||||
@ -107,9 +107,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
outputColorInfo,
|
||||
enableColorTransfers,
|
||||
inputType);
|
||||
samplingShaderProgram.setGlObjectsProvider(glObjectsProvider);
|
||||
textureManager =
|
||||
new BitmapTextureManager(samplingShaderProgram, videoFrameProcessingTaskExecutor);
|
||||
new BitmapTextureManager(
|
||||
glObjectsProvider, samplingShaderProgram, videoFrameProcessingTaskExecutor);
|
||||
inputs.put(inputType, new Input(textureManager, samplingShaderProgram));
|
||||
break;
|
||||
case VideoFrameProcessor.INPUT_TYPE_TEXTURE_ID:
|
||||
@ -122,9 +122,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
outputColorInfo,
|
||||
enableColorTransfers,
|
||||
inputType);
|
||||
samplingShaderProgram.setGlObjectsProvider(glObjectsProvider);
|
||||
textureManager =
|
||||
new TexIdTextureManager(samplingShaderProgram, videoFrameProcessingTaskExecutor);
|
||||
new TexIdTextureManager(
|
||||
glObjectsProvider, samplingShaderProgram, videoFrameProcessingTaskExecutor);
|
||||
inputs.put(inputType, new Input(textureManager, samplingShaderProgram));
|
||||
break;
|
||||
default:
|
||||
@ -155,6 +155,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
if (inputType == newInputType) {
|
||||
input.setChainingListener(
|
||||
new GatedChainingListenerWrapper(
|
||||
glObjectsProvider,
|
||||
input.samplingGlShaderProgram,
|
||||
this.downstreamShaderProgram,
|
||||
videoFrameProcessingTaskExecutor));
|
||||
@ -235,15 +236,20 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
implements GlShaderProgram.OutputListener, GlShaderProgram.InputListener {
|
||||
|
||||
private final ChainingGlShaderProgramListener chainingGlShaderProgramListener;
|
||||
private boolean isActive = false;
|
||||
|
||||
private boolean isActive;
|
||||
|
||||
public GatedChainingListenerWrapper(
|
||||
GlObjectsProvider glObjectsProvider,
|
||||
GlShaderProgram producingGlShaderProgram,
|
||||
GlShaderProgram consumingGlShaderProgram,
|
||||
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor) {
|
||||
this.chainingGlShaderProgramListener =
|
||||
new ChainingGlShaderProgramListener(
|
||||
producingGlShaderProgram, consumingGlShaderProgram, videoFrameProcessingTaskExecutor);
|
||||
glObjectsProvider,
|
||||
producingGlShaderProgram,
|
||||
consumingGlShaderProgram,
|
||||
videoFrameProcessingTaskExecutor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -20,6 +20,7 @@ import static androidx.media3.common.util.Assertions.checkArgument;
|
||||
import static java.lang.Math.round;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.media3.common.GlObjectsProvider;
|
||||
import androidx.media3.common.GlTextureInfo;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
|
||||
@ -54,10 +55,11 @@ import androidx.media3.common.VideoFrameProcessingException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
public void queueInputFrame(
|
||||
GlObjectsProvider glObjectsProvider, GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
framesReceived++;
|
||||
if (framesReceived % n == 0) {
|
||||
super.queueInputFrame(inputTexture, presentationTimeUs);
|
||||
super.queueInputFrame(glObjectsProvider, inputTexture, presentationTimeUs);
|
||||
} else {
|
||||
getInputListener().onInputFrameProcessed(inputTexture);
|
||||
getInputListener().onReadyToAcceptInputFrame();
|
||||
|
@ -21,13 +21,14 @@ import android.opengl.GLES10;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.FrameInfo;
|
||||
import androidx.media3.common.GlObjectsProvider;
|
||||
import androidx.media3.common.GlTextureInfo;
|
||||
import androidx.media3.common.OnInputFrameProcessedListener;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/**
|
||||
* Forwards a video frames made available via {@linkplain GLES10#GL_TEXTURE_2D traditional GLES
|
||||
* texture} to a {@link GlShaderProgram} for consumption.
|
||||
* Forwards frames made available via {@linkplain GLES10#GL_TEXTURE_2D traditional GLES textures} to
|
||||
* a {@link GlShaderProgram} for consumption.
|
||||
*
|
||||
* <p>Public methods in this class can be called from any thread.
|
||||
*/
|
||||
@ -41,16 +42,19 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param glObjectsProvider The {@link GlObjectsProvider} for using EGL and GLES.
|
||||
* @param shaderProgram The {@link GlShaderProgram} for which this {@code texIdTextureManager}
|
||||
* will be set as the {@link GlShaderProgram.InputListener}.
|
||||
* @param videoFrameProcessingTaskExecutor The {@link VideoFrameProcessingTaskExecutor}.
|
||||
*/
|
||||
public TexIdTextureManager(
|
||||
GlObjectsProvider glObjectsProvider,
|
||||
GlShaderProgram shaderProgram,
|
||||
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor) {
|
||||
this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor;
|
||||
frameConsumptionManager =
|
||||
new FrameConsumptionManager(shaderProgram, videoFrameProcessingTaskExecutor);
|
||||
new FrameConsumptionManager(
|
||||
glObjectsProvider, shaderProgram, videoFrameProcessingTaskExecutor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -32,8 +32,6 @@ import java.util.Queue;
|
||||
private final int capacity;
|
||||
private final boolean useHighPrecisionColorComponents;
|
||||
|
||||
private GlObjectsProvider glObjectsProvider;
|
||||
|
||||
/**
|
||||
* Creates a {@code TexturePool} instance.
|
||||
*
|
||||
@ -47,14 +45,6 @@ import java.util.Queue;
|
||||
|
||||
freeTextures = new ArrayDeque<>(capacity);
|
||||
inUseTextures = new ArrayDeque<>(capacity);
|
||||
|
||||
glObjectsProvider = new DefaultGlObjectsProvider(/* sharedEglContext= */ null);
|
||||
}
|
||||
|
||||
/** Sets the {@link GlObjectsProvider}. */
|
||||
public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) {
|
||||
checkState(!isConfigured());
|
||||
this.glObjectsProvider = glObjectsProvider;
|
||||
}
|
||||
|
||||
/** Returns whether the instance has been {@linkplain #ensureConfigured configured}. */
|
||||
@ -80,15 +70,16 @@ import java.util.Queue;
|
||||
*
|
||||
* <p>Reconfigures backing textures as needed.
|
||||
*/
|
||||
public void ensureConfigured(int width, int height) throws GlUtil.GlException {
|
||||
public void ensureConfigured(GlObjectsProvider glObjectsProvider, int width, int height)
|
||||
throws GlUtil.GlException {
|
||||
if (!isConfigured()) {
|
||||
createTextures(width, height);
|
||||
createTextures(glObjectsProvider, width, height);
|
||||
return;
|
||||
}
|
||||
GlTextureInfo texture = getIteratorToAllTextures().next();
|
||||
if (texture.getWidth() != width || texture.getHeight() != height) {
|
||||
deleteAllTextures();
|
||||
createTextures(width, height);
|
||||
createTextures(glObjectsProvider, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,7 +138,8 @@ import java.util.Queue;
|
||||
inUseTextures.clear();
|
||||
}
|
||||
|
||||
private void createTextures(int width, int height) throws GlUtil.GlException {
|
||||
private void createTextures(GlObjectsProvider glObjectsProvider, int width, int height)
|
||||
throws GlUtil.GlException {
|
||||
checkState(freeTextures.isEmpty());
|
||||
checkState(inUseTextures.isEmpty());
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
|
@ -72,21 +72,16 @@ import java.util.concurrent.Executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) {
|
||||
copyGlShaderProgram.setGlObjectsProvider(glObjectsProvider);
|
||||
wrappedGlShaderProgram.setGlObjectsProvider(glObjectsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
public void queueInputFrame(
|
||||
GlObjectsProvider glObjectsProvider, GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
// TODO(b/277726418) Properly report shader program capacity when switching from wrapped shader
|
||||
// program to copying shader program.
|
||||
if (presentationTimeUs >= startTimeUs && presentationTimeUs <= endTimeUs) {
|
||||
pendingWrappedGlShaderProgramFrames++;
|
||||
wrappedGlShaderProgram.queueInputFrame(inputTexture, presentationTimeUs);
|
||||
wrappedGlShaderProgram.queueInputFrame(glObjectsProvider, inputTexture, presentationTimeUs);
|
||||
} else {
|
||||
pendingCopyGlShaderProgramFrames++;
|
||||
copyGlShaderProgram.queueInputFrame(inputTexture, presentationTimeUs);
|
||||
copyGlShaderProgram.queueInputFrame(glObjectsProvider, inputTexture, presentationTimeUs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.GlObjectsProvider;
|
||||
import androidx.media3.common.GlTextureInfo;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.common.util.Util;
|
||||
@ -37,10 +38,12 @@ public final class ChainingGlShaderProgramListenerTest {
|
||||
private final VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor =
|
||||
new VideoFrameProcessingTaskExecutor(
|
||||
Util.newSingleThreadExecutor("Test"), mockFrameProcessorListener);
|
||||
private final GlObjectsProvider mockGlObjectsProvider = mock(GlObjectsProvider.class);
|
||||
private final GlShaderProgram mockProducingGlShaderProgram = mock(GlShaderProgram.class);
|
||||
private final GlShaderProgram mockConsumingGlShaderProgram = mock(GlShaderProgram.class);
|
||||
private final ChainingGlShaderProgramListener chainingGlShaderProgramListener =
|
||||
new ChainingGlShaderProgramListener(
|
||||
mockGlObjectsProvider,
|
||||
mockProducingGlShaderProgram,
|
||||
mockConsumingGlShaderProgram,
|
||||
videoFrameProcessingTaskExecutor);
|
||||
@ -83,7 +86,8 @@ public final class ChainingGlShaderProgramListenerTest {
|
||||
chainingGlShaderProgramListener.onOutputFrameAvailable(texture, presentationTimeUs);
|
||||
Thread.sleep(EXECUTOR_WAIT_TIME_MS);
|
||||
|
||||
verify(mockConsumingGlShaderProgram).queueInputFrame(texture, presentationTimeUs);
|
||||
verify(mockConsumingGlShaderProgram)
|
||||
.queueInputFrame(mockGlObjectsProvider, texture, presentationTimeUs);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -102,7 +106,8 @@ public final class ChainingGlShaderProgramListenerTest {
|
||||
chainingGlShaderProgramListener.onReadyToAcceptInputFrame();
|
||||
Thread.sleep(EXECUTOR_WAIT_TIME_MS);
|
||||
|
||||
verify(mockConsumingGlShaderProgram).queueInputFrame(texture, presentationTimeUs);
|
||||
verify(mockConsumingGlShaderProgram)
|
||||
.queueInputFrame(mockGlObjectsProvider, texture, presentationTimeUs);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -131,8 +136,10 @@ public final class ChainingGlShaderProgramListenerTest {
|
||||
chainingGlShaderProgramListener.onReadyToAcceptInputFrame();
|
||||
Thread.sleep(EXECUTOR_WAIT_TIME_MS);
|
||||
|
||||
verify(mockConsumingGlShaderProgram).queueInputFrame(firstTexture, firstPresentationTimeUs);
|
||||
verify(mockConsumingGlShaderProgram).queueInputFrame(secondTexture, secondPresentationTimeUs);
|
||||
verify(mockConsumingGlShaderProgram)
|
||||
.queueInputFrame(mockGlObjectsProvider, firstTexture, firstPresentationTimeUs);
|
||||
verify(mockConsumingGlShaderProgram)
|
||||
.queueInputFrame(mockGlObjectsProvider, secondTexture, secondPresentationTimeUs);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Loading…
x
Reference in New Issue
Block a user