1

There are no null fields in the Media block that launch the MediaPlayer

I have researched this extensively and have found nothing to explain this error.

I am building a Java Swing application for a class project and am attempting to use the JFX panel and media player to host and play several mp4 videos. I have successfully got it working one time through however when I come back to the window again I run into the error.

java.lang.NullPointerException at com.sun.media.jfxmediaimpl.platform.gstreamer.GSTMediaPlayer.playerSetBalance(Unknown Source) at com.sun.media.jfxmediaimpl.NativeMediaPlayer.setBalance(Unknown Source) at javafx.scene.media.MediaPlayer.init(Unknown Source) at javafx.scene.media.MediaPlayer.(Unknown Source) at project.screens.TutorialPlayerScreen.initMediaPlayer(TutorialPlayerScreen.java:156) at project.screens.TutorialPlayerScreen.init(TutorialPlayerScreen.java:122) at project.screens.TutorialPlayerScreen.(TutorialPlayerScreen.java:113) at project.buttons.PreKModuleSelectTutorialButtons$Button$7.doAction(PreKModuleSelectTutorialButtons.java:225) at project.screens.PreKModuleSelect.clicked(PreKModuleSelect.java:359) at project.tools.ContentPane.notifiyObserver(ContentPane.java:457) at project.tools.ContentPane$1.mousePressed(ContentPane.java:272) at java.awt.Component.processMouseEvent(Unknown Source) at javax.swing.JComponent.processMouseEvent(Unknown Source) at java.awt.Component.processEvent(Unknown Source) at java.awt.Container.processEvent(Unknown Source) at java.awt.Component.dispatchEventImpl(Unknown Source) at java.awt.Container.dispatchEventImpl(Unknown Source) at java.awt.Component.dispatchEvent(Unknown Source) at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source) at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source) at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source) at java.awt.Container.dispatchEventImpl(Unknown Source) at java.awt.Window.dispatchEventImpl(Unknown Source) at java.awt.Component.dispatchEvent(Unknown Source) at java.awt.EventQueue.dispatchEventImpl(Unknown Source) at java.awt.EventQueue.access$500(Unknown Source) at java.awt.EventQueue$3.run(Unknown Source) at java.awt.EventQueue$3.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source) at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source) at java.awt.EventQueue$4.run(Unknown Source) at java.awt.EventQueue$4.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source) at java.awt.EventQueue.dispatchEvent(Unknown Source) at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source) at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source) at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source) at java.awt.EventDispatchThread.pumpEvents(Unknown Source) at java.awt.EventDispatchThread.pumpEvents(Unknown Source) at java.awt.EventDispatchThread.run(Unknown Source)

Here is the code I used to start the JFX media:

' //Media Player Support private Group mediaRoot; private Scene mediaScene;

private Media tutorialVideo;
private MediaPlayer VideoPlayer;
private MediaView mediaViewer;
private JFXPanel fxPanel;

private int jfxPanelHeight = 394;//525;
private int jfxPanelWidth = 700;//700;

private void initMediaPlayer() throws IOException {
    this.fxPanel = new JFXPanel(); //initializes JFX variables
    fxPanel.setSize(this.jfxPanelWidth ,this.jfxPanelHeight);

    //Add JFX Panel component to the Main Window
    int padding = ((mainWindow.getPreferredSize().width - this.jfxPanelWidth) / 2);
    mainWindow.addLayer(fxPanel, MEDIA_LAYER, padding, 125);

    //Initialize FX Panel
    this.mediaRoot = new Group();
    this.mediaScene = new Scene(this.mediaRoot, 0, 0);

    //Open/prepare the file
    //String tutorialFilePath = new File("").getAbsolutePath() + DIRECTORY_PATH + "Tutorial.mp4";
    String tutorialFilePath = new File("").getAbsolutePath() + MEDIA_PATH + this.observer.getName() +"Tutorial.mp4";
    File mediaFile = new File(tutorialFilePath);
    this.tutorialVideo = new Media(mediaFile.toURI().toString());

    //Create the media player
    this.VideoPlayer = new MediaPlayer(this.tutorialVideo);  //Error here
    this.VideoPlayer.setAutoPlay(false);

    this.mediaViewer = new MediaView(this.VideoPlayer);
    this.mediaViewer.setFitHeight(this.jfxPanelHeight);
    this.mediaViewer.setFitWidth(this.jfxPanelWidth);
          ((Group)this.mediaScene.getRoot()).getChildren().add(this.mediaViewer);

    fxPanel.setScene(this.mediaScene);
}'

