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 271e8ff461..8c4ad83992 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java @@ -135,6 +135,25 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { */ private static final int RECONFIGURATION_STATE_QUEUE_PENDING = 2; + /** + * No reinit is needed + */ + private static final int REINIT_STATE_NONE = 0; + /** + * The input format has just changed. Signal an end of stream to the codec so that we can + * retrieve the very last decoded samples from the previous format. + */ + private static final int REINIT_STATE_SIGNAL_END_OF_STREAM = 1; + /** + * The end of stream has been sent, wait for the codec to acknowledge it. + */ + private static final int REINIT_STATE_WAIT_END_OF_STREAM = 2; + /** + * The last sample has been processed, we can safely reinit now. + */ + private static final int REINIT_STATE_DO_REINIT_NOW = 3; + + public final CodecCounters codecCounters; private final DrmSessionManager drmSessionManager; @@ -159,6 +178,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { private boolean openedDrmSession; private boolean codecReconfigured; private int codecReconfigurationState; + private int codecReinitState; private int trackIndex; private int sourceState; @@ -166,6 +186,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { private boolean outputStreamEnded; private boolean waitingForKeys; private boolean waitingForFirstSyncFrame; + private boolean hasQueuedOneInputBuffer; private long currentPositionUs; /** @@ -194,6 +215,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { formatHolder = new MediaFormatHolder(); decodeOnlyPresentationTimestamps = new ArrayList(); outputBufferInfo = new MediaCodec.BufferInfo(); + codecReinitState = REINIT_STATE_NONE; } @Override @@ -302,6 +324,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { inputIndex = -1; outputIndex = -1; waitingForFirstSyncFrame = true; + hasQueuedOneInputBuffer = false; codecCounters.codecInitCount++; } @@ -346,6 +369,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { codecReconfigured = false; codecIsAdaptive = false; codecReconfigurationState = RECONFIGURATION_STATE_NONE; + codecReinitState = REINIT_STATE_NONE; codecCounters.codecReleaseCount++; try { codec.stop(); @@ -493,6 +517,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { if (inputStreamEnded) { return false; } + + if (codecReinitState == REINIT_STATE_DO_REINIT_NOW) { + releaseCodec(); + maybeInitCodec(); + return false; + } + if (inputIndex < 0) { inputIndex = codec.dequeueInputBuffer(0); if (inputIndex < 0) { @@ -502,6 +533,16 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { sampleHolder.data.clear(); } + if (codecReinitState == REINIT_STATE_SIGNAL_END_OF_STREAM) { + codec.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); + inputIndex = -1; + codecReinitState = REINIT_STATE_WAIT_END_OF_STREAM; + return false; + } else if (codecReinitState != REINIT_STATE_NONE) { + // we are still waiting for the last samples to be output + return false; + } + int result; if (waitingForKeys) { // We've already read an encrypted sample into sampleHolder, and are waiting for keys. @@ -591,6 +632,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { codec.queueInputBuffer(inputIndex, 0 , bufferSize, presentationTimeUs, 0); } inputIndex = -1; + hasQueuedOneInputBuffer = true; codecReconfigurationState = RECONFIGURATION_STATE_NONE; } catch (CryptoException e) { notifyCryptoError(e); @@ -644,8 +686,12 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { codecReconfigured = true; codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING; } else { - releaseCodec(); - maybeInitCodec(); + if (!hasQueuedOneInputBuffer) { + // no need to signal end of stream if nothing has been queued, we can just reinit asap + codecReinitState = REINIT_STATE_DO_REINIT_NOW; + } else { + codecReinitState = REINIT_STATE_SIGNAL_END_OF_STREAM; + } } } @@ -733,7 +779,12 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { } if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { - outputStreamEnded = true; + if (codecReinitState == REINIT_STATE_WAIT_END_OF_STREAM) { + codecReinitState = REINIT_STATE_DO_REINIT_NOW; + } else { + outputStreamEnded = true; + } + return false; }