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 =
new AtomicReference<>();
private @MonotonicNonNull MediaFormat mediaFormat;
private @MonotonicNonNull FrameProcessorChain frameProcessorChain;
private volatile @MonotonicNonNull ImageReader outputImageReader;
private @MonotonicNonNull MediaFormat mediaFormat;
private volatile boolean frameProcessingEnded;
@After
public void release() {
@ -354,7 +355,17 @@ public final class FrameProcessorChainPixelTest {
checkNotNull(
FrameProcessorChain.create(
context,
/* listener= */ this.frameProcessingException::set,
new FrameProcessorChain.Listener() {
@Override
public void onFrameProcessingError(FrameProcessingException exception) {
frameProcessingException.set(exception);
}
@Override
public void onFrameProcessingEnded() {
frameProcessingEnded = true;
}
},
pixelWidthHeightRatio,
inputWidth,
inputHeight,
@ -421,7 +432,7 @@ public final class FrameProcessorChainPixelTest {
private Bitmap processFirstFrameAndEnd() throws InterruptedException {
checkNotNull(frameProcessorChain).signalEndOfInputStream();
Thread.sleep(FRAME_PROCESSING_WAIT_MS);
assertThat(frameProcessorChain.isEnded()).isTrue();
assertThat(frameProcessingEnded).isTrue();
assertThat(frameProcessingException.get()).isNull();
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.
*/
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);
/** 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();
}
/** 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() {
checkState(!inputStreamEnded);
inputStreamEnded = true;
}
/** Returns whether all frames have been processed. */
public boolean isEnded() {
return inputStreamEnded && getPendingFrameCount() == 0;
futures.add(singleThreadExecutorService.submit(this::signalEndOfOutputStream));
}
/**
* Releases all resources.
*
* <p>If the frame processor chain is released before it has {@linkplain #isEnded() ended}, it
* will attempt to cancel processing any input frames that have already become available. Input
* frames that become available after release are ignored.
* <p>If the frame processor chain is released before it has {@linkplain
* Listener#onFrameProcessingEnded() ended}, it will attempt to cancel processing any input frames
* 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.
*/
@ -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
* the OpenGL context.

View File

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