在 JavaFx 应用程序的不同分辨率下闪烁白屏

Flashing White Screen In Different Resolution of JavaFx Application

我们正在使用 JavaFx 和 JavaCv 开发视频流应用程序。 我们有 8X8 的 GridPane,每个单元格包含窗格布局和每个窗格 包含 ImageView.

每当用户将 url 拖到特定位置时,我都会调用播放功能 细胞。 我们有 64 个 Urls,所以我们调用播放函数 64 次(所有 64 url 都在播放 在后台线程中)。

for(int i=0;i<8;i++){
        for(int j=0;j<8;j++){
            //grid08(8X8)
            Pane p=new Pane();
            ImageView im=new ImageView();
            Label l=new Label("");
            im.fitWidthProperty().bind(p.widthProperty());
            im.fitHeightProperty().bind(p.heightProperty());
            l.prefWidthProperty().bind(p.widthProperty());
            l.prefHeightProperty().bind(p.heightProperty());
            p.getChildren().addAll(im,l);
            l.setVisible(false);
            l.setStyle("-fx-text-fill: #fff; -fx-font-size: 12px");
            l.setAlignment(Pos.CENTER);
            p.setStyle("-fx-background-color: black;");
            grid.add(p,j,i);//grid->instance of gridPane
            p.setOnDragOver(new EventHandler<DragEvent>() {
            @Override
            public void handle(DragEvent event) {
            if(!l.isVisible()){
                event.acceptTransferModes(TransferMode.ANY);
                   }
                }
            });
            p.setOnDragDropped(new EventHandler<DragEvent>() {
                @Override
                public void handle(DragEvent event) {
                   play(im,event.getDragboard().getString());
                }
            });
      }
     }

 play(ImageView im,String Url){
   new Thread(new Runnable(){
         frameGrabber = new FFmpegFrameGrabber(Url);
         frameGrabber.setVideoOption("preset","ultrafast");
         frameGrabber.setOption("rtsp_transport","tcp");
         frameGrabber.setOption("stimeout" , "60000");
         frameGrabber.setAudioChannels(0);
         frameGrabber.setAudioCodec(0);
        try {
        frameGrabber.start();
        start=true;
        } catch (Exception e) {
        }
        JavaFXFrameConverter jconverter=new JavaFXFrameConverter();
        while(true){
        Frame frame = frameGrabber.grabImage();
        Image image=jconverter.convert(frame);
        Platform.runlater(new Runnable(){
         im.setImage(image);
         });
        }
      }).start();  
 }

问题: 在增加一些阈值宽度和 高度。 在这里,我附上了不同屏幕尺寸的应用程序快照;

屏幕1

因为我们长期以来一直面临这个问题。 任何潜在客户都将受到高度赞赏。 提前致谢。

这里只是一个猜测,但是当图像视图与图像的大小不同时,图像视图在调用 setImage(...) 时有很多工作要做(它必须调整图像的大小) .因为你有很多这样的事情发生,可能是每一个脉冲,你可能向 Platform.runLater(...) 提交了比它能处理的更多的可运行对象(即你在从同一个线程提交的前一个可以处理之前提交了另一个 Platform.runLater(...)完全的)。这会导致渲染问题,因为渲染系统跟不上。

我无法对此进行测试,所以我不确定它是否会解决问题,但最好只在前一个调用完成后才向 Platform.runLater(...) 提交新调用(也就是说,即使这不是问题的原因,你也应该这样做)。

您可以使用

void play(ImageView im,String Url){
    new Thread(() -> {
        frameGrabber = new FFmpegFrameGrabber(Url);
        frameGrabber.setVideoOption("preset","ultrafast");
        frameGrabber.setOption("rtsp_transport","tcp");
        frameGrabber.setOption("stimeout" , "60000");
        frameGrabber.setAudioChannels(0);
        frameGrabber.setAudioCodec(0);
        try {
            frameGrabber.start();
            start=true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        JavaFXFrameConverter jconverter=new JavaFXFrameConverter();

        AtomicReference<Image> imageHolder = new AtomicReference<>(null);
        while(true){
            Frame frame = frameGrabber.grabImage();
            Image image=jconverter.convert(frame);
            if (imageHolder.getAndSet(image) == null) {
                Platform.runLater(() -> im.setImage(imageHolder.getAndSet(null)));
            }
        }
    }).start();  
}

请注意,在此代码(和原始代码)中,所有 图像都在同一线程(FX 应用程序线程)上调整大小。如果可能的话,在自己的线程中调整每个图像的大小可能会更有效。你不能绝对保证这一点(因为没有办法保证图像视图在后台线程中调整大小的图像和显示图像的图像视图之间不会改变大小),所以你仍然需要绑定 fitWidthfitHeight 属性,但这应该减少在 UI 线程上发生的图像大小调整量,因此应该提高性能。这依赖于您的图像采集卡 API(我不熟悉)中的某种机制来调整图像大小。

for(int i=0;i<8;i++){
    for(int j=0;j<8;j++){
        //grid08(8X8)
        Pane p=new Pane();
        AtomicReference<Bounds> bounds = new AtomicReference<>(p.getBoundsInLocal());
        p.boundsInLocalProperty().addListener((obs, oldBounds, newBounds) -> bounds.set(newBounds));
        ImageView im=new ImageView();
        Label l=new Label("");
        im.fitWidthProperty().bind(p.widthProperty());
        im.fitHeightProperty().bind(p.heightProperty());
        l.prefWidthProperty().bind(p.widthProperty());
        l.prefHeightProperty().bind(p.heightProperty());
        p.getChildren().addAll(im,l);
        l.setVisible(false);
        l.setStyle("-fx-text-fill: #fff; -fx-font-size: 12px");
        l.setAlignment(Pos.CENTER);
        p.setStyle("-fx-background-color: black;");
        grid.add(p,j,i);//grid->instance of gridPane
        p.setOnDragOver(new EventHandler<DragEvent>() {
            @Override
            public void handle(DragEvent event) {
                if(!l.isVisible()){
                    event.acceptTransferModes(TransferMode.ANY);
                }
            }
        });
        p.setOnDragDropped(new EventHandler<DragEvent>() {
            @Override
            public void handle(DragEvent event) {
               play(im,bounds,event.getDragboard().getString());
            }
        });
     }
 }

void play(ImageView im, AtomicReference<Bounds> bounds, String Url){
    new Thread(() -> {
        frameGrabber = new FFmpegFrameGrabber(Url);
        frameGrabber.setVideoOption("preset","ultrafast");
        frameGrabber.setOption("rtsp_transport","tcp");
        frameGrabber.setOption("stimeout" , "60000");
        frameGrabber.setAudioChannels(0);
        frameGrabber.setAudioCodec(0);
        try {
            frameGrabber.start();
            start=true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        JavaFXFrameConverter jconverter=new JavaFXFrameConverter();

        AtomicReference<Image> imageHolder = new AtomicReference<>(null);
        while(true){
            Frame frame = frameGrabber.grabImage();

            Bounds imgBounds = bounds.get();
            double width = imgBounds.getWidth();
            double height = imgBounds.getHeight();

            // resize frame to width, height....

            Image image=jconverter.convert(frame);

            if (imageHolder.getAndSet(image) == null) {
                Platform.runLater(() -> im.setImage(imageHolder.getAndSet(null)));
            }
        }
    }).start();  
}