-Fixed incorrect stream from being played after consecutive player errors.
-Fixed MediaSource reuse due to MediaSourceManager not resetting source on block.
This commit is contained in:
parent
2e414cfd63
commit
f1e52b8b92
6 changed files with 79 additions and 63 deletions
|
@ -32,6 +32,7 @@ import android.os.Build;
|
|||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
import android.support.annotation.IntRange;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
@ -49,6 +50,7 @@ import org.schabi.newpipe.extractor.MediaFormat;
|
|||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.util.ListHelper;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
@ -346,7 +348,7 @@ public final class BackgroundPlayer extends Service {
|
|||
public void onUpdateProgress(int currentProgress, int duration, int bufferPercent) {
|
||||
resetNotification();
|
||||
if (bigNotRemoteView != null) {
|
||||
if (currentInfo != null) {
|
||||
if (currentItem != null) {
|
||||
bigNotRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle());
|
||||
bigNotRemoteView.setTextViewText(R.id.notificationArtist, getUploaderName());
|
||||
}
|
||||
|
@ -354,7 +356,7 @@ public final class BackgroundPlayer extends Service {
|
|||
bigNotRemoteView.setTextViewText(R.id.notificationTime, getTimeString(currentProgress) + " / " + getTimeString(duration));
|
||||
}
|
||||
if (notRemoteView != null) {
|
||||
if (currentInfo != null) {
|
||||
if (currentItem != null) {
|
||||
notRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle());
|
||||
notRemoteView.setTextViewText(R.id.notificationArtist, getUploaderName());
|
||||
}
|
||||
|
@ -442,8 +444,8 @@ public final class BackgroundPlayer extends Service {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void sync(@Nullable final StreamInfo info) {
|
||||
super.sync(info);
|
||||
public void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info) {
|
||||
super.sync(item, info);
|
||||
|
||||
resetNotification();
|
||||
notRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle());
|
||||
|
|
|
@ -79,6 +79,7 @@ import org.schabi.newpipe.player.playback.MediaSourceManager;
|
|||
import org.schabi.newpipe.player.playback.PlaybackListener;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueueAdapter;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
|
@ -149,6 +150,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
private long videoPos = -1;
|
||||
|
||||
protected StreamInfo currentInfo;
|
||||
protected PlayQueueItem currentItem;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Player
|
||||
|
@ -729,23 +731,27 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
if (getCurrentState() == STATE_BLOCKED) changeState(STATE_BUFFERING);
|
||||
|
||||
simpleExoPlayer.prepare(mediaSource);
|
||||
simpleExoPlayer.seekToDefaultPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sync(@Nullable final StreamInfo info) {
|
||||
if (info == null || simpleExoPlayer == null) return;
|
||||
public void sync(@android.support.annotation.NonNull final PlayQueueItem item,
|
||||
@Nullable final StreamInfo info) {
|
||||
if (simpleExoPlayer == null) return;
|
||||
if (DEBUG) Log.d(TAG, "Syncing...");
|
||||
|
||||
currentItem = item;
|
||||
currentInfo = info;
|
||||
|
||||
// Check if on wrong window
|
||||
final int currentSourceIndex = playQueue.getIndex();
|
||||
if (!(simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex)) {
|
||||
final long startPos = currentInfo != null ? currentInfo.start_position : 0;
|
||||
if (simpleExoPlayer.getCurrentWindowIndex() != currentSourceIndex) {
|
||||
final long startPos = info != null ? info.start_position : 0;
|
||||
if (DEBUG) Log.d(TAG, "Rewinding to correct window: " + currentSourceIndex + " at: " + getTimeString((int)startPos));
|
||||
simpleExoPlayer.seekTo(currentSourceIndex, startPos);
|
||||
}
|
||||
|
||||
currentInfo = info;
|
||||
initThumbnail(info.thumbnail_url);
|
||||
initThumbnail(info == null ? item.getThumbnailUrl() : info.thumbnail_url);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -797,13 +803,14 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
}
|
||||
|
||||
public void onPlayPrevious() {
|
||||
if (simpleExoPlayer == null || playQueue == null || currentInfo == null) return;
|
||||
if (simpleExoPlayer == null || playQueue == null) return;
|
||||
if (DEBUG) Log.d(TAG, "onPlayPrevious() called");
|
||||
|
||||
/* If current playback has run for PLAY_PREV_ACTIVATION_LIMIT milliseconds, restart current track.
|
||||
* Also restart the track if the current track is the first in a queue.*/
|
||||
if (simpleExoPlayer.getCurrentPosition() > PLAY_PREV_ACTIVATION_LIMIT || playQueue.getIndex() == 0) {
|
||||
simpleExoPlayer.seekTo(currentInfo.start_position);
|
||||
final long startPos = currentInfo == null ? 0 : currentInfo.start_position;
|
||||
simpleExoPlayer.seekTo(startPos);
|
||||
} else {
|
||||
playQueue.offsetIndex(-1);
|
||||
}
|
||||
|
@ -947,15 +954,15 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
}
|
||||
|
||||
public String getVideoUrl() {
|
||||
return currentInfo == null ? null : currentInfo.url;
|
||||
return currentItem == null ? null : currentItem.getUrl();
|
||||
}
|
||||
|
||||
public String getVideoTitle() {
|
||||
return currentInfo == null ? null : currentInfo.name;
|
||||
return currentItem == null ? null : currentItem.getTitle();
|
||||
}
|
||||
|
||||
public String getUploaderName() {
|
||||
return currentInfo == null ? null : currentInfo.uploader_name;
|
||||
return currentItem == null ? null : currentItem.getUploader();
|
||||
}
|
||||
|
||||
public boolean isCompleted() {
|
||||
|
|
|
@ -26,6 +26,7 @@ import android.graphics.Color;
|
|||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
|
@ -43,6 +44,7 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
|||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.util.AnimationUtils;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.PermissionHelper;
|
||||
|
@ -250,8 +252,8 @@ public final class MainVideoPlayer extends Activity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void sync(@Nullable final StreamInfo info) {
|
||||
super.sync(info);
|
||||
public void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info) {
|
||||
super.sync(item, info);
|
||||
titleTextView.setText(getVideoTitle());
|
||||
channelTextView.setText(getUploaderName());
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import android.graphics.Color;
|
|||
import android.graphics.PorterDuff;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.Log;
|
||||
|
@ -64,6 +65,7 @@ import org.schabi.newpipe.R;
|
|||
import org.schabi.newpipe.extractor.MediaFormat;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.util.AnimationUtils;
|
||||
import org.schabi.newpipe.util.ListHelper;
|
||||
|
||||
|
@ -304,8 +306,8 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void sync(@Nullable final StreamInfo info) {
|
||||
super.sync(info);
|
||||
public void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info) {
|
||||
super.sync(item, info);
|
||||
|
||||
if (info != null) {
|
||||
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false);
|
||||
|
|
|
@ -29,7 +29,7 @@ import io.reactivex.functions.Consumer;
|
|||
public class MediaSourceManager implements DeferredMediaSource.Callback {
|
||||
private final String TAG = "MediaSourceManager@" + Integer.toHexString(hashCode());
|
||||
// One-side rolling window size for default loading
|
||||
// Effectively loads WINDOW_SIZE * 2 + 1 streams, should be at least 1 to ensure gapless playback
|
||||
// Effectively loads WINDOW_SIZE * 2 + 1 streams, must be greater than 0
|
||||
// todo: inject this parameter, allow user settings perhaps
|
||||
private static final int WINDOW_SIZE = 1;
|
||||
|
||||
|
@ -116,7 +116,6 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
|||
* */
|
||||
public void reset() {
|
||||
tryBlock();
|
||||
resetSources();
|
||||
populateSources();
|
||||
}
|
||||
|
||||
|
@ -149,6 +148,10 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
|||
private void onPlayQueueChanged(final PlayQueueMessage event) {
|
||||
// why no pattern matching in Java =(
|
||||
switch (event.type()) {
|
||||
case INIT:
|
||||
case REORDER:
|
||||
reset();
|
||||
break;
|
||||
case APPEND:
|
||||
populateSources();
|
||||
break;
|
||||
|
@ -159,10 +162,6 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
|||
final RemoveEvent removeEvent = (RemoveEvent) event;
|
||||
remove(removeEvent.index());
|
||||
break;
|
||||
case INIT:
|
||||
case REORDER:
|
||||
reset();
|
||||
break;
|
||||
case MOVE:
|
||||
final MoveEvent moveEvent = (MoveEvent) event;
|
||||
move(moveEvent.getFromIndex(), moveEvent.getToIndex());
|
||||
|
@ -195,6 +194,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
|||
private boolean tryBlock() {
|
||||
if (!isBlocked) {
|
||||
playbackListener.block();
|
||||
resetSources();
|
||||
isBlocked = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -202,7 +202,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
|||
}
|
||||
|
||||
private boolean tryUnblock() {
|
||||
if (isPlayQueueReady() && isBlocked) {
|
||||
if (isPlayQueueReady() && isBlocked && sources != null) {
|
||||
isBlocked = false;
|
||||
playbackListener.unblock(sources);
|
||||
return true;
|
||||
|
@ -216,7 +216,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
|||
final Consumer<StreamInfo> syncPlayback = new Consumer<StreamInfo>() {
|
||||
@Override
|
||||
public void accept(StreamInfo streamInfo) throws Exception {
|
||||
playbackListener.sync(streamInfo);
|
||||
playbackListener.sync(currentItem, streamInfo);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -224,7 +224,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
|||
@Override
|
||||
public void accept(Throwable throwable) throws Exception {
|
||||
Log.e(TAG, "Sync error:", throwable);
|
||||
playbackListener.sync(null);
|
||||
playbackListener.sync(currentItem,null);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -244,11 +244,12 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
|||
|
||||
private void resetSources() {
|
||||
if (this.sources != null) this.sources.releaseSource();
|
||||
|
||||
this.sources = new DynamicConcatenatingMediaSource();
|
||||
}
|
||||
|
||||
private void populateSources() {
|
||||
if (sources == null) return;
|
||||
|
||||
for (final PlayQueueItem item : playQueue.getStreams()) {
|
||||
insert(playQueue.indexOf(item), new DeferredMediaSource(item, this));
|
||||
}
|
||||
|
|
|
@ -1,54 +1,56 @@
|
|||
package org.schabi.newpipe.player.playback;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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.
|
||||
*
|
||||
* May be called at any time.
|
||||
* */
|
||||
/**
|
||||
* Called when the stream at the current queue index is not ready yet.
|
||||
* Signals to the listener to block the player from playing anything and notify the source
|
||||
* is now invalid.
|
||||
*
|
||||
* May be called at any time.
|
||||
* */
|
||||
void block();
|
||||
|
||||
/*
|
||||
* Called when the stream at the current queue index is ready.
|
||||
* Signals to the listener to resume the player.
|
||||
*
|
||||
* May be called only when the player is blocked.
|
||||
* */
|
||||
/**
|
||||
* Called when the stream at the current queue index is ready.
|
||||
* Signals to the listener to resume the player by preparing a new source.
|
||||
*
|
||||
* May be called only when the player is blocked.
|
||||
* */
|
||||
void unblock(final MediaSource mediaSource);
|
||||
|
||||
/*
|
||||
* Called when the queue index is refreshed.
|
||||
* Signals to the listener to synchronize the player's window to the manager's
|
||||
* window.
|
||||
*
|
||||
* May be null.
|
||||
* May be called only after playback is unblocked.
|
||||
* */
|
||||
void sync(@Nullable final StreamInfo info);
|
||||
/**
|
||||
* Called when the queue index is refreshed.
|
||||
* Signals to the listener to synchronize the player's window to the manager's
|
||||
* window.
|
||||
*
|
||||
* May be called only after unblock is called.
|
||||
* */
|
||||
void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info);
|
||||
|
||||
/*
|
||||
* Requests the listener to resolve a stream info into a media source
|
||||
* according to the listener's implementation (background, popup or main video player).
|
||||
*
|
||||
* May be called at any time.
|
||||
* */
|
||||
/**
|
||||
* Requests the listener to resolve a stream info into a media source
|
||||
* according to the listener's implementation (background, popup or main video player).
|
||||
*
|
||||
* May be called at any time.
|
||||
* */
|
||||
MediaSource sourceOf(final StreamInfo info);
|
||||
|
||||
/*
|
||||
* Called when the play queue can no longer to played or used.
|
||||
* Currently, this means the play queue is empty and complete.
|
||||
* Signals to the listener that it should shutdown.
|
||||
*
|
||||
* May be called at any time.
|
||||
* */
|
||||
/**
|
||||
* Called when the play queue can no longer to played or used.
|
||||
* Currently, this means the play queue is empty and complete.
|
||||
* Signals to the listener that it should shutdown.
|
||||
*
|
||||
* May be called at any time.
|
||||
* */
|
||||
void shutdown();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue