mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Mask ExoPlayer.isLoading when it transitions to IDLE or ENDED states
In some cases, the ExoPlayer immediately transitions to `STATE_IDLE` or `STATE_ENDED` on application thread, while `isLoading` can still remain as `true` before it is finally updated from playback thread. Issue: androidx/media#2133 #cherrypick PiperOrigin-RevId: 728724157
This commit is contained in:
parent
ae3eed2343
commit
daf8f9ff58
@ -7,6 +7,9 @@
|
|||||||
it easier to handle updates in other classes
|
it easier to handle updates in other classes
|
||||||
([#2128](https://github.com/androidx/media/issues/2128)).
|
([#2128](https://github.com/androidx/media/issues/2128)).
|
||||||
* ExoPlayer:
|
* ExoPlayer:
|
||||||
|
* Fix a bug where `ExoPlayer.isLoading()` remains `true` while it has
|
||||||
|
transitioned to `STATE_IDLE` or `STATE_ENDED`
|
||||||
|
([#2133](https://github.com/androidx/media/issues/2133)).
|
||||||
* Transformer:
|
* Transformer:
|
||||||
* Track Selection:
|
* Track Selection:
|
||||||
* Extractors:
|
* Extractors:
|
||||||
|
@ -531,8 +531,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||||||
}
|
}
|
||||||
PlaybackInfo playbackInfo = this.playbackInfo.copyWithPlaybackError(null);
|
PlaybackInfo playbackInfo = this.playbackInfo.copyWithPlaybackError(null);
|
||||||
playbackInfo =
|
playbackInfo =
|
||||||
playbackInfo.copyWithPlaybackState(
|
maskPlaybackState(
|
||||||
playbackInfo.timeline.isEmpty() ? STATE_ENDED : STATE_BUFFERING);
|
playbackInfo, playbackInfo.timeline.isEmpty() ? STATE_ENDED : STATE_BUFFERING);
|
||||||
// Trigger internal prepare first before updating the playback info and notifying external
|
// Trigger internal prepare first before updating the playback info and notifying external
|
||||||
// listeners to ensure that new operations issued in the listener notifications reach the
|
// listeners to ensure that new operations issued in the listener notifications reach the
|
||||||
// player after this prepare. The internal player can't change the playback info immediately
|
// player after this prepare. The internal player can't change the playback info immediately
|
||||||
@ -905,7 +905,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||||||
PlaybackInfo newPlaybackInfo = playbackInfo;
|
PlaybackInfo newPlaybackInfo = playbackInfo;
|
||||||
if (playbackInfo.playbackState == Player.STATE_READY
|
if (playbackInfo.playbackState == Player.STATE_READY
|
||||||
|| (playbackInfo.playbackState == Player.STATE_ENDED && !timeline.isEmpty())) {
|
|| (playbackInfo.playbackState == Player.STATE_ENDED && !timeline.isEmpty())) {
|
||||||
newPlaybackInfo = playbackInfo.copyWithPlaybackState(Player.STATE_BUFFERING);
|
newPlaybackInfo = maskPlaybackState(playbackInfo, Player.STATE_BUFFERING);
|
||||||
}
|
}
|
||||||
int oldMaskingMediaItemIndex = getCurrentMediaItemIndex();
|
int oldMaskingMediaItemIndex = getCurrentMediaItemIndex();
|
||||||
newPlaybackInfo =
|
newPlaybackInfo =
|
||||||
@ -1051,7 +1051,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||||||
if (playbackInfo.sleepingForOffload) {
|
if (playbackInfo.sleepingForOffload) {
|
||||||
playbackInfo = playbackInfo.copyWithEstimatedPosition();
|
playbackInfo = playbackInfo.copyWithEstimatedPosition();
|
||||||
}
|
}
|
||||||
playbackInfo = playbackInfo.copyWithPlaybackState(Player.STATE_IDLE);
|
playbackInfo = maskPlaybackState(playbackInfo, Player.STATE_IDLE);
|
||||||
playbackInfo = playbackInfo.copyWithLoadingMediaPeriodId(playbackInfo.periodId);
|
playbackInfo = playbackInfo.copyWithLoadingMediaPeriodId(playbackInfo.periodId);
|
||||||
playbackInfo.bufferedPositionUs = playbackInfo.positionUs;
|
playbackInfo.bufferedPositionUs = playbackInfo.positionUs;
|
||||||
playbackInfo.totalBufferedDurationUs = 0;
|
playbackInfo.totalBufferedDurationUs = 0;
|
||||||
@ -1882,7 +1882,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||||||
this.playbackInfo.copyWithLoadingMediaPeriodId(this.playbackInfo.periodId);
|
this.playbackInfo.copyWithLoadingMediaPeriodId(this.playbackInfo.periodId);
|
||||||
playbackInfo.bufferedPositionUs = playbackInfo.positionUs;
|
playbackInfo.bufferedPositionUs = playbackInfo.positionUs;
|
||||||
playbackInfo.totalBufferedDurationUs = 0;
|
playbackInfo.totalBufferedDurationUs = 0;
|
||||||
playbackInfo = playbackInfo.copyWithPlaybackState(Player.STATE_IDLE);
|
playbackInfo = maskPlaybackState(playbackInfo, Player.STATE_IDLE);
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
playbackInfo = playbackInfo.copyWithPlaybackError(error);
|
playbackInfo = playbackInfo.copyWithPlaybackError(error);
|
||||||
}
|
}
|
||||||
@ -2360,7 +2360,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||||||
maskingPlaybackState = STATE_BUFFERING;
|
maskingPlaybackState = STATE_BUFFERING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
newPlaybackInfo = newPlaybackInfo.copyWithPlaybackState(maskingPlaybackState);
|
newPlaybackInfo = maskPlaybackState(newPlaybackInfo, maskingPlaybackState);
|
||||||
internalPlayer.setMediaSources(
|
internalPlayer.setMediaSources(
|
||||||
holders, startWindowIndex, Util.msToUs(startPositionMs), shuffleOrder);
|
holders, startWindowIndex, Util.msToUs(startPositionMs), shuffleOrder);
|
||||||
boolean positionDiscontinuity =
|
boolean positionDiscontinuity =
|
||||||
@ -2434,7 +2434,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||||||
&& toIndex == currentMediaSourceCount
|
&& toIndex == currentMediaSourceCount
|
||||||
&& currentIndex >= newPlaybackInfo.timeline.getWindowCount();
|
&& currentIndex >= newPlaybackInfo.timeline.getWindowCount();
|
||||||
if (transitionsToEnded) {
|
if (transitionsToEnded) {
|
||||||
newPlaybackInfo = newPlaybackInfo.copyWithPlaybackState(STATE_ENDED);
|
newPlaybackInfo = maskPlaybackState(newPlaybackInfo, STATE_ENDED);
|
||||||
}
|
}
|
||||||
internalPlayer.removeMediaSources(fromIndex, toIndex, shuffleOrder);
|
internalPlayer.removeMediaSources(fromIndex, toIndex, shuffleOrder);
|
||||||
return newPlaybackInfo;
|
return newPlaybackInfo;
|
||||||
@ -2558,6 +2558,14 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||||||
return playbackInfo;
|
return playbackInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static PlaybackInfo maskPlaybackState(PlaybackInfo playbackInfo, int playbackState) {
|
||||||
|
playbackInfo = playbackInfo.copyWithPlaybackState(playbackState);
|
||||||
|
if (playbackState == STATE_IDLE || playbackState == STATE_ENDED) {
|
||||||
|
playbackInfo = playbackInfo.copyWithIsLoading(false);
|
||||||
|
}
|
||||||
|
return playbackInfo;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Pair<Object, Long> getPeriodPositionUsAfterTimelineChanged(
|
private Pair<Object, Long> getPeriodPositionUsAfterTimelineChanged(
|
||||||
Timeline oldTimeline,
|
Timeline oldTimeline,
|
||||||
|
@ -1891,6 +1891,20 @@ public class ExoPlayerTest {
|
|||||||
assertThat(totalBufferedDuration[2]).isEqualTo(0);
|
assertThat(totalBufferedDuration[2]).isEqualTo(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void stopWhileLoading_correctMaskingIsLoading() throws Exception {
|
||||||
|
ExoPlayer player = new TestExoPlayerBuilder(context).build();
|
||||||
|
player.setMediaSource(new FakeMediaSource(new FakeTimeline(/* windowCount= */ 1)));
|
||||||
|
player.prepare();
|
||||||
|
advance(player).untilLoadingIs(true);
|
||||||
|
|
||||||
|
assertThat(player.isLoading()).isTrue();
|
||||||
|
player.stop();
|
||||||
|
assertThat(player.isLoading()).isFalse();
|
||||||
|
|
||||||
|
player.release();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void stop_releasesMediaSource() throws Exception {
|
public void stop_releasesMediaSource() throws Exception {
|
||||||
Timeline timeline = new FakeTimeline();
|
Timeline timeline = new FakeTimeline();
|
||||||
@ -2089,6 +2103,18 @@ public class ExoPlayerTest {
|
|||||||
assertThat(totalBufferedDuration[2]).isEqualTo(0);
|
assertThat(totalBufferedDuration[2]).isEqualTo(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void releaseWhileLoading_correctMaskingIsLoading() throws Exception {
|
||||||
|
ExoPlayer player = new TestExoPlayerBuilder(context).build();
|
||||||
|
player.setMediaSource(new FakeMediaSource(new FakeTimeline(/* windowCount= */ 1)));
|
||||||
|
player.prepare();
|
||||||
|
advance(player).untilLoadingIs(true);
|
||||||
|
|
||||||
|
assertThat(player.isLoading()).isTrue();
|
||||||
|
player.release();
|
||||||
|
assertThat(player.isLoading()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void settingNewStartPositionPossibleAfterStopAndClearMediaItems() throws Exception {
|
public void settingNewStartPositionPossibleAfterStopAndClearMediaItems() throws Exception {
|
||||||
Timeline timeline = new FakeTimeline();
|
Timeline timeline = new FakeTimeline();
|
||||||
@ -8810,6 +8836,29 @@ public class ExoPlayerTest {
|
|||||||
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
|
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setMediaSourcesWhileLoading_noSeekEmpty_correctMaskingIsLoading() throws Exception {
|
||||||
|
ExoPlayer player = new TestExoPlayerBuilder(context).build();
|
||||||
|
player.setMediaSource(new FakeMediaSource(new FakeTimeline(/* windowCount= */ 1)));
|
||||||
|
player.prepare();
|
||||||
|
advance(player).untilLoadingIs(true);
|
||||||
|
|
||||||
|
assertThat(player.isLoading()).isTrue();
|
||||||
|
// Set a media item with empty timeline.
|
||||||
|
FakeMediaSource fakeMediaSource =
|
||||||
|
new FakeMediaSource(Timeline.EMPTY) {
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public Timeline getInitialTimeline() {
|
||||||
|
return getTimeline();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
player.setMediaSource(fakeMediaSource, /* resetPosition= */ false);
|
||||||
|
assertThat(player.isLoading()).isFalse();
|
||||||
|
|
||||||
|
player.release();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addMediaSources_whenEmptyInitialSeek_correctPeriodMasking() throws Exception {
|
public void addMediaSources_whenEmptyInitialSeek_correctPeriodMasking() throws Exception {
|
||||||
final long[] positions = new long[2];
|
final long[] positions = new long[2];
|
||||||
@ -9269,6 +9318,27 @@ public class ExoPlayerTest {
|
|||||||
assertThat(bufferedPositions[2]).isEqualTo(0);
|
assertThat(bufferedPositions[2]).isEqualTo(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void removeMediaItemsWhileLoading_currentItemRemovedThatIsTheLast_correctMaskingIsLoading()
|
||||||
|
throws Exception {
|
||||||
|
ExoPlayer player = new TestExoPlayerBuilder(context).build();
|
||||||
|
MediaSource firstMediaSource = new FakeMediaSource(new FakeTimeline(/* windowCount= */ 1));
|
||||||
|
MediaSource secondMediaSource = new FakeMediaSource(new FakeTimeline(/* windowCount= */ 1));
|
||||||
|
MediaSource thirdMediaSource = new FakeMediaSource(new FakeTimeline(/* windowCount= */ 1));
|
||||||
|
player.setMediaSources(
|
||||||
|
ImmutableList.of(firstMediaSource, secondMediaSource, thirdMediaSource),
|
||||||
|
/* startMediaItemIndex= */ 2,
|
||||||
|
/* startPositionMs= */ C.TIME_UNSET);
|
||||||
|
player.prepare();
|
||||||
|
advance(player).untilLoadingIs(true);
|
||||||
|
|
||||||
|
assertThat(player.isLoading()).isTrue();
|
||||||
|
player.removeMediaItem(/* index= */ 2);
|
||||||
|
assertThat(player.isLoading()).isFalse();
|
||||||
|
|
||||||
|
player.release();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void removeMediaItems_removeTailWithCurrentWindow_whenIdle_finishesPlayback()
|
public void removeMediaItems_removeTailWithCurrentWindow_whenIdle_finishesPlayback()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user