0

I am making a media player using Javafx and VLCJ.

The app contains a list view in which all the Media files. i.e. audio and video that the user selected are listed. Its like a playlist. I want to show the total Play time of the entire playlist. I also want to display the duration of individual media at the side. e.g.

1> my_visiting_video.mp4 | 01:21:35
2> avengers_end_game.flv | 02:13:40

TotalDuration: 03:35:15

above is the sample of my playlist that I would like to display in the app.

Following is what I did
DMediaPlayer.java

import uk.co.caprica.vlcj.factory.MediaPlayerFactory;
import uk.co.caprica.vlcj.javafx.fullscreen.JavaFXFullScreenStrategy;
import uk.co.caprica.vlcj.javafx.videosurface.ImageViewVideoSurface;
import uk.co.caprica.vlcj.player.base.MediaPlayer;
import uk.co.caprica.vlcj.player.base.MediaPlayerEventAdapter;
import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer;

public class DMediaPlayer implements MediaPlayerInterface {

    private ImageView mediaView;
    private final MediaPlayerFactory mediaPlayerFactory;

    private final EmbeddedMediaPlayer embeddedMediaPlayer;

public DMediaPlayer() {
        this.mediaPlayerFactory = new MediaPlayerFactory();
        this.embeddedMediaPlayer = mediaPlayerFactory.mediaPlayers().newEmbeddedMediaPlayer();
        
        this.mediaView = new ImageView();
        this.mediaView.setPreserveRatio(true);

        embeddedMediaPlayer.videoSurface().set(new ImageViewVideoSurface(this.mediaView));

    }
    @Override
    public void load(String filePath) throws FileNotFoundException {
        embeddedMediaPlayer.media().startPaused(filePath);
        embeddedMediaPlayer.controls().setPosition(0f);
    }

    @Override
    public ImageView getMediaView(){
        return mediaView;
    }
}

The above class returns a ImageView i.e. DMediaPlayer().getMediaView()

and this ImageView is added inside another class that controls all the playbacks, As well as that class has the playlist. For now the playlist only displays the available media files audio/video. It works fine.

But I want to add a new feature in which the list view not only displays the media files but also some additional information like total play time of the entire playlist and the play time of individual media file.

following is what I did.

File[] mediaList; // it is initialized in the constructor
private void updatePlayList(){

        CompletableFuture.runAsync(() -> {
            long totalDur = 0;
            MediaPlayerFactory factory = new MediaPlayerFactory();
            EmbeddedMediaPlayer player = factory.mediaPlayers().newEmbeddedMediaPlayer();
            for (int i = 0; i < mediaList.length; i++){
                long mediaDur = 0;
                
                player.media().startPaused(mediaList[i].getAbsolutePath());
                mediaDur = player.status().length();
                totalDur += mediaDur;
                drawer.addItem(
                        (i+1) + "> " +
                        mediaList[i].getName().substring(0, 25) + "..." +
                        " | " + millisToDuration(mediaDur)
                );
            }
            drawer.getTotalDuration().setText("Total Duration: " + totalDur);
        });
    }

drawer is a class that controls ListView. everything works fine except the vlcj. It throws the following error.

../include/vlc_xlib.h:46:vlc_xlib_init: Xlib not initialized for threads.
This process is probably using LibVLC incorrectly.
Pass "--no-xlib" to libvlc_new() to fix this.


Exception in thread "ForkJoinPool.commonPool-worker-3" java.lang.IllegalStateException: Not on FX application thread; currentThread = ForkJoinPool.commonPool-worker-3
    at javafx.graphics@19/com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:299)
    at javafx.graphics@19/com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:458)
    at javafx.graphics@19/javafx.scene.Parent$3.onProposedChange(Parent.java:474)
    at javafx.base@19/com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)
    at javafx.base@19/com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)
    at javafx.controls@19/javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:282)
    at javafx.controls@19/javafx.scene.control.skin.LabeledSkinBase.lambda$new$11(LabeledSkinBase.java:219)
    at javafx.controls@19/com.sun.javafx.scene.control.LambdaMultiplePropertyChangeListenerHandler.lambda$new$1(LambdaMultiplePropertyChangeListenerHandler.java:88)
    at javafx.base@19/javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:86)
    at javafx.base@19/com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:181)
    at javafx.base@19/com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.base@19/javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:104)
    at javafx.base@19/javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:111)
    at javafx.base@19/javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:145)
    at javafx.base@19/javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:50)
    at javafx.base@19/javafx.beans.property.StringProperty.setValue(StringProperty.java:71)
    at javafx.controls@19/javafx.scene.control.Labeled.setText(Labeled.java:147)
    at com.doruk.dplayer.controllers.PlayerController.lambda$updatePlayList$2(PlayerController.java:108)
    at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
    at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796)
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
    at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)

