Move stream's cache key generation in PlaybackResolver and improve PlaybackResolver's code

This commit is contained in:
Stypox 2022-05-08 14:39:24 +02:00 committed by AudricV
parent fbee310261
commit ef20d9b91a
No known key found for this signature in database
GPG key ID: DA92EC7905614198
4 changed files with 79 additions and 54 deletions

View file

@ -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_ALL;
import static com.google.android.exoplayer2.Player.REPEAT_MODE_OFF; import static com.google.android.exoplayer2.Player.REPEAT_MODE_OFF;
import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE; 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.IDLE_WINDOW_FLAGS;
import static org.schabi.newpipe.player.Player.PLAYER_TYPE; import static org.schabi.newpipe.player.Player.PLAYER_TYPE;
import static org.schabi.newpipe.player.helper.PlayerHelper.AutoplayType.AUTOPLAY_TYPE_ALWAYS; 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.R;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.MediaFormat; 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.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.SubtitlesStream; 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.extractor.utils.Utils;
import org.schabi.newpipe.player.MainPlayer; import org.schabi.newpipe.player.MainPlayer;
import org.schabi.newpipe.player.Player; 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, * Given a {@link StreamInfo} and the existing queue items,
* provide the {@link SinglePlayQueue} consisting of the next video for auto queueing. * provide the {@link SinglePlayQueue} consisting of the next video for auto queueing.

View file

@ -13,7 +13,6 @@ import com.google.android.exoplayer2.source.MediaSource;
import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.player.helper.PlayerDataSource; 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.MediaItemTag;
import org.schabi.newpipe.player.mediaitem.StreamInfoTag; import org.schabi.newpipe.player.mediaitem.StreamInfoTag;
import org.schabi.newpipe.util.ListHelper; import org.schabi.newpipe.util.ListHelper;
@ -57,7 +56,7 @@ public class AudioPlaybackResolver implements PlaybackResolver {
try { try {
return PlaybackResolver.buildMediaSource( return PlaybackResolver.buildMediaSource(
dataSource, audio, info, PlayerHelper.cacheKeyOf(info, audio), tag); dataSource, audio, info, PlaybackResolver.cacheKeyOf(info, audio), tag);
} catch (final IOException e) { } catch (final IOException e) {
Log.e(TAG, "Unable to create audio source:", e); Log.e(TAG, "Unable to create audio source:", e);
return null; return null;

View file

@ -1,5 +1,7 @@
package org.schabi.newpipe.player.resolver; 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.extractor.utils.Utils.isNullOrEmpty;
import static org.schabi.newpipe.player.helper.PlayerDataSource.LIVE_STREAM_EDGE_GAP_MILLIS; 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.SsManifest;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser; 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.ServiceList;
import org.schabi.newpipe.extractor.services.youtube.ItagItem; import org.schabi.newpipe.extractor.services.youtube.ItagItem;
import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.CreationException; import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.CreationException;
@ -49,6 +52,79 @@ import java.util.Objects;
public interface PlaybackResolver extends Resolver<StreamInfo, MediaSource> { public interface PlaybackResolver extends Resolver<StreamInfo, MediaSource> {
String TAG = PlaybackResolver.class.getSimpleName(); 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 @Nullable
static MediaSource maybeBuildLiveMediaSource(@NonNull final PlayerDataSource dataSource, static MediaSource maybeBuildLiveMediaSource(@NonNull final PlayerDataSource dataSource,
@NonNull final StreamInfo info) { @NonNull final StreamInfo info) {

View file

@ -95,7 +95,7 @@ public class VideoPlaybackResolver implements PlaybackResolver {
if (video != null) { if (video != null) {
try { try {
final MediaSource streamSource = PlaybackResolver.buildMediaSource( final MediaSource streamSource = PlaybackResolver.buildMediaSource(
dataSource, video, info, PlayerHelper.cacheKeyOf(info, video), tag); dataSource, video, info, PlaybackResolver.cacheKeyOf(info, video), tag);
mediaSources.add(streamSource); mediaSources.add(streamSource);
} catch (final IOException e) { } catch (final IOException e) {
Log.e(TAG, "Unable to create video source:", 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())) { if (audio != null && (video == null || video.isVideoOnly())) {
try { try {
final MediaSource audioSource = PlaybackResolver.buildMediaSource( final MediaSource audioSource = PlaybackResolver.buildMediaSource(
dataSource, audio, info, PlayerHelper.cacheKeyOf(info, audio), tag); dataSource, audio, info, PlaybackResolver.cacheKeyOf(info, audio), tag);
mediaSources.add(audioSource); mediaSources.add(audioSource);
streamSourceType = SourceType.VIDEO_WITH_SEPARATED_AUDIO; streamSourceType = SourceType.VIDEO_WITH_SEPARATED_AUDIO;
} catch (final IOException e) { } catch (final IOException e) {