-1

I created a app use javafx+vlcj,play mutiple local videos in a GridPane,16 videos are playing one time (i know it's need much CPU resource), vlcj-4.7.1 && vlcj-javafx-1.0.2 && org.openjfx-14.0.2.1, the demo code like this:

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.List;
import uk.co.caprica.vlcj.factory.MediaPlayerFactory;
import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer;

import static uk.co.caprica.vlcj.javafx.videosurface.ImageViewVideoSurfaceFactory.videoSurfaceForImageView;

public class VideoTest extends Application {
    MediaPlayerFactory mediaPlayerFactory;
    List<ImageView> imageViewList;
    GridPane gridPane;
    List<EmbeddedMediaPlayer> embeddedMediaPlayerList = new ArrayList<>();

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

    @Override
    public void start(Stage primaryStage) {
        mediaPlayerFactory = new MediaPlayerFactory();
        for (int i = 0; i < 16; i++) {
            embeddedMediaPlayerList.add(mediaPlayerFactory.mediaPlayers().newEmbeddedMediaPlayer());
        }

        AnchorPane anchorPane = new AnchorPane();
        Button button = new Button("play");
        button.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                int mVideoWidth = 187;
                int mVideoHeight = 331;
                for (int j = 0; j < 16; j++) {
                    ImageView videoImageView = imageViewList.get(j);
                    videoImageView.setPreserveRatio(true);
                    embeddedMediaPlayerList.get(j).videoSurface().set(videoSurfaceForImageView(videoImageView));
                    videoImageView.setFitWidth(mVideoWidth);
                    videoImageView.setFitHeight(mVideoHeight);
                    GridPane.setHgrow(videoImageView, Priority.ALWAYS);
                    gridPane.getChildren().add(videoImageView);
                    embeddedMediaPlayerList.get(j).media().play("xxx.avi");
                }
            }
        });
        gridPane = new GridPane();
        ColumnConstraints fillColumn = new ColumnConstraints();
        fillColumn.setHgrow(Priority.ALWAYS);
        fillColumn.setFillWidth(true);
        RowConstraints fillRow = new RowConstraints();
        fillRow.setVgrow(Priority.ALWAYS);
        fillRow.setFillHeight(true);
        int rows = 2;
        int cols = 8;

        for (int row = 0; row < rows; row++) {
            gridPane.getRowConstraints().add(fillRow);
        }

        for (int col = 0; col < cols; col++) {
            gridPane.getColumnConstraints().add(fillColumn);
        }

        int i = 0;
        imageViewList = new ArrayList<>();
        for (int j = 0; j < 16; j++) {
            imageViewList.add(new ImageView());
        }
        for (int row = 0; row < rows; row++) {
            for (int col = 0; col < cols; col++) {
                GridPane.setConstraints(imageViewList.get(i++), col, row);
            }
        }

        AnchorPane.setTopAnchor(gridPane, 100.0);
        anchorPane.getChildren().add(gridPane);
        anchorPane.getChildren().add(button);
        Scene scene = new Scene(anchorPane, 1080, 800);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

but sometimes(may be 1 in 3 times) get errors like this often.

