Replace FrameProcessorChain#isEnded with listener method.

PiperOrigin-RevId: 455114693
This commit is contained in:
hschlueter 2022-06-15 13:27:35 +00:00 committed by Marc Baechinger
parent ea7f1ca1e3
commit fdfca88019
3 changed files with 59 additions and 24 deletions

View File

@ -89,9 +89,10 @@ public final class FrameProcessorChainPixelTest {
private final AtomicReference<FrameProcessingException> frameProcessingException = private final AtomicReference<FrameProcessingException> frameProcessingException =
new AtomicReference<>(); new AtomicReference<>();
private @MonotonicNonNull MediaFormat mediaFormat;
private @MonotonicNonNull FrameProcessorChain frameProcessorChain; private @MonotonicNonNull FrameProcessorChain frameProcessorChain;
private volatile @MonotonicNonNull ImageReader outputImageReader; private volatile @MonotonicNonNull ImageReader outputImageReader;
private @MonotonicNonNull MediaFormat mediaFormat; private volatile boolean frameProcessingEnded;
@After @After
public void release() { public void release() {
@ -354,7 +355,17 @@ public final class FrameProcessorChainPixelTest {
checkNotNull( checkNotNull(
FrameProcessorChain.create( FrameProcessorChain.create(
context, context,
/* listener= */ this.frameProcessingException::set, new FrameProcessorChain.Listener() {
@Override
public void onFrameProcessingError(FrameProcessingException exception) {
frameProcessingException.set(exception);
}
@Override
public void onFrameProcessingEnded() {
frameProcessingEnded = true;
}
},
pixelWidthHeightRatio, pixelWidthHeightRatio,
inputWidth, inputWidth,
inputHeight, inputHeight,
@ -421,7 +432,7 @@ public final class FrameProcessorChainPixelTest {
private Bitmap processFirstFrameAndEnd() throws InterruptedException { private Bitmap processFirstFrameAndEnd() throws InterruptedException {
checkNotNull(frameProcessorChain).signalEndOfInputStream(); checkNotNull(frameProcessorChain).signalEndOfInputStream();
Thread.sleep(FRAME_PROCESSING_WAIT_MS); Thread.sleep(FRAME_PROCESSING_WAIT_MS);
assertThat(frameProcessorChain.isEnded()).isTrue(); assertThat(frameProcessingEnded).isTrue();
assertThat(frameProcessingException.get()).isNull(); assertThat(frameProcessingException.get()).isNull();
Image frameProcessorChainOutputImage = checkNotNull(outputImageReader).acquireLatestImage(); Image frameProcessorChainOutputImage = checkNotNull(outputImageReader).acquireLatestImage();

View File

@ -71,8 +71,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
* <p>This listener is only called from the {@link FrameProcessorChain}'s background thread. * <p>This listener is only called from the {@link FrameProcessorChain}'s background thread.
*/ */
public interface Listener { public interface Listener {
/** Called when an exception occurs during asynchronous frame processing. */ /**
* Called when an exception occurs during asynchronous frame processing.
*
* <p>If an error occurred, consuming and producing further frames will not work as expected and
* the {@link FrameProcessorChain} should be released.
*/
void onFrameProcessingError(FrameProcessingException exception); void onFrameProcessingError(FrameProcessingException exception);
/** Called after the frame processor has produced its final output frame. */
void onFrameProcessingEnded();
} }
/** /**
@ -454,22 +462,24 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
return pendingFrameCount.get(); return pendingFrameCount.get();
} }
/** Informs the {@code FrameProcessorChain} that no further input frames should be accepted. */ /**
* Informs the {@code FrameProcessorChain} that no further input frames should be accepted.
*
* @throws IllegalStateException If called more than once.
*/
public void signalEndOfInputStream() { public void signalEndOfInputStream() {
checkState(!inputStreamEnded);
inputStreamEnded = true; inputStreamEnded = true;
} futures.add(singleThreadExecutorService.submit(this::signalEndOfOutputStream));
/** Returns whether all frames have been processed. */
public boolean isEnded() {
return inputStreamEnded && getPendingFrameCount() == 0;
} }
/** /**
* Releases all resources. * Releases all resources.
* *
* <p>If the frame processor chain is released before it has {@linkplain #isEnded() ended}, it * <p>If the frame processor chain is released before it has {@linkplain
* will attempt to cancel processing any input frames that have already become available. Input * Listener#onFrameProcessingEnded() ended}, it will attempt to cancel processing any input frames
* frames that become available after release are ignored. * that have already become available. Input frames that become available after release are
* ignored.
* *
* <p>This method blocks until all OpenGL resources are released or releasing times out. * <p>This method blocks until all OpenGL resources are released or releasing times out.
*/ */
@ -563,6 +573,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
} }
} }
/** Calls {@link Listener#onFrameProcessingEnded()} once no more frames are pending. */
@WorkerThread
private void signalEndOfOutputStream() {
if (getPendingFrameCount() == 0) {
listener.onFrameProcessingEnded();
} else {
futures.add(singleThreadExecutorService.submit(this::signalEndOfOutputStream));
}
}
/** /**
* Releases the {@link SingleFrameGlTextureProcessor SingleFrameGlTextureProcessors} and destroys * Releases the {@link SingleFrameGlTextureProcessor SingleFrameGlTextureProcessors} and destroys
* the OpenGL context. * the OpenGL context.

View File

@ -51,8 +51,6 @@ import org.checkerframework.dataflow.qual.Pure;
private final EncoderWrapper encoderWrapper; private final EncoderWrapper encoderWrapper;
private final DecoderInputBuffer encoderOutputBuffer; private final DecoderInputBuffer encoderOutputBuffer;
private boolean signaledEndOfStreamToEncoder;
public VideoTranscodingSamplePipeline( public VideoTranscodingSamplePipeline(
Context context, Context context,
Format inputFormat, Format inputFormat,
@ -110,10 +108,23 @@ import org.checkerframework.dataflow.qual.Pure;
frameProcessorChain = frameProcessorChain =
FrameProcessorChain.create( FrameProcessorChain.create(
context, context,
/* listener= */ exception -> new FrameProcessorChain.Listener() {
@Override
public void onFrameProcessingError(FrameProcessingException exception) {
asyncErrorListener.onTransformationException( asyncErrorListener.onTransformationException(
TransformationException.createForFrameProcessorChain( TransformationException.createForFrameProcessorChain(
exception, TransformationException.ERROR_CODE_GL_PROCESSING_FAILED)), exception, TransformationException.ERROR_CODE_GL_PROCESSING_FAILED));
}
@Override
public void onFrameProcessingEnded() {
try {
encoderWrapper.signalEndOfInputStream();
} catch (TransformationException exception) {
asyncErrorListener.onTransformationException(exception);
}
}
},
inputFormat.pixelWidthHeightRatio, inputFormat.pixelWidthHeightRatio,
/* inputWidth= */ decodedWidth, /* inputWidth= */ decodedWidth,
/* inputHeight= */ decodedHeight, /* inputHeight= */ decodedHeight,
@ -157,13 +168,6 @@ import org.checkerframework.dataflow.qual.Pure;
@Override @Override
public boolean processData() throws TransformationException { public boolean processData() throws TransformationException {
if (frameProcessorChain.isEnded()) {
if (!signaledEndOfStreamToEncoder) {
encoderWrapper.signalEndOfInputStream();
signaledEndOfStreamToEncoder = true;
}
return false;
}
if (decoder.isEnded()) { if (decoder.isEnded()) {
return false; return false;
} }