-Modified quality change to persist with its binding player.

-Fixed media source stops loading when the sequence of failed media is longer than window size.
-Changed player to release and reset on intent start.
-Removed update event.
This commit is contained in:
John Zhen M 2017-09-24 13:44:31 -07:00 committed by John Zhen Mo
parent e742091a37
commit cb7e94449c
9 changed files with 62 additions and 70 deletions

View File

@ -31,6 +31,7 @@ import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import android.os.PowerManager; import android.os.PowerManager;
import android.support.annotation.IntRange; import android.support.annotation.IntRange;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import android.util.Log; import android.util.Log;
import android.widget.RemoteViews; import android.widget.RemoteViews;
@ -383,8 +384,8 @@ public final class BackgroundPlayer extends Service {
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void sync(final StreamInfo info, final int sortedStreamsIndex) { public void sync(@Nullable final StreamInfo info) {
super.sync(info, sortedStreamsIndex); super.sync(info);
notRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle()); notRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle());
notRemoteView.setTextViewText(R.id.notificationArtist, getUploaderName()); notRemoteView.setTextViewText(R.id.notificationArtist, getUploaderName());

View File

@ -32,6 +32,7 @@ import android.media.AudioManager;
import android.media.audiofx.AudioEffect; import android.media.audiofx.AudioEffect;
import android.net.Uri; import android.net.Uri;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.View; 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) { protected void initPlayback(@NonNull final PlaybackListener listener, @NonNull final PlayQueue queue) {
destroyPlayer();
initPlayer();
if (playQueue != null) playQueue.dispose(); if (playQueue != null) playQueue.dispose();
if (playbackManager != null) playbackManager.dispose(); if (playbackManager != null) playbackManager.dispose();
@ -736,13 +740,15 @@ public abstract class BasePlayer implements Player.EventListener,
} }
@Override @Override
public void sync(final StreamInfo info, final int sortedStreamsIndex) { public void sync(@Nullable final StreamInfo info) {
if (simpleExoPlayer == null) return; if (simpleExoPlayer == null) return;
if (DEBUG) Log.d(TAG, "Syncing..."); if (DEBUG) Log.d(TAG, "Syncing...");
currentInfo = info;
refreshTimeline(); refreshTimeline();
if (info == null) return;
currentInfo = info;
initThumbnail(info.thumbnail_url); initThumbnail(info.thumbnail_url);
} }

View File

