mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-02-02 12:19:16 +00:00
-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 {
|
ext {
|
||||||
supportLibVersion = '27.1.0'
|
supportLibVersion = '27.1.0'
|
||||||
exoPlayerLibVersion = '2.7.2'
|
exoPlayerLibVersion = '2.7.3'
|
||||||
roomDbLibVersion = '1.0.0'
|
roomDbLibVersion = '1.0.0'
|
||||||
leakCanaryLibVersion = '1.5.4'
|
leakCanaryLibVersion = '1.5.4'
|
||||||
okHttpLibVersion = '1.5.0'
|
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 PLAY_PREV_ACTIVATION_LIMIT_MILLIS = 5000; // 5 seconds
|
||||||
protected final static int PROGRESS_LOOP_INTERVAL_MILLIS = 500;
|
protected final static int PROGRESS_LOOP_INTERVAL_MILLIS = 500;
|
||||||
protected final static int RECOVERY_SKIP_THRESHOLD_MILLIS = 3000; // 3 seconds
|
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 CustomTrackSelector trackSelector;
|
||||||
protected PlayerDataSource dataSource;
|
protected PlayerDataSource dataSource;
|
||||||
@ -647,7 +646,7 @@ public abstract class BasePlayer implements
|
|||||||
// Is still synchronizing?
|
// Is still synchronizing?
|
||||||
seekToDefault();
|
seekToDefault();
|
||||||
|
|
||||||
} else if (isSynchronizing && presetStartPositionMillis != 0L) {
|
} else if (isSynchronizing && presetStartPositionMillis > 0L) {
|
||||||
if (DEBUG) Log.d(TAG, "Playback - Seeking to preset start " +
|
if (DEBUG) Log.d(TAG, "Playback - Seeking to preset start " +
|
||||||
"position=[" + presetStartPositionMillis + "]");
|
"position=[" + presetStartPositionMillis + "]");
|
||||||
// Has another start position?
|
// Has another start position?
|
||||||
@ -1033,25 +1032,8 @@ public abstract class BasePlayer implements
|
|||||||
&& simpleExoPlayer.getCurrentPosition() >= 0;
|
&& 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() {
|
public void seekToDefault() {
|
||||||
if (simpleExoPlayer == null) return;
|
if (simpleExoPlayer != null) {
|
||||||
if (isLive() && simpleExoPlayer.getDuration() < SHORT_LIVESTREAM_CHUNK_LENGTH_MILLIS) {
|
|
||||||
simpleExoPlayer.seekTo(simpleExoPlayer.getDuration());
|
|
||||||
} else {
|
|
||||||
simpleExoPlayer.seekToDefaultPosition();
|
simpleExoPlayer.seekToDefaultPosition();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import org.schabi.newpipe.playlist.events.RemoveEvent;
|
|||||||
import org.schabi.newpipe.playlist.events.ReorderEvent;
|
import org.schabi.newpipe.playlist.events.ReorderEvent;
|
||||||
import org.schabi.newpipe.util.ServiceHelper;
|
import org.schabi.newpipe.util.ServiceHelper;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -39,7 +40,8 @@ import io.reactivex.functions.Consumer;
|
|||||||
import io.reactivex.internal.subscriptions.EmptySubscription;
|
import io.reactivex.internal.subscriptions.EmptySubscription;
|
||||||
import io.reactivex.subjects.PublishSubject;
|
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;
|
import static org.schabi.newpipe.playlist.PlayQueue.DEBUG;
|
||||||
|
|
||||||
public class MediaSourceManager {
|
public class MediaSourceManager {
|
||||||
@ -267,6 +269,8 @@ public class MediaSourceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isPlaybackReady() {
|
private boolean isPlaybackReady() {
|
||||||
|
if (playlist.size() != playQueue.size()) return false;
|
||||||
|
|
||||||
final ManagedMediaSource mediaSource = playlist.get(playQueue.getIndex());
|
final ManagedMediaSource mediaSource = playlist.get(playQueue.getIndex());
|
||||||
if (mediaSource == null) return false;
|
if (mediaSource == null) return false;
|
||||||
|
|
||||||
@ -288,7 +292,7 @@ public class MediaSourceManager {
|
|||||||
private void maybeUnblock() {
|
private void maybeUnblock() {
|
||||||
if (DEBUG) Log.d(TAG, "maybeUnblock() called.");
|
if (DEBUG) Log.d(TAG, "maybeUnblock() called.");
|
||||||
|
|
||||||
if (isPlayQueueReady() && isPlaybackReady() && isBlocked.get()) {
|
if (isBlocked.get()) {
|
||||||
isBlocked.set(false);
|
isBlocked.set(false);
|
||||||
playbackListener.onPlaybackUnblock(playlist.getParentMediaSource());
|
playbackListener.onPlaybackUnblock(playlist.getParentMediaSource());
|
||||||
}
|
}
|
||||||
@ -299,10 +303,10 @@ public class MediaSourceManager {
|
|||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private void maybeSync() {
|
private void maybeSync() {
|
||||||
if (DEBUG) Log.d(TAG, "onPlaybackSynchronize() called.");
|
if (DEBUG) Log.d(TAG, "maybeSync() called.");
|
||||||
|
|
||||||
final PlayQueueItem currentItem = playQueue.getItem();
|
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<StreamInfo> onSuccess = info -> syncInternal(currentItem, info);
|
||||||
final Consumer<Throwable> onError = throwable -> syncInternal(currentItem, null);
|
final Consumer<Throwable> onError = throwable -> syncInternal(currentItem, null);
|
||||||
@ -321,10 +325,12 @@ public class MediaSourceManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeSynchronizePlayer() {
|
private synchronized void maybeSynchronizePlayer() {
|
||||||
|
if (isPlayQueueReady() && isPlaybackReady()) {
|
||||||
maybeUnblock();
|
maybeUnblock();
|
||||||
maybeSync();
|
maybeSync();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// MediaSource Loading
|
// MediaSource Loading
|
||||||
@ -346,37 +352,16 @@ public class MediaSourceManager {
|
|||||||
debouncedSignal.onNext(System.currentTimeMillis());
|
debouncedSignal.onNext(System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadImmediate() {
|
private synchronized void loadImmediate() {
|
||||||
if (DEBUG) Log.d(TAG, "MediaSource - loadImmediate() called");
|
if (DEBUG) Log.d(TAG, "MediaSource - loadImmediate() called");
|
||||||
// The current item has higher priority
|
final ItemsToLoad itemsToLoad = getItemsToLoad(playQueue, WINDOW_SIZE);
|
||||||
final int currentIndex = playQueue.getIndex();
|
if (itemsToLoad == null) return;
|
||||||
final PlayQueueItem currentItem = playQueue.getItem(currentIndex);
|
|
||||||
if (currentItem == null) return;
|
|
||||||
|
|
||||||
// Evict the items being loaded to free up memory
|
// Evict the previous items being loaded to free up memory, before start loading new ones
|
||||||
if (loaderReactor.size() > MAXIMUM_LOADER_SIZE) {
|
maybeClearLoaders();
|
||||||
loaderReactor.clear();
|
|
||||||
loadingItems.clear();
|
|
||||||
}
|
|
||||||
maybeLoadItem(currentItem);
|
|
||||||
|
|
||||||
// The rest are just for seamless playback
|
maybeLoadItem(itemsToLoad.center);
|
||||||
// Although timeline is not updated prior to the current index, these sources are still
|
for (final PlayQueueItem item : itemsToLoad.neighbors) {
|
||||||
// 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(item);
|
maybeLoadItem(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -476,6 +461,15 @@ public class MediaSourceManager {
|
|||||||
"index=[" + currentIndex + "], item=[" + currentItem.getTitle() + "]");
|
"index=[" + currentIndex + "], item=[" + currentItem.getTitle() + "]");
|
||||||
playlist.invalidate(currentIndex, this::loadImmediate);
|
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
|
// MediaSource Playlist Helpers
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
@ -493,4 +487,45 @@ public class MediaSourceManager {
|
|||||||
playlist.expand();
|
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
Block a user