diff --git a/library/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java b/library/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java index a7e05a010a..2f07fe5294 100644 --- a/library/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java @@ -19,6 +19,7 @@ import android.os.Handler; import android.os.Handler.Callback; import android.os.Looper; import android.os.Message; +import android.support.annotation.IntDef; import com.google.android.exoplayer2.BaseRenderer; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; @@ -26,6 +27,8 @@ import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.MimeTypes; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Collections; import java.util.List; @@ -52,6 +55,27 @@ public final class TextRenderer extends BaseRenderer implements Callback { } + @Retention(RetentionPolicy.SOURCE) + @IntDef({REPLACEMENT_STATE_NONE, REPLACEMENT_STATE_SIGNAL_END_OF_STREAM, + REPLACEMENT_STATE_WAIT_END_OF_STREAM}) + private @interface ReplacementState {} + /** + * The decoder does not need to be replaced. + */ + private static final int REPLACEMENT_STATE_NONE = 0; + /** + * The decoder needs to be replaced, but we haven't yet signaled an end of stream to the existing + * decoder. We need to do so in order to ensure that it outputs any remaining buffers before we + * release it. + */ + private static final int REPLACEMENT_STATE_SIGNAL_END_OF_STREAM = 1; + /** + * The decoder needs to be replaced, and we've signaled an end of stream to the existing decoder. + * We're waiting for the decoder to output an end of stream signal to indicate that it has output + * any remaining buffers before we release it. + */ + private static final int REPLACEMENT_STATE_WAIT_END_OF_STREAM = 2; + private static final int MSG_UPDATE_OUTPUT = 0; private final Handler outputHandler; @@ -61,6 +85,8 @@ public final class TextRenderer extends BaseRenderer implements Callback { private boolean inputStreamEnded; private boolean outputStreamEnded; + @ReplacementState private int decoderReplacementState; + private Format streamFormat; private SubtitleDecoder decoder; private SubtitleInputBuffer nextInputBuffer; private SubtitleOutputBuffer subtitle; @@ -105,20 +131,25 @@ public final class TextRenderer extends BaseRenderer implements Callback { @Override protected void onStreamChanged(Format[] formats) throws ExoPlaybackException { + streamFormat = formats[0]; if (decoder != null) { - decoder.release(); - nextInputBuffer = null; + decoderReplacementState = REPLACEMENT_STATE_SIGNAL_END_OF_STREAM; + } else { + decoder = decoderFactory.createDecoder(streamFormat); } - decoder = decoderFactory.createDecoder(formats[0]); } @Override protected void onPositionReset(long positionUs, boolean joining) { clearOutput(); - resetBuffers(); - decoder.flush(); inputStreamEnded = false; outputStreamEnded = false; + if (decoderReplacementState != REPLACEMENT_STATE_NONE) { + replaceDecoder(); + } else { + releaseBuffers(); + decoder.flush(); + } } @Override @@ -155,13 +186,12 @@ public final class TextRenderer extends BaseRenderer implements Callback { if (nextSubtitle != null) { if (nextSubtitle.isEndOfStream()) { if (!textRendererNeedsUpdate && getNextEventTime() == Long.MAX_VALUE) { - if (subtitle != null) { - subtitle.release(); - subtitle = null; + if (decoderReplacementState == REPLACEMENT_STATE_WAIT_END_OF_STREAM) { + replaceDecoder(); + } else { + releaseBuffers(); + outputStreamEnded = true; } - nextSubtitle.release(); - nextSubtitle = null; - outputStreamEnded = true; } } else if (nextSubtitle.timeUs <= positionUs) { // Advance to the next subtitle. Sync the next event index and trigger an update. @@ -180,6 +210,10 @@ public final class TextRenderer extends BaseRenderer implements Callback { updateOutput(subtitle.getCues(positionUs)); } + if (decoderReplacementState == REPLACEMENT_STATE_WAIT_END_OF_STREAM) { + return; + } + try { while (!inputStreamEnded) { if (nextInputBuffer == null) { @@ -188,6 +222,13 @@ public final class TextRenderer extends BaseRenderer implements Callback { return; } } + if (decoderReplacementState == REPLACEMENT_STATE_SIGNAL_END_OF_STREAM) { + nextInputBuffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM); + decoder.queueInputBuffer(nextInputBuffer); + nextInputBuffer = null; + decoderReplacementState = REPLACEMENT_STATE_WAIT_END_OF_STREAM; + return; + } // Try and read the next subtitle from the source. int result = readSource(formatHolder, nextInputBuffer, false); if (result == C.RESULT_BUFFER_READ) { @@ -200,7 +241,7 @@ public final class TextRenderer extends BaseRenderer implements Callback { decoder.queueInputBuffer(nextInputBuffer); nextInputBuffer = null; } else if (result == C.RESULT_NOTHING_READ) { - break; + return; } } } catch (SubtitleDecoderException e) { @@ -210,10 +251,9 @@ public final class TextRenderer extends BaseRenderer implements Callback { @Override protected void onDisabled() { + streamFormat = null; clearOutput(); - resetBuffers(); - decoder.release(); - decoder = null; + releaseDecoder(); super.onDisabled(); } @@ -229,7 +269,7 @@ public final class TextRenderer extends BaseRenderer implements Callback { return true; } - private void resetBuffers() { + private void releaseBuffers() { nextInputBuffer = null; nextSubtitleEventIndex = C.INDEX_UNSET; if (subtitle != null) { @@ -242,6 +282,18 @@ public final class TextRenderer extends BaseRenderer implements Callback { } } + private void releaseDecoder() { + releaseBuffers(); + decoder.release(); + decoder = null; + decoderReplacementState = REPLACEMENT_STATE_NONE; + } + + private void replaceDecoder() { + releaseDecoder(); + decoder = decoderFactory.createDecoder(streamFormat); + } + private long getNextEventTime() { return ((nextSubtitleEventIndex == C.INDEX_UNSET) || (nextSubtitleEventIndex >= subtitle.getEventTimeCount())) ? Long.MAX_VALUE @@ -267,8 +319,9 @@ public final class TextRenderer extends BaseRenderer implements Callback { case MSG_UPDATE_OUTPUT: invokeUpdateOutputInternal((List) msg.obj); return true; + default: + throw new IllegalStateException(); } - return false; } private void invokeUpdateOutputInternal(List cues) {