google / ExoPlayer

This project is deprecated and stale. The latest ExoPlayer code is available in https://github.com/androidx/media
https://developer.android.com/media/media3/exoplayer
Apache License 2.0
21.75k stars 6.03k forks source link

Migration guide: How to migrate to AndroidX Media3? #10471

Open marcbaechinger opened 2 years ago

marcbaechinger commented 2 years ago

We have recently released AndroidX Media3 1.0.0-beta02 which corresponds to ExoPlayer 2.18.1. The AndroidX Media3 library is published on GitHub just as ExoPlayer and the release artifacts are available over maven just as the artifacts of the ExoPlayer library.

The AndroidX Media3 library is the successor of the ExoPlayer library and we encourage apps to migrate to the new dependencies as soon as possible.

To facilitate the migration, we have written a migration guide that gives some guidance on migrating ExoPlayer, MediaSessionConnector and MediaBrowserServiceCompat. The migration guide is published on developers.android.com.

We are keen to make the migration as easy as possible for developers. For this we also provide a script that facilitates moving your code to the new module and package structure of AndroidX Media3.

Please take a look at these resources when migrating and please give us feedback if you find the migration guide/script can be improved and made more useful for future readers.

We are looking forward to see you again on the AndroidX Media3 side soon! :)

PaulWoitaschek commented 2 years ago

To be honest I feel really lost with the integration. Either I do not understand how Media3 Session is supposed to be used or it does not fit my use case. I'm developing an audiobook player and I'm basically facing multiple challenges. First is, that to the outside I don't really want to expose the underlying media at all. Let's say I have an audio book and that has 20 files and some files have chapter marks in them. In the UI I do not display any individual files but instead a list of merged chapter marks.

With my current understanding of how media3 works, this whole model can not really work as it appears that every playable item is a single file.

I do not want to allow that the playback of individual files can be controlled from the outside. If a session plays a book then my app should prepare the corresponding files for that book, and seeks to the persisted position.

The current implementation seems to be written mostly with the use case in mind, that it's a regular media player and I do not understand how. With my current implementation (media 1) I just have a callback and then prepare my player based on that.

https://github.com/PaulWoitaschek/Voice/blob/f80990a8a1cb0d97873fa8eee48bf7a651bffac9/playback/src/main/kotlin/voice/playback/session/MediaSessionCallback.kt

I would be very helpful for any hints if that is even possible with media3.

tonihei commented 2 years ago

@PaulWoitaschek Thanks for the feedback. Your app set up is interesting because it seems to have a completely different media model between the player itself and the UI.

I don't really want to expose the underlying media at all.

In MediaSession.Callback.onConnect, you can decide which commands/information the controller is allowed to access. I'm not sure if that is what you are looking for as it sounds you'd like to completely replace the media model and not restrict access to any particular methods.

I just have a callback and then prepare my player based on that

It sounds as if you need a connector class that connects the commands and information visible in the UI (=MediaSession) to your player. We are currently working on a simple base class for Player that can be used as an adapter or connector to connect to completely custom players. The resulting connector will look very similar to the existing media1 callbacks and probably the best approach to handle your case.

PaulWoitaschek commented 2 years ago

Thanks for the detailed response. For my current use case that would be great. However that would probably also mean, that I would lose the option to interact with other players seamlessly - like for example the cast player.

Another big topic that I'm struggeling is the model how the playback is set up. From my understanding, the clients control, which media items they set and what they prepare. How would this work with an application like an audiobook player, where every position is actually persisted? I.e. users always want to continue where they stopped the playback, the last time they used the app.

fourofspades commented 2 years ago

Migration script doesn't seem to do very much on MINGW64 on Windows11

marcbaechinger commented 2 years ago

Thanks for reporting. I think we should make sure this is working on Windows somehow. I don't think we specifically look into various environments on which this is possible. The Windows Subsystem for Linux is what we should make it run.

