MPG123 适配 arm64-v8a 架构

Github 上的 android-mp3decoders 开源库利用 NDK-build 完成了对 MPG123 移植到 Android 平台的工作。遗憾的是它仅做到了对x86, x86_64, armeabi, armeabi-v7a 四个架构的支持。且 ARM 仅支持 32 位的 armeabiarmeabi-v7a。目前(2019)而言,绝大部分 Android 手机均以 arm64-v8a 作为基准架构起步。它作为 64 位架构,性能自然比 32 位的要强不少。对于 MPG123 此类编解码工作的库而言,提升更是明显。(我自己测试,64位调用 readFrame() 读取耗时降低了 22.3 %)

所以这篇文章会简述我在为 libmpg123 适配 arm64-v8a 的整个过程,希望能给你一些参考。

前置知识

基于 NDK-build 的 NDK 项目,使用Android.mk文件来进行 Native 侧的配置。

建议你先阅读官方的文档,对此有些基础了解才是最好的。

我会将常用的配置变量含义注释在后续文章中,以便阅读。

现有的 Android.mk 解析

我们可以先看上面那个库已有的Android.mk文件结构是如何的:

# 此变量表示源文件在开发树中的位置
# 在这行代码中,编译系统提供的宏函数 my-dir 将返回当前目录(Android.mk文件本身所在的目录)的路径
LOCAL_PATH := $(call my-dir)

# CLEAR_VARS 变量指向一个特殊的 GNU Makefile,后者会清除许多 LOCAL_XXX 变量
# 在描述每个模块之前,必须声明(重新声明)此变量
include $(CLEAR_VARS)

# 可以使用此可选变量指定相对于 NDK root 目录的路径列表,以便在编译所有源文件(C、C++ 和 Assembly)时添加
# 到 include 搜索路径
LOCAL_C_INCLUDES := $(LOCAL_PATH)

# LOCAL_MODULE 变量存储您要编译的模块的名称
# 编译系统在生成最终共享库文件时,会对您分配给 LOCAL_MODULE 的名称自动添加正确的前缀和后缀
# 如果你模块名如下为 mpg123,那么编译系统会自动给你加上 lib 前缀;如果你已有该前缀,则不会自动帮你添加
LOCAL_MODULE     := mpg123

LOCAL_ARM_MODE   := arm
LOCAL_LDLIBS     := -llog

# LOCAL_SRC_FILES 变量必须包含要编译到模块中的 C 和/或 C++ 源文件列表
# 下面是所有架构都会用到的公共 c 文件,所以直接添加进来
LOCAL_SRC_FILES :=     podax_MPG123.c
LOCAL_SRC_FILES +=  libmpg123.c
LOCAL_SRC_FILES +=  compat.c
LOCAL_SRC_FILES +=  frame.c
LOCAL_SRC_FILES +=  id3.c
LOCAL_SRC_FILES +=  format.c
LOCAL_SRC_FILES +=  stringbuf.c
LOCAL_SRC_FILES +=  readers.c
LOCAL_SRC_FILES +=  icy.c icy2utf8.c
LOCAL_SRC_FILES +=  index.c
LOCAL_SRC_FILES +=  layer1.c layer2.c layer3.c
LOCAL_SRC_FILES +=  parse.c
LOCAL_SRC_FILES +=  optimize.c
LOCAL_SRC_FILES +=  synth.c synth_8bit.c
LOCAL_SRC_FILES +=  ntom.c
LOCAL_SRC_FILES +=  dct64.c
LOCAL_SRC_FILES +=  equalizer.c
LOCAL_SRC_FILES +=  tabinit.c
LOCAL_SRC_FILES +=  feature.c

# 下面是不同架构编译时,添加的不同文件和编译参数;这些配置都赋予 LOCAL_CFLAGS、LOCAL_SRC_FILES 变量
# armeabi 架构
ifeq ($(TARGET_ARCH_ABI),armeabi)
LOCAL_CFLAGS     := -DACCURATE_ROUNDING \
                    -DOPT_ARM \
                    -DREAL_IS_FIXED \
                    -DNO_REAL \
                    -DNO_32BIT \
                    -DHAVE_STRERROR \
                    -DASMALIGN_BYTE \
                    -Wno-int-to-pointer-cast \
                    -Wno-pointer-to-int-cast \
                    -ffast-math -O3
LOCAL_SRC_FILES +=  synth_arm.S synth_arm_accurate.S
endif

# armeabi-v7a 架构
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
#LOCAL_CFLAGS     := -DACCURATE_ROUNDING \
#                    -DOPT_NEON \
#                    -DHAVE_STRERROR \
#                    -Wno-int-to-pointer-cast \
#                    -Wno-pointer-to-int-cast \
#                    -ffast-math -O3
#LOCAL_SRC_FILES +=  synth_real.c synth_s32.c
#LOCAL_SRC_FILES +=  synth_neon.S synth_neon_accurate.S synth_neon_float.S synth_neon_s32.S
#LOCAL_SRC_FILES +=  dct36_neon.S dct64_neon_float.S synth_stereo_neon_accurate.S synth_stereo_neon_float.S synth_stereo_neon_s32.S
LOCAL_CFLAGS     := -DACCURATE_ROUNDING \
                    -DOPT_ARM \
                    -DREAL_IS_FIXED \
                    -DNO_REAL \
                    -DNO_32BIT \
                    -DHAVE_STRERROR \
                    -DASMALIGN_BYTE \
                    -Wno-int-to-pointer-cast \
                    -Wno-pointer-to-int-cast \
                    -ffast-math -O3
LOCAL_SRC_FILES +=  synth_arm.S synth_arm_accurate.S
endif

# x86 架构
ifeq ($(TARGET_ARCH_ABI),x86)
LOCAL_CFLAGS     := -DACCURATE_ROUNDING \
                    -DHAVE_STRERROR \
                    -DOPT_SSE \
                    -Wno-int-to-pointer-cast \
                    -Wno-pointer-to-int-cast \
                    -ffast-math -O3
LOCAL_SRC_FILES +=  synth_real.c synth_s32.c
LOCAL_SRC_FILES +=  synth_sse.S synth_sse_accurate.S synth_sse_float.S synth_sse_s32.S
LOCAL_SRC_FILES +=  synth_stereo_sse_accurate.S synth_stereo_sse_float.S synth_stereo_sse_s32.S
LOCAL_SRC_FILES +=  dct64_i386.c dct36_sse.S dct64_sse.S dct64_sse_float.S
LOCAL_SRC_FILES +=  tabinit_mmx.S
endif

# x86_64 架构

ifeq ($(TARGET_ARCH_ABI),x86_64)
LOCAL_CFLAGS     := -DACCURATE_ROUNDING \
                    -DHAVE_STRERROR \
                    -DOPT_X86_64 \
                    -Wno-int-to-pointer-cast \
                    -Wno-pointer-to-int-cast \
                    -ffast-math -O3
LOCAL_SRC_FILES +=  synth_real.c synth_s32.c
LOCAL_SRC_FILES +=  getcpuflags_x86_64.S
LOCAL_SRC_FILES +=  synth_x86_64.S synth_x86_64_s32.S synth_x86_64_accurate.S synth_x86_64_float.S
LOCAL_SRC_FILES +=  synth_stereo_x86_64_float.S synth_stereo_x86_64.S synth_stereo_x86_64_s32.S synth_stereo_x86_64_accurate.S
LOCAL_SRC_FILES +=  dct36_x86_64.S dct64_x86_64.S dct64_x86_64_float.S
endif

# 最后一行帮助系统将所有内容连接到一起
include $(BUILD_SHARED_LIBRARY)

文件结构还是非常清晰的,首先定义模块的一些参数,例如输出模块名、搜索路径等。

再之后是对不同架构添加不同对编译参数和源文件。这一步是我们移植适配时要做的事情。

在上述代码中,我们已经有了armeabi-v7aarmeabix86x86_64四种架构,我们要做的就是加上arm64-v8a 架构所需的编译选项和代码文件。

不过逻辑是这样,但是我们还要考虑实际的情况。这个库的最后一次提交是在 2015 年,当时的 MPG123 还是 1.22 版本。因为源码变动可能会导致现有的Android.mk 完全不可用,所以这里仅是对此做一些分析。不会使用这份 Android.mk 文件。

简单的复制

辗转之下,我在 Github 上找到了另外一份较新的Android.mk文件。SDL_mixer 是一个简单的 16bit 音频混响器。里面正好使用 MPG123 作为默认的解码器。

我们来看一下他的 Android.mk 文件是如何写的:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := libmpg123

LOCAL_C_INCLUDES := $(LOCAL_PATH)/android \
                    $(LOCAL_PATH)/src \
                    $(LOCAL_PATH)/src/compat \
                    $(LOCAL_PATH)/src/libmpg123 \

DECODER_CFLAGS_NEON := -DOPT_NEON -DREAL_IS_FLOAT

DECODER_SRC_NEON := \
    src/libmpg123/stringbuf.c \
    src/libmpg123/icy.c \
    src/libmpg123/icy2utf8.c \
    src/libmpg123/ntom.c \
    src/libmpg123/synth.c \
    src/libmpg123/synth_8bit.c \
    src/libmpg123/layer1.c \
    src/libmpg123/layer2.c \
    src/libmpg123/layer3.c \
    src/libmpg123/dct36_neon.S \
    src/libmpg123/dct64_neon_float.S \
    src/libmpg123/synth_neon_float.S \
    src/libmpg123/synth_neon_s32.S \
    src/libmpg123/synth_stereo_neon_float.S \
    src/libmpg123/synth_stereo_neon_s32.S \
    src/libmpg123/dct64_neon.S \
    src/libmpg123/synth_neon.S \
    src/libmpg123/synth_stereo_neon.S \
    src/libmpg123/synth_s32.c \
    src/libmpg123/synth_real.c \
    src/libmpg123/feature.c \

DECODER_CFLAGS_NEON64 := -DOPT_MULTI -DOPT_GENERIC -DOPT_GENERIC_DITHER -DOPT_NEON64 -DREAL_IS_FLOAT

DECODER_SRC_NEON64 := \
    src/libmpg123/stringbuf.c \
    src/libmpg123/icy.c \
    src/libmpg123/icy2utf8.c \
    src/libmpg123/ntom.c \
    src/libmpg123/synth.c \
    src/libmpg123/synth_8bit.c \
    src/libmpg123/layer1.c \
    src/libmpg123/layer2.c \
    src/libmpg123/layer3.c \
    src/libmpg123/dct36_neon64.S \
    src/libmpg123/dct64_neon64_float.S \
    src/libmpg123/synth_neon64_float.S \
    src/libmpg123/synth_neon64_s32.S \
    src/libmpg123/synth_stereo_neon64_float.S \
    src/libmpg123/synth_stereo_neon64_s32.S \
    src/libmpg123/dct64_neon64.S \
    src/libmpg123/synth_neon64.S \
    src/libmpg123/synth_stereo_neon64.S \
    src/libmpg123/synth_s32.c \
    src/libmpg123/synth_real.c \
    src/libmpg123/dither.c \
    src/libmpg123/getcpuflags_arm.c \
    src/libmpg123/check_neon.S \
    src/libmpg123/feature.c \

# Unfortunately the assembly isn't relocatable so doesn't work on modern
# Android devices
DECODER_CFLAGS_X86 := -DOPT_GENERIC -DREAL_IS_FLOAT
DECODER_CFLAGS_X86_ASM := -DOPT_MULTI -DOPT_GENERIC -DOPT_GENERIC_DITHER -DOPT_I386 -DOPT_I586 -DOPT_I586_DITHER -DOPT_MMX -DOPT_3DNOW -DOPT_3DNOW_VINTAGE -DOPT_3DNOWEXT -DOPT_3DNOWEXT_VINTAGE -DOPT_SSE -DOPT_SSE_VINTAGE -DREAL_IS_FLOAT

DECODER_SRC_X86 := \
    src/libmpg123/feature.c \
    src/libmpg123/icy2utf8.c \
    src/libmpg123/icy.c \
    src/libmpg123/layer1.c \
    src/libmpg123/layer2.c \
    src/libmpg123/layer3.c \
    src/libmpg123/ntom.c \
    src/libmpg123/stringbuf.c \
    src/libmpg123/synth_8bit.c \
    src/libmpg123/synth.c \
    src/libmpg123/synth_real.c \
    src/libmpg123/synth_s32.c \
    src/libmpg123/dither.c \

DECODER_SRC_X86_ASM := \
    src/libmpg123/stringbuf.c \
    src/libmpg123/icy.c \
    src/libmpg123/icy2utf8.c \
    src/libmpg123/ntom.c \
    src/libmpg123/synth.c \
    src/libmpg123/synth_8bit.c \
    src/libmpg123/layer1.c \
    src/libmpg123/layer2.c \
    src/libmpg123/layer3.c \
    src/libmpg123/synth_s32.c \
    src/libmpg123/synth_real.c \
    src/libmpg123/dct64_i386.c \
    src/libmpg123/synth_i586.S \
    src/libmpg123/synth_i586_dither.S \
    src/libmpg123/dct64_mmx.S \
    src/libmpg123/tabinit_mmx.S \
    src/libmpg123/synth_mmx.S \
    src/libmpg123/synth_3dnow.S \
    src/libmpg123/dct64_3dnow.S \
    src/libmpg123/equalizer_3dnow.S \
    src/libmpg123/dct36_3dnow.S \
    src/libmpg123/dct64_3dnowext.S \
    src/libmpg123/synth_3dnowext.S \
    src/libmpg123/dct36_3dnowext.S \
    src/libmpg123/dct64_sse_float.S \
    src/libmpg123/synth_sse_float.S \
    src/libmpg123/synth_stereo_sse_float.S \
    src/libmpg123/synth_sse_s32.S \
    src/libmpg123/synth_stereo_sse_s32.S \
    src/libmpg123/dct36_sse.S \
    src/libmpg123/dct64_sse.S \
    src/libmpg123/synth_sse.S \
    src/libmpg123/getcpuflags.S \
    src/libmpg123/dither.c \
    src/libmpg123/feature.c \

DECODER_CFLAGS_X64 := -DOPT_MULTI -DOPT_X86_64 -DOPT_GENERIC -DOPT_GENERIC_DITHER -DREAL_IS_FLOAT -DOPT_AVX

DECODER_SRC_X64 := \
    src/libmpg123/stringbuf.c \
    src/libmpg123/icy.c \
    src/libmpg123/icy.h \
    src/libmpg123/icy2utf8.c \
    src/libmpg123/icy2utf8.h \
    src/libmpg123/ntom.c \
    src/libmpg123/synth.c \
    src/libmpg123/synth.h \
    src/libmpg123/synth_8bit.c \
    src/libmpg123/synth_8bit.h \
    src/libmpg123/layer1.c \
    src/libmpg123/layer2.c \
    src/libmpg123/layer3.c \
    src/libmpg123/synth_s32.c \
    src/libmpg123/synth_real.c \
    src/libmpg123/dct36_x86_64.S \
    src/libmpg123/dct64_x86_64_float.S \
    src/libmpg123/synth_x86_64_float.S \
    src/libmpg123/synth_x86_64_s32.S \
    src/libmpg123/synth_stereo_x86_64_float.S \
    src/libmpg123/synth_stereo_x86_64_s32.S \
    src/libmpg123/synth_x86_64.S \
    src/libmpg123/dct64_x86_64.S \
    src/libmpg123/synth_stereo_x86_64.S \
    src/libmpg123/dither.c \
    src/libmpg123/dither.h \
    src/libmpg123/getcpuflags_x86_64.S \
    src/libmpg123/dct36_avx.S \
    src/libmpg123/dct64_avx_float.S \
    src/libmpg123/synth_stereo_avx_float.S \
    src/libmpg123/synth_stereo_avx_s32.S \
    src/libmpg123/dct64_avx.S \
    src/libmpg123/synth_stereo_avx.S \
    src/libmpg123/feature.c

