CelestialCoyote
CelestialCoyote

Reputation: 21

javafx media player with two views

I would like to create a JavaFX media player app that has two stages (primary and secondary) that show the same video or play the same audio file but are controlled from the primary stage. The idea is for the secondary stage to be moved to a second monitor or attached video projector and always be in fullscreen mode with no controls visible.

Is that possible?

Here is the code for the MediaPlayer.java.

package mediaplayer;

import java.io.File;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class FulldomeMediaPlayer extends Application {

  private File file = new   File("./media/video.mp4");
  private final String MEDIA_URL = file.toURI().toString();

  @Override
  public void start(Stage primaryStage) {
    // Create media player.
    Media media = new Media(MEDIA_URL);
    MediaPlayer mediaPlayer = new MediaPlayer(media);
    MediaControl mediaControl = new MediaControl(mediaPlayer);

    Group root = new Group();

    Scene scene = new Scene(root, 512, 512);
    scene.setRoot(mediaControl);

    primaryStage.setScene(scene);
    primaryStage.setTitle("Media Player");
    primaryStage.setFullScreen(false);
    primaryStage.show();

    Stage secondStage = new Stage();
    secondStage.setX(1920.0);
    secondStage.setY(0.0);
    secondStage.setFullScreen(true);

    //secondStage.setScene(scene);
    secondStage.setTitle("Second Media Player");
    secondStage.show();
  }

  public static void main(String[] args) {
    launch(args);
  }
}

Here is the code for the MediaControl.java that has the controls to start and stop the video. It is based on the Oracle example, but I modified it so that the video would resize in the center panel of the border pane.

package mediaplayer;

import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.layout.*;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaPlayer.Status;
import javafx.scene.media.MediaView;
import javafx.util.Duration;


public class MediaControl extends BorderPane {

