diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java index 7dd482b21..e0b1606dc 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -103,7 +103,7 @@ public class DownloadDialog extends DialogFragment @State AudioTracksWrapper wrappedAudioTracks; @State - int selectedAudioStreamIndex; + int selectedAudioTrackIndex; @State int selectedVideoIndex; // set in the constructor @State @@ -173,7 +173,7 @@ public class DownloadDialog extends DialogFragment final List> groupedAudioStreams = ListHelper.getGroupedAudioStreams(context, audioStreams); this.wrappedAudioTracks = new AudioTracksWrapper(groupedAudioStreams, context); - this.selectedAudioStreamIndex = + this.selectedAudioTrackIndex = ListHelper.getDefaultAudioTrackGroup(context, groupedAudioStreams); // TODO: Adapt this code when the downloader support other types of stream deliveries @@ -433,7 +433,7 @@ public class DownloadDialog extends DialogFragment } dialogBinding.audioTrackSpinner.setAdapter(audioTrackAdapter); - dialogBinding.audioTrackSpinner.setSelection(selectedAudioStreamIndex); + dialogBinding.audioTrackSpinner.setSelection(selectedAudioTrackIndex); } private void setupAudioSpinner() { @@ -619,8 +619,8 @@ public class DownloadDialog extends DialogFragment onItemSelectedSetFileName(); break; case R.id.audio_track_spinner: - final boolean trackChanged = selectedAudioStreamIndex != position; - selectedAudioStreamIndex = position; + final boolean trackChanged = selectedAudioTrackIndex != position; + selectedAudioTrackIndex = position; if (trackChanged) { updateSecondaryStreams(); fetchStreamsSize(); @@ -726,10 +726,10 @@ public class DownloadDialog extends DialogFragment } private StreamSizeWrapper getWrappedAudioStreams() { - if (selectedAudioStreamIndex < 0 || selectedAudioStreamIndex > wrappedAudioTracks.size()) { + if (selectedAudioTrackIndex < 0 || selectedAudioTrackIndex > wrappedAudioTracks.size()) { return StreamSizeWrapper.empty(); } - return wrappedAudioTracks.getTracksList().get(selectedAudioStreamIndex); + return wrappedAudioTracks.getTracksList().get(selectedAudioTrackIndex); } private int getSubtitleIndexBy(@NonNull final List streams) { diff --git a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java index bf0dc4a56..cd71c64e9 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java @@ -667,11 +667,8 @@ public final class PlayQueueActivity extends AppCompatActivity return; } - player.saveStreamProgressState(); final String newAudioTrack = availableStreams.get(itemId).getAudioTrackId(); - player.setRecovery(); player.setAudioTrack(newAudioTrack); - player.reloadPlayQueueManager(); }); } } diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index 144c754a9..89bdd9d69 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -180,13 +180,18 @@ public final class Player implements PlaybackListener, Listener { //////////////////////////////////////////////////////////////////////////*/ // play queue might be null e.g. while player is starting - @Nullable private PlayQueue playQueue; + @Nullable + private PlayQueue playQueue; - @Nullable private MediaSourceManager playQueueManager; + @Nullable + private MediaSourceManager playQueueManager; - @Nullable private PlayQueueItem currentItem; - @Nullable private MediaItemTag currentMetadata; - @Nullable private Bitmap currentThumbnail; + @Nullable + private PlayQueueItem currentItem; + @Nullable + private MediaItemTag currentMetadata; + @Nullable + private Bitmap currentThumbnail; /*////////////////////////////////////////////////////////////////////////// // Player @@ -195,12 +200,17 @@ public final class Player implements PlaybackListener, Listener { private ExoPlayer simpleExoPlayer; private AudioReactor audioReactor; - @NonNull private final DefaultTrackSelector trackSelector; - @NonNull private final LoadController loadController; - @NonNull private final DefaultRenderersFactory renderFactory; + @NonNull + private final DefaultTrackSelector trackSelector; + @NonNull + private final LoadController loadController; + @NonNull + private final DefaultRenderersFactory renderFactory; - @NonNull private final VideoPlaybackResolver videoResolver; - @NonNull private final AudioPlaybackResolver audioResolver; + @NonNull + private final VideoPlaybackResolver videoResolver; + @NonNull + private final AudioPlaybackResolver audioResolver; private final PlayerService service; //TODO try to remove and replace everything with context @@ -225,24 +235,32 @@ public final class Player implements PlaybackListener, Listener { private BroadcastReceiver broadcastReceiver; private IntentFilter intentFilter; - @Nullable private PlayerServiceEventListener fragmentListener = null; - @Nullable private PlayerEventListener activityListener = null; + @Nullable + private PlayerServiceEventListener fragmentListener = null; + @Nullable + private PlayerEventListener activityListener = null; - @NonNull private final SerialDisposable progressUpdateDisposable = new SerialDisposable(); - @NonNull private final CompositeDisposable databaseUpdateDisposable = new CompositeDisposable(); + @NonNull + private final SerialDisposable progressUpdateDisposable = new SerialDisposable(); + @NonNull + private final CompositeDisposable databaseUpdateDisposable = new CompositeDisposable(); // This is the only listener we need for thumbnail loading, since there is always at most only // one thumbnail being loaded at a time. This field is also here to maintain a strong reference, // which would otherwise be garbage collected since Picasso holds weak references to targets. - @NonNull private final Target currentThumbnailTarget; + @NonNull + private final Target currentThumbnailTarget; /*////////////////////////////////////////////////////////////////////////// // Utils //////////////////////////////////////////////////////////////////////////*/ - @NonNull private final Context context; - @NonNull private final SharedPreferences prefs; - @NonNull private final HistoryRecordManager recordManager; + @NonNull + private final Context context; + @NonNull + private final SharedPreferences prefs; + @NonNull + private final HistoryRecordManager recordManager; /*////////////////////////////////////////////////////////////////////////// @@ -334,7 +352,7 @@ public final class Player implements PlaybackListener, Listener { isAudioOnly = audioPlayerSelected(); if (intent.hasExtra(PLAYBACK_QUALITY)) { - setPlaybackQuality(intent.getStringExtra(PLAYBACK_QUALITY)); + videoResolver.setPlaybackQuality(intent.getStringExtra(PLAYBACK_QUALITY)); } // Resolve enqueue intents @@ -342,7 +360,7 @@ public final class Player implements PlaybackListener, Listener { playQueue.append(newQueue.getStreams()); return; - // Resolve enqueue next intents + // Resolve enqueue next intents } else if (intent.getBooleanExtra(ENQUEUE_NEXT, false) && playQueue != null) { final int currentIndex = playQueue.getIndex(); playQueue.append(newQueue.getStreams()); @@ -914,7 +932,7 @@ public final class Player implements PlaybackListener, Listener { private Disposable getProgressUpdateDisposable() { return Observable.interval(PROGRESS_LOOP_INTERVAL_MILLIS, MILLISECONDS, - AndroidSchedulers.mainThread()) + AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(ignored -> triggerProgressUpdate(), error -> Log.e(TAG, "Progress update failure: ", error)); @@ -923,7 +941,6 @@ public final class Player implements PlaybackListener, Listener { //endregion - /*////////////////////////////////////////////////////////////////////////// // Playback states //////////////////////////////////////////////////////////////////////////*/ @@ -1247,7 +1264,7 @@ public final class Player implements PlaybackListener, Listener { .flatMap(MediaItemTag::getMaybeStreamInfo).orElse(null); final MediaItemTag.AudioTrack previousAudioTrack = Optional.ofNullable(currentMetadata) - .flatMap(MediaItemTag::getMaybeAudioTrack).orElse(null); + .flatMap(MediaItemTag::getMaybeAudioTrack).orElse(null); currentMetadata = tag; if (!currentMetadata.getErrors().isEmpty()) { @@ -1270,9 +1287,9 @@ public final class Player implements PlaybackListener, Listener { updateMetadataWith(info); } else if (previousAudioTrack == null || tag.getMaybeAudioTrack() - .map(t -> t.getSelectedAudioStreamIndex() - != previousAudioTrack.getSelectedAudioStreamIndex()) - .orElse(false)) { + .map(t -> t.getSelectedAudioStreamIndex() + != previousAudioTrack.getSelectedAudioStreamIndex()) + .orElse(false)) { notifyAudioTrackUpdateToListeners(); } }); @@ -1361,6 +1378,7 @@ public final class Player implements PlaybackListener, Listener { // Errors //////////////////////////////////////////////////////////////////////////*/ //region Errors + /** * Process exceptions produced by {@link com.google.android.exoplayer2.ExoPlayer ExoPlayer}. *

