So I've been running into an issue lately. Whenever I want to play a videofile with JavaFX MediaPlayer combined with MediaView the video seems to stutter all over the place. I tried different types of videofiles (I rendered most of them myself as .mp4 with h264 encoding) where framerates range from 30fps to 60fps, the bitrate being 14.000.000 to 28.000.000 bps.
The lag also occurs with most files that I haven't rendered myself, so I'm quite sure that there is nothing wrong with my renderings.
edit
Further testing confirmed that it does not matter that I rendered the videofiles myself.
Do you guys know why this videostutter might be occuring and how to fix it? I'm open to any type of fixes, including using different ways of displaying the videofiles other than through JavaFX. Here is the main code of the videoplayer:
public class VideoPlayer extends JPanel implements Checkable
{
private MediaView view;
public JFXPanel pane;
private String path;
public MediaPlayer player;
private Dimension size;
public VideoPlayer(String path, Dimension size)
{
this.size = size;
this.path = path;
pane = new JFXPanel();
player = new MediaPlayer(new Media(path));
Platform.runLater(new Runnable() {
#Override
public void run() {
initFX(pane);
}
});
pane.setPreferredSize(size);
pane.setSize(size);
pane.setLocation(0,0);
add(pane);
}
public void initFX(JFXPanel fxPanel) {
Scene scene = createScene();
pane.setScene(scene);
}
private Scene createScene()
{
view = new MediaView();
view.setMediaPlayer(player);
Group root = new Group();
view.setPreserveRatio(true);
view.setFitWidth(size.width);
view.setSmooth(false);
root.getChildren().add(view);
Scene scene = null;
try{
scene = new Scene(root, size.width, size.height)
;}catch(Exception e){System.out.println(e);}
return scene;
}
public void play()
{
player.play();
}
}
Here, the videoplayer receives a String with the path to the videofile, the Dimension size is the size the player should be.
The class itself extends JPanel so that it can be added to a JFrame. This JPanel contains a JFXPanel which in its turn contains the MediaView.
If you guys can figgure out why the video is getting stutter it would be amazing. I'm open to all solutions!
Thanks for the help in advance!
Edits
As suggested by #AlmasB I made a bare-bone pure JavaFX application to play the exact same files. It might have reduced stutter by a tiny amount, but perhaps not at all. It was very hard to see wether it was better or not. Here is the code of the bare-bones JavaFX videoplayer:
public class VideoPlayerTest extends Application {
static MediaView view;
static MediaPlayer player;
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
StackPane root = new StackPane();
//root.getChildren().add(btn);
makeMediaView();
root.getChildren().add(view);
player.play();
Scene scene = new Scene(root, 1920, 1080);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
private static void makeMediaView(){
player = new MediaPlayer(new Media("file:///C:/Users/wesse/Documents/CowLite%20GabenQuest/resources/cutscenes/gabenquestepisode1intro.mp4.old"));
view = new MediaView(player);
view.setPreserveRatio(true);
view.setFitWidth(1920);
}
public static void main(String[] args) {
launch(args);
}
}
As suggested by #AlmasB some info:
System info
Windows 10
Latest JDK version (1.8)
gtx960, intel i5 3470, 8gb RAM
video tests
1080p 60fps 28mbit/s stutter
1080p 30fps 14mbit/s stutter
720p 29fps 1.8mbit/s no stutter, but poor video quality due to low bitrate/resolution, downloaded from YT
1080p 60fps 5.3mbit/s very slight stutter, decent video quality, downloaded from YT
1080p 60fps 6.3mbit/s medium stutter, perfect quality, self rendered
I'm rendering some videos myself now with lower bitrate to see if this is the problem.
Related
I have a code written using javafx and java 11. As I showed in the below code snippet, when I move my ImageView to right of the scene, The ImageView disappears.
At first:
and then after moving it using arrow keys with the help of scene event listener:
then:
and finally:
I don't use fxml. Here is my demo which has the problem too.
public class HelloApplication extends Application {
public String fetchResource(String path) {
return Objects.requireNonNull(getClass().getResource(path)).toString();
}
#Override
public void start(Stage stage) throws IOException {
Rectangle r = new Rectangle(100, 100);
ImageView spaceShip = new ImageView(fetchResource("spaceShip.png"));
spaceShip.setFitHeight(100);
spaceShip.setFitWidth(100);
spaceShip.setX(0);
spaceShip.setY(0);
EventHandler<KeyEvent> keyListener = event -> {
if (event.getCode() == KeyCode.RIGHT) {
spaceShip.setX(spaceShip.getX() + 20);
}
};
Group game = new Group(spaceShip);
Scene scene = new Scene(game, 1840, 1080);
scene.setOnKeyPressed(keyListener);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
I should mention that I create a rectangle and check the scenario with that, And there was not any problem. I think there is some problem with ImageView.
I couldn't reproduce the issue with your code, but I could with SceneBuilder. You may get the desired result if you wrap the Group in a Pane (at least it works in SceneBuilder).
Group game = new Group(spaceShip);
Pane pane = new Pane(game);
Scene scene = new Scene(pane, 1840, 1080);
stage.setScene(scene);
stage.show();
I'm experiencing poor performance of JavaFX when the size of a Stage increases, even if only a small portion of the area changes.
The following example updates only a little square of 25x25, changing its background.
public class WindowSizeTest extends Application {
private int speed = 1500000000;
#Override
public void start(Stage primaryStage) {
StackPane content = new StackPane();
content.setPrefSize(25, 25);
content.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
new AnimationTimer() {
#Override
public void handle(long now) {
double val = ((double) now % speed) / speed;
BackgroundFill backgroundFill = new BackgroundFill(Color.color(val, val, val), CornerRadii.EMPTY, Insets.EMPTY);
Background background = new Background(backgroundFill);
content.setBackground(background);
}
}.start();
StackPane root = new StackPane(content);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Everything goes smoothly when the window is small:
..but..
if maximized on my 4k external monitor, it uses a lot my GPU and the performance of the entire system goes down:
It seems that JavaFX redraws the entire window content and not only the modified part (same behaviour on Windows).
Is this the expected behaviour? Can someone tell me why this is happening?
I'm trying to put an image as a background in a JavaFX scene, but my code isn't working.
Im trying to make a Battleship-game program in java eclipse but i'm stuck at a graphics problem.
public class WindowGUI extends Application{
Game game;
Image image;
public WindowGUI(Game game) {
this.game = game;
}
public static void main(String[] args) {
Game game = new Game();
new WindowGUI(game);
}
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("Battleship");
image = new Image ("C:\\Users\\amali\\git\\inf101.v19.sem2\\inf101.v19.sem2\\src\\window\\battleshipbackground.jpg");
ImageView background = new ImageView(image);
Button startButton = new Button("START");
BorderPane newStack = new BorderPane();
newStack.getChildren().add(startButton);
newStack.getChildren().add(background);
stage.setScene(new Scene(newStack, 1300, 860));
stage.show();
startButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// START THE GAME
}
});
}
}
When I first tried to run it, it worked and a new window opened with a button in the center, but the bakcground was blank. When i try setting an image as background in the window, behing the 'start'-button, nothing happens..
A better way to do it is to use the Background class rather than trying to add an ImageView as a child of your BorderPane.
Image image = new Image("C:\\Users\\amali\\git\\inf101.v19.sem2\\inf101.v19.sem2\\src\\window\\battleshipbackground.jpg");
BackgroundSize size = new BackgroundSize(BackgroundSize.AUTO,
BackgroundSize.AUTO,
false,
false,
true,
false);
Background background = new Background(new BackgroundImage(image,
BackgroundRepeat.NO_REPEAT,
BackgroundRepeat.NO_REPEAT,
BackgroundPosition.CENTER,
size));
newStack.setBackground(background);
Use BackgroundImage class.
or try this
JavaFX How to set scene background image
I'm currently working on an app that will play a video using JavaFX. I want to place the volume slider on the right side of the border pane. Currently, the app will resize to the size of the window, which will overlap with my volume slider.
I have a controller that takes care of all the actions performed by the app and implements the Initializable interface. Here's how my initialize() method looks like.
#Override
public void initialize(URL locate, ResourceBundle resources){
String path = new File("src/media/dance.mp4").getAbsolutePath();
media = new Media (new File (path).toURI().toString());
player = new MediaPlayer(media);
mediaView.setMediaPlayer(player);
DoubleProperty width = mediaView.fitWidthProperty();
DoubleProperty height = mediaView.fitHeightProperty();
width.bind(Bindings.selectDouble(mediaView.sceneProperty(), "width"));
height.bind(Bindings.selectDouble(mediaView.sceneProperty(), "height"));
volumeSlider.setValue(player.getVolume() * 100);
volumeSlider.valueProperty().addListener(new InvalidationListener() {
#Override
public void invalidated(Observable observable) {
player.setVolume(volumeSlider.getValue() / 100);
}
});
}
I tried to subtract 64px directly after mediaView.fitWidthProperty() and mediaView.sceneProperty(), but both gave me an error because they are not of type double. I tried mediaView.fitWidthProperty().subtract(64), but it said I would need to change the data type from DoubleProperty to DoubleBind, which would not be compatible with Bindings.selectDouble() method that requires a DoubleProperty.
How can I get this to work?
EDITED
There is an easier way! Just bind to the property directly:
mediaView.fitWidthProperty().bind(
mediaView.getScene().widthProperty().subtract(64));
Here is a full app to demonstrate:
public class FXWidthBind extends Application {
#Override
public void start(Stage stage) {
Media media = new Media("http://i.imgur.com/OJTwZuc.mp4");
MediaPlayer player = new MediaPlayer(media);
MediaView mediaView = new MediaView(player);
Pane root = new Pane();
Scene scene = new Scene(root);
root.getChildren().add(mediaView);
stage.setWidth(1000);
stage.setHeight(800);
stage.setScene(scene);
stage.show();
mediaView.fitWidthProperty().bind(mediaView.getScene().widthProperty().subtract(64));
mediaView.fitHeightProperty().bind(mediaView.getScene().heightProperty());
}
public static void main(String[] args) {
launch(args);
}
}
The solution is to subtract the width property of your SceneProperty (video screen).
width.bind(Bindings.selectDouble(mediaView.sceneProperty(), "width").subtract(64.0));
Here, I'm binding the width of my MediaView with the width of my SceneProperty minus 64 pixels. Since DoubleProperty is a wrapper around the primitive type double, you can use the subtract() method to modify the actual value underneath.
Before
After
I recently found javafx 2.1 very useful for my project of making a video player but after a
success I faced a problem with the video size Dimensions. In other words, when I run the
program and video is playing normally I can't see the whole video because it's dimensions
are bigger than my screen resolution .What Can I do in the following code to resize the actual size of video in windows7 64bit:
public class HelloFx extends Application {
public static void main(String[] args){
launch(args);
}
#Override
public void start(final Stage stage) throws Exception {
stage.setTitle("Movie Player");
final BorderPane root = new BorderPane();
final Media media = new Media("file:///Users//user//Videos//Sintel.mp4");
final MediaPlayer player = new MediaPlayer(media);
final MediaView view = new MediaView(player);
// System.out.println("media.width: "+media.getWidth());
root.getChildren().add(view);
final Scene scene = new Scene(root, 400, 400, Color.BLACK);
stage.setScene(scene);
stage.show();
player.play();
player.setOnReady(new Runnable() {
#Override
public void run() {
int w = player.getMedia().getWidth();
int h = player.getMedia().getHeight();
stage.setMinWidth(w);
stage.setMinHeight(h);
}
});
//player.play();
}
}
The JavaFX 2 MediaView class has 2 functions which can help. They are .setFitHeight() and .setFitWidth() .
So, you could, instead of letting the media dictate the size of screen, let your stage set the size of the screen...
public void run() {
int w = stage.getWidth(); // player.getMedia().getWidth();
int h = stage.getHeight(); // player.getMedia().getHeight();
// stage.setMinWidth(w);
// stage.setMinHeight(h);
// make the video conform to the size of the stage now...
player.setFitWidth(w);
player.setFitHeight(h);
}
Then the video should fit inside of the stage. That above code is pretty crude, and you may want to "Scale" the video better, ie: find the ratio of the media width VS the stage width & media height VS stage height ... But that code above should get you started.