Java 在 javafx.scene.media.MediaPlayer.init 使用媒体播放器时出现 FX 空指针异常

Java FX Null Pointer Exception when using Media Player at javafx.scene.media.MediaPlayer.init

启动 MediaPlayer 的媒体块中没有空字段

我对此进行了广泛的研究,但没有发现任何可以解释此错误的原因。

我正在为 class 项目构建 Java Swing 应用程序,并尝试使用 JFX 面板和媒体播放器来托管和播放多个 mp4 视频。我已经成功让它工作了一次,但是当我再次回到 window 时,我 运行 进入了错误。

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.doAction(PreKModuleSelectTutorialButtons.java:225) at project.screens.PreKModuleSelect.clicked(PreKModuleSelect.java:359) at project.tools.ContentPane.notifiyObserver(ContentPane.java:457) at project.tools.ContentPane.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.access0(Unknown Source) at java.awt.EventQueue.run(Unknown Source) at java.awt.EventQueue.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain.doIntersectionPrivilege(Unknown Source) at java.security.ProtectionDomain.doIntersectionPrivilege(Unknown Source) at java.awt.EventQueue.run(Unknown Source) at java.awt.EventQueue.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain.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)

这是我用来启动 JFX 媒体的代码:

' //媒体播放器支持 私有组 mediaRoot; 私人场景 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);
}'

我尝试在离开屏幕前清理已用内存。

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();
}

如有任何帮助或意见,我们将不胜感激!谢谢!

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

这解决了问题。

我将 JFX 面板设置为由主 window 管理并且永远不会超出范围。为了解决仅在我想要的屏幕上可见的问题,我将功能更改为这些...

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

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);
}

感谢所有帮助过的人!

您的代码中存在线程问题,需要修复。具体来说,您必须创建 FX UI 组件并在 FX Application 线程上设置场景图。有关详细信息,请参阅 Javadocs for JFXPanel

但是,这是空指针异常的附带情况。我认为造成这种情况的原因是,当您从 UI 中完全删除 JFXPanel 时,FX 工具包正在关闭。为了防止这种情况,只需调用

Platform.setImplicitExit(false);

当您第一次初始化您的应用程序时。 (从任何线程调用此方法都是安全的。)

这是一个类似的 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);
    }
}