Makefile needed for compiling the Opus Codec for Android
Asked Answered
F

4

16

I am trying to compile the opus audio codec (http://www.opus-codec.org/downloads/) for use in an Android application. I am using Android NDK (release 6) to compile my libraries. Up to now, the native C libraries that I had to compile for my application has been pretty straightforward and I have been able to base my Android.mk files in jni mostly on tutorials or other examples. However, the compilation of Opus looks to be somewhat more complex. The tar.gz archive contains a solution file for compiling the libraries for Windows and it also contains some Makefiles for a standard Unix implementation, but translating these into an Android.mk makefile for use by the Android NDK is a bit of a challenge.

I have searched, but have been unable to find an online version for the Android makefile to compile libopus. Can someone perhaps link me to such a makefile? Alternatively, I might be missing something simpler? Is it perhaps possible to use Automake or some kind of converter to maybe generate the Android.mk file for me from the unix makefile that is already included in the tar.gz?

Fantastically answered 25/7, 2013 at 21:45 Comment(0)
F
13

The following is the Android.mk makefile that eventually worked for me. I hope this can help someone else too in future. Do take note that in the unix makefile included in the Opus Archive, the decision of whether to use the fixed or float silk sources is defined as an "ifdef" whereas here I am using "fixed". (To use "float" would be simple - simply update the paths "silk/fixed" to point to "silk/float" and update the CFLAGS.

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

MY_MODULE_DIR       := opus

LOCAL_MODULE        := $(MY_MODULE_DIR)
LOCAL_SRC_FILES     := \
    $(subst $(ROOT_DIR)/$(MY_MODULE_DIR)/,,$(wildcard $(ROOT_DIR)/$(MY_MODULE_DIR)/src/*.c*)) \
    $(subst $(ROOT_DIR)/$(MY_MODULE_DIR)/,,$(wildcard $(ROOT_DIR)/$(MY_MODULE_DIR)/celt/*.c*)) \
    $(subst $(ROOT_DIR)/$(MY_MODULE_DIR)/,,$(wildcard $(ROOT_DIR)/$(MY_MODULE_DIR)/silk/*.c*)) \
    $(subst $(ROOT_DIR)/$(MY_MODULE_DIR)/,,$(wildcard $(ROOT_DIR)/$(MY_MODULE_DIR)/silk/fixed/*.c*))
LOCAL_LDLIBS        := -lm -llog
LOCAL_C_INCLUDES    := \
    $(ROOT_DIR)/$(MY_MODULE_DIR)/include \
    $(ROOT_DIR)/$(MY_MODULE_DIR)/silk \
    $(ROOT_DIR)/$(MY_MODULE_DIR)/silk/fixed \
    $(ROOT_DIR)/$(MY_MODULE_DIR)/celt
LOCAL_CFLAGS        := -DNULL=0 -DSOCKLEN_T=socklen_t -DLOCALE_NOT_USED -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
LOCAL_CFLAGS    += -Drestrict='' -D__EMX__ -DOPUS_BUILD -DFIXED_POINT -DUSE_ALLOCA -DHAVE_LRINT -DHAVE_LRINTF -O3 -fno-math-errno
LOCAL_CPPFLAGS      := -DBSD=1 
LOCAL_CPPFLAGS          += -ffast-math -O3 -funroll-loops

include $(BUILD_STATIC_LIBRARY)
Fantastically answered 28/7, 2013 at 14:29 Comment(2)
Why not contribute it back to the Opus folks?Bernardo
Hey Stanley, Has this ever been added to an official opus release? Which version of opus was this makefile for? Do you have an updated makefile for v1.1?Geanticlinal
H
11

IMPORTANT EDIT The images might not be up to date but the work has been tested on below versions:

  • opus-1.1
  • opus-1.1.2

An updated version (works for opus-1.1.2 ) of @praneetloke's solution. A kind of different approach with some extras. First of all below is my structure to use (I intended to use more libraries, so I put opus in its own sub-folder. ) folder structor for opus JNI

Secondly I have a root Android.mk file and one another inside the folder opus-1.1.2 .

Here is the root Android.mk file:

LOCAL_PATH := $(call my-dir)
OPUS_DIR            := opus-1.1.2

include $(OPUS_DIR)/Android.mk

include $(CLEAR_VARS)

LOCAL_MODULE        := codec
LOCAL_SRC_FILES     := Opus_jni.cpp
LOCAL_CFLAGS        := -DNULL=0
LOCAL_LDLIBS        := -lm -llog
LOCAL_C_INCLUDES    := $(LOCAL_PATH)/$(OPUS_DIR)/include
LOCAL_SHARED_LIBRARIES := opus
include $(BUILD_SHARED_LIBRARY)

Android.mk file inside the opus-1.1.2 folder is below:

#Backing up previous LOCAL_PATH so it does not screw with the root Android.mk file
LOCAL_PATH_OLD := $(LOCAL_PATH)
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

#include the .mk files
include $(LOCAL_PATH)/celt_sources.mk
include $(LOCAL_PATH)/silk_sources.mk
include $(LOCAL_PATH)/opus_sources.mk

LOCAL_MODULE        := opus

#fixed point sources
SILK_SOURCES += $(SILK_SOURCES_FIXED)

#ARM build
CELT_SOURCES += $(CELT_SOURCES_ARM)
SILK_SOURCES += $(SILK_SOURCES_ARM)
LOCAL_SRC_FILES     := \
$(CELT_SOURCES) $(SILK_SOURCES) $(OPUS_SOURCES)

LOCAL_LDLIBS        := -lm -llog

LOCAL_C_INCLUDES    := \
$(LOCAL_PATH)/include \
$(LOCAL_PATH)/silk \
$(LOCAL_PATH)/silk/fixed \
$(LOCAL_PATH)/celt

LOCAL_CFLAGS        := -DNULL=0 -DSOCKLEN_T=socklen_t -DLOCALE_NOT_USED -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64

LOCAL_CFLAGS        += -Drestrict='' -D__EMX__ -DOPUS_BUILD -DFIXED_POINT=1 -DDISABLE_FLOAT_API -DUSE_ALLOCA -DHAVE_LRINT -DHAVE_LRINTF  -DAVOID_TABLES
LOCAL_CFLAGS        +=  -w -std=gnu99 -O3 -fno-strict-aliasing -fprefetch-loop-arrays  -fno-math-errno
LOCAL_CPPFLAGS      := -DBSD=1
LOCAL_CPPFLAGS      += -ffast-math -O3 -funroll-loops

include $(BUILD_SHARED_LIBRARY)

#Putting previous LOCAL_PATH back here
LOCAL_PATH := $(LOCAL_PATH_OLD)

This is how it looks like inside opus-1.1.2 folder:

Whole Opus Folder structure

The only touch to the original sources is addition of an Android.mk file. Nothing removed.

This way I can use and update opus just like a separate library.


Here is the extras for those who wants to compile and use opus in android; Wrapper source, partially:

#include <jni.h>
#include <android/log.h>
#include <opus.h>

/* Header for class net_abcdefgh_opustrial_codec_Opus */
#ifndef _Included_net_abcdefgh_opustrial_codec_Opus
#define _Included_net_abcdefgh_opustrial_codec_Opus

#define TAG "Opus_JNI"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , TAG,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO  , TAG,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN  , TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , TAG,__VA_ARGS__)
#ifdef __cplusplus
extern "C" {
    #endif
    JNIEXPORT jint JNICALL Java_net_abcdefgh_opustrial_codec_Opus_open
    (JNIEnv *env, jobject thiz){
        ...
        return error;
    }
    JNIEXPORT jint JNICALL Java_net_abcdefgh_opustrial_codec_Opus_decode
    (JNIEnv * env, jobject thiz, jbyteArray jencoded, jint jencodedOffset, jint jencodedLength, jbyteArray jpcm, jint jpcmOffset, jint jframeSize) {
        ...
        return decodedSize;
    }
    JNIEXPORT jint JNICALL Java_net_abcdefgh_opustrial_codec_Opus_encode
    (JNIEnv * env, jobject thiz, jbyteArray jpcm, jint jpcmOffset, jint jpcmLength, jbyteArray jencoded, jint jencodedOffset) {
        ...
        return encodedSize;
    }
    JNIEXPORT void JNICALL Java_net_abcdefgh_opustrial_codec_Opus_close
    (JNIEnv *env, jobject thiz){
        ...
    }
    #ifdef __cplusplus
}
#endif
#endif

Application.mk file (optional)

APP_ABI := all  # mips, armeabi, armeabi-v7a, x86 etc. builds
Homely answered 26/6, 2015 at 11:54 Comment(3)
Very helpful, thanks! I, too, have one similar to yours and the ones above for building a libopus.so for armeabi. However, I was wondering if you have looked into also building an armeabi-v7a version as well?Costermansville
@Costermansville I am not sure but, APP_ABI := all in Application.mk will generate bunch of .so file. If you want only armeabi and armeabi-v7a then you can use APP_ABI := armeabi armeabi-v7a. I have not tested other .so files tho.Homely
#include the .mk files include celt_sources.mk include silk_sources.mk include opus_sources.mk where did you get this mk files ?Batsman
P
3

EDIT: Just use the accepted answer's solution above, the original link is dead plus it's basically the same as the accepted answer at the time I answered.

Here is one, maybe a little outdated (it used opus 0.9.14):

https://github.com/haxar/mangler/blob/master/android/jni/Android.mk

You're to write some JNI wrappers after getting the library to compile, though...

Peppy answered 28/7, 2013 at 1:35 Comment(4)
Thank you, this helped set me on track. I got a few linker errors when using the libopus portion of the makefile you link to so I had to change it. I am upvoting because the link was helpful in showing that celt, silk and opus sources can be linked in a single compile and does not have to be separately compiled into different modules first.Fantastically
who knows how to call these JNI interface from Android? I want to record the voice, but I do not know how to use opus functions to record the voice and save them to file.Pyrogenous
@Pyrogenous You want to record audio samples using the Java API, then pass the recorded array of samples into C realm for encoding. For that part you do need to write the wrappers yourself, as it's obvious that Opus does not have any Java support for now. Hopefully it's simple to write such wrappers according to libopus API documentation; be sure to check it out. Then, writing the file can be done completely in Java after encoding is completed. Hope this helps!Peppy
Link is dead. Giving just a link is strongly discouraged.Homely
M
3

Thanks to @Stanley, I was able to create a shared library successfully by tweaking his solution a bit. I don't know yet if there is an advantage of having a static library vs. a shared library. All I know is I need a shared library for the JNI wrapper. This is what I have. Note the compiler flags for fixed point. Without those the compilation will fail for fixed point mode.

First copy the celt_sources.mk, silk_sources.mk and opus_sources.mk from the opus source tarball to your jni directory. Bringing these files into your Android.mk file will add different variables which you can use to include source files based on the type of the build. This is what the opus build process does as well.

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

#include the .mk files
include celt_sources.mk
include silk_sources.mk
include opus_sources.mk

MY_MODULE_DIR       := opus

LOCAL_MODULE        := $(MY_MODULE_DIR)

#fixed point sources
SILK_SOURCES += $(SILK_SOURCES_FIXED)

#ARM build
CELT_SOURCES += $(CELT_SOURCES_ARM)
SILK_SOURCES += $(SILK_SOURCES_ARM)
LOCAL_SRC_FILES     := \
$(CELT_SOURCES) $(SILK_SOURCES) $(OPUS_SOURCES)

LOCAL_LDLIBS        := -lm -llog

LOCAL_C_INCLUDES    := \
$(LOCAL_PATH)/include \
$(LOCAL_PATH)/silk \
$(LOCAL_PATH)/silk/fixed \
$(LOCAL_PATH)/celt

LOCAL_CFLAGS        := -DNULL=0 -DSOCKLEN_T=socklen_t -DLOCALE_NOT_USED -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
LOCAL_CFLAGS        += -Drestrict='' -D__EMX__ -DOPUS_BUILD -DFIXED_POINT=1 -DDISABLE_FLOAT_API -DUSE_ALLOCA -DHAVE_LRINT -DHAVE_LRINTF -O3 -fno-math-errno
LOCAL_CPPFLAGS      := -DBSD=1
LOCAL_CPPFLAGS      += -ffast-math -O3 -funroll-loops

#build a shared library not a static one like in Stanley's solution
include $(BUILD_SHARED_LIBRARY)

Here's my project structure for the opus library.

Project structure

Malaco answered 12/1, 2015 at 4:13 Comment(6)
It seems to work fine for ARM, but not so much for x86. Do you know how a makefile for x86 would look like?Selflove
What was the problem you were having?Malaco
Oh... It seems to work fine now, there was a problem with my code. I've compiled it together with opusfile and was reading the file using op_read(). It seems to work fine with _buf_size / 2 on x86, but it crashes with a segfault when I just pass _buf_size as jint (works fine on ARM). I noticed that in code written by another person, but I don't know the reason for this behavior. Why do I have to divide the buffer size by 2? The stream is mono, btw.Selflove
Passing 5760 (as recommended in the docs) as buffer size seems to work fine on both platforms.Selflove
@Malaco how to build it for x86 ?Batsman
@Selflove guys how did you managed to build for x86 or mips, when I adding TARGET_ARCH_ABI=mips, I still get the same SO file in directory opus-1.1.2/obj/local/armeabiBatsman

© 2022 - 2024 — McMap. All rights reserved.