mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +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);
|
e = e.copyWithMediaPeriodId(readingPeriod.info.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (e.isRecoverable
|
if (e.type == ExoPlaybackException.TYPE_RENDERER
|
||||||
&& (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
|
|
||||||
&& e.mediaPeriodId != null
|
&& e.mediaPeriodId != null
|
||||||
&& isRendererPrewarmingMediaPeriod(e.rendererIndex, e.mediaPeriodId)) {
|
&& isRendererPrewarmingMediaPeriod(e.rendererIndex, e.mediaPeriodId)) {
|
||||||
// TODO(b/380273486): Investigate recovery for pre-warming renderer errors
|
// TODO(b/380273486): Investigate recovery for pre-warming renderer errors
|
||||||
@ -778,6 +760,24 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
maybeContinueLoading();
|
maybeContinueLoading();
|
||||||
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
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 {
|
} else {
|
||||||
if (pendingRecoverableRendererError != null) {
|
if (pendingRecoverableRendererError != null) {
|
||||||
pendingRecoverableRendererError.addSuppressed(e);
|
pendingRecoverableRendererError.addSuppressed(e);
|
||||||
|
@ -1524,6 +1524,91 @@ public class ExoPlayerWithPrewarmingRenderersTest {
|
|||||||
assertThat(secondaryVideoState2).isEqualTo(Renderer.STATE_ENABLED);
|
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. */
|
/** {@link FakeMediaSource} that prevents any reading of samples off the sample queue. */
|
||||||
private static final class FakeBlockingMediaSource extends FakeMediaSource {
|
private static final class FakeBlockingMediaSource extends FakeMediaSource {
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user