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
This commit is contained in:
olly 2018-10-12 02:57:28 -07:00 committed by Oliver Woodman
parent 42c3ff3934
commit 842f622d29
3 changed files with 45 additions and 16 deletions

View File

@ -546,9 +546,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException { protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException {
inputStreamEnded = false; inputStreamEnded = false;
outputStreamEnded = false; outputStreamEnded = false;
if (codec != null) { flushOrReinitCodec();
flushCodec();
}
formatQueue.clear(); formatQueue.clear();
} }
@ -687,10 +685,36 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
decoderCounters.ensureUpdated(); 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}.
*
* <p>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; codecHotswapDeadlineMs = C.TIME_UNSET;
resetInputBuffer(); resetInputBuffer();
resetOutputBuffer(); resetOutputBuffer();
codecReceivedBuffers = false;
waitingForFirstSyncFrame = true; waitingForFirstSyncFrame = true;
waitingForKeys = false; waitingForKeys = false;
shouldSkipOutputBuffer = false; shouldSkipOutputBuffer = false;
@ -699,28 +723,27 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
shouldSkipAdaptationWorkaroundOutputBuffer = false; shouldSkipAdaptationWorkaroundOutputBuffer = false;
if (codecNeedsFlushWorkaround || (codecNeedsEosFlushWorkaround && codecReceivedEos)) { if (codecNeedsFlushWorkaround || (codecNeedsEosFlushWorkaround && codecReceivedEos)) {
releaseCodec(); releaseCodec();
maybeInitCodec(); return true;
} else if (codecReinitializationState != REINITIALIZATION_STATE_NONE) { } else if (codecReinitializationState != REINITIALIZATION_STATE_NONE) {
// We're already waiting to re-initialize the codec. Since we're now flushing, there's no need // We're already waiting to re-initialize the codec. Since we're now flushing, there's no need
// to wait any longer. // to wait any longer.
if (codecReinitializationIsRelease) { if (codecReinitializationIsRelease) {
releaseCodec(); releaseCodec();
maybeInitCodec(); return true;
} else { } else {
codec.flush(); codec.flush();
codecReceivedBuffers = false;
codecReinitializationState = REINITIALIZATION_STATE_NONE; codecReinitializationState = REINITIALIZATION_STATE_NONE;
} }
} else { } else {
// We can flush and re-use the existing decoder. // We can flush and re-use the existing decoder.
codec.flush(); codec.flush();
codecReceivedBuffers = false;
} }
if (codecReconfigured && format != null) { if (codecReconfigured && format != null) {
// Any reconfiguration data that we send shortly before the flush may be discarded. We // Any reconfiguration data that we send shortly before the flush may be discarded. We
// avoid this issue by sending reconfiguration data following every flush. // avoid this issue by sending reconfiguration data following every flush.
codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING; codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING;
} }
return false;
} }
private boolean initCodecWithFallback(MediaCrypto crypto, boolean drmSessionRequiresSecureDecoder) private boolean initCodecWithFallback(MediaCrypto crypto, boolean drmSessionRequiresSecureDecoder)
@ -1496,7 +1519,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
releaseCodec(); releaseCodec();
maybeInitCodec(); maybeInitCodec();
} else { } else {
flushCodec(); flushOrReinitCodec();
} }
} else { } else {
outputStreamEnded = true; outputStreamEnded = true;

View File

@ -520,9 +520,12 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
@CallSuper @CallSuper
@Override @Override
protected void flushCodec() throws ExoPlaybackException { protected boolean flushOrReleaseCodec() {
super.flushCodec(); try {
buffersInCodecCount = 0; return super.flushOrReleaseCodec();
} finally {
buffersInCodecCount = 0;
}
} }
@Override @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, // 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. // which releases all pending buffers buffers including the current output buffer.
updateDroppedBufferCounters(buffersInCodecCount + droppedSourceBufferCount); updateDroppedBufferCounters(buffersInCodecCount + droppedSourceBufferCount);
flushCodec(); flushOrReinitCodec();
return true; return true;
} }

View File

@ -105,9 +105,12 @@ public class DebugRenderersFactory extends DefaultRenderersFactory {
} }
@Override @Override
protected void flushCodec() throws ExoPlaybackException { protected boolean flushOrReleaseCodec() {
super.flushCodec(); try {
clearTimestamps(); return super.flushOrReleaseCodec();
} finally {
clearTimestamps();
}
} }
@Override @Override