Video Player using JavaFX & Fxml
Video Player using JavaFX & Fxml

Video Player using JavaFX 19 & Fxml

Posted on

This is a JavaFX program that provides a simple media player interface.

Video Player

The program has an FXML file that defines the user interface components, including a MediaView for displaying the video, a Slider for controlling the position of the video, a Slider for controlling the volume, a Label for displaying the total time of the video, and several Buttons for controlling the playback.

The program has a DeveController class that handles the logic of the media player. It implements the Initializable interface to define an initialize() method that sets up the volumeSlider to update the volume of the mediaPlayer.

The selectVideo() method is called when the user clicks a button to select a video file. It uses the FileChooser class to prompt the user to select a file, and then creates a Media object from the selected file’s URL. It creates a new MediaPlayer object with the Media object, sets the mediaView to display the video, and plays the video.

The videoSlider is updated in real-time as the video plays, and its value is used to seek to a specific position in the video when the user moves the slider. The currentTimeProperty() of the mediaPlayer is bound to the videoSlider to update its value as the video plays.

The mediaPlayer.statusProperty() is also bound to the videoSlider to set its maximum value to the total duration of the video when the media is ready.

The playMedia() method toggles the playback of the video when the user clicks the playBtn. It updates the mediaPlayer’s status and updates the imageView’s image accordingly.

The increaseVolume() and decreaseVolume() methods adjust the volume of the mediaPlayer and the volumeSlider when the user clicks the respective volume control buttons.

Overall, this program provides a simple media player interface with basic playback controls, volume controls, and position controls.

package devplay;

import java.io.File;
import static java.lang.Boolean.valueOf;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.concurrent.Callable;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.Slider;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaPlayer.Status;
import javafx.scene.media.MediaView;
import javafx.scene.text.Text;
import javafx.stage.FileChooser;
import javafx.util.*;

public class DeveController implements Initializable {

    @FXML
    private MediaView mediaView;
    @FXML
    private Slider videoSlider;
    @FXML
    private Slider volumeSlider;
    @FXML
    private Button playBtn;
    @FXML
    public Label totalTime;

    Image playImage = new Image("D:\\PROGRAMMING\\JAVA\\Java_Fx\\devplay\\src\\img\\play.png");
    Image pauseImage = new Image("D:\\PROGRAMMING\\JAVA\\Java_Fx\\devplay\\src\\img\\pause.png");
    
    
    @FXML
    ImageView imageView = new ImageView(playImage);

    private File file;
    private Media media;
    private MediaPlayer mediaPlayer;

    private String videoUrl = null;
    private String videoName = null;

    public void setMediaPlayer(MediaPlayer mediaPlayer) {
        this.mediaPlayer = mediaPlayer;
        volumeSlider.setValue(mediaPlayer.getVolume() * 100);
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        volumeSlider.valueProperty().addListener((observable, oldValue, newValue) -> {
            if (mediaPlayer != null) {
                mediaPlayer.setVolume(newValue.doubleValue() / 100);
            }
        });
    }

