Skip to content
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

currentMediaItem is empty when a CastPlayer is being used #25

Open
spun opened this issue Jan 8, 2022 · 13 comments
Open

currentMediaItem is empty when a CastPlayer is being used #25

spun opened this issue Jan 8, 2022 · 13 comments
Assignees
Labels

Comments

@spun
Copy link

spun commented Jan 8, 2022

I'm using a MediaLibraryService that switches the player from ExoPlayer to CastPlayer when a CastSession is available. When the ExoPlayer is active, calling getCurrentMediaItem() from a MediaController returns the correct MediaItem, but if the same call is done when the CastPlayer is the active one, I receive an empty MediaItem.

The method getWindow(...) inside CastTimeline is the one creating and using an empty MediaItem instead of the expected currentMediaItem.

@marcbaechinger
Copy link
Contributor

marcbaechinger commented Jan 10, 2022

This is a known issue which becomes more sever with AndroidX Media3 MediaSession. Implementing the Player interface properly is more important when we aim to be able to build a MediaSession with a MediaSession.Builder.

For right now I'm afraid I can't really give a better advice than that you need to maintain your own list of MediaItems that represents the playlist and from where you can lookup the media item by index with the given window index.

Somewhat aside: I think an approach to have a MediaSession with a Player that can change (local vs. remote playback with Cast) would be to have a custom Player implementation that delegates to an actual Player implementation and that allows to swap the actual implementation. It may for instance have a setPlayer(Player) method for swapping the current player with another. This way you can build a MediaSession with a MediaSession.Builder and delegate commands sent by a MediaController to either of the implementations under the hood. If you do that, that custom Player implementation would be the best place to maintain the list of MediaItems that is the playlist. It can also help you to work around #26 that is related to the CastPlayer not properly implementing the Player interface in the current state.

--
Some comments for context when someone is going to fix this (I stick with the bug label because I agree this is now a bug in context of AndroidX Media3):

There is an unwritten contract that a Player should provide an equal MediaItem with Player.getMediaItemAt(int index) that have been passed to the set/addMediaItem(MediaItem) methods. The same should be the case for Timeline.getWindow(...) as well.

CastPlayer currently fails to do that which we need to improve. At the very least CastPlayer should propagate the mediaId correctly which would be a first step although if we fix this that way we probably should comply with the whole MediaItem because the mediaId is not necessarily unique. There is a commit in the ExoPlayer repo for the dev2 branch which includes the mediaId when un/marshalling which is a first step towards this. CastTimeline is another step as mentioned above in the user's post.

@spun
Copy link
Author

spun commented Jan 10, 2022

Thank you very much @marcbaechinger for the idea of the custom Player. I was able to fix the empty MediaItem problem with a modified CastTimeline, CastTimelineTracker and a custom MediaItemConverter (like you already said, the default is a bit limited), but your idea is way better than calling MediaSession#setPlayer(...) each time the player changes. By the way, something like that inside the media3-cast would be awesome.

Thanks again!

@NielsMasdorp
Copy link

NielsMasdorp commented Feb 13, 2022

@spun Could you elaborate how you managed to solve the empty MediaItem? I am currently relying on onMediaItemTransition and onMediaMetadataChanged callbacks inside my MediaController and I need the mediaId and mediaMetadata in said callbacks. Obviously this doesn't work when I switch to the CastPlayer in the MediaSessionService.

@spun
Copy link
Author

spun commented Feb 14, 2022

Hi @NielsMasdorp. Unfortunately, I decided to wait until the issue is fixed. I'm currently running a modified version of the library with some changes made to CastTimeline (I could link you a Gist with the changes if you want) to fix the empty MediaItem problem until something is implemented in media3.

You could try the custom Player that switches between local and cast and also maintains the list of MediaItems. It's still a promising solution but, when I tried to do it, I was a bit overwhelmed with the RemoteMediaClient also being able to change the list of MediaItems and I don't have a working implementation yet.

If you can wait, when this issue is fixed, we should be able to at least retrieve the mediaId from the "empty" MediaItem. I think there was already a first step, but CastTimeline still returns the empty one, so we'll have to wait a bit longer.

@NielsMasdorp
Copy link

NielsMasdorp commented Feb 14, 2022

@spun Thanks for the update. In the meantime i've decided to tackle the problem as well. I am currently working on my own implementation of a custom Player which indeed switches from local to cast internally. It works, but it is tailored to my use case. I don't have a list of items but only playback a single MediaItem at a time, so the solution is just to keep a reference to the current MediaItem and that's that. But I am sure i've missed some edge cases. After that I will wait for updates in the media3 implementation (or at least a CastPlayer which implements the Player correctly).

rohitjoins pushed a commit that referenced this issue Jul 12, 2022
The media item needs to be assigned to `Window.mediaItem` in `CastTimeline.setWindow`. For this the `MediaItem` needs to be available in the timeline.

When a `MediaItem` is passed to the `set/addMediaItems` method, we can't yet know the Cast `MediaQueueItem.itemId` that is generated on the device and arrives with an async update of the `RemoteMediaClient` state. Hence in the `CastTimelineTracker`, we need to store the `MediaItem` by Casts's `MediaItem.contentId`. When we then receive the updated queue, we look the media item up by the content ID to augment the `ItemData` that is available in the `CastTimeline`.

Issue: #25
Issue: google/ExoPlayer#8212

#minor-release

PiperOrigin-RevId: 460325235
rohitjoins pushed a commit to google/ExoPlayer that referenced this issue Jul 12, 2022
The media item needs to be assigned to `Window.mediaItem` in `CastTimeline.setWindow`. For this the `MediaItem` needs to be available in the timeline.

