Correctly handle stream replacement in text renderer

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=150436867
This commit is contained in:
olly 2017-03-17 07:20:17 -07:00 committed by Oliver Woodman
parent c8059e5969
commit f2de393d83

View File

@ -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<Cue>) msg.obj);
return true;
default:
throw new IllegalStateException();
}
return false;
}
private void invokeUpdateOutputInternal(List<Cue> cues) {