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 (cherry picked from commit 6e510c26df0d354312abe480b238afa47abedd3d)
This commit is contained in:
parent
2939bfccbe
commit
ef8f72d684
@ -750,7 +750,26 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
: readingPeriod.info.id);
|
||||
}
|
||||
}
|
||||
if (e.isRecoverable
|
||||
if (e.type == ExoPlaybackException.TYPE_RENDERER
|
||||
&& e.mediaPeriodId != null
|
||||
&& isRendererPrewarmingMediaPeriod(e.rendererIndex, e.mediaPeriodId)) {
|
||||
// TODO(b/380273486): Investigate recovery for pre-warming renderer errors
|
||||
isPrewarmingDisabledUntilNextTransition = true;
|
||||
disableAndResetPrewarmingRenderers();
|
||||
// Remove periods from the queue starting at the pre-warming period.
|
||||
MediaPeriodHolder prewarmingPeriod = queue.getPrewarmingPeriod();
|
||||
MediaPeriodHolder periodToRemoveAfter = queue.getPlayingPeriod();
|
||||
if (queue.getPlayingPeriod() != prewarmingPeriod) {
|
||||
while (periodToRemoveAfter != null && periodToRemoveAfter.getNext() != prewarmingPeriod) {
|
||||
periodToRemoveAfter = periodToRemoveAfter.getNext();
|
||||
}
|
||||
}
|
||||
queue.removeAfter(periodToRemoveAfter);
|
||||
if (playbackInfo.playbackState != Player.STATE_ENDED) {
|
||||
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)) {
|
||||
@ -768,25 +787,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
// 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
|
||||
&& renderers[e.rendererIndex % renderers.length].isRendererPrewarming(
|
||||
/* id= */ e.rendererIndex)) {
|
||||
// TODO(b/380273486): Investigate recovery for pre-warming renderer errors
|
||||
isPrewarmingDisabledUntilNextTransition = true;
|
||||
disableAndResetPrewarmingRenderers();
|
||||
// Remove periods from the queue starting at the pre-warming period.
|
||||
MediaPeriodHolder prewarmingPeriod = queue.getPrewarmingPeriod();
|
||||
MediaPeriodHolder periodToRemoveAfter = queue.getPlayingPeriod();
|
||||
if (queue.getPlayingPeriod() != prewarmingPeriod) {
|
||||
while (periodToRemoveAfter != null && periodToRemoveAfter.getNext() != prewarmingPeriod) {
|
||||
periodToRemoveAfter = periodToRemoveAfter.getNext();
|
||||
}
|
||||
}
|
||||
queue.removeAfter(periodToRemoveAfter);
|
||||
if (playbackInfo.playbackState != Player.STATE_ENDED) {
|
||||
maybeContinueLoading();
|
||||
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
||||
}
|
||||
} 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