Stop suppressing exceptions in MediaCodec.Callback
during flush
Previously `AsynchronousMediaCodecCallback.mediaCodecException` was
cleared when flushing completed. This behaviour was changed in
aeff51c507
so now the exception is not cleared.
The result after that commit was that we would **only** suppress/ignore
the expression if a flush was currently pending, and we would throw it
both before and after the flush. This doesn't really make sense, so this
commit changes the behaviour to also throw the exception during the
flush.
This commit also corrects the assertion in
`flush_withPendingError_resetsError` and deflakes it so that it
consistently passes. The previous version of this test, although the
assertion was incorrect, would often pass because the
`dequeueInputBuffer` call would happen while the `flush` was still
pending, so the exception was suppressed.
#minor-release
PiperOrigin-RevId: 540237228
This commit is contained in:
parent
b69b33206e
commit
248d1d99ec
@ -126,10 +126,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
*/
|
||||
public int dequeueInputBufferIndex() {
|
||||
synchronized (lock) {
|
||||
maybeThrowException();
|
||||
if (isFlushingOrShutdown()) {
|
||||
return MediaCodec.INFO_TRY_AGAIN_LATER;
|
||||
} else {
|
||||
maybeThrowException();
|
||||
return availableInputBuffers.isEmpty()
|
||||
? MediaCodec.INFO_TRY_AGAIN_LATER
|
||||
: availableInputBuffers.remove();
|
||||
@ -145,10 +145,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
*/
|
||||
public int dequeueOutputBufferIndex(MediaCodec.BufferInfo bufferInfo) {
|
||||
synchronized (lock) {
|
||||
maybeThrowException();
|
||||
if (isFlushingOrShutdown()) {
|
||||
return MediaCodec.INFO_TRY_AGAIN_LATER;
|
||||
} else {
|
||||
maybeThrowException();
|
||||
if (availableOutputBuffers.isEmpty()) {
|
||||
return MediaCodec.INFO_TRY_AGAIN_LATER;
|
||||
} else {
|
||||
|
@ -30,6 +30,7 @@ import android.os.Looper;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.junit.After;
|
||||
@ -105,6 +106,36 @@ public class AsynchronousMediaCodecCallbackTest {
|
||||
.isEqualTo(MediaCodec.INFO_TRY_AGAIN_LATER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dequeInputBufferIndex_withPendingFlushAndError_throwsError() throws Exception {
|
||||
AtomicBoolean beforeFlushCompletes = new AtomicBoolean();
|
||||
AtomicBoolean flushCompleted = new AtomicBoolean();
|
||||
Looper callbackThreadLooper = callbackThread.getLooper();
|
||||
Handler callbackHandler = new Handler(callbackThreadLooper);
|
||||
ShadowLooper shadowCallbackLooper = shadowOf(callbackThreadLooper);
|
||||
// Pause the callback thread so that flush() never completes.
|
||||
shadowCallbackLooper.pause();
|
||||
|
||||
// Send two input buffers to the callback, then an error, and then flush().
|
||||
asynchronousMediaCodecCallback.onInputBufferAvailable(codec, 0);
|
||||
asynchronousMediaCodecCallback.onInputBufferAvailable(codec, 1);
|
||||
MediaCodec.CodecException expectedException = createCodecException();
|
||||
asynchronousMediaCodecCallback.onError(codec, expectedException);
|
||||
callbackHandler.post(() -> beforeFlushCompletes.set(true));
|
||||
asynchronousMediaCodecCallback.flush();
|
||||
callbackHandler.post(() -> flushCompleted.set(true));
|
||||
while (!beforeFlushCompletes.get()) {
|
||||
shadowCallbackLooper.runOneTask();
|
||||
}
|
||||
|
||||
assertThat(flushCompleted.get()).isFalse();
|
||||
MediaCodec.CodecException actualException =
|
||||
assertThrows(
|
||||
MediaCodec.CodecException.class,
|
||||
() -> asynchronousMediaCodecCallback.dequeueInputBufferIndex());
|
||||
assertThat(actualException).isSameInstanceAs(expectedException);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dequeInputBufferIndex_afterFlush_returnsTryAgain() {
|
||||
Looper callbackThreadLooper = callbackThread.getLooper();
|
||||
@ -218,6 +249,39 @@ public class AsynchronousMediaCodecCallbackTest {
|
||||
.isEqualTo(MediaCodec.INFO_TRY_AGAIN_LATER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dequeOutputBufferIndex_withPendingFlushAndError_throwsError() throws Exception {
|
||||
AtomicBoolean beforeFlushCompletes = new AtomicBoolean();
|
||||
AtomicBoolean flushCompleted = new AtomicBoolean();
|
||||
Looper callbackThreadLooper = callbackThread.getLooper();
|
||||
Handler callbackHandler = new Handler(callbackThreadLooper);
|
||||
ShadowLooper shadowCallbackLooper = shadowOf(callbackThreadLooper);
|
||||
// Pause the callback thread so that flush() never completes.
|
||||
shadowCallbackLooper.pause();
|
||||
|
||||
// Send two output buffers to the callback, then an error, and then flush().
|
||||
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
||||
asynchronousMediaCodecCallback.onOutputBufferAvailable(codec, 0, bufferInfo);
|
||||
asynchronousMediaCodecCallback.onOutputBufferAvailable(codec, 1, bufferInfo);
|
||||
MediaCodec.CodecException expectedException = createCodecException();
|
||||
asynchronousMediaCodecCallback.onError(codec, expectedException);
|
||||
callbackHandler.post(() -> beforeFlushCompletes.set(true));
|
||||
asynchronousMediaCodecCallback.flush();
|
||||
callbackHandler.post(() -> flushCompleted.set(true));
|
||||
while (beforeFlushCompletes.get()) {
|
||||
shadowCallbackLooper.runOneTask();
|
||||
}
|
||||
|
||||
assertThat(flushCompleted.get()).isFalse();
|
||||
MediaCodec.CodecException actualException =
|
||||
assertThrows(
|
||||
MediaCodec.CodecException.class,
|
||||
() ->
|
||||
asynchronousMediaCodecCallback.dequeueOutputBufferIndex(
|
||||
new MediaCodec.BufferInfo()));
|
||||
assertThat(actualException).isSameInstanceAs(expectedException);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dequeOutputBufferIndex_afterFlush_returnsTryAgain() {
|
||||
Looper callbackThreadLooper = callbackThread.getLooper();
|
||||
@ -438,13 +502,24 @@ public class AsynchronousMediaCodecCallbackTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void flush_withPendingError_resetsError() throws Exception {
|
||||
asynchronousMediaCodecCallback.onError(codec, createCodecException());
|
||||
// Calling flush should clear any pending error.
|
||||
asynchronousMediaCodecCallback.flush();
|
||||
public void flush_withPendingError_doesntResetError() throws Exception {
|
||||
AtomicBoolean flushCompleted = new AtomicBoolean();
|
||||
Looper callbackThreadLooper = callbackThread.getLooper();
|
||||
ShadowLooper shadowCallbackLooper = shadowOf(callbackThreadLooper);
|
||||
|
||||
assertThat(asynchronousMediaCodecCallback.dequeueInputBufferIndex())
|
||||
.isEqualTo(MediaCodec.INFO_TRY_AGAIN_LATER);
|
||||
MediaCodec.CodecException expectedException = createCodecException();
|
||||
asynchronousMediaCodecCallback.onError(codec, expectedException);
|
||||
// Flush and progress the looper so that flush is completed.
|
||||
asynchronousMediaCodecCallback.flush();
|
||||
new Handler(callbackThreadLooper).post(() -> flushCompleted.set(true));
|
||||
shadowCallbackLooper.idle();
|
||||
|
||||
assertThat(flushCompleted.get()).isTrue();
|
||||
MediaCodec.CodecException actualException =
|
||||
assertThrows(
|
||||
MediaCodec.CodecException.class,
|
||||
() -> asynchronousMediaCodecCallback.dequeueInputBufferIndex());
|
||||
assertThat(actualException).isSameInstanceAs(expectedException);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -456,7 +531,11 @@ public class AsynchronousMediaCodecCallbackTest {
|
||||
}
|
||||
|
||||
/** Reflectively create a {@link MediaCodec.CodecException}. */
|
||||
private static MediaCodec.CodecException createCodecException() throws Exception {
|
||||
private static MediaCodec.CodecException createCodecException()
|
||||
throws NoSuchMethodException,
|
||||
InvocationTargetException,
|
||||
IllegalAccessException,
|
||||
InstantiationException {
|
||||
Constructor<MediaCodec.CodecException> constructor =
|
||||
MediaCodec.CodecException.class.getDeclaredConstructor(
|
||||
Integer.TYPE, Integer.TYPE, String.class);
|
||||
|
Loading…
x
Reference in New Issue
Block a user