-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.
This commit is contained in:
parent
b3b2748bb7
commit
77da40e507
2 changed files with 25 additions and 22 deletions
|
@ -16,6 +16,7 @@ import com.google.android.exoplayer2.upstream.TransferListener;
|
||||||
|
|
||||||
public class PlayerDataSource {
|
public class PlayerDataSource {
|
||||||
private static final int MANIFEST_MINIMUM_RETRY = 5;
|
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 static final int LIVE_STREAM_EDGE_GAP_MILLIS = 10000;
|
||||||
|
|
||||||
private final DataSource.Factory cacheDataSourceFactory;
|
private final DataSource.Factory cacheDataSourceFactory;
|
||||||
|
@ -63,11 +64,12 @@ public class PlayerDataSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExtractorMediaSource.Factory getExtractorMediaSourceFactory() {
|
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) {
|
public ExtractorMediaSource.Factory getExtractorMediaSourceFactory(@NonNull final String key) {
|
||||||
return new ExtractorMediaSource.Factory(cacheDataSourceFactory).setCustomCacheKey(key);
|
return getExtractorMediaSourceFactory().setCustomCacheKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SingleSampleMediaSource.Factory getSampleMediaSourceFactory() {
|
public SingleSampleMediaSource.Factory getSampleMediaSourceFactory() {
|
||||||
|
|
|
@ -38,11 +38,12 @@ import io.reactivex.subjects.PublishSubject;
|
||||||
import static org.schabi.newpipe.playlist.PlayQueue.DEBUG;
|
import static org.schabi.newpipe.playlist.PlayQueue.DEBUG;
|
||||||
|
|
||||||
public class MediaSourceManager {
|
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 PlaybackListener playbackListener;
|
||||||
private final PlayQueue playQueue;
|
private final PlayQueue playQueue;
|
||||||
private final long expirationTimeMillis;
|
private final long expirationTimeMillis;
|
||||||
|
@ -68,23 +69,19 @@ public class MediaSourceManager {
|
||||||
|
|
||||||
public MediaSourceManager(@NonNull final PlaybackListener listener,
|
public MediaSourceManager(@NonNull final PlaybackListener listener,
|
||||||
@NonNull final PlayQueue playQueue) {
|
@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,
|
private MediaSourceManager(@NonNull final PlaybackListener listener,
|
||||||
@NonNull final PlayQueue playQueue,
|
@NonNull final PlayQueue playQueue,
|
||||||
final int windowSize,
|
|
||||||
final long loadDebounceMillis,
|
final long loadDebounceMillis,
|
||||||
final long expirationTimeMillis,
|
final long expirationTimeMillis,
|
||||||
@NonNull final TimeUnit expirationTimeUnit) {
|
@NonNull final TimeUnit expirationTimeUnit) {
|
||||||
if (windowSize <= 0) {
|
|
||||||
throw new UnsupportedOperationException(
|
|
||||||
"MediaSourceManager window size must be greater than 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.playbackListener = listener;
|
this.playbackListener = listener;
|
||||||
this.playQueue = playQueue;
|
this.playQueue = playQueue;
|
||||||
this.windowSize = windowSize;
|
|
||||||
this.loadDebounceMillis = loadDebounceMillis;
|
this.loadDebounceMillis = loadDebounceMillis;
|
||||||
this.expirationTimeMillis = expirationTimeMillis;
|
this.expirationTimeMillis = expirationTimeMillis;
|
||||||
this.expirationTimeUnit = expirationTimeUnit;
|
this.expirationTimeUnit = expirationTimeUnit;
|
||||||
|
@ -234,7 +231,7 @@ public class MediaSourceManager {
|
||||||
private boolean isPlayQueueReady() {
|
private boolean isPlayQueueReady() {
|
||||||
if (playQueue == null) return false;
|
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;
|
return playQueue.isComplete() || isWindowLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,10 +241,14 @@ public class MediaSourceManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
final MediaSource mediaSource = sources.getMediaSource(playQueue.getIndex());
|
final MediaSource mediaSource = sources.getMediaSource(playQueue.getIndex());
|
||||||
if (!(mediaSource instanceof LoadedMediaSource)) return false;
|
|
||||||
|
|
||||||
final PlayQueueItem playQueueItem = playQueue.getItem();
|
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() {
|
private void tryBlock() {
|
||||||
|
@ -318,11 +319,11 @@ public class MediaSourceManager {
|
||||||
loadItem(currentItem);
|
loadItem(currentItem);
|
||||||
|
|
||||||
// The rest are just for seamless playback
|
// The rest are just for seamless playback
|
||||||
final int leftBound = Math.max(0, currentIndex - windowSize);
|
final int leftBound = currentIndex + 1;
|
||||||
final int rightLimit = currentIndex + windowSize + 1;
|
final int rightLimit = leftBound + WINDOW_SIZE;
|
||||||
final int rightBound = Math.min(playQueue.size(), rightLimit);
|
final int rightBound = Math.min(playQueue.size(), rightLimit);
|
||||||
final List<PlayQueueItem> items = new ArrayList<>(playQueue.getStreams().subList(leftBound,
|
final List<PlayQueueItem> items = new ArrayList<>(
|
||||||
rightBound));
|
playQueue.getStreams().subList(leftBound,rightBound));
|
||||||
|
|
||||||
// Do a round robin
|
// Do a round robin
|
||||||
final int excess = rightLimit - playQueue.size();
|
final int excess = rightLimit - playQueue.size();
|
||||||
|
|
Loading…
Reference in a new issue