Commit a5f4651d by christosts

AsynchronousMediaCodecAdapter: surface queueing errors sooner

The AsynchronousMediaCodecAdapter's queuing thread stores any exceptions
raised by MediaCodec and re-throws them on the next call to
queueInputBuffer()/queueSecureInputBuffer(). However, if MediaCodec
raises and error while queueing, it goes into a failed state and does
not announce available input buffers. If there is no input available
input buffer, the MediaCodecRenderer will never call
queueInputBuffer()/queueSecureInputBuffer(), hence playback is stalled.

This change surfaces the queueing error through the adapter's dequeueing
methods.

PiperOrigin-RevId: 508637346
parent 5e3cd7a3
...@@ -191,11 +191,13 @@ import java.nio.ByteBuffer; ...@@ -191,11 +191,13 @@ import java.nio.ByteBuffer;
@Override @Override
public int dequeueInputBufferIndex() { public int dequeueInputBufferIndex() {
bufferEnqueuer.maybeThrowException();
return asynchronousMediaCodecCallback.dequeueInputBufferIndex(); return asynchronousMediaCodecCallback.dequeueInputBufferIndex();
} }
@Override @Override
public int dequeueOutputBufferIndex(MediaCodec.BufferInfo bufferInfo) { public int dequeueOutputBufferIndex(MediaCodec.BufferInfo bufferInfo) {
bufferEnqueuer.maybeThrowException();
return asynchronousMediaCodecCallback.dequeueOutputBufferIndex(bufferInfo); return asynchronousMediaCodecCallback.dequeueOutputBufferIndex(bufferInfo);
} }
......
...@@ -162,7 +162,8 @@ class AsynchronousMediaCodecBufferEnqueuer { ...@@ -162,7 +162,8 @@ class AsynchronousMediaCodecBufferEnqueuer {
blockUntilHandlerThreadIsIdle(); blockUntilHandlerThreadIsIdle();
} }
private void maybeThrowException() { /** Throw any exception that occurred on the enqueuer's background queueing thread. */
public void maybeThrowException() {
@Nullable RuntimeException exception = pendingRuntimeException.getAndSet(null); @Nullable RuntimeException exception = pendingRuntimeException.getAndSet(null);
if (exception != null) { if (exception != null) {
throw exception; throw exception;
......
...@@ -263,11 +263,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ...@@ -263,11 +263,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
// else, pendingOutputFormat may already be non-null following a previous flush, and remains // else, pendingOutputFormat may already be non-null following a previous flush, and remains
// set in this case. // set in this case.
// mediaCodecException is not reset to null. If the codec has raised an error, then it remains
// in FAILED_STATE even after flushing.
availableInputBuffers.clear(); availableInputBuffers.clear();
availableOutputBuffers.clear(); availableOutputBuffers.clear();
bufferInfos.clear(); bufferInfos.clear();
formats.clear(); formats.clear();
mediaCodecException = null;
} }
@GuardedBy("lock") @GuardedBy("lock")
......
...@@ -81,6 +81,20 @@ public class AsynchronousMediaCodecAdapterTest { ...@@ -81,6 +81,20 @@ public class AsynchronousMediaCodecAdapterTest {
} }
@Test @Test
public void dequeueInputBufferIndex_withPendingQueueingError_throwsException() {
// Force MediaCodec to throw an error by attempting to queue input buffer -1.
adapter.queueInputBuffer(
/* index= */ -1,
/* offset= */ 0,
/* size= */ 0,
/* presentationTimeUs= */ 0,
/* flags= */ 0);
shadowOf(queueingThread.getLooper()).idle();
assertThrows(IllegalStateException.class, () -> adapter.dequeueInputBufferIndex());
}
@Test
public void dequeueInputBufferIndex_afterShutdown_returnsTryAgainLater() { public void dequeueInputBufferIndex_afterShutdown_returnsTryAgainLater() {
adapter.release(); adapter.release();
...@@ -124,6 +138,20 @@ public class AsynchronousMediaCodecAdapterTest { ...@@ -124,6 +138,20 @@ public class AsynchronousMediaCodecAdapterTest {
} }
@Test @Test
public void dequeueOutputBufferIndex_withPendingQueueingError_throwsException() {
// Force MediaCodec to throw an error by attempting to queue input buffer -1.
adapter.queueInputBuffer(
/* index= */ -1,
/* offset= */ 0,
/* size= */ 0,
/* presentationTimeUs= */ 0,
/* flags= */ 0);
shadowOf(queueingThread.getLooper()).idle();
assertThrows(IllegalStateException.class, () -> adapter.dequeueOutputBufferIndex(bufferInfo));
}
@Test
public void dequeueOutputBufferIndex_afterShutdown_returnsTryAgainLater() { public void dequeueOutputBufferIndex_afterShutdown_returnsTryAgainLater() {
int index = adapter.dequeueInputBufferIndex(); int index = adapter.dequeueInputBufferIndex();
adapter.queueInputBuffer(index, 0, 0, 0, 0); adapter.queueInputBuffer(index, 0, 0, 0, 0);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment