B4J Question Native library

yo3ggx

Active Member
Licensed User
Longtime User
Hello.

I've build a B4J library using Eclipse. Some native libraries are used inside the library jar file.
In a test application (directly in Eclipse), everything is working as expected.
I added the library jar file in the External libraries folder in B4J (including the library containing the native files).
When I run my B4J app, I get the error:
java.lang.UnsatisfiedLinkError: no mylib in java.library.path: [...]

The dll file (for Windows) is present in my app jar file in the subfolder "win-x86-64".

What can I do to tell my app where to search for the dll file?

Thank you.
 

yo3ggx

Active Member
Licensed User
Longtime User
This is the part of the library class that loads the native library.

B4X:
public class MyClass {
  
    static {
        try {
            System.loadLibrary("mylib");
        } catch (UnsatisfiedLinkError e1) {
            try {
                File f = Native.extractFromResourcePath("mylib");
                System.load(f.getAbsolutePath());
            } catch (Exception e2) {
                e1.printStackTrace();
                e2.printStackTrace();
            }
        }
    }
...
}

And this is the error I get in B4J.

B4X:
Native library (win32-x86-64/mylib.dll) not found in resource path (...\Objects\myapp.jar)[...]

Which is not true, as the dll file is there.

As stated in my previous email, a test app written directly in the lib, works as expected (when I run it directly from Eclipse):

B4X:
public class Test {
      public static void main(String[] args) throws Exception {
         MyClass mc = new MyClass();
          mc.test();
      }

}

Where 'test' is a test public routine in MyClass that use the native library.
 
Last edited:
Upvote 0

yo3ggx

Active Member
Licensed User
Longtime User
I updated the post in between. The first error that occurs is:

B4X:
Native library (win32-x86-64/mylib.dll) not found in resource path (...\Objects\myapp.jar)[...]
which is not true, as I can find the dll in my app jar file, in the correct folder
 
Upvote 0

yo3ggx

Active Member
Licensed User
Longtime User
The subfolder is the one from the 3rd party jar library that I'm using in my B4J library. I don't have any control where B4J is putting the native files in my app jar file.
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
I've had this problem, but I cannot find my source code files.
i seem to recall I either set the 'java.library.path' property in the java code to include where the dll is, or I used the '-Djava.library.path=xxx" on jvm args.
 
Upvote 0

yo3ggx

Active Member
Licensed User
Longtime User
Don't put the dll inside the jar. Put it next to the jar (in the objects folder).
I don't have control where the dll is located, as is part of the jar external library.
The library jar is in the External Libraries folder.
The 3rd party library contains native libraries for Windows (32/64), Linux (32/64) and Mac (64).

I want my app to be able to run on all these platforms.
 
Upvote 0

yo3ggx

Active Member
Licensed User
Longtime User
I've had this problem, but I cannot find my source code files.
i seem to recall I either set the 'java.library.path' property in the java code to include where the dll is, or I used the '-Djava.library.path=xxx" on jvm args.
I want my app to run on all platforms, so is not obly about the dll, but about de .so and .dylib files too.
Why to set the java path, as the native libraries are included in the jar file of the external library I'm using and is sotred in the ExternaLibs folder?
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
As I recall you have to tell the link-loader where the libraries are as it only looks at the path and classpath to find them.
 
Upvote 0

yo3ggx

Active Member
Licensed User
Longtime User
To be more specific, is about a wrapper for the Java Opus Wrapper available here: https://github.com/tbocek/opus-wrapper/tree/master
as a jar file from here: https://tomp2p.net/dev/mvn/net/tomp2p/opus-wrapper/1.3/opus-wrapper-1.3.jar

My library class looks like that:
B4X:
import java.io.File;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.List;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;

import com.sun.jna.Native;
import com.sun.jna.ptr.PointerByReference;

import net.tomp2p.opuswrapper.*;
    
    public class OpusTest {
        
        static {
            try {
                System.loadLibrary("opus");
            } catch (UnsatisfiedLinkError e1) {
                try {
                    File f = Native.extractFromResourcePath("opus");
                    System.load(f.getAbsolutePath());
                } catch (Exception e2) {
                    e1.printStackTrace();
                    e2.printStackTrace();
                }
            }
        }

    

        public void testCodec() throws LineUnavailableException {
            
            AudioFormat format = new AudioFormat(8000.0f, 16, 1, true, true);
            ShortBuffer dataFromMic = recordFromMicrophone(format, 5000);
            List<ByteBuffer> packets = encode(dataFromMic);
            // packets go over network
            ShortBuffer decodedFromNetwork = decode(packets);
            playBack(format, decodedFromNetwork);
        }

        private ShortBuffer decode(List<ByteBuffer> packets) {
            IntBuffer error = IntBuffer.allocate(4);
            PointerByReference opusDecoder = Opus.INSTANCE.opus_decoder_create(8000, 1, error);

            ShortBuffer shortBuffer = ShortBuffer.allocate(1024 * 1024);
            for (ByteBuffer dataBuffer : packets) {
                byte[] transferedBytes = new byte[dataBuffer.remaining()];
                dataBuffer.get(transferedBytes);
                int decoded = Opus.INSTANCE.opus_decode(opusDecoder, transferedBytes, transferedBytes.length,
                        shortBuffer, 80, 0);
                shortBuffer.position(shortBuffer.position() + decoded);
            }
            shortBuffer.flip();

            Opus.INSTANCE.opus_decoder_destroy(opusDecoder);
            return shortBuffer;
        }

        private List<ByteBuffer> encode(ShortBuffer shortBuffer) {
            IntBuffer error = IntBuffer.allocate(4);
            PointerByReference opusEncoder = Opus.INSTANCE.opus_encoder_create(8000, 1,
                    Opus.OPUS_APPLICATION_RESTRICTED_LOWDELAY, error);
            int read = 0;
            List<ByteBuffer> list = new ArrayList<>();
            while (shortBuffer.hasRemaining()) {
                ByteBuffer dataBuffer = ByteBuffer.allocate(1024);
                int toRead = Math.min(shortBuffer.remaining(), dataBuffer.remaining());
                read = Opus.INSTANCE.opus_encode(opusEncoder, shortBuffer, 80, dataBuffer, toRead);
                dataBuffer.position(dataBuffer.position() + read);
                dataBuffer.flip();
                list.add(dataBuffer);
                shortBuffer.position(shortBuffer.position() + 80);
            }
            Opus.INSTANCE.opus_encoder_destroy(opusEncoder);
            // used for debugging
            shortBuffer.flip();
            return list;
        }

        private void playBack(AudioFormat format, ShortBuffer shortBuffer) throws LineUnavailableException {
            SourceDataLine speaker = AudioSystem.getSourceDataLine(format);
            speaker.open(format);
            speaker.start();

            short[] shortAudioBuffer = new short[shortBuffer.remaining()];
            shortBuffer.get(shortAudioBuffer);
            byte[] audio = ShortToByte_Twiddle_Method(shortAudioBuffer);
            speaker.write(audio, 0, audio.length);
        }

        private ShortBuffer recordFromMicrophone(AudioFormat format, int lengthMillis)
                throws LineUnavailableException {
            DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
            if (!AudioSystem.isLineSupported(info)) {
                throw new LineUnavailableException("not supported");
            }
            TargetDataLine microphone = AudioSystem.getTargetDataLine(format);
            // Obtain and open the line.
            microphone.open(format);

            // Assume that the TargetDataLine, line, has already been obtained and
            // opened.

            byte[] data = new byte[microphone.getBufferSize() / 5];

            // Begin audio capture.
            microphone.start();
            // probably way too big
            ShortBuffer shortBuffer = ShortBuffer.allocate(1024 * 1024);
            // Here, stopped is a global boolean set by another thread.
            long start = System.currentTimeMillis();
            int numBytesRead;
            while (System.currentTimeMillis() - start < lengthMillis) {
                // Read the next chunk of data from the TargetDataLine.
                numBytesRead = microphone.read(data, 0, data.length);
                // Save this chunk of data.
                for (int i = 0; i < numBytesRead; i += 2) {
                    int b1 = data[i + 1] & 0xff;
                    int b2 = data[i] << 8;
                    shortBuffer.put((short) (b1 | b2));
                }
            }
            shortBuffer.flip();
            return shortBuffer;
        }

        private byte[] ShortToByte_Twiddle_Method(final short[] input) {
            final int len = input.length;
            final byte[] buffer = new byte[len * 2];
            for (int i = 0; i < len; i++) {
                buffer[(i * 2) + 1] = (byte) (input[i]);
                buffer[(i * 2)] = (byte) (input[i] >> 8);
            }
            return buffer;
        }
    }

Then I have a java app in Eclipse:
B4X:
public class Test {
      public static void main(String[] args) throws Exception {
         OpusTest ot = new OpusTest();
          ot.testCodec();
      }

}


When I run this directly in Eclipse, is working as expected.

If I'm running from my app, I get the error.
I don't want to access the jar file (opus-wrapper-1.3.jar) directly, as most of the functions are implemented in my own B4J library, not in the B4J application.
 
Upvote 0

yo3ggx

Active Member
Licensed User
Longtime User
Most probably the root cause is that I've built a wrapper library for a wrapper library, but I don't know how to address this.
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
Ok , I am confused now, is mylib in java or a dll/so?
 
Upvote 0

yo3ggx

Active Member
Licensed User
Longtime User
I
Ok , I am confused now, is mylib in java or a dll/so?
Check the code I've posted above. opus-wrapper-1.3.jar file is used as an external jar library in my B4J library (in Eclipse).
If called from Eclipse, everything is working as expected.
If the initialization routine (from my B4J library) is called from the B4J application, I get the error.
B4X:
java.io.IOException: Native library (win32-x86-64/opus.dll) not found in resource path (P:\MyApp\Objects\myapp.jar)

In fact, If I'm looking in the app jar file (myapp.jar), the dll is there, in the folder win32-x86-64, so the error message is wrong.
 
Upvote 0

yo3ggx

Active Member
Licensed User
Longtime User
I integrated Opus class directly in my lib and copied the native libraries in the bin folder of my Eclipse project.
Now is working in Eclipse directly (by running Main class), but the same message in my app. The explanation now is that the native files are no more present in my app jar. How can I add native files in B4J to be included in the jar? Or is possible to include the native libs in my B4J lib when exporting from Eclipse?
 
Upvote 0
Top