Bug? Compiling app does not include all libs in Apk.

moster67

Expert
Licensed User
Longtime User
In these days, I have been updating the Vitamio and the FFmpegEncoder libraries (both based on FFmpeg) and noted that if I use both wrappers in the same B4A-project/App, some libs (also so-libs) are not exported into the resulting Apk and of course the app will not work.

If use my libraries in separate projects (not mixing them), the libraries work just fine.

The B4A IDE does not give any error messages when loading the libraries:
upload_2018-1-8_13-39-18.png


Also during compilation, there are no error messages:
upload_2018-1-8_13-40-18.png


If I compile a similar project with Android Studio using the original libraries, all libraries (vitamio-ffmpeg and ffmpegencoder under assets) are included in the resulting Apk as you can see here:
upload_2018-1-8_13-45-51.png


After analyzing the Apk generated by B4A, I noted that my ffmpegencoder-libraries (under assets) are not included in the resulting Apk. Only the relative Vitamio ffmpeg libs are included:
upload_2018-1-8_13-48-36.png


My wrappers are structured as follows:
Vitamio:
upload_2018-1-8_13-50-3.png


And ffmpegencoder:
upload_2018-1-8_13-50-55.png


The Apk:s were compiled in Release-mode in B4A and Android Studio.

Maybe I am doing something wrong but I don't understand why B4A does not include my ffmpegencoder-libs into the resulting Apk while with Android Studio it works fine.

Any ideas?

Thanks and sorry for all images but sometimes it is easier to explain with help of images.
 

moster67

Expert
Licensed User
Longtime User
I guess you mean that I should change the folder name from assets to lib in my ffmpegencoder-wrapper.

I will try that although I am not sure if it will work since the original class which looks for the ffmpeg-location seems to search using getAssets():

B4X:
InputStream inputStream = context.provide().getAssets().open(prefix + "ffmpeg");

The original code is:

B4X:
class FileUtils
private static final String FFMPEG_FILE_NAME = "ffmpeg";
    
    static File getFFmpeg(Context context) {
        File folder = context.getFilesDir();
        return new File(folder, FFMPEG_FILE_NAME);
    }
    
    
class FFmpeg   
public boolean isSupported() {
        // check if arch is supported
        CpuArch cpuArch = CpuArchHelper.getCpuArch();
        if (cpuArch == CpuArch.NONE) {
            Log.e("arch not supported");
            return false;
        }

        // get ffmpeg file
        File ffmpeg = FileUtils.getFFmpeg(context.provide());

        SharedPreferences settings = context.provide().getSharedPreferences("ffmpeg_prefs", Context.MODE_PRIVATE);
        int version = settings.getInt(KEY_PREF_VERSION, 0);

        // check if ffmpeg file exists
        if (!ffmpeg.exists() || version < VERSION) {
            String prefix = "arm/";
            if (cpuArch == CpuArch.x86) {
                prefix = "x86/";
            }
            Log.d("file does not exist, creating it...");

            try {
                InputStream inputStream = context.provide().getAssets().open(prefix + "ffmpeg");
                if (!FileUtils.inputStreamToFile(inputStream, ffmpeg)) {
                    return false;
                }

                Log.d("successfully wrote ffmpeg file!");

                settings.edit().putInt(KEY_PREF_VERSION, VERSION).apply();
            } catch (IOException e) {
                Log.e("error while opening assets", e);
                return false;
            }
        }

        // check if ffmpeg can be executed
        if (!ffmpeg.canExecute()) {
            // try to make executable
            try {
                try {
                    Runtime.getRuntime().exec("chmod -R 777 " + ffmpeg.getAbsolutePath()).waitFor();
                } catch (InterruptedException e) {
                    Log.e("interrupted exception", e);
                    return false;
                } catch (IOException e) {
                    Log.e("io exception", e);
                    return false;
                }

                if (!ffmpeg.canExecute()) {
                    // our last hope!
                    if (!ffmpeg.setExecutable(true)) {
                        Log.e("unable to make executable");
                        return false;
                    }
                }
            } catch (SecurityException e) {
                Log.e("security exception", e);
                return false;
            }
        }

        Log.d("ffmpeg is ready!");

        return true;
    }

Fortunately I can modify the sources and re-compile them if necessary.
 

moster67

Expert
Licensed User
Longtime User
I tried what you first suggested but it did not work.
I will try using aar as suggested by you.
Thanks for your support.
 

moster67

Expert
Licensed User
Longtime User
the ffmpegencoder-lib does not work with aar - not even when using it alone in a project.

I created the aar-file with the following structure:
my.aar:
-assets
-arm
-ffmpeg​
-x86
-ffmpeg​
-res

Then in my wrapper I added a reference to my.aar:
@BA.DependsOn(values = {"my.aar"})
Added thereafter the aar-file to my additional libs folder.

The classes.jar was removed from the aar-file and included in the compilation of the library under the libs-folder.

This is the error I get after refreshing the libs and cleaning the project:
B4X:
java.lang.NoClassDefFoundError: Failed resolution of: Lnl/bravobit/ffmpeg/FFmpeg;
    at com.tillekesoft.ffmpegencoder2.FfmpegEncoderWrapper.loadFFMpegBinary(FfmpegEncoderWrapper.java:41)
    at com.tillekesoft.ffmpegencoder2.FfmpegEncoderWrapper.Initialize(FfmpegEncoderWrapper.java:32)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.shell.Shell.runVoidMethod(Shell.java:755)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:345)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:249)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:139)
    at b4a.example.main.afterFirstLayout(main.java:102)
    at b4a.example.main.access$000(main.java:17)
    at b4a.example.main$WaitForLayout.run(main.java:80)
    at android.os.Handler.handleCallback(Handler.java:751)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6077)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
Caused by: java.lang.ClassNotFoundException: Didn't find class "nl.bravobit.ffmpeg.FFmpeg" on path: DexPathList[[zip file "/data/app/b4a.example-1/base.apk"],nativeLibraryDirectories=[/data/app/b4a.example-1/lib/x86, /system/lib, /vendor/lib]]
    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
    ... 18 more
 

moster67

Expert
Licensed User
Longtime User
OK, thanks. Will do it that way and let you know how it goes.
 

moster67

Expert
Licensed User
Longtime User
Tried that as well but no luck. The wrapper gets loaded correctly but the ffmpeg-lib is not found (which I verified through a log message in the original java class).
It seems like I need to compile and set up SLC like I did initially which did work although it does not play well in a project where also Vitamio is present.
I will try to investigate further or otherwise I will try to rewrite the code in the original sources (which I am fortunate to have) so it picks up the ffmpeg-lib in the dir-folder.
 
Last edited:

moster67

Expert
Licensed User
Longtime User
Thank you Don. Yes, I remember that thread but I forgot about it - I have now bookmarked it :)

Well, I have been using Android Studio for a long time now for wrapping libs. It's true that I have not created AARs to include in the wrapper (or maybe a few). I still do the opposite, after I have written my wrapper-class and perhaps modified the original sources, I normally compile everything and then in the lib's build directory, you can find a folder called output and inside there, you will find the release.aar. Then I extract the classes.jar (rename it) and put it into SLC's libs-folder. If I need to add so-files, I put them in SLC's additional folder etc..

You can compile the project easily using the Build Variant tool under the Build menu:
upload_2018-1-8_20-3-58.png


Then I normally rebuild the project and I have my release.aar file :)

Anyway I will try @corwin42's tutorial.
 
Top