When a `MediaItem` is passed to the `set/addMediaItems` method, we can't yet know the Cast `MediaQueueItem.itemId` that is generated on the device and arrives with an async update of the `RemoteMediaClient` state. Hence in the `CastTimelineTracker`, we need to store the `MediaItem` by Casts's `MediaItem.contentId`. When we then receive the updated queue, we look the media item up by the content ID to augment the `ItemData` that is available in the `CastTimeline`.

Issue: androidx/media#25
Issue: #8212

#minor-release

PiperOrigin-RevId: 460325235
rohitjoins pushed a commit that referenced this issue Jul 13, 2022
Issue: #25
PiperOrigin-RevId: 460476841
rohitjoins pushed a commit to google/ExoPlayer that referenced this issue Jul 13, 2022
@tonihei
Copy link
Collaborator

tonihei commented Jul 15, 2022

Fixed by the commits above.

@tonihei tonihei closed this as completed Jul 15, 2022
rohitjoins pushed a commit to google/ExoPlayer that referenced this issue Jul 15, 2022
The media item needs to be assigned to `Window.mediaItem` in `CastTimeline.setWindow`. For this the `MediaItem` needs to be available in the timeline.

When a `MediaItem` is passed to the `set/addMediaItems` method, we can't yet know the Cast `MediaQueueItem.itemId` that is generated on the device and arrives with an async update of the `RemoteMediaClient` state. Hence in the `CastTimelineTracker`, we need to store the `MediaItem` by Casts's `MediaItem.contentId`. When we then receive the updated queue, we look the media item up by the content ID to augment the `ItemData` that is available in the `CastTimeline`.

Issue: androidx/media#25
Issue: #8212

#minor-release

PiperOrigin-RevId: 460325235
(cherry picked from commit 02e1484)
rohitjoins pushed a commit to google/ExoPlayer that referenced this issue Jul 15, 2022
Issue: androidx/media#25
PiperOrigin-RevId: 460476841
(cherry picked from commit d6659e9)
rohitjoins pushed a commit that referenced this issue Jul 15, 2022
The media item needs to be assigned to `Window.mediaItem` in `CastTimeline.setWindow`. For this the `MediaItem` needs to be available in the timeline.

When a `MediaItem` is passed to the `set/addMediaItems` method, we can't yet know the Cast `MediaQueueItem.itemId` that is generated on the device and arrives with an async update of the `RemoteMediaClient` state. Hence in the `CastTimelineTracker`, we need to store the `MediaItem` by Casts's `MediaItem.contentId`. When we then receive the updated queue, we look the media item up by the content ID to augment the `ItemData` that is available in the `CastTimeline`.

Issue: #25
Issue: google/ExoPlayer#8212

#minor-release

PiperOrigin-RevId: 460325235
(cherry picked from commit 30fbc3a)
rohitjoins pushed a commit that referenced this issue Jul 15, 2022
Issue: #25
PiperOrigin-RevId: 460476841
(cherry picked from commit 6922bd5)
@Djsolutions1
Copy link

Nice work

@NielsMasdorp
Copy link

NielsMasdorp commented Mar 1, 2023

@marcbaechinger Sorry to open this again, but during active play of CastPlayer getCurrentMediaItem() returns the correct MediaItem however when listening to onCastSessionUnavailable() callback and subsequently checking getCurrentMediaItem() it is empty again. Is this intentional? Looking at the source it seems that the Timeline is cleared by the CastPlayer before the onCastSessionUnavailable() callback is invoked.

This means we still have to resort to locally caching the MediaItems, no? Or am I missing something?

@marcbaechinger
Copy link
Contributor

marcbaechinger commented Mar 1, 2023

When the remote media client is set to null the timeline is updated and set to an empty timeline. I agree that's surprising and unfortunate for a Player implementation, specifically that this happens before the callback is called.

we still have to resort to locally caching the MediaItem

I think this is a known behaviour though. CastPlayer is missing abilities to nicely connect/reconnect. To resume playback, the playlist media items need to be set again. Apps probably mostly maintain the playlist, position, window index and the like to be able to either setup a local ExoPlayer or CastPlayer like PlayerManager in the demo app. UAMP uses a ForwardingPlayer.

However, now that we support the media items in the timeline with CastPlayer, we could and probably should do better. Re-opened to track decoupling cast session and CastPlayer state and keeping timeline, currentMediaIndex and currentPosition when the cast session ends (probably similar to when ExoPlayer.stop() is called). Then we probably also need to think about the behaviour when the session/remote media client returns again.

I don't know when we come around to look into this I'm afraid .

@marcbaechinger marcbaechinger reopened this Mar 1, 2023
@NielsMasdorp
Copy link

Thank you, seems logical. I was just verifying to be sure I didn't miss anything. I'll keep an eye on improvements in the future.

@TomasValenta
Copy link

Thank you for this comment @marcbaechinger. I stumbled upon it accidentally, I wish I could have read it before e.g. in README, it would have saved me a lot of time and debugging.

@fdfmobileapps
Copy link

fdfmobileapps commented Nov 17, 2023

Hi @marcbaechinger

Just wondering if there were and updates on The empty media item issue?

It presents quite a significant problem, when resuming of a session is required. Caching a copy of the playlist, for the purpose of recovering from a Cast connection loss, if fairly ugly and highly prone to issues.

I am working around it by retaining a playlist of current items, which I reference rather than metadata on castplayer. I do this to avoid re-pushing a play queue if a resume is required. It's not ideal, due to potential to become out of sync, if queue items change for example. Have to be very careful to keep that list in sync on queue edits and so forth

I have noticed that the notification published by media 3 seems to have awareness of metadata post resume. I Was wondering how the notification is obtaining this information?

Cheers

@fdfmobileapps
Copy link

Hi @marcbaechinger

Was wondering if you had any updates or further advice on this issue, as yet?

Cheers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants