diff --git a/app/build.gradle b/app/build.gradle
index cf4db5ccc..a0b4da510 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -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'
diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
index e3d351671..c9b67a7c9 100644
--- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
@@ -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.
- *
- * 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();
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java b/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java
index 583c4b8e7..a0f67d398 100644
--- a/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java
+++ b/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java
@@ -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 onSuccess = info -> syncInternal(currentItem, info);
final Consumer 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 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 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 neighbors;
+
+ ItemsToLoad(@NonNull final PlayQueueItem center,
+ @NonNull final Collection neighbors) {
+ this.center = center;
+ this.neighbors = neighbors;
+ }
+ }
}