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

View File

@ -319,35 +319,27 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
recordManager = null;
}
public MediaSource buildMediaSource(String url) {
return buildMediaSource(url, "");
}
public MediaSource buildMediaSource(String url, String overrideExtension) {
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);
int type = TextUtils.isEmpty(overrideExtension) ? Util.inferContentType(uri) : Util.inferContentType("." + overrideExtension);
MediaSource mediaSource;
int type = TextUtils.isEmpty(overrideExtension) ? Util.inferContentType(uri) :
Util.inferContentType("." + overrideExtension);
switch (type) {
case C.TYPE_SS:
mediaSource = ssMediaSourceFactory.createMediaSource(uri);
break;
return ssMediaSourceFactory.createMediaSource(uri);
case C.TYPE_DASH:
mediaSource = dashMediaSourceFactory.createMediaSource(uri);
break;
return dashMediaSourceFactory.createMediaSource(uri);
case C.TYPE_HLS:
mediaSource = hlsMediaSourceFactory.createMediaSource(uri);
break;
return hlsMediaSourceFactory.createMediaSource(uri);
case C.TYPE_OTHER:
mediaSource = extractorMediaSourceFactory.createMediaSource(uri);
break;
return extractorMediaSourceFactory.createMediaSource(uri);
default: {
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) {
if (DEBUG) Log.d(TAG, "onTimelineChanged(), timeline size = " + timeline.getWindowCount());
if (playbackManager != null) {
playbackManager.load();
switch (reason) {
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
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
@ -732,9 +730,9 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
@Override
public MediaSource sourceOf(PlayQueueItem item, StreamInfo info) {
if (!info.getHlsUrl().isEmpty()) {
return buildMediaSource(info.getHlsUrl());
return buildMediaSource(info.getHlsUrl(), "m3u8");
} else if (!info.getDashMpdUrl().isEmpty()) {
return buildMediaSource(info.getDashMpdUrl());
return buildMediaSource(info.getDashMpdUrl(), "mpd");
}
return null;

View File

@ -25,6 +25,10 @@ public class FailedMediaSource implements ManagedMediaSource {
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,
@NonNull final Throwable error) {
this.playQueueItem = playQueueItem;

View File

@ -40,6 +40,8 @@ public class MediaSourceManager {
private final int windowSize;
private final PlaybackListener playbackListener;
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)
// 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,
@NonNull final PlayQueue playQueue) {
this(listener, playQueue, 1, 400L);
this(listener, playQueue, 1, 400L, 2, TimeUnit.HOURS);
}
private MediaSourceManager(@NonNull final PlaybackListener listener,
@NonNull final PlayQueue playQueue,
final int windowSize,
final long loadDebounceMillis) {
final long loadDebounceMillis,
final long expirationTimeMillis,
@NonNull final TimeUnit expirationTimeUnit) {
if (windowSize <= 0) {
throw new UnsupportedOperationException(
"MediaSourceManager window size must be greater than 0");
@ -77,6 +81,8 @@ public class MediaSourceManager {
this.playQueue = playQueue;
this.windowSize = windowSize;
this.loadDebounceMillis = loadDebounceMillis;
this.expirationTimeMillis = expirationTimeMillis;
this.expirationTimeUnit = expirationTimeUnit;
this.loaderReactor = new CompositeDisposable();
this.debouncedLoadSignal = PublishSubject.create();
@ -87,9 +93,11 @@ public class MediaSourceManager {
this.syncReactor = new SerialDisposable();
this.loadingItems = Collections.synchronizedSet(new HashSet<>());
playQueue.getBroadcastReceiver()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getReactor());
if (playQueue.getBroadcastReceiver() != null) {
playQueue.getBroadcastReceiver()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getReactor());
}
}
/*//////////////////////////////////////////////////////////////////////////
@ -225,7 +233,7 @@ public class MediaSourceManager {
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() {
return sources != null && playQueue != null && sources.getSize() > playQueue.getIndex() &&
!(sources.getMediaSource(playQueue.getIndex()) instanceof PlaceholderMediaSource);
@ -351,11 +359,11 @@ public class MediaSourceManager {
final MediaSource source = playbackListener.sourceOf(stream, streamInfo);
if (source == null) {
return new FailedMediaSource(stream, new IllegalStateException(
"MediaSource resolution is null"));
"MediaSource cannot be resolved"));
}
final long expiration = System.currentTimeMillis() +
TimeUnit.MILLISECONDS.convert(2, TimeUnit.HOURS);
TimeUnit.MILLISECONDS.convert(expirationTimeMillis, expirationTimeUnit);
return new LoadedMediaSource(source, expiration);
}).onErrorReturn(throwable -> new FailedMediaSource(stream, throwable));
}

View File

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