javafxmediamediaview

JavaFX setOnEndOfMedia on next MediaPlayer


I have List<MediaPlayer> that is populating by

private List<MediaPlayer> players() {

    for (String file : path.list((dir1, name) -> {
        if (name.endsWith(SUPPORTED_VIDEO_FILE_EXTENSIONS)){
            return true;
        }
        return false;
    })) {
        players.add(createPlayer(Paths.get(path + "/" + file).toUri().toString()));
        System.out.println(Paths.get(path + "/" + file).toUri().toString());
    }
    return players;
}

In my case size of List<MediaPlayer> is 3. Than i use it there

if (currentNumOfVideo == -1) {
    currentNumOfVideo = 0;
}
mv = new MediaView();
MediaPlayer currentPlayer = players.get(currentNumOfVideo);
mv.setMediaPlayer(currentPlayer);
currentPlayer.play();

players.get(currentNumOfVideo).setOnEndOfMedia(() -> {
    players.get(currentNumOfVideo).stop();
    if (currentNumOfVideo < players.size()) {
        currentNumOfVideo += 1;
        mv.setMediaPlayer(players.get(currentNumOfVideo));
        players.get(currentNumOfVideo).play();
    } else {
        currentNumOfVideo = 0;
        mv.setMediaPlayer(players.get(currentNumOfVideo));
        players.get(currentNumOfVideo).play();
    }
});

First video playing, when it ends second video starts. After second video MediaPlayer stops and didn't play third video.

I understand that because of my setOnEndOfMedia that is only on first MediaPlayer. When the second video starts it doesn't have setOnEndOfMedia. How can I setOnEndOfMedia on every video in my List<MediaPlayer>.


Solution

  • Personally, I would not create all the MediaPlayer instances ahead-of-time. Instead, get a list of Media objects or at least the URIs pointing to the media. Then create a method which is responsible for playing the next video when the current video ends. That method will dispose the old MediaPlayer, create the next MediaPlayer, and configure it to call the same method upon completion. For example:

    import java.util.List;
    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.scene.layout.StackPane;
    import javafx.scene.media.Media;
    import javafx.scene.media.MediaPlayer;
    import javafx.scene.media.MediaView;
    import javafx.stage.Stage;
    
    public class Main extends Application {
    
      private MediaView mediaView;
    
      private List<Media> media;
      private int curIndex;
    
      @Override
      public void start(Stage primaryStage) throws Exception {
        media = ...; // get media from somewhere
    
        mediaView = new MediaView();
        primaryStage.setScene(new Scene(new StackPane(mediaView), 720, 480));
        primaryStage.show();
    
        playNextVideo();
      }
    
      private void playNextVideo() {
        disposePlayer();
        if (curIndex == media.size()) {
          return; // no more videos to play
        }
    
        MediaPlayer player = new MediaPlayer(media.get(curIndex++));
        player.setAutoPlay(true); // play ASAP
        player.setOnEndOfMedia(this::playNextVideo); // play next video when this one ends
        mediaView.setMediaPlayer(player);
      }
    
      private void disposePlayer() {
        MediaPlayer player = mediaView.getMediaPlayer();
        if (player != null) {
          player.dispose(); // release resources
        }
      }
    }
    

    This may cause a pause between videos. If that's not acceptable you could create the next MediaPlayer ahead-of-time, either at the same time as the current MediaPlayer or when the current player reaches a certain timestamp (e.g. 10 seconds before the end). But I still wouldn't create all the MediaPlayer instances ahead-of-time.