2
votes

Background:

I am writing a cross platform audio player in Java. On Linux, I am distributing it in the form of an appimage (https://appimage.org/). I am using VLC/VLCJ as the decoding engine.

The basic guideline for appimages is to include all necessary libraries with the appimage which can't be reasonably expected to be included by default with the distribution. This is done so the user isn't required to resolve dependencies and/or wrestle with versioning differences between libraries and programs. They also recommend testing against the previous version of a handful of Linux distributions to confirm everything works.

Ubuntu 16.04 and Fedora 27.16 do not install libvlc by default. I'm sure this is common for many other distributions. Accordingly, I would like to package libvlc libraries in my appimage.

Issue:

On Linux, I am not able to get vlcj to recognize/find libvlc.so and libvlccore.so unless they are installed through the distribution.

Setup:

  1. I installed VLC through my distribution and my program runs and works properly.

  2. I copied libvlc.so, libvlccore.so, and other associated libraries from their default location in my distribution to a folder within my project.

  3. I added that folder to the Native Library Search path (see code below).

  4. I uninstalled VLC.

  5. I tried to run my program. It crashes with the error pasted below.

Note: I used this same basic method for Windows, and it works perfectly there.

Simplified Code:

String nativeVLCLibPath = Hypnos.getRootDirectory().resolve( "lib/nix/vlc/" ).toAbsolutePath().toString();
System.out.println ( "Trying to look for libraries in: " + nativeVLCLibPath );
NativeLibrary.addSearchPath( RuntimeUtil.getLibVlcLibraryName(), nativeVLCLibPath );

Error Message:

Trying to look for libraries in: /d/programming/workspace/MusicPlayer/stage/lib/nix/vlc SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. Aug 5, 2018 22:14:32 PM net.joshuad.hypnos.Hypnos start SEVERE: class java.lang.RuntimeException: Exception caught at top level of Hypnos. Exiting. java.lang.RuntimeException: Failed to load the native library.

The error was "Unable to load library 'vlc': JNA native support (linux-amd64/libvlc.so) not found in resource path (/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/resources.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jsse.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jce.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/charsets.jar:/usr/share/java/openjfx/jre/lib/ext/jfxrt.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/icedtea-sound.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/nashorn.jar:/usr/share/java/java-atk-wrapper.jar:/d/programming/workspace/MusicPlayer/stage/bin:/d/programming/workspace/MusicPlayer/stage/lib/commons-cli-1.4.jar:/d/programming/workspace/MusicPlayer/stage/lib/fuzzywuzzy-1.1.8.jar:/d/programming/workspace/MusicPlayer/stage/lib/jaudiotagger-2.2.6-SNAPSHOT.jar:/d/programming/workspace/MusicPlayer/stage/lib/commons-io-2.5.jar:/d/programming/workspace/MusicPlayer/stage/lib/jsoup-1.11.2.jar:/d/programming/workspace/MusicPlayer/stage/lib/commons-text-1.1.jar:/d/programming/workspace/MusicPlayer/stage/lib/commons-lang3-3.7.jar:/d/programming/workspace/MusicPlayer/stage/lib/jnativehook-2.0.2.jar:/d/programming/workspace/MusicPlayer/stage/lib/jlastfm.jar:/usr/lib/jvm/java-8-openjdk-amd64/lib/javafx-mx.jar:/d/programming/workspace/MusicPlayer/stage/lib/jna-3.5.2.jar:/d/programming/workspace/MusicPlayer/stage/lib/platform-3.5.2.jar:/d/programming/workspace/MusicPlayer/stage/lib/slf4j-api-1.7.10.jar:/d/programming/workspace/MusicPlayer/stage/lib/vlcj-3.10.1.jar)".

The required native libraries are named "libvlc.so" and "libvlccore.so".

In the text below represents the name of the directory containing "libvlc.so" and "libvlccore.so"...

There are a number of different ways to specify where to find the native libraries: 1. Include NativeLibrary.addSearchPath("vlc", ""); at the start of your application code. 2. Include System.setProperty("jna.library.path", ""); at the start of your application code. 3. Specify -Djna.library.path= on the command-line when starting your application. 4. Add to the system search path (and reboot).

If this still does not work, then it may be necessary to explicitly add the native library directory to the operating system configuration - e.g. on Linux this might mean setting the LD_LIBRARY_PATH environment variable, or adding configuration to the "/etc/ld.so.conf" file or the "/etc/ld.so.conf.d" directory. Of these options, setting LD_LIBRARY_PATH is the only one that would not require root privileges.

Finally, it is not possible to mix CPU architectures - it is not possible for a 64-bit Java Virtual Machine to load 32-bit native libraries.

More information may be available in the log.

at uk.co.caprica.vlcj.binding.LibVlcFactory.create(LibVlcFactory.java:198) at uk.co.caprica.vlcj.player.MediaPlayerFactory.(MediaPlayerFactory.java:259) at uk.co.caprica.vlcj.component.AudioMediaPlayerComponent.onGetMediaPlayerFactory(AudioMediaPlayerComponent.java:177) at uk.co.caprica.vlcj.component.AudioMediaPlayerComponent.(AudioMediaPlayerComponent.java:109) at net.joshuad.hypnos.audio.VLCAudioPlayer.(VLCAudioPlayer.java:75) at net.joshuad.hypnos.audio.AudioSystem.(AudioSystem.java:85) at net.joshuad.hypnos.Hypnos.start(Hypnos.java:726) at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$8(LauncherImpl.java:863) at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$7(PlatformImpl.java:326) at com.sun.javafx.application.PlatformImpl.lambda$null$5(PlatformImpl.java:295) at java.security.AccessController.doPrivileged(Native Method) at com.sun.javafx.application.PlatformImpl.lambda$runLater$6(PlatformImpl.java:294) at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95) at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method) at com.sun.glass.ui.gtk.GtkApplication.lambda$null$5(GtkApplication.java:139) at java.lang.Thread.run(Thread.java:748)

Aug 5, 2018 22:14:32 PM net.joshuad.hypnos.Hypnos exit INFO: Exit requested: UNKNOWN_ERROR

Exception in Application start method Aug 5, 2018 22:14:32 PM net.joshuad.hypnos.Hypnos exit INFO: Exit requested: NORMAL

Exception in Application stop method java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389) at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767) Caused by: java.lang.RuntimeException: Exception in Application start method at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917) at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$1(LauncherImpl.java:182) at java.lang.Thread.run(Thread.java:748) Caused by: java.lang.NullPointerException at net.joshuad.hypnos.Hypnos.exit(Hypnos.java:692) at net.joshuad.hypnos.Hypnos.start(Hypnos.java:872) at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$8(LauncherImpl.java:863) at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$7(PlatformImpl.java:326) at com.sun.javafx.application.PlatformImpl.lambda$null$5(PlatformImpl.java:295) at java.security.AccessController.doPrivileged(Native Method) at com.sun.javafx.application.PlatformImpl.lambda$runLater$6(PlatformImpl.java:294) at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95) at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method) at com.sun.glass.ui.gtk.GtkApplication.lambda$null$5(GtkApplication.java:139) ... 1 more Exception running application net.joshuad.hypnos.Hypnos

Contents of searched Folder (showing libvlc.so and others are there):

joshua @Joshua-PC /d/programming/workspace/MusicPlayer/stage/lib/nix/vlc 
$ ls -l                                                                                                                                                                       
total 2.5M
-rwxr-xr-x 1 joshua joshua 1.1M May 15 00:26 libvlccore.so
-rwxr-xr-x 1 joshua joshua 1.1M May 15 00:26 libvlccore.so.9.0.0
lrwxrwxrwx 1 joshua joshua   21 Aug  2 22:57 libvlc_pulse.so -> libvlc_pulse.so.0.0.0
lrwxrwxrwx 1 joshua joshua   21 Aug  2 22:57 libvlc_pulse.so.0 -> libvlc_pulse.so.0.0.0
-rwxr-xr-x 1 joshua joshua  14K May 15 00:26 libvlc_pulse.so.0.0.0
-rwxr-xr-x 1 joshua joshua 150K May 15 00:26 libvlc.so
-rwxr-xr-x 1 joshua joshua 150K May 15 00:26 libvlc.so.5.6.0
lrwxrwxrwx 1 joshua joshua   21 Aug  2 22:57 libvlc_vdpau.so -> libvlc_vdpau.so.0.0.0
lrwxrwxrwx 1 joshua joshua   21 Aug  2 22:57 libvlc_vdpau.so.0 -> libvlc_vdpau.so.0.0.0
-rwxr-xr-x 1 joshua joshua  18K May 15 00:26 libvlc_vdpau.so.0.0.0
lrwxrwxrwx 1 joshua joshua   26 Aug  2 22:57 libvlc_xcb_events.so -> libvlc_xcb_events.so.0.0.0
lrwxrwxrwx 1 joshua joshua   26 Aug  2 22:57 libvlc_xcb_events.so.0 -> libvlc_xcb_events.so.0.0.0
-rwxr-xr-x 1 joshua joshua 9.9K May 15 00:26 libvlc_xcb_events.so.0.0.0
drwxr-xr-x 1 joshua joshua 4.0K Aug  2 22:57 lua
drwxr-xr-x 1 joshua joshua 4.0K Aug  2 22:57 plugins
-rwxr-xr-x 1 joshua joshua  11K May 15 00:26 vlc-cache-gen
3
To rule out the obvious - are you sure the arch of those libraries is definitely 64 bit? (Running file libvlc.so should tell you on most systems.)Michael Berry
Thanks, yes. They are 64 bit, as is the jvm and the rest of my system.JoshuaD

3 Answers

2
votes

I'll bet the distro's version of the native lib has been built with hard-coded paths, possibly absolute paths. Unfortunately, it is not uncommon for libraries to be written to require these -- they're usually passed as flags into the ./configure script or makefile or whatever at build-time. The only fix is to build the lib yourself, or install parallel symlinks/hardlinks, in the distro's lib's expected system dirs, on the target system, to point to your libs. Or execute under chroot, but that is an extreme option that is probably impractical for you.

1
votes

I can see the appeal of using appimage, but what I do instead is to create a distribution package (.deb or whatever) that contains my application and vlcj and have that depend on LibVLC (and a jdk).

1
votes

I solved this by downloading an appimage of vlc, unpackaging it using the --appimage-extract cli option, stripping out things I didn't need, and then repackaging it into my appimage.