-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ExoPlayer 2.17.1 update and MediaSource management rework #8020
Conversation
app/src/main/java/org/schabi/newpipe/player/helper/MediaSessionManager.java
Show resolved
Hide resolved
app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java
Outdated
Show resolved
Hide resolved
app/src/main/java/org/schabi/newpipe/player/mediasession/PlayQueueNavigator.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the PR!
A ExoPlayer update was long overdue in my opinion, good that you picked it up.
Very good PR in my opinion, I like the coding style and the usage of new Java 8 features such as the Streams API 👍
Didn't test it yet but I found some things in the review that should be processed:
Update: Also please have a look at the Sonarcloud warnings
@@ -798,7 +816,6 @@ public void handleIntent(@NonNull final Intent intent) { | |||
if (oldPlayerType != playerType && playQueue != null) { | |||
// If playerType changes from one to another we should reload the player | |||
// (to disable/enable video stream or to set quality) | |||
setRecovery(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was this removed?
app/src/main/java/org/schabi/newpipe/player/resolver/ExtractorDataSourceResolver.java
Outdated
Show resolved
Hide resolved
app/src/main/java/org/schabi/newpipe/player/resolver/ExtractorDataSourceResolver.java
Outdated
Show resolved
Hide resolved
app/src/main/java/org/schabi/newpipe/player/resolver/ExtractorDataSourceResolver.java
Outdated
Show resolved
Hide resolved
app/src/main/java/org/schabi/newpipe/player/resolver/ExtractorDataSourceResolver.java
Outdated
Show resolved
Hide resolved
app/src/main/java/org/schabi/newpipe/player/resolver/ExtractorDataSourceResolver.java
Outdated
Show resolved
Hide resolved
Many thanks for the thorough review @litetex. Given the serialization usage coincides with the Eventually when we decide to move to the ExoPlayer model, we will have the same issue using |
05dad02
to
9fb95e3
Compare
ed0e8bb
to
6554599
Compare
added: MediaItemTag for ManagedMediaSources. added: silent track for FailedMediaSource. added: keyframe fast forward at initial playback buffer. added: error notification on silently skipped streams.
fixed: checkStyle failures.
6554599
to
fd6cb44
Compare
fd6cb44
to
77ed300
Compare
…ediaSource. fixed: onPlaybackSynchronize to rewind when not playing, which was incorrectly removed in previous commit. fixed: sonar and checkstyle issues.
77ed300
to
b81eb35
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some minor things I noticed:
app/src/main/java/org/schabi/newpipe/player/mediaitem/MediaItemTag.java
Outdated
Show resolved
Hide resolved
app/src/main/java/org/schabi/newpipe/player/mediasource/FailedMediaSource.java
Outdated
Show resolved
Hide resolved
…fications are created. updated: Throwable usage to Exceptions. updated: minor styles and documentations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nearly perfect, only minor things:
app/src/main/java/org/schabi/newpipe/player/mediasource/FailedMediaSource.java
Outdated
Show resolved
Hide resolved
app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java
Outdated
Show resolved
Hide resolved
…onds before allowing retry. updated: minor style fixes.
Kudos, SonarCloud Quality Gate passed! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Found one problem while testing (however this is likely unrelated): When enqueuing a video it gets enqueued 2x.
Otherwise it looks like it' working fine 👍
Other issues that should be fixed in tandem:
|
It seems I found a regression with this PR: use the Play all button on the Creative Commons channel, open the video with Stacy Allison-Cassin, enable autogenerated subtitles, then switch to the next video. Wait for lyrics and you will notice that English subtitles are enabled, even if subtitles are marked as disabled by the player. You need to switch to another subtitle language and then go back to the English one to make the player show that English subtitles are enabled. |
Confirmed - can reproduce by queuing any 2 videos, first with a manual subtitle (e.g. english) and the second with only auto-generated subtitle of the previous language (e.g. english auto-generated) and the rest is the same as @TiA4f8R describes. I suspect this is because of how ExoPlayer compare language names, we can probably fix this by updating the selected ExoPlayer subtitle track according to the selected caption dropdown on metadata update. Will take a look when next available. |
if (surface.isValid()) { | ||
// initially set the surface manually otherwise | ||
// onRenderedFirstFrame() will not be called | ||
simpleExoPlayer.setVideoSurface(surface); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be replaced by simpleExoPlayer.setVideoSurfaceHolder(binding.surfaceView.getHolder());
, or would there be issues? After some testing in #8170 I couldn't notice any difference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely, that is the better solution. setVideoSurfaceHolder
is doing the same null checks here: https://github.com/google/ExoPlayer/blob/release-v2/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java#L1262
onMetadataChanged(metadata); | ||
maybeAutoQueueNextStream(streamInfo); | ||
onMetadataChanged(streamInfo); | ||
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, why does this now have forceRecreate = true
? Also, it seems like you are calling createNotificationIfNeededAndUpdate
twice when updateMetadataWith
is called, since there is a createNotificationIfNeededAndUpdate(this, false);
also inside onMetadataChanged
. And as you can see that one has forceRecreate = false
.
Note for myself: check NotificationPlayerUi.onMetadataChanged after getting a response here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oversight on my part. createNotificationIfNeededAndUpdate
in onMetadataChanged
should be removed.
app/src/main/java/org/schabi/newpipe/player/resolver/ExtractorDataSourceResolver.java
Outdated
Show resolved
Hide resolved
app/src/main/java/org/schabi/newpipe/player/mediasource/PlaybackDispatch.java
Outdated
Show resolved
Hide resolved
onMetadataChanged(metadata); | ||
maybeAutoQueueNextStream(streamInfo); | ||
onMetadataChanged(streamInfo); | ||
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oversight on my part. createNotificationIfNeededAndUpdate
in onMetadataChanged
should be removed.
if (surface.isValid()) { | ||
// initially set the surface manually otherwise | ||
// onRenderedFirstFrame() will not be called | ||
simpleExoPlayer.setVideoSurface(surface); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely, that is the better solution. setVideoSurfaceHolder
is doing the same null checks here: https://github.com/google/ExoPlayer/blob/release-v2/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java#L1262
Sorry about that folks, got too used to Stash PR workflow and didn't realize on Github all my review replies stayed in |
DefaultControlDispatcher was removed in ExoPlayer 2.16.0, so the class extending it that handled play/pause was removed in TeamNewPipe#8020. The new solution is to use an instance of ForwardingPlayer. Call sessionConnector.setPlayer with an instance of ForwardingPlayer that overrides play() and pause() and calls the callback methods.
What is it?
Description of the changes in your PR
Hi team,
I took a shot at upgrading ExoPlayer to 2.17.0 and realized that quite a few API has been made obsolete. For example,
getTag
has been dropped and will need even more hacks to get it to work. Since a rework is needed anyways, I took the liberty to toss out the old custom MediaSource management in favour of a more vanilla ExoPlayer model, following this comment. This is an attempt to help with refactoring the player so we can shift to expose ExoPlayer interface for player management instead of managing our ownPlayQueue
for playlist modifications.This means ExoPlayer will now be responsible for managing the internal playlist, and
PlayQueue
will no longer play a central role to playback management. It will become a player proxy for user issued commands, and acts as a view for the player playlist, since ExoPlayer doesn't provide an API for the entire playlist. As an added bonus, shuffle now works properly. You may also notice buffer will start immediately when seeking to an unprepared stream, as opposed to the old model, where playback resumes untilStreamInfo
is fetched before buffering starts. This is becauseStreamInfo
are now loaded on-demand inside theMediaSource
in a customCompositeMediaSource
that dispatches a call to resolve/build the actualMediaSource
using thePlaybackResolvers
.Also, each
MediaSource
is now associated with aMediaItem
that holds metadata.Tag
objects are now contained inside thisMediaItem
instead. As a result, the oldMediaSourceTag
are now revamped into a generic interface that bridges betweenPlayQueueItem
andStreamInfo
since they both hold similar metadata. This can be expanded later so this interface object is exposed to external adapters and properly display if a stream is properly loaded or has errors (e.g. content error).Going back to
MediaSource
management, a challenge arising from the new model is its lifecycle, whereMediaSource
in ExoPlayer aren't really allowed to self update. This means when a stream expires, we cannot update thePlaylist MediaSource
with a new one, nor should we since ExoPlayer should take care of it instead. This brings inDataSpec
resolution, which is an interface ExoPlayer exposes that lets us examine an URI and replace it with another. So the solution here is to update ourPlaybackResolver
to encapsulate the Extractor Stream metadata (e.g.AudioStream
,VideoStream
,SubtitleStream
, etc.) in an URI, and deliver that to the newDataSpecResolver
to convert our custom URI to a real stream URI. This means we can rely on our own Extractor cache to handle expiration and resolve stream metadata into the real URL and ExoPlayer will handle the rest.Concerns:
Because ExoPlayer now takes care of managing its own playlist, the
MediaSourceManager
(nowPlayQueueReactor
) no longer correct desynchronizations between thePlayQueue
and ExoPlayer's own playlist. SincePlayQueue
andPlayQueueReactor
communicate with a message bus, non-atomic calls toPlayQueue
can cause desyncs. For example,Enqueue next
was broken because it works by appending to the queue and moving the last item to the current playing. When the append event message gets to the reactor, the item has already moved on the play queue. This is temporarily fixed by introducing a new eventInsert
to avoid mixing logic with auto-queuing onAppend
events. But a proper solution would be to refactorPlayQueue
into an interface and create a new wrapperActivePlayQueue
on the basePlayQueue
to handle all the player controlled play queue logic. The ultimate end-state forPlayQueue
in this new model is to mainly act as a quick-access view for the adapters, and all actions to the queue are exposed via ExoPlayer interface instead.Since ExoPlayer controls the shuffling now, we also lose some control (for the time being) in shuffled mode. For example, currently
PlayQueue
shuffle reorders the queue and is presented to the user (I actually like this better) instead of ExoPlayer's model of jumping to a random index. But this now also breaks theEnqueue next
feature when used in shuffle mode. We can potentially create our own shuffle order as an individual component, though it means more code and kind of defeats the purpose of lifting the feature to ExoPlayer.As mentioned before,
StreamInfo
preparation will now also be part of the buffering. This means, when selecting a stream that is not yet loadedm at all, buffering will feel a bit slower than before. Whereas before, theStreamInfo
would load in the background while the stream continues to play before the buffering starts. I tried to mitigate this by pre-emptively loading theStreamInfo
of the nextMediaItem
, but this issue will never fully go away since we can't load every item on the queue.As you can see, there are quite a few more things that can be worked on to get this to a perfect state. But since the PR size is getting larger, I wanted to get this PR out now to get some opinion on if we want to make this shift in player modelling as part of the greater refactor work (or if this path isn't really worth proceeding). And if we want to make the shift, have the changes tested by more people and the code reviewed since it might break existing features.
APK testing
The APK can be found by going to the "Checks" tab below the title. On the left pane, click on "CI", scroll down to "artifacts" and click "app" to download the zip file which contains the debug APK of this PR.
Fixes the following issue(s)
Due diligence