mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add a test for MediaCodecRenderer handling of IllegalStateException
This is a regression test for the bug introduced inbb9ff30c3awhich was manually spotted and fixed in0d2bf49d6a. Reverting the fix causes this test to fail. This test is a bit hacky because we have to munge the stack trace of the `IllegalStateException` to make it look like it was thrown from inside `MediaCodec`. We deliberately do this 'badly' (e.g. using `fakeMethod`) to avoid a future reader being confused by a fake-but-plausible stack trace. PiperOrigin-RevId: 652820878
This commit is contained in:
parent
d4c6e39dfb
commit
99679645fc
1 changed files with 185 additions and 1 deletions
|
|
@ -18,6 +18,8 @@ package androidx.media3.exoplayer.mediacodec;
|
||||||
import static androidx.media3.exoplayer.DecoderReuseEvaluation.REUSE_RESULT_YES_WITHOUT_RECONFIGURATION;
|
import static androidx.media3.exoplayer.DecoderReuseEvaluation.REUSE_RESULT_YES_WITHOUT_RECONFIGURATION;
|
||||||
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM;
|
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM;
|
||||||
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
|
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
|
@ -26,14 +28,20 @@ import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.inOrder;
|
import static org.mockito.Mockito.inOrder;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
|
|
||||||
|
import android.media.MediaCodec;
|
||||||
import android.media.MediaCrypto;
|
import android.media.MediaCrypto;
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.PersistableBundle;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.view.Surface;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.util.Clock;
|
import androidx.media3.common.util.Clock;
|
||||||
|
import androidx.media3.decoder.CryptoInfo;
|
||||||
import androidx.media3.exoplayer.DecoderReuseEvaluation;
|
import androidx.media3.exoplayer.DecoderReuseEvaluation;
|
||||||
import androidx.media3.exoplayer.ExoPlaybackException;
|
import androidx.media3.exoplayer.ExoPlaybackException;
|
||||||
import androidx.media3.exoplayer.RendererCapabilities;
|
import androidx.media3.exoplayer.RendererCapabilities;
|
||||||
|
|
@ -46,7 +54,9 @@ import androidx.media3.exoplayer.upstream.DefaultAllocator;
|
||||||
import androidx.media3.test.utils.FakeSampleStream;
|
import androidx.media3.test.utils.FakeSampleStream;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -462,6 +472,59 @@ public class MediaCodecRendererTest {
|
||||||
inOrder, renderer, /* presentationTimeUs= */ 500, /* isDecodeOnly= */ false);
|
inOrder, renderer, /* presentationTimeUs= */ 500, /* isDecodeOnly= */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void render_wrapsIllegalStateExceptionFromMediaCodecInExoPlaybackException()
|
||||||
|
throws Exception {
|
||||||
|
MediaCodecAdapter.Factory throwingMediaCodecAdapterFactory =
|
||||||
|
new ThrowingMediaCodecAdapter.Factory(
|
||||||
|
() -> {
|
||||||
|
IllegalStateException ise = new IllegalStateException("ISE from inside MediaCodec");
|
||||||
|
StackTraceElement[] stackTrace = ise.getStackTrace();
|
||||||
|
stackTrace[0] =
|
||||||
|
new StackTraceElement(
|
||||||
|
"android.media.MediaCodec",
|
||||||
|
"fakeMethod",
|
||||||
|
stackTrace[0].getFileName(),
|
||||||
|
stackTrace[0].getLineNumber());
|
||||||
|
ise.setStackTrace(stackTrace);
|
||||||
|
return ise;
|
||||||
|
});
|
||||||
|
TestRenderer renderer = new TestRenderer(throwingMediaCodecAdapterFactory);
|
||||||
|
renderer.init(/* index= */ 0, PlayerId.UNSET, Clock.DEFAULT);
|
||||||
|
Format format =
|
||||||
|
new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).setAverageBitrate(1000).build();
|
||||||
|
FakeSampleStream fakeSampleStream =
|
||||||
|
createFakeSampleStream(format, /* sampleTimesUs...= */ 0, 100, 200, 300, 400, 500);
|
||||||
|
MediaSource.MediaPeriodId mediaPeriodId = new MediaSource.MediaPeriodId(new Object());
|
||||||
|
renderer.enable(
|
||||||
|
RendererConfiguration.DEFAULT,
|
||||||
|
new Format[] {format},
|
||||||
|
fakeSampleStream,
|
||||||
|
/* positionUs= */ 0,
|
||||||
|
/* joining= */ false,
|
||||||
|
/* mayRenderStartOfStream= */ true,
|
||||||
|
/* startPositionUs= */ 400,
|
||||||
|
/* offsetUs= */ 0,
|
||||||
|
mediaPeriodId);
|
||||||
|
renderer.start();
|
||||||
|
|
||||||
|
ExoPlaybackException playbackException =
|
||||||
|
assertThrows(
|
||||||
|
ExoPlaybackException.class,
|
||||||
|
() -> renderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime()));
|
||||||
|
|
||||||
|
assertThat(playbackException.type).isEqualTo(ExoPlaybackException.TYPE_RENDERER);
|
||||||
|
assertThat(playbackException)
|
||||||
|
.hasCauseThat()
|
||||||
|
.hasCauseThat()
|
||||||
|
.isInstanceOf(IllegalStateException.class);
|
||||||
|
assertThat(playbackException)
|
||||||
|
.hasCauseThat()
|
||||||
|
.hasCauseThat()
|
||||||
|
.hasMessageThat()
|
||||||
|
.contains("ISE from inside MediaCodec");
|
||||||
|
}
|
||||||
|
|
||||||
private FakeSampleStream createFakeSampleStream(Format format, long... sampleTimesUs) {
|
private FakeSampleStream createFakeSampleStream(Format format, long... sampleTimesUs) {
|
||||||
ImmutableList.Builder<FakeSampleStream.FakeSampleStreamItem> sampleListBuilder =
|
ImmutableList.Builder<FakeSampleStream.FakeSampleStreamItem> sampleListBuilder =
|
||||||
ImmutableList.builder();
|
ImmutableList.builder();
|
||||||
|
|
@ -484,9 +547,13 @@ public class MediaCodecRendererTest {
|
||||||
private static class TestRenderer extends MediaCodecRenderer {
|
private static class TestRenderer extends MediaCodecRenderer {
|
||||||
|
|
||||||
public TestRenderer() {
|
public TestRenderer() {
|
||||||
|
this(MediaCodecAdapter.Factory.getDefault(ApplicationProvider.getApplicationContext()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestRenderer(MediaCodecAdapter.Factory mediaCodecAdapterFactory) {
|
||||||
super(
|
super(
|
||||||
C.TRACK_TYPE_AUDIO,
|
C.TRACK_TYPE_AUDIO,
|
||||||
MediaCodecAdapter.Factory.getDefault(ApplicationProvider.getApplicationContext()),
|
mediaCodecAdapterFactory,
|
||||||
/* mediaCodecSelector= */ (mimeType, requiresSecureDecoder, requiresTunnelingDecoder) ->
|
/* mediaCodecSelector= */ (mimeType, requiresSecureDecoder, requiresTunnelingDecoder) ->
|
||||||
Collections.singletonList(
|
Collections.singletonList(
|
||||||
MediaCodecInfo.newInstance(
|
MediaCodecInfo.newInstance(
|
||||||
|
|
@ -570,6 +637,123 @@ public class MediaCodecRendererTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link MediaCodecAdapter} that throws a pre-specified exception from every decoding-related
|
||||||
|
* interaction.
|
||||||
|
*/
|
||||||
|
private static class ThrowingMediaCodecAdapter implements MediaCodecAdapter {
|
||||||
|
|
||||||
|
public static class Factory implements MediaCodecAdapter.Factory {
|
||||||
|
|
||||||
|
private final Supplier<RuntimeException> exceptionSupplier;
|
||||||
|
|
||||||
|
public Factory(Supplier<RuntimeException> exceptionSupplier) {
|
||||||
|
this.exceptionSupplier = exceptionSupplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MediaCodecAdapter createAdapter(Configuration configuration) throws IOException {
|
||||||
|
return new ThrowingMediaCodecAdapter(exceptionSupplier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Supplier<RuntimeException> exceptionSupplier;
|
||||||
|
|
||||||
|
private ThrowingMediaCodecAdapter(Supplier<RuntimeException> exceptionSupplier) {
|
||||||
|
this.exceptionSupplier = exceptionSupplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int dequeueInputBufferIndex() {
|
||||||
|
throw exceptionSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int dequeueOutputBufferIndex(MediaCodec.BufferInfo bufferInfo) {
|
||||||
|
throw exceptionSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MediaFormat getOutputFormat() {
|
||||||
|
throw exceptionSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ByteBuffer getInputBuffer(int index) {
|
||||||
|
throw exceptionSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ByteBuffer getOutputBuffer(int index) {
|
||||||
|
throw exceptionSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void queueInputBuffer(
|
||||||
|
int index, int offset, int size, long presentationTimeUs, int flags) {
|
||||||
|
throw exceptionSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void queueSecureInputBuffer(
|
||||||
|
int index, int offset, CryptoInfo info, long presentationTimeUs, int flags) {
|
||||||
|
throw exceptionSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void releaseOutputBuffer(int index, boolean render) {
|
||||||
|
throw exceptionSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void releaseOutputBuffer(int index, long renderTimeStampNs) {
|
||||||
|
throw exceptionSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush() {
|
||||||
|
throw exceptionSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void release() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOnFrameRenderedListener(OnFrameRenderedListener listener, Handler handler) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean registerOnBufferAvailableListener(OnBufferAvailableListener listener) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOutputSurface(Surface surface) {
|
||||||
|
throw exceptionSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setParameters(Bundle params) {
|
||||||
|
throw exceptionSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVideoScalingMode(@C.VideoScalingMode int scalingMode) {
|
||||||
|
throw exceptionSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needsReconfiguration() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PersistableBundle getMetrics() {
|
||||||
|
return new PersistableBundle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void verifyProcessOutputBufferDecodeOnly(
|
private static void verifyProcessOutputBufferDecodeOnly(
|
||||||
InOrder inOrder, MediaCodecRenderer renderer, long presentationTimeUs, boolean isDecodeOnly)
|
InOrder inOrder, MediaCodecRenderer renderer, long presentationTimeUs, boolean isDecodeOnly)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue