Search code examples
androidandroidxexoplayerandroid-media3

How do you play a song when ExoPlayer reaches STATE_ENDED


I am implementing an algorithm for music that involves waiting for ExoPlayer to finish playing and then appending a song to the end of the playlist and starting the playlist from that song.

As a proof of concept, I tried to repeat a song by sending commands when the player completed (I am not trying to use the loop single song feature of ExoPlayer; read the first paragraph again if you think I am). I thought that could be done when the player reaches STATE_ENDED, however, none of the commands I send to the player are getting sent.

Here is the code

    private val listener = object : Player.Listener {

        override fun onPlaybackStateChanged(playbackState: Int) {
            super.onPlaybackStateChanged(playbackState)
            if(playbackState == STATE_ENDED){
                playerHolder.getPlayer().seekToPreviousMediaItem()
                playerHolder.getPlayer().prepare()
                playerHolder.play()
            }
        }

    }

I also tried this:

        override fun onPlaybackStateChanged(playbackState: Int) {
            super.onPlaybackStateChanged(playbackState)
            if(playbackState == STATE_ENDED){
                Handler(playerHolder.getPlayer().applicationLooper).post {
                    playerHolder.getPlayer().seekToPreviousMediaItem()
                    playerHolder.getPlayer().prepare()
                    playerHolder.play()
                }
            }
        }

I have verified that this code runs when the player's playback state changes with the debugger using breakpoints, and stepping through, all the lines are executed when the playback state is STATE_ENDED.

The goal in this snippet was to play the song that just ended. It was a proof of concept for the algorithm I am trying to implement, but it does not work.

How do you send commands to ExoPlayer when the state has reached STATE_ENDED?

As a side note, I have a button in the UI that sends commands via a controller, and when a song ends and the user presses the button, the same song plays again, so it does seem to be possible.


Solution

  • This code works for the proof of concept. The reason being is that the playWhenReady field of the player is still true when in the callback, and a seek command is needed to start playback. When the controller sends a play() command, it implicitly adds a seek call, so that is why the controller play command works where the callbacks calling play() did not. seekToPrevious() did not work because the currentMediaItemIndex was still 0 in the callback, so there was no previous media item to seek to.

            override fun onPlaybackStateChanged(playbackState: Int) {
                super.onPlaybackStateChanged(playbackState)
                if (playbackState == STATE_ENDED) {
                    val player = playerHolder.getPlayer()
                    player.addMediaItem(
                        player.currentMediaItem!!
                    )
                    player.seekTo(
                        player.mediaItemCount - 1,
                        C.TIME_UNSET
                    )
                }
            }