diff --git a/app/build.gradle b/app/build.gradle index e6a78a093..94bec47fe 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -105,7 +105,7 @@ ext { androidxRoomVersion = '2.3.0' icepickVersion = '3.2.0' - exoPlayerVersion = '2.12.3' + exoPlayerVersion = '2.14.2' googleAutoServiceVersion = '1.0' groupieVersion = '2.10.0' markwonVersion = '4.6.2' 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 5435b9f81..d2e89baa0 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -1,12 +1,13 @@ package org.schabi.newpipe.player; -import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_AD_INSERTION; +import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_AUTO_TRANSITION; import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_INTERNAL; -import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_PERIOD_TRANSITION; +import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_REMOVE; import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK; import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT; +import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SKIP; import static com.google.android.exoplayer2.Player.DiscontinuityReason; -import static com.google.android.exoplayer2.Player.EventListener; +import static com.google.android.exoplayer2.Player.Listener; import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL; import static com.google.android.exoplayer2.Player.REPEAT_MODE_OFF; import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE; @@ -116,6 +117,7 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.DefaultRenderersFactory; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.PlaybackParameters; +import com.google.android.exoplayer2.Player.PositionInfo; import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; @@ -123,13 +125,14 @@ import com.google.android.exoplayer2.source.BehindLiveWindowException; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; -import com.google.android.exoplayer2.text.CaptionStyleCompat; +import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; +import com.google.android.exoplayer2.ui.CaptionStyleCompat; import com.google.android.exoplayer2.ui.SubtitleView; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.util.Util; -import com.google.android.exoplayer2.video.VideoListener; +import com.google.android.exoplayer2.video.VideoSize; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.squareup.picasso.Picasso; import com.squareup.picasso.Target; @@ -197,9 +200,8 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.disposables.SerialDisposable; public final class Player implements - EventListener, PlaybackListener, - VideoListener, + Listener, SeekBar.OnSeekBarChangeListener, View.OnClickListener, PopupMenu.OnMenuItemClickListener, @@ -501,10 +503,6 @@ public final class Player implements // Setup video view setupVideoSurface(); - simpleExoPlayer.addVideoListener(this); - - // Setup subtitle view - simpleExoPlayer.addTextOutput(binding.subtitleView); // enable media tunneling if (DEBUG && PreferenceManager.getDefaultSharedPreferences(context) @@ -513,7 +511,7 @@ public final class Player implements + "media tunneling disabled in debug preferences"); } else if (DeviceUtils.shouldSupportMediaTunneling()) { trackSelector.setParameters(trackSelector.buildUponParameters() - .setTunnelingAudioSessionId(C.generateAudioSessionIdV21(context))); + .setTunnelingEnabled(true)); } else if (DEBUG) { Log.d(TAG, "[" + Util.DEVICE_DEBUG_INFO + "] does not support media tunneling"); } @@ -809,7 +807,6 @@ public final class Player implements if (!exoPlayerIsNull()) { simpleExoPlayer.removeListener(this); - simpleExoPlayer.removeVideoListener(this); simpleExoPlayer.stop(); simpleExoPlayer.release(); } @@ -898,7 +895,7 @@ public final class Player implements public void smoothStopPlayer() { // Pausing would make transition from one stream to a new stream not smooth, so only stop - simpleExoPlayer.stop(false); + simpleExoPlayer.stop(); } //endregion @@ -2437,7 +2434,9 @@ public final class Player implements } @Override - public void onPositionDiscontinuity(@DiscontinuityReason final int discontinuityReason) { + public void onPositionDiscontinuity( + final PositionInfo oldPosition, final PositionInfo newPosition, + @DiscontinuityReason final int discontinuityReason) { if (DEBUG) { Log.d(TAG, "ExoPlayer - onPositionDiscontinuity() called with " + "discontinuityReason = [" + discontinuityReason + "]"); @@ -2449,7 +2448,7 @@ public final class Player implements // Refresh the playback if there is a transition to the next video final int newWindowIndex = simpleExoPlayer.getCurrentWindowIndex(); switch (discontinuityReason) { - case DISCONTINUITY_REASON_PERIOD_TRANSITION: + case DISCONTINUITY_REASON_REMOVE: // When player is in single repeat mode and a period transition occurs, // we need to register a view count here since no metadata has changed if (getRepeatMode() == REPEAT_MODE_ONE && newWindowIndex == playQueue.getIndex()) { @@ -2470,7 +2469,8 @@ public final class Player implements playQueue.setIndex(newWindowIndex); } break; - case DISCONTINUITY_REASON_AD_INSERTION: + case DISCONTINUITY_REASON_SKIP: + case DISCONTINUITY_REASON_AUTO_TRANSITION: break; // only makes Android Studio linter happy, as there are no ads } @@ -2482,6 +2482,11 @@ public final class Player implements //TODO check if this causes black screen when switching to fullscreen animate(binding.surfaceForeground, false, DEFAULT_CONTROLS_DURATION); } + + @Override + public void onCues(final List cues) { + binding.subtitleView.onCues(cues); + } //endregion @@ -2503,7 +2508,7 @@ public final class Player implements * * * @see #processSourceError(IOException) - * @see com.google.android.exoplayer2.Player.EventListener#onPlayerError(ExoPlaybackException) + * @see com.google.android.exoplayer2.Player.Listener#onPlayerError(ExoPlaybackException) */ @Override public void onPlayerError(@NonNull final ExoPlaybackException error) { @@ -3867,19 +3872,17 @@ public final class Player implements } @Override // exoplayer listener - public void onVideoSizeChanged(final int width, final int height, - final int unappliedRotationDegrees, - final float pixelWidthHeightRatio) { + public void onVideoSizeChanged(final VideoSize videoSize) { if (DEBUG) { Log.d(TAG, "onVideoSizeChanged() called with: " - + "width / height = [" + width + " / " + height - + " = " + (((float) width) / height) + "], " - + "unappliedRotationDegrees = [" + unappliedRotationDegrees + "], " - + "pixelWidthHeightRatio = [" + pixelWidthHeightRatio + "]"); + + "width / height = [" + videoSize.width + " / " + videoSize.height + + " = " + (((float) videoSize.width) / videoSize.height) + "], " + + "unappliedRotationDegrees = [" + videoSize.unappliedRotationDegrees + "], " + + "pixelWidthHeightRatio = [" + videoSize.pixelWidthHeightRatio + "]"); } - binding.surfaceView.setAspectRatio(((float) width) / height); - isVerticalVideo = width < height; + binding.surfaceView.setAspectRatio(((float) videoSize.width) / videoSize.height); + isVerticalVideo = videoSize.width < videoSize.height; if (globalScreenOrientationLocked(context) && isFullscreen diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java b/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java index 2e2fda86c..b36f9f234 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java @@ -16,7 +16,6 @@ import androidx.media.AudioManagerCompat; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.analytics.AnalyticsListener; -import com.google.android.exoplayer2.decoder.DecoderCounters; public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, AnalyticsListener { @@ -150,15 +149,9 @@ public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, An //////////////////////////////////////////////////////////////////////////*/ @Override - public void onAudioSessionId(final EventTime eventTime, final int audioSessionId) { + public void onAudioSessionIdChanged(final EventTime eventTime, final int audioSessionId) { notifyAudioSessionUpdate(true, audioSessionId); } - - @Override - public void onAudioDisabled(final EventTime eventTime, final DecoderCounters counters) { - notifyAudioSessionUpdate(false, player.getAudioSessionId()); - } - private void notifyAudioSessionUpdate(final boolean active, final int audioSessionId) { if (!PlayerHelper.isUsingDSP()) { return; diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/LoadController.java b/app/src/main/java/org/schabi/newpipe/player/helper/LoadController.java index 71cfcc818..ca3b1a3c1 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/LoadController.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/LoadController.java @@ -4,7 +4,7 @@ import com.google.android.exoplayer2.DefaultLoadControl; import com.google.android.exoplayer2.LoadControl; import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.source.TrackGroupArray; -import com.google.android.exoplayer2.trackselection.TrackSelectionArray; +import com.google.android.exoplayer2.trackselection.ExoTrackSelection; import com.google.android.exoplayer2.upstream.Allocator; public class LoadController implements LoadControl { @@ -47,7 +47,7 @@ public class LoadController implements LoadControl { @Override public void onTracksSelected(final Renderer[] renderers, final TrackGroupArray trackGroups, - final TrackSelectionArray trackSelections) { + final ExoTrackSelection[] trackSelections) { internalLoadControl.onTracksSelected(renderers, trackGroups, trackSelections); } @@ -91,11 +91,12 @@ public class LoadController implements LoadControl { @Override public boolean shouldStartPlayback(final long bufferedDurationUs, final float playbackSpeed, - final boolean rebuffering) { + final boolean rebuffering, final long targetLiveOffsetUs) { final boolean isInitialPlaybackBufferFilled = bufferedDurationUs >= this.initialPlaybackBufferUs * playbackSpeed; final boolean isInternalStartingPlayback = internalLoadControl - .shouldStartPlayback(bufferedDurationUs, playbackSpeed, rebuffering); + .shouldStartPlayback(bufferedDurationUs, playbackSpeed, rebuffering, + targetLiveOffsetUs); return isInitialPlaybackBufferFilled || isInternalStartingPlayback; } diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java index 5fea4761b..42a7838c3 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java @@ -19,7 +19,7 @@ import com.google.android.exoplayer2.upstream.TransferListener; public class PlayerDataSource { private static final int MANIFEST_MINIMUM_RETRY = 5; private static final int EXTRACTOR_MINIMUM_RETRY = Integer.MAX_VALUE; - private static final int LIVE_STREAM_EDGE_GAP_MILLIS = 10000; + public static final int LIVE_STREAM_EDGE_GAP_MILLIS = 10000; private final DataSource.Factory cacheDataSourceFactory; private final DataSource.Factory cachelessDataSourceFactory; @@ -50,8 +50,7 @@ public class PlayerDataSource { return new DashMediaSource.Factory(new DefaultDashChunkSource.Factory( cachelessDataSourceFactory), cachelessDataSourceFactory) .setLoadErrorHandlingPolicy( - new DefaultLoadErrorHandlingPolicy(MANIFEST_MINIMUM_RETRY)) - .setLivePresentationDelayMs(LIVE_STREAM_EDGE_GAP_MILLIS, true); + new DefaultLoadErrorHandlingPolicy(MANIFEST_MINIMUM_RETRY)); } public SsMediaSource.Factory getSsMediaSourceFactory() { @@ -74,11 +73,6 @@ public class PlayerDataSource { new DefaultLoadErrorHandlingPolicy(EXTRACTOR_MINIMUM_RETRY)); } - public ProgressiveMediaSource.Factory getExtractorMediaSourceFactory( - @NonNull final String key) { - return getExtractorMediaSourceFactory().setCustomCacheKey(key); - } - public SingleSampleMediaSource.Factory getSampleMediaSourceFactory() { return new SingleSampleMediaSource.Factory(cacheDataSourceFactory); } diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java index 828833a8d..80dacc801 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java @@ -21,11 +21,11 @@ import androidx.preference.PreferenceManager; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player.RepeatMode; import com.google.android.exoplayer2.SeekParameters; -import com.google.android.exoplayer2.text.CaptionStyleCompat; import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection; -import com.google.android.exoplayer2.trackselection.TrackSelection; +import com.google.android.exoplayer2.trackselection.ExoTrackSelection; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout.ResizeMode; +import com.google.android.exoplayer2.ui.CaptionStyleCompat; import com.google.android.exoplayer2.util.MimeTypes; import org.schabi.newpipe.R; diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/CustomTrackSelector.java b/app/src/main/java/org/schabi/newpipe/player/playback/CustomTrackSelector.java index d70707fdb..389be7062 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playback/CustomTrackSelector.java +++ b/app/src/main/java/org/schabi/newpipe/player/playback/CustomTrackSelector.java @@ -13,7 +13,7 @@ import com.google.android.exoplayer2.RendererCapabilities.Capabilities; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; -import com.google.android.exoplayer2.trackselection.TrackSelection; +import com.google.android.exoplayer2.trackselection.ExoTrackSelection; import com.google.android.exoplayer2.util.Assertions; /** @@ -28,7 +28,7 @@ public class CustomTrackSelector extends DefaultTrackSelector { private String preferredTextLanguage; public CustomTrackSelector(final Context context, - final TrackSelection.Factory adaptiveTrackSelectionFactory) { + final ExoTrackSelection.Factory adaptiveTrackSelectionFactory) { super(context, adaptiveTrackSelectionFactory); } @@ -50,7 +50,7 @@ public class CustomTrackSelector extends DefaultTrackSelector { @Override @Nullable - protected Pair selectTextTrack( + protected Pair selectTextTrack( final TrackGroupArray groups, @NonNull final int[][] formatSupport, @NonNull final Parameters params, @@ -86,7 +86,7 @@ public class CustomTrackSelector extends DefaultTrackSelector { } } return selectedGroup == null ? null - : Pair.create(new TrackSelection.Definition(selectedGroup, selectedTrackIndex), + : Pair.create(new ExoTrackSelection.Definition(selectedGroup, selectedTrackIndex), Assertions.checkNotNull(selectedTrackScore)); } } 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 81e629c2f..d0c2009a1 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 @@ -35,7 +35,7 @@ public interface PlaybackResolver extends Resolver { return null; } - +0 @NonNull default MediaSource buildLiveMediaSource(@NonNull final PlayerDataSource dataSource, @NonNull final String sourceUrl, @@ -48,7 +48,12 @@ public interface PlaybackResolver extends Resolver { .createMediaSource(MediaItem.fromUri(uri)); case C.TYPE_DASH: return dataSource.getLiveDashMediaSourceFactory().setTag(metadata) - .createMediaSource(MediaItem.fromUri(uri)); + .createMediaSource( + new MediaItem.Builder() + .setUri(uri) + .setLiveTargetOffsetMs( + PlayerDataSource.LIVE_STREAM_EDGE_GAP_MILLIS) + .build()); case C.TYPE_HLS: return dataSource.getLiveHlsMediaSourceFactory().setTag(metadata) .createMediaSource(MediaItem.fromUri(uri)); @@ -78,8 +83,12 @@ public interface PlaybackResolver extends Resolver { return dataSource.getHlsMediaSourceFactory().setTag(metadata) .createMediaSource(MediaItem.fromUri(uri)); case C.TYPE_OTHER: - return dataSource.getExtractorMediaSourceFactory(cacheKey).setTag(metadata) - .createMediaSource(MediaItem.fromUri(uri)); + return dataSource.getExtractorMediaSourceFactory().setTag(metadata) + .createMediaSource( + new MediaItem.Builder() + .setUri(uri) + .setCustomCacheKey(cacheKey) + .build()); default: throw new IllegalStateException("Unsupported type: " + type); }