DECODER_CFLAGS_MIPS := -DOPT_GENERIC -DREAL_IS_FLOAT

DECODER_SRC_MIPS := \
    src/libmpg123/stringbuf.c \
    src/libmpg123/icy.c \
    src/libmpg123/icy2utf8.c \
    src/libmpg123/ntom.c \
    src/libmpg123/synth.c \
    src/libmpg123/synth_8bit.c \
    src/libmpg123/layer1.c \
    src/libmpg123/layer2.c \
    src/libmpg123/layer3.c \
    src/libmpg123/synth_s32.c \
    src/libmpg123/synth_real.c \
    src/libmpg123/feature.c

ifeq ($(TARGET_ARCH_ABI),armeabi)
DECODER_CFLAGS := $(DECODER_CFLAGS_NEON)
DECODER_SRC := $(DECODER_SRC_NEON)
endif
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
DECODER_CFLAGS := $(DECODER_CFLAGS_NEON)
DECODER_SRC := $(DECODER_SRC_NEON)
endif
ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
DECODER_CFLAGS := $(DECODER_CFLAGS_NEON64)
DECODER_SRC := $(DECODER_SRC_NEON64)
endif
ifeq ($(TARGET_ARCH_ABI),x86)
DECODER_CFLAGS := $(DECODER_CFLAGS_X86)
DECODER_SRC := $(DECODER_SRC_X86)
endif
ifeq ($(TARGET_ARCH_ABI),x86_64)
DECODER_CFLAGS := $(DECODER_CFLAGS_X64)
DECODER_SRC := $(DECODER_SRC_X64)
endif
ifeq ($(TARGET_ARCH_ABI),mips)
DECODER_CFLAGS := $(DECODER_CFLAGS_MIPS)
DECODER_SRC := $(DECODER_SRC_MIPS)
endif
ifeq ($(TARGET_ARCH_ABI),mips64)
DECODER_CFLAGS := $(DECODER_CFLAGS_MIPS)
DECODER_SRC := $(DECODER_SRC_MIPS)
endif

LOCAL_CFLAGS := $(DECODER_CFLAGS)

LOCAL_SRC_FILES := \
    src/libmpg123/parse.c \
    src/libmpg123/frame.c \
    src/libmpg123/format.c \
    src/libmpg123/dct64.c \
    src/libmpg123/equalizer.c \
    src/libmpg123/id3.c \
    src/libmpg123/optimize.c \
    src/libmpg123/readers.c \
    src/libmpg123/tabinit.c \
    src/libmpg123/libmpg123.c \
    src/libmpg123/index.c \
    src/compat/compat_str.c \
    src/compat/compat.c \
    $(DECODER_SRC)


LOCAL_EXPORT_C_INCLUDES += $(LOCAL_C_INCLUDES)

include $(BUILD_SHARED_LIBRARY)

经过文章开头的解析,相信你已经能理解上述描述文件的大致架构。DECODER_SRC_NEON64实际上就是arm64-v8a这个架构的配置项。如你有兴趣,也可以看看 libmpg123 的源码。

结尾

此文仅作对现有 Android.mk 文件的一些分析,以及对不同架构直接适配的流程介绍。并不涉及具体的源码分析。(因为我太菜...hhh

添加新评论

评论列表