-Added debouncing to index change reactor.

-Fixed repeat mode on background notification.
This commit is contained in:
John Zhen M 2017-09-05 23:49:00 -07:00 committed by John Zhen Mo
parent 7e51695afa
commit fde3a37933
10 changed files with 147 additions and 139 deletions

View file

@ -311,7 +311,7 @@ public class BackgroundPlayer extends Service {
super.onRepeatClicked();
int opacity = 255;
switch (currentRepeatMode) {
switch (simpleExoPlayer.getRepeatMode()) {
case Player.REPEAT_MODE_OFF:
opacity = 77;
break;
@ -338,17 +338,17 @@ public class BackgroundPlayer extends Service {
@Override
public void onFastRewind() {
if (isPlayerBuffering()) return;
if (!isPlayerReady()) return;
playQueue.setIndex(playQueue.getIndex() - 1);
playQueue.offsetIndex(-1);
triggerProgressUpdate();
}
@Override
public void onFastForward() {
if (isPlayerBuffering()) return;
if (!isPlayerReady()) return;
playQueue.setIndex(playQueue.getIndex() + 1);
playQueue.offsetIndex(+1);
triggerProgressUpdate();
}

View file

@ -74,6 +74,8 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.player.playback.PlaybackManager;
import org.schabi.newpipe.player.playback.PlaybackListener;
import org.schabi.newpipe.playlist.ExternalPlayQueue;
import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.PlayQueueItem;
@ -96,10 +98,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public abstract class BasePlayer implements Player.EventListener,
AudioManager.OnAudioFocusChangeListener, MediaSourceManager.PlaybackListener {
AudioManager.OnAudioFocusChangeListener, PlaybackListener {
// TODO: Check api version for deprecated audio manager methods
public static final boolean DEBUG = true;
public static final boolean DEBUG = false;
public static final String TAG = "BasePlayer";
protected Context context;
@ -139,7 +141,7 @@ public abstract class BasePlayer implements Player.EventListener,
// Playlist
//////////////////////////////////////////////////////////////////////////*/
protected MediaSourceManager playbackManager;
protected PlaybackManager playbackManager;
protected PlayQueue playQueue;
protected int restoreQueueIndex;
@ -270,7 +272,7 @@ public abstract class BasePlayer implements Player.EventListener,
playQueue = new ExternalPlayQueue(serviceId, nextPageUrl, info, index);
playQueue.init();
playbackManager = new MediaSourceManager(this, playQueue);
playbackManager = new PlaybackManager(this, playQueue);
}
@SuppressWarnings("unchecked")
@ -281,7 +283,7 @@ public abstract class BasePlayer implements Player.EventListener,
playQueue = new SinglePlayQueue((StreamInfo) serializable, PlayQueueItem.DEFAULT_QUALITY);
playQueue.init();
playbackManager = new MediaSourceManager(this, playQueue);
playbackManager = new PlaybackManager(this, playQueue);
}
public void initThumbnail() {
@ -494,9 +496,6 @@ public abstract class BasePlayer implements Player.EventListener,
// Repeat
//////////////////////////////////////////////////////////////////////////*/
protected int currentRepeatMode = Player.REPEAT_MODE_OFF;
public void onRepeatClicked() {
if (DEBUG) Log.d(TAG, "onRepeatClicked() called");
@ -569,6 +568,10 @@ public abstract class BasePlayer implements Player.EventListener,
changeState(playWhenReady ? STATE_PLAYING : STATE_PAUSED);
break;
case Player.STATE_ENDED: // 4
if (playQueue.getIndex() < playQueue.size() - 1) {
playQueue.offsetIndex(+1);
break;
}
changeState(STATE_COMPLETED);
isPrepared = false;
break;
@ -589,9 +592,7 @@ public abstract class BasePlayer implements Player.EventListener,
int newIndex = simpleExoPlayer.getCurrentWindowIndex();
if (DEBUG) Log.d(TAG, "onPositionDiscontinuity() called with: index = [" + newIndex + "]");
if (newIndex == playbackManager.getCurrentSourceIndex() + 1) {
playbackManager.refresh(newIndex);
}
playbackManager.refresh(newIndex);
}
/*//////////////////////////////////////////////////////////////////////////
@ -601,7 +602,7 @@ public abstract class BasePlayer implements Player.EventListener,
@Override
public void block() {
if (simpleExoPlayer == null) return;
Log.d(TAG, "Blocking...");
if (DEBUG) Log.d(TAG, "Blocking...");
simpleExoPlayer.stop();
@ -611,7 +612,7 @@ public abstract class BasePlayer implements Player.EventListener,
@Override
public void unblock() {
if (simpleExoPlayer == null) return;
Log.d(TAG, "Unblocking...");
if (DEBUG) Log.d(TAG, "Unblocking...");
if (restoreQueueIndex != playQueue.getIndex()) {
restoreQueueIndex = playQueue.getIndex();
@ -626,7 +627,7 @@ public abstract class BasePlayer implements Player.EventListener,
@Override
public void sync(final StreamInfo info, final int sortedStreamsIndex) {
if (simpleExoPlayer == null) return;
Log.d(TAG, "Syncing...");
if (DEBUG) Log.d(TAG, "Syncing...");
videoUrl = info.url;
videoThumbnailUrl = info.thumbnail_url;
@ -635,11 +636,11 @@ public abstract class BasePlayer implements Player.EventListener,
initThumbnail();
if (simpleExoPlayer.getCurrentWindowIndex() != playbackManager.getCurrentSourceIndex()) {
Log.w(TAG, "Rewinding to correct window");
if (DEBUG) Log.w(TAG, "Rewinding to correct window");
if (simpleExoPlayer.getCurrentTimeline().getWindowCount() > playbackManager.getCurrentSourceIndex()) {
simpleExoPlayer.seekToDefaultPosition(playbackManager.getCurrentSourceIndex());
} else {
Toast.makeText(context, "Player out of sync", Toast.LENGTH_SHORT).show();
Toast.makeText(context, "Play Queue out of sync", Toast.LENGTH_SHORT).show();
simpleExoPlayer.seekToDefaultPosition();
}
}
@ -649,7 +650,7 @@ public abstract class BasePlayer implements Player.EventListener,
@Override
public void shutdown() {
Log.d(TAG, "Shutting down...");
if (DEBUG) Log.d(TAG, "Shutting down...");
playbackManager.dispose();
playQueue.dispose();
@ -898,7 +899,7 @@ public abstract class BasePlayer implements Player.EventListener,
return playQueue;
}
public boolean isPlayerBuffering() {
return currentState == STATE_BUFFERING;
public boolean isPlayerReady() {
return currentState == STATE_PLAYING || currentState == STATE_COMPLETED || currentState == STATE_PAUSED;
}
}

View file

@ -471,13 +471,13 @@ public class MainVideoPlayer extends Activity {
public boolean onDoubleTap(MotionEvent e) {
if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY());
//if (!playerImpl.isPlaying()) return false;
if (playerImpl.isPlayerBuffering()) return false;
if (!playerImpl.isPlayerReady()) return false;
if (e.getX() > playerImpl.getRootView().getWidth() / 2)
playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() + 1);
playerImpl.playQueue.offsetIndex(+1);
//playerImpl.onFastForward();
else
playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() - 1);
playerImpl.playQueue.offsetIndex(-1);
//playerImpl.onFastRewind();
return true;

View file

@ -580,14 +580,14 @@ public class PopupVideoPlayer extends Service {
public boolean onDoubleTap(MotionEvent e) {
if (DEBUG)
Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY());
if (!playerImpl.isPlaying() || playerImpl.isPlayerBuffering()) return false;
if (!playerImpl.isPlaying() || !playerImpl.isPlayerReady()) return false;
if (e.getX() > popupWidth / 2) {
//playerImpl.onFastForward();
playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() + 1);
playerImpl.playQueue.offsetIndex(+1);
} else {
//playerImpl.onFastRewind();
playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() - 1);
playerImpl.playQueue.offsetIndex(-1);
}
return true;

View file

@ -57,6 +57,7 @@ import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.player.playback.PlaybackManager;
import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.PlayQueueItem;
import org.schabi.newpipe.playlist.SinglePlayQueue;
@ -66,7 +67,6 @@ import org.schabi.newpipe.util.ListHelper;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
@ -226,7 +226,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
playQueue = new SinglePlayQueue((StreamInfo) serializable, sortedStreamsIndex);
playQueue.init();
playbackManager = new MediaSourceManager(this, playQueue);
playbackManager = new PlaybackManager(this, playQueue);
}
@SuppressWarnings("unchecked")
@ -239,7 +239,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
playQueue = (PlayQueue) serializable;
playQueue.init();
playbackManager = new MediaSourceManager(this, playQueue);
playbackManager = new PlaybackManager(this, playQueue);
}
@Override

View file

@ -0,0 +1,37 @@
package org.schabi.newpipe.player.playback;
import com.google.android.exoplayer2.source.MediaSource;
import org.schabi.newpipe.extractor.stream.StreamInfo;
public interface PlaybackListener {
/*
* Called when the stream at the current queue index is not ready yet.
* Signals to the listener to block the player from playing anything.
* */
void block();
/*
* Called when the stream at the current queue index is ready.
* Signals to the listener to resume the player.
* May be called at any time, even when the player is unblocked.
* */
void unblock();
/*
* Called when the queue index is refreshed.
* Signals to the listener to synchronize the player's window to the manager's
* window.
*
* CAN ONLY BE CALLED ONCE UNBLOCKED!
* */
void sync(final StreamInfo info, final int sortedStreamsIndex);
/*
* Requests the listener to resolve a stream info into a media source respective
* of the listener's implementation (background, popup or main video player),
* */
MediaSource sourceOf(final StreamInfo info, final int sortedStreamsIndex);
void shutdown();
}

View file

@ -1,4 +1,4 @@
package org.schabi.newpipe.player;
package org.schabi.newpipe.player.playback;
import android.support.annotation.Nullable;
@ -25,11 +25,11 @@ import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
class MediaSourceManager {
private final String TAG = "MediaSourceManager@" + Integer.toHexString(hashCode());
public class PlaybackManager {
private final String TAG = "PlaybackManager@" + Integer.toHexString(hashCode());
// One-side rolling window size for default loading
// Effectively loads WINDOW_SIZE * 2 streams
private static final int WINDOW_SIZE = 3;
private static final int WINDOW_SIZE = 2;
private final PlaybackListener playbackListener;
private final PlayQueue playQueue;
@ -41,43 +41,13 @@ class MediaSourceManager {
private List<Integer> sourceToQueueIndex;
private Subscription playQueueReactor;
private Subscription loadingReactor;
private Disposable syncReactor;
private CompositeDisposable disposables;
private boolean isBlocked;
interface PlaybackListener {
/*
* Called when the stream at the current queue index is not ready yet.
* Signals to the listener to block the player from playing anything.
* */
void block();
/*
* Called when the stream at the current queue index is ready.
* Signals to the listener to resume the player.
* May be called at any time, even when the player is unblocked.
* */
void unblock();
/*
* Called when the queue index is refreshed.
* Signals to the listener to synchronize the player's window to the manager's
* window.
* */
void sync(final StreamInfo info, final int sortedStreamsIndex);
/*
* Requests the listener to resolve a stream info into a media source respective
* of the listener's implementation (background, popup or main video player),
* */
MediaSource sourceOf(final StreamInfo info, final int sortedStreamsIndex);
void shutdown();
}
MediaSourceManager(@NonNull final MediaSourceManager.PlaybackListener listener,
@NonNull final PlayQueue playQueue) {
public PlaybackManager(@NonNull final PlaybackListener listener,
@NonNull final PlayQueue playQueue) {
this.playbackListener = listener;
this.playQueue = playQueue;
@ -98,25 +68,28 @@ class MediaSourceManager {
/*
* Returns the media source index of the currently playing stream.
* */
int getCurrentSourceIndex() {
public int getCurrentSourceIndex() {
return sourceToQueueIndex.indexOf(playQueue.getIndex());
}
@NonNull
DynamicConcatenatingMediaSource getMediaSource() {
public DynamicConcatenatingMediaSource getMediaSource() {
return sources;
}
/*
* Called when the player has transitioned to another stream.
* */
void refresh(final int newSourceIndex) {
if (sourceToQueueIndex.indexOf(newSourceIndex) != -1) {
playQueue.setIndex(sourceToQueueIndex.indexOf(newSourceIndex));
public void refresh(final int newSourceIndex) {
if (sourceToQueueIndex.indexOf(newSourceIndex) != -1 && newSourceIndex == getCurrentSourceIndex() + 1) {
playQueue.offsetIndex(+1);
// free up some memory
if (sourceToQueueIndex.size() > 1) remove(sourceToQueueIndex.get(0));
}
}
void report(final Exception error) {
public void report(final Exception error) {
// ignore error checking for now, just remove the current index
if (error == null || !tryBlock()) return;
@ -127,27 +100,28 @@ class MediaSourceManager {
load();
}
int queueIndexOf(final int sourceIndex) {
return sourceIndex < sourceToQueueIndex.size() ? sourceToQueueIndex.get(sourceIndex) : -1;
}
void updateCurrent(final int newSortedStreamsIndex) {
public void updateCurrent(final int newSortedStreamsIndex) {
if (!tryBlock()) return;
PlayQueueItem item = playQueue.getCurrent();
item.setSortedQualityIndex(newSortedStreamsIndex);
resetSources();
load();
}
void dispose() {
if (loadingReactor != null) loadingReactor.cancel();
public void dispose() {
if (playQueueReactor != null) playQueueReactor.cancel();
if (disposables != null) disposables.dispose();
if (syncReactor != null) syncReactor.dispose();
if (sources != null) sources.releaseSource();
if (sourceToQueueIndex != null) sourceToQueueIndex.clear();
loadingReactor = null;
playQueueReactor = null;
disposables = null;
syncReactor = null;
sources = null;
sourceToQueueIndex = null;
}
/*//////////////////////////////////////////////////////////////////////////
@ -182,8 +156,8 @@ class MediaSourceManager {
case SWAP:
final SwapEvent swapEvent = (SwapEvent) event;
swap(swapEvent.getFrom(), swapEvent.getTo());
load();
break;
case NEXT:
default:
break;
}
@ -240,14 +214,18 @@ class MediaSourceManager {
/*
* Responds to a SELECT event.
* When a change occur, the manager prepares by loading more.
* If the current item has not been fully loaded,
* If the selected item is already loaded, then we simply synchronize and
* start loading some more items.
*
* If the current item has not been fully loaded, then the player will be
* blocked. The sources will be reset and reloaded, to conserve memory.
* */
private void onSelect() {
if (isCurrentIndexLoaded()) {
if (isCurrentIndexLoaded() && !isBlocked) {
sync();
} else {
tryBlock();
resetSources();
}
load();
@ -256,14 +234,14 @@ class MediaSourceManager {
private void sync() {
final PlayQueueItem currentItem = playQueue.getCurrent();
final Consumer<StreamInfo> onSuccess = new Consumer<StreamInfo>() {
final Consumer<StreamInfo> syncPlayback = new Consumer<StreamInfo>() {
@Override
public void accept(StreamInfo streamInfo) throws Exception {
playbackListener.sync(streamInfo, currentItem.getSortedQualityIndex());
}
};
currentItem.getStream().subscribe(onSuccess);
currentItem.getStream().subscribe(syncPlayback);
}
private void load() {
@ -287,11 +265,13 @@ class MediaSourceManager {
item.getStream().subscribe(new SingleObserver<StreamInfo>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
if (disposables != null) {
disposables.add(d);
} else {
if (disposables == null) {
d.dispose();
return;
}
if (disposables.size() > 8) disposables.clear();
disposables.add(d);
}
@Override
@ -321,16 +301,6 @@ class MediaSourceManager {
// Media Source List Manipulation
//////////////////////////////////////////////////////////////////////////*/
private void reset(final int queueIndex) {
if (queueIndex < 0) return;
final int sourceIndex = sourceToQueueIndex.indexOf(queueIndex);
if (sourceIndex != -1) {
sourceToQueueIndex.remove(sourceIndex);
sources.removeMediaSource(sourceIndex);
}
}
// Insert source into playlist with position in respect to the play queue
// If the play queue index already exists, then the insert is ignored
private void insert(final int queueIndex, final MediaSource source) {