diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java index 198b674dce..75bac45b1a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java @@ -84,6 +84,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { private boolean streamIsFinal; private boolean throwRendererExceptionIsExecuting; private Timeline timeline; + @Nullable private MediaSource.MediaPeriodId mediaPeriodId; @GuardedBy("lock") @Nullable @@ -144,6 +145,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { throws ExoPlaybackException { Assertions.checkState(state == STATE_DISABLED); this.configuration = configuration; + this.mediaPeriodId = mediaPeriodId; state = STATE_ENABLED; onEnabled(joining, mayRenderStartOfStream); replaceStream(formats, stream, startPositionUs, offsetUs, mediaPeriodId); @@ -167,6 +169,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { throws ExoPlaybackException { Assertions.checkState(!streamIsFinal); this.stream = stream; + this.mediaPeriodId = mediaPeriodId; if (readingPositionUs == C.TIME_END_OF_SOURCE) { readingPositionUs = startPositionUs; } @@ -242,6 +245,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { streamFormats = null; streamIsFinal = false; onDisabled(); + mediaPeriodId = null; } @Override @@ -485,6 +489,15 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { return timeline; } + /** + * The {@link MediaSource.MediaPeriodId} of the {@link MediaPeriod} producing the {@code stream}, + * or {@code null} if the renderer is disabled. + */ + @Nullable + protected final MediaSource.MediaPeriodId getMediaPeriodId() { + return mediaPeriodId; + } + /** * Creates an {@link ExoPlaybackException} of type {@link ExoPlaybackException#TYPE_RENDERER} for * this renderer. @@ -530,7 +543,14 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { } } return ExoPlaybackException.createForRenderer( - cause, getName(), getIndex(), format, formatSupport, isRecoverable, errorCode); + cause, + getName(), + getIndex(), + format, + formatSupport, + mediaPeriodId, + isRecoverable, + errorCode); } /** diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java index e8bea26feb..9818cd5250 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java @@ -131,6 +131,31 @@ public final class ExoPlaybackException extends PlaybackException { return new ExoPlaybackException(TYPE_SOURCE, cause, errorCode); } + /** + * @deprecated Use {@link #createForRenderer(Throwable, String, int, Format, int, MediaPeriodId, + * boolean, int)} instead. + */ + @Deprecated + @UnstableApi + public static ExoPlaybackException createForRenderer( + Throwable cause, + String rendererName, + int rendererIndex, + @Nullable Format rendererFormat, + @FormatSupport int rendererFormatSupport, + boolean isRecoverable, + @ErrorCode int errorCode) { + return createForRenderer( + cause, + rendererName, + rendererIndex, + rendererFormat, + rendererFormatSupport, + /* mediaPeriodId= */ null, + isRecoverable, + errorCode); + } + /** * Creates an instance of type {@link #TYPE_RENDERER}. * @@ -142,6 +167,8 @@ public final class ExoPlaybackException extends PlaybackException { * or null if the renderer wasn't using a {@link Format}. * @param rendererFormatSupport The {@link FormatSupport} of the renderer for {@code * rendererFormat}. Ignored if {@code rendererFormat} is null. + * @param mediaPeriodId The {@link MediaPeriodId mediaPeriodId} of the media associated with this + * error, or null if undetermined. * @param isRecoverable If the failure can be recovered by disabling and re-enabling the renderer. * @param errorCode See {@link #errorCode}. * @return The created instance. @@ -153,9 +180,9 @@ public final class ExoPlaybackException extends PlaybackException { int rendererIndex, @Nullable Format rendererFormat, @FormatSupport int rendererFormatSupport, + @Nullable MediaPeriodId mediaPeriodId, boolean isRecoverable, @ErrorCode int errorCode) { - return new ExoPlaybackException( TYPE_RENDERER, cause, @@ -165,6 +192,7 @@ public final class ExoPlaybackException extends PlaybackException { rendererIndex, rendererFormat, rendererFormat == null ? C.FORMAT_HANDLED : rendererFormatSupport, + mediaPeriodId, isRecoverable); } @@ -208,6 +236,7 @@ public final class ExoPlaybackException extends PlaybackException { /* rendererIndex= */ C.INDEX_UNSET, /* rendererFormat= */ null, /* rendererFormatSupport= */ C.FORMAT_HANDLED, + /* mediaPeriodId= */ null, /* isRecoverable= */ false); } @@ -221,6 +250,7 @@ public final class ExoPlaybackException extends PlaybackException { /* rendererIndex= */ C.INDEX_UNSET, /* rendererFormat= */ null, /* rendererFormatSupport= */ C.FORMAT_HANDLED, + /* mediaPeriodId= */ null, /* isRecoverable= */ false); } @@ -233,6 +263,7 @@ public final class ExoPlaybackException extends PlaybackException { int rendererIndex, @Nullable Format rendererFormat, @FormatSupport int rendererFormatSupport, + @Nullable MediaPeriodId mediaPeriodId, boolean isRecoverable) { this( deriveMessage( @@ -249,7 +280,7 @@ public final class ExoPlaybackException extends PlaybackException { rendererIndex, rendererFormat, rendererFormatSupport, - /* mediaPeriodId= */ null, + mediaPeriodId, /* timestampMs= */ SystemClock.elapsedRealtime(), isRecoverable); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java index dc41a6ac40..af9133ee22 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java @@ -693,7 +693,7 @@ import java.util.concurrent.atomic.AtomicBoolean; } catch (ExoPlaybackException e) { if (e.type == ExoPlaybackException.TYPE_RENDERER) { @Nullable MediaPeriodHolder readingPeriod = queue.getReadingPeriod(); - if (readingPeriod != null) { + if (readingPeriod != null && e.mediaPeriodId == null) { // We can assume that all renderer errors happen in the context of the reading period. See // [internal: b/150584930#comment4] for exceptions that aren't covered by this assumption. e = diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/MediaCodecAudioRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/MediaCodecAudioRendererTest.java index d6d819f67f..c2c9212b88 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/MediaCodecAudioRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/MediaCodecAudioRendererTest.java @@ -312,6 +312,7 @@ public class MediaCodecAudioRendererTest { /* rendererIndex= */ 0, format, C.FORMAT_HANDLED, + /* mediaPeriodId= */ null, /* isRecoverable= */ false, PlaybackException.ERROR_CODE_UNSPECIFIED)); } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeRenderer.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeRenderer.java index 214ade1288..7854202af6 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeRenderer.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeRenderer.java @@ -120,6 +120,7 @@ public class FakeRenderer extends BaseRenderer { getIndex(), format, C.FORMAT_UNSUPPORTED_TYPE, + getMediaPeriodId(), /* isRecoverable= */ false, PlaybackException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED); }