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:
|
* Common Library:
|
||||||
* ExoPlayer:
|
* 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:
|
* Transformer:
|
||||||
* Track Selection:
|
* Track Selection:
|
||||||
* Extractors:
|
* Extractors:
|
||||||
|
@ -35,7 +35,6 @@ import static java.lang.annotation.ElementType.TYPE_USE;
|
|||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.media.MediaCodec.CodecException;
|
import android.media.MediaCodec.CodecException;
|
||||||
import android.media.MediaCodec.CryptoException;
|
|
||||||
import android.media.MediaCrypto;
|
import android.media.MediaCrypto;
|
||||||
import android.media.MediaCryptoException;
|
import android.media.MediaCryptoException;
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
@ -883,6 +882,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
readSourceOmittingSampleData(FLAG_PEEK);
|
readSourceOmittingSampleData(FLAG_PEEK);
|
||||||
}
|
}
|
||||||
decoderCounters.ensureUpdated();
|
decoderCounters.ensureUpdated();
|
||||||
|
} catch (MediaCodec.CryptoException e) {
|
||||||
|
throw createRendererException(
|
||||||
|
e, inputFormat, Util.getErrorCodeForMediaDrmErrorCode(e.getErrorCode()));
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
if (isMediaCodecException(e)) {
|
if (isMediaCodecException(e)) {
|
||||||
onCodecError(e);
|
onCodecError(e);
|
||||||
@ -1391,22 +1393,17 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
processEndOfStream();
|
processEndOfStream();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
if (codecNeedsEosPropagation) {
|
||||||
if (codecNeedsEosPropagation) {
|
// Do nothing.
|
||||||
// Do nothing.
|
} else {
|
||||||
} else {
|
codecReceivedEos = true;
|
||||||
codecReceivedEos = true;
|
codec.queueInputBuffer(
|
||||||
codec.queueInputBuffer(
|
inputIndex,
|
||||||
inputIndex,
|
/* offset= */ 0,
|
||||||
/* offset= */ 0,
|
/* size= */ 0,
|
||||||
/* size= */ 0,
|
/* presentationTimeUs= */ 0,
|
||||||
/* presentationTimeUs= */ 0,
|
MediaCodec.BUFFER_FLAG_END_OF_STREAM);
|
||||||
MediaCodec.BUFFER_FLAG_END_OF_STREAM);
|
resetInputBuffer();
|
||||||
resetInputBuffer();
|
|
||||||
}
|
|
||||||
} catch (CryptoException e) {
|
|
||||||
throw createRendererException(
|
|
||||||
e, inputFormat, Util.getErrorCodeForMediaDrmErrorCode(e.getErrorCode()));
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1463,23 +1460,18 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
|
|
||||||
onQueueInputBuffer(buffer);
|
onQueueInputBuffer(buffer);
|
||||||
int flags = getCodecBufferFlags(buffer);
|
int flags = getCodecBufferFlags(buffer);
|
||||||
try {
|
if (bufferEncrypted) {
|
||||||
if (bufferEncrypted) {
|
checkNotNull(codec)
|
||||||
checkNotNull(codec)
|
.queueSecureInputBuffer(
|
||||||
.queueSecureInputBuffer(
|
inputIndex, /* offset= */ 0, buffer.cryptoInfo, presentationTimeUs, flags);
|
||||||
inputIndex, /* offset= */ 0, buffer.cryptoInfo, presentationTimeUs, flags);
|
} else {
|
||||||
} else {
|
checkNotNull(codec)
|
||||||
checkNotNull(codec)
|
.queueInputBuffer(
|
||||||
.queueInputBuffer(
|
inputIndex,
|
||||||
inputIndex,
|
/* offset= */ 0,
|
||||||
/* offset= */ 0,
|
checkNotNull(buffer.data).limit(),
|
||||||
checkNotNull(buffer.data).limit(),
|
presentationTimeUs,
|
||||||
presentationTimeUs,
|
flags);
|
||||||
flags);
|
|
||||||
}
|
|
||||||
} catch (CryptoException e) {
|
|
||||||
throw createRendererException(
|
|
||||||
e, inputFormat, Util.getErrorCodeForMediaDrmErrorCode(e.getErrorCode()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resetInputBuffer();
|
resetInputBuffer();
|
||||||
|
@ -30,6 +30,7 @@ import static org.mockito.Mockito.spy;
|
|||||||
|
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.media.MediaCrypto;
|
import android.media.MediaCrypto;
|
||||||
|
import android.media.MediaDrm;
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@ -525,6 +526,43 @@ public class MediaCodecRendererTest {
|
|||||||
.contains("ISE from inside MediaCodec");
|
.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) {
|
private FakeSampleStream createFakeSampleStream(Format format, long... sampleTimesUs) {
|
||||||
ImmutableList.Builder<FakeSampleStream.FakeSampleStreamItem> sampleListBuilder =
|
ImmutableList.Builder<FakeSampleStream.FakeSampleStreamItem> sampleListBuilder =
|
||||||
ImmutableList.builder();
|
ImmutableList.builder();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user