mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Move MCR CryptoException
handling to top-level render()
method
Handling for `MediaCodec.CryptoException` was originally added only around calls to `MediaCodec.queueSecureInputBuffer` and `queueInputBuffer` (because these are the only methods that can throw this exception). When asynchronous interaction with `MediaCodec` was added in <unknown commit>, exceptions from `MediaCodec` started being stored and bubbled out of **later** interactions with `MediaCodecAdapter`. This means that `MediaCodecRenderer` can now see `CryptoException` thrown from a different method, like `MediaCodecAdapter.dequeueInputBufferIndex()`, and this ends up missing the `catch (CryptoException)` code in `MediaCodecRenderer`. This results in an "unexpected runtime error" stack trace like [A]. This change fixes the stack trace to: 1. Make it a "renderer exception" instead of "unexpected runtime error" 2. Include the correct DRM error code -> `@PlaybackException.ErrorCode` mapping. You can see the corrected stack trace below [B]. ----- [A] (synthesized from manually throwing a `CryptoException` from `AsynchronousMediaCodecBufferEnqueuer#doQueueSecureInputBuffer`) ``` playerFailed [eventTime=11.56, mediaPos=10.35, window=0, period=0, errorCode=ERROR_CODE_UNSPECIFIED androidx.media3.exoplayer.ExoPlaybackException: Unexpected runtime error at androidx.media3.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:729) at android.os.Handler.dispatchMessage(Handler.java:103) at android.os.Looper.loopOnce(Looper.java:232) at android.os.Looper.loop(Looper.java:317) at android.os.HandlerThread.run(HandlerThread.java:85) Caused by: android.media.MediaCodec$CryptoException: Test error message at androidx.media3.exoplayer.mediacodec.AsynchronousMediaCodecBufferEnqueuer.doQueueSecureInputBuffer(AsynchronousMediaCodecBufferEnqueuer.java:232) at androidx.media3.exoplayer.mediacodec.AsynchronousMediaCodecBufferEnqueuer.doHandleMessage(AsynchronousMediaCodecBufferEnqueuer.java:196) at androidx.media3.exoplayer.mediacodec.AsynchronousMediaCodecBufferEnqueuer.access$000(AsynchronousMediaCodecBufferEnqueuer.java:47) at androidx.media3.exoplayer.mediacodec.AsynchronousMediaCodecBufferEnqueuer$1.handleMessage(AsynchronousMediaCodecBufferEnqueuer.java:93) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loopOnce(Looper.java:232) at android.os.Looper.loop(Looper.java:317) at android.os.HandlerThread.run(HandlerThread.java:85) ``` [B] ``` Playback error androidx.media3.exoplayer.ExoPlaybackException: MediaCodecAudioRenderer error, index=1, format=Format(0, null, null, audio/mp4a-latm, mp4a.40.2, 134359, en, [-1, -1, -1.0, null], [2, 44100]), format_supported=YES at androidx.media3.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:649) at android.os.Handler.dispatchMessage(Handler.java:103) at android.os.Looper.loopOnce(Looper.java:232) at android.os.Looper.loop(Looper.java:317) at android.os.HandlerThread.run(HandlerThread.java:85) Caused by: android.media.MediaCodec$CryptoException: Test error message at androidx.media3.exoplayer.mediacodec.AsynchronousMediaCodecBufferEnqueuer.doQueueSecureInputBuffer(AsynchronousMediaCodecBufferEnqueuer.java:232) at androidx.media3.exoplayer.mediacodec.AsynchronousMediaCodecBufferEnqueuer.doHandleMessage(AsynchronousMediaCodecBufferEnqueuer.java:196) at androidx.media3.exoplayer.mediacodec.AsynchronousMediaCodecBufferEnqueuer.access$000(AsynchronousMediaCodecBufferEnqueuer.java:47) at androidx.media3.exoplayer.mediacodec.AsynchronousMediaCodecBufferEnqueuer$1.handleMessage(AsynchronousMediaCodecBufferEnqueuer.java:93) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loopOnce(Looper.java:232) at android.os.Looper.loop(Looper.java:317) at android.os.HandlerThread.run(HandlerThread.java:85) ``` PiperOrigin-RevId: 670951229
This commit is contained in:
parent
e27c7d5d45
commit
0933f561b7
@ -4,6 +4,9 @@
|
||||
|
||||
* Common Library:
|
||||
* ExoPlayer:
|
||||
* Fix `MediaCodec.CryptoException` sometimes being reported as an
|
||||
"unexpected runtime error" when `MediaCodec` is operated in asynchronous
|
||||
mode (default behaviour on API 31+).
|
||||
* Transformer:
|
||||
* Track Selection:
|
||||
* Extractors:
|
||||
|
@ -35,7 +35,6 @@ import static java.lang.annotation.ElementType.TYPE_USE;
|
||||
import android.annotation.TargetApi;
|
||||
import android.media.MediaCodec;
|
||||
import android.media.MediaCodec.CodecException;
|
||||
import android.media.MediaCodec.CryptoException;
|
||||
import android.media.MediaCrypto;
|
||||
import android.media.MediaCryptoException;
|
||||
import android.media.MediaFormat;
|
||||
@ -883,6 +882,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
readSourceOmittingSampleData(FLAG_PEEK);
|
||||
}
|
||||
decoderCounters.ensureUpdated();
|
||||
} catch (MediaCodec.CryptoException e) {
|
||||
throw createRendererException(
|
||||
e, inputFormat, Util.getErrorCodeForMediaDrmErrorCode(e.getErrorCode()));
|
||||
} catch (IllegalStateException e) {
|
||||
if (isMediaCodecException(e)) {
|
||||
onCodecError(e);
|
||||
@ -1391,7 +1393,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
processEndOfStream();
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
if (codecNeedsEosPropagation) {
|
||||
// Do nothing.
|
||||
} else {
|
||||
@ -1404,10 +1405,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
MediaCodec.BUFFER_FLAG_END_OF_STREAM);
|
||||
resetInputBuffer();
|
||||
}
|
||||
} catch (CryptoException e) {
|
||||
throw createRendererException(
|
||||
e, inputFormat, Util.getErrorCodeForMediaDrmErrorCode(e.getErrorCode()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1463,7 +1460,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
|
||||
onQueueInputBuffer(buffer);
|
||||
int flags = getCodecBufferFlags(buffer);
|
||||
try {
|
||||
if (bufferEncrypted) {
|
||||
checkNotNull(codec)
|
||||
.queueSecureInputBuffer(
|
||||
@ -1477,10 +1473,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
presentationTimeUs,
|
||||
flags);
|
||||
}
|
||||
} catch (CryptoException e) {
|
||||
throw createRendererException(
|
||||
e, inputFormat, Util.getErrorCodeForMediaDrmErrorCode(e.getErrorCode()));
|
||||
}
|
||||
|
||||
resetInputBuffer();
|
||||
codecReceivedBuffers = true;
|
||||
|
@ -30,6 +30,7 @@ import static org.mockito.Mockito.spy;
|
||||
|
||||
import android.media.MediaCodec;
|
||||
import android.media.MediaCrypto;
|
||||
import android.media.MediaDrm;
|
||||
import android.media.MediaFormat;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
@ -525,6 +526,43 @@ public class MediaCodecRendererTest {
|
||||
.contains("ISE from inside MediaCodec");
|
||||
}
|
||||
|
||||
// b/347367307#comment6
|
||||
@Test
|
||||
public void render_wrapsCryptoExceptionFromAnyMediaCodecMethod() throws Exception {
|
||||
MediaCodecAdapter.Factory throwingMediaCodecAdapterFactory =
|
||||
new ThrowingMediaCodecAdapter.Factory(
|
||||
() ->
|
||||
new MediaCodec.CryptoException(
|
||||
MediaDrm.ErrorCodes.ERROR_INSUFFICIENT_OUTPUT_PROTECTION, "Test exception"));
|
||||
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().isInstanceOf(MediaCodec.CryptoException.class);
|
||||
assertThat(playbackException).hasCauseThat().hasMessageThat().contains("Test exception");
|
||||
}
|
||||
|
||||
private FakeSampleStream createFakeSampleStream(Format format, long... sampleTimesUs) {
|
||||
ImmutableList.Builder<FakeSampleStream.FakeSampleStreamItem> sampleListBuilder =
|
||||
ImmutableList.builder();
|
||||
|
Loading…
x
Reference in New Issue
Block a user