Work around broken AAC decoder EoS handling on L.

SoftAAC2 would cause an exception to be thrown from
dequeueOutputBuffer/releaseOutputBuffer after queueing an end-of-stream buffer
for certain streams.

The bug was introduced in L and fixed in L MR1, so the workaround is targeted to
API 21.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=147613659
This commit is contained in:
andrewlewis 2017-02-15 10:30:40 -08:00 committed by Oliver Woodman
parent d6e15b7953
commit ec98bd9ea1

View File

@ -183,6 +183,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
private boolean codecNeedsAdaptationWorkaround; private boolean codecNeedsAdaptationWorkaround;
private boolean codecNeedsEosPropagationWorkaround; private boolean codecNeedsEosPropagationWorkaround;
private boolean codecNeedsEosFlushWorkaround; private boolean codecNeedsEosFlushWorkaround;
private boolean codecNeedsEosOutputExceptionWorkaround;
private boolean codecNeedsMonoChannelCountWorkaround; private boolean codecNeedsMonoChannelCountWorkaround;
private boolean codecNeedsAdaptationWorkaroundBuffer; private boolean codecNeedsAdaptationWorkaroundBuffer;
private boolean shouldSkipAdaptationWorkaroundOutputBuffer; private boolean shouldSkipAdaptationWorkaroundOutputBuffer;
@ -342,6 +343,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
codecNeedsAdaptationWorkaround = codecNeedsAdaptationWorkaround(codecName); codecNeedsAdaptationWorkaround = codecNeedsAdaptationWorkaround(codecName);
codecNeedsEosPropagationWorkaround = codecNeedsEosPropagationWorkaround(codecName); codecNeedsEosPropagationWorkaround = codecNeedsEosPropagationWorkaround(codecName);
codecNeedsEosFlushWorkaround = codecNeedsEosFlushWorkaround(codecName); codecNeedsEosFlushWorkaround = codecNeedsEosFlushWorkaround(codecName);
codecNeedsEosOutputExceptionWorkaround = codecNeedsEosOutputExceptionWorkaround(codecName);
codecNeedsMonoChannelCountWorkaround = codecNeedsMonoChannelCountWorkaround(codecName, format); codecNeedsMonoChannelCountWorkaround = codecNeedsMonoChannelCountWorkaround(codecName, format);
try { try {
long codecInitializingTimestamp = SystemClock.elapsedRealtime(); long codecInitializingTimestamp = SystemClock.elapsedRealtime();
@ -513,7 +515,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
codecNeedsAdaptationWorkaroundBuffer = false; codecNeedsAdaptationWorkaroundBuffer = false;
shouldSkipAdaptationWorkaroundOutputBuffer = false; shouldSkipAdaptationWorkaroundOutputBuffer = false;
if (codecNeedsFlushWorkaround || (codecNeedsEosFlushWorkaround && codecReceivedEos)) { if (codecNeedsFlushWorkaround || (codecNeedsEosFlushWorkaround && codecReceivedEos)) {
// Workaround framework bugs. See [Internal: b/8347958, b/8578467, b/8543366, b/23361053].
releaseCodec(); releaseCodec();
maybeInitCodec(); maybeInitCodec();
} else if (codecReinitializationState != REINITIALIZATION_STATE_NONE) { } else if (codecReinitializationState != REINITIALIZATION_STATE_NONE) {
@ -867,7 +868,22 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
private boolean drainOutputBuffer(long positionUs, long elapsedRealtimeUs) private boolean drainOutputBuffer(long positionUs, long elapsedRealtimeUs)
throws ExoPlaybackException { throws ExoPlaybackException {
if (outputIndex < 0) { if (outputIndex < 0) {
outputIndex = codec.dequeueOutputBuffer(outputBufferInfo, getDequeueOutputBufferTimeoutUs()); if (codecNeedsEosOutputExceptionWorkaround && codecReceivedEos) {
try {
outputIndex = codec.dequeueOutputBuffer(outputBufferInfo,
getDequeueOutputBufferTimeoutUs());
} catch (IllegalStateException e) {
processEndOfStream();
if (outputStreamEnded) {
// Release the codec, as it's in an error state.
releaseCodec();
}
return false;
}
} else {
outputIndex = codec.dequeueOutputBuffer(outputBufferInfo,
getDequeueOutputBufferTimeoutUs());
}
if (outputIndex >= 0) { if (outputIndex >= 0) {
// We've dequeued a buffer. // We've dequeued a buffer.
if (shouldSkipAdaptationWorkaroundOutputBuffer) { if (shouldSkipAdaptationWorkaroundOutputBuffer) {
@ -906,9 +922,27 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
} }
if (processOutputBuffer(positionUs, elapsedRealtimeUs, codec, outputBuffers[outputIndex], boolean processedOutputBuffer;
outputIndex, outputBufferInfo.flags, outputBufferInfo.presentationTimeUs, if (codecNeedsEosOutputExceptionWorkaround && codecReceivedEos) {
shouldSkipOutputBuffer)) { try {
processedOutputBuffer = processOutputBuffer(positionUs, elapsedRealtimeUs, codec,
outputBuffers[outputIndex], outputIndex, outputBufferInfo.flags,
outputBufferInfo.presentationTimeUs, shouldSkipOutputBuffer);
} catch (IllegalStateException e) {
processEndOfStream();
if (outputStreamEnded) {
// Release the codec, as it's in an error state.
releaseCodec();
}
return false;
}
} else {
processedOutputBuffer = processOutputBuffer(positionUs, elapsedRealtimeUs, codec,
outputBuffers[outputIndex], outputIndex, outputBufferInfo.flags,
outputBufferInfo.presentationTimeUs, shouldSkipOutputBuffer);
}
if (processedOutputBuffer) {
onProcessedOutputBuffer(outputBufferInfo.presentationTimeUs); onProcessedOutputBuffer(outputBufferInfo.presentationTimeUs);
outputIndex = C.INDEX_UNSET; outputIndex = C.INDEX_UNSET;
return true; return true;
@ -1010,6 +1044,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
* <p> * <p>
* If true is returned, the renderer will work around the issue by releasing the decoder and * If true is returned, the renderer will work around the issue by releasing the decoder and
* instantiating a new one rather than flushing the current instance. * instantiating a new one rather than flushing the current instance.
* <p>
* See [Internal: b/8347958, b/8543366].
* *
* @param name The name of the decoder. * @param name The name of the decoder.
* @return True if the decoder is known to fail when flushed. * @return True if the decoder is known to fail when flushed.
@ -1079,6 +1115,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
* <p> * <p>
* If true is returned, the renderer will work around the issue by instantiating a new decoder * If true is returned, the renderer will work around the issue by instantiating a new decoder
* when this case occurs. * when this case occurs.
* <p>
* See [Internal: b/8578467, b/23361053].
* *
* @param name The name of the decoder. * @param name The name of the decoder.
* @return True if the decoder is known to behave incorrectly if flushed after receiving an input * @return True if the decoder is known to behave incorrectly if flushed after receiving an input
@ -1091,6 +1129,21 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|| "OMX.amlogic.avc.decoder.awesome.secure".equals(name))); || "OMX.amlogic.avc.decoder.awesome.secure".equals(name)));
} }
/**
* Returns whether the decoder may throw an {@link IllegalStateException} from
* {@link MediaCodec#dequeueOutputBuffer(MediaCodec.BufferInfo, long)} or
* {@link MediaCodec#releaseOutputBuffer(int, boolean)} after receiving an input
* buffer with {@link MediaCodec#BUFFER_FLAG_END_OF_STREAM} set.
* <p>
* See [Internal: b/17933838].
*
* @param name The name of the decoder.
* @return True if the decoder may throw an exception after receiving an end-of-stream buffer.
*/
private static boolean codecNeedsEosOutputExceptionWorkaround(String name) {
return Util.SDK_INT == 21 && "OMX.google.aac.decoder".equals(name);
}
/** /**
* Returns whether the decoder is known to set the number of audio channels in the output format * Returns whether the decoder is known to set the number of audio channels in the output format
* to 2 for the given input format, whilst only actually outputting a single channel. * to 2 for the given input format, whilst only actually outputting a single channel.