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.Handler.Callback;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.support.annotation.IntDef;
|
||||||
import com.google.android.exoplayer2.BaseRenderer;
|
import com.google.android.exoplayer2.BaseRenderer;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
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.FormatHolder;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
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 static final int MSG_UPDATE_OUTPUT = 0;
|
||||||
|
|
||||||
private final Handler outputHandler;
|
private final Handler outputHandler;
|
||||||
@ -61,6 +85,8 @@ public final class TextRenderer extends BaseRenderer implements Callback {
|
|||||||
|
|
||||||
private boolean inputStreamEnded;
|
private boolean inputStreamEnded;
|
||||||
private boolean outputStreamEnded;
|
private boolean outputStreamEnded;
|
||||||
|
@ReplacementState private int decoderReplacementState;
|
||||||
|
private Format streamFormat;
|
||||||
private SubtitleDecoder decoder;
|
private SubtitleDecoder decoder;
|
||||||
private SubtitleInputBuffer nextInputBuffer;
|
private SubtitleInputBuffer nextInputBuffer;
|
||||||
private SubtitleOutputBuffer subtitle;
|
private SubtitleOutputBuffer subtitle;
|
||||||
@ -105,20 +131,25 @@ public final class TextRenderer extends BaseRenderer implements Callback {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStreamChanged(Format[] formats) throws ExoPlaybackException {
|
protected void onStreamChanged(Format[] formats) throws ExoPlaybackException {
|
||||||
|
streamFormat = formats[0];
|
||||||
if (decoder != null) {
|
if (decoder != null) {
|
||||||
decoder.release();
|
decoderReplacementState = REPLACEMENT_STATE_SIGNAL_END_OF_STREAM;
|
||||||
nextInputBuffer = null;
|
} else {
|
||||||
|
decoder = decoderFactory.createDecoder(streamFormat);
|
||||||
}
|
}
|
||||||
decoder = decoderFactory.createDecoder(formats[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPositionReset(long positionUs, boolean joining) {
|
protected void onPositionReset(long positionUs, boolean joining) {
|
||||||
clearOutput();
|
clearOutput();
|
||||||
resetBuffers();
|
|
||||||
decoder.flush();
|
|
||||||
inputStreamEnded = false;
|
inputStreamEnded = false;
|
||||||
outputStreamEnded = false;
|
outputStreamEnded = false;
|
||||||
|
if (decoderReplacementState != REPLACEMENT_STATE_NONE) {
|
||||||
|
replaceDecoder();
|
||||||
|
} else {
|
||||||
|
releaseBuffers();
|
||||||
|
decoder.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -155,13 +186,12 @@ public final class TextRenderer extends BaseRenderer implements Callback {
|
|||||||
if (nextSubtitle != null) {
|
if (nextSubtitle != null) {
|
||||||
if (nextSubtitle.isEndOfStream()) {
|
if (nextSubtitle.isEndOfStream()) {
|
||||||
if (!textRendererNeedsUpdate && getNextEventTime() == Long.MAX_VALUE) {
|
if (!textRendererNeedsUpdate && getNextEventTime() == Long.MAX_VALUE) {
|
||||||
if (subtitle != null) {
|
if (decoderReplacementState == REPLACEMENT_STATE_WAIT_END_OF_STREAM) {
|
||||||
subtitle.release();
|
replaceDecoder();
|
||||||
subtitle = null;
|
} else {
|
||||||
|
releaseBuffers();
|
||||||
|
outputStreamEnded = true;
|
||||||
}
|
}
|
||||||
nextSubtitle.release();
|
|
||||||
nextSubtitle = null;
|
|
||||||
outputStreamEnded = true;
|
|
||||||
}
|
}
|
||||||
} else if (nextSubtitle.timeUs <= positionUs) {
|
} else if (nextSubtitle.timeUs <= positionUs) {
|
||||||
// Advance to the next subtitle. Sync the next event index and trigger an update.
|
// 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));
|
updateOutput(subtitle.getCues(positionUs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (decoderReplacementState == REPLACEMENT_STATE_WAIT_END_OF_STREAM) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (!inputStreamEnded) {
|
while (!inputStreamEnded) {
|
||||||
if (nextInputBuffer == null) {
|
if (nextInputBuffer == null) {
|
||||||
@ -188,6 +222,13 @@ public final class TextRenderer extends BaseRenderer implements Callback {
|
|||||||
return;
|
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.
|
// Try and read the next subtitle from the source.
|
||||||
int result = readSource(formatHolder, nextInputBuffer, false);
|
int result = readSource(formatHolder, nextInputBuffer, false);
|
||||||
if (result == C.RESULT_BUFFER_READ) {
|
if (result == C.RESULT_BUFFER_READ) {
|
||||||
@ -200,7 +241,7 @@ public final class TextRenderer extends BaseRenderer implements Callback {
|
|||||||
decoder.queueInputBuffer(nextInputBuffer);
|
decoder.queueInputBuffer(nextInputBuffer);
|
||||||
nextInputBuffer = null;
|
nextInputBuffer = null;
|
||||||
} else if (result == C.RESULT_NOTHING_READ) {
|
} else if (result == C.RESULT_NOTHING_READ) {
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (SubtitleDecoderException e) {
|
} catch (SubtitleDecoderException e) {
|
||||||
@ -210,10 +251,9 @@ public final class TextRenderer extends BaseRenderer implements Callback {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDisabled() {
|
protected void onDisabled() {
|
||||||
|
streamFormat = null;
|
||||||
clearOutput();
|
clearOutput();
|
||||||
resetBuffers();
|
releaseDecoder();
|
||||||
decoder.release();
|
|
||||||
decoder = null;
|
|
||||||
super.onDisabled();
|
super.onDisabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +269,7 @@ public final class TextRenderer extends BaseRenderer implements Callback {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetBuffers() {
|
private void releaseBuffers() {
|
||||||
nextInputBuffer = null;
|
nextInputBuffer = null;
|
||||||
nextSubtitleEventIndex = C.INDEX_UNSET;
|
nextSubtitleEventIndex = C.INDEX_UNSET;
|
||||||
if (subtitle != null) {
|
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() {
|
private long getNextEventTime() {
|
||||||
return ((nextSubtitleEventIndex == C.INDEX_UNSET)
|
return ((nextSubtitleEventIndex == C.INDEX_UNSET)
|
||||||
|| (nextSubtitleEventIndex >= subtitle.getEventTimeCount())) ? Long.MAX_VALUE
|
|| (nextSubtitleEventIndex >= subtitle.getEventTimeCount())) ? Long.MAX_VALUE
|
||||||
@ -267,8 +319,9 @@ public final class TextRenderer extends BaseRenderer implements Callback {
|
|||||||
case MSG_UPDATE_OUTPUT:
|
case MSG_UPDATE_OUTPUT:
|
||||||
invokeUpdateOutputInternal((List<Cue>) msg.obj);
|
invokeUpdateOutputInternal((List<Cue>) msg.obj);
|
||||||
return true;
|
return true;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void invokeUpdateOutputInternal(List<Cue> cues) {
|
private void invokeUpdateOutputInternal(List<Cue> cues) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user