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

How to update current media metadata (user rating) without interrupting the playback? #33

Closed
yareg-com opened this issue Jan 27, 2022 · 9 comments
Assignees

Comments

@yareg-com
Copy link

I am using androidx.media3 to develop an audio player app which provides users with an option to rate currently playng media. So, inside my MediaSessionCallback I do this:

override fun onSetRating(
    session: MediaSession,
    controller: ControllerInfo,
    rating: Rating
): ListenableFuture<SessionResult> {

    val item = session.player.currentMediaItem

    item?.let {
        val metadata  = it.mediaMetadata.buildUpon().setUserRating(rating).build()
        val mediaItem = it.buildUpon().setMediaMetadata(metadata).build()
        session.player.setMediaItem(mediaItem, false)
    }
    
    return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
}

This works, but with the only caveat - it interrupts the audio for the moment. Please, help me to understand how user rating should work.

@marcbaechinger
Copy link
Contributor

marcbaechinger commented Jan 30, 2022

It's currently not possible to update the media item I'm afraid. I think you need to maintain these ratings in your app instead like in a map for each mediaId as a key or similar.

You need to do this anyway I think as you loose the updated media items once they are removed from the player. So at some point the rating needs to be stored. It's probably best to provide the rating in the viewModel of the UI read it and update it from there instead of the field in the media item.

@yareg-com
Copy link
Author

yareg-com commented Jan 30, 2022

Thanks for your answer.

I guess I need to give more details of what I'm trying to achive and why I need to update current metadata. When the media is playing the service posts a mediastyle notification that has a custom action with a heart icon. This icon reflects the current state of the media rating - outlined if unrated and filled if rated. When user taps on that action he changes the rating and the notification needs to be updated.

I am using MediaSessionService class that has onNotificationUpdated method. I've overrided that method to build my custom notification, but this function is triggered only when currently playing media changes, specifically 'playWhenReady' field or 'mediaMetadata' field:

if (Util.areEqual(oldPlayerInfo.mediaMetadata, newPlayerInfo.mediaMetadata)
        && oldPlayerInfo.playWhenReady == newPlayerInfo.playWhenReady) {
      return;
    }
    updateNotificationIfNeeded(session); 

So theoretically I can write my own code for that but it will not be the cleanest solution, because in that case I need to update the notification manually, for example like this:

NotificationUtil.setNotification(
    applicationContext,
    NOTIFICATION_ID,
    onUpdateNotification(session).notification
)

This is ugly. It would be better if I could just update the current media metadata and the notification will update automatically.

P.S. I have no problem maintaining the rating outside of the service scope, this is all about updating the notification. I know I can return null inside onUpdateNotification method, but I really like automatic foreground/background transition which lets me witing less code.

@marcbaechinger
Copy link
Contributor

marcbaechinger commented Feb 22, 2022

Thanks for the detailed clarification. I understand your use case.

I marked this issue as an enhancement.

As a note for future implementation:

There are two things we may want to look into:

  1. Apps need a way to invalidate the notification and trigger that a new notification is built and posted. This makes sure that apps can add custom actions (or information) that may change independently from the state of the player that actually triggers an auitomatic update of the notification. That's pretty much what we had with PlayerNotificationmanager.invalidate() in the old world.

  2. In general we want to think of a way that app can update the metadata of the media item of the media source, that eventually is passed to onMediaMetadataChanged(). At least for the use case describe in this issue this would be the most natural way to bring in updated metadata and just let the service/notificationManager do it's usual task with the new metadata.

Point 2 is a bit more complicated because the media item has it's roots in the media source and is currently not supposed to change. Point 1 seems a valuable addition to the changes we are currently working on to have better notification support.

I can't give you any time commitments for any of these points but I will make sure that 1) is part of the first stable release of Media3.

@y20k
Copy link

y20k commented Aug 16, 2022

Hi,

for streaming radio apps it is also a problem, that is not possible to update the MediaSession, respectively the Notification for a MediaItem. Radio streams often publish "Currently Playing" metadata, which can be received in onMetadata() in the Player.Listener. If you want to surface the radio station metadata in the Notification, you need a way to update the Notification / MediaSession.

I think podcast apps that support chapters might run into the same issue.

@tonihei
Copy link
Collaborator

tonihei commented Aug 16, 2022

@y20k The metadata received via onMetadata should already update the notification because it's part of player.getMediaMetadata() (which is used for the notification). If you think this isn't working in specific cases, could you file a new issue for this? This issue here is more updating the app-provided metadata in the MediaItem.

@y20k
Copy link

y20k commented Aug 16, 2022

Hi @y20k

I was not aware Icecast metadata is already being put into MediaMetadata. That is great! ❤️ No need to intercept the notification updates when new Icecast metadata arrives.

I think I still found an issue. An important part of Icecast metadata seems not to be used when creating MediaMetadata.
I am not sure, if is a bug, or intended. I created the issue #153 to document my findings.

@sampengilly
Copy link

sampengilly commented Feb 28, 2023

Not sure I follow on the last few responses here. Is that to say that if a Live audio stream internally holds metadata through this IcyInfo standard it will get automatically picked up?

What about the use case where the stream itself doesn't have such metadata, but information is provided through other means and must be updated in the UI/Notification. For instance an API that returns the currently airing radio program along with a timestamp for when the next program is scheduled to start. Is there no way currently to update the MediaMetadata with information received through this mechanism after the MediaItem containing the stream has started playing? If not that seems like a pretty major shortcoming given that Media3 is geared around automatically setting up behaviours driven by MediaMetadata (e.g. the notification behaviour).

In the app I'm working on I'm also hoping to treat the MediaMetadata as the single source of truth for what to display on the app's own player UI as well.

@sampengilly
Copy link

sampengilly commented Feb 28, 2023

Looks like the functionality described above (sourcing metadata about a live radio stream) could be achieved through a MediaSource and providing various windows? (Correct me if that is completely the wrong assumption or ballpark).

Though the use of media sources seems to be something that's limited to the ExoPlayer class itself and isn't exposed on the Player interface or MediaController, and playback of MediaItems needs to go through the MediaSession.Callback onAddMediaItems callback to populate the media URI given request metadata. There doesn't seem like an obvious entry point for making use of MediaSources

EDIT: Nevermind, I see the setMediaSourceFactory method on the ExoPlayer builder can be used to generate appropriate media sources given a media item.

rohitjoins pushed a commit that referenced this issue Jul 14, 2023
This changes all MediaSources in our library to allow updates to
their MediaItems (if supported).

Issue: google/ExoPlayer#9978
Issue: #33
PiperOrigin-RevId: 546808812
rohitjoins pushed a commit to google/ExoPlayer that referenced this issue Jul 14, 2023
This changes all MediaSources in our library to allow updates to
their MediaItems (if supported).

Issue: #9978
Issue: androidx/media#33
PiperOrigin-RevId: 546808812
@tonihei
Copy link
Collaborator

tonihei commented Aug 30, 2023

This will be possible via replaceMediaItem from 1.2.0.

@tonihei tonihei closed this as completed Aug 30, 2023
@androidx androidx locked and limited conversation to collaborators Oct 30, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants