From dfc46c3b6cb53aa4297be9c9069c2bc823b1a096 Mon Sep 17 00:00:00 2001 From: evermind Date: Sat, 17 Dec 2022 00:38:49 +0100 Subject: [PATCH 1/2] Support audio only background for services only supporting video streams Some services may only have video streams and no separate audio streams available. This commit will add audio background playback support for those services. It uses the video source as audio source for background playback. --- .../resolver/AudioPlaybackResolver.java | 42 ++++++++++++++++--- .../player/resolver/PlaybackResolver.java | 20 +++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) 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 934beba19..e87c93114 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 @@ -11,7 +11,9 @@ import androidx.annotation.Nullable; import com.google.android.exoplayer2.source.MediaSource; import org.schabi.newpipe.extractor.stream.AudioStream; +import org.schabi.newpipe.extractor.stream.Stream; import org.schabi.newpipe.extractor.stream.StreamInfo; +import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.player.helper.PlayerDataSource; import org.schabi.newpipe.player.mediaitem.MediaItemTag; import org.schabi.newpipe.player.mediaitem.StreamInfoTag; @@ -41,22 +43,50 @@ public class AudioPlaybackResolver implements PlaybackResolver { return liveSource; } - final List audioStreams = getNonTorrentStreams(info.getAudioStreams()); - - final int index = ListHelper.getDefaultAudioFormat(context, audioStreams); - if (index < 0 || index >= info.getAudioStreams().size()) { + final Stream stream = getAudioSource(info); + if (stream == null) { return null; } - final AudioStream audio = info.getAudioStreams().get(index); final MediaItemTag tag = StreamInfoTag.of(info); try { return PlaybackResolver.buildMediaSource( - dataSource, audio, info, PlaybackResolver.cacheKeyOf(info, audio), tag); + dataSource, stream, info, PlaybackResolver.cacheKeyOf(info, stream), tag); } catch (final ResolverException e) { Log.e(TAG, "Unable to create audio source", e); return null; } } + + /** + * Get a stream to be played as audio. If a service has no separate {@link AudioStream}s we + * use a video stream as audio source to support audio background playback. + * + * @param info of the stream + * @return the audio source to use or null if none could be found + */ + @Nullable + private Stream getAudioSource(@NonNull final StreamInfo info) { + final List audioStreams = getNonTorrentStreams(info.getAudioStreams()); + if (!audioStreams.isEmpty()) { + final int index = ListHelper.getDefaultAudioFormat(context, audioStreams); + return getStreamForIndex(index, audioStreams); + } else { + final List videoStreams = getNonTorrentStreams(info.getVideoStreams()); + if (!videoStreams.isEmpty()) { + final int index = ListHelper.getDefaultResolutionIndex(context, videoStreams); + return getStreamForIndex(index, videoStreams); + } + } + return null; + } + + @Nullable + Stream getStreamForIndex(final int index, @NonNull final List streams) { + if (index >= 0 && index < streams.size()) { + return streams.get(index); + } + 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 ead127250..9c8cbb8f6 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 @@ -158,6 +158,26 @@ public interface PlaybackResolver extends Resolver { return cacheKey.toString(); } + + /** + * Use common base type {@link Stream} to handle {@link AudioStream} or {@link VideoStream} + * transparently. For more info see {@link #cacheKeyOf(StreamInfo, AudioStream)} or + * {@link #cacheKeyOf(StreamInfo, VideoStream)}. + * + * @param info the {@link StreamInfo stream info}, to distinguish between streams with + * the same features but coming from different stream infos + * @param stream the {@link Stream} ({@link AudioStream} or {@link VideoStream}) + * for which the cache key should be created + * @return a key to be used to store the cache of the provided {@link Stream} + */ + static String cacheKeyOf(final StreamInfo info, final Stream stream) { + if (stream instanceof AudioStream) { + return cacheKeyOf(info, (AudioStream) stream); + } else if (stream instanceof VideoStream) { + return cacheKeyOf(info, (VideoStream) stream); + } + throw new RuntimeException("no audio or video stream. That should never happen"); + } //endregion From f4a5b3bcbf2e04ee860a9b906f8471f65eb69468 Mon Sep 17 00:00:00 2001 From: evermind Date: Sun, 1 Jan 2023 21:47:47 +0100 Subject: [PATCH 2/2] set 'playback in background button' visible if there are videostreams --- .../schabi/newpipe/fragments/detail/VideoDetailFragment.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index bf0b63e4b..705427c23 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -1666,8 +1666,9 @@ public final class VideoDetailFragment binding.detailControlsDownload.setVisibility( StreamTypeUtil.isLiveStream(info.getStreamType()) ? View.GONE : View.VISIBLE); - binding.detailControlsBackground.setVisibility(info.getAudioStreams().isEmpty() - ? View.GONE : View.VISIBLE); + binding.detailControlsBackground.setVisibility( + info.getAudioStreams().isEmpty() && info.getVideoStreams().isEmpty() + ? View.GONE : View.VISIBLE); final boolean noVideoStreams = info.getVideoStreams().isEmpty() && info.getVideoOnlyStreams().isEmpty();