From 8e9aadc5e58701448625ffbbe9952bd3b7f4286d Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Tue, 1 Sep 2015 13:56:10 +0100 Subject: [PATCH] Workaround for vorbis EOS flush bug (23361053). I'm assuming this will be fixed in API level 24. --- .../exoplayer/MediaCodecTrackRenderer.java | 62 ++++++++++++++----- 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java index 84a24db6e3..e119f9a23e 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java @@ -192,7 +192,9 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer private DrmInitData drmInitData; private MediaCodec codec; private boolean codecIsAdaptive; - private boolean codecNeedsEndOfStreamWorkaround; + private boolean codecNeedsEosPropagationWorkaround; + private boolean codecNeedsEosFlushWorkaround; + private boolean codecReceivedEos; private ByteBuffer[] inputBuffers; private ByteBuffer[] outputBuffers; private long codecHotswapTimeMs; @@ -319,7 +321,8 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer String decoderName = decoderInfo.name; codecIsAdaptive = decoderInfo.adaptive; - codecNeedsEndOfStreamWorkaround = codecNeedsEndOfStreamWorkaround(decoderName); + codecNeedsEosPropagationWorkaround = codecNeedsEosPropagationWorkaround(decoderName); + codecNeedsEosFlushWorkaround = codecNeedsEosFlushWorkaround(decoderName); try { long codecInitializingTimestamp = SystemClock.elapsedRealtime(); TraceUtil.beginSection("createByCodecName(" + decoderName + ")"); @@ -395,7 +398,9 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer codecReconfigured = false; codecHasQueuedBuffers = false; codecIsAdaptive = false; - codecNeedsEndOfStreamWorkaround = false; + codecNeedsEosPropagationWorkaround = false; + codecNeedsEosFlushWorkaround = false; + codecReceivedEos = false; codecReconfigurationState = RECONFIGURATION_STATE_NONE; codecReinitializationState = REINITIALIZATION_STATE_NONE; codecCounters.codecReleaseCount++; @@ -480,14 +485,19 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer waitingForFirstSyncFrame = true; waitingForKeys = false; decodeOnlyPresentationTimestamps.clear(); - // Workaround for framework bugs. - // See [Internal: b/8347958], [Internal: b/8578467], [Internal: b/8543366]. - if (Util.SDK_INT >= 18 && codecReinitializationState == REINITIALIZATION_STATE_NONE) { - codec.flush(); - codecHasQueuedBuffers = false; - } else { + if (Util.SDK_INT < 18 || (codecNeedsEosFlushWorkaround && codecReceivedEos)) { + // Workaround framework bugs. See [Internal: b/8347958, b/8578467, b/8543366, b/23361053]. releaseCodec(); maybeInitCodec(); + } else if (codecReinitializationState != REINITIALIZATION_STATE_NONE) { + // We're already waiting to release and re-initialize the codec. Since we're now flushing, + // there's no need to wait any longer. + releaseCodec(); + maybeInitCodec(); + } else { + // We can flush and re-use the existing decoder. + codec.flush(); + codecHasQueuedBuffers = false; } if (codecReconfigured && format != null) { // Any reconfiguration data that we send shortly before the flush may be discarded. We @@ -524,9 +534,10 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer if (codecReinitializationState == REINITIALIZATION_STATE_SIGNAL_END_OF_STREAM) { // We need to re-initialize the codec. Send an end of stream signal to the existing codec so // that it outputs any remaining buffers before we release it. - if (codecNeedsEndOfStreamWorkaround) { + if (codecNeedsEosPropagationWorkaround) { // Do nothing. } else { + codecReceivedEos = true; codec.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); inputIndex = -1; } @@ -581,9 +592,10 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer } inputStreamEnded = true; try { - if (codecNeedsEndOfStreamWorkaround) { + if (codecNeedsEosPropagationWorkaround) { // Do nothing. } else { + codecReceivedEos = true; codec.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); inputIndex = -1; } @@ -781,7 +793,7 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer codecCounters.outputBuffersChangedCount++; return true; } else if (outputIndex < 0) { - if (codecNeedsEndOfStreamWorkaround && (inputStreamEnded + if (codecNeedsEosPropagationWorkaround && (inputStreamEnded || codecReinitializationState == REINITIALIZATION_STATE_WAIT_END_OF_STREAM)) { processEndOfStream(); return true; @@ -879,17 +891,18 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer } /** - * Returns whether the decoder is known to handle {@link MediaCodec#BUFFER_FLAG_END_OF_STREAM} - * incorrectly on the host device. + * Returns whether the decoder is known to handle the propagation of the + * {@link MediaCodec#BUFFER_FLAG_END_OF_STREAM} flag incorrectly on the host device. *

* If true is returned, the renderer will work around the issue by approximating end of stream - * behavior without involvement of the underlying decoder. + * behavior without relying on the flag being propagated through to an output buffer by the + * underlying decoder. * * @param name The name of the decoder. * @return True if the decoder is known to handle {@link MediaCodec#BUFFER_FLAG_END_OF_STREAM} - * incorrectly on the host device. False otherwise. + * propagation incorrectly on the host device. False otherwise. */ - private static boolean codecNeedsEndOfStreamWorkaround(String name) { + private static boolean codecNeedsEosPropagationWorkaround(String name) { return Util.SDK_INT <= 17 && "OMX.rk.video_decoder.avc".equals(name) && ("ht7s3".equals(Util.DEVICE) // Tesco HUDL @@ -897,4 +910,19 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer || "rk31sdk".equals(Util.DEVICE)); // Rockchip rk31 } + /** + * Returns whether the decoder is known to behave incorrectly if flushed after receiving an input + * buffer with {@link MediaCodec#BUFFER_FLAG_END_OF_STREAM} set. + *

+ * If true is returned, the renderer will work around the issue by instantiating a new decoder + * when this case occurs. + * + * @param name The name of the decoder. + * @return True if the decoder is known to behave incorrectly if flushed after receiving an input + * buffer with {@link MediaCodec#BUFFER_FLAG_END_OF_STREAM} set. False otherwise. + */ + private static boolean codecNeedsEosFlushWorkaround(String name) { + return Util.SDK_INT <= 23 && "OMX.google.vorbis.decoder".equals(name); + } + }