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 [background](https://www.w3.org/TR/webvtt1/#default-text-background) colors
([PR #4178](https://github.com/google/ExoPlayer/pull/4178), ([PR #4178](https://github.com/google/ExoPlayer/pull/4178),
[issue #6581](https://github.com/google/ExoPlayer/issues/6581)). [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: * DRM:
* Add support for attaching DRM sessions to clear content in the demo app. * Add support for attaching DRM sessions to clear content in the demo app.
* Remove `DrmSessionManager` references from all renderers. * Remove `DrmSessionManager` references from all renderers.

View File

@ -150,6 +150,7 @@ public abstract class SimpleDecoder<
while (!queuedOutputBuffers.isEmpty()) { while (!queuedOutputBuffers.isEmpty()) {
queuedOutputBuffers.removeFirst().release(); queuedOutputBuffers.removeFirst().release();
} }
exception = null;
} }
} }
@ -226,6 +227,7 @@ public abstract class SimpleDecoder<
if (inputBuffer.isDecodeOnly()) { if (inputBuffer.isDecodeOnly()) {
outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY); outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
} }
@Nullable E exception;
try { try {
exception = decode(inputBuffer, outputBuffer, resetDecoder); exception = decode(inputBuffer, outputBuffer, resetDecoder);
} catch (RuntimeException e) { } catch (RuntimeException e) {
@ -239,8 +241,9 @@ public abstract class SimpleDecoder<
exception = createUnexpectedDecodeException(e); exception = createUnexpectedDecodeException(e);
} }
if (exception != null) { 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; return false;
} }
} }

View File

@ -23,11 +23,11 @@ import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
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.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.util.Assertions; 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.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
@ -45,6 +45,8 @@ import java.util.List;
*/ */
public final class TextRenderer extends BaseRenderer implements Callback { public final class TextRenderer extends BaseRenderer implements Callback {
private static final String TAG = "TextRenderer";
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({ @IntDef({
@ -143,19 +145,13 @@ public final class TextRenderer extends BaseRenderer implements Callback {
@Override @Override
protected void onPositionReset(long positionUs, boolean joining) { protected void onPositionReset(long positionUs, boolean joining) {
clearOutput();
inputStreamEnded = false; inputStreamEnded = false;
outputStreamEnded = false; outputStreamEnded = false;
if (decoderReplacementState != REPLACEMENT_STATE_NONE) { resetOutputAndDecoder();
replaceDecoder();
} else {
releaseBuffers();
decoder.flush();
}
} }
@Override @Override
public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { public void render(long positionUs, long elapsedRealtimeUs) {
if (outputStreamEnded) { if (outputStreamEnded) {
return; return;
} }
@ -165,7 +161,8 @@ public final class TextRenderer extends BaseRenderer implements Callback {
try { try {
nextSubtitle = decoder.dequeueOutputBuffer(); nextSubtitle = decoder.dequeueOutputBuffer();
} catch (SubtitleDecoderException e) { } 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) { } 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); 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();
}
}
} }