From a8160ce65ec8795551d01c387a69bdda08206e73 Mon Sep 17 00:00:00 2001 From: Seil0 Date: Fri, 3 Apr 2020 16:03:52 +0200 Subject: [PATCH] new media player based on vlcj [Part 2] the new media player replaces the old one by now --- .../HomeFlix/player/NewMediaPlayer.java | 115 --------- .../kellerkinder/HomeFlix/player/Player.java | 6 +- .../HomeFlix/player/PlayerController.java | 236 ++++++++---------- 3 files changed, 104 insertions(+), 253 deletions(-) delete mode 100644 src/main/java/kellerkinder/HomeFlix/player/NewMediaPlayer.java diff --git a/src/main/java/kellerkinder/HomeFlix/player/NewMediaPlayer.java b/src/main/java/kellerkinder/HomeFlix/player/NewMediaPlayer.java deleted file mode 100644 index c6952a0..0000000 --- a/src/main/java/kellerkinder/HomeFlix/player/NewMediaPlayer.java +++ /dev/null @@ -1,115 +0,0 @@ -package kellerkinder.HomeFlix.player; - -import java.nio.ByteBuffer; - -import javafx.application.Platform; -import javafx.scene.Scene; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.image.PixelBuffer; -import javafx.scene.image.PixelFormat; -import javafx.scene.image.WritableImage; -import javafx.scene.layout.StackPane; -import javafx.stage.Stage; -import kellerkinder.HomeFlix.application.Main; -import uk.co.caprica.vlcj.factory.MediaPlayerFactory; -import uk.co.caprica.vlcj.player.base.MediaPlayer; -import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer; -import uk.co.caprica.vlcj.player.embedded.videosurface.CallbackVideoSurface; -import uk.co.caprica.vlcj.player.embedded.videosurface.VideoSurfaceAdapters; -import uk.co.caprica.vlcj.player.embedded.videosurface.callback.BufferFormat; -import uk.co.caprica.vlcj.player.embedded.videosurface.callback.BufferFormatCallback; -import uk.co.caprica.vlcj.player.embedded.videosurface.callback.RenderCallback; -import uk.co.caprica.vlcj.player.embedded.videosurface.callback.format.RV32BufferFormat; - -public class NewMediaPlayer { - - private MediaPlayerFactory mediaPlayerFactory; - private EmbeddedMediaPlayer embeddedMediaPlayer; - private WritableImage videoImage; - private PixelBuffer videoPixelBuffer; - private ImageView videoImageView; - - private Stage stage; - private StackPane pane; - private Scene scene; - - public NewMediaPlayer() { - mediaPlayerFactory = new MediaPlayerFactory(); - embeddedMediaPlayer = mediaPlayerFactory.mediaPlayers().newEmbeddedMediaPlayer(); - embeddedMediaPlayer.videoSurface().set(new FXCallbackVideoSurface()); - } - - public void init() { - // Auto-generated method stub - stage = new Stage(); - pane = new StackPane(); - scene = new Scene(pane); - - videoImageView = new ImageView(); - videoImageView.setPreserveRatio(true); - videoImageView.fitWidthProperty().bind(pane.widthProperty()); - videoImageView.fitHeightProperty().bind(pane.heightProperty()); - pane.getChildren().add(videoImageView); - - stage.setScene(scene); - stage.setTitle("HomeFlix"); - stage.getIcons().add(new Image(Main.class.getResourceAsStream("/icons/Homeflix_Icon_64x64.png"))); - stage.setOnCloseRequest(event -> { - //DBController.getInstance().setCurrentTime(streamURL, playerController.getCurrentTime()); - //playerController.getMediaPlayer().stop(); - stop(); - stage.close(); - }); - stage.show(); - } - - public void play(String streamURL) { - embeddedMediaPlayer.media().play(streamURL); - } - - public void stop() { - embeddedMediaPlayer.controls().stop(); - embeddedMediaPlayer.release(); - mediaPlayerFactory.release(); - System.out.println("released"); - } - - private class FXCallbackVideoSurface extends CallbackVideoSurface { - FXCallbackVideoSurface() { - super(new FXBufferFormatCallback(), new FXRenderCallback(), true, - VideoSurfaceAdapters.getVideoSurfaceAdapter()); - } - } - - private class FXBufferFormatCallback implements BufferFormatCallback { - private int sourceWidth; - private int sourceHeight; - - @Override - public BufferFormat getBufferFormat(int sourceWidth, int sourceHeight) { - this.sourceWidth = sourceWidth; - this.sourceHeight = sourceHeight; - return new RV32BufferFormat(sourceWidth, sourceHeight); - } - - @Override - public void allocatedBuffers(ByteBuffer[] buffers) { - assert buffers[0].capacity() == sourceWidth * sourceHeight * 4; - PixelFormat pixelFormat = PixelFormat.getByteBgraPreInstance(); - videoPixelBuffer = new PixelBuffer<>(sourceWidth, sourceHeight, buffers[0], pixelFormat); - videoImage = new WritableImage(videoPixelBuffer); - videoImageView.setImage(videoImage); - } - } - - private class FXRenderCallback implements RenderCallback { - @Override - public void display(MediaPlayer mediaPlayer, ByteBuffer[] nativeBuffers, BufferFormat bufferFormat) { - Platform.runLater(() -> { - videoPixelBuffer.updateBuffer(pb -> null); - }); - } - } - -} diff --git a/src/main/java/kellerkinder/HomeFlix/player/Player.java b/src/main/java/kellerkinder/HomeFlix/player/Player.java index 6544a51..b608df6 100644 --- a/src/main/java/kellerkinder/HomeFlix/player/Player.java +++ b/src/main/java/kellerkinder/HomeFlix/player/Player.java @@ -81,14 +81,14 @@ public class Player { stage.setTitle("HomeFlix"); stage.getIcons().add(new Image(Main.class.getResourceAsStream("/icons/Homeflix_Icon_64x64.png"))); stage.setOnCloseRequest(event -> { - playerController.stop2(); + playerController.stop(); stage.close(); }); //stage.setFullScreen(true); stage.show(); - playerController.init2(); - playerController.start2(); + playerController.init(); + playerController.start(); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/kellerkinder/HomeFlix/player/PlayerController.java b/src/main/java/kellerkinder/HomeFlix/player/PlayerController.java index bc131a4..1cdc86e 100644 --- a/src/main/java/kellerkinder/HomeFlix/player/PlayerController.java +++ b/src/main/java/kellerkinder/HomeFlix/player/PlayerController.java @@ -44,16 +44,14 @@ import javafx.scene.image.WritableImage; import javafx.scene.input.MouseEvent; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; -import javafx.scene.media.Media; -import javafx.scene.media.MediaPlayer; import javafx.scene.media.MediaView; -import javafx.util.Duration; import kellerkinder.HomeFlix.controller.DBController; import kellerkinder.HomeFlix.controller.XMLController; import kellerkinder.HomeFlix.datatypes.FilmTabelDataType; import uk.co.caprica.vlcj.factory.MediaPlayerFactory; +import uk.co.caprica.vlcj.player.base.MediaPlayer; import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer; import uk.co.caprica.vlcj.player.embedded.videosurface.CallbackVideoSurface; import uk.co.caprica.vlcj.player.embedded.videosurface.VideoSurfaceAdapters; @@ -83,23 +81,20 @@ public class PlayerController { @FXML private ImageView fullscreenIcon; private Player player; - private Media media; - private MediaPlayer mediaPlayer2; - private MediaPlayerFactory mediaPlayerFactory; private EmbeddedMediaPlayer embeddedMediaPlayer; private WritableImage videoImage; private PixelBuffer videoPixelBuffer; private FilmTabelDataType film; - private long currentTime = 0; - private long seekTime = 0; private long startTime = 0; + private long currentTime = 0; + private long endTime = 0; + private long skipTime = 0; private long duration = 0; private int season = 0; private int episode = 0; - private int countdown = 0; private boolean mousePressed = false; private boolean showControls = true; private boolean autoplay; @@ -111,20 +106,19 @@ public class PlayerController { /** * create a new PlayerWindow object - * @param mainWCon the MainWindowController TODO do we need this? * @param player the player object (needed for closing action) * @param film the film object */ - public PlayerController(Player player, String streamURL) { + public PlayerController(Player player, String mediaURL) { this.player = player; - this.film = DBController.getInstance().getStream(streamURL); + this.film = DBController.getInstance().getStream(mediaURL); mediaPlayerFactory = new MediaPlayerFactory(); embeddedMediaPlayer = mediaPlayerFactory.mediaPlayers().newEmbeddedMediaPlayer(); embeddedMediaPlayer.videoSurface().set(new FXCallbackVideoSurface()); } - public void init2() { + public void init() { // initialize the image view videoImageView.setPreserveRatio(true); videoImageView.fitWidthProperty().bind(player.getStage().widthProperty()); @@ -136,114 +130,71 @@ public class PlayerController { season = !film.getSeason().isEmpty() ? Integer.parseInt(film.getSeason()) : 0; episode = !film.getEpisode().isEmpty() ? Integer.parseInt(film.getEpisode()) : 0; - initActions2(); - } - - private void initActions2() { - embeddedMediaPlayer.events().addMediaPlayerEventListener( new HFMediaPlayerEventListener() { - - @Override - public void timeChanged(uk.co.caprica.vlcj.player.base.MediaPlayer mediaPlayer, long newTime) { - timeSlider.setValue((newTime / 1000) / 60); - } - - @Override - public void error(uk.co.caprica.vlcj.player.base.MediaPlayer mediaPlayer) { - // Auto-generated method stub - - } - - @Override - public void lengthChanged(uk.co.caprica.vlcj.player.base.MediaPlayer mediaPlayer, long newLength) { - duration = newLength; - timeSlider.setMax((duration / 1000) / 60); - } - - }); - } - - public void start2() { - embeddedMediaPlayer.media().play(film.getStreamUrl()); - embeddedMediaPlayer.controls().skipTime((long) startTime); - } - - public void stop2() { - DBController.getInstance().setCurrentTime(film.getStreamUrl(), embeddedMediaPlayer.status().time()); - embeddedMediaPlayer.controls().stop(); - embeddedMediaPlayer.release(); - mediaPlayerFactory.release(); - } - - - /** - * initialize the PlayerWindow - */ - public void init() { - initActions(); - + initPlayerWindow(); initMediaPlayer(); + initSlider(); } private void initMediaPlayer() { - // start the media if the player is ready - mediaPlayer2.setOnReady(new Runnable() { + embeddedMediaPlayer.events().addMediaPlayerEventListener( new HFMediaPlayerEventListener() { + @Override - public void run() { - duration = (long) media.getDuration().toMillis(); - timeSlider.setMax((duration / 1000) / 60); - - mediaPlayer2.play(); - mediaPlayer2.seek(Duration.millis(startTime)); + public void timeChanged(MediaPlayer mediaPlayer, long newTime) { + currentTime = newTime; + endTime = duration - newTime; + + Platform.runLater(() -> { + updateControls(); + }); } - }); - - // every time the play time changes execute this - mediaPlayer2.currentTimeProperty().addListener(new ChangeListener() { + @Override - public void changed(ObservableValue observable, Duration oldValue, Duration newValue) { - currentTime = (long) newValue.toMillis(); // set the current time - double timeToEnd = (duration - currentTime); - - if (timeToEnd < 20000 && episode != 0 && autoplay) { - // show 20 seconds before the end a button (next episode in 10 seconds) - if (!nextEpBtn.isVisible()) - nextEpBtn.setVisible(true); - - if (countdown != (int) ((timeToEnd - 10000) / 1000)) { - countdown = (int) ((timeToEnd - 10000) / 1000); - nextEpBtn.setText("next episode in " + countdown + " seconds"); // TODO translate - bottomVBox.setVisible(true); - } - - // if we are end time -10 seconds, do autoplay, if activated - if (timeToEnd < 10000) { - nextEpBtn.setVisible(false); - autoPlayNewFilm(); - } - } else if (timeToEnd < 120) { - // if we are 120ms to the end stop the media - mediaPlayer2.stop(); - DBController.getInstance().setCurrentTime(film.getStreamUrl(), 0); // reset old video start time - playIcon.setImage(playArrow); - } else { - if (nextEpBtn.isVisible()) - nextEpBtn.setVisible(false); - } - - if (!mousePressed) { - timeSlider.setValue((currentTime / 1000) / 60); - } + public void error(MediaPlayer mediaPlayer) { + // Auto-generated method stub + } + + @Override + public void lengthChanged(MediaPlayer mediaPlayer, long newLength) { + duration = newLength; + timeSlider.setMax((duration / 1000) / 60); // TODO move timeslider to seconds } - }); + }); } + + private void initSlider() { + // if the mouse on the timeSlider is released, skip to the new position + timeSlider.setOnMouseReleased(new EventHandler() { + @Override + public void handle(MouseEvent event) { + embeddedMediaPlayer.controls().skipTime(skipTime); + mousePressed = false; + } + }); + timeSlider.setOnMousePressed(new EventHandler() { + @Override + public void handle(MouseEvent event) { + mousePressed = true; + } + }); + + // get the new skip time + timeSlider.valueProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue ov, Number old_val, Number new_val) { + skipTime = ((new_val.longValue() * 1000 * 60) - currentTime); + } + }); + } + /** * initialize some PlayerWindow GUI-Elements actions */ - private void initActions() { + private void initPlayerWindow() { player.getScene().addEventFilter(MouseEvent.MOUSE_MOVED, new EventHandler() { + // hide controls timer initialization final Timer timer = new Timer(); TimerTask controlAnimationTask = null; // task to execute save operation @@ -273,35 +224,55 @@ public class PlayerController { timer.schedule(controlAnimationTask, delayTime); } }); - - // if the mouse on the timeSlider is released seek to the new position - timeSlider.setOnMouseReleased(new EventHandler() { - @Override - public void handle(MouseEvent event) { - mediaPlayer2.seek(new Duration(seekTime)); - mousePressed = false; + } + + public void start() { + embeddedMediaPlayer.media().play(film.getStreamUrl()); + embeddedMediaPlayer.controls().skipTime((long) startTime); // skipt to the start time + } + + public void stop() { + DBController.getInstance().setCurrentTime(film.getStreamUrl(), embeddedMediaPlayer.status().time()); + embeddedMediaPlayer.controls().stop(); + embeddedMediaPlayer.release(); + mediaPlayerFactory.release(); + } + + /** + * call this every second to update all timers + */ + private void updateControls() { + // update slider position, if the mouse does not press on the time + if (!mousePressed) { + timeSlider.setValue((currentTime / 1000) / 60); + } + + // TODO update endTime label + + System.out.println(endTime / 1000); + // show the next episode button 20 seconds before the end of a episode + if (endTime < 21000 && episode != 0 && autoplay) { + int countdown = (int) ((endTime / 1000) - 10); // a 10 seconds countdown + + if (!nextEpBtn.isVisible()) { + nextEpBtn.setVisible(true); } - }); - - timeSlider.setOnMousePressed(new EventHandler() { - @Override - public void handle(MouseEvent event) { - mousePressed = true; + + if (endTime > 10000) { + nextEpBtn.setText("next episode in " + countdown + " seconds"); // TODO translate + bottomVBox.setVisible(true); + System.out.println("next episode in " + countdown + " seconds"); + } else { + nextEpBtn.setVisible(false); + // TODO start next episode + System.out.println("playing next episode ..."); } - }); - - // get the new seek time - timeSlider.valueProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue ov, Number old_val, Number new_val) { - seekTime = (long) ((double) new_val * 1000 * 60); - } - }); + } } @FXML void stopBtnAction(ActionEvent event) { - stop2(); + stop(); player.getStage().close(); } @@ -329,7 +300,7 @@ public class PlayerController { @FXML void nextEpBtnAction(ActionEvent event) { - autoPlayNewFilm(); + autoPlayNewFilm(); // TODO } private void autoPlayNewFilm() { @@ -337,17 +308,12 @@ public class PlayerController { DBController.getInstance().setCurrentTime(film.getStreamUrl(), 0); // reset old video start time FilmTabelDataType nextFilm = DBController.getInstance().getNextEpisode(film.getTitle(), episode, season); if (nextFilm != null) { - mediaPlayer2.stop(); + //mediaPlayer2.stop(); film = nextFilm; - init(); autoplay = true; } } - public MediaPlayer getMediaPlayer() { - return mediaPlayer2; - } - public double getCurrentTime() { return currentTime; }