Exception in thread "JavaFX Application Thread" java.lang.IndexOutOfBoundsException: Index -1 out of bounds for length 6
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
at java.base/java.util.Objects.checkIndex(Objects.java:372)
at java.base/java.util.ArrayList.get(ArrayList.java:459)
at javafx.scene.Parent.updateCachedBounds(Parent.java:1704)
at javafx.scene.Parent.recomputeBounds(Parent.java:1648)
at javafx.scene.Parent.doComputeGeomBounds(Parent.java:1501)
at javafx.scene.Parent$1.doComputeGeomBounds(Parent.java:115)
at com.sun.javafx.scene.ParentHelper.computeGeomBoundsImpl(ParentHelper.java:84)
at com.sun.javafx.scene.layout.RegionHelper.superComputeGeomBoundsImpl(RegionHelper.java:78)
at com.sun.javafx.scene.layout.RegionHelper.superComputeGeomBounds(RegionHelper.java:62)
at javafx.scene.layout.Region.doComputeGeomBounds(Region.java:3289)
at javafx.scene.layout.Region$1.doComputeGeomBounds(Region.java:168)
at com.sun.javafx.scene.layout.RegionHelper.computeGeomBoundsImpl(RegionHelper.java:89)
at com.sun.javafx.scene.NodeHelper.computeGeomBounds(NodeHelper.java:115)
at javafx.scene.Node.updateGeomBounds(Node.java:3843)
at javafx.scene.Node.getGeomBounds(Node.java:3805)
at javafx.scene.Node.getLocalBounds(Node.java:3753)
at javafx.scene.Node.updateTxBounds(Node.java:3907)
at javafx.scene.Node.getTransformedBounds(Node.java:3699)
at javafx.scene.Node.updateBounds(Node.java:762)
at javafx.scene.Parent.updateBounds(Parent.java:1835)
at javafx.scene.Parent.updateBounds(Parent.java:1833)
at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2525)
at com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:412)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:411)
at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:438)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:563)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:543)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:536)
at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:342)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
at java.base/java.lang.Thread.run(Thread.java:834)

i don't know why? pls help

jewelsea
  • 150,031
  • 14
  • 366
  • 406
