mirror of
https://github.com/androidx/media.git
synced 2025-04-29 22:36:54 +08:00
Set that any error during pre-warming disables and resets pre-warming
For now, even if a recoverable error occurs during pre-warming, the current process will be that pre-warming is disabled until subsequent media item transition. PiperOrigin-RevId: 740349517
This commit is contained in:
parent
2642d895bd
commit
6e510c26df
@ -741,25 +741,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
e = e.copyWithMediaPeriodId(readingPeriod.info.id);
|
||||
}
|
||||
}
|
||||
if (e.isRecoverable
|
||||
&& (pendingRecoverableRendererError == null
|
||||
|| e.errorCode == PlaybackException.ERROR_CODE_AUDIO_TRACK_OFFLOAD_INIT_FAILED
|
||||
|| e.errorCode == PlaybackException.ERROR_CODE_AUDIO_TRACK_OFFLOAD_WRITE_FAILED)) {
|
||||
// If pendingRecoverableRendererError != null and error was
|
||||
// ERROR_CODE_AUDIO_TRACK_OFFLOAD_WRITE_FAILED then upon retry, renderer will attempt with
|
||||
// offload disabled.
|
||||
Log.w(TAG, "Recoverable renderer error", e);
|
||||
if (pendingRecoverableRendererError != null) {
|
||||
pendingRecoverableRendererError.addSuppressed(e);
|
||||
e = pendingRecoverableRendererError;
|
||||
} else {
|
||||
pendingRecoverableRendererError = e;
|
||||
}
|
||||
// Given that the player is now in an unhandled exception state, the error needs to be
|
||||
// recovered or the player stopped before any other message is handled.
|
||||
handler.sendMessageAtFrontOfQueue(
|
||||
handler.obtainMessage(MSG_ATTEMPT_RENDERER_ERROR_RECOVERY, e));
|
||||
} else if (e.type == ExoPlaybackException.TYPE_RENDERER
|
||||
if (e.type == ExoPlaybackException.TYPE_RENDERER
|
||||
&& e.mediaPeriodId != null
|
||||
&& isRendererPrewarmingMediaPeriod(e.rendererIndex, e.mediaPeriodId)) {
|
||||
// TODO(b/380273486): Investigate recovery for pre-warming renderer errors
|
||||
@ -778,6 +760,24 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
maybeContinueLoading();
|
||||
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
||||
}
|
||||
} else if (e.isRecoverable
|
||||
&& (pendingRecoverableRendererError == null
|
||||
|| e.errorCode == PlaybackException.ERROR_CODE_AUDIO_TRACK_OFFLOAD_INIT_FAILED
|
||||
|| e.errorCode == PlaybackException.ERROR_CODE_AUDIO_TRACK_OFFLOAD_WRITE_FAILED)) {
|
||||
// If pendingRecoverableRendererError != null and error was
|
||||
// ERROR_CODE_AUDIO_TRACK_OFFLOAD_WRITE_FAILED then upon retry, renderer will attempt with
|
||||
// offload disabled.
|
||||
Log.w(TAG, "Recoverable renderer error", e);
|
||||
if (pendingRecoverableRendererError != null) {
|
||||
pendingRecoverableRendererError.addSuppressed(e);
|
||||
e = pendingRecoverableRendererError;
|
||||
} else {
|
||||
pendingRecoverableRendererError = e;
|
||||
}
|
||||
// Given that the player is now in an unhandled exception state, the error needs to be
|
||||
// recovered or the player stopped before any other message is handled.
|
||||
handler.sendMessageAtFrontOfQueue(
|
||||
handler.obtainMessage(MSG_ATTEMPT_RENDERER_ERROR_RECOVERY, e));
|
||||
} else {
|
||||
if (pendingRecoverableRendererError != null) {
|
||||
pendingRecoverableRendererError.addSuppressed(e);
|
||||
|
@ -1524,6 +1524,91 @@ public class ExoPlayerWithPrewarmingRenderersTest {
|
||||
assertThat(secondaryVideoState2).isEqualTo(Renderer.STATE_ENABLED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
play_recoverableErrorWithPrimaryRendererDuringPrewarming_doesNotResetSecondaryRenderer()
|
||||
throws Exception {
|
||||
Clock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
||||
Player.Listener listener = mock(Player.Listener.class);
|
||||
AtomicBoolean shouldPrimaryRendererThrowRecoverable = new AtomicBoolean(false);
|
||||
ExoPlayer player =
|
||||
new TestExoPlayerBuilder(context)
|
||||
.setClock(fakeClock)
|
||||
.setRenderersFactory(
|
||||
new FakeRenderersFactorySupportingSecondaryVideoRenderer(fakeClock) {
|
||||
@Override
|
||||
public Renderer[] createRenderers(
|
||||
Handler eventHandler,
|
||||
VideoRendererEventListener videoRendererEventListener,
|
||||
AudioRendererEventListener audioRendererEventListener,
|
||||
TextOutput textRendererOutput,
|
||||
MetadataOutput metadataRendererOutput) {
|
||||
HandlerWrapper clockAwareHandler =
|
||||
clock.createHandler(eventHandler.getLooper(), /* callback= */ null);
|
||||
return new Renderer[] {
|
||||
new FakeVideoRenderer(clockAwareHandler, videoRendererEventListener) {
|
||||
@Override
|
||||
public void render(long positionUs, long elapsedRealtimeUs)
|
||||
throws ExoPlaybackException {
|
||||
if (!shouldPrimaryRendererThrowRecoverable.get()) {
|
||||
super.render(positionUs, elapsedRealtimeUs);
|
||||
} else {
|
||||
shouldPrimaryRendererThrowRecoverable.set(false);
|
||||
throw createRendererException(
|
||||
new MediaCodecRenderer.DecoderInitializationException(
|
||||
new Format.Builder().build(),
|
||||
new IllegalArgumentException(),
|
||||
false,
|
||||
0),
|
||||
this.getFormatHolder().format,
|
||||
true,
|
||||
PlaybackException.ERROR_CODE_DECODER_INIT_FAILED);
|
||||
}
|
||||
}
|
||||
},
|
||||
new FakeAudioRenderer(clockAwareHandler, audioRendererEventListener)
|
||||
};
|
||||
}
|
||||
})
|
||||
.build();
|
||||
player.addListener(listener);
|
||||
Renderer videoRenderer = player.getRenderer(/* index= */ 0);
|
||||
Renderer secondaryVideoRenderer = player.getSecondaryRenderer(/* index= */ 0);
|
||||
// Set a playlist that allows a new renderer to be enabled early.
|
||||
player.setMediaSources(
|
||||
ImmutableList.of(
|
||||
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT),
|
||||
new FakeBlockingMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT),
|
||||
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT)));
|
||||
player.prepare();
|
||||
|
||||
// Play a bit until the second renderer is pre-warming.
|
||||
player.play();
|
||||
advance(player)
|
||||
.untilBackgroundThreadCondition(
|
||||
() -> secondaryVideoRenderer.getState() == Renderer.STATE_ENABLED);
|
||||
@Renderer.State int videoState1 = videoRenderer.getState();
|
||||
@Renderer.State int secondaryVideoState1 = secondaryVideoRenderer.getState();
|
||||
advance(player)
|
||||
.untilBackgroundThreadCondition(() -> videoRenderer.getState() == Renderer.STATE_ENABLED);
|
||||
@Renderer.State int videoState2 = videoRenderer.getState();
|
||||
@Renderer.State int secondaryVideoState2 = secondaryVideoRenderer.getState();
|
||||
shouldPrimaryRendererThrowRecoverable.set(true);
|
||||
advance(player)
|
||||
.untilBackgroundThreadCondition(() -> videoRenderer.getState() == Renderer.STATE_DISABLED);
|
||||
@Renderer.State int videoState3 = videoRenderer.getState();
|
||||
@Renderer.State int secondaryVideoState3 = secondaryVideoRenderer.getState();
|
||||
player.release();
|
||||
|
||||
verify(listener).onPositionDiscontinuity(any(), any(), anyInt());
|
||||
assertThat(videoState1).isEqualTo(Renderer.STATE_STARTED);
|
||||
assertThat(secondaryVideoState1).isEqualTo(Renderer.STATE_ENABLED);
|
||||
assertThat(videoState2).isEqualTo(Renderer.STATE_ENABLED);
|
||||
assertThat(secondaryVideoState2).isEqualTo(Renderer.STATE_STARTED);
|
||||
assertThat(videoState3).isEqualTo(Renderer.STATE_DISABLED);
|
||||
assertThat(secondaryVideoState3).isEqualTo(Renderer.STATE_STARTED);
|
||||
}
|
||||
|
||||
/** {@link FakeMediaSource} that prevents any reading of samples off the sample queue. */
|
||||
private static final class FakeBlockingMediaSource extends FakeMediaSource {
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user