   private MediaPlayer mp;
   private MediaView mediaView;
   private final boolean repeat = false;
   private boolean stopRequested = false;
   private boolean atEndOfMedia = false;
   private Duration duration;
   private Slider timeSlider;
   private Label playTime;
   private Slider volumeSlider;
   private HBox mediaBar;

public MediaControl(final MediaPlayer mp) {
    this.mp = mp;
    setStyle("-fx-background-color: #bfc2c7;");
    mediaView = new MediaView(mp);


    StackPane mvPane = new StackPane();

    mvPane.getChildren().add(mediaView);
    mediaView.fitWidthProperty().bind(mvPane.widthProperty());
    mediaView.fitHeightProperty().bind(mvPane.heightProperty());
    mediaView.setPreserveRatio(true);

    setCenter(mvPane);

    mediaBar = new HBox();
    mediaBar.setAlignment(Pos.CENTER);
    mediaBar.setPadding(new Insets(5, 10, 5, 10));
    BorderPane.setAlignment(mediaBar, Pos.CENTER);

    final Button playButton = new Button(">");

    playButton.setOnAction(new EventHandler<ActionEvent>() {
        public void handle(ActionEvent e) {
            Status status = mp.getStatus();

            if (status == Status.UNKNOWN || status == Status.HALTED) {
                // don't do anything in these states
                return;
            }

            if (status == Status.PAUSED
                    || status == Status.READY
                    || status == Status.STOPPED) {
                // rewind the movie if we're sitting at the end
                if (atEndOfMedia) {
                    mp.seek(mp.getStartTime());
                    atEndOfMedia = false;
                }
                mp.play();
            } else {
                mp.pause();
            }
        }
    });
    mp.currentTimeProperty().addListener(new InvalidationListener() {
        public void invalidated(Observable ov) {
            updateValues();
        }
    });

    mp.setOnPlaying(new Runnable() {
        public void run() {
            if (stopRequested) {
                mp.pause();
                stopRequested = false;
            } else {
                playButton.setText("||");
            }
        }
    });

    mp.setOnPaused(new Runnable() {
        public void run() {
            System.out.println("onPaused");
            playButton.setText(">");
        }
    });

    mp.setOnReady(new Runnable() {
        public void run() {
            duration = mp.getMedia().getDuration();
            updateValues();
        }
    });

    mp.setCycleCount(repeat ? MediaPlayer.INDEFINITE : 1);
    mp.setOnEndOfMedia(new Runnable() {
        public void run() {
            if (!repeat) {
                playButton.setText(">");
                stopRequested = true;
                atEndOfMedia = true;
            }
        }
    });

    mediaBar.getChildren().add(playButton);
    // Add spacer
    Label spacer = new Label("   ");
    mediaBar.getChildren().add(spacer);

    // Add Time label
    Label timeLabel = new Label("Time: ");
    mediaBar.getChildren().add(timeLabel);

    // Add time slider
    timeSlider = new Slider();
    HBox.setHgrow(timeSlider, Priority.ALWAYS);
    timeSlider.setMinWidth(50);
    timeSlider.setMaxWidth(Double.MAX_VALUE);
    timeSlider.valueProperty().addListener(new InvalidationListener() {
        public void invalidated(Observable ov) {
            if (timeSlider.isValueChanging()) {
                // multiply duration by percentage calculated by slider position
                mp.seek(duration.multiply(timeSlider.getValue() / 100.0));
            }
        }
    });
    mediaBar.getChildren().add(timeSlider);

    // Add Play label
    playTime = new Label();
    playTime.setPrefWidth(130);
    playTime.setMinWidth(50);
    mediaBar.getChildren().add(playTime);

    // Add the volume label
    Label volumeLabel = new Label("Vol: ");
    mediaBar.getChildren().add(volumeLabel);

    // Add Volume slider
    volumeSlider = new Slider();
    volumeSlider.setPrefWidth(70);
    volumeSlider.setMaxWidth(Region.USE_PREF_SIZE);
    volumeSlider.setMinWidth(30);
    volumeSlider.valueProperty().addListener(new InvalidationListener() {
        public void invalidated(Observable ov) {
            if (volumeSlider.isValueChanging()) {
                mp.setVolume(volumeSlider.getValue() / 100.0);
            }
        }
    });
    mediaBar.getChildren().add(volumeSlider);

    setBottom(mediaBar);
}

protected void updateValues() {
    if (playTime != null && timeSlider != null && volumeSlider != null) {
        Platform.runLater(new Runnable() {
            public void run() {
                Duration currentTime = mp.getCurrentTime();
                playTime.setText(formatTime(currentTime, duration));
                timeSlider.setDisable(duration.isUnknown());
                if (!timeSlider.isDisabled()
                        && duration.greaterThan(Duration.ZERO)
                        && !timeSlider.isValueChanging()) {
                    timeSlider.setValue(currentTime.divide(duration).toMillis()
                            * 100.0);
                }
                if (!volumeSlider.isValueChanging()) {
                    volumeSlider.setValue((int) Math.round(mp.getVolume()
                            * 100));
                }
            }
        });
    }
}

private static String formatTime(Duration elapsed, Duration duration) {
    int intElapsed = (int) Math.floor(elapsed.toSeconds());
    int elapsedHours = intElapsed / (60 * 60);
    if (elapsedHours > 0) {
        intElapsed -= elapsedHours * 60 * 60;
    }
    int elapsedMinutes = intElapsed / 60;
    int elapsedSeconds = intElapsed - elapsedHours * 60 * 60
            - elapsedMinutes * 60;

    if (duration.greaterThan(Duration.ZERO)) {
        int intDuration = (int) Math.floor(duration.toSeconds());
        int durationHours = intDuration / (60 * 60);
        if (durationHours > 0) {
            intDuration -= durationHours * 60 * 60;
        }
        int durationMinutes = intDuration / 60;
        int durationSeconds = intDuration - durationHours * 60 * 60
                - durationMinutes * 60;
        if (durationHours > 0) {
            return String.format("%d:%02d:%02d/%d:%02d:%02d",
                    elapsedHours, elapsedMinutes, elapsedSeconds,
                    durationHours, durationMinutes, durationSeconds);
        } else {
            return String.format("%02d:%02d/%02d:%02d",
                    elapsedMinutes, elapsedSeconds, durationMinutes,
                    durationSeconds);
        }
    } else {
        if (elapsedHours > 0) {
            return String.format("%d:%02d:%02d", elapsedHours,
                    elapsedMinutes, elapsedSeconds);
        } else {
            return String.format("%02d:%02d", elapsedMinutes,
                    elapsedSeconds);
        }
    }
  }
}

Thanks.

Upvotes: 1

Views: 1162

Answers (1)

jewelsea
jewelsea

Reputation: 159526

Just create a new MediaView in a new Scene to be displayed in your second window. Reference the same MediaPlayer you created for use in your first scene and the controls supplied in the first scene will control both MediaViews simultaneously.

secondStage.setScene(new Scene(new StackPane(new MediaView(mediaPlayer))));

Upvotes: 1

Related Questions