Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify selection of video stream #10730

Open
wants to merge 5 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 5 additions & 24 deletions app/src/main/java/org/schabi/newpipe/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@
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;

Expand Down Expand Up @@ -116,7 +114,6 @@
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;
Expand Down Expand Up @@ -292,7 +289,7 @@ public Player(@NonNull final PlayerService service) {
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();
Expand All @@ -306,25 +303,6 @@ public Player(@NonNull final PlayerService service) {
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


Expand Down Expand Up @@ -1908,7 +1886,10 @@ public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
// 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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ public AudioPlaybackResolver(@NonNull final Context context,
* @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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,10 @@
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();


Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -54,17 +52,23 @@ public enum SourceType {
VIDEO_WITH_AUDIO_OR_AUDIO_ONLY
}

/**
* Depending on the player we select different video streams.
*/
public enum SelectedPlayer {
MAIN,
POPUP
}

public VideoPlaybackResolver(@NonNull final Context context,
@NonNull final PlayerDataSource dataSource,
@NonNull final QualityResolver qualityResolver) {
@NonNull final PlayerDataSource dataSource) {
this.context = context;
this.dataSource = dataSource;
this.qualityResolver = qualityResolver;
}

@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;
Expand All @@ -80,14 +84,30 @@ public MediaSource resolve(@NonNull final StreamInfo info) {
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 =
Expand Down Expand Up @@ -195,10 +215,4 @@ public String getAudioTrack() {
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);
}
}
110 changes: 59 additions & 51 deletions app/src/main/java/org/schabi/newpipe/util/ListHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,23 +78,11 @@ private 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
Expand All @@ -104,24 +92,11 @@ public static int getResolutionIndex(final Context context,
*/
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,
Expand Down Expand Up @@ -390,23 +365,42 @@ private static <S extends Stream> List<S> getFilteredStreamList(
.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 maxResolution = getResolutionLimit(context);
if (maxResolution != null
&& (resolution.equals(context.getString(R.string.best_resolution_key))
|| compareVideoStreamResolution(maxResolution, resolution) < 1)) {
resolution = maxResolution;
final String preferredResolution = preferences.getString(
context.getString(defaultResolutionKey),
context.getString(defaultResolutionDefaultValue)
);

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

/**
Expand Down Expand Up @@ -627,14 +621,14 @@ static int getVideoStreamIndex(@NonNull final String targetResolution,

/**
* 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,
Expand Down Expand Up @@ -680,6 +674,14 @@ private static MediaFormat getMediaFormatFromKey(@NonNull final Context context,
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 {
Expand All @@ -696,31 +698,37 @@ private static int compareVideoStreamResolution(@NonNull final String r1,
}
}

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
Expand All @@ -747,7 +755,7 @@ private static Comparator<AudioStream> getAudioFormatComparator(
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));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public SecondaryStreamHelper(@NonNull final StreamInfoWrapper<T> streams,
* 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
Expand All @@ -56,7 +56,7 @@ public static AudioStream getAudioStreamFor(@NonNull final Context context,
}

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);
Expand Down