pkxutao
  • 9
  • 1
  • 2
    This kind of error is usually caused by a threading error. Make sure you are doing anything that needs to update the UI on the JavaFX Application Thread. – James_D Oct 27 '21 at 03:33
  • JavaFX has a media player. If you use that, you would not have to worry about multi-threading. Why use vlcj instead of JavaFX media? – jewelsea Oct 27 '21 at 03:43
  • @James_D i just play videos use vlc ,nothing else i did – pkxutao Oct 27 '21 at 04:02
  • @jewelsea because vlc has hardware acceleration,i had to play mutiple videos – pkxutao Oct 27 '21 at 04:03
  • I believe that the JavaFX media player has hardware accelerated and would have no issues playing multiple videos. – jewelsea Oct 27 '21 at 04:29
  • 1
    Your error is still likely a threading error even if you don’t know how you caused it. See [related](https://stackoverflow.com/questions/64942488/exception-on-javafx-when-moving-labels-around-their-container-indexoutofboundse) and [related](https://stackoverflow.com/questions/56090082/how-to-fix-indexoutofbounds-exception-when-javafx-recomputes-parent-node-bounds) and [related](https://stackoverflow.com/questions/59796573/how-do-i-find-out-whats-causing-this-java-fx-application-thread-exception) all with similar exceptions and all caused by errors dealing with multiple threads. – jewelsea Oct 27 '21 at 04:41
  • i tried JavaFx media,but found that JavaFx media can't play a .avi video although the .avi video encoded by H.264 @jewelsea – pkxutao Oct 27 '21 at 06:18
  • 1
    To get further assistance on your issues integrating vlcj, you should edit the question to include a [mcve] with execution instructions and a detailed description of your build and runtime environments including exact versions of all components so that somebody could replicate your issue. With the currently supplied information I do not believe any further assistance is possible. – jewelsea Oct 27 '21 at 06:37
  • JavaFX Media can playback H.264, but it must be encoded as either a HLS stream or in an MP4 container, not AVI, as it does not support the AVI format. If the videos you wish to playback must be encoded in an AVI container then you are correct, JavaFX media will be unable to play it back and would not be the correct choice for your app. See the summary of [supported media formats](https://openjfx.io/javadoc/17/javafx.media/javafx/scene/media/package-summary.html). – jewelsea Oct 27 '21 at 06:38
  • Please provide enough code so others can better understand or reproduce the problem. – Community Oct 27 '21 at 09:21
  • 1
    @jewelsea i add detailed demo codes, thanks very much – pkxutao Oct 28 '21 at 02:42
  • @pkxutao, your supplied code doesn't compile. I updated it to add the missing imports, but, even after that and adding a dependency on uk.co.caprica artifact vlcj version 4.7.1 and adding `requires uk.co.caprica.vlcj;` in my module-info file, it still incomplete. There is no definition of `videoSurfaceForImageView`. So I can't replicate your issue and cannot assist you. – jewelsea Oct 29 '21 at 02:40
  • @pkxutao even if I could get it to compile I don't know what media to test it with. I don't have access to the `"xxx.avi"` file listed in the source. – jewelsea Oct 29 '21 at 02:43
  • 1
    @jewelsea `videoSurfaceForImageView` from `vlcj-javafx-1.0.2`,and you can replace any videos with the "xxx.avi",thanks – pkxutao Oct 29 '21 at 02:56
  • @pkutao I was able to get it to compile by seeing I had missed that you also have a dependency on vlcj-javafx-1.0.2, so once I added that dependency and the import statements and requires uk.co.caprica.vlcj.javafx; to the module-info it could compile. It would not run on my system though, the native library does not load `java.lang.UnsatisfiedLinkError: Unable to load library 'vlc'`. I don't have time to troubleshoot that at the moment. – jewelsea Oct 29 '21 at 02:57
  • I looked at the [vlcj javafx guide](https://github.com/caprica/vlcj-javafx) and also your source code. I don't see anything obvious you are doing. As you now have code which can consistently reproduce the issue, I suggest you [create a new issue for the vlcj javafx project](https://github.com/caprica/vlcj-javafx/issues) to the vlcj developers to assist you in solving it. You could link back to this question in when you create the issue. – jewelsea Oct 29 '21 at 03:00
  • 1
    yeah,i created the issue [link](https://github.com/caprica/vlcj-javafx-demo/issues/44),the vlcj developer is very kind but seems he also has no idea for this – pkxutao Oct 29 '21 at 03:12
  • Interesting thread on the link, thanks for that. So the vlcj developer tried it on Linux, but couldn't reproduce your issue, but your issue occurs on Windows. So that is a shame and may be a platform specific error. As a work-around, you could try doing all of the work in your application *after* you have shown the stage, to give the JavaFX system some time to fully start up. Also don't create and start the videos all at the same time, put a delay between creating and starting each one. Use a JavaFX Timeline to create the staggered starts (shouldn't be necessary, but perhaps might help). – jewelsea Oct 29 '21 at 09:19

1 Answers1

0

I was a finally (after some effort) able to reproduce this issue.

OS: Windows 10

Device: MS SurfaceBook 2

Java version:

>"C:\Program Files\Java\jdk-17\bin\java.exe" -version
java version "17" 2021-09-14 LTS
Java(TM) SE Runtime Environment (build 17+35-LTS-2724)
Java HotSpot(TM) 64-Bit Server VM (build 17+35-LTS-2724, mixed mode, sharing)

vlcj 4.7.1

vlcj-javafx 1.0.2

JavaFX version 17.0.0.1

Issue was unrelated to the mixed JavaFX versions described below and occurred after that was fixed and all JavaFX components were at version 17.0.0.1.

VLC downloaded from this link and installed (was necessary to ensure that required native dll libraries for vlcj could be found):

Error and behavior was as described in the question.

Error occurs when Play button is hit to start playing videos.

Sample video file

(Low Quality) Sample Workaround

I re-worked the sample code such that it no longer throws a Java exception.

However, it does come at a cost. On my SurfaceBook the performance wasn't really good enough to handle playing 16 videos in this manner at the same time using the vlcj solution. What I needed to do was do a lot of the initialization work up front, then space out starting playing each video by a full two seconds.

When I did so, it was at least able to play the videos semi-reliably, but it wasn't very smooth playback and the CPU was continually maxed out at 100%.

I believe that the original error which appeared to a threading error might just occur because of the general slowness and overload introduced by trying to play so many videos simultaneously, the system can't keep up and ends up getting itself so confused that it breaks. When many videos are played at once, errors are printed, not just in the Java system, but also by the native VLC components, so there is a general failure across the board.

Sample errors from (what I believe) is probably native vlc code:

[000001e5306a6c20] mmdevice audio output error: cannot initialize COM (error 0x80010106)
[000001e5306a6c20] mmdevice audio output error: cannot initialize COM (error 0x80010106)
[000001e53cf7d980] main vout display error: Failed to set on top
[000001e53cf7d1a0] main vout display error: Failed to set on top

I do not know enough about the vlcj solution to know why it is slow for this task. It is obviously using some native code, but I do not know if it is taking advantage of hardware acceleration or not.

So, I do not recommend this solution for this problem of playing multiple simultaneous videos.

I do not have a recommendation for an alternate solution.

For what it is worth, here is the re-worked code:

import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.value.*;
import javafx.collections.ObservableList;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.List;

import javafx.util.Duration;
import uk.co.caprica.vlcj.factory.MediaPlayerFactory;
import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer;

import static uk.co.caprica.vlcj.javafx.videosurface.ImageViewVideoSurfaceFactory.videoSurfaceForImageView;

public class VideoTest extends Application {

    private static final int VIDEO_WIDTH = 187;
    private static final int VIDEO_HEIGHT = 331;

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

    @Override
    public void start(Stage stage) {
        VBox layout = new VBox(100);
        layout.setPrefSize(3000, 800);
        Scene scene = new Scene(layout, 1080, 800, Color.AZURE);
        stage.setScene(scene);
        stage.show();

        MediaPlayerFactory mediaPlayerFactory;
        List<EmbeddedMediaPlayer> embeddedMediaPlayerList = new ArrayList<>();

        mediaPlayerFactory = new MediaPlayerFactory();

        List<ImageView> imageViewList = new ArrayList<>();
        for (int j = 0; j < 16; j++) {
            ImageView imageView = new ImageView();

            imageView.setPreserveRatio(true);
            imageView.setFitWidth(VIDEO_WIDTH);
 //           imageView.setFitHeight(VIDEO_HEIGHT);

            EmbeddedMediaPlayer player =
                    mediaPlayerFactory
                            .mediaPlayers()
                            .newEmbeddedMediaPlayer();

            embeddedMediaPlayerList.add(player);

            player.videoSurface().set(
                    videoSurfaceForImageView(imageView)
            );

            imageViewList.add(imageView);

            final int jcopy = j;
            imageView.boundsInLocalProperty().addListener(new ChangeListener<Bounds>() {
                @Override
                public void changed(ObservableValue<? extends Bounds> observable, Bounds oldValue, Bounds newValue) {
                    System.out.println("img " + jcopy + " bounds" + newValue);
                }
            });
        }

        int rows = 2;
        int cols = 8;
        int i = 0;

        GridPane gridPane = new GridPane();
        for (int row = 0; row < rows; row++) {
            for (int col = 0; col < cols; col++) {
                gridPane.add(imageViewList.get(i++), col, row);
            }
        }

        Button button = new Button("play");
        button.setOnAction(event -> {
            button.setDisable(true);

            Timeline timeline = new Timeline();
            ObservableList<KeyFrame> keyframes = timeline.getKeyFrames();

            for (int j = 0; j < 16; j++) {
                final int jcopy = j;
                keyframes.add(
                        new KeyFrame(Duration.millis(2_000 * j),
                                frameEvent ->
                                        embeddedMediaPlayerList
                                                .get(jcopy)
                                                .media()
                                                .play("file_example_AVI_1920_2_3MG.avi")
                        )
                );
            }

            timeline.play();
            timeline.setOnFinished(e -> button.setDisable(false));
        });

        layout.getChildren().addAll(button, gridPane);
    }
}

Potential mixed JavaFX dependency versions issue

This may not be an issue for you, but was for me.

It caused other issues with other JavaFX applications in the same project.

I needed to add the exclusion section to the vlcj-javafx dependency to get it to exclude the javafx-graphics version 14. After that, the JavaFX system started working again.

Without the exclusion I would get this error:

Which is different from yours but I was running some other code and the JavaFX versions were all mixed up so the behavior would be unpredictable for any project.

<dependencies>
    <dependency>
        <groupId>uk.co.caprica</groupId>
        <artifactId>vlcj</artifactId>
        <version>4.7.1</version>
    </dependency>
    <dependency>
        <groupId>uk.co.caprica</groupId>
        <artifactId>vlcj-javafx</artifactId>
        <version>1.0.2</version>
        <exclusions>
            <exclusion>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-graphics</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-controls</artifactId>
        <version>17.0.0.1</version>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-fxml</artifactId>
        <version>17.0.0.1</version>
    </dependency>
</dependencies>

I found this using mvn dependency:tree. See:

By adding this plugin configuration to my maven config:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>3.2.0</version>
</plugin>

Tree output with exclusion:

[INFO] --- maven-dependency-plugin:3.2.0:tree (default-cli) @ fxdemo ---
[INFO] org.jewelsea:fxdemo:jar:1.0-SNAPSHOT
[INFO] +- uk.co.caprica:vlcj:jar:4.7.1:compile
[INFO] |  \- uk.co.caprica:vlcj-natives:jar:4.1.0:compile
[INFO] |     +- net.java.dev.jna:jna:jar:5.2.0:compile
[INFO] |     \- net.java.dev.jna:jna-platform:jar:5.2.0:compile
[INFO] +- uk.co.caprica:vlcj-javafx:jar:1.0.2:compile
[INFO] +- org.openjfx:javafx-controls:jar:17.0.0.1:compile
[INFO] |  +- org.openjfx:javafx-controls:jar:win:17.0.0.1:compile
[INFO] |  \- org.openjfx:javafx-graphics:jar:17.0.0.1:compile
[INFO] |     +- org.openjfx:javafx-graphics:jar:win:17.0.0.1:compile
[INFO] |     \- org.openjfx:javafx-base:jar:17.0.0.1:compile
[INFO] |        \- org.openjfx:javafx-base:jar:win:17.0.0.1:compile
[INFO] \- org.openjfx:javafx-fxml:jar:17.0.0.1:compile
[INFO]    \- org.openjfx:javafx-fxml:jar:win:17.0.0.1:compile

Tree output without exclusion:

[INFO] --- maven-dependency-plugin:3.2.0:tree (default-cli) @ fxdemo ---
[INFO] org.jewelsea:fxdemo:jar:1.0-SNAPSHOT
[INFO] +- uk.co.caprica:vlcj:jar:4.7.1:compile
[INFO] |  \- uk.co.caprica:vlcj-natives:jar:4.1.0:compile
[INFO] |     +- net.java.dev.jna:jna:jar:5.2.0:compile
[INFO] |     \- net.java.dev.jna:jna-platform:jar:5.2.0:compile
[INFO] +- uk.co.caprica:vlcj-javafx:jar:1.0.2:compile
[INFO] |  \- org.openjfx:javafx-graphics:jar:14:compile
[INFO] |     +- org.openjfx:javafx-graphics:jar:win:14:compile
[INFO] |     \- org.openjfx:javafx-base:jar:14:compile
[INFO] |        \- org.openjfx:javafx-base:jar:win:14:compile
[INFO] +- org.openjfx:javafx-controls:jar:17.0.0.1:compile
[INFO] |  \- org.openjfx:javafx-controls:jar:win:17.0.0.1:compile
[INFO] \- org.openjfx:javafx-fxml:jar:17.0.0.1:compile
[INFO]    \- org.openjfx:javafx-fxml:jar:win:17.0.0.1:compile

Note the mixed JavaFX versions due to the uk.co.caprica:vlcj-javafx dependency.

Proposed change of scope for vlcj-javafx module javafx-graphics dependency

I think that it might have been preferable if the scope of the dependency for vlcj-javafx module on the javafx-graphics module had have been a provided dependency rather than a compile dependency.

Had it been defined that way, this mixed version scenario for JavaFX dependencies could not have been caused by the vlcj-javafx module.

There is a drawback in doing that though because vlcj-javafx does require a JavaFX installation in order to work, so that would need to be communicated or enforced through some other means.

Anyway, the decision of whether or not to make such a change in dependency scope for future vlcj-javafx versions lies with the maintainer of the vlcj-javafx module and not the application developers using it.

jewelsea
  • 150,031
  • 14
  • 366
  • 406