Did you have a chance to check whether is works on the Windows Subsystem for Linux (https://learn.microsoft.com/en-us/windows/wsl/)?

yuroyami commented 1 year ago

In MediaSession.Callback.onConnect, you can decide which commands/information the controller is allowed to access. I'm not sure if that is what you are looking for as it sounds you'd like to completely replace the media model and not restrict access to any particular methods.

@tonihei, The documentation string on override fun onCustomCommandfor the session service, should mention something about this. I spent too much time trying to figure out why the custom commands are not going through and they were just throwing permission denied result code. Were it not for your reply here, I wouldn't have had it fixed, thanks a lot. Honestly, the documentation for sendCustomCommand or onCustomCommand callback should definitely have this mentioned at least briefly.

tonihei commented 1 year ago

@yuroyami: Thanks for pointing this out. We have some previous guidance for custom commands published in https://github.com/androidx/media/issues/38#issuecomment-1138578035 and we are working on getting this onto the official guide pages for Media3. As you pointed out, it's also worth referencing this in the Javadoc and we should add this there too.

ashutoshgngwr commented 1 year ago

I agree with @PaulWoitaschek! I have an app that manages multiple ExoPlayer instances under a single Media Session. It allows playing multiple audios in parallel. With Media 3, the integration will be very cumbersome.

With Media Session Compat API, we currently have a manager class to manage all ExoPlayer instances. It builds an aggregate state for UI components and handles playback controls. To interact with the platform, we have another wrapper class to update the media session's playback state and metadata based on the manager's state. This connector also delivers media session callbacks to the manager.

With Media 3, we will have to implement the Player interface in our manager class, which is very infeasible, given how complicated the Player interface is for our use case. I'm yet to explore Google Cast integration with Media 3. Given the radical changes in Media 3 Session API, I imagine that it will also come with similar complexities.

tonihei commented 1 year ago

@ashutoshgngwr Have you tried to use SimpleBasePlayer? This provides a way to create a Player interface with a similar callback / state-based model as the previous MediaSessionCompat. This is useful for custom players or more complicated wrapper layers like your example.

ashutoshgngwr commented 1 year ago

@tonihei Thank you for the quick response! I must have missed SimpleBasePlayer. It appears to be exactly what I need. Let me try it out now!

tonihei commented 1 year ago

It's not very prominently documented at the moment, but there are more documentation pages on their way that highlight this more clearly.

tonihei commented 1 year ago

But adb shell dumpsys media_session shows metadata: size=0

I think your code is missing setAvailableCommands in the State.Builder. This specifies what controllers are allowed to access and you need Player.COMMAND_GET_CURRENT_MEDIA_ITEM and Player.COMMAND_GET_MEDIA_ITEMS_METADATA for the controller to access your metadata. Actually, the first one is only needed because the MediaSession checks the state too strictly. We can fix that, so that only the latter command is needed.

I can't see the Media Style notification when it is supposed to appear,

The notification appears if there is an item in the playlist and the state is not STATE_IDLE. You could call setState(Player.STATE_READY) for the notification to appear.

MediaSessionCompat.setPlaybackToRemote() and MediaSessionCompat.setPlaybackToLocal()

These are called automatically by the Media3 library depending on the DeviceInfo. You can specify a DeviceInfo (local or remote) via State.Builder.setDeviceInfo.

ashutoshgngwr commented 1 year ago

@tonihei Thanks. I had already resolved issues with metadata and notification. I removed my comment once I realised that this isn't the Media 3 repo and raised a question there (https://github.com/androidx/media/issues/328). I'll go ahead and close it.


Edit: How do I obtain this DeviceInfo?


Edit: If I set the DeviceInfo as done in the ExoPlayer's cast extension, the hardware buttons stop responding instead of adjusting the Chromecast volume.


Adding COMMAND_GET_DEVICE_VOLUME, COMMAND_SET_DEVICE_VOLUME and COMMAND_ADJUST_DEVICE_VOLUME commands to SimpleBasePlayer worked!