From 77da40e507ecebc8c0b361d1ed846cf0fa3ca356 Mon Sep 17 00:00:00 2001 From: John Zhen Mo Date: Mon, 26 Feb 2018 22:37:19 -0800 Subject: [PATCH] -Added perpetual extractor source loading on network failures. -Fixed play queue playlist desynchronization caused by media source manager window loading expansion on sublist prior to current item. -Fixed failed media source not treated as ready for playback. --- .../player/helper/PlayerDataSource.java | 6 ++- .../player/playback/MediaSourceManager.java | 41 ++++++++++--------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java index 40548aa6c..133121269 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java @@ -16,6 +16,7 @@ import com.google.android.exoplayer2.upstream.TransferListener; public class PlayerDataSource { private static final int MANIFEST_MINIMUM_RETRY = 5; + private static final int EXTRACTOR_MINIMUM_RETRY = Integer.MAX_VALUE; private static final int LIVE_STREAM_EDGE_GAP_MILLIS = 10000; private final DataSource.Factory cacheDataSourceFactory; @@ -63,11 +64,12 @@ public class PlayerDataSource { } public ExtractorMediaSource.Factory getExtractorMediaSourceFactory() { - return new ExtractorMediaSource.Factory(cacheDataSourceFactory); + return new ExtractorMediaSource.Factory(cacheDataSourceFactory) + .setMinLoadableRetryCount(EXTRACTOR_MINIMUM_RETRY); } public ExtractorMediaSource.Factory getExtractorMediaSourceFactory(@NonNull final String key) { - return new ExtractorMediaSource.Factory(cacheDataSourceFactory).setCustomCacheKey(key); + return getExtractorMediaSourceFactory().setCustomCacheKey(key); } public SingleSampleMediaSource.Factory getSampleMediaSourceFactory() { 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 2a2a4ccb4..0f30169a7 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 @@ -38,11 +38,12 @@ import io.reactivex.subjects.PublishSubject; import static org.schabi.newpipe.playlist.PlayQueue.DEBUG; public class MediaSourceManager { - private final String TAG = "MediaSourceManager"; + private final static String TAG = "MediaSourceManager"; + + // WINDOW_SIZE determines how many streams AFTER the current stream should be loaded. + // The default value (1) ensures seamless playback under typical network settings. + private final static int WINDOW_SIZE = 1; - // One-side rolling window size for default loading - // Effectively loads windowSize * 2 + 1 streams per call to load, must be greater than 0 - private final int windowSize; private final PlaybackListener playbackListener; private final PlayQueue playQueue; private final long expirationTimeMillis; @@ -68,23 +69,19 @@ public class MediaSourceManager { public MediaSourceManager(@NonNull final PlaybackListener listener, @NonNull final PlayQueue playQueue) { - this(listener, playQueue, 1, 400L, 2, TimeUnit.HOURS); + this(listener, playQueue, + /*loadDebounceMillis=*/400L, + /*expirationTimeMillis=*/2, + /*expirationTimeUnit=*/TimeUnit.HOURS); } private MediaSourceManager(@NonNull final PlaybackListener listener, @NonNull final PlayQueue playQueue, - final int windowSize, final long loadDebounceMillis, final long expirationTimeMillis, @NonNull final TimeUnit expirationTimeUnit) { - if (windowSize <= 0) { - throw new UnsupportedOperationException( - "MediaSourceManager window size must be greater than 0"); - } - this.playbackListener = listener; this.playQueue = playQueue; - this.windowSize = windowSize; this.loadDebounceMillis = loadDebounceMillis; this.expirationTimeMillis = expirationTimeMillis; this.expirationTimeUnit = expirationTimeUnit; @@ -234,7 +231,7 @@ public class MediaSourceManager { private boolean isPlayQueueReady() { if (playQueue == null) return false; - final boolean isWindowLoaded = playQueue.size() - playQueue.getIndex() > windowSize; + final boolean isWindowLoaded = playQueue.size() - playQueue.getIndex() > WINDOW_SIZE; return playQueue.isComplete() || isWindowLoaded; } @@ -244,10 +241,14 @@ public class MediaSourceManager { } final MediaSource mediaSource = sources.getMediaSource(playQueue.getIndex()); - if (!(mediaSource instanceof LoadedMediaSource)) return false; - final PlayQueueItem playQueueItem = playQueue.getItem(); - return playQueueItem == ((LoadedMediaSource) mediaSource).getStream(); + + if (mediaSource instanceof LoadedMediaSource) { + return playQueueItem == ((LoadedMediaSource) mediaSource).getStream(); + } else if (mediaSource instanceof FailedMediaSource) { + return playQueueItem == ((FailedMediaSource) mediaSource).getStream(); + } + return false; } private void tryBlock() { @@ -318,11 +319,11 @@ public class MediaSourceManager { loadItem(currentItem); // The rest are just for seamless playback - final int leftBound = Math.max(0, currentIndex - windowSize); - final int rightLimit = currentIndex + windowSize + 1; + final int leftBound = currentIndex + 1; + final int rightLimit = leftBound + WINDOW_SIZE; final int rightBound = Math.min(playQueue.size(), rightLimit); - final List items = new ArrayList<>(playQueue.getStreams().subList(leftBound, - rightBound)); + final List items = new ArrayList<>( + playQueue.getStreams().subList(leftBound,rightBound)); // Do a round robin final int excess = rightLimit - playQueue.size();