-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.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());

View File

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

View File

@ -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());

View File

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

View File

@ -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() {

View File

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

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.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));

View File

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

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