Catch-and-log all subtitle decode errors

issue:#6885
PiperOrigin-RevId: 295931197
This commit is contained in:
ibaker 2020-02-19 11:09:53 +00:00 committed by Oliver Woodman
parent 0b7f5260ca
commit 4107375c9d
3 changed files with 37 additions and 13 deletions

View File

@ -46,6 +46,9 @@
[background](https://www.w3.org/TR/webvtt1/#default-text-background) colors
([PR #4178](https://github.com/google/ExoPlayer/pull/4178),
[issue #6581](https://github.com/google/ExoPlayer/issues/6581)).
* Catch-and-log all fatal exceptions in `TextRenderer` instead of re-throwing,
allowing playback to continue even if subtitles fail
([#6885](https://github.com/google/ExoPlayer/issues/6885)).
* DRM:
* Add support for attaching DRM sessions to clear content in the demo app.
* Remove `DrmSessionManager` references from all renderers.

View File

@ -150,6 +150,7 @@ public abstract class SimpleDecoder<
while (!queuedOutputBuffers.isEmpty()) {
queuedOutputBuffers.removeFirst().release();
}
exception = null;
}
}
@ -226,6 +227,7 @@ public abstract class SimpleDecoder<
if (inputBuffer.isDecodeOnly()) {
outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
}
@Nullable E exception;
try {
exception = decode(inputBuffer, outputBuffer, resetDecoder);
} catch (RuntimeException e) {
@ -239,8 +241,9 @@ public abstract class SimpleDecoder<
exception = createUnexpectedDecodeException(e);
}
if (exception != null) {
// Memory barrier to ensure that the decoder exception is visible from the playback thread.
synchronized (lock) {}
synchronized (lock) {
this.exception = exception;
}
return false;
}
}

View File

@ -23,11 +23,11 @@ import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.BaseRenderer;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
import java.lang.annotation.Documented;
@ -45,6 +45,8 @@ import java.util.List;
*/
public final class TextRenderer extends BaseRenderer implements Callback {
private static final String TAG = "TextRenderer";
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
@ -143,19 +145,13 @@ public final class TextRenderer extends BaseRenderer implements Callback {
@Override
protected void onPositionReset(long positionUs, boolean joining) {
clearOutput();
inputStreamEnded = false;
outputStreamEnded = false;
if (decoderReplacementState != REPLACEMENT_STATE_NONE) {
replaceDecoder();
} else {
releaseBuffers();
decoder.flush();
}
resetOutputAndDecoder();
}
@Override
public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
public void render(long positionUs, long elapsedRealtimeUs) {
if (outputStreamEnded) {
return;
}
@ -165,7 +161,8 @@ public final class TextRenderer extends BaseRenderer implements Callback {
try {
nextSubtitle = decoder.dequeueOutputBuffer();
} catch (SubtitleDecoderException e) {
throw createRendererException(e, streamFormat);
handleDecoderError(e);
return;
}
}
@ -247,7 +244,8 @@ public final class TextRenderer extends BaseRenderer implements Callback {
}
}
} catch (SubtitleDecoderException e) {
throw createRendererException(e, streamFormat);
handleDecoderError(e);
return;
}
}
@ -329,4 +327,24 @@ public final class TextRenderer extends BaseRenderer implements Callback {
output.onCues(cues);
}
/**
* Called when {@link #decoder} throws an exception, so it can be logged and playback can
* continue.
*
* <p>Logs {@code e} and resets state to allow decoding the next sample.
*/
private void handleDecoderError(SubtitleDecoderException e) {
Log.e(TAG, "Subtitle decoding failed. streamFormat=" + streamFormat, e);
resetOutputAndDecoder();
}
private void resetOutputAndDecoder() {
clearOutput();
if (decoderReplacementState != REPLACEMENT_STATE_NONE) {
replaceDecoder();
} else {
releaseBuffers();
decoder.flush();
}
}
}