-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:
John Zhen Mo 2018-04-07 16:45:42 -07:00
parent a275d7ff50
commit c9915bba18
3 changed files with 73 additions and 56 deletions

View File

@ -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'

View File

@ -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();
}
}

View File

@ -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;
}
}
}