I attempt to clean up the used memory before leaving the screen.

public void tearDown(){

    //Stop the JFX Player and Remove
    this.mainWindow.removeLayer(this.fxPanel);
    this.VideoPlayer.stop();
    this.VideoPlayer.dispose();
    this.fxPanel.removeAll();
    this.mediaRoot.getChildren().removeAll();

    this.mediaRoot = null;
    this.mediaScene = null;
    this.mediaViewer = null;
    this.tutorialVideo  = null;
    this.VideoPlayer = null;
    this.fxPanel = null;
}

private JLayeredPane contentPane;    //The content pane of this JFrame.
public void removeLayer(JComponent component) {
    contentPane.remove(component);
    contentPane.revalidate();
    contentPane.repaint();
}

Any help or comment would be much appreciated! Thank you!

cjm
  • 13
  • 1
  • 4
  • Does `tutorialFilePath` actually point to a valid file? – MadProgrammer Apr 27 '15 at 00:00
  • @MadProgrammer Yes it does. This first time running this code works fine. When I go through the second launch I get an error. This line: this.VideoPlayer = new MediaPlayer(this.tutorialVideo); //Error here – cjm Apr 27 '15 at 00:04
  • From what you've shown us so far it's difficult to infer, but one of your fields declared is definitely `null` before you invoke `initMediaPlayer`. – Makoto Apr 27 '15 at 00:06
  • @Makoto I've checked the fields. It doesn't seem to have any nulls fields. But I'll check again. – cjm Apr 27 '15 at 00:09
  • Meta post: http://meta.stackoverflow.com/questions/291537/this-question-was-dupe-superpowered-and-i-dont-think-it-should-have-been – durron597 Apr 27 '15 at 00:17
  • 1
    If it works first and then throws this exception after re-navigating, something must have changed when you change views. Your `tearDown()` method seems like a high probability of being the cause: what happens when you don't set those values to `null`? Can you narrow it down that way? You're removing a lot of stuff in that method, maybe something isn't being rebuilt? – Jeroen Vannevel Apr 27 '15 at 00:35
  • 1
    This might not be the issue causing the `NullPointerException`, but you need to make the different method calls on different threads. In particular, `new JFXPanel()` must be called on the AWT event dispatch thread, whereas the rest of `initMediaPlayer` needs to be called on the FX Application Thread. See the [`JFXPanel` javadocs](http://docs.oracle.com/javase/8/javafx/api/javafx/embed/swing/JFXPanel.html). (And while this might not be the cause of the problem, it is conceivable that it is.) – James_D Apr 27 '15 at 00:37
  • @Jeroen I actually have tried the removing everything but the this.mainWindow.removeLayer(this.fxPanel); this.VideoPlayer.stop(); However, if I don't run the removeLayer the fxPanel remains in the middle of the application window. – cjm Apr 27 '15 at 11:44
  • @James_D Just to check on what is meant... Should the JFXPanel be owned by the AWT application window and my class should attach the media player to the existing JFXPanel? – cjm Apr 27 '15 at 14:43
  • @cjm I don't know what you mean by "owned by". `JFXPanel` is a subclass of `JComponent`, so you can only display it in a Swing/AWT application. I was referring to threads, though. The `Scene` that is displayed in the `JFXPanel` must be created and accessed from the FX Application Thread, whereas the `JFXPanel` itself must be created on the AWT event dispatch thread. (`jfxPanel.setScene(...)` may be called on either thread.) Again, see the [javadocs](http://docs.oracle.com/javase/8/javafx/api/javafx/embed/swing/JFXPanel.html) – James_D Apr 27 '15 at 16:32

2 Answers2

3

You have threading issues in your code, which need to be fixed. Specifically, you must create FX UI components and set up the scene graph on the FX Application thread. See the Javadocs for JFXPanel for details.

This is incidental to the Null Pointer Exception, however. What I think is causing that is that the FX toolkit is closing down when you remove the JFXPanel completely from the UI. In order to prevent this, just call

Platform.setImplicitExit(false);

when you first initialize your application. (This method is safe to call from any thread.)

Here's a similar-looking SSCCE:

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.FlowLayout;

import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


public class MediaPlayerInSwing {

    private static final String MEDIA_URL = "http://download.oracle.com/otndocs/products/javafx/oow2010-2.flv" ;

    private JFrame window ;

    private JFXPanel jfxPanel ;
    private Media media ;
    private MediaPlayer player ;
    private MediaView mediaView ;
    private BorderPane root ;
    private Scene scene ;

    private Button button ;

    // create on AWT Event Dispatch Thread
    public MediaPlayerInSwing() {
        Platform.setImplicitExit(false);
        initGUI();
    }

    private void initGUI()  {
        window = new JFrame();
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setLayout(new BorderLayout());

        JPanel controls = new JPanel();
        controls.setLayout(new FlowLayout());
        button = new Button("Show video");
        controls.add(button);
        button.addActionListener(e -> {
            if (jfxPanel == null) {
                showVideo();
            } else {
                tearDownVideo();
            }
        });

        window.add(controls, BorderLayout.SOUTH);
        window.setSize(600, 480);
        window.setLocationRelativeTo(null);
        window.setVisible(true);
    }

    private void showVideo() {


        jfxPanel = new JFXPanel();

        Platform.runLater(() -> {
            media = new Media(MEDIA_URL);
            player = new MediaPlayer(media);
            player.play(); 
            mediaView = new MediaView(player);
            root = new BorderPane(mediaView);
            scene = new Scene(root, 600, 400);
            jfxPanel.setScene(scene);
        });

        window.add(jfxPanel, BorderLayout.CENTER);
        button.setLabel("Hide video");
    }

    private void tearDownVideo() {

        window.remove(jfxPanel);

        Platform.runLater(() -> {
            player.stop();
            player.dispose();
            player = null ;
            mediaView = null ;
            root = null ;
            jfxPanel.setScene(null);
            scene = null ;
            SwingUtilities.invokeLater(() -> {
                jfxPanel = null ;
                // force window to repaint...
                window.getRootPane().repaint();
            });
        });
        button.setLabel("Show video");
    }

    public static void main(String[] args) throws Exception {
        SwingUtilities.invokeLater(MediaPlayerInSwing::new);
    }
}
James_D
  • 201,275
  • 16
  • 291
  • 322
0

This might not be the issue causing the NullPointerException, but you need to make the different method calls on different threads. In particular, new JFXPanel() must be called on the AWT event dispatch thread, whereas the rest of initMediaPlayer needs to be called on the FX Application Thread. See the JFXPanel javadocs. (And while this might not be the cause of the problem, it is conceivable that it is.) – James_D

This solved the problem.

I set the JFX Panel to be managed by the main window and never goes out of scope. To solve the issue of visibility only on the screens that I want it to I changed the functions to these...

public void tearDown(){
    this.mediaPanel.getMediaPanel().setVisible(false);
    this.VideoPlayer.stop();
}

and

private void initMediaPlayer() throws IOException {
    this.mediaPanel.getMediaPanel().setVisible(true);

    //Initialize FX Panel
    this.mediaRoot = new Group();
    this.mediaScene = new Scene(this.mediaRoot, 0, 0);

    //Open/prepare the file
    //String tutorialFilePath = new File("").getAbsolutePath() + DIRECTORY_PATH + "Tutorial.mp4";
    String tutorialFilePath = new File("").getAbsolutePath() + MEDIA_PATH + this.observer.getName() +"Tutorial.mp4";
    File mediaFile = new File(tutorialFilePath);
    this.tutorialVideo = new Media(mediaFile.toURI().toString());

    //Create the media player
    this.VideoPlayer = new MediaPlayer(this.tutorialVideo);  //Error here
    this.VideoPlayer.setAutoPlay(false);

    this.mediaViewer = new MediaView(this.VideoPlayer);
    this.mediaViewer.setFitHeight(this.mediaPanel.getPanelHeight());
    this.mediaViewer.setFitWidth(this.mediaPanel.getPanelWidth());
        ((Group)this.mediaScene.getRoot()).getChildren().add(this.mediaViewer);

    this.mediaPanel.getMediaPanel().setScene(this.mediaScene);
}

Thanks to everyone who helped!

cjm
  • 13
  • 1
  • 4
  • Actually, it's not quite what's happening... The threading is an issue, but the NPE is happening because the FX toolkit closes when you remove the JFXPanel completely. See answer. – James_D Apr 27 '15 at 17:37