Commit 3b26c218 by tonihei Committed by Oliver Woodman

Deduplicate clear playlist code for stop(true) calls.

The logic to clear the playlist is currently duplicated in various
reset methods so that calls to player.stop(true) can clear the playlist.

This can be deduplicated by clearing the playlist as a seperate
operation that reuses the existing code.

PiperOrigin-RevId: 321578759
parent 161dea66
......@@ -394,6 +394,7 @@ public class PlayerActivity extends AppCompatActivity
if (!hasAds) {
releaseAdsLoader();
}
mediaItems.add(0, MediaItem.fromUri("https://html5demos.com/assets/dizzy.mp4"));
return mediaItems;
}
......
......@@ -304,13 +304,10 @@ import java.util.concurrent.TimeoutException;
if (playbackInfo.playbackState != Player.STATE_IDLE) {
return;
}
PlaybackInfo playbackInfo =
getResetPlaybackInfo(
/* clearPlaylist= */ false,
/* resetError= */ true,
/* playbackState= */ this.playbackInfo.timeline.isEmpty()
? Player.STATE_ENDED
: Player.STATE_BUFFERING);
PlaybackInfo playbackInfo = this.playbackInfo.copyWithPlaybackError(null);
playbackInfo =
playbackInfo.copyWithPlaybackState(
playbackInfo.timeline.isEmpty() ? Player.STATE_ENDED : Player.STATE_BUFFERING);
// 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
// player after this prepare. The internal player can't change the playback info immediately
......@@ -440,8 +437,14 @@ import java.util.concurrent.TimeoutException;
@Override
public void removeMediaItems(int fromIndex, int toIndex) {
Assertions.checkArgument(toIndex > fromIndex);
removeMediaItemsInternal(fromIndex, toIndex);
PlaybackInfo playbackInfo = removeMediaItemsInternal(fromIndex, toIndex);
updatePlaybackInfo(
playbackInfo,
/* positionDiscontinuity= */ false,
/* ignored */ Player.DISCONTINUITY_REASON_INTERNAL,
/* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
/* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
/* seekProcessed= */ false);
}
@Override
......@@ -474,10 +477,7 @@ import java.util.concurrent.TimeoutException;
@Override
public void clearMediaItems() {
if (mediaSourceHolderSnapshots.isEmpty()) {
return;
}
removeMediaItemsInternal(/* fromIndex= */ 0, /* toIndex= */ mediaSourceHolderSnapshots.size());
removeMediaItems(/* fromIndex= */ 0, /* toIndex= */ mediaSourceHolderSnapshots.size());
}
@Override
......@@ -690,17 +690,20 @@ import java.util.concurrent.TimeoutException;
@Override
public void stop(boolean reset) {
PlaybackInfo playbackInfo =
getResetPlaybackInfo(
/* clearPlaylist= */ reset,
/* resetError= */ reset,
/* playbackState= */ Player.STATE_IDLE);
// Trigger internal stop first before updating the playback info and notifying external
// listeners to ensure that new operations issued in the listener notifications reach the
// player after this stop. The internal player can't change the playback info immediately
// because it uses a callback.
PlaybackInfo playbackInfo;
if (reset) {
playbackInfo =
removeMediaItemsInternal(
/* fromIndex= */ 0, /* toIndex= */ mediaSourceHolderSnapshots.size());
playbackInfo = playbackInfo.copyWithPlaybackError(null);
} else {
playbackInfo = this.playbackInfo.copyWithLoadingMediaPeriodId(this.playbackInfo.periodId);
playbackInfo.bufferedPositionUs = playbackInfo.positionUs;
playbackInfo.totalBufferedDurationUs = 0;
}
playbackInfo = playbackInfo.copyWithPlaybackState(Player.STATE_IDLE);
pendingOperationAcks++;
internalPlayer.stop(reset);
internalPlayer.stop();
updatePlaybackInfo(
playbackInfo,
/* positionDiscontinuity= */ false,
......@@ -726,11 +729,10 @@ import java.util.concurrent.TimeoutException;
if (analyticsCollector != null) {
bandwidthMeter.removeEventListener(analyticsCollector);
}
playbackInfo =
getResetPlaybackInfo(
/* clearPlaylist= */ false,
/* resetError= */ false,
/* playbackState= */ Player.STATE_IDLE);
playbackInfo = playbackInfo.copyWithPlaybackState(Player.STATE_IDLE);
playbackInfo = playbackInfo.copyWithLoadingMediaPeriodId(playbackInfo.periodId);
playbackInfo.bufferedPositionUs = playbackInfo.positionUs;
playbackInfo.totalBufferedDurationUs = 0;
}
@Override
......@@ -924,7 +926,9 @@ import java.util.concurrent.TimeoutException;
if (!this.playbackInfo.timeline.isEmpty() && newTimeline.isEmpty()) {
// Update the masking variables, which are used when the timeline becomes empty because a
// ConcatenatingMediaSource has been cleared.
resetMaskingPosition();
maskingWindowIndex = C.INDEX_UNSET;
maskingWindowPositionMs = 0;
maskingPeriodIndex = 0;
}
if (!newTimeline.isEmpty()) {
List<Timeline> timelines = ((PlaylistTimeline) newTimeline).getChildTimelines();
......@@ -945,41 +949,6 @@ import java.util.concurrent.TimeoutException;
}
}
private PlaybackInfo getResetPlaybackInfo(
boolean clearPlaylist, boolean resetError, @Player.State int playbackState) {
if (clearPlaylist) {
// Reset list of media source holders which are used for creating the masking timeline.
removeMediaSourceHolders(
/* fromIndex= */ 0, /* toIndexExclusive= */ mediaSourceHolderSnapshots.size());
resetMaskingPosition();
}
Timeline timeline = playbackInfo.timeline;
MediaPeriodId mediaPeriodId = playbackInfo.periodId;
long requestedContentPositionUs = playbackInfo.requestedContentPositionUs;
long positionUs = playbackInfo.positionUs;
if (clearPlaylist) {
timeline = Timeline.EMPTY;
mediaPeriodId = PlaybackInfo.getDummyPeriodForEmptyTimeline();
requestedContentPositionUs = C.TIME_UNSET;
positionUs = 0;
}
return new PlaybackInfo(
timeline,
mediaPeriodId,
requestedContentPositionUs,
playbackState,
resetError ? null : playbackInfo.playbackError,
/* isLoading= */ false,
clearPlaylist ? TrackGroupArray.EMPTY : playbackInfo.trackGroups,
clearPlaylist ? emptyTrackSelectorResult : playbackInfo.trackSelectorResult,
mediaPeriodId,
playbackInfo.playWhenReady,
playbackInfo.playbackSuppressionReason,
positionUs,
/* totalBufferedDurationUs= */ 0,
positionUs);
}
private void updatePlaybackInfo(
PlaybackInfo playbackInfo,
boolean positionDiscontinuity,
......@@ -1139,7 +1108,7 @@ import java.util.concurrent.TimeoutException;
return holders;
}
private void removeMediaItemsInternal(int fromIndex, int toIndex) {
private PlaybackInfo removeMediaItemsInternal(int fromIndex, int toIndex) {
Assertions.checkArgument(
fromIndex >= 0 && toIndex >= fromIndex && toIndex <= mediaSourceHolderSnapshots.size());
int currentWindowIndex = getCurrentWindowIndex();
......@@ -1164,13 +1133,7 @@ import java.util.concurrent.TimeoutException;
newPlaybackInfo = newPlaybackInfo.copyWithPlaybackState(STATE_ENDED);
}
internalPlayer.removeMediaSources(fromIndex, toIndex, shuffleOrder);
updatePlaybackInfo(
newPlaybackInfo,
/* positionDiscontinuity= */ false,
/* ignored */ Player.DISCONTINUITY_REASON_INTERNAL,
/* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
/* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
/* seekProcessed= */ false);
return newPlaybackInfo;
}
private void removeMediaSourceHolders(int fromIndex, int toIndexExclusive) {
......@@ -1389,12 +1352,6 @@ import java.util.concurrent.TimeoutException;
}
}
private void resetMaskingPosition() {
maskingWindowIndex = C.INDEX_UNSET;
maskingWindowPositionMs = 0;
maskingPeriodIndex = 0;
}
private long periodPositionUsToWindowPositionMs(MediaPeriodId periodId, long positionUs) {
long positionMs = C.usToMs(positionUs);
playbackInfo.timeline.getPeriodByUid(periodId.periodUid, period);
......
......@@ -306,8 +306,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
handler.obtainMessage(MSG_SET_SEEK_PARAMETERS, seekParameters).sendToTarget();
}
public void stop(boolean reset) {
handler.obtainMessage(MSG_STOP, reset ? 1 : 0, 0).sendToTarget();
public void stop() {
handler.obtainMessage(MSG_STOP).sendToTarget();
}
public void setMediaSources(
......@@ -475,10 +475,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
/* foregroundMode= */ msg.arg1 != 0, /* processedFlag= */ (AtomicBoolean) msg.obj);
break;
case MSG_STOP:
stopInternal(
/* forceResetRenderers= */ false,
/* resetPositionAndState= */ msg.arg1 != 0,
/* acknowledgeStop= */ true);
stopInternal(/* forceResetRenderers= */ false, /* acknowledgeStop= */ true);
break;
case MSG_PERIOD_PREPARED:
handlePeriodPrepared((MediaPeriod) msg.obj);
......@@ -537,10 +534,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
}
Log.e(TAG, "Playback error", e);
stopInternal(
/* forceResetRenderers= */ true,
/* resetPositionAndState= */ false,
/* acknowledgeStop= */ false);
stopInternal(/* forceResetRenderers= */ true, /* acknowledgeStop= */ false);
playbackInfo = playbackInfo.copyWithPlaybackError(e);
maybeNotifyPlaybackInfoChanged();
} catch (IOException e) {
......@@ -551,10 +545,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
error = error.copyWithMediaPeriodId(playingPeriod.info.id);
}
Log.e(TAG, "Playback error", error);
stopInternal(
/* forceResetRenderers= */ false,
/* resetPositionAndState= */ false,
/* acknowledgeStop= */ false);
stopInternal(/* forceResetRenderers= */ false, /* acknowledgeStop= */ false);
playbackInfo = playbackInfo.copyWithPlaybackError(error);
maybeNotifyPlaybackInfoChanged();
} catch (RuntimeException | OutOfMemoryError e) {
......@@ -563,10 +554,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
? ExoPlaybackException.createForOutOfMemoryError((OutOfMemoryError) e)
: ExoPlaybackException.createForUnexpected((RuntimeException) e);
Log.e(TAG, "Playback error", error);
stopInternal(
/* forceResetRenderers= */ true,
/* resetPositionAndState= */ false,
/* acknowledgeStop= */ false);
stopInternal(/* forceResetRenderers= */ true, /* acknowledgeStop= */ false);
playbackInfo = playbackInfo.copyWithPlaybackError(error);
maybeNotifyPlaybackInfoChanged();
}
......@@ -647,7 +635,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
/* resetRenderers= */ false,
/* resetPosition= */ false,
/* releaseMediaSourceList= */ false,
/* clearMediaSourceList= */ false,
/* resetError= */ true);
loadControl.onPrepared();
setState(playbackInfo.timeline.isEmpty() ? Player.STATE_ENDED : Player.STATE_BUFFERING);
......@@ -1036,7 +1023,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
/* resetRenderers= */ false,
/* resetPosition= */ true,
/* releaseMediaSourceList= */ false,
/* clearMediaSourceList= */ false,
/* resetError= */ true);
} else {
// Execute the seek in the current media periods.
......@@ -1203,14 +1189,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
}
private void stopInternal(
boolean forceResetRenderers, boolean resetPositionAndState, boolean acknowledgeStop) {
private void stopInternal(boolean forceResetRenderers, boolean acknowledgeStop) {
resetInternal(
/* resetRenderers= */ forceResetRenderers || !foregroundMode,
/* resetPosition= */ resetPositionAndState,
/* resetPosition= */ false,
/* releaseMediaSourceList= */ true,
/* clearMediaSourceList= */ resetPositionAndState,
/* resetError= */ resetPositionAndState);
/* resetError= */ false);
playbackInfoUpdate.incrementPendingOperationAcks(acknowledgeStop ? 1 : 0);
loadControl.onStopped();
setState(Player.STATE_IDLE);
......@@ -1219,9 +1203,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
private void releaseInternal() {
resetInternal(
/* resetRenderers= */ true,
/* resetPosition= */ true,
/* resetPosition= */ false,
/* releaseMediaSourceList= */ true,
/* clearMediaSourceList= */ true,
/* resetError= */ false);
loadControl.onReleased();
setState(Player.STATE_IDLE);
......@@ -1236,7 +1219,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
boolean resetRenderers,
boolean resetPosition,
boolean releaseMediaSourceList,
boolean clearMediaSourceList,
boolean resetError) {
handler.removeMessages(MSG_DO_SOME_WORK);
rebuffering = false;
......@@ -1262,26 +1244,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
enabledRendererCount = 0;
Timeline timeline = playbackInfo.timeline;
if (clearMediaSourceList) {
timeline = mediaSourceList.clear(/* shuffleOrder= */ null);
for (PendingMessageInfo pendingMessageInfo : pendingMessages) {
pendingMessageInfo.message.markAsProcessed(/* isDelivered= */ false);
}
pendingMessages.clear();
resetPosition = true;
}
MediaPeriodId mediaPeriodId = playbackInfo.periodId;
long startPositionUs = playbackInfo.positionUs;
long requestedContentPositionUs =
shouldUseRequestedContentPosition(playbackInfo, period, window)
? playbackInfo.requestedContentPositionUs
: playbackInfo.positionUs;
boolean resetTrackInfo = clearMediaSourceList;
boolean resetTrackInfo = false;
if (resetPosition) {
pendingInitialSeekPosition = null;
Pair<MediaPeriodId, Long> firstPeriodAndPosition =
getPlaceholderFirstMediaPeriodPosition(timeline);
getPlaceholderFirstMediaPeriodPosition(playbackInfo.timeline);
mediaPeriodId = firstPeriodAndPosition.first;
startPositionUs = firstPeriodAndPosition.second;
requestedContentPositionUs = C.TIME_UNSET;
......@@ -1295,7 +1268,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
playbackInfo =
new PlaybackInfo(
timeline,
playbackInfo.timeline,
mediaPeriodId,
requestedContentPositionUs,
playbackInfo.playbackState,
......@@ -1667,7 +1640,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
/* resetRenderers= */ false,
/* resetPosition= */ false,
/* releaseMediaSourceList= */ false,
/* clearMediaSourceList= */ false,
/* resetError= */ true);
}
if (!periodPositionChanged) {
......
......@@ -1193,19 +1193,21 @@ public interface Player {
* player instance can still be used, and {@link #release()} must still be called on the player if
* it's no longer required.
*
* <p>Calling this method does not reset the playback position.
* <p>Calling this method does not clear the playlist, reset the playback position or the playback
* error.
*/
void stop();
/**
* Stops playback and optionally resets the player. Use {@link #pause()} rather than this method
* if the intention is to pause playback.
* Stops playback and optionally clears the playlist and resets the position and playback error.
* Use {@link #pause()} rather than this method if the intention is to pause playback.
*
* <p>Calling this method will cause the playback state to transition to {@link #STATE_IDLE}. The
* player instance can still be used, and {@link #release()} must still be called on the player if
* it's no longer required.
*
* @param reset Whether the player should be reset.
* @param reset Whether the playlist should be cleared and whether the playback position and
* playback error should be reset.
*/
void stop(boolean reset);
......
......@@ -351,7 +351,7 @@ public abstract class StubExoPlayer extends BasePlayer implements ExoPlayer {
}
@Override
public void stop(boolean resetStateAndPosition) {
public void stop(boolean reset) {
throw new UnsupportedOperationException();
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment