Merge 71d88d0bc0
into 2af95cc1d4
This commit is contained in:
commit
c9cea600ca
|
@ -42,8 +42,6 @@ import static org.schabi.newpipe.player.notification.NotificationConstants.ACTIO
|
|||
import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_RECREATE_NOTIFICATION;
|
||||
import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_REPEAT;
|
||||
import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_SHUFFLE;
|
||||
import static org.schabi.newpipe.util.ListHelper.getPopupResolutionIndex;
|
||||
import static org.schabi.newpipe.util.ListHelper.getResolutionIndex;
|
||||
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
|
@ -116,7 +114,6 @@ import org.schabi.newpipe.player.ui.PlayerUiList;
|
|||
import org.schabi.newpipe.player.ui.PopupPlayerUi;
|
||||
import org.schabi.newpipe.player.ui.VideoPlayerUi;
|
||||
import org.schabi.newpipe.util.DependentPreferenceHelper;
|
||||
import org.schabi.newpipe.util.ListHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.image.PicassoHelper;
|
||||
import org.schabi.newpipe.util.SerializedCache;
|
||||
|
@ -292,7 +289,7 @@ public final class Player implements PlaybackListener, Listener {
|
|||
context.getString(
|
||||
R.string.use_exoplayer_decoder_fallback_key), false));
|
||||
|
||||
videoResolver = new VideoPlaybackResolver(context, dataSource, getQualityResolver());
|
||||
videoResolver = new VideoPlaybackResolver(context, dataSource);
|
||||
audioResolver = new AudioPlaybackResolver(context, dataSource);
|
||||
|
||||
currentThumbnailTarget = getCurrentThumbnailTarget();
|
||||
|
@ -306,25 +303,6 @@ public final class Player implements PlaybackListener, Listener {
|
|||
new NotificationPlayerUi(this)
|
||||
);
|
||||
}
|
||||
|
||||
private VideoPlaybackResolver.QualityResolver getQualityResolver() {
|
||||
return new VideoPlaybackResolver.QualityResolver() {
|
||||
@Override
|
||||
public int getDefaultResolutionIndex(final List<VideoStream> sortedVideos) {
|
||||
return videoPlayerSelected()
|
||||
? ListHelper.getDefaultResolutionIndex(context, sortedVideos)
|
||||
: ListHelper.getPopupDefaultResolutionIndex(context, sortedVideos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOverrideResolutionIndex(final List<VideoStream> sortedVideos,
|
||||
final String playbackQuality) {
|
||||
return videoPlayerSelected()
|
||||
? getResolutionIndex(context, sortedVideos, playbackQuality)
|
||||
: getPopupResolutionIndex(context, sortedVideos, playbackQuality);
|
||||
}
|
||||
};
|
||||
}
|
||||
//endregion
|
||||
|
||||
|
||||
|
@ -1908,7 +1886,10 @@ public final class Player implements PlaybackListener, Listener {
|
|||
// Note that the video is not fetched when the app is in background because the video
|
||||
// renderer is fully disabled (see useVideoSource method), except for HLS streams
|
||||
// (see https://github.com/google/ExoPlayer/issues/9282).
|
||||
return videoResolver.resolve(info);
|
||||
return videoResolver.resolve(info, videoPlayerSelected()
|
||||
? VideoPlaybackResolver.SelectedPlayer.MAIN
|
||||
: VideoPlaybackResolver.SelectedPlayer.POPUP
|
||||
);
|
||||
}
|
||||
|
||||
public void disablePreloadingOfCurrentTrack() {
|
||||
|
|
|
@ -45,7 +45,6 @@ public class AudioPlaybackResolver implements PlaybackResolver {
|
|||
* @param info of the stream
|
||||
* @return the audio source to use or null if none could be found
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public MediaSource resolve(@NonNull final StreamInfo info) {
|
||||
final MediaSource liveSource = PlaybackResolver.maybeBuildLiveMediaSource(dataSource, info);
|
||||
|
|
|
@ -46,11 +46,10 @@ import java.nio.charset.StandardCharsets;
|
|||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* This interface is just a shorthand for {@link Resolver} with {@link StreamInfo} as source and
|
||||
* {@link MediaSource} as product. It contains many static methods that can be used by classes
|
||||
* This interface contains many static methods that can be used by classes
|
||||
* implementing this interface, and nothing else.
|
||||
*/
|
||||
public interface PlaybackResolver extends Resolver<StreamInfo, MediaSource> {
|
||||
public interface PlaybackResolver {
|
||||
String TAG = PlaybackResolver.class.getSimpleName();
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
package org.schabi.newpipe.player.resolver;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public interface Resolver<Source, Product> {
|
||||
@Nullable
|
||||
Product resolve(@NonNull Source source);
|
||||
}
|
|
@ -39,8 +39,6 @@ public class VideoPlaybackResolver implements PlaybackResolver {
|
|||
private final Context context;
|
||||
@NonNull
|
||||
private final PlayerDataSource dataSource;
|
||||
@NonNull
|
||||
private final QualityResolver qualityResolver;
|
||||
private SourceType streamSourceType;
|
||||
|
||||
@Nullable
|
||||
|
@ -54,17 +52,23 @@ public class VideoPlaybackResolver implements PlaybackResolver {
|
|||
VIDEO_WITH_AUDIO_OR_AUDIO_ONLY
|
||||
}
|
||||
|
||||
public VideoPlaybackResolver(@NonNull final Context context,
|
||||
@NonNull final PlayerDataSource dataSource,
|
||||
@NonNull final QualityResolver qualityResolver) {
|
||||
this.context = context;
|
||||
this.dataSource = dataSource;
|
||||
this.qualityResolver = qualityResolver;
|
||||
/**
|
||||
* Depending on the player we select different video streams.
|
||||
*/
|
||||
public enum SelectedPlayer {
|
||||
MAIN,
|
||||
POPUP
|
||||
}
|
||||
|
||||
public VideoPlaybackResolver(@NonNull final Context context,
|
||||
@NonNull final PlayerDataSource dataSource) {
|
||||
this.context = context;
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public MediaSource resolve(@NonNull final StreamInfo info) {
|
||||
public MediaSource resolve(@NonNull final StreamInfo info,
|
||||
@NonNull final SelectedPlayer selectedPlayer) {
|
||||
final MediaSource liveSource = PlaybackResolver.maybeBuildLiveMediaSource(dataSource, info);
|
||||
if (liveSource != null) {
|
||||
streamSourceType = SourceType.LIVE_STREAM;
|
||||
|
@ -80,14 +84,30 @@ public class VideoPlaybackResolver implements PlaybackResolver {
|
|||
final List<AudioStream> audioStreamsList =
|
||||
getFilteredAudioStreams(context, info.getAudioStreams());
|
||||
|
||||
final int videoIndex;
|
||||
int videoIndex = -999;
|
||||
if (videoStreamsList.isEmpty()) {
|
||||
videoIndex = -1;
|
||||
} else if (playbackQuality == null) {
|
||||
videoIndex = qualityResolver.getDefaultResolutionIndex(videoStreamsList);
|
||||
switch (selectedPlayer) {
|
||||
case MAIN -> {
|
||||
videoIndex = ListHelper.getDefaultResolutionIndex(
|
||||
context,
|
||||
videoStreamsList
|
||||
);
|
||||
}
|
||||
case POPUP -> {
|
||||
videoIndex = ListHelper.getPopupDefaultResolutionIndex(
|
||||
context,
|
||||
videoStreamsList
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
videoIndex = qualityResolver.getOverrideResolutionIndex(videoStreamsList,
|
||||
getPlaybackQuality());
|
||||
videoIndex = ListHelper.getDefaultResolutionWithDefaultFormat(
|
||||
context,
|
||||
getPlaybackQuality(),
|
||||
videoStreamsList
|
||||
);
|
||||
}
|
||||
|
||||
final int audioIndex =
|
||||
|
@ -195,10 +215,4 @@ public class VideoPlaybackResolver implements PlaybackResolver {
|
|||
public void setAudioTrack(@Nullable final String audioLanguage) {
|
||||
this.audioTrack = audioLanguage;
|
||||
}
|
||||
|
||||
public interface QualityResolver {
|
||||
int getDefaultResolutionIndex(List<VideoStream> sortedVideos);
|
||||
|
||||
int getOverrideResolutionIndex(List<VideoStream> sortedVideos, String playbackQuality);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,23 +78,11 @@ public final class ListHelper {
|
|||
*/
|
||||
public static int getDefaultResolutionIndex(final Context context,
|
||||
final List<VideoStream> videoStreams) {
|
||||
final String defaultResolution = computeDefaultResolution(context,
|
||||
final String defaultResolution = getPreferredResolutionOrCurrentLimit(context,
|
||||
R.string.default_resolution_key, R.string.default_resolution_value);
|
||||
return getDefaultResolutionWithDefaultFormat(context, defaultResolution, videoStreams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context Android app context
|
||||
* @param videoStreams list of the video streams to check
|
||||
* @param defaultResolution the default resolution to look for
|
||||
* @return index of the video stream with the default index
|
||||
* @see #getDefaultResolutionIndex(String, String, MediaFormat, List)
|
||||
*/
|
||||
public static int getResolutionIndex(final Context context,
|
||||
final List<VideoStream> videoStreams,
|
||||
final String defaultResolution) {
|
||||
return getDefaultResolutionWithDefaultFormat(context, defaultResolution, videoStreams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context Android app context
|
||||
|
@ -104,24 +92,11 @@ public final class ListHelper {
|
|||
*/
|
||||
public static int getPopupDefaultResolutionIndex(final Context context,
|
||||
final List<VideoStream> videoStreams) {
|
||||
final String defaultResolution = computeDefaultResolution(context,
|
||||
final String defaultResolution = getPreferredResolutionOrCurrentLimit(context,
|
||||
R.string.default_popup_resolution_key, R.string.default_popup_resolution_value);
|
||||
return getDefaultResolutionWithDefaultFormat(context, defaultResolution, videoStreams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context Android app context
|
||||
* @param videoStreams list of the video streams to check
|
||||
* @param defaultResolution the default resolution to look for
|
||||
* @return index of the video stream with the default index
|
||||
* @see #getDefaultResolutionIndex(String, String, MediaFormat, List)
|
||||
*/
|
||||
public static int getPopupResolutionIndex(final Context context,
|
||||
final List<VideoStream> videoStreams,
|
||||
final String defaultResolution) {
|
||||
return getDefaultResolutionWithDefaultFormat(context, defaultResolution, videoStreams);
|
||||
}
|
||||
|
||||
public static int getDefaultAudioFormat(final Context context,
|
||||
final List<AudioStream> audioStreams) {
|
||||
return getAudioIndexByHighestRank(audioStreams,
|
||||
|
@ -390,23 +365,42 @@ public final class ListHelper {
|
|||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static String computeDefaultResolution(@NonNull final Context context, final int key,
|
||||
final int value) {
|
||||
/** Lookup the preferred resolution and the current resolution limit.
|
||||
*
|
||||
* @param context App context
|
||||
* @param defaultResolutionKey The defaultResolution preference key
|
||||
* @param defaultResolutionDefaultValue Default resolution if key does not exist
|
||||
* @return The smaller resolution of either the preference or the current limit.
|
||||
*/
|
||||
private static String getPreferredResolutionOrCurrentLimit(
|
||||
@NonNull final Context context,
|
||||
final int defaultResolutionKey,
|
||||
final int defaultResolutionDefaultValue
|
||||
) {
|
||||
final SharedPreferences preferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
// Load the preferred resolution otherwise the best available
|
||||
String resolution = preferences != null
|
||||
? preferences.getString(context.getString(key), context.getString(value))
|
||||
: context.getString(R.string.best_resolution_key);
|
||||
final String preferredResolution = preferences.getString(
|
||||
context.getString(defaultResolutionKey),
|
||||
context.getString(defaultResolutionDefaultValue)
|
||||
);
|
||||
|
||||
final String maxResolution = getResolutionLimit(context);
|
||||
if (maxResolution != null
|
||||
&& (resolution.equals(context.getString(R.string.best_resolution_key))
|
||||
|| compareVideoStreamResolution(maxResolution, resolution) < 1)) {
|
||||
resolution = maxResolution;
|
||||
// clamp to the currently maximum allowed resolution
|
||||
final String result;
|
||||
final String resolutionLimit = getCurrentResolutionLimit(context);
|
||||
if (resolutionLimit != null
|
||||
&& (
|
||||
// if the preference is best_resolution
|
||||
preferredResolution.equals(context.getString(R.string.best_resolution_key))
|
||||
// or the preference is higher than the current max allowed resolution
|
||||
|| compareVideoStreamResolution(resolutionLimit, preferredResolution) < 1
|
||||
)) {
|
||||
result = resolutionLimit;
|
||||
} else {
|
||||
result = preferredResolution;
|
||||
}
|
||||
return resolution;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -627,14 +621,14 @@ public final class ListHelper {
|
|||
|
||||
/**
|
||||
* Fetches the desired resolution or returns the default if it is not found.
|
||||
* The resolution will be reduced if video chocking is active.
|
||||
* The resolution will be reduced if video choking is active.
|
||||
*
|
||||
* @param context Android app context
|
||||
* @param defaultResolution the default resolution
|
||||
* @param videoStreams the list of video streams to check
|
||||
* @return the index of the preferred video stream
|
||||
*/
|
||||
private static int getDefaultResolutionWithDefaultFormat(@NonNull final Context context,
|
||||
public static int getDefaultResolutionWithDefaultFormat(@NonNull final Context context,
|
||||
final String defaultResolution,
|
||||
final List<VideoStream> videoStreams) {
|
||||
final MediaFormat defaultFormat = getDefaultFormat(context,
|
||||
|
@ -677,6 +671,14 @@ public final class ListHelper {
|
|||
return format;
|
||||
}
|
||||
|
||||
/** #Comparator for two resolution strings.
|
||||
*
|
||||
* See {@link #sortStreamList} for ordering.
|
||||
*
|
||||
* @param r1 first
|
||||
* @param r2 second
|
||||
* @return comparison int
|
||||
*/
|
||||
private static int compareVideoStreamResolution(@NonNull final String r1,
|
||||
@NonNull final String r2) {
|
||||
try {
|
||||
|
@ -693,31 +695,37 @@ public final class ListHelper {
|
|||
}
|
||||
}
|
||||
|
||||
static boolean isLimitingDataUsage(@NonNull final Context context) {
|
||||
return getResolutionLimit(context) != null;
|
||||
/** Does the application have a maximum resolution set?
|
||||
*
|
||||
* @param context App context
|
||||
* @return whether a max resolution is set
|
||||
*/
|
||||
static boolean isCurrentlyLimitingDataUsage(@NonNull final Context context) {
|
||||
return getCurrentResolutionLimit(context) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum resolution allowed.
|
||||
* The maximum current resolution allowed by application settings.
|
||||
* Takes into account whether we are on a metered network.
|
||||
*
|
||||
* @param context App context
|
||||
* @return maximum resolution allowed or null if there is no maximum
|
||||
* @return current maximum resolution allowed or null if there is no maximum
|
||||
*/
|
||||
private static String getResolutionLimit(@NonNull final Context context) {
|
||||
String resolutionLimit = null;
|
||||
private static String getCurrentResolutionLimit(@NonNull final Context context) {
|
||||
String currentResolutionLimit = null;
|
||||
if (isMeteredNetwork(context)) {
|
||||
final SharedPreferences preferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(context);
|
||||
final String defValue = context.getString(R.string.limit_data_usage_none_key);
|
||||
final String value = preferences.getString(
|
||||
context.getString(R.string.limit_mobile_data_usage_key), defValue);
|
||||
resolutionLimit = defValue.equals(value) ? null : value;
|
||||
currentResolutionLimit = defValue.equals(value) ? null : value;
|
||||
}
|
||||
return resolutionLimit;
|
||||
return currentResolutionLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current network is metered (like mobile data)?
|
||||
* Is the current network metered (like mobile data)?
|
||||
*
|
||||
* @param context App context
|
||||
* @return {@code true} if connected to a metered network
|
||||
|
@ -744,7 +752,7 @@ public final class ListHelper {
|
|||
final @NonNull Context context) {
|
||||
final MediaFormat defaultFormat = getDefaultFormat(context,
|
||||
R.string.default_audio_format_key, R.string.default_audio_format_value);
|
||||
return getAudioFormatComparator(defaultFormat, isLimitingDataUsage(context));
|
||||
return getAudioFormatComparator(defaultFormat, isCurrentlyLimitingDataUsage(context));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,7 +31,7 @@ public class SecondaryStreamHelper<T extends Stream> {
|
|||
* Finds an audio stream compatible with the provided video-only stream, so that the two streams
|
||||
* can be combined in a single file by the downloader. If there are multiple available audio
|
||||
* streams, chooses either the highest or the lowest quality one based on
|
||||
* {@link ListHelper#isLimitingDataUsage(Context)}.
|
||||
* {@link ListHelper#isCurrentlyLimitingDataUsage(Context)}.
|
||||
*
|
||||
* @param context Android context
|
||||
* @param audioStreams list of audio streams
|
||||
|
@ -56,7 +56,7 @@ public class SecondaryStreamHelper<T extends Stream> {
|
|||
}
|
||||
|
||||
final boolean m4v = mediaFormat == MediaFormat.MPEG_4;
|
||||
final boolean isLimitingDataUsage = ListHelper.isLimitingDataUsage(context);
|
||||
final boolean isLimitingDataUsage = ListHelper.isCurrentlyLimitingDataUsage(context);
|
||||
|
||||
Comparator<AudioStream> comparator = ListHelper.getAudioFormatComparator(
|
||||
m4v ? MediaFormat.M4A : MediaFormat.WEBMA, isLimitingDataUsage);
|
||||
|
|
Loading…
Reference in New Issue