Taslim Oseni
Taslim Oseni

Reputation: 6263

Exoplayer2.x: Player.STATE_ENDED fires twice when a video ends ,how to implement exoplayer in android?

I'm using Exoplayer to play a video in a playlist. I want to auto play the next video when the current video end. To do this, I added an EventListener to my player. Here's a snippet:

private val videoPlayerListener = object: Player.EventListener {
    override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
        when(playbackState) {
            Player.STATE_IDLE -> showLoadingView()
            Player.STATE_READY -> hideLoadingView()
            Player.STATE_BUFFERING -> showVideoProgressWheel()
            Player.STATE_ENDED -> {
                Log.d(TAG, "fired")
                playNextVideo()
            }
        }
    }

    override fun onPlayerError(error: ExoPlaybackException?) {
        // handle error event
    }
}

The problem with this approach is that my logs show that Player.STATE_ENDED fires twice. The chain effect of this is that my playlist plays from

video 1 -> video 3 -> video 5 -> video 7...

instead of

video 1 -> video 2 -> video 3 -> video 4...

I did some research and I found this issue on Github. I checked my code and I realized I was actually calling a method that contains addListener(videoPlayerListener) in OnCreate(), OnStart(), OnPause(), OnResume() and OnStop(). To solve this problem, I added the following before the addListener line:

try {
    player.removeListener(videoPlayerListener)
    Log.d(TAG, "Listener temporarily deleted")
}
catch (e: Exception) {}

Kindly note that there is only one function in my code that contains addListener(...) and I put the removeListener() call directly before the addListener() line. Despite this, Player.STATE_ENDED still gets called twice every single time a video ends.

What do I do?

Upvotes: 4

Views: 3028

Answers (2)

Surajkaran Meghwanshi
Surajkaran Meghwanshi

Reputation: 676

Use this code

override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
                super.onPlayerStateChanged(playWhenReady, playbackState)
                if (playbackState == Player.STATE_BUFFERING){
                    binding.loaderBar.visibility = View.VISIBLE
                }else if (playbackState == Player.STATE_READY){
                    MainActivity.isAnyVideoPlaying = true
                    binding.apply {
                        playerView.visibility = View.VISIBLE
                        loaderBar.visibility = View.GONE
                        ivSlider.visibility = View.GONE
                        ivPlay.visibility = View.GONE
                    }
                }else if (playbackState == Player.STATE_ENDED){
                    Log.d("state_ended-", "${System.currentTimeMillis()}  for ${banners[position]}")
                    MainActivity.isAnyVideoPlaying = false
                }
            }

Upvotes: 0

Quick learner
Quick learner

Reputation: 11457

Use this code

Take a variable like this globally

var isVideoEnded=false

then in your listener do this

private val videoPlayerListener = object: Player.EventListener {
    override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
        when(playbackState) {
            Player.STATE_IDLE -> showLoadingView()
            Player.STATE_READY -> {
                    hideLoadingView()
                    isVideoEnded=false
                                }
            Player.STATE_BUFFERING -> showVideoProgressWheel()
            Player.STATE_ENDED -> {
                   if(!isVideoEnded){
                      playNextVideo()
                      isVideoEnded=true
                  }

                Log.d(TAG, "fired")

            }
        }
    }

    override fun onPlayerError(error: ExoPlaybackException?) {
        // handle error event
    }
}

Upvotes: 6

Related Questions