mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-01-24 00:23:17 +01:00
-Added toggle to enable fast inexact seek in players.
-Improved player sync calls to recognize different metadata updates. -Changed MediaSourceManager to synchronize only after timeline changes and reenabled multiple sync calls to player. -Renamed listener and synchronization methods related to MediaSourceManager.
This commit is contained in:
parent
0c17f0825b
commit
a88e19a8ed
@ -380,11 +380,10 @@ public final class BackgroundPlayer extends Service {
|
|||||||
// Playback Listener
|
// Playback Listener
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
protected void onMetadataChanged(@NonNull final PlayQueueItem item,
|
||||||
public void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info) {
|
@Nullable final StreamInfo info,
|
||||||
if (currentItem == item && currentInfo == info) return;
|
final int newPlayQueueIndex,
|
||||||
super.sync(item, info);
|
final boolean hasPlayQueueItemChanged) {
|
||||||
|
|
||||||
resetNotification();
|
resetNotification();
|
||||||
updateNotification(-1);
|
updateNotification(-1);
|
||||||
updateMetadata();
|
updateMetadata();
|
||||||
@ -405,8 +404,8 @@ public final class BackgroundPlayer extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void onPlaybackShutdown() {
|
||||||
super.shutdown();
|
super.onPlaybackShutdown();
|
||||||
onClose();
|
onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +61,7 @@ import org.schabi.newpipe.history.HistoryRecordManager;
|
|||||||
import org.schabi.newpipe.player.helper.AudioReactor;
|
import org.schabi.newpipe.player.helper.AudioReactor;
|
||||||
import org.schabi.newpipe.player.helper.LoadController;
|
import org.schabi.newpipe.player.helper.LoadController;
|
||||||
import org.schabi.newpipe.player.helper.PlayerDataSource;
|
import org.schabi.newpipe.player.helper.PlayerDataSource;
|
||||||
|
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||||
import org.schabi.newpipe.player.playback.CustomTrackSelector;
|
import org.schabi.newpipe.player.playback.CustomTrackSelector;
|
||||||
import org.schabi.newpipe.player.playback.MediaSourceManager;
|
import org.schabi.newpipe.player.playback.MediaSourceManager;
|
||||||
import org.schabi.newpipe.player.playback.PlaybackListener;
|
import org.schabi.newpipe.player.playback.PlaybackListener;
|
||||||
@ -196,6 +197,7 @@ public abstract class BasePlayer implements
|
|||||||
|
|
||||||
simpleExoPlayer.addListener(this);
|
simpleExoPlayer.addListener(this);
|
||||||
simpleExoPlayer.setPlayWhenReady(true);
|
simpleExoPlayer.setPlayWhenReady(true);
|
||||||
|
simpleExoPlayer.setSeekParameters(PlayerHelper.getSeekParameters(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initListeners() {}
|
public void initListeners() {}
|
||||||
@ -688,7 +690,7 @@ public abstract class BasePlayer implements
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
showUnrecoverableError(error);
|
showUnrecoverableError(error);
|
||||||
shutdown();
|
onPlaybackShutdown();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -774,9 +776,9 @@ public abstract class BasePlayer implements
|
|||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void block() {
|
public void onPlaybackBlock() {
|
||||||
if (simpleExoPlayer == null) return;
|
if (simpleExoPlayer == null) return;
|
||||||
if (DEBUG) Log.d(TAG, "Playback - block() called");
|
if (DEBUG) Log.d(TAG, "Playback - onPlaybackBlock() called");
|
||||||
|
|
||||||
currentItem = null;
|
currentItem = null;
|
||||||
currentInfo = null;
|
currentInfo = null;
|
||||||
@ -787,9 +789,9 @@ public abstract class BasePlayer implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unblock(final MediaSource mediaSource) {
|
public void onPlaybackUnblock(final MediaSource mediaSource) {
|
||||||
if (simpleExoPlayer == null) return;
|
if (simpleExoPlayer == null) return;
|
||||||
if (DEBUG) Log.d(TAG, "Playback - unblock() called");
|
if (DEBUG) Log.d(TAG, "Playback - onPlaybackUnblock() called");
|
||||||
|
|
||||||
if (getCurrentState() == STATE_BLOCKED) changeState(STATE_BUFFERING);
|
if (getCurrentState() == STATE_BLOCKED) changeState(STATE_BUFFERING);
|
||||||
|
|
||||||
@ -798,34 +800,50 @@ public abstract class BasePlayer implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sync(@NonNull final PlayQueueItem item,
|
public void onPlaybackSynchronize(@NonNull final PlayQueueItem item,
|
||||||
@Nullable final StreamInfo info) {
|
@Nullable final StreamInfo info) {
|
||||||
if (currentItem == item && currentInfo == info) return;
|
if (DEBUG) Log.d(TAG, "Playback - onPlaybackSynchronize() called with " +
|
||||||
currentItem = item;
|
|
||||||
currentInfo = info;
|
|
||||||
|
|
||||||
if (DEBUG) Log.d(TAG, "Playback - sync() called with " +
|
|
||||||
(info == null ? "available" : "null") + " info, " +
|
(info == null ? "available" : "null") + " info, " +
|
||||||
"item=[" + item.getTitle() + "], url=[" + item.getUrl() + "]");
|
"item=[" + item.getTitle() + "], url=[" + item.getUrl() + "]");
|
||||||
if (simpleExoPlayer == null) return;
|
|
||||||
|
final boolean hasPlayQueueItemChanged = currentItem != item;
|
||||||
|
final boolean hasStreamInfoChanged = currentInfo != info;
|
||||||
|
if (!hasPlayQueueItemChanged && !hasStreamInfoChanged) {
|
||||||
|
return; // Nothing to synchronize
|
||||||
|
}
|
||||||
|
|
||||||
|
currentItem = item;
|
||||||
|
currentInfo = info;
|
||||||
|
if (hasPlayQueueItemChanged) {
|
||||||
|
// updates only to the stream info should not trigger another view count
|
||||||
|
registerView();
|
||||||
|
initThumbnail(info == null ? item.getThumbnailUrl() : info.getThumbnailUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
final int currentSourceIndex = playQueue.indexOf(item);
|
||||||
|
onMetadataChanged(item, info, currentSourceIndex, hasPlayQueueItemChanged);
|
||||||
|
|
||||||
// Check if on wrong window
|
// Check if on wrong window
|
||||||
final int currentSourceIndex = playQueue.indexOf(item);
|
if (simpleExoPlayer == null) return;
|
||||||
if (currentSourceIndex != playQueue.getIndex()) {
|
if (currentSourceIndex != playQueue.getIndex()) {
|
||||||
Log.e(TAG, "Play Queue may be desynchronized: item index=[" + currentSourceIndex +
|
Log.e(TAG, "Play Queue may be desynchronized: item index=[" + currentSourceIndex +
|
||||||
"], queue index=[" + playQueue.getIndex() + "]");
|
"], queue index=[" + playQueue.getIndex() + "]");
|
||||||
} else if (simpleExoPlayer.getCurrentPeriodIndex() != currentSourceIndex || !isPlaying()) {
|
|
||||||
|
// on metadata changed
|
||||||
|
} else if (simpleExoPlayer.getCurrentWindowIndex() != currentSourceIndex || !isPlaying()) {
|
||||||
final long startPos = info != null ? info.start_position : 0;
|
final long startPos = info != null ? info.start_position : 0;
|
||||||
if (DEBUG) Log.d(TAG, "Rewinding to correct window=[" + currentSourceIndex + "]," +
|
if (DEBUG) Log.d(TAG, "Rewinding to correct window=[" + currentSourceIndex + "]," +
|
||||||
" at=[" + getTimeString((int)startPos) + "]," +
|
" at=[" + getTimeString((int)startPos) + "]," +
|
||||||
" from=[" + simpleExoPlayer.getCurrentPeriodIndex() + "].");
|
" from=[" + simpleExoPlayer.getCurrentWindowIndex() + "].");
|
||||||
simpleExoPlayer.seekTo(currentSourceIndex, startPos);
|
simpleExoPlayer.seekTo(currentSourceIndex, startPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
registerView();
|
|
||||||
initThumbnail(info == null ? item.getThumbnailUrl() : info.thumbnail_url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract protected void onMetadataChanged(@NonNull final PlayQueueItem item,
|
||||||
|
@Nullable final StreamInfo info,
|
||||||
|
final int newPlayQueueIndex,
|
||||||
|
final boolean hasPlayQueueItemChanged);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public MediaSource sourceOf(PlayQueueItem item, StreamInfo info) {
|
public MediaSource sourceOf(PlayQueueItem item, StreamInfo info) {
|
||||||
@ -839,7 +857,7 @@ public abstract class BasePlayer implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void onPlaybackShutdown() {
|
||||||
if (DEBUG) Log.d(TAG, "Shutting down...");
|
if (DEBUG) Log.d(TAG, "Shutting down...");
|
||||||
destroy();
|
destroy();
|
||||||
}
|
}
|
||||||
|
@ -391,31 +391,32 @@ public final class MainVideoPlayer extends Activity {
|
|||||||
updatePlaybackButtons();
|
updatePlaybackButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Playback Listener
|
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void shutdown() {
|
|
||||||
super.shutdown();
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info) {
|
|
||||||
super.sync(item, info);
|
|
||||||
titleTextView.setText(getVideoTitle());
|
|
||||||
channelTextView.setText(getUploaderName());
|
|
||||||
|
|
||||||
//playPauseButton.setImageResource(R.drawable.ic_pause_white);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onShuffleClicked() {
|
public void onShuffleClicked() {
|
||||||
super.onShuffleClicked();
|
super.onShuffleClicked();
|
||||||
updatePlaybackButtons();
|
updatePlaybackButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Playback Listener
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
protected void onMetadataChanged(@NonNull final PlayQueueItem item,
|
||||||
|
@Nullable final StreamInfo info,
|
||||||
|
final int newPlayQueueIndex,
|
||||||
|
final boolean hasPlayQueueItemChanged) {
|
||||||
|
super.onMetadataChanged(item, info, newPlayQueueIndex, false);
|
||||||
|
|
||||||
|
titleTextView.setText(getVideoTitle());
|
||||||
|
channelTextView.setText(getUploaderName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlaybackShutdown() {
|
||||||
|
super.onPlaybackShutdown();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Player Overrides
|
// Player Overrides
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -535,7 +535,8 @@ public final class PopupVideoPlayer extends Service {
|
|||||||
|
|
||||||
private void updatePlayback() {
|
private void updatePlayback() {
|
||||||
if (activityListener != null && simpleExoPlayer != null && playQueue != null) {
|
if (activityListener != null && simpleExoPlayer != null && playQueue != null) {
|
||||||
activityListener.onPlaybackUpdate(currentState, getRepeatMode(), playQueue.isShuffled(), simpleExoPlayer.getPlaybackParameters());
|
activityListener.onPlaybackUpdate(currentState, getRepeatMode(),
|
||||||
|
playQueue.isShuffled(), simpleExoPlayer.getPlaybackParameters());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -574,16 +575,17 @@ public final class PopupVideoPlayer extends Service {
|
|||||||
// Playback Listener
|
// Playback Listener
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
protected void onMetadataChanged(@NonNull final PlayQueueItem item,
|
||||||
public void sync(@NonNull PlayQueueItem item, @Nullable StreamInfo info) {
|
@Nullable final StreamInfo info,
|
||||||
if (currentItem == item && currentInfo == info) return;
|
final int newPlayQueueIndex,
|
||||||
super.sync(item, info);
|
final boolean hasPlayQueueItemChanged) {
|
||||||
|
super.onMetadataChanged(item, info, newPlayQueueIndex, false);
|
||||||
updateMetadata();
|
updateMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void onPlaybackShutdown() {
|
||||||
super.shutdown();
|
super.onPlaybackShutdown();
|
||||||
onClose();
|
onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,9 +324,10 @@ public abstract class VideoPlayer extends BasePlayer
|
|||||||
|
|
||||||
protected abstract int getOverrideResolutionIndex(final List<VideoStream> sortedVideos, final String playbackQuality);
|
protected abstract int getOverrideResolutionIndex(final List<VideoStream> sortedVideos, final String playbackQuality);
|
||||||
|
|
||||||
@Override
|
protected void onMetadataChanged(@NonNull final PlayQueueItem item,
|
||||||
public void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info) {
|
@Nullable final StreamInfo info,
|
||||||
super.sync(item, info);
|
final int newPlayQueueIndex,
|
||||||
|
final boolean hasPlayQueueItemChanged) {
|
||||||
qualityTextView.setVisibility(View.GONE);
|
qualityTextView.setVisibility(View.GONE);
|
||||||
playbackSpeedTextView.setVisibility(View.GONE);
|
playbackSpeedTextView.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import android.content.SharedPreferences;
|
|||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.SeekParameters;
|
||||||
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
|
|
||||||
@ -109,6 +110,12 @@ public class PlayerHelper {
|
|||||||
return isRememberingPopupDimensions(context, true);
|
return isRememberingPopupDimensions(context, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static SeekParameters getSeekParameters(@NonNull final Context context) {
|
||||||
|
return isUsingInexactSeek(context, false) ?
|
||||||
|
SeekParameters.CLOSEST_SYNC : SeekParameters.EXACT;
|
||||||
|
}
|
||||||
|
|
||||||
public static long getPreferredCacheSize(@NonNull final Context context) {
|
public static long getPreferredCacheSize(@NonNull final Context context) {
|
||||||
return 64 * 1024 * 1024L;
|
return 64 * 1024 * 1024L;
|
||||||
}
|
}
|
||||||
@ -176,4 +183,8 @@ public class PlayerHelper {
|
|||||||
private static boolean isRememberingPopupDimensions(@Nonnull final Context context, final boolean b) {
|
private static boolean isRememberingPopupDimensions(@Nonnull final Context context, final boolean b) {
|
||||||
return getPreferences(context).getBoolean(context.getString(R.string.popup_remember_size_pos_key), b);
|
return getPreferences(context).getBoolean(context.getString(R.string.popup_remember_size_pos_key), b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isUsingInexactSeek(@NonNull final Context context, final boolean b) {
|
||||||
|
return getPreferences(context).getBoolean(context.getString(R.string.use_inexact_seek_key), b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ public class MediaSourceManager {
|
|||||||
* streams before will only be cached for future usage.
|
* streams before will only be cached for future usage.
|
||||||
*
|
*
|
||||||
* @see #onMediaSourceReceived(PlayQueueItem, ManagedMediaSource)
|
* @see #onMediaSourceReceived(PlayQueueItem, ManagedMediaSource)
|
||||||
* @see #update(int, MediaSource)
|
* @see #update(int, MediaSource, Runnable)
|
||||||
* */
|
* */
|
||||||
private final static int WINDOW_SIZE = 1;
|
private final static int WINDOW_SIZE = 1;
|
||||||
|
|
||||||
@ -95,15 +95,13 @@ public class MediaSourceManager {
|
|||||||
* */
|
* */
|
||||||
private final static int MAXIMUM_LOADER_SIZE = WINDOW_SIZE * 2 + 1;
|
private final static int MAXIMUM_LOADER_SIZE = WINDOW_SIZE * 2 + 1;
|
||||||
@NonNull private final CompositeDisposable loaderReactor;
|
@NonNull private final CompositeDisposable loaderReactor;
|
||||||
@NonNull private Set<PlayQueueItem> loadingItems;
|
@NonNull private final Set<PlayQueueItem> loadingItems;
|
||||||
@NonNull private final SerialDisposable syncReactor;
|
@NonNull private final SerialDisposable syncReactor;
|
||||||
|
|
||||||
@NonNull private final AtomicBoolean isBlocked;
|
@NonNull private final AtomicBoolean isBlocked;
|
||||||
|
|
||||||
@NonNull private DynamicConcatenatingMediaSource sources;
|
@NonNull private DynamicConcatenatingMediaSource sources;
|
||||||
|
|
||||||
@Nullable private PlayQueueItem syncedItem;
|
|
||||||
|
|
||||||
public MediaSourceManager(@NonNull final PlaybackListener listener,
|
public MediaSourceManager(@NonNull final PlaybackListener listener,
|
||||||
@NonNull final PlayQueue playQueue) {
|
@NonNull final PlayQueue playQueue) {
|
||||||
this(listener, playQueue,
|
this(listener, playQueue,
|
||||||
@ -159,8 +157,6 @@ public class MediaSourceManager {
|
|||||||
loaderReactor.dispose();
|
loaderReactor.dispose();
|
||||||
syncReactor.dispose();
|
syncReactor.dispose();
|
||||||
sources.releaseSource();
|
sources.releaseSource();
|
||||||
|
|
||||||
syncedItem = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -182,9 +178,7 @@ public class MediaSourceManager {
|
|||||||
public void reset() {
|
public void reset() {
|
||||||
if (DEBUG) Log.d(TAG, "reset() called.");
|
if (DEBUG) Log.d(TAG, "reset() called.");
|
||||||
|
|
||||||
tryBlock();
|
maybeBlock();
|
||||||
|
|
||||||
syncedItem = null;
|
|
||||||
populateSources();
|
populateSources();
|
||||||
}
|
}
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
@ -215,7 +209,7 @@ public class MediaSourceManager {
|
|||||||
|
|
||||||
private void onPlayQueueChanged(final PlayQueueEvent event) {
|
private void onPlayQueueChanged(final PlayQueueEvent event) {
|
||||||
if (playQueue.isEmpty() && playQueue.isComplete()) {
|
if (playQueue.isEmpty() && playQueue.isComplete()) {
|
||||||
playbackListener.shutdown();
|
playbackListener.onPlaybackShutdown();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,7 +255,7 @@ public class MediaSourceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isPlayQueueReady()) {
|
if (!isPlayQueueReady()) {
|
||||||
tryBlock();
|
maybeBlock();
|
||||||
playQueue.fetch();
|
playQueue.fetch();
|
||||||
}
|
}
|
||||||
playQueueReactor.request(1);
|
playQueueReactor.request(1);
|
||||||
@ -290,23 +284,23 @@ public class MediaSourceManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryBlock() {
|
private void maybeBlock() {
|
||||||
if (DEBUG) Log.d(TAG, "tryBlock() called.");
|
if (DEBUG) Log.d(TAG, "maybeBlock() called.");
|
||||||
|
|
||||||
if (isBlocked.get()) return;
|
if (isBlocked.get()) return;
|
||||||
|
|
||||||
playbackListener.block();
|
playbackListener.onPlaybackBlock();
|
||||||
resetSources();
|
resetSources();
|
||||||
|
|
||||||
isBlocked.set(true);
|
isBlocked.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryUnblock() {
|
private void maybeUnblock() {
|
||||||
if (DEBUG) Log.d(TAG, "tryUnblock() called.");
|
if (DEBUG) Log.d(TAG, "maybeUnblock() called.");
|
||||||
|
|
||||||
if (isPlayQueueReady() && isPlaybackReady() && isBlocked.get()) {
|
if (isPlayQueueReady() && isPlaybackReady() && isBlocked.get()) {
|
||||||
isBlocked.set(false);
|
isBlocked.set(false);
|
||||||
playbackListener.unblock(sources);
|
playbackListener.onPlaybackUnblock(sources);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,8 +308,8 @@ public class MediaSourceManager {
|
|||||||
// Metadata Synchronization TODO: maybe this should be a separate manager
|
// Metadata Synchronization TODO: maybe this should be a separate manager
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private void sync() {
|
private void maybeSync() {
|
||||||
if (DEBUG) Log.d(TAG, "sync() called.");
|
if (DEBUG) Log.d(TAG, "onPlaybackSynchronize() called.");
|
||||||
|
|
||||||
final PlayQueueItem currentItem = playQueue.getItem();
|
final PlayQueueItem currentItem = playQueue.getItem();
|
||||||
if (isBlocked.get() || currentItem == null) return;
|
if (isBlocked.get() || currentItem == null) return;
|
||||||
@ -323,23 +317,25 @@ public class MediaSourceManager {
|
|||||||
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);
|
||||||
|
|
||||||
if (syncedItem != currentItem) {
|
final Disposable sync = currentItem.getStream()
|
||||||
syncedItem = currentItem;
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
final Disposable sync = currentItem.getStream()
|
.subscribe(onSuccess, onError);
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
syncReactor.set(sync);
|
||||||
.subscribe(onSuccess, onError);
|
|
||||||
syncReactor.set(sync);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void syncInternal(@NonNull final PlayQueueItem item,
|
private void syncInternal(@NonNull final PlayQueueItem item,
|
||||||
@Nullable final StreamInfo info) {
|
@Nullable final StreamInfo info) {
|
||||||
// Ensure the current item is up to date with the play queue
|
// Ensure the current item is up to date with the play queue
|
||||||
if (playQueue.getItem() == item && playQueue.getItem() == syncedItem) {
|
if (playQueue.getItem() == item) {
|
||||||
playbackListener.sync(item, info);
|
playbackListener.onPlaybackSynchronize(item, info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void maybeSynchronizePlayer() {
|
||||||
|
maybeUnblock();
|
||||||
|
maybeSync();
|
||||||
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// MediaSource Loading
|
// MediaSource Loading
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
@ -404,8 +400,7 @@ public class MediaSourceManager {
|
|||||||
loaderReactor.add(loader);
|
loaderReactor.add(loader);
|
||||||
}
|
}
|
||||||
|
|
||||||
tryUnblock();
|
maybeSynchronizePlayer();
|
||||||
sync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Single<ManagedMediaSource> getLoadedMediaSource(@NonNull final PlayQueueItem stream) {
|
private Single<ManagedMediaSource> getLoadedMediaSource(@NonNull final PlayQueueItem stream) {
|
||||||
@ -431,17 +426,15 @@ public class MediaSourceManager {
|
|||||||
if (DEBUG) Log.d(TAG, "MediaSource - Loaded: [" + item.getTitle() +
|
if (DEBUG) Log.d(TAG, "MediaSource - Loaded: [" + item.getTitle() +
|
||||||
"] with url: " + item.getUrl());
|
"] with url: " + item.getUrl());
|
||||||
|
|
||||||
|
loadingItems.remove(item);
|
||||||
|
|
||||||
final int itemIndex = playQueue.indexOf(item);
|
final int itemIndex = playQueue.indexOf(item);
|
||||||
// Only update the playlist timeline for items at the current index or after.
|
// Only update the playlist timeline for items at the current index or after.
|
||||||
if (itemIndex >= playQueue.getIndex() && isCorrectionNeeded(item)) {
|
if (itemIndex >= playQueue.getIndex() && isCorrectionNeeded(item)) {
|
||||||
if (DEBUG) Log.d(TAG, "MediaSource - Updating: [" + item.getTitle() +
|
if (DEBUG) Log.d(TAG, "MediaSource - Updating: [" + item.getTitle() +
|
||||||
"] with url: " + item.getUrl());
|
"] with url: " + item.getUrl());
|
||||||
update(itemIndex, mediaSource);
|
update(itemIndex, mediaSource, this::maybeSynchronizePlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadingItems.remove(item);
|
|
||||||
tryUnblock();
|
|
||||||
sync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -533,10 +526,11 @@ public class MediaSourceManager {
|
|||||||
* this will modify the playback timeline prior to the index and may cause desynchronization
|
* this will modify the playback timeline prior to the index and may cause desynchronization
|
||||||
* on the playing item between {@link PlayQueue} and {@link DynamicConcatenatingMediaSource}.
|
* on the playing item between {@link PlayQueue} and {@link DynamicConcatenatingMediaSource}.
|
||||||
* */
|
* */
|
||||||
private synchronized void update(final int index, @NonNull final MediaSource source) {
|
private synchronized void update(final int index, @NonNull final MediaSource source,
|
||||||
|
@Nullable final Runnable finalizingAction) {
|
||||||
if (index < 0 || index >= sources.getSize()) return;
|
if (index < 0 || index >= sources.getSize()) return;
|
||||||
|
|
||||||
sources.addMediaSource(index + 1, source, () ->
|
sources.addMediaSource(index + 1, source, () ->
|
||||||
sources.removeMediaSource(index));
|
sources.removeMediaSource(index, finalizingAction));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ public interface PlaybackListener {
|
|||||||
*
|
*
|
||||||
* May be called at any time.
|
* May be called at any time.
|
||||||
* */
|
* */
|
||||||
void block();
|
void onPlaybackBlock();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the stream at the current queue index is ready.
|
* Called when the stream at the current queue index is ready.
|
||||||
@ -26,18 +26,16 @@ public interface PlaybackListener {
|
|||||||
*
|
*
|
||||||
* May be called only when the player is blocked.
|
* May be called only when the player is blocked.
|
||||||
* */
|
* */
|
||||||
void unblock(final MediaSource mediaSource);
|
void onPlaybackUnblock(final MediaSource mediaSource);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the queue index is refreshed.
|
* Called when the queue index is refreshed.
|
||||||
* Signals to the listener to synchronize the player's window to the manager's
|
* Signals to the listener to synchronize the player's window to the manager's
|
||||||
* window.
|
* window.
|
||||||
*
|
*
|
||||||
* Occurs once only per play queue item change.
|
* May be called anytime at any amount once unblock is called.
|
||||||
*
|
|
||||||
* May be called only after unblock is called.
|
|
||||||
* */
|
* */
|
||||||
void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info);
|
void onPlaybackSynchronize(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests the listener to resolve a stream info into a media source
|
* Requests the listener to resolve a stream info into a media source
|
||||||
@ -55,5 +53,5 @@ public interface PlaybackListener {
|
|||||||
*
|
*
|
||||||
* May be called at any time.
|
* May be called at any time.
|
||||||
* */
|
* */
|
||||||
void shutdown();
|
void onPlaybackShutdown();
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
<string name="player_gesture_controls_key" translatable="false">player_gesture_controls</string>
|
<string name="player_gesture_controls_key" translatable="false">player_gesture_controls</string>
|
||||||
<string name="resume_on_audio_focus_gain_key" translatable="false">resume_on_audio_focus_gain</string>
|
<string name="resume_on_audio_focus_gain_key" translatable="false">resume_on_audio_focus_gain</string>
|
||||||
<string name="popup_remember_size_pos_key" translatable="false">popup_remember_size_pos_key</string>
|
<string name="popup_remember_size_pos_key" translatable="false">popup_remember_size_pos_key</string>
|
||||||
|
<string name="use_inexact_seek_key" translatable="false">use_inexact_seek_key</string>
|
||||||
|
|
||||||
<string name="default_resolution_key" translatable="false">default_resolution</string>
|
<string name="default_resolution_key" translatable="false">default_resolution</string>
|
||||||
<string name="default_resolution_value" translatable="false">360p</string>
|
<string name="default_resolution_value" translatable="false">360p</string>
|
||||||
|
@ -72,6 +72,8 @@
|
|||||||
<string name="black_theme_title">Black</string>
|
<string name="black_theme_title">Black</string>
|
||||||
<string name="popup_remember_size_pos_title">Remember popup size and position</string>
|
<string name="popup_remember_size_pos_title">Remember popup size and position</string>
|
||||||
<string name="popup_remember_size_pos_summary">Remember last size and position of popup</string>
|
<string name="popup_remember_size_pos_summary">Remember last size and position of popup</string>
|
||||||
|
<string name="use_inexact_seek_title">Use fast inexact seek</string>
|
||||||
|
<string name="use_inexact_seek_summary">Inexact seek allows the player to seek to positions faster with reduced precision</string>
|
||||||
<string name="player_gesture_controls_title">Player gesture controls</string>
|
<string name="player_gesture_controls_title">Player gesture controls</string>
|
||||||
<string name="player_gesture_controls_summary">Use gestures to control the brightness and volume of the player</string>
|
<string name="player_gesture_controls_summary">Use gestures to control the brightness and volume of the player</string>
|
||||||
<string name="show_search_suggestions_title">Search suggestions</string>
|
<string name="show_search_suggestions_title">Search suggestions</string>
|
||||||
|
@ -100,5 +100,10 @@
|
|||||||
android:summary="@string/popup_remember_size_pos_summary"
|
android:summary="@string/popup_remember_size_pos_summary"
|
||||||
android:title="@string/popup_remember_size_pos_title"/>
|
android:title="@string/popup_remember_size_pos_title"/>
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="@string/use_inexact_seek_key"
|
||||||
|
android:summary="@string/use_inexact_seek_summary"
|
||||||
|
android:title="@string/use_inexact_seek_title"/>
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user