google / lyra

A Very Low-Bitrate Codec for Speech Compression
Apache License 2.0
3.84k stars 355 forks source link

building cc_library target for inclusing in android app #64

Open nsornin opened 3 years ago

nsornin commented 3 years ago

dear all. I am trying to build a .so library fro the encoder and decoder for inclusion in an android app under android studio. The tutorial says to build the cc_library but i have no idea what that means. Could someone kindly provide an example bazel command line for this ? Thank you so much !

aluebs commented 3 years ago

To build the Lyra encoder and decoder using fixed point weights:

bazel build -c opt --copt="-DUSE_FIXED16" :lyra_encoder
bazel build -c opt --copt="-DUSE_FIXED16" :lyra_decoder

To build for android, you also have to add an additional --config=android_arm64 flag.

bkekelic2 commented 3 years ago

Hi, I followed those steps for building .so files for both encoder and decoder and tried to integrate them into android app. Build succeeded and it generated both .so files and also libc++_shared.so file which is obviously needed by those, but when I try to load library in my app:

static{ System.loadLibrary("lyra_encoder"); }

it fails with error like this:

java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "_ZN11chromemedia5codec26kNumExpectedOutputFeaturesE" referenced by "/data/app/com.example.android.lyra-9rYYUaE5xu0iD0jGIXmw-g==/base.apk!/lib/arm64-v8a/liblyra_encoder.so"...

I put .so files under right architecture jniLibs->amr64-v8a->lyra_encoder.so, lyra_decoder.so, libc++_shared.so (when I didn't add libc++_shared.so it fails with error like this .so is missing - so I'm assuming that it is needed)... Also, I tried with different SDK versions in my gradle file (compile, min, target) to match device on which I tried to run (Motorola, Android 10, API 29) and I also put buildToolsVersion to 29.0.3.

When checking location of kNumExpectedOutputFeatures variable I can see that it is located in lyra_config.h/cc.

So, I'm not sure what I'm missing and I would appreciate any help (should I add headers in build somehow?).

Thanks in advance.

aluebs commented 3 years ago

I wouldn't know, cause I never tried to build the so library and load it from Java (see our android example), maybe someone else can chime in? @nsornin, did you end up figuring this out?

bkekelic2 commented 3 years ago

Eventually, I figured it out. For some reason, the above steps you provided didn't work for me. All methods were actually located in the .so file but they were undefined and not linked. Maybe I'm missing sth, but it's probably due to _lyraencoder and _lyradecoder are cc_libraries. The problem is maybe related with this issue, as cc_library won't link it's dependencies and the .so file will behave like an archive.

Instead of this approach, I followed your android example and made another example app but just with android_binary linking encode and decode API classes. Afterwards I gathered the .so file from the resulting .apk file.

aluebs commented 3 years ago

I am glad to hear that you figured it out. And thank you for sharing, so that your workaround can help other people trying to achieve the same thing :)

JetaimeCat commented 2 years ago

@bkekelic2 Hi, can you tell me in detail how you solved it. I also want to call in android studio with .so file.

bkekelic2 commented 2 years ago

Hi @JetaimeCat. I can't share the code, but here are detailed steps. Basically, the idea is to make similar example as android_example is, and when you build it with bazel you will get .apk file. If you extract that .apk file (as .apk is sth similar to zip file so you can extract content) inside you will find the .so file which contains methods which you defined and implemented in jni_sth_lib.cc file. Those methods you can call via native methods from java.

So, first I cloned android_example and created new one - wrapper_example. Then I wrote jni_sth_lib.cc file which contains "raw" encode and decode methods (which are written in C++ lang) - those methods are calling Lyra C code. I followed those steps for writing JNI C++ wrapper for Codec2. After that, I move on to the BUILD file and just modified it to use only deps I needed: lyra_config, lyra_encoder and lyra_decoder. Next, you also need to write down Lyra.java wrapper file, which you will use in android app and which contains java native methods (Example for Codec2). And that's it. Now you can build the .apk file with bazel with further command: bazel build wrapper_example:lyra --config=android_arm64 --copt=-DBENCHMARK. Finally, when .apk is build unzip it and find .so file inside.

For using it in android studio, copy it under proper architecture folder and copy Lyra.java wrapper also inside proper project structure folder. And that's it.

Extra: you can check bottom of this conversation/issue for some extra info regarding Lyra and android.

Hope this helps. 😉

JianhaoPeng commented 2 months ago

@bkekelic2 Hi, I also try to build my own app with separate encode and decode methods, I follow the steps to first build the lyra example app, and then build the app with the separate methods. But after I installed the lyra example app to my phone, I encountered a crash, it seems that the Android failed to open the .so file. Do you have a clue for this problem, or if it ok for you to share the .so file? Really appreciate your help.

bkekelic2 commented 2 months ago

Firstly, you can check whether device architecture is the same as the architecture for which you built the .so file. In upper example, I build for arm64 arch, maybe your device operates on different one: bazel build wrapper_example:lyra --config=android_arm64 --copt=-DBENCHMARK Additionally, I would check whether the .so file is in proper place. Finally, some logs would be useful for debugging, but not sure how much I can help as I didn't touch this topic for 2 years now.