-Added on change event bus to Play Queue.

-Added playback manager for player interaction.
This commit is contained in:
John Zhen M 2017-08-31 10:07:18 -07:00 committed by John Zhen Mo
parent 7c9c3de644
commit dcdcf17f5e
9 changed files with 419 additions and 122 deletions

View file

@ -47,9 +47,7 @@ import com.google.android.exoplayer2.RenderersFactory;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
import com.google.android.exoplayer2.source.DynamicConcatenatingMediaSource;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.LoopingMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.dash.DashMediaSource;
@ -72,6 +70,7 @@ import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListene
import org.schabi.newpipe.Downloader;
import org.schabi.newpipe.R;
import org.schabi.newpipe.playlist.PlayQueue;
import java.io.File;
import java.text.DecimalFormat;
@ -86,9 +85,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
* @author mauriciocolli
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public abstract class BasePlayer implements Player.EventListener, AudioManager.OnAudioFocusChangeListener {
public abstract class BasePlayer implements Player.EventListener,
AudioManager.OnAudioFocusChangeListener, PlaybackManager.PlaybackListener {
// TODO: Check api version for deprecated audio manager methods
public static final boolean DEBUG = false;
public static final String TAG = "BasePlayer";
@ -117,6 +116,13 @@ public abstract class BasePlayer implements Player.EventListener, AudioManager.O
protected long videoStartPos = -1;
protected String uploaderName = "";
/*//////////////////////////////////////////////////////////////////////////
// Playlist
//////////////////////////////////////////////////////////////////////////*/
protected PlaybackManager playbackManager;
protected PlayQueue playQueue;
/*//////////////////////////////////////////////////////////////////////////
// Player
//////////////////////////////////////////////////////////////////////////*/
@ -540,6 +546,22 @@ public abstract class BasePlayer implements Player.EventListener, AudioManager.O
@Override
public void onPositionDiscontinuity() {
int newIndex = simpleExoPlayer.getCurrentWindowIndex();
}
/*//////////////////////////////////////////////////////////////////////////
// Playback Listener
//////////////////////////////////////////////////////////////////////////*/
@Override
public void block() {
if (currentState != STATE_LOADING) changeState(STATE_LOADING);
}
@Override
public void unblock() {
if (currentState != STATE_PLAYING) changeState(STATE_PLAYING);
}
/*//////////////////////////////////////////////////////////////////////////

View file

@ -3,31 +3,200 @@ package org.schabi.newpipe.player;
import com.google.android.exoplayer2.source.DynamicConcatenatingMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.PlayQueueEvent;
import org.schabi.newpipe.playlist.PlayQueueItem;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import io.reactivex.Maybe;
import io.reactivex.annotations.NonNull;
public class PlaybackManager {
private DynamicConcatenatingMediaSource source;
private DynamicConcatenatingMediaSource mediaSource;
private List<PlayQueueItem> queueSource;
private int sourceIndex;
private PlaybackListener listener;
private PlayQueue playQueue;
private int index;
private List<MediaSource> sources;
public PlaybackManager(PlayQueue playQueue, int index) {
this.source = new DynamicConcatenatingMediaSource();
this.playQueue = playQueue;
this.index = index;
private Subscription playQueueReactor;
interface PlaybackListener {
void block();
void unblock();
void sync();
MediaSource sourceOf(StreamInfo info);
}
interface OnChangeListener {
void isLoading();
void isLoaded();
public PlaybackManager(@NonNull final PlaybackListener listener,
@NonNull final PlayQueue playQueue) {
this.mediaSource = new DynamicConcatenatingMediaSource();
this.queueSource = Collections.synchronizedList(new ArrayList<PlayQueueItem>(10));
this.sourceIndex = 0;
this.listener = listener;
this.playQueue = playQueue;
playQueue.getPlayQueueFlowable().subscribe(getReactor());
}
@NonNull
public DynamicConcatenatingMediaSource getMediaSource() {
return mediaSource;
}
private void reload() {
listener.block();
load(0);
}
public void refreshMedia(final int newMediaIndex) {
if (newMediaIndex == sourceIndex) return;
if (newMediaIndex == sourceIndex + 1) {
playQueue.incrementIndex();
mediaSource.removeMediaSource(0);
queueSource.remove(0);
} else {
//something went wrong
onInit();
}
}
private void removeCurrent() {
listener.block();
mediaSource.removeMediaSource(0);
queueSource.remove(0);
listener.unblock();
}
private Subscription loaderReactor;
private void load() {
if (mediaSource.getSize() < 5 && queueSource.size() < 5) load(mediaSource.getSize());
}
private void load(final int from) {
clear(from);
if (loaderReactor != null) loaderReactor.cancel();
List<Maybe<StreamInfo>> maybes = new ArrayList<>();
for (int i = from; i < 5; i++) {
final int index = playQueue.getIndex() + i;
final PlayQueueItem item = playQueue.get(index);
queueSource.set(i, item);
maybes.add(item.getStream());
}
Maybe.concat(maybes).subscribe(new Subscriber<StreamInfo>() {
@Override
public void onSubscribe(Subscription s) {
loaderReactor = s;
}
@Override
public void onNext(StreamInfo streamInfo) {
mediaSource.addMediaSource(listener.sourceOf(streamInfo));
onLoaded();
}
@Override
public void onError(Throwable t) {
playQueue.remove(queueSource.size());
}
@Override
public void onComplete() {
}
});
}
private void onLoaded() {
if (mediaSource.getSize() > 0 && queueSource.size() > 0) listener.unblock();
}
private void onInit() {
listener.block();
load();
}
private void clear(int from) {
listener.block();
while (mediaSource.getSize() > from) {
queueSource.remove(from);
mediaSource.removeMediaSource(from);
}
listener.unblock();
}
private Subscriber<PlayQueueEvent> getReactor() {
return new Subscriber<PlayQueueEvent>() {
@Override
public void onSubscribe(@NonNull Subscription d) {
if (playQueueReactor != null) playQueueReactor.cancel();
playQueueReactor = d;
playQueueReactor.request(1);
}
@Override
public void onNext(@NonNull PlayQueueEvent event) {
if (playQueue.getStreams().size() - playQueue.getIndex() < 10 && !playQueue.isComplete()) {
listener.block();
playQueue.fetch();
}
switch (event) {
case INIT:
onInit();
break;
case APPEND:
load();
break;
case REMOVE_CURRENT:
removeCurrent();
load();
break;
case SELECT:
reload();
break;
case REMOVE:
case SWAP:
load(1);
break;
case CLEAR:
clear(0);
break;
case NEXT:
default:
break;
}
onLoaded();
if (playQueueReactor != null) playQueueReactor.request(1);
}
@Override
public void onError(@NonNull Throwable e) {
}
@Override
public void onComplete() {
// Never completes, only canceled
}
};
}
public void dispose() {
if (playQueueReactor != null) playQueueReactor.cancel();
}
}