-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:
parent
e742091a37
commit
cb7e94449c
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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<TrackGroupInfo> 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<VideoStream> videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false);
|
||||
selectedIndexStream = videos.get(ListHelper.getDefaultResolutionIndex(context, videos));
|
||||
if (info != null) {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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<StreamInfo> syncPlayback = new Consumer<StreamInfo>() {
|
||||
@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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue