diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java index 2c5dc4f36..0d0954d98 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java @@ -31,6 +31,7 @@ import android.os.Build; import android.os.IBinder; import android.os.PowerManager; import android.support.annotation.IntRange; +import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; import android.util.Log; import android.widget.RemoteViews; @@ -383,8 +384,8 @@ public final class BackgroundPlayer extends Service { //////////////////////////////////////////////////////////////////////////*/ @Override - public void sync(final StreamInfo info, final int sortedStreamsIndex) { - super.sync(info, sortedStreamsIndex); + public void sync(@Nullable final StreamInfo info) { + super.sync(info); notRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle()); notRemoteView.setTextViewText(R.id.notificationArtist, getUploaderName()); diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java index b42162292..661c8c646 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -32,6 +32,7 @@ import android.media.AudioManager; import android.media.audiofx.AudioEffect; import android.net.Uri; import android.preference.PreferenceManager; +import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Log; import android.view.View; @@ -298,6 +299,9 @@ public abstract class BasePlayer implements Player.EventListener, } protected void initPlayback(@NonNull final PlaybackListener listener, @NonNull final PlayQueue queue) { + destroyPlayer(); + initPlayer(); + if (playQueue != null) playQueue.dispose(); if (playbackManager != null) playbackManager.dispose(); @@ -736,13 +740,15 @@ public abstract class BasePlayer implements Player.EventListener, } @Override - public void sync(final StreamInfo info, final int sortedStreamsIndex) { + public void sync(@Nullable final StreamInfo info) { if (simpleExoPlayer == null) return; if (DEBUG) Log.d(TAG, "Syncing..."); - currentInfo = info; refreshTimeline(); + if (info == null) return; + + currentInfo = info; initThumbnail(info.thumbnail_url); } diff --git a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java index 5b4235a55..3769263fd 100644 --- a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java @@ -238,8 +238,8 @@ public final class MainVideoPlayer extends Activity { } @Override - public void sync(final StreamInfo info, final int sortedStreamsIndex) { - super.sync(info, sortedStreamsIndex); + public void sync(@Nullable final StreamInfo info) { + super.sync(info); titleTextView.setText(getVideoTitle()); channelTextView.setText(getUploaderName()); diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java index abf8975d8..ba6fea97c 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java @@ -29,9 +29,9 @@ import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.PorterDuff; -import android.net.Uri; import android.os.Build; import android.os.Handler; +import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; import android.util.Log; import android.view.Menu; @@ -53,8 +53,8 @@ import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MergingMediaSource; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.FixedTrackSelection; -import com.google.android.exoplayer2.trackselection.MappingTrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; @@ -106,10 +106,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. private static final float[] PLAYBACK_SPEEDS = {0.5f, 0.75f, 1f, 1.25f, 1.5f, 1.75f, 2f}; private static final TrackSelection.Factory FIXED_FACTORY = new FixedTrackSelection.Factory(); - private int videoRendererIndex; - private TrackGroupArray videoTrackGroups; private List trackGroupInfos; - private MappingTrackSelector.SelectionOverride override; private boolean startedFromNewPipe = true; protected boolean wasPlaying = false; @@ -247,11 +244,13 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. } @Override - public void sync(final StreamInfo info, final int sortedStreamsIndex) { - super.sync(info, sortedStreamsIndex); + public void sync(@Nullable final StreamInfo info) { + super.sync(info); - final List videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false); - selectedIndexStream = videos.get(ListHelper.getDefaultResolutionIndex(context, videos)); + if (info != null) { + final List videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false); + selectedIndexStream = videos.get(ListHelper.getDefaultResolutionIndex(context, videos)); + } playbackSpeedPopupMenu.getMenu().removeGroup(playbackSpeedPopupMenuGroupId); buildPlaybackSpeedMenu(playbackSpeedPopupMenu); @@ -369,23 +368,29 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { super.onTracksChanged(trackGroups, trackSelections); + if (trackSelector.getCurrentMappedTrackInfo() == null) { + qualityTextView.setVisibility(View.GONE); + return; + } else { + qualityTextView.setVisibility(View.VISIBLE); + } + + int videoRendererIndex = -1; for (int t = 0; t < simpleExoPlayer.getRendererCount(); t++) { if (simpleExoPlayer.getRendererType(t) == C.TRACK_TYPE_VIDEO) { videoRendererIndex = t; } } - videoTrackGroups = trackSelector.getCurrentMappedTrackInfo().getTrackGroups(videoRendererIndex); - override = trackSelector.getSelectionOverride(videoRendererIndex, videoTrackGroups); - + final TrackGroupArray videoTrackGroups = trackSelector.getCurrentMappedTrackInfo().getTrackGroups(videoRendererIndex); final Format format = trackSelections.get(videoRendererIndex).getSelectedFormat(); - final String resolution = Math.min(format.width, format.height) + "p"; - qualityTextView.setText(resolution); + + qualityTextView.setText(resolutionStringOf(format)); qualityPopupMenu.getMenu().removeGroup(qualityPopupMenuGroupId); - buildQualityMenu(qualityPopupMenu); + buildQualityMenu(qualityPopupMenu, videoTrackGroups); } - private void buildQualityMenu(PopupMenu popupMenu) { + private void buildQualityMenu(PopupMenu popupMenu, TrackGroupArray videoTrackGroups) { trackGroupInfos = new ArrayList<>(); int acc = 0; for (int groupIndex = 0; groupIndex < videoTrackGroups.length; groupIndex++) { @@ -396,8 +401,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. final MediaFormat mediaFormat = MediaFormat.getFromMimeType(format.sampleMimeType); final String mediaName = mediaFormat == null ? format.sampleMimeType : mediaFormat.name; - final String resolution = Math.min(format.width, format.height) + "p"; - + final String resolution = resolutionStringOf(format); popupMenu.getMenu().add(qualityPopupMenuGroupId, acc, Menu.NONE, mediaName + " " + resolution); trackGroupInfos.add(new TrackGroupInfo(trackIndex, groupIndex, format)); acc++; @@ -513,10 +517,10 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. if (qualityPopupMenuGroupId == menuItem.getGroupId()) { final int itemId = menuItem.getItemId(); final TrackGroupInfo info = trackGroupInfos.get(itemId); - qualityTextView.setText(menuItem.getTitle()); - override = new MappingTrackSelector.SelectionOverride(FIXED_FACTORY, info.group, info.track); - trackSelector.setSelectionOverride(videoRendererIndex, videoTrackGroups, override); + final DefaultTrackSelector.Parameters parameters = new DefaultTrackSelector.Parameters() + .withMaxVideoSize(info.format.width, Integer.MAX_VALUE); + trackSelector.setParameters(parameters); return true; } else if (playbackSpeedPopupMenuGroupId == menuItem.getGroupId()) { @@ -537,7 +541,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. public void onDismiss(PopupMenu menu) { if (DEBUG) Log.d(TAG, "onDismiss() called with: menu = [" + menu + "]"); isSomePopupMenuVisible = false; - qualityTextView.setText(getSelectedVideoStream().resolution); } public void onQualitySelectorClicked() { @@ -597,6 +600,11 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. // Utils //////////////////////////////////////////////////////////////////////////*/ + public String resolutionStringOf(final Format format) { + final String frameRate = format.frameRate > 0 ? String.valueOf((int) format.frameRate) : ""; + return Math.min(format.width, format.height) + "p" + frameRate; + } + public boolean isControlsVisible() { return controlsRoot != null && controlsRoot.getVisibility() == View.VISIBLE; } diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java b/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java index 3752136b5..e5b806113 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java +++ b/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java @@ -29,10 +29,10 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { // One-side rolling window size for default loading // Effectively loads WINDOW_SIZE * 2 + 1 streams, should be at least 1 to ensure gapless playback // todo: inject this parameter, allow user settings perhaps - private static final int WINDOW_SIZE = 1; + private static final int WINDOW_SIZE = 2; - private final PlaybackListener playbackListener; - private final PlayQueue playQueue; + private PlaybackListener playbackListener; + private PlayQueue playQueue; private DynamicConcatenatingMediaSource sources; // sourceToQueueIndex maps media source index to play queue index @@ -74,10 +74,6 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { return sourceToQueueIndex.get(sourceIndex); } - public int expectedTimelineSize() { - return sources.getSize(); - } - public void dispose() { if (playQueueReactor != null) playQueueReactor.cancel(); if (syncReactor != null) syncReactor.dispose(); @@ -88,6 +84,8 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { syncReactor = null; sources = null; sourceToQueueIndex = null; + playbackListener = null; + playQueue = null; } public void load() { @@ -141,12 +139,10 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { break; } case INIT: - case UPDATE: case REORDER: tryBlock(); resetSources(); populateSources(); - if (tryUnblock()) sync(); break; default: break; @@ -208,7 +204,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { final Consumer syncPlayback = new Consumer() { @Override public void accept(StreamInfo streamInfo) throws Exception { - playbackListener.sync(streamInfo, currentItem.getSortedQualityIndex()); + playbackListener.sync(streamInfo); } }; @@ -216,6 +212,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { @Override public void accept(Throwable throwable) throws Exception { Log.e(TAG, "Sync error:", throwable); + playbackListener.sync(null); } }; @@ -230,6 +227,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { final DeferredMediaSource mediaSource = (DeferredMediaSource) sources.getMediaSource(playQueue.indexOf(item)); if (mediaSource.state() == DeferredMediaSource.STATE_PREPARED) mediaSource.load(); + if (tryUnblock()) sync(); } private void resetSources() { diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/PlaybackListener.java b/app/src/main/java/org/schabi/newpipe/player/playback/PlaybackListener.java index 32bed87ae..7718437c7 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playback/PlaybackListener.java +++ b/app/src/main/java/org/schabi/newpipe/player/playback/PlaybackListener.java @@ -1,5 +1,7 @@ package org.schabi.newpipe.player.playback; +import android.support.annotation.Nullable; + import com.google.android.exoplayer2.source.MediaSource; import org.schabi.newpipe.extractor.stream.StreamInfo; @@ -28,9 +30,10 @@ public interface PlaybackListener { * Signals to the listener to synchronize the player's window to the manager's * window. * - * May be called only when playback is unblocked. + * May be null. + * May be called only after playback is unblocked. * */ - void sync(final StreamInfo info, final int sortedStreamsIndex); + void sync(@Nullable final StreamInfo info); /* * Requests the listener to resolve a stream info into a media source diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java index badc1cf86..f15acabe8 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java @@ -11,7 +11,6 @@ import org.schabi.newpipe.playlist.events.PlayQueueMessage; import org.schabi.newpipe.playlist.events.RemoveEvent; import org.schabi.newpipe.playlist.events.ReorderEvent; import org.schabi.newpipe.playlist.events.SelectEvent; -import org.schabi.newpipe.playlist.events.UpdateEvent; import java.io.Serializable; import java.util.ArrayList; @@ -68,9 +67,14 @@ public abstract class PlayQueue implements Serializable { } public void dispose() { - streamsEventBroadcast.onComplete(); + if (backup != null) backup.clear(); + if (streams != null) streams.clear(); + if (streamsEventBroadcast != null) streamsEventBroadcast.onComplete(); + if (indexEventBroadcast != null) indexEventBroadcast.onComplete(); if (reportingReactor != null) reportingReactor.cancel(); + + broadcastReceiver = null; reportingReactor = null; } @@ -141,13 +145,6 @@ public abstract class PlayQueue implements Serializable { 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... items) { streams.addAll(Arrays.asList(items)); broadcast(new AppendEvent(items.length)); diff --git a/app/src/main/java/org/schabi/newpipe/playlist/events/PlayQueueEvent.java b/app/src/main/java/org/schabi/newpipe/playlist/events/PlayQueueEvent.java index eccf9bea7..5c996ccdf 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/events/PlayQueueEvent.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/events/PlayQueueEvent.java @@ -15,9 +15,6 @@ public enum PlayQueueEvent { // sent when two streams swap place in the play queue MOVE, - // sent when a stream is updated - UPDATE, - // send when queue is shuffled REORDER } diff --git a/app/src/main/java/org/schabi/newpipe/playlist/events/UpdateEvent.java b/app/src/main/java/org/schabi/newpipe/playlist/events/UpdateEvent.java deleted file mode 100644 index 9fd5de0dd..000000000 --- a/app/src/main/java/org/schabi/newpipe/playlist/events/UpdateEvent.java +++ /dev/null @@ -1,18 +0,0 @@ -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; - } -}