From ef20d9b91a7cca87d2dc1f4860d1ee3b80f98178 Mon Sep 17 00:00:00 2001 From: Stypox Date: Sun, 8 May 2022 14:39:24 +0200 Subject: [PATCH] Move stream's cache key generation in PlaybackResolver and improve PlaybackResolver's code --- .../newpipe/player/helper/PlayerHelper.java | 50 ------------ .../resolver/AudioPlaybackResolver.java | 3 +- .../player/resolver/PlaybackResolver.java | 76 +++++++++++++++++++ .../resolver/VideoPlaybackResolver.java | 4 +- 4 files changed, 79 insertions(+), 54 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java index d924f9314..2131861bf 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java @@ -3,8 +3,6 @@ package org.schabi.newpipe.player.helper; import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL; import static com.google.android.exoplayer2.Player.REPEAT_MODE_OFF; import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE; -import static org.schabi.newpipe.extractor.stream.AudioStream.UNKNOWN_BITRATE; -import static org.schabi.newpipe.extractor.stream.VideoStream.RESOLUTION_UNKNOWN; import static org.schabi.newpipe.player.Player.IDLE_WINDOW_FLAGS; import static org.schabi.newpipe.player.Player.PLAYER_TYPE; import static org.schabi.newpipe.player.helper.PlayerHelper.AutoplayType.AUTOPLAY_TYPE_ALWAYS; @@ -47,11 +45,9 @@ import com.google.android.exoplayer2.util.MimeTypes; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.MediaFormat; -import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.SubtitlesStream; -import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.player.MainPlayer; import org.schabi.newpipe.player.Player; @@ -197,52 +193,6 @@ public final class PlayerHelper { } } - @NonNull - public static String cacheKeyOf(@NonNull final StreamInfo info, - @NonNull final VideoStream videoStream) { - String cacheKey = info.getUrl() + " " + videoStream.getId(); - - final String resolution = videoStream.getResolution(); - final MediaFormat mediaFormat = videoStream.getFormat(); - if (resolution.equals(RESOLUTION_UNKNOWN) && mediaFormat == null) { - // The hash code is only used in the cache key in the case when the resolution and the - // media format are unknown - cacheKey += " " + videoStream.hashCode(); - } else { - if (mediaFormat != null) { - cacheKey += " " + videoStream.getFormat().getName(); - } - if (!resolution.equals(RESOLUTION_UNKNOWN)) { - cacheKey += " " + resolution; - } - } - - return cacheKey; - } - - @NonNull - public static String cacheKeyOf(@NonNull final StreamInfo info, - @NonNull final AudioStream audioStream) { - String cacheKey = info.getUrl() + " " + audioStream.getId(); - - final int averageBitrate = audioStream.getAverageBitrate(); - final MediaFormat mediaFormat = audioStream.getFormat(); - if (averageBitrate == UNKNOWN_BITRATE && mediaFormat == null) { - // The hash code is only used in the cache key in the case when the resolution and the - // media format are unknown - cacheKey += " " + audioStream.hashCode(); - } else { - if (mediaFormat != null) { - cacheKey += " " + audioStream.getFormat().getName(); - } - if (averageBitrate != UNKNOWN_BITRATE) { - cacheKey += " " + averageBitrate; - } - } - - return cacheKey; - } - /** * Given a {@link StreamInfo} and the existing queue items, * provide the {@link SinglePlayQueue} consisting of the next video for auto queueing. diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java index 765475b2f..85c15faf1 100644 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java +++ b/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java @@ -13,7 +13,6 @@ import com.google.android.exoplayer2.source.MediaSource; import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.player.helper.PlayerDataSource; -import org.schabi.newpipe.player.helper.PlayerHelper; import org.schabi.newpipe.player.mediaitem.MediaItemTag; import org.schabi.newpipe.player.mediaitem.StreamInfoTag; import org.schabi.newpipe.util.ListHelper; @@ -57,7 +56,7 @@ public class AudioPlaybackResolver implements PlaybackResolver { try { return PlaybackResolver.buildMediaSource( - dataSource, audio, info, PlayerHelper.cacheKeyOf(info, audio), tag); + dataSource, audio, info, PlaybackResolver.cacheKeyOf(info, audio), tag); } catch (final IOException e) { Log.e(TAG, "Unable to create audio source:", e); return null; diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java index 4c1b67dfc..3cbca7628 100644 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java +++ b/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java @@ -1,5 +1,7 @@ package org.schabi.newpipe.player.resolver; +import static org.schabi.newpipe.extractor.stream.AudioStream.UNKNOWN_BITRATE; +import static org.schabi.newpipe.extractor.stream.VideoStream.RESOLUTION_UNKNOWN; import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import static org.schabi.newpipe.player.helper.PlayerDataSource.LIVE_STREAM_EDGE_GAP_MILLIS; @@ -20,6 +22,7 @@ import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest; import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser; +import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.extractor.services.youtube.ItagItem; import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.CreationException; @@ -49,6 +52,79 @@ import java.util.Objects; public interface PlaybackResolver extends Resolver { String TAG = PlaybackResolver.class.getSimpleName(); + @NonNull + private static StringBuilder commonCacheKeyOf(@NonNull final StreamInfo info, + @NonNull final Stream stream, + final boolean resolutionOrBitrateUnknown) { + // stream info service id + final StringBuilder cacheKey = new StringBuilder(info.getServiceId()); + + // stream info id + cacheKey.append(" "); + cacheKey.append(info.getId()); + + // stream id (even if unknown) + cacheKey.append(" "); + cacheKey.append(stream.getId()); + + // mediaFormat (if not null) + final MediaFormat mediaFormat = stream.getFormat(); + if (mediaFormat != null) { + cacheKey.append(" "); + cacheKey.append(mediaFormat.getName()); + } + + // content (only if other information is missing) + // If the media format and the resolution/bitrate are both missing, then we don't have + // enough information to distinguish this stream from other streams. + // So, only in that case, we use the content (i.e. url or manifest) to differentiate + // between streams. + // Note that if the content were used even when other information is present, then two + // streams with the same stats but with different contents (e.g. because the url was + // refreshed) will be considered different (i.e. with a different cacheKey), making the + // cache useless. + if (resolutionOrBitrateUnknown && mediaFormat == null) { + cacheKey.append(" "); + Objects.hash(stream.getContent(), stream.getManifestUrl()); + } + + return cacheKey; + } + + @NonNull + static String cacheKeyOf(@NonNull final StreamInfo info, + @NonNull final VideoStream videoStream) { + final boolean resolutionUnknown = videoStream.getResolution().equals(RESOLUTION_UNKNOWN); + final StringBuilder cacheKey = commonCacheKeyOf(info, videoStream, resolutionUnknown); + + // resolution (if known) + if (!resolutionUnknown) { + cacheKey.append(" "); + cacheKey.append(videoStream.getResolution()); + } + + // isVideoOnly + cacheKey.append(" "); + cacheKey.append(videoStream.isVideoOnly()); + + return cacheKey.toString(); + } + + @NonNull + static String cacheKeyOf(@NonNull final StreamInfo info, + @NonNull final AudioStream audioStream) { + final boolean averageBitrateUnknown = audioStream.getAverageBitrate() == UNKNOWN_BITRATE; + final StringBuilder cacheKey = commonCacheKeyOf(info, audioStream, averageBitrateUnknown); + + // averageBitrate (if known) + if (!averageBitrateUnknown) { + cacheKey.append(" "); + cacheKey.append(audioStream.getAverageBitrate()); + } + + return cacheKey.toString(); + } + @Nullable static MediaSource maybeBuildLiveMediaSource(@NonNull final PlayerDataSource dataSource, @NonNull final StreamInfo info) { diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java index 24ca2e63a..317c49fc9 100644 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java +++ b/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java @@ -95,7 +95,7 @@ public class VideoPlaybackResolver implements PlaybackResolver { if (video != null) { try { final MediaSource streamSource = PlaybackResolver.buildMediaSource( - dataSource, video, info, PlayerHelper.cacheKeyOf(info, video), tag); + dataSource, video, info, PlaybackResolver.cacheKeyOf(info, video), tag); mediaSources.add(streamSource); } catch (final IOException e) { Log.e(TAG, "Unable to create video source:", e); @@ -114,7 +114,7 @@ public class VideoPlaybackResolver implements PlaybackResolver { if (audio != null && (video == null || video.isVideoOnly())) { try { final MediaSource audioSource = PlaybackResolver.buildMediaSource( - dataSource, audio, info, PlayerHelper.cacheKeyOf(info, audio), tag); + dataSource, audio, info, PlaybackResolver.cacheKeyOf(info, audio), tag); mediaSources.add(audioSource); streamSourceType = SourceType.VIDEO_WITH_SEPARATED_AUDIO; } catch (final IOException e) {