【问题标题】:Makefile needed for compiling the Opus Codec for Android为 Android 编译 Opus 编解码器所需的 Makefile
【发布时间】:2013-07-26 00:20:06
【问题描述】:

我正在尝试编译 opus 音频编解码器 (http://www.opus-codec.org/downloads/) 以用于 Android 应用程序。我正在使用 Android NDK(版本 6)来编译我的库。到目前为止,我必须为我的应用程序编译的本机 C 库非常简单,我已经能够将我的 Android.mk 文件基于 jni,主要基于教程或其他示例。但是,Opus 的编译看起来要复杂一些。 tar.gz 存档包含一个用于为 Windows 编译库的解决方案文件,它还包含一些用于标准 Unix 实现的 Makefile,但将这些文件转换为 Android.mk makefile 以供 Android NDK 使用有点挑战。

我已经搜索过,但一直无法找到用于编译 libopus 的 Android makefile 的在线版本。有人可以将我链接到这样的makefile吗?或者,我可能会遗漏一些更简单的东西?是否可以使用 Automake 或某种转换器从 tar.gz 中已包含的 unix makefile 为我生成 Android.mk 文件?

【问题讨论】:

    标签: android-ndk arm opus


    【解决方案1】:

    编辑:只需使用上面接受的答案的解决方案,原始链接已失效,而且它与我回答时接受的答案基本相同。

    这是一个,可能有点过时了(它使用了作品 0.9.14):

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

    不过,在编译完库之后,您需要编写一些 JNI 包装器...

    【讨论】:

    • 谢谢,这帮助我走上了正轨。在使用您链接到的 makefile 的 libopus 部分时,我遇到了一些链接器错误,因此我不得不更改它。我赞成,因为该链接有助于表明 celt、silk 和 opus 源可以在单个编译中链接,而不必先单独编译到不同的模块中。
    • 谁知道如何从Android调用这些JNI接口?我想录语音,但是不知道怎么用opus函数录语音保存到文件。
    • @brucenan 您想使用the Java API 录制音频样本,然后将录制的样本数组传递到C 领域进行编码。对于这部分,您确实需要自己编写包装器,因为很明显 Opus 目前没有任何 Java 支持。希望根据libopus API documentation 编写这样的包装器很简单;一定要检查出来。然后,在编码完成后,就可以在Java中完全完成文件的写入了。希望这会有所帮助!
    • 链接已失效。强烈建议不要只提供链接。
    【解决方案2】:

    以下是最终为我工作的 Android.mk 生成文件。我希望这也可以在将来对其他人有所帮助。请注意,在 Opus 存档中包含的 unix makefile 中,是否使用固定或浮动丝绸源的决定被定义为“ifdef”,而这里我使用的是“固定”。 (使用“float”很简单 - 只需将路径“silk/fixed”更新为指向“silk/float”并更新 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)
    

    【讨论】:

    • 为什么不把它回馈给 Opus 人呢?
    • 嘿斯坦利,这有没有被添加到官方作品版本中?这个 makefile 是用于哪个版本的作品?你有更新 v1.1 的 makefile 吗?
    【解决方案3】:

    感谢@Stanley,通过稍微调整他的解决方案,我能够成功地创建一个共享库。我还不知道拥有静态库与共享库相比是否有优势。我只知道我需要一个用于 JNI 包装器的共享库。这就是我所拥有的。注意定点的编译器标志。没有这些,定点模式的编译将失败。

    首先将 celt_sources.mksilk_sources.mkopus_sources.mk 从 opus 源 tarball 复制到您的 jni 目录。将这些文件带入您的Android.mk 文件将添加不同的变量,您可以使用这些变量来根据构建的类型包含源文件。这也是 opus 构建过程所做的。

    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)
    

    这是我的作品库的项目结构。

    【讨论】:

    • 它似乎在 ARM 上工作得很好,但对 x86 来说就不行了。你知道 x86 的 makefile 是什么样子的吗?
    • 您遇到了什么问题?
    • 哦...现在似乎工作正常,我的代码有问题。我已经将它与 opusfile 一起编译,并正在使用 op_read() 读取文件。它似乎在 x86 上与 _buf_size / 2 一起工作正常,但是当我将 _buf_size 作为 jint 传递时它会因段错误而崩溃(在 ARM 上工作正常)。我注意到在另一个人编写的代码中,但我不知道这种行为的原因。为什么我必须将缓冲区大小除以 2?流是单声道的,顺便说一句。
    • 通过 5760(如文档中的建议)作为缓冲区大小似乎在两个平台上都可以正常工作。
    • @praneetloke 如何为 x86 构建它?
    【解决方案4】:

    重要修改 图片可能不是最新的,但该作品已在以下版本上进行了测试:

    • 作品1.1
    • 作品-1.1.2

    @praneetloke 解决方案的更新版本(适用于 opus-1.1.2)。一种带有一些附加功能的不同方法。 首先下面是我要使用的结构(我打算使用更多的库,所以我将 opus 放在它自己的子文件夹中。)

    其次,我在文件夹 opus-1.1.2 中有一个根 Android.mk 文件和另一个。

    这是根Android.mk 文件:

    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)
    
    opus-1.1.2 文件夹内的

    Android.mk 文件如下:

    #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)
    

    这是 opus-1.1.2 文件夹中的样子:

    对原始来源的唯一改动是添加了一个Android.mk 文件。没有删除任何内容。

    这样我就可以像使用单独的库一样使用和更新 opus。


    这里是那些想要在 android 中编译和使用 opus 的人的附加功能;包装器来源,部分:

    #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 文件(可选)

    APP_ABI := all  # mips, armeabi, armeabi-v7a, x86 etc. builds
    

    【讨论】:

    • 非常有帮助,谢谢!我也有一个类似于你的和上面的用于为 armeabi 构建 libopus.so 的。但是,我想知道您是否也考虑过构建 armeabi-v7a 版本?
    • @leenephi 我不确定,但是Application.mk 中的APP_ABI := all 会生成一堆.so 文件。如果你只想要 armeabi 和 armeabi-v7a,那么你可以使用APP_ABI := armeabi armeabi-v7a。我还没有测试过其他 .so 文件。
    • 把我拖出去!谢谢你!~
    • #include .mk 文件包括 celt_sources.mk 包括 Silk_sources.mk 包括 opus_sources.mk 你从哪里得到这个 mk 文件?
    猜你喜欢
    • 2016-10-30
    • 2017-05-24
    • 1970-01-01
    • 2020-08-14
    • 1970-01-01
    • 2020-06-20
    • 1970-01-01
    • 2016-02-08
    • 1970-01-01
    相关资源
    最近更新 更多