diff --git a/RELEASENOTES.md b/RELEASENOTES.md index e11d2b0619..320a4e7494 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -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. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleDecoder.java index 6dbf9f5173..94d4fd11f0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleDecoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleDecoder.java @@ -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; } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java index c9b04d9758..fc34a1422b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java @@ -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. + * + *

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(); + } + } }