Skip to content

Commit

Permalink
Flush: Move TextureManager flush logic to abstract class.
Browse files Browse the repository at this point in the history
This also adds flush support to texture input.

PiperOrigin-RevId: 578536040
  • Loading branch information
dway123 authored and copybara-github committed Nov 1, 2023
1 parent 7b76264 commit 44f3176
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import android.graphics.Bitmap;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.FrameInfo;
import androidx.media3.common.GlObjectsProvider;
Expand All @@ -43,15 +42,14 @@
* <p>Public methods in this class can be called from any thread.
*/
@UnstableApi
/* package */ final class BitmapTextureManager implements TextureManager {
/* package */ final class BitmapTextureManager extends TextureManager {

private static final String UNSUPPORTED_IMAGE_CONFIGURATION =
"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.
private final Queue<BitmapFrameSequenceInfo> pendingBitmaps;

Expand All @@ -61,8 +59,6 @@
private boolean currentInputStreamEnded;
private boolean isNextFrameInTexture;

@Nullable private volatile VideoFrameProcessingTaskExecutor.Task onFlushCompleteTask;

/**
* Creates a new instance.
*
Expand All @@ -76,9 +72,9 @@ public BitmapTextureManager(
GlObjectsProvider glObjectsProvider,
GlShaderProgram shaderProgram,
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor) {
super(videoFrameProcessingTaskExecutor);
this.glObjectsProvider = glObjectsProvider;
this.shaderProgram = shaderProgram;
this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor;
pendingBitmaps = new LinkedBlockingQueue<>();
}

Expand All @@ -91,11 +87,6 @@ public void onReadyToAcceptInputFrame() {
});
}

@Override
public void onFlush() {
videoFrameProcessingTaskExecutor.submit(this::flush);
}

@Override
public void queueInputBitmap(
Bitmap inputBitmap,
Expand Down Expand Up @@ -129,11 +120,6 @@ public void signalEndOfCurrentInputStream() {
});
}

@Override
public void setOnFlushCompleteListener(@Nullable VideoFrameProcessingTaskExecutor.Task task) {
onFlushCompleteTask = task;
}

@Override
public void release() {
videoFrameProcessingTaskExecutor.submit(
Expand Down Expand Up @@ -199,11 +185,10 @@ private void maybeQueueToShaderProgram() throws VideoFrameProcessingException {
}
}

private void flush() {
@Override
protected void flush() {
pendingBitmaps.clear();
if (onFlushCompleteTask != null) {
videoFrameProcessingTaskExecutor.submitWithHighPriority(onFlushCompleteTask);
}
super.flush();
}

/** Information needed to generate all the frames associated with a specific {@link Bitmap}. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
* Forwards externally produced frames that become available via a {@link SurfaceTexture} to an
* {@link ExternalShaderProgram} for consumption.
*/
/* package */ final class ExternalTextureManager implements TextureManager {
/* package */ final class ExternalTextureManager extends TextureManager {

private static final String TAG = "ExtTexMgr";
private static final String TIMER_THREAD_NAME = "ExtTexMgr:Timer";
Expand All @@ -62,7 +62,6 @@
: 500;

private final GlObjectsProvider glObjectsProvider;
private final VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor;
private final ExternalShaderProgram externalShaderProgram;
private final int externalTexId;
private final Surface surface;
Expand All @@ -87,8 +86,6 @@
// Set to null on any thread. Read and set to non-null on the GL thread only.
@Nullable private volatile FrameInfo currentFrame;

// TODO(b/238302341) Remove the use of after flush task, block the calling thread instead.
@Nullable private volatile VideoFrameProcessingTaskExecutor.Task onFlushCompleteTask;
@Nullable private Future<?> forceSignalEndOfStreamFuture;

// Whether to reject frames from the SurfaceTexture. Accessed only on GL thread.
Expand All @@ -110,9 +107,9 @@ public ExternalTextureManager(
ExternalShaderProgram externalShaderProgram,
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor)
throws VideoFrameProcessingException {
super(videoFrameProcessingTaskExecutor);
this.glObjectsProvider = glObjectsProvider;
this.externalShaderProgram = externalShaderProgram;
this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor;
try {
externalTexId = GlUtil.createExternalTexture();
} catch (GlUtil.GlException e) {
Expand Down Expand Up @@ -187,16 +184,6 @@ public void onInputFrameProcessed(GlTextureInfo inputTexture) {
});
}

@Override
public void setOnFlushCompleteListener(@Nullable VideoFrameProcessingTaskExecutor.Task task) {
onFlushCompleteTask = task;
}

@Override
public void onFlush() {
videoFrameProcessingTaskExecutor.submit(this::flush);
}

/**
* Notifies the {@code ExternalTextureManager} that a frame with the given {@link FrameInfo} will
* become available via the {@link SurfaceTexture} eventually.
Expand Down Expand Up @@ -245,10 +232,10 @@ public void release() {
}

private void maybeExecuteAfterFlushTask() {
if (onFlushCompleteTask == null || numberOfFramesToDropOnBecomingAvailable > 0) {
if (numberOfFramesToDropOnBecomingAvailable > 0) {
return;
}
videoFrameProcessingTaskExecutor.submitWithHighPriority(onFlushCompleteTask);
super.flush();
}

// Methods that must be called on the GL thread.
Expand Down Expand Up @@ -290,7 +277,8 @@ private void forceSignalEndOfStream() {
signalEndOfCurrentInputStream();
}

private void flush() {
@Override
protected void flush() {
// A frame that is registered before flush may arrive after flush.
numberOfFramesToDropOnBecomingAvailable = pendingFrames.size() - availableFrameCount;
removeAllSurfaceTextureFrames();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,11 @@
import static androidx.media3.common.util.Assertions.checkNotNull;

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 androidx.media3.common.VideoFrameProcessingException;
import androidx.media3.common.util.GlUtil;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;

Expand All @@ -34,8 +32,7 @@
*
* <p>Public methods in this class can be called from any thread.
*/
/* package */ final class TexIdTextureManager implements TextureManager {
private final VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor;
/* package */ final class TexIdTextureManager extends TextureManager {
private final FrameConsumptionManager frameConsumptionManager;

private @MonotonicNonNull OnInputFrameProcessedListener frameProcessedListener;
Expand All @@ -53,7 +50,7 @@ public TexIdTextureManager(
GlObjectsProvider glObjectsProvider,
GlShaderProgram shaderProgram,
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor) {
this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor;
super(videoFrameProcessingTaskExecutor);
frameConsumptionManager =
new FrameConsumptionManager(
glObjectsProvider, shaderProgram, videoFrameProcessingTaskExecutor);
Expand All @@ -72,11 +69,6 @@ public void onInputFrameProcessed(GlTextureInfo inputTexture) {
.onInputFrameProcessed(inputTexture.texId, GlUtil.createGlSyncFence()));
}

@Override
public void onFlush() {
videoFrameProcessingTaskExecutor.submit(frameConsumptionManager::onFlush);
}

@Override
public void queueInputTexture(int inputTexId, long presentationTimeUs) {
FrameInfo frameInfo = checkNotNull(this.inputFrameInfo);
Expand Down Expand Up @@ -124,12 +116,15 @@ public void signalEndOfCurrentInputStream() {
}

@Override
public void setOnFlushCompleteListener(@Nullable VideoFrameProcessingTaskExecutor.Task task) {
public void release() {
// Do nothing.
}

// Methods that must be called on the GL thread.

@Override
public void release() throws VideoFrameProcessingException {
// Do nothing.
protected synchronized void flush() {
frameConsumptionManager.onFlush();
super.flush();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,47 @@
import android.graphics.Bitmap;
import android.graphics.SurfaceTexture;
import android.view.Surface;
import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable;
import androidx.media3.common.FrameInfo;
import androidx.media3.common.OnInputFrameProcessedListener;
import androidx.media3.common.VideoFrameProcessingException;
import androidx.media3.common.VideoFrameProcessor;
import androidx.media3.common.util.TimestampIterator;

/** Handles {@code DefaultVideoFrameProcessor}'s input. */
/* package */ interface TextureManager extends GlShaderProgram.InputListener {
/**
* Handles {@code DefaultVideoFrameProcessor}'s input.
*
* <p>All instance methods must be called from either the thread that owns {@code this} instance, or
* an internal GL thread.
*/
/* package */ abstract class TextureManager implements GlShaderProgram.InputListener {

protected final VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor;

private final Object lock;

// TODO(b/238302341) Remove the use of onFlushCompleteTask, block the calling thread instead.
@GuardedBy("lock")
@Nullable
private VideoFrameProcessingTaskExecutor.Task onFlushCompleteTask;

/**
* Creates a new instance.
*
* @param videoFrameProcessingTaskExecutor The {@link VideoFrameProcessingTaskExecutor}.
*/
public TextureManager(VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor) {
this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor;
lock = new Object();
}

/**
* See {@link DefaultVideoFrameProcessor#setInputDefaultBufferSize}.
*
* <p>Only works when the input is received on a {@link SurfaceTexture}.
*/
default void setDefaultBufferSize(int width, int height) {
public void setDefaultBufferSize(int width, int height) {
throw new UnsupportedOperationException();
}

Expand All @@ -48,7 +73,7 @@ default void setDefaultBufferSize(int width, int height) {
* at. The timestamps should be monotonically increasing.
* @param useHdr Whether input and/or output colors are HDR.
*/
default void queueInputBitmap(
public void queueInputBitmap(
Bitmap inputBitmap,
FrameInfo frameInfo,
TimestampIterator inStreamOffsetsUs,
Expand All @@ -61,7 +86,7 @@ default void queueInputBitmap(
*
* @see VideoFrameProcessor#queueInputTexture
*/
default void queueInputTexture(int inputTexId, long presentationTimeUs) {
public void queueInputTexture(int inputTexId, long presentationTimeUs) {
throw new UnsupportedOperationException();
}

Expand All @@ -70,7 +95,7 @@ default void queueInputTexture(int inputTexId, long presentationTimeUs) {
*
* @see VideoFrameProcessor#setOnInputFrameProcessedListener
*/
default void setOnInputFrameProcessedListener(OnInputFrameProcessedListener listener) {
public void setOnInputFrameProcessedListener(OnInputFrameProcessedListener listener) {
throw new UnsupportedOperationException();
}

Expand All @@ -83,7 +108,7 @@ default void setOnInputFrameProcessedListener(OnInputFrameProcessedListener list
* <p>Pixels are expanded using the {@link FrameInfo#pixelWidthHeightRatio} so that the output
* frames' pixels have a ratio of 1.
*/
default void setInputFrameInfo(FrameInfo inputFrameInfo) {
public void setInputFrameInfo(FrameInfo inputFrameInfo) {
// Do nothing.
}

Expand All @@ -92,28 +117,48 @@ default void setInputFrameInfo(FrameInfo inputFrameInfo) {
*
* <p>Only works when the input is received on a {@link SurfaceTexture}.
*/
default Surface getInputSurface() {
public Surface getInputSurface() {
throw new UnsupportedOperationException();
}

/** Informs the {@code TextureManager} that a frame will be queued. */
default void registerInputFrame(FrameInfo frameInfo) {
public void registerInputFrame(FrameInfo frameInfo) {
throw new UnsupportedOperationException();
}

/** See {@link VideoFrameProcessor#getPendingInputFrameCount}. */
int getPendingFrameCount();
public abstract int getPendingFrameCount();

/** Signals the end of the current input stream. */
void signalEndOfCurrentInputStream();
public abstract void signalEndOfCurrentInputStream();

/** Sets the task to run on completing flushing, or {@code null} to clear any task. */
void setOnFlushCompleteListener(@Nullable VideoFrameProcessingTaskExecutor.Task task);
public final void setOnFlushCompleteListener(
@Nullable VideoFrameProcessingTaskExecutor.Task task) {
synchronized (lock) {
onFlushCompleteTask = task;
}
}

@Override
public final void onFlush() {
videoFrameProcessingTaskExecutor.submit(this::flush);
}

/**
* Releases all resources.
*
* @see VideoFrameProcessor#release()
*/
void release() throws VideoFrameProcessingException;
public abstract void release() throws VideoFrameProcessingException;

// Methods that must be called on the GL thread.

protected void flush() {
synchronized (lock) {
if (onFlushCompleteTask != null) {
videoFrameProcessingTaskExecutor.submitWithHighPriority(onFlushCompleteTask);
}
}
}
}

0 comments on commit 44f3176

Please sign in to comment.