53

I am trying to play videos in the form of a playlist one after the other. I am using android Exoplayer to play my files, but there are no listeners in mediaplayer to listen to an end of the media file. Is there a way to listen to the end of media file using exoplayer.

Gowtham
  • 11,853
  • 12
  • 43
  • 64
user2949215
  • 663
  • 1
  • 7
  • 11

11 Answers11

63

I know this is old, but just to expand on the accepted answer:

exoPlayer.addListener(this);

.....

@Override
public void onPlayerStateChanged(boolean playWhenReady, int state) {

    if (state == ExoPlayer.STATE_ENDED){
        //player back ended
    }
}
Joe Maher
  • 5,354
  • 5
  • 28
  • 44
43

Exoplayer offer advance implementation which media player do not.

in Advance example of Exoplayer by Android, in FullPlayerActivity.java they have implemented onStateChanged, which offers STATE_ENDED

You can download example from the right hand section of this page under term RELATED SAMPLES

Gowtham
  • 11,853
  • 12
  • 43
  • 64
Murtaza Khursheed Hussain
  • 15,176
  • 7
  • 58
  • 83
37

Google has deprecate ExoPlayer.STATE_ENDED and has replaced with Player.STATE_ENDED.

Since this post has been a while, I will write a Kotlin version of the listener down below.

this.simpleExoPlayer?.addListener(object : Player.DefaultEventListener() {
    override fun onPlayerStateChanged(playWhenReady: Boolean,playbackState: Int) {
        when (playbackState) {
            Player.STATE_IDLE -> {}
            Player.STATE_BUFFERING -> {}
            Player.STATE_READY -> {}
            Player.STATE_ENDED -> {}
        }
    }
})

UPDATE: 05-2020 Since DefaultEventListener and onPlayerStateChanged() are deprecated.

player.addListener(object : Player.EventListener {
     override fun onPlaybackStateChanged(state: Int) {
           if (state == Player.STATE_ENDED) {
              // your code
           }
     }
})
Morgan Koh
  • 2,297
  • 24
  • 24
  • DefaultEventListener() is deprecated use instead this.player?.addListener(object :Player.EventListener{ override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) { when (playbackState) { Player.STATE_IDLE -> { progressBar.visibility=View.VISIBLE} Player.STATE_BUFFERING -> { progressBar.visibility=View.VISIBLE } Player.STATE_READY -> { progressBar.visibility=View.GONE} Player.STATE_ENDED -> { progressBar.visibility=View.GONE} } } }) – Black mamba Jul 08 '19 at 12:09
  • 2
    seems like `EventListener` is also deprecated now, it seems to just need `Player.Listener` now – a_local_nobody Aug 09 '21 at 09:49
14

You can do this:

playerExo.addListener(new ExoPlayer.Listener() {

            @Override
            public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {

                switch(playbackState) {
                    case ExoPlayer.STATE_BUFFERING:
                        break;
                    case ExoPlayer.STATE_ENDED:
                        //do what you want
                        break;
                    case ExoPlayer.STATE_IDLE:
                        break;
                    case ExoPlayer.STATE_PREPARING:
                        break;
                    case ExoPlayer.STATE_READY:
                        break;
                    default:
                        break;
                }
            }

            @Override
            public void onPlayWhenReadyCommitted() {

            }

            @Override
            public void onPlayerError(ExoPlaybackException error) {

            }
        });
        playerExo.seekTo(0);
        playerExo.setPlayWhenReady(true);

But @Murtaza Khursheed Hussain answer is right! Have a nice code day! Let me know if you need something else!

Cristofer
  • 1,046
  • 13
  • 21
11

You have to implement the interface Player.EventLister and add to your exoPlayer.

Just write your code on the method onPlayerStateChanged. See the code.

