-Refactored Playback manager to expose only readonly methods.
-Removed swap and move operations. -Code clean up.
This commit is contained in:
parent
9413856463
commit
f8abf92a66
12 changed files with 128 additions and 226 deletions
|
@ -382,9 +382,6 @@ public class BackgroundPlayer extends Service {
|
||||||
public void sync(final StreamInfo info, final int sortedStreamsIndex) {
|
public void sync(final StreamInfo info, final int sortedStreamsIndex) {
|
||||||
super.sync(info, sortedStreamsIndex);
|
super.sync(info, sortedStreamsIndex);
|
||||||
|
|
||||||
setVideoTitle(info.name);
|
|
||||||
setUploaderName(info.uploader_name);
|
|
||||||
|
|
||||||
notRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle());
|
notRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle());
|
||||||
notRemoteView.setTextViewText(R.id.notificationArtist, getUploaderName());
|
notRemoteView.setTextViewText(R.id.notificationArtist, getUploaderName());
|
||||||
bigNotRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle());
|
bigNotRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle());
|
||||||
|
|
|
@ -93,7 +93,6 @@ import io.reactivex.Observable;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.annotations.NonNull;
|
import io.reactivex.annotations.NonNull;
|
||||||
import io.reactivex.disposables.Disposable;
|
import io.reactivex.disposables.Disposable;
|
||||||
import io.reactivex.disposables.SerialDisposable;
|
|
||||||
import io.reactivex.functions.Consumer;
|
import io.reactivex.functions.Consumer;
|
||||||
import io.reactivex.functions.Predicate;
|
import io.reactivex.functions.Predicate;
|
||||||
|
|
||||||
|
@ -136,11 +135,6 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
public static final String RESTORE_QUEUE_INDEX = "restore_queue_index";
|
public static final String RESTORE_QUEUE_INDEX = "restore_queue_index";
|
||||||
public static final String RESTORE_WINDOW_POS = "restore_window_pos";
|
public static final String RESTORE_WINDOW_POS = "restore_window_pos";
|
||||||
|
|
||||||
protected String videoUrl = "";
|
|
||||||
protected String videoTitle = "";
|
|
||||||
protected String videoThumbnailUrl = "";
|
|
||||||
protected String uploaderName = "";
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Playlist
|
// Playlist
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -148,8 +142,11 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
protected PlaybackManager playbackManager;
|
protected PlaybackManager playbackManager;
|
||||||
protected PlayQueue playQueue;
|
protected PlayQueue playQueue;
|
||||||
|
|
||||||
protected int queueStartPos = 0;
|
private boolean isRecovery = false;
|
||||||
protected long videoStartPos = -1;
|
private int queuePos = 0;
|
||||||
|
private long videoPos = -1;
|
||||||
|
|
||||||
|
protected StreamInfo currentInfo;
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Player
|
// Player
|
||||||
|
@ -246,8 +243,10 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
|
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
|
||||||
if (intent == null) return;
|
if (intent == null) return;
|
||||||
|
|
||||||
queueStartPos = intent.getIntExtra(RESTORE_QUEUE_INDEX, 0);
|
setRecovery(
|
||||||
videoStartPos = intent.getLongExtra(START_POSITION, 0);
|
intent.getIntExtra(RESTORE_QUEUE_INDEX, 0),
|
||||||
|
intent.getLongExtra(START_POSITION, 0)
|
||||||
|
);
|
||||||
setPlaybackSpeed(intent.getFloatExtra(PLAYBACK_SPEED, getPlaybackSpeed()));
|
setPlaybackSpeed(intent.getFloatExtra(PLAYBACK_SPEED, getPlaybackSpeed()));
|
||||||
|
|
||||||
switch (intent.getStringExtra(INTENT_TYPE)) {
|
switch (intent.getStringExtra(INTENT_TYPE)) {
|
||||||
|
@ -535,21 +534,37 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
public void onTimelineChanged(Timeline timeline, Object manifest) {
|
public void onTimelineChanged(Timeline timeline, Object manifest) {
|
||||||
if (DEBUG) Log.d(TAG, "onTimelineChanged(), timeline size = " + timeline.getWindowCount());
|
if (DEBUG) Log.d(TAG, "onTimelineChanged(), timeline size = " + timeline.getWindowCount());
|
||||||
|
|
||||||
if (simpleExoPlayer.getCurrentWindowIndex() != playbackManager.getCurrentSourceIndex()) {
|
final int currentSourceIndex = playbackManager.getCurrentSourceIndex();
|
||||||
if (timeline.getWindowCount() > playbackManager.getCurrentSourceIndex()) {
|
|
||||||
if (DEBUG) Log.d(TAG, "Rewinding to correct window");
|
// Check timeline has window
|
||||||
simpleExoPlayer.seekToDefaultPosition(playbackManager.getCurrentSourceIndex());
|
if (simpleExoPlayer.getCurrentTimeline().getWindowCount() <= currentSourceIndex) return;
|
||||||
}
|
|
||||||
|
// Check if window is ready
|
||||||
|
Timeline.Window window = new Timeline.Window();
|
||||||
|
simpleExoPlayer.getCurrentTimeline().getWindow(currentSourceIndex, window);
|
||||||
|
if (window.isDynamic) return;
|
||||||
|
|
||||||
|
// Check if already playing correct window
|
||||||
|
final boolean isCurrentWindowCorrect = simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex;
|
||||||
|
if (isCurrentWindowCorrect && getCurrentState() == STATE_PLAYING) return;
|
||||||
|
|
||||||
|
// Check if recovering on correct item
|
||||||
|
if (isRecovery && queuePos == playQueue.getIndex() && isCurrentWindowCorrect) {
|
||||||
|
if (DEBUG) Log.d(TAG, "Rewinding to recovery window: " + currentSourceIndex + " at: " + getTimeString((int)videoPos));
|
||||||
|
simpleExoPlayer.seekTo(currentSourceIndex, videoPos);
|
||||||
|
isRecovery = false;
|
||||||
|
} else if (!isCurrentWindowCorrect) { // Or if on wrong window
|
||||||
|
final long startPos = currentInfo != null ? currentInfo.start_position : 0;
|
||||||
|
if (DEBUG) Log.d(TAG, "Rewinding to correct window: " + currentSourceIndex + " at: " + getTimeString((int)startPos));
|
||||||
|
simpleExoPlayer.seekTo(currentSourceIndex, startPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!simpleExoPlayer.isCurrentWindowDynamic() && simpleExoPlayer.isCurrentWindowSeekable()) {
|
// Good to go...
|
||||||
simpleExoPlayer.setPlayWhenReady(true);
|
simpleExoPlayer.setPlayWhenReady(true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||||
Log.w(TAG, "onTracksChanged() called, unsupported operation. Is this expected?");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -604,8 +619,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
@Override
|
@Override
|
||||||
public void onPlayerError(ExoPlaybackException error) {
|
public void onPlayerError(ExoPlaybackException error) {
|
||||||
if (DEBUG) Log.d(TAG, "onPlayerError() called with: error = [" + error + "]");
|
if (DEBUG) Log.d(TAG, "onPlayerError() called with: error = [" + error + "]");
|
||||||
playbackManager.report(error);
|
playQueue.remove(playQueue.getIndex());
|
||||||
|
|
||||||
onError(error);
|
onError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -615,7 +629,9 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
int newIndex = simpleExoPlayer.getCurrentWindowIndex();
|
int newIndex = simpleExoPlayer.getCurrentWindowIndex();
|
||||||
if (DEBUG) Log.d(TAG, "onPositionDiscontinuity() called with: index = [" + newIndex + "]");
|
if (DEBUG) Log.d(TAG, "onPositionDiscontinuity() called with: index = [" + newIndex + "]");
|
||||||
|
|
||||||
playbackManager.refresh(newIndex);
|
if (newIndex == playbackManager.getCurrentSourceIndex() + 1) {
|
||||||
|
playQueue.offsetIndex(+1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -633,6 +649,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
if (DEBUG) Log.d(TAG, "Blocking...");
|
if (DEBUG) Log.d(TAG, "Blocking...");
|
||||||
|
|
||||||
simpleExoPlayer.stop();
|
simpleExoPlayer.stop();
|
||||||
|
isPrepared = false;
|
||||||
|
|
||||||
changeState(STATE_BUFFERING);
|
changeState(STATE_BUFFERING);
|
||||||
}
|
}
|
||||||
|
@ -642,13 +659,8 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
if (simpleExoPlayer == null) return;
|
if (simpleExoPlayer == null) return;
|
||||||
if (DEBUG) Log.d(TAG, "Unblocking...");
|
if (DEBUG) Log.d(TAG, "Unblocking...");
|
||||||
|
|
||||||
if (queueStartPos != playQueue.getIndex()) {
|
simpleExoPlayer.prepare(playbackManager.getMediaSource(), true, true);
|
||||||
queueStartPos = playQueue.getIndex();
|
isPrepared = true;
|
||||||
videoStartPos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
simpleExoPlayer.prepare(playbackManager.getMediaSource());
|
|
||||||
simpleExoPlayer.seekTo(playbackManager.getCurrentSourceIndex(), videoStartPos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -656,13 +668,10 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
if (simpleExoPlayer == null) return;
|
if (simpleExoPlayer == null) return;
|
||||||
if (DEBUG) Log.d(TAG, "Syncing...");
|
if (DEBUG) Log.d(TAG, "Syncing...");
|
||||||
|
|
||||||
videoUrl = info.url;
|
currentInfo = info;
|
||||||
videoThumbnailUrl = info.thumbnail_url;
|
|
||||||
videoTitle = info.name;
|
|
||||||
|
|
||||||
onTimelineChanged(simpleExoPlayer.getCurrentTimeline(), null);
|
onTimelineChanged(simpleExoPlayer.getCurrentTimeline(), null);
|
||||||
|
|
||||||
initThumbnail(videoThumbnailUrl);
|
initThumbnail(info.thumbnail_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -774,7 +783,11 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
}
|
}
|
||||||
|
|
||||||
public void triggerProgressUpdate() {
|
public void triggerProgressUpdate() {
|
||||||
onUpdateProgress((int) simpleExoPlayer.getCurrentPosition(), (int) simpleExoPlayer.getDuration(), simpleExoPlayer.getBufferedPercentage());
|
onUpdateProgress(
|
||||||
|
(int) simpleExoPlayer.getCurrentPosition(),
|
||||||
|
(int) simpleExoPlayer.getDuration(),
|
||||||
|
simpleExoPlayer.getBufferedPercentage()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void animateAudio(final float from, final float to, int duration) {
|
public void animateAudio(final float from, final float to, int duration) {
|
||||||
|
@ -823,35 +836,19 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getVideoUrl() {
|
public String getVideoUrl() {
|
||||||
return videoUrl;
|
return currentInfo.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVideoUrl(String videoUrl) {
|
public long getVideoPos() {
|
||||||
this.videoUrl = videoUrl;
|
return videoPos;
|
||||||
}
|
|
||||||
|
|
||||||
public long getVideoStartPos() {
|
|
||||||
return videoStartPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVideoStartPos(long videoStartPos) {
|
|
||||||
this.videoStartPos = videoStartPos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getVideoTitle() {
|
public String getVideoTitle() {
|
||||||
return videoTitle;
|
return currentInfo.name;
|
||||||
}
|
|
||||||
|
|
||||||
public void setVideoTitle(String videoTitle) {
|
|
||||||
this.videoTitle = videoTitle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUploaderName() {
|
public String getUploaderName() {
|
||||||
return uploaderName;
|
return currentInfo.uploader_name;
|
||||||
}
|
|
||||||
|
|
||||||
public void setUploaderName(String uploaderName) {
|
|
||||||
this.uploaderName = uploaderName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCompleted() {
|
public boolean isCompleted() {
|
||||||
|
@ -866,14 +863,6 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
isPrepared = prepared;
|
isPrepared = prepared;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getVideoThumbnailUrl() {
|
|
||||||
return videoThumbnailUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVideoThumbnailUrl(String videoThumbnailUrl) {
|
|
||||||
this.videoThumbnailUrl = videoThumbnailUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getPlaybackSpeed() {
|
public float getPlaybackSpeed() {
|
||||||
return simpleExoPlayer.getPlaybackParameters().speed;
|
return simpleExoPlayer.getPlaybackParameters().speed;
|
||||||
}
|
}
|
||||||
|
@ -897,4 +886,15 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
public boolean isProgressLoopRunning() {
|
public boolean isProgressLoopRunning() {
|
||||||
return progressUpdateReactor != null && !progressUpdateReactor.isDisposed();
|
return progressUpdateReactor != null && !progressUpdateReactor.isDisposed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getRecovery() {
|
||||||
|
return isRecovery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRecovery(final int queuePos, final long windowPos) {
|
||||||
|
if (DEBUG) Log.d(TAG, "Setting recovery, queue: " + queuePos + ", pos: " + windowPos);
|
||||||
|
this.isRecovery = true;
|
||||||
|
this.queuePos = queuePos;
|
||||||
|
this.videoPos = windowPos;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,10 @@ public class MainVideoPlayer extends Activity {
|
||||||
if (DEBUG) Log.d(TAG, "onStop() called");
|
if (DEBUG) Log.d(TAG, "onStop() called");
|
||||||
activityPaused = true;
|
activityPaused = true;
|
||||||
if (playerImpl.getPlayer() != null) {
|
if (playerImpl.getPlayer() != null) {
|
||||||
playerImpl.setVideoStartPos((int) playerImpl.getPlayer().getCurrentPosition());
|
playerImpl.setRecovery(
|
||||||
|
playerImpl.getCurrentQueueIndex(),
|
||||||
|
(int) playerImpl.getPlayer().getCurrentPosition()
|
||||||
|
);
|
||||||
playerImpl.destroyPlayer();
|
playerImpl.destroyPlayer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,13 +227,6 @@ public class MainVideoPlayer extends Activity {
|
||||||
screenRotationButton.setOnClickListener(this);
|
screenRotationButton.setOnClickListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleIntent(Intent intent) {
|
|
||||||
super.handleIntent(intent);
|
|
||||||
titleTextView.setText(getVideoTitle());
|
|
||||||
channelTextView.setText(getUploaderName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Playback Listener
|
// Playback Listener
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -263,7 +259,8 @@ public class MainVideoPlayer extends Activity {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.startService(NavigationHelper.getOpenVideoPlayerIntent(context, PopupVideoPlayer.class, this));
|
final Intent intent = NavigationHelper.getOpenVideoPlayerIntent(context, PopupVideoPlayer.class, this);
|
||||||
|
context.startService(intent);
|
||||||
destroyPlayer();
|
destroyPlayer();
|
||||||
|
|
||||||
((View) getControlAnimationView().getParent()).setVisibility(View.GONE);
|
((View) getControlAnimationView().getParent()).setVisibility(View.GONE);
|
||||||
|
|
|
@ -50,15 +50,11 @@ import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
|
||||||
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.BuildConfig;
|
import org.schabi.newpipe.BuildConfig;
|
||||||
import org.schabi.newpipe.MainActivity;
|
import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.ReCaptchaActivity;
|
import org.schabi.newpipe.ReCaptchaActivity;
|
||||||
import org.schabi.newpipe.extractor.MediaFormat;
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
||||||
|
@ -68,19 +64,16 @@ import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.player.old.PlayVideoActivity;
|
import org.schabi.newpipe.player.old.PlayVideoActivity;
|
||||||
import org.schabi.newpipe.player.playback.PlaybackManager;
|
import org.schabi.newpipe.player.playback.PlaybackManager;
|
||||||
import org.schabi.newpipe.playlist.PlayQueue;
|
|
||||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
import org.schabi.newpipe.report.UserAction;
|
import org.schabi.newpipe.report.UserAction;
|
||||||
import org.schabi.newpipe.util.Constants;
|
import org.schabi.newpipe.util.Constants;
|
||||||
import org.schabi.newpipe.util.ExtractorHelper;
|
import org.schabi.newpipe.util.ExtractorHelper;
|
||||||
import org.schabi.newpipe.util.ListHelper;
|
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.Disposable;
|
import io.reactivex.disposables.Disposable;
|
||||||
|
@ -745,9 +738,6 @@ public class PopupVideoPlayer extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onReceive(final StreamInfo info) {
|
public void onReceive(final StreamInfo info) {
|
||||||
if (info.start_position > 0) playerImpl.setVideoStartPos(info.start_position * 1000);
|
|
||||||
else playerImpl.setVideoStartPos(-1);
|
|
||||||
|
|
||||||
mainHandler.post(new Runnable() {
|
mainHandler.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
|
@ -234,8 +234,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||||
final Serializable serializable = intent.getSerializableExtra(PLAY_QUEUE);
|
final Serializable serializable = intent.getSerializableExtra(PLAY_QUEUE);
|
||||||
if (!(serializable instanceof PlayQueue)) return;
|
if (!(serializable instanceof PlayQueue)) return;
|
||||||
|
|
||||||
selectedIndexStream = intent.getIntExtra(INDEX_SEL_VIDEO_STREAM, -1);
|
|
||||||
|
|
||||||
playQueue = (PlayQueue) serializable;
|
playQueue = (PlayQueue) serializable;
|
||||||
playQueue.init();
|
playQueue.init();
|
||||||
|
|
||||||
|
@ -406,12 +404,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||||
public void onPrepared(boolean playWhenReady) {
|
public void onPrepared(boolean playWhenReady) {
|
||||||
if (DEBUG) Log.d(TAG, "onPrepared() called with: playWhenReady = [" + playWhenReady + "]");
|
if (DEBUG) Log.d(TAG, "onPrepared() called with: playWhenReady = [" + playWhenReady + "]");
|
||||||
|
|
||||||
if (videoStartPos > 0) {
|
|
||||||
playbackSeekBar.setProgress((int) videoStartPos);
|
|
||||||
playbackCurrentTime.setText(getTimeString((int) videoStartPos));
|
|
||||||
videoStartPos = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
playbackSeekBar.setMax((int) simpleExoPlayer.getDuration());
|
playbackSeekBar.setMax((int) simpleExoPlayer.getDuration());
|
||||||
playbackEndTime.setText(getTimeString((int) simpleExoPlayer.getDuration()));
|
playbackEndTime.setText(getTimeString((int) simpleExoPlayer.getDuration()));
|
||||||
playbackSpeed.setText(formatSpeed(getPlaybackSpeed()));
|
playbackSpeed.setText(formatSpeed(getPlaybackSpeed()));
|
||||||
|
@ -453,6 +445,8 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||||
|
|
||||||
protected void onFullScreenButtonClicked() {
|
protected void onFullScreenButtonClicked() {
|
||||||
if (!isPlayerReady()) return;
|
if (!isPlayerReady()) return;
|
||||||
|
|
||||||
|
changeState(STATE_BUFFERING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -492,11 +486,11 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||||
Log.d(TAG, "onMenuItemClick() called with: menuItem = [" + menuItem + "], menuItem.getItemId = [" + menuItem.getItemId() + "]");
|
Log.d(TAG, "onMenuItemClick() called with: menuItem = [" + menuItem + "], menuItem.getItemId = [" + menuItem.getItemId() + "]");
|
||||||
|
|
||||||
if (qualityPopupMenuGroupId == menuItem.getGroupId()) {
|
if (qualityPopupMenuGroupId == menuItem.getGroupId()) {
|
||||||
if (selectedIndexStream == menuItem.getItemId()) return true;
|
if (selectedIndexStream == menuItem.getItemId() || getRecovery()) return true;
|
||||||
|
|
||||||
queueStartPos = playQueue.getIndex();
|
final int index = playQueue.getIndex();
|
||||||
videoStartPos = simpleExoPlayer.getCurrentPosition();
|
setRecovery(index, simpleExoPlayer.getCurrentPosition());
|
||||||
playbackManager.updateCurrent(menuItem.getItemId());
|
playQueue.updateIndex(index, menuItem.getItemId());
|
||||||
|
|
||||||
qualityTextView.setText(menuItem.getTitle());
|
qualityTextView.setText(menuItem.getTitle());
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -12,7 +12,6 @@ import org.schabi.newpipe.playlist.PlayQueue;
|
||||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||||
import org.schabi.newpipe.playlist.events.PlayQueueMessage;
|
import org.schabi.newpipe.playlist.events.PlayQueueMessage;
|
||||||
import org.schabi.newpipe.playlist.events.RemoveEvent;
|
import org.schabi.newpipe.playlist.events.RemoveEvent;
|
||||||
import org.schabi.newpipe.playlist.events.MoveEvent;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -29,7 +28,7 @@ public class PlaybackManager {
|
||||||
private final String TAG = "PlaybackManager@" + Integer.toHexString(hashCode());
|
private final String TAG = "PlaybackManager@" + Integer.toHexString(hashCode());
|
||||||
// One-side rolling window size for default loading
|
// One-side rolling window size for default loading
|
||||||
// Effectively loads WINDOW_SIZE * 2 streams
|
// Effectively loads WINDOW_SIZE * 2 streams
|
||||||
private static final int WINDOW_SIZE = 2;
|
private static final int WINDOW_SIZE = 3;
|
||||||
|
|
||||||
private final PlaybackListener playbackListener;
|
private final PlaybackListener playbackListener;
|
||||||
private final PlayQueue playQueue;
|
private final PlayQueue playQueue;
|
||||||
|
@ -77,37 +76,6 @@ public class PlaybackManager {
|
||||||
return sources;
|
return sources;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Called when the player has transitioned to another stream.
|
|
||||||
* */
|
|
||||||
public void refresh(final int newSourceIndex) {
|
|
||||||
if (sourceToQueueIndex.indexOf(newSourceIndex) != -1 && newSourceIndex == getCurrentSourceIndex() + 1) {
|
|
||||||
playQueue.offsetIndex(+1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void report(final Exception error) {
|
|
||||||
// ignore error checking for now, just remove the current index
|
|
||||||
if (error == null) return;
|
|
||||||
|
|
||||||
tryBlock();
|
|
||||||
|
|
||||||
final int index = playQueue.getIndex();
|
|
||||||
playQueue.remove(index);
|
|
||||||
|
|
||||||
resetSources();
|
|
||||||
load();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateCurrent(final int newSortedStreamsIndex) {
|
|
||||||
tryBlock();
|
|
||||||
|
|
||||||
PlayQueueItem item = playQueue.getCurrent();
|
|
||||||
item.setSortedQualityIndex(newSortedStreamsIndex);
|
|
||||||
|
|
||||||
resetSources();
|
|
||||||
load();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
if (playQueueReactor != null) playQueueReactor.cancel();
|
if (playQueueReactor != null) playQueueReactor.cancel();
|
||||||
|
@ -146,14 +114,19 @@ public class PlaybackManager {
|
||||||
case APPEND:
|
case APPEND:
|
||||||
break;
|
break;
|
||||||
case SELECT:
|
case SELECT:
|
||||||
onSelect();
|
if (isBlocked) break;
|
||||||
|
if (isCurrentIndexLoaded()) sync(); else tryBlock();
|
||||||
break;
|
break;
|
||||||
case REMOVE:
|
case REMOVE:
|
||||||
final RemoveEvent removeEvent = (RemoveEvent) event;
|
final RemoveEvent removeEvent = (RemoveEvent) event;
|
||||||
|
if (removeEvent.isCurrent()) tryBlock();
|
||||||
remove(removeEvent.index());
|
remove(removeEvent.index());
|
||||||
break;
|
break;
|
||||||
case MOVE:
|
case UPDATE:
|
||||||
throw new UnsupportedOperationException("Move not yet supported");
|
case SHUFFLE:
|
||||||
|
tryBlock();
|
||||||
|
resetSources();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -208,30 +181,6 @@ public class PlaybackManager {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Responds to a SELECT event.
|
|
||||||
*
|
|
||||||
* If the player is being blocked, then nothing should happen.
|
|
||||||
*
|
|
||||||
* Otherwise:
|
|
||||||
*
|
|
||||||
* When the selected item is already loaded, then we simply synchronize and
|
|
||||||
* start loading some more items.
|
|
||||||
*
|
|
||||||
* When 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 (isBlocked) return;
|
|
||||||
|
|
||||||
if (isCurrentIndexLoaded()) {
|
|
||||||
sync();
|
|
||||||
} else {
|
|
||||||
tryBlock();
|
|
||||||
resetSources();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sync() {
|
private void sync() {
|
||||||
final PlayQueueItem currentItem = playQueue.getCurrent();
|
final PlayQueueItem currentItem = playQueue.getCurrent();
|
||||||
|
|
||||||
|
@ -319,13 +268,14 @@ public class PlaybackManager {
|
||||||
if (queueIndex < 0) return;
|
if (queueIndex < 0) return;
|
||||||
|
|
||||||
final int sourceIndex = sourceToQueueIndex.indexOf(queueIndex);
|
final int sourceIndex = sourceToQueueIndex.indexOf(queueIndex);
|
||||||
if (sourceIndex != -1) {
|
if (sourceIndex == -1) return;
|
||||||
sourceToQueueIndex.remove(sourceIndex);
|
|
||||||
sources.removeMediaSource(sourceIndex);
|
sourceToQueueIndex.remove(sourceIndex);
|
||||||
// Will be slow on really large arrays, fast enough for typical use case
|
sources.removeMediaSource(sourceIndex);
|
||||||
for (int i = sourceIndex; i < sourceToQueueIndex.size(); i++) {
|
|
||||||
sourceToQueueIndex.set(i, sourceToQueueIndex.get(i) - 1);
|
// Will be slow on really large arrays, fast enough for typical use case
|
||||||
}
|
for (int i = sourceIndex; i < sourceToQueueIndex.size(); i++) {
|
||||||
|
sourceToQueueIndex.set(i, sourceToQueueIndex.get(i) - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,14 +10,13 @@ import org.schabi.newpipe.playlist.events.InitEvent;
|
||||||
import org.schabi.newpipe.playlist.events.PlayQueueMessage;
|
import org.schabi.newpipe.playlist.events.PlayQueueMessage;
|
||||||
import org.schabi.newpipe.playlist.events.RemoveEvent;
|
import org.schabi.newpipe.playlist.events.RemoveEvent;
|
||||||
import org.schabi.newpipe.playlist.events.SelectEvent;
|
import org.schabi.newpipe.playlist.events.SelectEvent;
|
||||||
import org.schabi.newpipe.playlist.events.MoveEvent;
|
import org.schabi.newpipe.playlist.events.UpdateEvent;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import io.reactivex.BackpressureStrategy;
|
import io.reactivex.BackpressureStrategy;
|
||||||
|
@ -96,7 +95,7 @@ public abstract class PlayQueue implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public int indexOf(final PlayQueueItem item) {
|
public int indexOf(final PlayQueueItem item) {
|
||||||
// reference equality, can't think of a better way to do this
|
// referential equality, can't think of a better way to do this
|
||||||
// todo: better than this
|
// todo: better than this
|
||||||
return streams.indexOf(item);
|
return streams.indexOf(item);
|
||||||
}
|
}
|
||||||
|
@ -134,6 +133,13 @@ public abstract class PlayQueue implements Serializable {
|
||||||
setIndex(getIndex() + offset);
|
setIndex(getIndex() + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized void updateIndex(final int index, final int selectedQuality) {
|
||||||
|
if (index < 0 || index >= streams.size()) return;
|
||||||
|
|
||||||
|
get(index).setSortedQualityIndex(selectedQuality);
|
||||||
|
broadcast(new UpdateEvent(index));
|
||||||
|
}
|
||||||
|
|
||||||
protected synchronized void append(final PlayQueueItem item) {
|
protected synchronized void append(final PlayQueueItem item) {
|
||||||
streams.add(item);
|
streams.add(item);
|
||||||
broadcast(new AppendEvent(1));
|
broadcast(new AppendEvent(1));
|
||||||
|
@ -158,29 +164,6 @@ public abstract class PlayQueue implements Serializable {
|
||||||
broadcast(new RemoveEvent(index, isCurrent));
|
broadcast(new RemoveEvent(index, isCurrent));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected synchronized void swap(final int source, final int target) {
|
|
||||||
if (source < 0 || target < 0) return;
|
|
||||||
|
|
||||||
final List<PlayQueueItem> items = streams;
|
|
||||||
if (source < items.size() && target < items.size()) {
|
|
||||||
// Swap two items
|
|
||||||
final PlayQueueItem sourceItem = items.get(source);
|
|
||||||
final PlayQueueItem targetItem = items.get(target);
|
|
||||||
|
|
||||||
items.set(target, sourceItem);
|
|
||||||
items.set(source, targetItem);
|
|
||||||
|
|
||||||
// If the current playing index is one of the swapped indices, change that as well
|
|
||||||
final int index = queueIndex.get();
|
|
||||||
if (index == source || index == target) {
|
|
||||||
final int newIndex = index == source ? target : source;
|
|
||||||
queueIndex.set(newIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
broadcast(new MoveEvent(source, target));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Rx Broadcast
|
// Rx Broadcast
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
|
@ -82,10 +82,6 @@ public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
|
||||||
playQueue.remove(index);
|
playQueue.remove(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void swap(final int source, final int target) {
|
|
||||||
playQueue.swap(source, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startReactor() {
|
private void startReactor() {
|
||||||
final Observer<PlayQueueMessage> observer = new Observer<PlayQueueMessage>() {
|
final Observer<PlayQueueMessage> observer = new Observer<PlayQueueMessage>() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
package org.schabi.newpipe.playlist.events;
|
|
||||||
|
|
||||||
|
|
||||||
public class MoveEvent implements PlayQueueMessage {
|
|
||||||
final private int from;
|
|
||||||
final private int to;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PlayQueueEvent type() {
|
|
||||||
return PlayQueueEvent.MOVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MoveEvent(final int from, final int to) {
|
|
||||||
this.from = from;
|
|
||||||
this.to = to;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getFrom() {
|
|
||||||
return from;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getTo() {
|
|
||||||
return to;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,10 +3,7 @@ package org.schabi.newpipe.playlist.events;
|
||||||
public enum PlayQueueEvent {
|
public enum PlayQueueEvent {
|
||||||
INIT,
|
INIT,
|
||||||
|
|
||||||
// sent when the user is seamlessly transitioned by exoplayer to the next stream
|
// sent when the index is changed
|
||||||
NEXT,
|
|
||||||
|
|
||||||
// sent when the user transitions to an unbuffered period
|
|
||||||
SELECT,
|
SELECT,
|
||||||
|
|
||||||
// sent when more streams are added to the play queue
|
// sent when more streams are added to the play queue
|
||||||
|
@ -16,6 +13,12 @@ public enum PlayQueueEvent {
|
||||||
REMOVE,
|
REMOVE,
|
||||||
|
|
||||||
// sent when two streams swap place in the play queue
|
// sent when two streams swap place in the play queue
|
||||||
MOVE
|
MOVE,
|
||||||
|
|
||||||
|
// sent when a stream is updated
|
||||||
|
UPDATE,
|
||||||
|
|
||||||
|
// send when queue is shuffled
|
||||||
|
SHUFFLE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package org.schabi.newpipe.playlist.events;
|
||||||
|
|
||||||
|
public class UpdateEvent implements PlayQueueMessage {
|
||||||
|
final private int updatedIndex;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlayQueueEvent type() {
|
||||||
|
return PlayQueueEvent.UPDATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UpdateEvent(final int updatedIndex) {
|
||||||
|
this.updatedIndex = updatedIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int index() {
|
||||||
|
return updatedIndex;
|
||||||
|
}
|
||||||
|
}
|
|
@ -71,7 +71,6 @@ public class NavigationHelper {
|
||||||
return new Intent(context, targetClazz)
|
return new Intent(context, targetClazz)
|
||||||
.putExtra(BasePlayer.INTENT_TYPE, VideoPlayer.PLAYER_INTENT)
|
.putExtra(BasePlayer.INTENT_TYPE, VideoPlayer.PLAYER_INTENT)
|
||||||
.putExtra(VideoPlayer.PLAY_QUEUE, instance.getPlayQueue())
|
.putExtra(VideoPlayer.PLAY_QUEUE, instance.getPlayQueue())
|
||||||
.putExtra(VideoPlayer.INDEX_SEL_VIDEO_STREAM, instance.getSelectedStreamIndex())
|
|
||||||
.putExtra(VideoPlayer.RESTORE_QUEUE_INDEX, instance.getCurrentQueueIndex())
|
.putExtra(VideoPlayer.RESTORE_QUEUE_INDEX, instance.getCurrentQueueIndex())
|
||||||
.putExtra(BasePlayer.START_POSITION, instance.getPlayer().getCurrentPosition())
|
.putExtra(BasePlayer.START_POSITION, instance.getPlayer().getCurrentPosition())
|
||||||
.putExtra(BasePlayer.PLAYBACK_SPEED, instance.getPlaybackSpeed());
|
.putExtra(BasePlayer.PLAYBACK_SPEED, instance.getPlaybackSpeed());
|
||||||
|
|
Loading…
Reference in a new issue