-Fixed potential NPE when obtaining broadcast receiver.

-Extracted expiration time in media source manager.
-Re-enabled long click on live stream info items.
-Fixed dash source building to use mpd instead of extractor.
This commit is contained in:
John Zhen Mo 2018-02-25 20:12:20 -08:00
parent ac431e3ece
commit 1444fe5468
6 changed files with 54 additions and 47 deletions

View File

@ -59,23 +59,20 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
itemBuilder.getImageLoader() itemBuilder.getImageLoader()
.displayImage(item.thumbnail_url, itemThumbnailView, StreamInfoItemHolder.DISPLAY_THUMBNAIL_OPTIONS); .displayImage(item.thumbnail_url, itemThumbnailView, StreamInfoItemHolder.DISPLAY_THUMBNAIL_OPTIONS);
itemView.setOnClickListener(new View.OnClickListener() { itemView.setOnClickListener(view -> {
@Override if (itemBuilder.getOnStreamSelectedListener() != null) {
public void onClick(View view) { itemBuilder.getOnStreamSelectedListener().selected(item);
if (itemBuilder.getOnStreamSelectedListener() != null) {
itemBuilder.getOnStreamSelectedListener().selected(item);
}
} }
}); });
switch (item.stream_type) { switch (item.stream_type) {
case AUDIO_STREAM: case AUDIO_STREAM:
case VIDEO_STREAM: case VIDEO_STREAM:
case FILE:
enableLongClick(item);
break;
case LIVE_STREAM: case LIVE_STREAM:
case AUDIO_LIVE_STREAM: case AUDIO_LIVE_STREAM:
enableLongClick(item);
break;
case FILE:
case NONE: case NONE:
default: default:
disableLongClick(); disableLongClick();
@ -85,14 +82,11 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
private void enableLongClick(final StreamInfoItem item) { private void enableLongClick(final StreamInfoItem item) {
itemView.setLongClickable(true); itemView.setLongClickable(true);
itemView.setOnLongClickListener(new View.OnLongClickListener() { itemView.setOnLongClickListener(view -> {
@Override if (itemBuilder.getOnStreamSelectedListener() != null) {
public boolean onLongClick(View view) { itemBuilder.getOnStreamSelectedListener().held(item);
if (itemBuilder.getOnStreamSelectedListener() != null) {
itemBuilder.getOnStreamSelectedListener().held(item);
}
return true;
} }
return true;
}); });
} }

View File

@ -319,35 +319,27 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
recordManager = null; recordManager = null;
} }
public MediaSource buildMediaSource(String url) {
return buildMediaSource(url, "");
}
public MediaSource buildMediaSource(String url, String overrideExtension) { public MediaSource buildMediaSource(String url, String overrideExtension) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "buildMediaSource() called with: url = [" + url + "], overrideExtension = [" + overrideExtension + "]"); Log.d(TAG, "buildMediaSource() called with: url = [" + url +
"], overrideExtension = [" + overrideExtension + "]");
} }
Uri uri = Uri.parse(url); Uri uri = Uri.parse(url);
int type = TextUtils.isEmpty(overrideExtension) ? Util.inferContentType(uri) : Util.inferContentType("." + overrideExtension); int type = TextUtils.isEmpty(overrideExtension) ? Util.inferContentType(uri) :
MediaSource mediaSource; Util.inferContentType("." + overrideExtension);
switch (type) { switch (type) {
case C.TYPE_SS: case C.TYPE_SS:
mediaSource = ssMediaSourceFactory.createMediaSource(uri); return ssMediaSourceFactory.createMediaSource(uri);
break;
case C.TYPE_DASH: case C.TYPE_DASH:
mediaSource = dashMediaSourceFactory.createMediaSource(uri); return dashMediaSourceFactory.createMediaSource(uri);
break;
case C.TYPE_HLS: case C.TYPE_HLS:
mediaSource = hlsMediaSourceFactory.createMediaSource(uri); return hlsMediaSourceFactory.createMediaSource(uri);
break;
case C.TYPE_OTHER: case C.TYPE_OTHER:
mediaSource = extractorMediaSourceFactory.createMediaSource(uri); return extractorMediaSourceFactory.createMediaSource(uri);
break;
default: { default: {
throw new IllegalStateException("Unsupported type: " + type); throw new IllegalStateException("Unsupported type: " + type);
} }
} }
return mediaSource;
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -514,8 +506,13 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
public void onTimelineChanged(Timeline timeline, Object manifest, int reason) { public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
if (DEBUG) Log.d(TAG, "onTimelineChanged(), timeline size = " + timeline.getWindowCount()); if (DEBUG) Log.d(TAG, "onTimelineChanged(), timeline size = " + timeline.getWindowCount());
if (playbackManager != null) { switch (reason) {
playbackManager.load(); case Player.TIMELINE_CHANGE_REASON_PREPARED:
case Player.TIMELINE_CHANGE_REASON_RESET:
case Player.TIMELINE_CHANGE_REASON_DYNAMIC:
default:
if (playbackManager != null) playbackManager.load();
break;
} }
} }
@ -526,7 +523,8 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
@Override @Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
if (DEBUG) Log.d(TAG, "playbackParameters(), speed: " + playbackParameters.speed + ", pitch: " + playbackParameters.pitch); if (DEBUG) Log.d(TAG, "playbackParameters(), speed: " + playbackParameters.speed +
", pitch: " + playbackParameters.pitch);
} }
@Override @Override
@ -732,9 +730,9 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
@Override @Override
public MediaSource sourceOf(PlayQueueItem item, StreamInfo info) { public MediaSource sourceOf(PlayQueueItem item, StreamInfo info) {
if (!info.getHlsUrl().isEmpty()) { if (!info.getHlsUrl().isEmpty()) {
return buildMediaSource(info.getHlsUrl()); return buildMediaSource(info.getHlsUrl(), "m3u8");
} else if (!info.getDashMpdUrl().isEmpty()) { } else if (!info.getDashMpdUrl().isEmpty()) {
return buildMediaSource(info.getDashMpdUrl()); return buildMediaSource(info.getDashMpdUrl(), "mpd");
} }
return null; return null;

View File

@ -25,6 +25,10 @@ public class FailedMediaSource implements ManagedMediaSource {
this.retryTimestamp = retryTimestamp; this.retryTimestamp = retryTimestamp;
} }
/**
* Permanently fail the play queue item associated with this source, with no hope of retrying.
* The error will always be propagated to ExoPlayer.
* */
public FailedMediaSource(@NonNull final PlayQueueItem playQueueItem, public FailedMediaSource(@NonNull final PlayQueueItem playQueueItem,
@NonNull final Throwable error) { @NonNull final Throwable error) {
this.playQueueItem = playQueueItem; this.playQueueItem = playQueueItem;

View File

@ -40,6 +40,8 @@ public class MediaSourceManager {
private final int windowSize; private final int windowSize;
private final PlaybackListener playbackListener; private final PlaybackListener playbackListener;
private final PlayQueue playQueue; private final PlayQueue playQueue;
private final long expirationTimeMillis;
private final TimeUnit expirationTimeUnit;
// Process only the last load order when receiving a stream of load orders (lessens I/O) // Process only the last load order when receiving a stream of load orders (lessens I/O)
// The higher it is, the less loading occurs during rapid noncritical timeline changes // The higher it is, the less loading occurs during rapid noncritical timeline changes
@ -61,13 +63,15 @@ public class MediaSourceManager {
public MediaSourceManager(@NonNull final PlaybackListener listener, public MediaSourceManager(@NonNull final PlaybackListener listener,
@NonNull final PlayQueue playQueue) { @NonNull final PlayQueue playQueue) {
this(listener, playQueue, 1, 400L); this(listener, playQueue, 1, 400L, 2, TimeUnit.HOURS);
} }
private MediaSourceManager(@NonNull final PlaybackListener listener, private MediaSourceManager(@NonNull final PlaybackListener listener,
@NonNull final PlayQueue playQueue, @NonNull final PlayQueue playQueue,
final int windowSize, final int windowSize,
final long loadDebounceMillis) { final long loadDebounceMillis,
final long expirationTimeMillis,
@NonNull final TimeUnit expirationTimeUnit) {
if (windowSize <= 0) { if (windowSize <= 0) {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"MediaSourceManager window size must be greater than 0"); "MediaSourceManager window size must be greater than 0");
@ -77,6 +81,8 @@ public class MediaSourceManager {
this.playQueue = playQueue; this.playQueue = playQueue;
this.windowSize = windowSize; this.windowSize = windowSize;
this.loadDebounceMillis = loadDebounceMillis; this.loadDebounceMillis = loadDebounceMillis;
this.expirationTimeMillis = expirationTimeMillis;
this.expirationTimeUnit = expirationTimeUnit;
this.loaderReactor = new CompositeDisposable(); this.loaderReactor = new CompositeDisposable();
this.debouncedLoadSignal = PublishSubject.create(); this.debouncedLoadSignal = PublishSubject.create();
@ -87,9 +93,11 @@ public class MediaSourceManager {
this.syncReactor = new SerialDisposable(); this.syncReactor = new SerialDisposable();
this.loadingItems = Collections.synchronizedSet(new HashSet<>()); this.loadingItems = Collections.synchronizedSet(new HashSet<>());
playQueue.getBroadcastReceiver() if (playQueue.getBroadcastReceiver() != null) {
.observeOn(AndroidSchedulers.mainThread()) playQueue.getBroadcastReceiver()
.subscribe(getReactor()); .observeOn(AndroidSchedulers.mainThread())
.subscribe(getReactor());
}
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -225,7 +233,7 @@ public class MediaSourceManager {
return playQueue.isComplete() || isWindowLoaded; return playQueue.isComplete() || isWindowLoaded;
} }
// Checks if the current playback media source is a placeholder, if so, then it is not ready // Checks if the current playback media source is a placeholder, if so, then it is not ready.
private boolean isPlaybackReady() { private boolean isPlaybackReady() {
return sources != null && playQueue != null && sources.getSize() > playQueue.getIndex() && return sources != null && playQueue != null && sources.getSize() > playQueue.getIndex() &&
!(sources.getMediaSource(playQueue.getIndex()) instanceof PlaceholderMediaSource); !(sources.getMediaSource(playQueue.getIndex()) instanceof PlaceholderMediaSource);
@ -351,11 +359,11 @@ public class MediaSourceManager {
final MediaSource source = playbackListener.sourceOf(stream, streamInfo); final MediaSource source = playbackListener.sourceOf(stream, streamInfo);
if (source == null) { if (source == null) {
return new FailedMediaSource(stream, new IllegalStateException( return new FailedMediaSource(stream, new IllegalStateException(
"MediaSource resolution is null")); "MediaSource cannot be resolved"));
} }
final long expiration = System.currentTimeMillis() + final long expiration = System.currentTimeMillis() +
TimeUnit.MILLISECONDS.convert(2, TimeUnit.HOURS); TimeUnit.MILLISECONDS.convert(expirationTimeMillis, expirationTimeUnit);
return new LoadedMediaSource(source, expiration); return new LoadedMediaSource(source, expiration);
}).onErrorReturn(throwable -> new FailedMediaSource(stream, throwable)); }).onErrorReturn(throwable -> new FailedMediaSource(stream, throwable));
} }

View File

@ -1,6 +1,7 @@
package org.schabi.newpipe.playlist; package org.schabi.newpipe.playlist;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import org.reactivestreams.Subscriber; import org.reactivestreams.Subscriber;
@ -170,7 +171,7 @@ public abstract class PlayQueue implements Serializable {
* Returns the play queue's update broadcast. * Returns the play queue's update broadcast.
* May be null if the play queue message bus is not initialized. * May be null if the play queue message bus is not initialized.
* */ * */
@NonNull @Nullable
public Flowable<PlayQueueEvent> getBroadcastReceiver() { public Flowable<PlayQueueEvent> getBroadcastReceiver() {
return broadcastReceiver; return broadcastReceiver;
} }

View File

@ -99,7 +99,9 @@ public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
} }
}; };
playQueue.getBroadcastReceiver().toObservable().subscribe(observer); if (playQueue.getBroadcastReceiver() != null) {
playQueue.getBroadcastReceiver().toObservable().subscribe(observer);
}
} }
private void onPlayQueueChanged(final PlayQueueEvent message) { private void onPlayQueueChanged(final PlayQueueEvent message) {