exoPlayer.addListener( new Player.EventListener() {
            @Override
            public void onTimelineChanged(Timeline timeline, @Nullable Object manifest, int reason) {

            }

            @Override
            public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {

            }

            @Override
            public void onLoadingChanged(boolean isLoading) {

            }

            @Override
            public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {

                switch(playbackState) {
                    case Player.STATE_BUFFERING:
                        break;
                    case Player.STATE_ENDED:
                        //Here you do what you want
                        break;
                    case Player.STATE_IDLE:
                        break;
                    case Player.STATE_READY:
                        break;
                    default:
                        break;
                }
            }

            @Override
            public void onRepeatModeChanged(int repeatMode) {

            }

            @Override
            public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {

            }

            @Override
            public void onPlayerError(ExoPlaybackException error) {

            }

            @Override
            public void onPositionDiscontinuity(int reason) {

            }

            @Override
            public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {

            }

            @Override
            public void onSeekProcessed() {

            }


        });
Leo Paim
  • 500
  • 7
  • 9
4

Google has deprecate ExoPlayer.STATE_ENDED. Use Player.STATE_ENDED instead. See the code below.

player.addListener(new Player.EventListener() {
    @Override
    public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {


    }

    @Override
    public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {

    }

    @Override
    public void onLoadingChanged(boolean isLoading) {

    }

    @Override
    public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {

        if (playWhenReady && playbackState == Player.STATE_READY) {
            // Active playback.
        } else if (playbackState == Player.STATE_ENDED) {
            //The player finished playing all media

           //Add your code here

        } else if (playWhenReady) {
            // Not playing because playback ended, the player is buffering, stopped or
            // failed. Check playbackState and player.getPlaybackError for details.
        } else {
            // Paused by app.
        }

    }

    @Override
    public void onRepeatModeChanged(int repeatMode) {

    }

    @Override
    public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {

    }

    @Override
    public void onPlayerError(ExoPlaybackException error) {

    }

    @Override
    public void onPositionDiscontinuity(int reason) {
        //THIS METHOD GETS CALLED FOR EVERY NEW SOURCE THAT IS PLAYED
      //  int latestWindowIndex = player.getCurrentWindowIndex();


    }

    @Override
    public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {

    }

    @Override
    public void onSeekProcessed() {

    }
});

You can check ExoPlayer Developer page for more information.

adiga
  • 34,372
  • 9
  • 61
  • 83
Oyewo Remi
  • 406
  • 5
  • 8
4

For non-concatenated MediaSource (representing whatever individual piece of media you want to play), you'll receive STATE_ENDED when the piece of media has finished playing.

