Clean up codec reinitialization

Issue: #2826

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=218346327
This commit is contained in:
olly 2018-10-23 08:34:32 -07:00 committed by Oliver Woodman
parent 66c508651a
commit d56b7ad811

View File

@ -228,28 +228,25 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({ @IntDef({DRAIN_STATE_NONE, DRAIN_STATE_SIGNAL_END_OF_STREAM, DRAIN_STATE_WAIT_END_OF_STREAM})
REINITIALIZATION_STATE_NONE, private @interface DrainState {}
REINITIALIZATION_STATE_SIGNAL_END_OF_STREAM, /** The codec is not being drained. */
REINITIALIZATION_STATE_WAIT_END_OF_STREAM private static final int DRAIN_STATE_NONE = 0;
}) /** The codec needs to be drained, but we haven't signaled an end of stream to it yet. */
private @interface ReinitializationState {} private static final int DRAIN_STATE_SIGNAL_END_OF_STREAM = 1;
/** /** The codec needs to be drained, and we're waiting for it to output an end of stream. */
* The codec does not need to be re-initialized. private static final int DRAIN_STATE_WAIT_END_OF_STREAM = 2;
*/
private static final int REINITIALIZATION_STATE_NONE = 0; @Documented
/** @Retention(RetentionPolicy.SOURCE)
* The input format has changed in a way that requires the codec to be re-initialized, but we @IntDef({DRAIN_ACTION_NONE, DRAIN_ACTION_FLUSH, DRAIN_ACTION_REINITIALIZE})
* haven't yet signaled an end of stream to the existing codec. We need to do so in order to private @interface DrainAction {}
* ensure that it outputs any remaining buffers before we release it. /** No special action should be taken. */
*/ private static final int DRAIN_ACTION_NONE = 0;
private static final int REINITIALIZATION_STATE_SIGNAL_END_OF_STREAM = 1; /** The codec should be flushed. */
/** private static final int DRAIN_ACTION_FLUSH = 1;
* The input format has changed in a way that requires the codec to be re-initialized, and we've /** The codec should be re-initialized. */
* signaled an end of stream to the existing codec. We're waiting for the codec to output an end private static final int DRAIN_ACTION_REINITIALIZE = 2;
* of stream signal to indicate that it has output any remaining buffers before we release it.
*/
private static final int REINITIALIZATION_STATE_WAIT_END_OF_STREAM = 2;
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@ -283,8 +280,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
private static final int ADAPTATION_WORKAROUND_SLICE_WIDTH_HEIGHT = 32; private static final int ADAPTATION_WORKAROUND_SLICE_WIDTH_HEIGHT = 32;
private final MediaCodecSelector mediaCodecSelector; private final MediaCodecSelector mediaCodecSelector;
@Nullable @Nullable private final DrmSessionManager<FrameworkMediaCrypto> drmSessionManager;
private final DrmSessionManager<FrameworkMediaCrypto> drmSessionManager;
private final boolean playClearSamplesWithoutKeys; private final boolean playClearSamplesWithoutKeys;
private final float assumedMinimumCodecOperatingRate; private final float assumedMinimumCodecOperatingRate;
private final DecoderInputBuffer buffer; private final DecoderInputBuffer buffer;
@ -303,10 +299,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
private float rendererOperatingRate; private float rendererOperatingRate;
private float codecOperatingRate; private float codecOperatingRate;
private boolean codecConfiguredWithOperatingRate; private boolean codecConfiguredWithOperatingRate;
private @Nullable ArrayDeque<MediaCodecInfo> availableCodecInfos; @Nullable private ArrayDeque<MediaCodecInfo> availableCodecInfos;
private @Nullable DecoderInitializationException preferredDecoderInitializationException; @Nullable private DecoderInitializationException preferredDecoderInitializationException;
private @Nullable MediaCodecInfo codecInfo; @Nullable private MediaCodecInfo codecInfo;
private @AdaptationWorkaroundMode int codecAdaptationWorkaroundMode; @AdaptationWorkaroundMode private int codecAdaptationWorkaroundMode;
private boolean codecNeedsReconfigureWorkaround; private boolean codecNeedsReconfigureWorkaround;
private boolean codecNeedsDiscardToSpsWorkaround; private boolean codecNeedsDiscardToSpsWorkaround;
private boolean codecNeedsFlushWorkaround; private boolean codecNeedsFlushWorkaround;
@ -324,9 +320,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
private ByteBuffer outputBuffer; private ByteBuffer outputBuffer;
private boolean shouldSkipOutputBuffer; private boolean shouldSkipOutputBuffer;
private boolean codecReconfigured; private boolean codecReconfigured;
private @ReconfigurationState int codecReconfigurationState; @ReconfigurationState private int codecReconfigurationState;
private @ReinitializationState int codecReinitializationState; @DrainState private int codecDrainState;
private boolean codecReinitializationIsRelease; @DrainAction private int codecDrainAction;
private boolean codecReceivedBuffers; private boolean codecReceivedBuffers;
private boolean codecReceivedEos; private boolean codecReceivedEos;
@ -371,7 +367,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
decodeOnlyPresentationTimestamps = new ArrayList<>(); decodeOnlyPresentationTimestamps = new ArrayList<>();
outputBufferInfo = new MediaCodec.BufferInfo(); outputBufferInfo = new MediaCodec.BufferInfo();
codecReconfigurationState = RECONFIGURATION_STATE_NONE; codecReconfigurationState = RECONFIGURATION_STATE_NONE;
codecReinitializationState = REINITIALIZATION_STATE_NONE; codecDrainState = DRAIN_STATE_NONE;
codecDrainAction = DRAIN_ACTION_NONE;
codecOperatingRate = CODEC_OPERATING_RATE_UNSET; codecOperatingRate = CODEC_OPERATING_RATE_UNSET;
rendererOperatingRate = 1f; rendererOperatingRate = 1f;
} }
@ -567,9 +564,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
waitingForKeys = false; waitingForKeys = false;
decodeOnlyPresentationTimestamps.clear(); decodeOnlyPresentationTimestamps.clear();
codecInfo = null; codecInfo = null;
codecReconfigured = false;
codecReconfigurationState = RECONFIGURATION_STATE_NONE;
codecReinitializationState = REINITIALIZATION_STATE_NONE;
decoderCounters.decoderReleaseCount++; decoderCounters.decoderReleaseCount++;
try { try {
codec.stop(); codec.stop();
@ -674,15 +668,14 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
if (codec == null) { if (codec == null) {
return false; return false;
} }
if (codecNeedsFlushWorkaround if (codecDrainAction == DRAIN_ACTION_REINITIALIZE
|| (codecNeedsEosFlushWorkaround && codecReceivedEos) || codecNeedsFlushWorkaround
|| (codecReinitializationState != REINITIALIZATION_STATE_NONE || (codecNeedsEosFlushWorkaround && codecReceivedEos)) {
&& codecReinitializationIsRelease)) {
releaseCodec(); releaseCodec();
return true; return true;
} }
codec.flush();
codec.flush();
resetInputBuffer(); resetInputBuffer();
resetOutputBuffer(); resetOutputBuffer();
codecHotswapDeadlineMs = C.TIME_UNSET; codecHotswapDeadlineMs = C.TIME_UNSET;
@ -695,7 +688,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
waitingForKeys = false; waitingForKeys = false;
decodeOnlyPresentationTimestamps.clear(); decodeOnlyPresentationTimestamps.clear();
codecReinitializationState = REINITIALIZATION_STATE_NONE; codecDrainState = DRAIN_STATE_NONE;
codecDrainAction = DRAIN_ACTION_NONE;
// Reconfiguration data sent shortly before the flush may not have been processed by the // Reconfiguration data sent shortly before the flush may not have been processed by the
// decoder. If the codec has been reconfigured we always send reconfiguration data again to // decoder. If the codec has been reconfigured we always send reconfiguration data again to
// guarantee that it's processed. // guarantee that it's processed.
@ -836,12 +830,16 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
getState() == STATE_STARTED getState() == STATE_STARTED
? (SystemClock.elapsedRealtime() + MAX_CODEC_HOTSWAP_TIME_MS) ? (SystemClock.elapsedRealtime() + MAX_CODEC_HOTSWAP_TIME_MS)
: C.TIME_UNSET; : C.TIME_UNSET;
codecReconfigured = false;
codecReconfigurationState = RECONFIGURATION_STATE_NONE;
codecReceivedEos = false; codecReceivedEos = false;
codecReceivedBuffers = false; codecReceivedBuffers = false;
waitingForFirstSyncFrame = true; codecDrainState = DRAIN_STATE_NONE;
codecDrainAction = DRAIN_ACTION_NONE;
codecNeedsAdaptationWorkaroundBuffer = false; codecNeedsAdaptationWorkaroundBuffer = false;
shouldSkipAdaptationWorkaroundOutputBuffer = false; shouldSkipAdaptationWorkaroundOutputBuffer = false;
shouldSkipOutputBuffer = false; shouldSkipOutputBuffer = false;
waitingForFirstSyncFrame = true;
decoderCounters.decoderInitCount++; decoderCounters.decoderInitCount++;
long elapsed = codecInitializedTimestamp - codecInitializingTimestamp; long elapsed = codecInitializedTimestamp - codecInitializingTimestamp;
@ -897,9 +895,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
* @throws ExoPlaybackException If an error occurs feeding the input buffer. * @throws ExoPlaybackException If an error occurs feeding the input buffer.
*/ */
private boolean feedInputBuffer() throws ExoPlaybackException { private boolean feedInputBuffer() throws ExoPlaybackException {
if (codec == null || codecReinitializationState == REINITIALIZATION_STATE_WAIT_END_OF_STREAM if (codec == null || codecDrainState == DRAIN_STATE_WAIT_END_OF_STREAM || inputStreamEnded) {
|| inputStreamEnded) {
// We need to re-initialize the codec or the input stream has ended.
return false; return false;
} }
@ -912,7 +908,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
buffer.clear(); buffer.clear();
} }
if (codecReinitializationState == REINITIALIZATION_STATE_SIGNAL_END_OF_STREAM) { if (codecDrainState == DRAIN_STATE_SIGNAL_END_OF_STREAM) {
// We need to re-initialize the codec. Send an end of stream signal to the existing codec so // 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. // that it outputs any remaining buffers before we release it.
if (codecNeedsEosPropagation) { if (codecNeedsEosPropagation) {
@ -922,7 +918,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
codec.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); codec.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
resetInputBuffer(); resetInputBuffer();
} }
codecReinitializationState = REINITIALIZATION_STATE_WAIT_END_OF_STREAM; codecDrainState = DRAIN_STATE_WAIT_END_OF_STREAM;
return false; return false;
} }
@ -1109,19 +1105,19 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
// We have an existing codec that we may need to reconfigure or re-initialize. If the existing // We have an existing codec that we may need to reconfigure or re-initialize. If the existing
// codec instance is being kept then its operating rate may need to be updated. // codec instance is being kept then its operating rate may need to be updated.
if (pendingDrmSession != drmSession) { if (pendingDrmSession != drmSession) {
reinitializeCodec(/* release= */ true); drainAndReinitializeCodec();
} else { } else {
switch (canKeepCodec(codec, codecInfo, oldFormat, format)) { switch (canKeepCodec(codec, codecInfo, oldFormat, format)) {
case KEEP_CODEC_RESULT_NO: case KEEP_CODEC_RESULT_NO:
reinitializeCodec(/* release= */ true); drainAndReinitializeCodec();
break; break;
case KEEP_CODEC_RESULT_YES_WITH_FLUSH: case KEEP_CODEC_RESULT_YES_WITH_FLUSH:
reinitializeCodec(/* release= */ false); drainAndFlushCodec();
updateCodecOperatingRate(); updateCodecOperatingRate();
break; break;
case KEEP_CODEC_RESULT_YES_WITH_RECONFIGURATION: case KEEP_CODEC_RESULT_YES_WITH_RECONFIGURATION:
if (codecNeedsReconfigureWorkaround) { if (codecNeedsReconfigureWorkaround) {
reinitializeCodec(/* release= */ true); drainAndReinitializeCodec();
} else { } else {
codecReconfigured = true; codecReconfigured = true;
codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING; codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING;
@ -1253,15 +1249,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
this.codecOperatingRate = codecOperatingRate; this.codecOperatingRate = codecOperatingRate;
if (codec == null if (codec == null || codecDrainAction == DRAIN_ACTION_REINITIALIZE) {
|| (codecReinitializationState != REINITIALIZATION_STATE_NONE
&& codecReinitializationIsRelease)) {
// Either no codec, or it's about to be released due to re-initialization anyway. // Either no codec, or it's about to be released due to re-initialization anyway.
} else if (codecOperatingRate == CODEC_OPERATING_RATE_UNSET } else if (codecOperatingRate == CODEC_OPERATING_RATE_UNSET
&& codecConfiguredWithOperatingRate) { && codecConfiguredWithOperatingRate) {
// We need to clear the operating rate. The only way to do so is to instantiate a new codec // We need to clear the operating rate. The only way to do so is to instantiate a new codec
// instance. See [Internal ref: b/71987865]. // instance. See [Internal ref: b/71987865].
reinitializeCodec(/* release= */ true); drainAndReinitializeCodec();
} else if (codecOperatingRate != CODEC_OPERATING_RATE_UNSET } else if (codecOperatingRate != CODEC_OPERATING_RATE_UNSET
&& (codecConfiguredWithOperatingRate && (codecConfiguredWithOperatingRate
|| codecOperatingRate > assumedMinimumCodecOperatingRate)) { || codecOperatingRate > assumedMinimumCodecOperatingRate)) {
@ -1274,26 +1268,26 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
} }
/** /** Starts draining the codec for flush. */
* Starts the process of re-initializing the codec. This may occur immediately, or be deferred private void drainAndFlushCodec() {
* until any final output buffers have been dequeued.
*
* @param release Whether re-initialization requires fully releasing the codec and instantiating a
* new instance, as opposed to flushing and reusing the current instance.
* @throws ExoPlaybackException If an error occurs releasing or initializing a codec.
*/
private void reinitializeCodec(boolean release) throws ExoPlaybackException {
if (codecReceivedBuffers) { if (codecReceivedBuffers) {
// Signal end of stream and wait for any final output buffers before re-initialization. codecDrainState = DRAIN_STATE_SIGNAL_END_OF_STREAM;
codecReinitializationState = REINITIALIZATION_STATE_SIGNAL_END_OF_STREAM; codecDrainAction = DRAIN_ACTION_FLUSH;
codecReinitializationIsRelease = release;
return;
} }
}
// Nothing has been queued to the decoder. If we need to fully release the codec and instantiate /**
// a new instance, do so immediately. If only a flush is required then we can do nothing, since * Starts draining the codec for re-initialization. Re-initialization may occur immediately if no
// flushing will be a no-op. * buffers have been queued to the codec.
if (release) { *
* @throws ExoPlaybackException If an error occurs re-initializing a codec.
*/
private void drainAndReinitializeCodec() throws ExoPlaybackException {
if (codecReceivedBuffers) {
codecDrainState = DRAIN_STATE_SIGNAL_END_OF_STREAM;
codecDrainAction = DRAIN_ACTION_REINITIALIZE;
} else {
// Nothing has been queued to the decoder, so we can re-initialize immediately.
releaseCodec(); releaseCodec();
maybeInitCodec(); maybeInitCodec();
} }
@ -1334,8 +1328,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
/* MediaCodec.INFO_TRY_AGAIN_LATER (-1) or unknown negative return value */ /* MediaCodec.INFO_TRY_AGAIN_LATER (-1) or unknown negative return value */
if (codecNeedsEosPropagation if (codecNeedsEosPropagation
&& (inputStreamEnded && (inputStreamEnded || codecDrainState == DRAIN_STATE_WAIT_END_OF_STREAM)) {
|| codecReinitializationState == REINITIALIZATION_STATE_WAIT_END_OF_STREAM)) {
processEndOfStream(); processEndOfStream();
} }
return false; return false;
@ -1498,17 +1491,19 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
* @throws ExoPlaybackException If an error occurs processing the signal. * @throws ExoPlaybackException If an error occurs processing the signal.
*/ */
private void processEndOfStream() throws ExoPlaybackException { private void processEndOfStream() throws ExoPlaybackException {
if (codecReinitializationState == REINITIALIZATION_STATE_WAIT_END_OF_STREAM) { switch (codecDrainAction) {
// We're waiting to re-initialize the codec, and have now processed all final buffers. case DRAIN_ACTION_REINITIALIZE:
if (codecReinitializationIsRelease) {
releaseCodec(); releaseCodec();
maybeInitCodec(); maybeInitCodec();
} else { break;
case DRAIN_ACTION_FLUSH:
flushOrReinitCodec(); flushOrReinitCodec();
} break;
} else { case DRAIN_ACTION_NONE:
outputStreamEnded = true; default:
renderToEndOfStream(); outputStreamEnded = true;
renderToEndOfStream();
break;
} }
} }