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
This commit is contained in:
parent
a6dfcf7799
commit
706431059c
@ -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 {
|
|||||||
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;
|
|||||||
// 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")
|
||||||
|
@ -80,6 +80,20 @@ public class AsynchronousMediaCodecAdapterTest {
|
|||||||
assertThrows(IllegalStateException.class, () -> adapter.dequeueInputBufferIndex());
|
assertThrows(IllegalStateException.class, () -> adapter.dequeueInputBufferIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
@Test
|
||||||
public void dequeueInputBufferIndex_afterShutdown_returnsTryAgainLater() {
|
public void dequeueInputBufferIndex_afterShutdown_returnsTryAgainLater() {
|
||||||
adapter.release();
|
adapter.release();
|
||||||
@ -123,6 +137,20 @@ public class AsynchronousMediaCodecAdapterTest {
|
|||||||
assertThrows(IllegalStateException.class, () -> adapter.dequeueOutputBufferIndex(bufferInfo));
|
assertThrows(IllegalStateException.class, () -> adapter.dequeueOutputBufferIndex(bufferInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
@Test
|
||||||
public void dequeueOutputBufferIndex_afterShutdown_returnsTryAgainLater() {
|
public void dequeueOutputBufferIndex_afterShutdown_returnsTryAgainLater() {
|
||||||
int index = adapter.dequeueInputBufferIndex();
|
int index = adapter.dequeueInputBufferIndex();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user