For a ConcatenatingMediaSource it occurs when the entire concatenation has finished playing (i.e. you've played up to the end of the final item in the concatenation). So, STATE_ENDED occurs when the entire MediaSource has finished playing.

For a ConcatenatingMediaSource, the best callback to detect end of current media and start of next media playback is "onPositionDiscontinuity" You should use onPositionDiscontinuity() to figure out when transitions occur. Please note, onPositionDiscontinuity is invoked for some other cases too, but you can call getCurrentWindowIndex() and compare it to what window you think you're in to determine whether a transition has occurred. You can do something like below:

public void onPositionDiscontinuity() {
  int newIndex = player.getCurrentWindowIndex();
  if (newIndex != currentIndex) {
    // The index has changed ==> last media has ended and next media playback is started.
    currentIndex = newIndex;
  }
}

Please note: onPositionDiscontinuity() is not gets called for the 1st item in the playlist unless we explicitly call player.seekTo(position,0). So to handle anything you are doing for all media playback in the playlist you have to handle separately for the 1st item in the playlist.

Akki
  • 775
  • 8
  • 19
  • `onPositionDiscontinuity()` is called when the track is changed, but what if I want to do something right before it changes? – Sam Chen Mar 31 '21 at 13:24
  • HI @SamChen, onPositionDiscontinuity is dispatched apparently after the first frame of the "next video" is already rendered. But I don’t think there is any such event available that could fire just before the first frame of the "next video" got rendered. But you could add a **VideoFrameMetadataListener** to the video renderer that calls a method on the rendering thread every time a frame is about to be released [link](https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/video/VideoFrameMetadataListener.html) – Akki Apr 01 '21 at 06:35
  • @SamChen Found one discussion on this which may help [link] (https://github.com/google/ExoPlayer/issues/6576) – Akki Apr 01 '21 at 08:02
  • Thank you, but what I mean is not the "next video", is the end of current video. I also created the post for this: https://stackoverflow.com/questions/66889341/android-exoplayer-playlist-pause-current-track-when-it-finishes. – Sam Chen Apr 01 '21 at 15:11
  • @SamChen, I have posted a few options which you can try on your post. I hope that would help. Good luck!! – Akki Apr 01 '21 at 20:08
2

ExoPlayer.STATE_ENDED is depreciated. Use Player.STATE_ENDED instead. Example:

@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
    if(playbackState == Player.STATE_ENDED){
        // your brain put here
    }
}
Tomero Indonesia
  • 1,685
  • 2
  • 16
  • 17
1

I was using playlist with exoplayer and I wanted to stop the media after it was complete.

For me the Player.STATE_ENDED was being called when the playlist ended for me inside

So to fulfill my use case I first set this in exoplayer

 simpleExoPlayer.setRepeatMode(Player.REPEAT_MODE_ONE);

So instead of using EventListener, I had used the AnalyticsListener which gives the onPositionDiscontinuity to override with eventTime parameter and just add a simple code to know whether the media has restarted.

   @Override
   public void onPositionDiscontinuity(EventTime eventTime, int reason) {
        if (eventTime.currentPlaybackPositionMs / 1000 < 1) {
            exoPlayer.setPlayWhenReady(false);
            tvReplay.setVisibility(View.VISIBLE);
        }
    }

Hope this helps your usecase

saurabhlahoti
  • 466
  • 8
  • 21
  • `onPositionDiscontinuity()` is called after the track has changed or right before it changes? Because your formula `currentPlaybackPositionMs / 1000 < 1` looks like the beginning of the track, which should be the next track. – Sam Chen Mar 31 '21 at 13:29
  • Since I have set the REPEAT_MODE_ONE, the track would loop back to its initial state. So it the same track at the beginning – saurabhlahoti Apr 01 '21 at 12:10
  • I see, but is there a way to make it pause at the end of the current track? – Sam Chen Apr 01 '21 at 15:13
  • In that case, you can store the timestamp of the media that is currently being played in this function "onPositionDiscontinuity". And reset and run a counter till the end of the total media length. Keep the repeat mode as REPEAT_MODE_NONE. So everytime a track changes or is seeked onPositionDiscontinuity method will be called and your counter will be reset and run till the end of the media. And when it ends you can pause your media. – saurabhlahoti Apr 03 '21 at 06:36
1

Since DefaultEventListener and onPlayerStateChanged() are deprecated.

Use Player.EventListener and onPlaybackStateChanged()

Kotlin code:

 player!!.addListener(object : Player.EventListener {
            override fun onPlaybackStateChanged(state: Int) {
                if (state == Player.STATE_ENDED) {
                    // your code here
                }
            }
        })
Zeus Almighty
  • 155
  • 1
  • 10
0

Use this

      exoPlayer.addListener(new Player.EventListener() {
            @Override
            public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {

            }

            @Override
            public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {

            }

            @Override
            public void onLoadingChanged(boolean isLoading) {

            }

            @Override
            public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
                if (playbackState == Player.STATE_ENDED) {

                  
                }
            }

            @Override
            public void onRepeatModeChanged(int repeatMode) {

            }

            @Override
            public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {

            }

            @Override
            public void onPlayerError(ExoPlaybackException error) {

            }

            @Override
            public void onPositionDiscontinuity(int reason) {

            }

            @Override
            public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {

            }

            @Override
            public void onSeekProcessed() {

            }
        });
Hesam Kh
  • 111
  • 2
  • 1