@ -238,8 +238,8 @@ public final class MainVideoPlayer extends Activity {
} }
@Override @Override
public void sync(final StreamInfo info, final int sortedStreamsIndex) { public void sync(@Nullable final StreamInfo info) {
super.sync(info, sortedStreamsIndex); super.sync(info);
titleTextView.setText(getVideoTitle()); titleTextView.setText(getVideoTitle());
channelTextView.setText(getUploaderName()); channelTextView.setText(getUploaderName());

View File

@ -29,9 +29,9 @@ import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.util.Log; import android.util.Log;
import android.view.Menu; 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.MergingMediaSource;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray; 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.FixedTrackSelection;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; 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 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 static final TrackSelection.Factory FIXED_FACTORY = new FixedTrackSelection.Factory();
private int videoRendererIndex;
private TrackGroupArray videoTrackGroups;
private List<TrackGroupInfo> trackGroupInfos; private List<TrackGroupInfo> trackGroupInfos;
private MappingTrackSelector.SelectionOverride override;
private boolean startedFromNewPipe = true; private boolean startedFromNewPipe = true;
protected boolean wasPlaying = false; protected boolean wasPlaying = false;
@ -247,11 +244,13 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
} }
@Override @Override
public void sync(final StreamInfo info, final int sortedStreamsIndex) { public void sync(@Nullable final StreamInfo info) {
super.sync(info, sortedStreamsIndex); super.sync(info);
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false); if (info != null) {
selectedIndexStream = videos.get(ListHelper.getDefaultResolutionIndex(context, videos)); final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false);
selectedIndexStream = videos.get(ListHelper.getDefaultResolutionIndex(context, videos));
}
playbackSpeedPopupMenu.getMenu().removeGroup(playbackSpeedPopupMenuGroupId); playbackSpeedPopupMenu.getMenu().removeGroup(playbackSpeedPopupMenuGroupId);
buildPlaybackSpeedMenu(playbackSpeedPopupMenu); buildPlaybackSpeedMenu(playbackSpeedPopupMenu);
@ -369,23 +368,29 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
super.onTracksChanged(trackGroups, 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++) { for (int t = 0; t < simpleExoPlayer.getRendererCount(); t++) {
if (simpleExoPlayer.getRendererType(t) == C.TRACK_TYPE_VIDEO) { if (simpleExoPlayer.getRendererType(t) == C.TRACK_TYPE_VIDEO) {
videoRendererIndex = t; videoRendererIndex = t;
} }
} }
videoTrackGroups = trackSelector.getCurrentMappedTrackInfo().getTrackGroups(videoRendererIndex); final TrackGroupArray videoTrackGroups = trackSelector.getCurrentMappedTrackInfo().getTrackGroups(videoRendererIndex);
override = trackSelector.getSelectionOverride(videoRendererIndex, videoTrackGroups);
final Format format = trackSelections.get(videoRendererIndex).getSelectedFormat(); 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); qualityPopupMenu.getMenu().removeGroup(qualityPopupMenuGroupId);
buildQualityMenu(qualityPopupMenu); buildQualityMenu(qualityPopupMenu, videoTrackGroups);
} }
private void buildQualityMenu(PopupMenu popupMenu) { private void buildQualityMenu(PopupMenu popupMenu, TrackGroupArray videoTrackGroups) {
trackGroupInfos = new ArrayList<>(); trackGroupInfos = new ArrayList<>();
int acc = 0; int acc = 0;
for (int groupIndex = 0; groupIndex < videoTrackGroups.length; groupIndex++) { 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 MediaFormat mediaFormat = MediaFormat.getFromMimeType(format.sampleMimeType);
final String mediaName = mediaFormat == null ? format.sampleMimeType : mediaFormat.name; 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); popupMenu.getMenu().add(qualityPopupMenuGroupId, acc, Menu.NONE, mediaName + " " + resolution);
trackGroupInfos.add(new TrackGroupInfo(trackIndex, groupIndex, format)); trackGroupInfos.add(new TrackGroupInfo(trackIndex, groupIndex, format));
acc++; acc++;
@ -513,10 +517,10 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
if (qualityPopupMenuGroupId == menuItem.getGroupId()) { if (qualityPopupMenuGroupId == menuItem.getGroupId()) {
final int itemId = menuItem.getItemId(); final int itemId = menuItem.getItemId();
final TrackGroupInfo info = trackGroupInfos.get(itemId); final TrackGroupInfo info = trackGroupInfos.get(itemId);
qualityTextView.setText(menuItem.getTitle());
override = new MappingTrackSelector.SelectionOverride(FIXED_FACTORY, info.group, info.track); final DefaultTrackSelector.Parameters parameters = new DefaultTrackSelector.Parameters()
trackSelector.setSelectionOverride(videoRendererIndex, videoTrackGroups, override); .withMaxVideoSize(info.format.width, Integer.MAX_VALUE);
trackSelector.setParameters(parameters);
return true; return true;
} else if (playbackSpeedPopupMenuGroupId == menuItem.getGroupId()) { } else if (playbackSpeedPopupMenuGroupId == menuItem.getGroupId()) {
@ -537,7 +541,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
public void onDismiss(PopupMenu menu) { public void onDismiss(PopupMenu menu) {
if (DEBUG) Log.d(TAG, "onDismiss() called with: menu = [" + menu + "]"); if (DEBUG) Log.d(TAG, "onDismiss() called with: menu = [" + menu + "]");
isSomePopupMenuVisible = false; isSomePopupMenuVisible = false;
qualityTextView.setText(getSelectedVideoStream().resolution);
} }
public void onQualitySelectorClicked() { public void onQualitySelectorClicked() {
@ -597,6 +600,11 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
// Utils // 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() { public boolean isControlsVisible() {
return controlsRoot != null && controlsRoot.getVisibility() == View.VISIBLE; return controlsRoot != null && controlsRoot.getVisibility() == View.VISIBLE;
} }

View File

@ -29,10 +29,10 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
// One-side rolling window size for default loading // 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, should be at least 1 to ensure gapless playback
// todo: inject this parameter, allow user settings perhaps // 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 PlaybackListener playbackListener;
private final PlayQueue playQueue; private PlayQueue playQueue;
private DynamicConcatenatingMediaSource sources; private DynamicConcatenatingMediaSource sources;
// sourceToQueueIndex maps media source index to play queue index // sourceToQueueIndex maps media source index to play queue index
@ -74,10 +74,6 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
return sourceToQueueIndex.get(sourceIndex); return sourceToQueueIndex.get(sourceIndex);
} }
public int expectedTimelineSize() {
return sources.getSize();
}
public void dispose() { public void dispose() {
if (playQueueReactor != null) playQueueReactor.cancel(); if (playQueueReactor != null) playQueueReactor.cancel();
if (syncReactor != null) syncReactor.dispose(); if (syncReactor != null) syncReactor.dispose();
@ -88,6 +84,8 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
syncReactor = null; syncReactor = null;
sources = null; sources = null;
sourceToQueueIndex = null; sourceToQueueIndex = null;
playbackListener = null;
playQueue = null;
} }
public void load() { public void load() {
@ -141,12 +139,10 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
break; break;
} }
case INIT: case INIT:
case UPDATE:
case REORDER: case REORDER:
tryBlock(); tryBlock();
resetSources(); resetSources();
populateSources(); populateSources();
if (tryUnblock()) sync();
break; break;
default: default:
break; break;
@ -208,7 +204,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
final Consumer<StreamInfo> syncPlayback = new Consumer<StreamInfo>() { final Consumer<StreamInfo> syncPlayback = new Consumer<StreamInfo>() {
@Override @Override
public void accept(StreamInfo streamInfo) throws Exception { 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 @Override
public void accept(Throwable throwable) throws Exception { public void accept(Throwable throwable) throws Exception {
Log.e(TAG, "Sync error:", throwable); 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)); final DeferredMediaSource mediaSource = (DeferredMediaSource) sources.getMediaSource(playQueue.indexOf(item));
if (mediaSource.state() == DeferredMediaSource.STATE_PREPARED) mediaSource.load(); if (mediaSource.state() == DeferredMediaSource.STATE_PREPARED) mediaSource.load();
if (tryUnblock()) sync();
} }
private void resetSources() { private void resetSources() {

View File

@ -1,5 +1,7 @@
package org.schabi.newpipe.player.playback; package org.schabi.newpipe.player.playback;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import org.schabi.newpipe.extractor.stream.StreamInfo; 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 * Signals to the listener to synchronize the player's window to the manager's
* window. * 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 * Requests the listener to resolve a stream info into a media source

View File

@ -11,7 +11,6 @@ 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.ReorderEvent; import org.schabi.newpipe.playlist.events.ReorderEvent;
import org.schabi.newpipe.playlist.events.SelectEvent; import org.schabi.newpipe.playlist.events.SelectEvent;
import org.schabi.newpipe.playlist.events.UpdateEvent;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
@ -68,9 +67,14 @@ public abstract class PlayQueue implements Serializable {
} }
public void dispose() { 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(); if (reportingReactor != null) reportingReactor.cancel();
broadcastReceiver = null;
reportingReactor = null; reportingReactor = null;
} }
@ -141,13 +145,6 @@ 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... items) { protected synchronized void append(final PlayQueueItem... items) {
streams.addAll(Arrays.asList(items)); streams.addAll(Arrays.asList(items));
broadcast(new AppendEvent(items.length)); broadcast(new AppendEvent(items.length));

View File

@ -15,9 +15,6 @@ public enum PlayQueueEvent {
// 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 // send when queue is shuffled
REORDER REORDER
} }

View File

@ -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;
}
}