There are multiple types of errors:

@@ -1387,8 +1405,9 @@ public final class Player implements PlaybackListener, Listener { * For any error above that is not explicitly catchable, the player will * create a notification so users are aware. * + * * @see com.google.android.exoplayer2.Player.Listener#onPlayerError(PlaybackException) - * */ + */ // Any error code not explicitly covered here are either unrelated to NewPipe use case // (e.g. DRM) or not recoverable (e.g. Decoder error). In both cases, the player should // shutdown. @@ -2141,7 +2160,7 @@ public final class Player implements PlaybackListener, Listener { // because the stream source will be probably the same as the current played if (sourceType == SourceType.VIDEO_WITH_SEPARATED_AUDIO || (sourceType == SourceType.VIDEO_WITH_AUDIO_OR_AUDIO_ONLY - && isNullOrEmpty(streamInfo.getAudioStreams()))) { + && isNullOrEmpty(streamInfo.getAudioStreams()))) { // It's not needed to reload the play queue manager only if the content's stream type // is a video stream, a live stream or an ended live stream return !StreamTypeUtil.isVideo(streamType); @@ -2203,12 +2222,18 @@ public final class Player implements PlaybackListener, Listener { } public void setPlaybackQuality(@Nullable final String quality) { + saveStreamProgressState(); + setRecovery(); videoResolver.setPlaybackQuality(quality); + reloadPlayQueueManager(); } public void setAudioTrack(@Nullable final String audioTrackId) { + saveStreamProgressState(); + setRecovery(); videoResolver.setAudioTrack(audioTrackId); audioResolver.setAudioTrack(audioTrackId); + reloadPlayQueueManager(); } @@ -2286,7 +2311,7 @@ public final class Player implements PlaybackListener, Listener { /** * Get the video renderer index of the current playing stream. - * + *

* This method returns the video renderer index of the current * {@link MappingTrackSelector.MappedTrackInfo} or {@link #RENDERER_UNAVAILABLE} if the current * {@link MappingTrackSelector.MappedTrackInfo} is null or if there is no video renderer index. 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 e94295724..063a2a297 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 @@ -38,6 +38,13 @@ public class AudioPlaybackResolver implements PlaybackResolver { this.dataSource = dataSource; } + /** + * Get a media source providing 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 + */ @Override @Nullable public MediaSource resolve(@NonNull final StreamInfo info) { diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java index 8aff0af87..2638ff041 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java +++ b/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java @@ -110,7 +110,8 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa protected PlayerBinding binding; private final Handler controlsVisibilityHandler = new Handler(Looper.getMainLooper()); - @Nullable private SurfaceHolderCallback surfaceHolderCallback; + @Nullable + private SurfaceHolderCallback surfaceHolderCallback; boolean surfaceIsSetup = false; @@ -533,6 +534,7 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa /** * Sets the current duration into the corresponding elements. + * * @param currentProgress the current progress, in milliseconds */ private void updatePlayBackElementsCurrentDuration(final int currentProgress) { @@ -545,6 +547,7 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa /** * Sets the video duration time into all control components (e.g. seekbar). + * * @param duration the video duration, in milliseconds */ private void setVideoDurationToControls(final int duration) { @@ -1261,11 +1264,8 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa return; } - player.saveStreamProgressState(); final String newResolution = availableStreams.get(menuItemIndex).getResolution(); - player.setRecovery(); player.setPlaybackQuality(newResolution); - player.reloadPlayQueueManager(); binding.qualityTextView.setText(menuItem.getTitle()); } @@ -1285,11 +1285,8 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa return; } - player.saveStreamProgressState(); final String newAudioTrack = availableStreams.get(menuItemIndex).getAudioTrackId(); - player.setRecovery(); player.setAudioTrack(newAudioTrack); - player.reloadPlayQueueManager(); binding.audioTrackTextView.setText(menuItem.getTitle()); } diff --git a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java index f8a800b0e..b036d55c5 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java @@ -715,9 +715,9 @@ public final class ListHelper { /** * Get a {@link Comparator} to compare {@link AudioStream}s by their tracks. * - *

In this order:

+ *

Tracks will be compared this order:

*
    - *
  1. If {@code preferOriginalAudio}: is original audio
  2. + *
  3. If {@code preferOriginalAudio}: use original audio
  4. *
  5. Language matches {@code preferredLanguage}
  6. *
  7. * Track type ranks highest in this order: @@ -752,9 +752,9 @@ public final class ListHelper { /** * Get a {@link Comparator} to compare {@link AudioStream}s by their tracks. * - *

    In this order:

    + *

    Tracks will be compared this order:

    *
      - *
    1. If {@code preferOriginalAudio}: is original audio
    2. + *
    3. If {@code preferOriginalAudio}: use original audio
    4. *
    5. Language matches {@code preferredLanguage}
    6. *
    7. * Track type ranks highest in this order: diff --git a/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java b/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java index 4bcdfd02a..2eb63ff41 100644 --- a/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java @@ -224,6 +224,8 @@ public class StreamItemAdapter extends BaseA public static class StreamSizeWrapper implements Serializable { private static final StreamSizeWrapper EMPTY = new StreamSizeWrapper<>(Collections.emptyList(), null); + private static final int SIZE_UNSET = -2; + private final List streamsList; private final long[] streamSizes; private final String unknownSize; @@ -251,7 +253,7 @@ public class StreamItemAdapter extends BaseA final Callable fetchAndSet = () -> { boolean hasChanged = false; for (final X stream : streamsWrapper.getStreamsList()) { - if (streamsWrapper.getSizeInBytes(stream) > -2) { + if (streamsWrapper.getSizeInBytes(stream) > SIZE_UNSET) { continue; } @@ -270,7 +272,7 @@ public class StreamItemAdapter extends BaseA } public void resetSizes() { - Arrays.fill(streamSizes, -2); + Arrays.fill(streamSizes, SIZE_UNSET); } public static StreamSizeWrapper empty() {