diff --git a/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java b/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java index c7239a7b49..6fa803e65e 100644 --- a/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java +++ b/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java @@ -179,6 +179,15 @@ public interface VideoFrameProcessor { */ Surface getInputSurface(); + /** + * Informs the {@code VideoFrameProcessor} that a new input stream will be queued. + * + *
Call {@link #setInputFrameInfo} before this method if the {@link FrameInfo} of the new input
+ * stream differs from that of the current input stream.
+ */
+ // TODO(b/274109008) Merge this and setInputFrameInfo.
+ void registerInputStream(@InputType int inputType);
+
/**
* Sets information about the input frames.
*
diff --git a/libraries/effect/src/androidTest/java/androidx/media3/effect/DefaultVideoFrameProcessorVideoFrameReleaseTest.java b/libraries/effect/src/androidTest/java/androidx/media3/effect/DefaultVideoFrameProcessorVideoFrameReleaseTest.java
index 1c9443e281..2cacecc40b 100644
--- a/libraries/effect/src/androidTest/java/androidx/media3/effect/DefaultVideoFrameProcessorVideoFrameReleaseTest.java
+++ b/libraries/effect/src/androidTest/java/androidx/media3/effect/DefaultVideoFrameProcessorVideoFrameReleaseTest.java
@@ -350,10 +350,11 @@ public final class DefaultVideoFrameProcessorVideoFrameReleaseTest {
checkNotNull(defaultVideoFrameProcessor)
.setInputFrameInfo(new FrameInfo.Builder(WIDTH, HEIGHT).build());
// A frame needs to be registered despite not queuing any external input to ensure
- // that
- // the video frame processor knows about the stream offset.
+ // that the video frame processor knows about the stream offset.
+ defaultVideoFrameProcessor.registerInputStream(INPUT_TYPE_SURFACE);
defaultVideoFrameProcessor.registerInputFrame();
blankFrameProducer.produceBlankFramesAndQueueEndOfStream(inputPresentationTimesUs);
+ defaultVideoFrameProcessor.signalEndOfInput();
});
videoFrameProcessingEndedCountDownLatch.await();
@Nullable
diff --git a/libraries/effect/src/main/java/androidx/media3/effect/BitmapTextureManager.java b/libraries/effect/src/main/java/androidx/media3/effect/BitmapTextureManager.java
index f3898e7458..146a2ea4d9 100644
--- a/libraries/effect/src/main/java/androidx/media3/effect/BitmapTextureManager.java
+++ b/libraries/effect/src/main/java/androidx/media3/effect/BitmapTextureManager.java
@@ -48,9 +48,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private int downstreamShaderProgramCapacity;
private int framesToQueueForCurrentBitmap;
private double currentPresentationTimeUs;
- private boolean inputEnded;
private boolean useHdr;
- private boolean outputEnded;
+ private volatile boolean inputEnded;
/**
* Creates a new instance.
@@ -90,12 +89,20 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
return 0;
}
+ @Override
+ public void signalEndOfCurrentInputStream() {
+ // Do nothing here. End of current input signaling is handled in maybeQueueToShaderProgram().
+ }
+
@Override
public void signalEndOfInput() {
videoFrameProcessingTaskExecutor.submit(
() -> {
- inputEnded = true;
- maybeSignalEndOfOutput();
+ if (framesToQueueForCurrentBitmap == 0 && pendingBitmaps.isEmpty()) {
+ shaderProgram.signalEndOfCurrentInputStream();
+ } else {
+ inputEnded = true;
+ }
});
}
@@ -120,9 +127,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
Bitmap bitmap, long durationUs, long offsetUs, float frameRate, boolean useHdr)
throws VideoFrameProcessingException {
this.useHdr = useHdr;
- if (inputEnded) {
- return;
- }
int framesToAdd = round(frameRate * (durationUs / (float) C.MICROS_PER_SECOND));
double frameDurationUs = C.MICROS_PER_SECOND / frameRate;
pendingBitmaps.add(new BitmapFrameSequenceInfo(bitmap, offsetUs, frameDurationUs, framesToAdd));
@@ -174,17 +178,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
currentPresentationTimeUs += currentBitmapInfo.frameDurationUs;
if (framesToQueueForCurrentBitmap == 0) {
pendingBitmaps.remove();
- maybeSignalEndOfOutput();
- }
- }
-
- private void maybeSignalEndOfOutput() {
- if (framesToQueueForCurrentBitmap == 0
- && pendingBitmaps.isEmpty()
- && inputEnded
- && !outputEnded) {
- shaderProgram.signalEndOfCurrentInputStream();
- outputEnded = true;
+ if (pendingBitmaps.isEmpty() && inputEnded) {
+ // Only signal end of stream after all pending bitmaps are processed.
+ // TODO(b/269424561): Call signalEndOfCurrentInputStream on every bitmap
+ shaderProgram.signalEndOfCurrentInputStream();
+ }
}
}
diff --git a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java
index 33bc03137d..ec1cf7b0a0 100644
--- a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java
+++ b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java
@@ -50,6 +50,8 @@ import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
@@ -254,6 +256,10 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
private final boolean releaseFramesAutomatically;
private final FinalShaderProgramWrapper finalShaderProgramWrapper;
private final ImmutableList This method must be called on the last input stream, before calling {@link
+ * #signalEndOfInput}.
+ */
+ void signalEndOfCurrentInputStream();
+
/**
* Signals the end of the input.
*
diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoFrameProcessorTestRunner.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoFrameProcessorTestRunner.java
index acc663f664..73227b2c36 100644
--- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoFrameProcessorTestRunner.java
+++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoFrameProcessorTestRunner.java
@@ -15,6 +15,7 @@
*/
package androidx.media3.test.utils;
+import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_BITMAP;
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_SURFACE;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkStateNotNull;
@@ -324,6 +325,7 @@ public final class VideoFrameProcessorTestRunner {
mediaFormat.getInteger(MediaFormat.KEY_HEIGHT))
.setPixelWidthHeightRatio(pixelWidthHeightRatio)
.build());
+ videoFrameProcessor.registerInputStream(INPUT_TYPE_SURFACE);
videoFrameProcessor.registerInputFrame();
}
@@ -343,6 +345,7 @@ public final class VideoFrameProcessorTestRunner {
.setPixelWidthHeightRatio(pixelWidthHeightRatio)
.setOffsetToAddUs(offsetToAddUs)
.build());
+ videoFrameProcessor.registerInputStream(INPUT_TYPE_BITMAP);
videoFrameProcessor.queueInputBitmap(inputBitmap, durationUs, frameRate);
}
diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoSamplePipeline.java b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoSamplePipeline.java
index 74d2b15872..da307a304a 100644
--- a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoSamplePipeline.java
+++ b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoSamplePipeline.java
@@ -218,6 +218,8 @@ import org.checkerframework.dataflow.qual.Pure;
.setPixelWidthHeightRatio(trackFormat.pixelWidthHeightRatio)
.setOffsetToAddUs(mediaItemOffsetUs.get())
.build());
+ videoFrameProcessor.registerInputStream(
+ MimeTypes.isVideo(trackFormat.sampleMimeType) ? INPUT_TYPE_SURFACE : INPUT_TYPE_BITMAP);
}
mediaItemOffsetUs.addAndGet(durationUs);
}