From 842f622d29eb1fa3929a0b2e91f5fc425c78313b Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 12 Oct 2018 02:57:28 -0700 Subject: [PATCH] Allow codec flushing without re-initialization For decoder reuse, we want disable() to flush the decoder. However, if the flush needs to release the decoder for some reason, it seems non-ideal to immediately re-initialize it. Re-initialization can also throw ExoPlaybackException, which we don't want for disabling. This change allows a variant of flush that wont re-initialize the decoder if it has to be released, which will be used from disable(). Issue: #2826 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=216834862 --- .../mediacodec/MediaCodecRenderer.java | 41 +++++++++++++++---- .../video/MediaCodecVideoRenderer.java | 11 +++-- .../testutil/DebugRenderersFactory.java | 9 ++-- 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index 87d56fdf3d..8554d84420 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -546,9 +546,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException { inputStreamEnded = false; outputStreamEnded = false; - if (codec != null) { - flushCodec(); - } + flushOrReinitCodec(); formatQueue.clear(); } @@ -687,10 +685,36 @@ public abstract class MediaCodecRenderer extends BaseRenderer { decoderCounters.ensureUpdated(); } - protected void flushCodec() throws ExoPlaybackException { + /** + * Flushes the codec. If flushing is not possible, the codec will be released and re-instantiated. + * This method is a no-op if the codec is {@code null}. + * + *

The implementation of this method calls {@link #flushOrReleaseCodec()}, and {@link + * #maybeInitCodec()} if the codec needs to be re-instantiated. + * + * @throws ExoPlaybackException If an error occurs re-instantiating the codec. + */ + protected final void flushOrReinitCodec() throws ExoPlaybackException { + if (flushOrReleaseCodec()) { + maybeInitCodec(); + } + } + + /** + * Flushes the codec. If flushing is not possible, the codec will be released. This method is a + * no-op if the codec is {@code null}. + * + * @return Whether the codec was released. + */ + protected boolean flushOrReleaseCodec() { + if (codec == null) { + // Nothing to do. + return false; + } codecHotswapDeadlineMs = C.TIME_UNSET; resetInputBuffer(); resetOutputBuffer(); + codecReceivedBuffers = false; waitingForFirstSyncFrame = true; waitingForKeys = false; shouldSkipOutputBuffer = false; @@ -699,28 +723,27 @@ public abstract class MediaCodecRenderer extends BaseRenderer { shouldSkipAdaptationWorkaroundOutputBuffer = false; if (codecNeedsFlushWorkaround || (codecNeedsEosFlushWorkaround && codecReceivedEos)) { releaseCodec(); - maybeInitCodec(); + return true; } else if (codecReinitializationState != REINITIALIZATION_STATE_NONE) { // We're already waiting to re-initialize the codec. Since we're now flushing, there's no need // to wait any longer. if (codecReinitializationIsRelease) { releaseCodec(); - maybeInitCodec(); + return true; } else { codec.flush(); - codecReceivedBuffers = false; codecReinitializationState = REINITIALIZATION_STATE_NONE; } } else { // We can flush and re-use the existing decoder. codec.flush(); - codecReceivedBuffers = false; } if (codecReconfigured && format != null) { // Any reconfiguration data that we send shortly before the flush may be discarded. We // avoid this issue by sending reconfiguration data following every flush. codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING; } + return false; } private boolean initCodecWithFallback(MediaCrypto crypto, boolean drmSessionRequiresSecureDecoder) @@ -1496,7 +1519,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { releaseCodec(); maybeInitCodec(); } else { - flushCodec(); + flushOrReinitCodec(); } } else { outputStreamEnded = true; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index d217b47785..2b2e628017 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -520,9 +520,12 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { @CallSuper @Override - protected void flushCodec() throws ExoPlaybackException { - super.flushCodec(); - buffersInCodecCount = 0; + protected boolean flushOrReleaseCodec() { + try { + return super.flushOrReleaseCodec(); + } finally { + buffersInCodecCount = 0; + } } @Override @@ -859,7 +862,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { // We dropped some buffers to catch up, so update the decoder counters and flush the codec, // which releases all pending buffers buffers including the current output buffer. updateDroppedBufferCounters(buffersInCodecCount + droppedSourceBufferCount); - flushCodec(); + flushOrReinitCodec(); return true; } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/DebugRenderersFactory.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/DebugRenderersFactory.java index 627b5b72f3..39194d48fe 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/DebugRenderersFactory.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/DebugRenderersFactory.java @@ -105,9 +105,12 @@ public class DebugRenderersFactory extends DefaultRenderersFactory { } @Override - protected void flushCodec() throws ExoPlaybackException { - super.flushCodec(); - clearTimestamps(); + protected boolean flushOrReleaseCodec() { + try { + return super.flushOrReleaseCodec(); + } finally { + clearTimestamps(); + } } @Override