2018-04-02 18:29:59 +02:00
/ * *
* Project - HomeFlix
*
2020-04-01 19:48:44 +02:00
* Copyright 2016 - 2020 < seil0 @mosad.xyz >
2018-04-02 18:29:59 +02:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston ,
* MA 02110 - 1301 , USA .
*
* /
2018-12-04 22:31:11 +01:00
2020-04-22 18:29:39 +02:00
package org.mosad.homeflix.player ;
2018-04-01 23:24:49 +02:00
2020-04-01 19:48:44 +02:00
import java.nio.ByteBuffer ;
2020-07-09 19:42:26 +02:00
import java.util.ArrayList ;
import java.util.List ;
2018-04-02 02:18:43 +02:00
import java.util.Timer ;
import java.util.TimerTask ;
2020-04-18 12:15:58 +02:00
import java.util.concurrent.TimeUnit ;
2018-04-01 23:24:49 +02:00
2020-04-22 18:29:39 +02:00
import org.mosad.homeflix.controller.DBController ;
import org.mosad.homeflix.controller.XMLController ;
import org.mosad.homeflix.datatypes.FilmTabelDataType ;
2018-04-01 23:24:49 +02:00
import com.jfoenix.controls.JFXButton ;
2020-05-22 16:46:46 +02:00
import com.jfoenix.controls.JFXDialogLayout ;
2020-07-09 19:42:26 +02:00
import com.jfoenix.controls.JFXListView ;
2020-05-22 16:46:46 +02:00
import com.jfoenix.controls.JFXPopup ;
import com.jfoenix.controls.JFXPopup.PopupHPosition ;
import com.jfoenix.controls.JFXPopup.PopupVPosition ;
2018-04-02 02:18:43 +02:00
import com.jfoenix.controls.JFXSlider ;
2018-04-01 23:24:49 +02:00
2020-04-01 19:48:44 +02:00
import javafx.application.Platform ;
2018-04-02 02:18:43 +02:00
import javafx.beans.value.ChangeListener ;
import javafx.beans.value.ObservableValue ;
2018-04-01 23:24:49 +02:00
import javafx.event.ActionEvent ;
2018-04-02 02:18:43 +02:00
import javafx.event.EventHandler ;
2018-04-01 23:24:49 +02:00
import javafx.fxml.FXML ;
2020-07-09 19:42:26 +02:00
import javafx.geometry.Insets ;
2018-04-02 02:18:43 +02:00
import javafx.scene.Cursor ;
2020-04-18 12:15:58 +02:00
import javafx.scene.control.Label ;
2018-04-02 02:18:43 +02:00
import javafx.scene.image.Image ;
import javafx.scene.image.ImageView ;
2020-04-01 19:48:44 +02:00
import javafx.scene.image.PixelBuffer ;
import javafx.scene.image.PixelFormat ;
import javafx.scene.image.WritableImage ;
2018-04-02 02:18:43 +02:00
import javafx.scene.input.MouseEvent ;
2020-05-22 16:46:46 +02:00
import javafx.scene.layout.AnchorPane ;
2018-04-01 23:24:49 +02:00
import javafx.scene.layout.HBox ;
2020-07-10 16:38:19 +02:00
import javafx.scene.layout.StackPane ;
2018-04-02 02:18:43 +02:00
import javafx.scene.layout.VBox ;
2020-07-12 14:39:01 +02:00
import javafx.scene.paint.Color ;
2020-05-22 16:46:46 +02:00
import javafx.scene.text.Text ;
2020-07-09 19:42:26 +02:00
import javafx.util.StringConverter ;
2020-04-01 19:48:44 +02:00
import uk.co.caprica.vlcj.factory.MediaPlayerFactory ;
2020-07-12 01:18:27 +02:00
import uk.co.caprica.vlcj.media.Media ;
import uk.co.caprica.vlcj.media.MediaEventAdapter ;
import uk.co.caprica.vlcj.media.MediaParsedStatus ;
2020-04-03 16:03:52 +02:00
import uk.co.caprica.vlcj.player.base.MediaPlayer ;
2020-07-09 19:42:26 +02:00
import uk.co.caprica.vlcj.player.base.TrackDescription ;
2020-04-01 19:48:44 +02:00
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 ;
2020-07-10 16:38:19 +02:00
/ * *
* The PlayerController class is the main component of the HomeFlix Player .
* It uses vlcj to play videos and some modified jfoenix GUI components to
* create a Netflix like GUI .
*
* @author seil0
*
* TODO this class needs heavy cleaning
* /
2018-04-01 23:24:49 +02:00
public class PlayerController {
2020-05-22 16:46:46 +02:00
@FXML private AnchorPane panePlayer ;
2020-04-01 19:48:44 +02:00
@FXML private ImageView videoImageView ;
2018-04-07 17:14:35 +02:00
2020-04-18 12:15:58 +02:00
@FXML private HBox hBoxTop ;
2018-12-08 22:48:13 +01:00
@FXML private HBox controlsHBox ;
2020-04-18 12:15:58 +02:00
@FXML private VBox bottomVBox ;
2018-04-01 23:24:49 +02:00
2018-12-08 22:48:13 +01:00
@FXML private JFXSlider timeSlider ;
2018-04-07 17:14:35 +02:00
2020-04-18 13:19:24 +02:00
@FXML private JFXButton btnBack ;
2020-04-21 23:37:02 +02:00
@FXML private JFXButton btnPlay ;
@FXML private JFXButton btnReplay ;
@FXML private JFXButton btnForward ;
2020-05-22 16:46:46 +02:00
@FXML private JFXButton btnAudio ;
@FXML private JFXButton btnFullscreen ;
@FXML private JFXButton btnNextEpisode ;
2019-06-19 16:31:49 +02:00
@FXML private ImageView stopIcon ;
@FXML private ImageView playIcon ;
@FXML private ImageView fullscreenIcon ;
2020-04-18 12:15:58 +02:00
@FXML private Label lblTitle ;
@FXML private Label lblEndTime ;
2018-04-01 23:24:49 +02:00
private Player player ;
2020-04-01 19:48:44 +02:00
private MediaPlayerFactory mediaPlayerFactory ;
private EmbeddedMediaPlayer embeddedMediaPlayer ;
private WritableImage videoImage ;
private PixelBuffer < ByteBuffer > videoPixelBuffer ;
2018-04-07 17:14:35 +02:00
2020-04-18 13:19:24 +02:00
private FilmTabelDataType media ;
2020-04-01 19:48:44 +02:00
private long startTime = 0 ;
2020-04-03 16:03:52 +02:00
private long currentTime = 0 ;
private long endTime = 0 ;
private long skipTime = 0 ;
2020-04-01 19:48:44 +02:00
private long duration = 0 ;
2020-07-09 19:42:26 +02:00
private List < TrackDescription > tracks = new ArrayList < > ( ) ;
private int currentTrack = 0 ;
2020-04-01 19:48:44 +02:00
2018-12-08 22:48:13 +01:00
private int season = 0 ;
private int episode = 0 ;
2020-07-12 01:18:27 +02:00
2018-04-02 02:18:43 +02:00
private boolean mousePressed = false ;
private boolean showControls = true ;
2018-04-07 17:14:35 +02:00
private boolean autoplay ;
2020-04-21 23:37:02 +02:00
private Image playArrow = new Image ( " icons/baseline_play_arrow_white_48dp.png " ) ;
private Image pause = new Image ( " icons/baseline_pause_white_48dp.png " ) ;
private Image fullscreen = new Image ( " icons/baseline_fullscreen_white_48dp.png " ) ;
private Image fullscreenExit = new Image ( " icons/baseline_fullscreen_exit_white_48dp.png " ) ;
2018-12-08 23:44:17 +01:00
2020-07-09 19:42:26 +02:00
private JFXPopup audioPopup ;
2020-07-12 01:18:27 +02:00
// fix wrong buffer resolution
private int videoWidth = 0 ;
private int videoHeigth = 0 ;
2018-12-08 23:44:17 +01:00
/ * *
* create a new PlayerWindow object
* @param player the player object ( needed for closing action )
* @param film the film object
2018-04-02 18:29:59 +02:00
* /
2020-04-03 16:03:52 +02:00
public PlayerController ( Player player , String mediaURL ) {
2018-04-01 23:24:49 +02:00
this . player = player ;
2020-04-18 13:19:24 +02:00
this . media = DBController . getInstance ( ) . getStream ( mediaURL ) ;
2020-04-01 19:48:44 +02:00
mediaPlayerFactory = new MediaPlayerFactory ( ) ;
embeddedMediaPlayer = mediaPlayerFactory . mediaPlayers ( ) . newEmbeddedMediaPlayer ( ) ;
embeddedMediaPlayer . videoSurface ( ) . set ( new FXCallbackVideoSurface ( ) ) ;
}
2020-04-03 16:03:52 +02:00
public void init ( ) {
2020-04-01 19:48:44 +02:00
// initialize the image view
videoImageView . setPreserveRatio ( true ) ;
videoImageView . fitWidthProperty ( ) . bind ( player . getStage ( ) . widthProperty ( ) ) ;
videoImageView . fitHeightProperty ( ) . bind ( player . getStage ( ) . heightProperty ( ) ) ;
// set needed variables
2020-04-18 13:19:24 +02:00
startTime = ( long ) DBController . getInstance ( ) . getCurrentTime ( media . getStreamUrl ( ) ) ;
2020-04-01 19:48:44 +02:00
autoplay = XMLController . isAutoplay ( ) ;
2020-04-18 13:19:24 +02:00
season = ! media . getSeason ( ) . isEmpty ( ) ? Integer . parseInt ( media . getSeason ( ) ) : 0 ;
episode = ! media . getEpisode ( ) . isEmpty ( ) ? Integer . parseInt ( media . getEpisode ( ) ) : 0 ;
2020-04-01 19:48:44 +02:00
2020-07-12 01:18:27 +02:00
if ( episode > 0 ) {
// if the media is a TV show, add season + episode to the title
lblTitle . setText ( media . getTitle ( ) + " S " + season + " :E " + episode ) ;
} else {
lblTitle . setText ( media . getTitle ( ) ) ;
}
2020-04-03 16:03:52 +02:00
initPlayerWindow ( ) ;
initMediaPlayer ( ) ;
2020-07-10 16:38:19 +02:00
initTimeSlider ( ) ;
2018-12-08 23:44:17 +01:00
}
2020-04-01 19:48:44 +02:00
2020-04-18 12:15:58 +02:00
/ * *
* initialize some PlayerWindow GUI - Elements actions
* /
private void initPlayerWindow ( ) {
player . getScene ( ) . addEventFilter ( MouseEvent . MOUSE_MOVED , new EventHandler < MouseEvent > ( ) {
// hide controls timer initialization
final Timer timer = new Timer ( ) ;
TimerTask controlAnimationTask = null ; // task to execute save operation
2020-07-13 23:44:28 +02:00
final long delayTime = 3000 ; // hide the controls after 2 seconds
2020-04-18 12:15:58 +02:00
@Override
public void handle ( MouseEvent mouseEvent ) {
2020-07-09 19:42:26 +02:00
2020-04-18 12:15:58 +02:00
// show controls
if ( ! showControls ) {
2020-08-13 23:48:50 +02:00
showControls = true ;
updateControls ( ) ; // update controls before showing them
2020-04-18 12:15:58 +02:00
player . getScene ( ) . setCursor ( Cursor . DEFAULT ) ;
hBoxTop . setVisible ( true ) ;
bottomVBox . setVisible ( true ) ;
2020-08-13 23:48:50 +02:00
showControls = true ;
2020-04-18 12:15:58 +02:00
}
// hide controls
if ( controlAnimationTask ! = null )
controlAnimationTask . cancel ( ) ;
controlAnimationTask = new TimerTask ( ) {
@Override
public void run ( ) {
2020-07-09 19:42:26 +02:00
// TODO a animation would be nice
2020-04-18 12:15:58 +02:00
hBoxTop . setVisible ( false ) ;
bottomVBox . setVisible ( false ) ;
player . getScene ( ) . setCursor ( Cursor . NONE ) ;
showControls = false ;
}
} ;
timer . schedule ( controlAnimationTask , delayTime ) ;
}
} ) ;
2020-07-13 23:44:28 +02:00
if ( XMLController . isFullscreen ( ) ) {
player . getStage ( ) . setFullScreen ( ! player . getStage ( ) . isFullScreen ( ) ) ;
fullscreenIcon . setImage ( player . getStage ( ) . isFullScreen ( ) ? fullscreenExit : fullscreen ) ;
}
2020-07-26 00:01:35 +02:00
// fix focused button has rippler fill https://github.com/jfoenixadmin/JFoenix/issues/1051
btnBack . setStyle ( " -jfx-rippler-fill: black; " ) ;
2020-04-18 12:15:58 +02:00
}
2020-05-22 16:46:46 +02:00
/ * *
* initialize the embedded media player
* /
2020-04-03 16:03:52 +02:00
private void initMediaPlayer ( ) {
2020-04-01 19:48:44 +02:00
embeddedMediaPlayer . events ( ) . addMediaPlayerEventListener ( new HFMediaPlayerEventListener ( ) {
2020-05-22 16:46:46 +02:00
@Override
public void mediaPlayerReady ( MediaPlayer mediaPlayer ) {
2020-07-09 19:42:26 +02:00
tracks = mediaPlayer . audio ( ) . trackDescriptions ( ) ;
currentTrack = mediaPlayer . audio ( ) . track ( ) ;
2020-05-22 16:46:46 +02:00
}
2020-04-01 19:48:44 +02:00
@Override
2020-04-03 16:03:52 +02:00
public void timeChanged ( MediaPlayer mediaPlayer , long newTime ) {
currentTime = newTime ;
endTime = duration - newTime ;
Platform . runLater ( ( ) - > {
updateControls ( ) ;
} ) ;
2020-04-01 19:48:44 +02:00
}
@Override
2020-04-03 16:03:52 +02:00
public void error ( MediaPlayer mediaPlayer ) {
2020-04-01 19:48:44 +02:00
// Auto-generated method stub
}
@Override
2020-04-03 16:03:52 +02:00
public void lengthChanged ( MediaPlayer mediaPlayer , long newLength ) {
2020-04-01 19:48:44 +02:00
duration = newLength ;
2020-07-09 19:42:26 +02:00
timeSlider . setMax ( duration / 1000 ) ;
2020-04-01 19:48:44 +02:00
}
2020-07-12 01:18:27 +02:00
} ) ;
embeddedMediaPlayer . events ( ) . addMediaEventListener ( new MediaEventAdapter ( ) {
@Override
public void mediaParsedChanged ( Media media , MediaParsedStatus newStatus ) {
super . mediaParsedChanged ( media , newStatus ) ;
videoWidth = embeddedMediaPlayer . video ( ) . videoDimension ( ) . width ;
videoHeigth = embeddedMediaPlayer . video ( ) . videoDimension ( ) . height ;
// start the video
embeddedMediaPlayer . media ( ) . play ( media . newMediaRef ( ) ) ;
2021-12-23 12:48:14 +01:00
embeddedMediaPlayer . controls ( ) . skipTime ( startTime ) ; // skip to the start time
2020-07-12 01:18:27 +02:00
}
2020-04-01 19:48:44 +02:00
} ) ;
}
2020-05-22 16:46:46 +02:00
/ * *
* initialize the time slider
* /
private void initTimeSlider ( ) {
2020-04-03 16:03:52 +02:00
// if the mouse on the timeSlider is released, skip to the new position
timeSlider . setOnMouseReleased ( new EventHandler < MouseEvent > ( ) {
2018-04-07 17:14:35 +02:00
@Override
2020-04-03 16:03:52 +02:00
public void handle ( MouseEvent event ) {
embeddedMediaPlayer . controls ( ) . skipTime ( skipTime ) ;
2020-07-13 23:44:28 +02:00
2020-07-26 00:01:35 +02:00
// update time-stamps if video is paused
if ( ! embeddedMediaPlayer . status ( ) . isPlaying ( ) ) {
Platform . runLater ( ( ) - > {
lblEndTime . setText ( String . format ( " %d:%02d:%02d " ,
TimeUnit . MILLISECONDS . toHours ( endTime - skipTime ) % 24 ,
TimeUnit . MILLISECONDS . toMinutes ( endTime - skipTime ) % 60 ,
TimeUnit . MILLISECONDS . toSeconds ( endTime - skipTime ) % 60 ) ) ;
} ) ;
}
2020-07-13 23:44:28 +02:00
2020-04-03 16:03:52 +02:00
mousePressed = false ;
2018-04-07 17:14:35 +02:00
}
} ) ;
2020-04-03 16:03:52 +02:00
timeSlider . setOnMousePressed ( new EventHandler < MouseEvent > ( ) {
2018-04-07 17:14:35 +02:00
@Override
2020-04-03 16:03:52 +02:00
public void handle ( MouseEvent event ) {
mousePressed = true ;
2018-04-07 17:14:35 +02:00
}
2018-04-02 02:18:43 +02:00
} ) ;
2018-04-07 17:14:35 +02:00
2020-05-22 16:46:46 +02:00
// on value change, get the new skip time
2020-04-03 16:03:52 +02:00
timeSlider . valueProperty ( ) . addListener ( new ChangeListener < Number > ( ) {
@Override
public void changed ( ObservableValue < ? extends Number > ov , Number old_val , Number new_val ) {
2020-07-09 19:42:26 +02:00
skipTime = ( ( new_val . longValue ( ) * 1000 ) - currentTime ) ;
//System.out.println(timeSlider.getChildrenUnmodifiable());
2020-04-03 16:03:52 +02:00
}
} ) ;
2020-07-09 19:42:26 +02:00
timeSlider . setOnMouseMoved ( new EventHandler < MouseEvent > ( ) {
@Override
public void handle ( MouseEvent event ) {
//System.out.println("TEST");
}
} ) ;
// show h:mm:ss in the animated thumb
StringConverter < Double > convert = new StringConverter < Double > ( ) {
@Override
public String toString ( Double object ) {
long time = object . longValue ( ) ;
return String . format ( " %d:%02d:%02d " , TimeUnit . SECONDS . toHours ( time ) % 24 ,
TimeUnit . SECONDS . toMinutes ( time ) % 60 , TimeUnit . SECONDS . toSeconds ( time ) % 60 ) ;
}
@Override
public Double fromString ( String string ) {
return null ;
}
} ;
timeSlider . setLabelFormatter ( convert ) ;
2020-07-10 16:38:19 +02:00
// get the animated thumb as StackPane
StackPane animatedThumb = ( StackPane ) timeSlider . lookup ( " .animated-thumb " ) ;
// System.out.println(animatedThumb);
// System.out.println(animatedThumb.getChildren());
// modify the animated thumb
Text thumbText = ( Text ) animatedThumb . getChildren ( ) . get ( 0 ) ;
thumbText . setStyle ( " -fx-font-size: 15px; -fx-fill: white; " ) ;
2020-07-09 19:42:26 +02:00
// TODO add a preview to the animated thumb, if that's possible
2020-07-10 16:38:19 +02:00
// ImageView iv = new ImageView(fullscreenExit);
// animatedThumb.getChildren().add(iv);
2018-04-02 02:18:43 +02:00
}
2020-04-03 16:03:52 +02:00
2020-07-12 01:18:27 +02:00
// Start the parsing the media meta data
2020-04-03 16:03:52 +02:00
public void start ( ) {
2020-07-12 01:18:27 +02:00
// parse meta data
embeddedMediaPlayer . media ( ) . prepare ( media . getStreamUrl ( ) ) ;
embeddedMediaPlayer . media ( ) . parsing ( ) . parse ( ) ;
2020-04-03 16:03:52 +02:00
}
2020-05-22 16:46:46 +02:00
/ * *
* Stop and release the media player .
* Always call this method to stop the media player .
* /
2020-04-03 16:03:52 +02:00
public void stop ( ) {
2020-04-18 13:19:24 +02:00
DBController . getInstance ( ) . setCurrentTime ( media . getStreamUrl ( ) , embeddedMediaPlayer . status ( ) . time ( ) ) ;
2020-04-03 16:03:52 +02:00
embeddedMediaPlayer . controls ( ) . stop ( ) ;
embeddedMediaPlayer . release ( ) ;
mediaPlayerFactory . release ( ) ;
}
/ * *
* call this every second to update all timers
* /
private void updateControls ( ) {
2020-08-13 23:48:50 +02:00
// update control if they are visible
if ( showControls ) {
// update slider position, if the mouse does not press on the time
if ( ! mousePressed ) {
timeSlider . setValue ( currentTime / 1000 ) ;
}
// update endTime label
lblEndTime . setText ( String . format ( " %d:%02d:%02d " ,
TimeUnit . MILLISECONDS . toHours ( endTime ) % 24 ,
TimeUnit . MILLISECONDS . toMinutes ( endTime ) % 60 ,
TimeUnit . MILLISECONDS . toSeconds ( endTime ) % 60 ) ) ;
}
2020-04-18 12:15:58 +02:00
2020-04-18 13:19:24 +02:00
// show the next episode button 30 seconds before the end of a episode
if ( endTime < 31000 & & episode ! = 0 & & autoplay ) {
int countdown = ( int ) ( ( endTime / 1000 ) - 20 ) ; // a 10 seconds countdown
2020-04-03 16:03:52 +02:00
2020-05-22 16:46:46 +02:00
if ( ! btnNextEpisode . isVisible ( ) ) {
btnNextEpisode . setVisible ( true ) ;
2018-04-07 17:14:35 +02:00
}
2020-04-03 16:03:52 +02:00
2020-04-18 13:19:24 +02:00
if ( endTime > 20000 ) {
2020-05-22 16:46:46 +02:00
btnNextEpisode . setText ( XMLController . getLocalBundle ( ) . getString ( " nextEpisode " )
2020-04-18 13:19:24 +02:00
+ countdown + XMLController . getLocalBundle ( ) . getString ( " seconds " ) ) ;
2020-04-03 16:03:52 +02:00
bottomVBox . setVisible ( true ) ;
} else {
2020-05-22 16:46:46 +02:00
btnNextEpisode . setVisible ( false ) ;
2020-04-21 23:37:02 +02:00
playNextMedia ( ) ;
2018-04-02 02:18:43 +02:00
}
2020-04-03 16:03:52 +02:00
}
2018-04-02 02:18:43 +02:00
}
2020-04-18 13:19:24 +02:00
2018-04-02 02:18:43 +02:00
@FXML
2020-04-18 13:19:24 +02:00
void btnBackAction ( ActionEvent event ) {
2020-04-03 16:03:52 +02:00
stop ( ) ;
2018-04-02 02:18:43 +02:00
player . getStage ( ) . close ( ) ;
2018-04-01 23:24:49 +02:00
}
2020-04-21 23:37:02 +02:00
@FXML
void btnPlayAction ( ActionEvent event ) {
if ( embeddedMediaPlayer . status ( ) . isPlaying ( ) ) {
embeddedMediaPlayer . controls ( ) . pause ( ) ;
playIcon . setImage ( playArrow ) ;
} else {
embeddedMediaPlayer . controls ( ) . play ( ) ;
playIcon . setImage ( pause ) ;
}
}
@FXML
void btnReplayAction ( ActionEvent event ) {
2020-07-26 00:01:35 +02:00
embeddedMediaPlayer . controls ( ) . skipTime ( - 10000 ) ;
if ( ! embeddedMediaPlayer . status ( ) . isPlaying ( ) ) {
currentTime = currentTime - 10000 ;
endTime = endTime + 10000 ;
Platform . runLater ( ( ) - > {
updateControls ( ) ;
} ) ;
}
2020-04-21 23:37:02 +02:00
}
@FXML
void btnForwardAction ( ActionEvent event ) {
2020-07-26 00:01:35 +02:00
embeddedMediaPlayer . controls ( ) . skipTime ( 10000 ) ;
if ( ! embeddedMediaPlayer . status ( ) . isPlaying ( ) ) {
currentTime = currentTime + 10000 ;
endTime = endTime - 10000 ;
Platform . runLater ( ( ) - > {
updateControls ( ) ;
} ) ;
}
2020-04-21 23:37:02 +02:00
}
2018-04-01 23:24:49 +02:00
@FXML
2020-05-22 16:46:46 +02:00
void btnAudioAction ( ActionEvent event ) {
2020-07-09 19:42:26 +02:00
if ( audioPopup = = null ) {
audioPopup = new JFXPopup ( ) ;
JFXListView < String > list = new JFXListView < String > ( ) ;
tracks . forEach ( track - > {
list . getItems ( ) . add ( track . description ( ) ) ;
} ) ;
list . getSelectionModel ( ) . select ( currentTrack ) ;
list . setOnMouseClicked ( ev - > {
setAudioTrack ( list . getSelectionModel ( ) . getSelectedIndex ( ) ) ;
audioPopup . hide ( ) ;
} ) ;
2020-07-12 14:39:01 +02:00
Text heading = new Text ( " Audio " ) ;
heading . setFill ( Color . WHITE ) ;
2020-07-09 19:42:26 +02:00
JFXDialogLayout content = new JFXDialogLayout ( ) ;
content . setPrefSize ( 150 , 200 ) ;
2020-07-12 14:39:01 +02:00
content . setHeading ( heading ) ;
2020-07-09 19:42:26 +02:00
content . setBody ( list ) ;
content . setPadding ( new Insets ( - 20 , - 20 , - 20 , - 20 ) ) ; // fix JFXDialogLayout padding
content . setSpacing ( - 10 ) ; // fix JFXDialogLayout spacing
audioPopup . setPopupContent ( content ) ;
}
if ( ! audioPopup . isShowing ( ) ) {
// TODO this does not work properly
audioPopup . show ( btnAudio , PopupVPosition . BOTTOM , PopupHPosition . RIGHT ,
0 , - 1 * bottomVBox . getHeight ( ) ) ;
} else {
audioPopup . hide ( ) ;
}
2020-05-22 16:46:46 +02:00
}
@FXML
2020-07-13 23:44:28 +02:00
private void btnFullscreenAction ( ActionEvent event ) {
2020-07-12 01:18:27 +02:00
player . getStage ( ) . setFullScreen ( ! player . getStage ( ) . isFullScreen ( ) ) ;
fullscreenIcon . setImage ( player . getStage ( ) . isFullScreen ( ) ? fullscreenExit : fullscreen ) ;
2018-04-01 23:24:49 +02:00
}
2018-12-08 22:48:13 +01:00
@FXML
2020-07-13 23:44:28 +02:00
private void btnNextEpisodeAction ( ActionEvent event ) {
2020-05-22 16:46:46 +02:00
btnNextEpisode . setVisible ( false ) ;
2020-04-21 23:37:02 +02:00
playNextMedia ( ) ;
2018-12-08 22:48:13 +01:00
}
2020-07-09 19:42:26 +02:00
/ * *
* play the next media
* /
2020-04-18 12:15:58 +02:00
private void playNextMedia ( ) {
2018-12-08 22:48:13 +01:00
autoplay = false ;
2020-04-18 13:19:24 +02:00
DBController . getInstance ( ) . setCurrentTime ( media . getStreamUrl ( ) , 0 ) ; // reset old video start time
FilmTabelDataType nextMedia = DBController . getInstance ( ) . getNextEpisode ( media . getTitle ( ) , episode , season ) ;
if ( nextMedia ! = null ) {
2020-04-21 23:37:02 +02:00
embeddedMediaPlayer . media ( ) . play ( nextMedia . getStreamUrl ( ) ) ;
2020-04-18 13:19:24 +02:00
media = nextMedia ;
2018-12-08 22:48:13 +01:00
autoplay = true ;
}
}
2020-07-09 19:42:26 +02:00
/ * *
* change the audio track
* @param track the index of the audio track
* /
private void setAudioTrack ( int track ) {
embeddedMediaPlayer . audio ( ) . setTrack ( track ) ;
currentTrack = track ;
}
2018-04-01 23:24:49 +02:00
2018-04-02 02:18:43 +02:00
public double getCurrentTime ( ) {
return currentTime ;
}
2020-04-01 19:48:44 +02:00
private class FXCallbackVideoSurface extends CallbackVideoSurface {
FXCallbackVideoSurface ( ) {
super ( new FXBufferFormatCallback ( ) , new FXRenderCallback ( ) , true ,
VideoSurfaceAdapters . getVideoSurfaceAdapter ( ) ) ;
}
}
private class FXBufferFormatCallback implements BufferFormatCallback {
2020-07-12 14:39:01 +02:00
2020-04-01 19:48:44 +02:00
@Override
2020-07-12 14:39:01 +02:00
public BufferFormat getBufferFormat ( int sourceWidth , int sourceHeight ) {
2020-04-01 19:48:44 +02:00
return new RV32BufferFormat ( sourceWidth , sourceHeight ) ;
}
@Override
public void allocatedBuffers ( ByteBuffer [ ] buffers ) {
PixelFormat < ByteBuffer > pixelFormat = PixelFormat . getByteBgraPreInstance ( ) ;
2020-07-12 01:18:27 +02:00
// fixes buffer resolution, video resolution mismatch
videoPixelBuffer = new PixelBuffer < > ( videoWidth , videoHeigth , buffers [ 0 ] , pixelFormat ) ;
//videoPixelBuffer = new PixelBuffer<>(sourceWidth, sourceHeight, buffers[0], pixelFormat);
2020-04-01 19:48:44 +02:00
videoImage = new WritableImage ( videoPixelBuffer ) ;
videoImageView . setImage ( videoImage ) ;
}
}
private class FXRenderCallback implements RenderCallback {
@Override
2020-07-12 01:18:27 +02:00
public void display ( MediaPlayer mediaPlayer , ByteBuffer [ ] nativeBuffers , BufferFormat bufferFormat ) {
Platform . runLater ( ( ) - > videoPixelBuffer . updateBuffer ( pb - > null ) ) ;
2020-04-01 19:48:44 +02:00
}
}
2018-04-01 23:24:49 +02:00
}