From 9870fe3f182b530139fc543f50c575dc726a627a Mon Sep 17 00:00:00 2001 From: Stefan Schueller Date: Sun, 24 Jan 2021 17:58:40 +0100 Subject: [PATCH] feat: added HLS playback support --- .../fragment/VideoPlayerFragment.java | 28 +++++-- .../net/schueller/peertube/model/State.java | 39 ++++++++++ .../peertube/model/StreamingPlaylist.java | 77 +++++++++++++++++++ .../net/schueller/peertube/model/Video.java | 52 ++++++++++++- .../peertube/service/VideoPlayerService.java | 16 +++- 5 files changed, 198 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/net/schueller/peertube/model/State.java create mode 100644 app/src/main/java/net/schueller/peertube/model/StreamingPlaylist.java diff --git a/app/src/main/java/net/schueller/peertube/fragment/VideoPlayerFragment.java b/app/src/main/java/net/schueller/peertube/fragment/VideoPlayerFragment.java index b51a988..12faf1b 100644 --- a/app/src/main/java/net/schueller/peertube/fragment/VideoPlayerFragment.java +++ b/app/src/main/java/net/schueller/peertube/fragment/VideoPlayerFragment.java @@ -241,17 +241,29 @@ public class VideoPlayerFragment extends Fragment implements VideoRendererEventL Integer videoQuality = sharedPref.getInt(getString(R.string.pref_quality_key), 999999); + String urlToPlay = null; + boolean isHLS = false; + + // try HLS stream first // get video qualities // TODO: if auto is set all versions except 0p should be added to a track and have exoplayer auto select optimal bitrate - if (video.getFiles().size() > 0) { - String urlToPlay = video.getFiles().get( 0 ).getFileUrl(); // default, take first found, usually highest res - for ( File file : video.getFiles() ) { - // Set quality if it matches - if ( file.getResolution().getId().equals( videoQuality ) ) { - urlToPlay = file.getFileUrl(); + if (video.getStreamingPlaylists().size() > 0) { + urlToPlay = video.getStreamingPlaylists().get( 0 ).getPlaylistUrl(); + isHLS = true; + } else { + if (video.getFiles().size() > 0) { + urlToPlay = video.getFiles().get( 0 ).getFileUrl(); // default, take first found, usually highest res + for ( File file : video.getFiles() ) { + // Set quality if it matches + if ( file.getResolution().getId().equals( videoQuality ) ) { + urlToPlay = file.getFileUrl(); + } } } - mService.setCurrentStreamUrl( urlToPlay ); + } + + if (!urlToPlay.isEmpty()) { + mService.setCurrentStreamUrl( urlToPlay, isHLS); torrentStatus.setVisibility(View.GONE); startPlayer(); } else { @@ -357,7 +369,7 @@ public class VideoPlayerFragment extends Fragment implements VideoRendererEventL public void onStreamReady(Torrent torrent) { String videopath = Uri.fromFile(torrent.getVideoFile()).toString(); Log.d(TAG, "Ready! torrentStream videopath:" + videopath); - mService.setCurrentStreamUrl(videopath); + mService.setCurrentStreamUrl(videopath, false); startPlayer(); } diff --git a/app/src/main/java/net/schueller/peertube/model/State.java b/app/src/main/java/net/schueller/peertube/model/State.java new file mode 100644 index 0000000..c0bc50d --- /dev/null +++ b/app/src/main/java/net/schueller/peertube/model/State.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 Stefan Schüller + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package net.schueller.peertube.model; + +public class State { + + private Integer id; + private String label; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } +} diff --git a/app/src/main/java/net/schueller/peertube/model/StreamingPlaylist.java b/app/src/main/java/net/schueller/peertube/model/StreamingPlaylist.java new file mode 100644 index 0000000..eb43120 --- /dev/null +++ b/app/src/main/java/net/schueller/peertube/model/StreamingPlaylist.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 Stefan Schüller + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package net.schueller.peertube.model; + +import java.util.ArrayList; + +public class StreamingPlaylist { + + private Integer id; + private Integer type; + private String playlistUrl; + private String segmentsSha256Url; + private ArrayList redundancies; + private ArrayList files; + + public Integer getId() { + return id; + } + + public void setId(final Integer id) { + this.id = id; + } + + public Integer getType() { + return type; + } + + public void setType(final Integer type) { + this.type = type; + } + + public String getPlaylistUrl() { + return playlistUrl; + } + + public void setPlaylistUrl(final String playlistUrl) { + this.playlistUrl = playlistUrl; + } + + public String getSegmentsSha256Url() { + return segmentsSha256Url; + } + + public void setSegmentsSha256Url(final String segmentsSha256Url) { + this.segmentsSha256Url = segmentsSha256Url; + } + + public ArrayList getRedundancies() { + return redundancies; + } + + public void setRedundancies(final ArrayList redundancies) { + this.redundancies = redundancies; + } + + public ArrayList getFiles() { + return files; + } + + public void setFiles(final ArrayList files) { + this.files = files; + } +} diff --git a/app/src/main/java/net/schueller/peertube/model/Video.java b/app/src/main/java/net/schueller/peertube/model/Video.java index f82d2c1..c6b0d0c 100644 --- a/app/src/main/java/net/schueller/peertube/model/Video.java +++ b/app/src/main/java/net/schueller/peertube/model/Video.java @@ -60,11 +60,19 @@ public class Video { private Channel channel; private Account account; - private ArrayList tags; + private ArrayList tags; private Boolean commentsEnabled; + private Boolean downloadEnabled; + private Boolean waitTranscoding; + + private State state; + private ArrayList trackerUrls; + private ArrayList files; + private ArrayList streamingPlaylists; + public Video() { } @@ -253,11 +261,11 @@ public class Video { this.account = account; } - public ArrayList getTags() { + public ArrayList getTags() { return tags; } - public void setTags(ArrayList tags) { + public void setTags(ArrayList tags) { this.tags = tags; } @@ -277,7 +285,45 @@ public class Video { this.files = files; } + public Boolean getDownloadEnabled() { + return downloadEnabled; + } + public void setDownloadEnabled(final Boolean downloadEnabled) { + this.downloadEnabled = downloadEnabled; + } + + public Boolean getWaitTranscoding() { + return waitTranscoding; + } + + public void setWaitTranscoding(final Boolean waitTranscoding) { + this.waitTranscoding = waitTranscoding; + } + + public State getState() { + return state; + } + + public void setState(final State state) { + this.state = state; + } + + public ArrayList getTrackerUrls() { + return trackerUrls; + } + + public void setTrackerUrls(final ArrayList trackerUrls) { + this.trackerUrls = trackerUrls; + } + + public ArrayList getStreamingPlaylists() { + return streamingPlaylists; + } + + public void setStreamingPlaylists(final ArrayList streamingPlaylists) { + this.streamingPlaylists = streamingPlaylists; + } public static MediaDescriptionCompat getMediaDescription(Context context, Video video) { diff --git a/app/src/main/java/net/schueller/peertube/service/VideoPlayerService.java b/app/src/main/java/net/schueller/peertube/service/VideoPlayerService.java index 0e73eca..9c5fde3 100644 --- a/app/src/main/java/net/schueller/peertube/service/VideoPlayerService.java +++ b/app/src/main/java/net/schueller/peertube/service/VideoPlayerService.java @@ -56,6 +56,7 @@ import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSourceFactory; import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.ProgressiveMediaSource; +import com.google.android.exoplayer2.source.hls.HlsMediaSource; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.ui.PlayerNotificationManager; import com.google.android.exoplayer2.upstream.DataSource; @@ -93,6 +94,8 @@ public class VideoPlayerService extends Service { private String currentStreamUrl; + private boolean currentStreamUrlIsHLS; + private PlayerNotificationManager playerNotificationManager; private IntentFilter becomeNoisyIntentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY); @@ -190,8 +193,9 @@ public class VideoPlayerService extends Service { currentVideo = video; } - public void setCurrentStreamUrl(String streamUrl) { + public void setCurrentStreamUrl(String streamUrl, boolean isHLS) { Log.v(TAG, "setCurrentStreamUrl..." + streamUrl); + currentStreamUrlIsHLS = isHLS; currentStreamUrl = streamUrl; } @@ -233,8 +237,14 @@ public class VideoPlayerService extends Service { DataSource.Factory dataSourceFactory = new OkHttpDataSourceFactory(okhttpClientBuilder.build(), Util.getUserAgent(getApplicationContext(), "PeerTube")); // Create a progressive media source pointing to a stream uri. - MediaSource mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory) - .createMediaSource(MediaItem.fromUri(Uri.parse(currentStreamUrl))); + MediaSource mediaSource; + if (currentStreamUrlIsHLS) { + mediaSource = new HlsMediaSource.Factory(dataSourceFactory) + .createMediaSource(MediaItem.fromUri(Uri.parse(currentStreamUrl))); + } else { + mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory) + .createMediaSource(MediaItem.fromUri(Uri.parse(currentStreamUrl))); + } // Set the media source to be played. player.setMediaSource(mediaSource);