Now how can I fix this error, or how can I run the vlcj in the background? I mentioned background because, additional window opens that plays the video when I run the app.

or am I following the wrong approach...? is there better way...? all I want is to display the playtime of each media in the list.



I went through several pages in this website but all I could find was to get the total time of the file currently being played. So I followed the above approach. i.e. create a separate media player and start it and then calculate the play time.

Chrishdev
  • 1
  • 2
  • The easiest solution is probably to use `Platform#runLater(Runnable)`, either directly or via e.g., changing to `supplyAsync` and then use `thenAcceptAsync(/* set text */, Platform::runLater)`. Though there may be a better approach with vlcj that I'm not aware of. – Slaw Feb 20 '23 at 08:26
  • @Slaw it still is not working, however, even if I manage to get it work, what about the new video player window it opens...? can you suggest any other way I can try...? – Chrishdev Feb 20 '23 at 08:51
  • Hi, it works after putting all the code inside the Platform.runLater(); however this approach does not executes the code in a separate thread, it is blocking the code execution. I tried putting the Platform.runLater() inside the completable future like this: CompletableFuture.runAsync(()-> Platform.runLater()) but it gives the same error. – Chrishdev Feb 20 '23 at 10:27
  • as already suggested in your previous question: [mcve] please .. – kleopatra Feb 20 '23 at 11:27
  • No. Only put the code that **needs** to be executed on the FX thread in the `runLater`. – Slaw Feb 20 '23 at 11:37
  • just in case you didn't try yet: searching for "LibVCL vlc_xlib_init: Xlib not initialized for threads" came up with some hits (none of which I read to any depths, sry ;) - they might have info beyond the `--no-xlib` argument in the error message (wondering of what?) – kleopatra Feb 20 '23 at 11:37
  • 2
    This entire approach is questionable IMO, you are starting each media item with playPaused just to get the duration? LibVLC and therefore vlcj have Media abstraction that can be used to get various metadata without playing and pausing each item in turn. There are even vlcj examples that show how to do this (vlcj-examples at GitHub) – caprica Feb 20 '23 at 21:04

1 Answers1

0

Running on Linux? If so I had to call LibX11.INSTANCE.XInitThreads(); before the request for the media player factory.

import uk.co.caprica.vlcj.factory.MediaPlayerFactory;
import uk.co.caprica.vlcj.javafx.fullscreen.JavaFXFullScreenStrategy;
import uk.co.caprica.vlcj.javafx.videosurface.ImageViewVideoSurface;
import uk.co.caprica.vlcj.player.base.MediaPlayer;
import uk.co.caprica.vlcj.player.base.MediaPlayerEventAdapter;
import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer;
//this import
import uk.co.caprica.vlcj.binding.lib.LibX11;

public class DMediaPlayer implements MediaPlayerInterface {

    private ImageView mediaView;
    private final MediaPlayerFactory mediaPlayerFactory;

    private final EmbeddedMediaPlayer embeddedMediaPlayer;

//add this initialization
static{
LibX11.INSTANCE.XInitThreads();
}

public DMediaPlayer() {

        this.mediaPlayerFactory = new MediaPlayerFactory();
        this.embeddedMediaPlayer = mediaPlayerFactory.mediaPlayers().newEmbeddedMediaPlayer();
        
        this.mediaView = new ImageView();
        this.mediaView.setPreserveRatio(true);

        embeddedMediaPlayer.videoSurface().set(new ImageViewVideoSurface(this.mediaView));

    }
    @Override
    public void load(String filePath) throws FileNotFoundException {
        embeddedMediaPlayer.media().startPaused(filePath);
        embeddedMediaPlayer.controls().setPosition(0f);
    }

    @Override
    public ImageView getMediaView(){
        return mediaView;
    }
}

DDS
  • 2,340
  • 16
  • 34