    public void selectVideo() {
        try {
            FileChooser fe = new FileChooser();
            fe.setInitialDirectory(new File("D:/Screen Record/Intro Outro"));
            fe.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("VIDEO.File", "*.mp4"));
            file = fe.showOpenDialog(null);
            if (file != null) {
                String videoUrl = file.toURI().toString();

                if (mediaPlayer != null) {
                    mediaPlayer.stop();
                }

                Media media = new Media(videoUrl);
                mediaPlayer = new MediaPlayer(media);
                mediaView.setFitWidth(650);
                mediaView.setFitHeight(305);
                mediaView.setMediaPlayer(mediaPlayer);
                mediaPlayer.play();
            }

            videoSlider.valueProperty().addListener((observable, oldValue, newValue) -> {
                if (videoSlider.isValueChanging()) {
                    mediaPlayer.seek(Duration.seconds(newValue.doubleValue()));
                }
            });

            mediaPlayer.currentTimeProperty().addListener((observable, oldValue, newValue) -> {
                videoSlider.setValue(newValue.toSeconds());
                if (newValue.equals(mediaPlayer.getTotalDuration())) {
                    mediaPlayer.stop();
                    videoSlider.setValue(videoSlider.getMax());
                }

                Duration duration = mediaPlayer.getCurrentTime();
                int totalSeconds = (int) duration.toSeconds();
                int hours = totalSeconds / 3600;
                int minutes = (totalSeconds % 3600) / 60;
                int seconds = totalSeconds % 60;
                String formattedTime = String.format("%02d:%02d:%02d", hours, minutes, seconds);
                totalTime.setText(formattedTime);
            });

            mediaPlayer.statusProperty().addListener((observable, oldValue, newValue) -> {
                if (newValue == MediaPlayer.Status.READY) {
                    videoSlider.setMax(mediaPlayer.getTotalDuration().toSeconds());
                }
            });

            if (file != null) {
                mediaPlayer.play();
            }

            if (mediaPlayer.getStatus() == Status.PLAYING) {
                imageView.setImage(playImage);
            } else {
                imageView.setImage(pauseImage);
            }

        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    public void playMedia() {
        if (mediaPlayer.getStatus() == Status.PLAYING) {
            mediaPlayer.pause();
            imageView.setImage(playImage);
        } else {
            mediaPlayer.play();
            imageView.setImage(pauseImage);
        }
    }

    public void resetMedia() {
        if (mediaPlayer.getStatus() != MediaPlayer.Status.READY) {
            mediaPlayer.seek(Duration.seconds(0.0));
        }
    }

    @FXML
    private void increaseVolume() {
        if (mediaPlayer != null) {
            double volume = mediaPlayer.getVolume() * 100;
            volume += 5;
            if (volume > 100) {
                volume = 100;
            }
            mediaPlayer.setVolume(volume / 100);
            volumeSlider.setValue(volume);
        }
    }

    @FXML
    private void decreaseVolume() {
        if (mediaPlayer != null) {
            double volume = mediaPlayer.getVolume() * 100;
            volume -= 5;
            if (volume < 0) {
                volume = 0;
            }
            mediaPlayer.setVolume(volume / 100);
            volumeSlider.setValue(volume);
        }
    }
}

The given source code is an XML file that defines a user interface layout for a JavaFX application. The UI consists of a VBox container, which contains several child nodes, including a MediaView for displaying video content, a Slider for controlling the playback position of the video, a Button for playing/pausing the video, and other UI elements for controlling the volume, resetting the video, and selecting a new video file.

Each child node in the VBox container is defined using XML tags, with attributes and nested child nodes for specifying layout and functionality. For example, the MediaView node has an fx:id attribute for referencing it in the Java code, and a fitHeight and fitWidth attribute for specifying its size. The Button node has an onAction attribute for specifying a Java method to be called when the button is clicked, and a graphic child node for specifying an icon image to be displayed on the button. The font of the Label node is also customized using the nested Font node.

The XML file also includes several import statements at the beginning, which import various JavaFX classes and components that are used in the UI definition. These import statements allow the XML file to reference these classes and components by name, without needing to specify their full package names in the XML tags.

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Slider?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.media.MediaView?>
<?import javafx.scene.text.Font?>


<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="357.0" prefWidth="527.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="devplay.DeveController">
   <children>
      <MediaView fx:id="mediaView" fitHeight="300.0" fitWidth="520.0" />
      <Slider fx:id="videoSlider" prefHeight="14.0" prefWidth="591.0">
         <VBox.margin>
            <Insets left="5.0" right="5.0" />
         </VBox.margin>
      </Slider>
      <HBox alignment="CENTER_LEFT" prefHeight="42.0" prefWidth="600.0">
         <children>
            <Button fx:id="playBtn" onAction="#playMedia" prefHeight="23.0" prefWidth="31.0">
               <graphic>
                  <ImageView fx:id="imageView" fitHeight="22.0" fitWidth="25.0" pickOnBounds="true" preserveRatio="true">
                     <image>
                        <Image url="@../img/pause.png" />
                     </image>
                  </ImageView>
               </graphic>
               <HBox.margin>
                  <Insets left="10.0" />
               </HBox.margin>
            </Button>
            <HBox alignment="CENTER_RIGHT" prefHeight="42.0" prefWidth="162.0">
               <children>
                  <Label prefHeight="17.0" prefWidth="51.0" text="Volume" />
                  <Slider fx:id="volumeSlider" nodeOrientation="LEFT_TO_RIGHT" prefHeight="14.0" prefWidth="89.0">
                     <opaqueInsets>
                        <Insets />
                     </opaqueInsets>
                  </Slider>
               </children>
            </HBox>
            <HBox alignment="CENTER" prefHeight="42.0" prefWidth="91.0">
               <children>
                  <Label fx:id="totalTime" layoutX="10.0" layoutY="21.0" prefHeight="21.0" prefWidth="68.0" text="00:00:00">
                     <font>
                        <Font size="15.0" />
                     </font>
                  </Label>
               </children>
            </HBox>
            <HBox alignment="CENTER_RIGHT" prefHeight="42.0" prefWidth="48.0">
               <children>
                  <Button mnemonicParsing="false" onAction="#resetMedia" prefHeight="29.0" prefWidth="36.0">
                     <graphic>
                        <ImageView fitHeight="28.0" fitWidth="20.0" pickOnBounds="true" preserveRatio="true">
                           <image>
                              <Image url="@../img/undo.png" />
                           </image>
                        </ImageView>
                     </graphic>
                     <HBox.margin>
                        <Insets left="20.0" />
                     </HBox.margin>
                  </Button>
               </children>
            </HBox>
            <HBox alignment="CENTER_RIGHT" prefHeight="42.0" prefWidth="159.0">
               <children>
                  <Button mnemonicParsing="false" onAction="#selectVideo" prefHeight="20.0" prefWidth="124.0" text="Choose Video">
                     <graphic>
                        <ImageView fitHeight="25.0" fitWidth="23.0" pickOnBounds="true" preserveRatio="true">
                           <image>
                              <Image url="@../img/folder.png" />
                           </image>
                        </ImageView>
                     </graphic>
                     <HBox.margin>
                        <Insets left="20.0" />
                     </HBox.margin>
                  </Button>
               </children>
            </HBox>
         </children>
         <opaqueInsets>
            <Insets />
         </opaqueInsets>
      </HBox>
   </children>
</VBox>