Skip to content

Commit

Permalink
Fix playability of Peertube videos, try to add quality selector for l…
Browse files Browse the repository at this point in the history
…ivestreams and ended livestreams
  • Loading branch information
AudricV committed Jul 8, 2021
1 parent 3cdb2df commit 827276e
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 56 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ dependencies {
// name and the commit hash with the commit hash of the (pushed) commit you want to test
// This works thanks to JitPack: https://jitpack.io/
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
implementation 'com.github.TiA4f8R:NewPipeExtractor:7564042a6ab873f257c5557b4edcb36356e0a428'
implementation 'com.github.TiA4f8R:NewPipeExtractor:d8ccc7c825b8f0c313a71a8a3d8c02dc3c83c9a7'

/** Checkstyle **/
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
import com.google.android.exoplayer2.source.MediaSource;

import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.DeliveryMethod;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.player.helper.PlayerDataSource;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.util.ListHelper;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class AudioPlaybackResolver implements PlaybackResolver {
@NonNull
Expand All @@ -31,25 +33,34 @@ public AudioPlaybackResolver(@NonNull final Context context,
@Override
@Nullable
public MediaSource resolve(@NonNull final StreamInfo info) {
final MediaSource liveSource = maybeBuildLiveMediaSource(dataSource, info);
if (liveSource != null) {
return liveSource;
final MediaSource hlsLiveSource = maybeBuildHlsLiveMediaSource(dataSource, info);
if (hlsLiveSource != null) {
return hlsLiveSource;
}
final List<AudioStream> audioStreams = new ArrayList<>(info.getAudioStreams());
removeTorrentStreams(audioStreams);

final int index = ListHelper.getDefaultAudioFormat(context, info.getAudioStreams());
final int index = ListHelper.getDefaultAudioFormat(context, audioStreams);
if (index < 0 || index >= info.getAudioStreams().size()) {
return null;
}

final AudioStream audio = info.getAudioStreams().get(index);
final MediaSourceTag tag = new MediaSourceTag(info);
MediaSource mediaSource = null;
if (audio.getDeliveryMethod() != DeliveryMethod.TORRENT) {
final StreamType streamType = info.getStreamType();
if (streamType != StreamType.LIVE_STREAM
&& streamType != StreamType.AUDIO_LIVE_STREAM) {
try {
mediaSource = buildMediaSource(dataSource, audio, PlayerHelper.cacheKeyOf(info,
audio), tag);
} catch (final IOException ignored) {
}
} else {
try {
mediaSource = buildLiveMediaSource(dataSource, audio, tag);
} catch (final IOException ignored) {
}
}
return mediaSource;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
Expand All @@ -20,45 +19,65 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;

public interface PlaybackResolver extends Resolver<StreamInfo, MediaSource> {

@Nullable
default MediaSource maybeBuildLiveMediaSource(@NonNull final PlayerDataSource dataSource,
@NonNull final StreamInfo info) {
default MediaSource maybeBuildHlsLiveMediaSource(@NonNull final PlayerDataSource dataSource,
@NonNull final StreamInfo info) {
final StreamType streamType = info.getStreamType();
if (!(streamType == StreamType.AUDIO_LIVE_STREAM || streamType == StreamType.LIVE_STREAM)) {
if (!(streamType == StreamType.AUDIO_LIVE_STREAM
|| streamType == StreamType.LIVE_STREAM)) {
return null;
}

final MediaSourceTag tag = new MediaSourceTag(info);
if (!info.getHlsUrl().isEmpty()) {
return buildLiveMediaSource(dataSource, info.getHlsUrl(), C.TYPE_HLS, tag);
} else if (!info.getDashMpdUrl().isEmpty()) {
return buildLiveMediaSource(dataSource, info.getDashMpdUrl(), C.TYPE_DASH, tag);
final String hlsUrl = info.getHlsUrl();
if (!hlsUrl.isEmpty() && info.getDashMpdUrl().isEmpty()) {
final MediaSourceTag tag = new MediaSourceTag(info);
return dataSource.getLiveHlsMediaSourceFactory().setTag(tag)
.createMediaSource(MediaItem.fromUri(hlsUrl));
}

return null;
}

@NonNull
default MediaSource buildLiveMediaSource(@NonNull final PlayerDataSource dataSource,
@NonNull final String sourceUrl,
@C.ContentType final int type,
@NonNull final MediaSourceTag metadata) {
final Uri uri = Uri.parse(sourceUrl);
switch (type) {
case C.TYPE_SS:
return dataSource.getLiveSsMediaSourceFactory().setTag(metadata)
.createMediaSource(MediaItem.fromUri(uri));
case C.TYPE_DASH:
@NonNull final Stream stream,
@NonNull final MediaSourceTag metadata)
throws IOException {
final DeliveryMethod deliveryMethod = stream.getDeliveryMethod();
if (deliveryMethod.equals(DeliveryMethod.DASH)) {
if (stream.isUrl()) {
return dataSource.getLiveDashMediaSourceFactory().setTag(metadata)
.createMediaSource(MediaItem.fromUri(Uri.parse(stream.getContent())));
} else {
final DashManifest dashManifest;
try {
final ByteArrayInputStream dashManifestInput = new ByteArrayInputStream(
stream.getContent().getBytes(StandardCharsets.UTF_8));
dashManifest = new DashManifestParser().parse(Uri.parse(stream.getBaseUrl()),
dashManifestInput);
} catch (final IOException e) {
throw new IOException("Error when parsing manual DASH manifest", e);
}
return dataSource.getLiveDashMediaSourceFactory().setTag(metadata)
.createMediaSource(MediaItem.fromUri(uri));
case C.TYPE_HLS:
.createMediaSource(dashManifest);
}
} else if (deliveryMethod.equals(DeliveryMethod.HLS)) {
if (stream.isUrl()) {
return dataSource.getLiveHlsMediaSourceFactory().setTag(metadata)
.createMediaSource(MediaItem.fromUri(uri));
default:
throw new IllegalStateException("Unsupported type: " + type);
.createMediaSource(MediaItem.fromUri(Uri.parse(stream.getContent())));
} else {
throw new IllegalArgumentException(
"HLS streams which are not URLs are not supported");
}

} else {
throw new IllegalArgumentException(
"Only DASH and HLS streams are supported to create live media sources");
}
}

Expand All @@ -68,21 +87,20 @@ default MediaSource buildMediaSource(@NonNull final PlayerDataSource dataSource,
@NonNull final String cacheKey,
@NonNull final MediaSourceTag metadata)
throws IOException {
final DeliveryMethod deliveryFormat = stream.getDeliveryMethod();
if (deliveryFormat.equals(DeliveryMethod.PROGRESSIVE_HTTP)) {
final DeliveryMethod deliveryMethod = stream.getDeliveryMethod();
if (deliveryMethod.equals(DeliveryMethod.PROGRESSIVE_HTTP)) {
final String url = stream.getContent();
return dataSource.getExtractorMediaSourceFactory(cacheKey).setTag(metadata)
.createMediaSource(MediaItem.fromUri((url)));
} else if (deliveryFormat.equals(DeliveryMethod.HLS)) {
} else if (deliveryMethod.equals(DeliveryMethod.HLS)) {
if (stream.isUrl()) {
return dataSource.getHlsMediaSourceFactory().setTag(metadata)
.createMediaSource(MediaItem.fromUri(Uri.parse(stream.getContent())));
} else {
throw new IllegalArgumentException(
"HLS streams which are not URLs are not supported");
}
// Other contents are not supported right now
} else if (deliveryFormat.equals(DeliveryMethod.DASH)) {
} else if (deliveryMethod.equals(DeliveryMethod.DASH)) {
if (stream.isUrl()) {
return dataSource.getDashMediaSourceFactory().setTag(metadata)
.createMediaSource(MediaItem.fromUri(Uri.parse(stream.getContent())));
Expand All @@ -100,7 +118,17 @@ default MediaSource buildMediaSource(@NonNull final PlayerDataSource dataSource,
.createMediaSource(dashManifest);
}
} else {
throw new IllegalArgumentException("Unsupported delivery type" + deliveryFormat);
throw new IllegalArgumentException("Unsupported delivery type" + deliveryMethod);
}
}

default void removeTorrentStreams(@NonNull final List<? extends Stream> streamList) {
final Iterator<? extends Stream> streamIterator = streamList.iterator();
while (streamIterator.hasNext()) {
final Stream stream = streamIterator.next();
if (stream.getDeliveryMethod() == DeliveryMethod.TORRENT) {
streamIterator.remove();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.DeliveryMethod;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.stream.SubtitlesStream;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.player.helper.PlayerDataSource;
Expand Down Expand Up @@ -47,16 +48,21 @@ public VideoPlaybackResolver(@NonNull final Context context,
@Override
@Nullable
public MediaSource resolve(@NonNull final StreamInfo info) {
final MediaSource liveSource = maybeBuildLiveMediaSource(dataSource, info);
final MediaSource liveSource = maybeBuildHlsLiveMediaSource(dataSource, info);
if (liveSource != null) {
return liveSource;
}

final List<MediaSource> mediaSources = new ArrayList<>();
final List<VideoStream> videoStreams = new ArrayList<>(info.getVideoStreams());
final List<VideoStream> videoOnlyStreams = new ArrayList<>(info.getVideoOnlyStreams());
final StreamType streamType = info.getStreamType();
removeTorrentStreams(videoStreams);
removeTorrentStreams(videoOnlyStreams);

// Create video stream source
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context,
info.getVideoStreams(), info.getVideoOnlyStreams(), false);
videoStreams, videoOnlyStreams, false);
final int index;
if (videos.isEmpty()) {
index = -1;
Expand All @@ -68,32 +74,48 @@ public MediaSource resolve(@NonNull final StreamInfo info) {
final MediaSourceTag tag = new MediaSourceTag(info, videos, index);
@Nullable final VideoStream video = tag.getSelectedVideoStream();

// Torrent streams are not supported by ExoPlayer
if (video != null && video.getDeliveryMethod() != DeliveryMethod.TORRENT) {
try {
final MediaSource streamSource = buildMediaSource(dataSource, video,
PlayerHelper.cacheKeyOf(info, video), tag);
mediaSources.add(streamSource);
} catch (final IOException e) {
return null;
if (video != null) {
if (streamType != StreamType.LIVE_STREAM) {
try {
final MediaSource streamSource = buildMediaSource(dataSource, video,
PlayerHelper.cacheKeyOf(info, video), tag);
mediaSources.add(streamSource);
} catch (final IOException e) {
return null;
}
} else {
try {
final MediaSource streamSource = buildLiveMediaSource(dataSource, video, tag);
mediaSources.add(streamSource);
} catch (final IOException e) {
return null;
}
}
}

// Create optional audio stream source
final List<AudioStream> audioStreams = info.getAudioStreams();
removeTorrentStreams(audioStreams);
final AudioStream audio = audioStreams.isEmpty() ? null : audioStreams.get(
ListHelper.getDefaultAudioFormat(context, audioStreams));
// Use the audio stream if there is no video stream, or
// merge with audio stream in case if video does not contain audio
// Torrent streams are not supported by ExoPlayer
if (audio != null && audio.getDeliveryMethod() != DeliveryMethod.TORRENT
&& (video == null || video.isVideoOnly())) {
try {
final MediaSource audioSource = buildMediaSource(dataSource, audio,
PlayerHelper.cacheKeyOf(info, audio), tag);
mediaSources.add(audioSource);
} catch (final IOException e) {
return null;
if (audio != null && (video == null || video.isVideoOnly())) {
if (streamType != StreamType.LIVE_STREAM) {
try {
final MediaSource audioSource = buildMediaSource(dataSource, audio,
PlayerHelper.cacheKeyOf(info, audio), tag);
mediaSources.add(audioSource);
} catch (final IOException e) {
return null;
}
} else {
try {
final MediaSource audioSource = buildLiveMediaSource(dataSource, audio, tag);
mediaSources.add(audioSource);
} catch (final IOException e) {
return null;
}
}
}

Expand All @@ -107,7 +129,7 @@ public MediaSource resolve(@NonNull final StreamInfo info) {
if (info.getSubtitles() != null) {
for (final SubtitlesStream subtitle : info.getSubtitles()) {
final String mimeType = PlayerHelper.subtitleMimeTypesOf(subtitle.getFormat());
// Torrent streams are not supported by ExoPlayer
// Torrent subtitles are not supported by ExoPlayer
if (mimeType == null || subtitle.getDeliveryMethod() != DeliveryMethod.TORRENT) {
continue;
}
Expand Down

0 comments on commit 827276e

Please sign in to comment.