Correctly handle stream replacement in text renderer
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=150436867
This commit is contained in:
parent
c8059e5969
commit
f2de393d83
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user