-Reversed special seek logic for short buffer livestreams.
-Fixed loader cleaning potentially canceling existing correct loading items. -Updated ExoPlayer to 2.7.3.
This commit is contained in:
parent
a275d7ff50
commit
c9915bba18
|
@ -42,7 +42,7 @@ android {
|
|||
|
||||
ext {
|
||||
supportLibVersion = '27.1.0'
|
||||
exoPlayerLibVersion = '2.7.2'
|
||||
exoPlayerLibVersion = '2.7.3'
|
||||
roomDbLibVersion = '1.0.0'
|
||||
leakCanaryLibVersion = '1.5.4'
|
||||
okHttpLibVersion = '1.5.0'
|
||||
|
|
|
@ -144,7 +144,6 @@ public abstract class BasePlayer implements
|
|||
protected final static int PLAY_PREV_ACTIVATION_LIMIT_MILLIS = 5000; // 5 seconds
|
||||
protected final static int PROGRESS_LOOP_INTERVAL_MILLIS = 500;
|
||||
protected final static int RECOVERY_SKIP_THRESHOLD_MILLIS = 3000; // 3 seconds
|
||||
protected final static int SHORT_LIVESTREAM_CHUNK_LENGTH_MILLIS = 60000; // 1 minute
|
||||
|
||||
protected CustomTrackSelector trackSelector;
|
||||
protected PlayerDataSource dataSource;
|
||||
|
@ -647,7 +646,7 @@ public abstract class BasePlayer implements
|
|||
// Is still synchronizing?
|
||||
seekToDefault();
|
||||
|
||||
} else if (isSynchronizing && presetStartPositionMillis != 0L) {
|
||||
} else if (isSynchronizing && presetStartPositionMillis > 0L) {
|
||||
if (DEBUG) Log.d(TAG, "Playback - Seeking to preset start " +
|
||||
"position=[" + presetStartPositionMillis + "]");
|
||||
// Has another start position?
|
||||
|
@ -1033,25 +1032,8 @@ public abstract class BasePlayer implements
|
|||
&& simpleExoPlayer.getCurrentPosition() >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seeks to the default position of the currently playing
|
||||
* {@link com.google.android.exoplayer2.Timeline.Window}. Does nothing if the
|
||||
* {@link #simpleExoPlayer} is not initialized.
|
||||
* <br><br>
|
||||
* If the current window is non-live, then this will seek to the start of the window.
|
||||
* If the window is live but has a buffer length greater than
|
||||
* {@link #SHORT_LIVESTREAM_CHUNK_LENGTH_MILLIS}, then this will seek to the default
|
||||
* live edge position through {@link SimpleExoPlayer#seekToDefaultPosition}.
|
||||
* Otherwise, this will seek to the maximum position possible for the current buffer
|
||||
* given by {@link SimpleExoPlayer#getDuration}.
|
||||
*
|
||||
* @see SimpleExoPlayer#seekToDefaultPosition
|
||||
* */
|
||||
public void seekToDefault() {
|
||||
if (simpleExoPlayer == null) return;
|
||||
if (isLive() && simpleExoPlayer.getDuration() < SHORT_LIVESTREAM_CHUNK_LENGTH_MILLIS) {
|
||||
simpleExoPlayer.seekTo(simpleExoPlayer.getDuration());
|
||||
} else {
|
||||
if (simpleExoPlayer != null) {
|
||||
simpleExoPlayer.seekToDefaultPosition();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.schabi.newpipe.playlist.events.RemoveEvent;
|
|||
import org.schabi.newpipe.playlist.events.ReorderEvent;
|
||||
import org.schabi.newpipe.util.ServiceHelper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -39,7 +40,8 @@ import io.reactivex.functions.Consumer;
|
|||
import io.reactivex.internal.subscriptions.EmptySubscription;
|
||||
import io.reactivex.subjects.PublishSubject;
|
||||
|
||||
import static org.schabi.newpipe.player.mediasource.FailedMediaSource.*;
|
||||
import static org.schabi.newpipe.player.mediasource.FailedMediaSource.MediaSourceResolutionException;
|
||||
import static org.schabi.newpipe.player.mediasource.FailedMediaSource.StreamInfoLoadException;
|
||||
import static org.schabi.newpipe.playlist.PlayQueue.DEBUG;
|
||||
|
||||
public class MediaSourceManager {
|
||||
|
@ -267,6 +269,8 @@ public class MediaSourceManager {
|
|||
}
|
||||
|
||||
private boolean isPlaybackReady() {
|
||||
if (playlist.size() != playQueue.size()) return false;
|
||||
|
||||
final ManagedMediaSource mediaSource = playlist.get(playQueue.getIndex());
|
||||
if (mediaSource == null) return false;
|
||||
|
||||
|
@ -288,7 +292,7 @@ public class MediaSourceManager {
|
|||
private void maybeUnblock() {
|
||||
if (DEBUG) Log.d(TAG, "maybeUnblock() called.");
|
||||
|
||||
if (isPlayQueueReady() && isPlaybackReady() && isBlocked.get()) {
|
||||
if (isBlocked.get()) {
|
||||
isBlocked.set(false);
|
||||
playbackListener.onPlaybackUnblock(playlist.getParentMediaSource());
|
||||
}
|
||||
|
@ -299,10 +303,10 @@ public class MediaSourceManager {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void maybeSync() {
|
||||
if (DEBUG) Log.d(TAG, "onPlaybackSynchronize() called.");
|
||||
if (DEBUG) Log.d(TAG, "maybeSync() called.");
|
||||
|
||||
final PlayQueueItem currentItem = playQueue.getItem();
|
||||
if (isBlocked.get() || !isPlaybackReady() || currentItem == null) return;
|
||||
if (isBlocked.get() || currentItem == null) return;
|
||||
|
||||
final Consumer<StreamInfo> onSuccess = info -> syncInternal(currentItem, info);
|
||||
final Consumer<Throwable> onError = throwable -> syncInternal(currentItem, null);
|
||||
|
@ -321,9 +325,11 @@ public class MediaSourceManager {
|
|||
}
|
||||
}
|
||||
|
||||
private void maybeSynchronizePlayer() {
|
||||
maybeUnblock();
|
||||
maybeSync();
|
||||
private synchronized void maybeSynchronizePlayer() {
|
||||
if (isPlayQueueReady() && isPlaybackReady()) {
|
||||
maybeUnblock();
|
||||
maybeSync();
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -346,37 +352,16 @@ public class MediaSourceManager {
|
|||
debouncedSignal.onNext(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private void loadImmediate() {
|
||||
private synchronized void loadImmediate() {
|
||||
if (DEBUG) Log.d(TAG, "MediaSource - loadImmediate() called");
|
||||
// The current item has higher priority
|
||||
final int currentIndex = playQueue.getIndex();
|
||||
final PlayQueueItem currentItem = playQueue.getItem(currentIndex);
|
||||
if (currentItem == null) return;
|
||||
final ItemsToLoad itemsToLoad = getItemsToLoad(playQueue, WINDOW_SIZE);
|
||||
if (itemsToLoad == null) return;
|
||||
|
||||
// Evict the items being loaded to free up memory
|
||||
if (loaderReactor.size() > MAXIMUM_LOADER_SIZE) {
|
||||
loaderReactor.clear();
|
||||
loadingItems.clear();
|
||||
}
|
||||
maybeLoadItem(currentItem);
|
||||
// Evict the previous items being loaded to free up memory, before start loading new ones
|
||||
maybeClearLoaders();
|
||||
|
||||
// The rest are just for seamless playback
|
||||
// Although timeline is not updated prior to the current index, these sources are still
|
||||
// loaded into the cache for faster retrieval at a potentially later time.
|
||||
final int leftBound = Math.max(0, currentIndex - WINDOW_SIZE);
|
||||
final int rightLimit = currentIndex + WINDOW_SIZE + 1;
|
||||
final int rightBound = Math.min(playQueue.size(), rightLimit);
|
||||
final Set<PlayQueueItem> items = new ArraySet<>(
|
||||
playQueue.getStreams().subList(leftBound,rightBound));
|
||||
|
||||
// Do a round robin
|
||||
final int excess = rightLimit - playQueue.size();
|
||||
if (excess >= 0) {
|
||||
items.addAll(playQueue.getStreams().subList(0, Math.min(playQueue.size(), excess)));
|
||||
}
|
||||
items.remove(currentItem);
|
||||
|
||||
for (final PlayQueueItem item : items) {
|
||||
maybeLoadItem(itemsToLoad.center);
|
||||
for (final PlayQueueItem item : itemsToLoad.neighbors) {
|
||||
maybeLoadItem(item);
|
||||
}
|
||||
}
|
||||
|
@ -476,6 +461,15 @@ public class MediaSourceManager {
|
|||
"index=[" + currentIndex + "], item=[" + currentItem.getTitle() + "]");
|
||||
playlist.invalidate(currentIndex, this::loadImmediate);
|
||||
}
|
||||
|
||||
private void maybeClearLoaders() {
|
||||
if (DEBUG) Log.d(TAG, "MediaSource - maybeClearLoaders() called.");
|
||||
if (!loadingItems.contains(playQueue.getItem()) &&
|
||||
loaderReactor.size() > MAXIMUM_LOADER_SIZE) {
|
||||
loaderReactor.clear();
|
||||
loadingItems.clear();
|
||||
}
|
||||
}
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// MediaSource Playlist Helpers
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
@ -493,4 +487,45 @@ public class MediaSourceManager {
|
|||
playlist.expand();
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Manager Helpers
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@Nullable
|
||||
private static ItemsToLoad getItemsToLoad(@NonNull final PlayQueue playQueue,
|
||||
final int windowSize) {
|
||||
// The current item has higher priority
|
||||
final int currentIndex = playQueue.getIndex();
|
||||
final PlayQueueItem currentItem = playQueue.getItem(currentIndex);
|
||||
if (currentItem == null) return null;
|
||||
|
||||
// The rest are just for seamless playback
|
||||
// Although timeline is not updated prior to the current index, these sources are still
|
||||
// loaded into the cache for faster retrieval at a potentially later time.
|
||||
final int leftBound = Math.max(0, currentIndex - windowSize);
|
||||
final int rightLimit = currentIndex + windowSize + 1;
|
||||
final int rightBound = Math.min(playQueue.size(), rightLimit);
|
||||
final Set<PlayQueueItem> neighbors = new ArraySet<>(
|
||||
playQueue.getStreams().subList(leftBound,rightBound));
|
||||
|
||||
// Do a round robin
|
||||
final int excess = rightLimit - playQueue.size();
|
||||
if (excess >= 0) {
|
||||
neighbors.addAll(playQueue.getStreams().subList(0, Math.min(playQueue.size(), excess)));
|
||||
}
|
||||
neighbors.remove(currentItem);
|
||||
|
||||
return new ItemsToLoad(currentItem, neighbors);
|
||||
}
|
||||
|
||||
private static class ItemsToLoad {
|
||||
@NonNull final private PlayQueueItem center;
|
||||
@NonNull final private Collection<PlayQueueItem> neighbors;
|
||||
|
||||
ItemsToLoad(@NonNull final PlayQueueItem center,
|
||||
@NonNull final Collection<PlayQueueItem> neighbors) {
|
||||
this.center = center;
|
||||
this.neighbors = neighbors;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue