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.
This commit is contained in:
evermind 2022-12-17 00:38:49 +01:00
parent 5942add141
commit dfc46c3b6c
2 changed files with 56 additions and 6 deletions

View file

@ -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<AudioStream> 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<AudioStream> audioStreams = getNonTorrentStreams(info.getAudioStreams());
if (!audioStreams.isEmpty()) {
final int index = ListHelper.getDefaultAudioFormat(context, audioStreams);
return getStreamForIndex(index, audioStreams);
} else {
final List<VideoStream> 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<? extends Stream> streams) {
if (index >= 0 && index < streams.size()) {
return streams.get(index);
}
return null;
}
}

View file

@ -158,6 +158,26 @@ public interface PlaybackResolver extends Resolver<StreamInfo, MediaSource> {
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