Android mac 交叉编译与ffmpeg编译踩坑记
环境:
system: mac
NDK: android-ndk-r17c
Fffmpeg: ffmpeg-4.0.2
Cmake: 3.10.2
Gradle: 4.1.3
tips: 本文记录踩坑过程,具体细节如果感兴趣可以在评论区留言交流讨论!
mac 编译 (动态库(so))
首先来回顾一下,mac原始库是如何编译的
先来创建一个最简单的so文件:
只需要输入:
gcc -fPIC -shared main.c -o libmain.so
这样就可以将c代码编译成so库
mac 编译 (静态库(a))
静态库稍微麻烦一点,需要先生成 .o文件 [ gcc -fPIC -c main.c -o main.o ]
然后通过 .o文件生成 .a文件 [ar -r libmain.a main.o]
这样一来,就很轻易的编译成了静态库
android 交叉编译(动态库 arm-v7a)
这里就有疑问了, 为什么需要交叉编译 ?
因为刚才我们的代码是在mac上编译的 ,android并不认识, 要想android认识,就引出了交叉编译
就好像越南人来china买东西,必须吧越南盾换成人民币一样,必须要认识,才能够使用
在mac上,编译的方式是采用gcc
那么NDK也为我们提供了GCC
那么只能这样:
此时就会出现第一个坑:
找不到头文件
他的头文件都在这里, 只需要再次配置一下就OK
配置完成头文件后,就会有第二个坑:
找不到 asm/types.h
asm/types.h 在这里:
那么这个也需要配置一下:
最终编译, 还是不成功! .
最后还需要告诉gcc,当前编译的是什么android架构,当前编译的是v7a,那么只需要告诉gcc即可
最终编译:
完整命令:
/Users/ndk/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc --sysroot=$NDK/platforms/android-21/arch-arm -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/arm-linux-androideabi -fPIC -shared main.c -o libmain-ndk-32.so
这样一来, 打包成的so库就可以在android v7a的手机上运行了!
但是问题就来了,我不可能每次打包都这么麻烦,输这么长的指令,实在是太丑了, 所以说我将它配置成了全局变量,并写成了shell
配置全局变量:
# .bash_profile# NDK路径
export NDK="/Users/ndk/android-ndk-r17c"# NDK的命令操作 配置到 Linux环境变量里面去
export PATH=$NDK:$PATH# 下面是交叉编译相关
# ================= GCC ==================================
export NDK_GCC_x86="$NDK/toolchains/x86-4.9/prebuilt/darwin-x86_64/bin/i686-linux-android-gcc"export NDK_GCC_x64="$NDK/toolchains/x86_64-4.9/prebuilt/darwin-x86_64/bin/x86_64-linux-android-gcc"export NDK_GCC_arm="$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc"export NDK_GCC_arm_64="$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-gcc"# ================================== config 配置架构 ==================================
export NDK_CFIG_x86="--sysroot=$NDK/platforms/android-21/arch-x86 -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/i686-linux-android"export NDK_CFIG_x64="--sysroot=$NDK/platforms/android-21/arch-x86_64 -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/x86_64-linux-android"export NDK_CFIG_arm="--sysroot=$NDK/platforms/android-21/arch-arm -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/arm-linux-androideabi"export NDK_CFIG_arm_64="--sysroot=$NDK/platforms/android-21/arch-arm64 -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/aarch64-linux-android"
记得保存:
source ~/.bash_profile
Shell 命令
重新编译v7a 动态库(so):
android 交叉编译(动态库 arm-v8a)
v8a 的方式也是一样的,只是选择架构的时候选择v8a即可
为了在android端演示, 写一个util方法,里面有加减乘除函数, 通过JNI调用试试
可以直接采用sh命令来直接生成交叉编译后的so,
然后可以通过 file. XXX.so 来查看编译结果!
在android中测试打包后的so库:
创建一个C++工程, 配置CMake
记得在gradle中设置NDK架构:
最后直接来调用即可:
android 交叉编译 (静态库 arm-v7a)
静态库与动态库类似,只需要将linux的编译方式换成NDK的编译方式即可
Linux编译方式:
1. 生成.o文件
gcc -fPIC -c main.c -o main.o 2. 通过.o文件来构建 静态库
ar -r libmain.a main.o
先来找到NDK对应v7a架构的ar, 因为这里是通过ar来生成.a 文件的
同样为了方便使用,我把它配置成了全局变量,并且写成了shell命令
# .bash_profile# 下面是交叉编译相关
# ================= GCC ==================================
export NDK_GCC_x86="$NDK/toolchains/x86-4.9/prebuilt/darwin-x86_64/bin/i686-linux-android-gcc"export NDK_GCC_x64="$NDK/toolchains/x86_64-4.9/prebuilt/darwin-x86_64/bin/x86_64-linux-android-gcc"export NDK_GCC_arm="$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc"export NDK_GCC_arm_64="$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-gcc"# ================================== config 配置架构 ==================================
export NDK_CFIG_x86="--sysroot=$NDK/platforms/android-21/arch-x86 -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/i686-linux-android"export NDK_CFIG_x64="--sysroot=$NDK/platforms/android-21/arch-x86_64 -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/x86_64-linux-android"export NDK_CFIG_arm="--sysroot=$NDK/platforms/android-21/arch-arm -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/arm-linux-androideabi"export NDK_CFIG_arm_64="--sysroot=$NDK/platforms/android-21/arch-arm64 -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/aarch64-linux-android"# ================================== ar配置 用来生成静态库 ==================================
export NDK_AR_x86="$NDK/toolchains/x86-4.9/prebuilt/darwin-x86_64/bin/i686-linux-android-ar"export NDK_AR_x64="$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-ar"export NDK_AR_arm="$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ar"export NDK_AR_arm_64="$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-ar"
同样记得保存:
source ~/.bash_profile
shell命令:
#!bin/bashif [ $1 != 'v7a' ] && [ $1 != 'v8a' ]; thenecho "请选择平台 正确指令: sh $0 v7a或v8a(生成架构) xxx"exit 0
fiecho 您选择编译的平台是: $1if [ -e $NDK ]; thenif [ "v7a" = $1 ]; then# 交叉编译生成.o文件$NDK_GCC_arm $NDK_CFIG_arm -fPIC -c $2.c -o $2.oecho "生成$2.o中..."sleep 1$NDK_AR_arm rcs -o lib-$2.a $2.oelse $NDK_GCC_arm_64 $NDK_CFIG_arm_64 -fPIC -c $2.c -o $2.oecho "生成$2.o中..."sleep 1$NDK_AR_arm_64 rcs -o lib-$2.a $2.ofiecho "执行成功"elseecho "执行失败"
fi
使用:
只需要简单的:
sh static_build.sh v7a util 即可
android 交叉编译 (静态库 arm-v8a)
Arm-v8a编译静态库与动态库编译方式是一样的; 使用同一个shell命令
在android中使用:
使用效果和动态库类似,只是配置的方式不同
动态库与静态库的区别
- 静态库不会显示在apk中,在编译过程中,静态库的太吗都会copy到主库中
相反动态库则全会打包到apk中
ffmpeg编译(armv7a)
ffmpeg如何下载我就不说了, 写了一个脚本,我测试了很多次,只要NDK地址给对 80%都是可以编译成功的. 坑我全踩了 花了2天时间
build32.sh
#!/bin/bash# NDK_PATH
NDK_ROOT=/Users/ndk/android-ndk-r17c# NDK中gcc地址 arm-v7a地址
TOOLCHAIN=$NDK_ROOT/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64FLAGS="-isystem $NDK_ROOT/sysroot/usr/include/arm-linux-androideabi -D__ANDROID_API__=21 -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb -Wa,--noexecstack -Wformat -Werror=format-security -O0 -fPIC"
INCLUDES=" -isystem $NDK_ROOT/sources/android/support/include"# 1.定义编译后,所存放的目录
PREFIX=./android/arm32# 2.--enable-small 优化大小 非常重要,必须优化才行的哦
# 3.--disable-programs 不编译ffmpeg程序(命令行工具),我们是需要获取静态、动态库
# 4.--disable-avdevice 关闭avdevice模块,此模块在android中无用
# 5.--disable-encoders 关闭所有编码器(播放不需要编码)
# 6.--disable-muxers 关闭所有复用器(封装器),不需要生成mp4这样的文件,所有关闭
# 7.--disable-filters 关闭所有滤镜
# 8.--enable-cross-compile 开启交叉编译(ffmpeg是跨平台的,注意:并不是所有库都有这么happy的选项)
# 9.--cross-prefix 看右边的值就知道是干嘛的,gcc的前缀..
# 10.disable-shared / enable-static 这个不写也可以,默认就是这样的,(代表关闭动态库,开启静态库)
# 11.--sysroot
# 12.--extra-cflags 会传给gcc的参数
# 13.--arch --target-os 编译版本 v7a=arm; v8a=arm64;./configure \
--prefix=$PREFIX \
--enable-small \
--disable-programs \
--disable-avdevice \
--disable-encoders \
--disable-muxers \
--disable-filters \
--enable-cross-compile \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--disable-shared \
--enable-static \
--sysroot=$NDK_ROOT/platforms/android-21/arch-arm \
--extra-cflags="$FLAGS $INCLUDES" \
--extra-cflags="-isysroot $NDK_ROOT/sysroot/" \
--arch=arm \
--target-os=androidmake cleanmake install
如果你想编译v7a的,直接在这里创建build32.sh 吧NDK路径换成你自己的
最好是手动创建好编译后存放的目录 [android/arm32]
编译大约3-5分钟就OK了
sh build32.sh
切记,编译时候切换到root环境编译
ffmpeg编译(armv8a)
还是同样的套路,写一个shell脚本:
build64.sh
#!/bin/bash# NDK位置
NDK_ROOT=/Users/ndk/android-ndk-r17c# GCC
TOOLCHAIN=$NDK_ROOT/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64FLAGS="-isystem $NDK_ROOT/sysroot/usr/include/aarch64-linux-android -D__ANDROID_API__=21 -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -march=armv8-a -mcpu=generic -Wa,--noexecstack -Wformat -Werror=format-security -O0 -fPIC"
INCLUDES=" -isystem $NDK_ROOT/sources/android/support/include"# 1.定义编译后,所存放的目录
PREFIX=./android/arm64# 2.--enable-small 优化大小 非常重要,必须优化才行的哦
# 3.--disable-programs 不编译ffmpeg程序(命令行工具),我们是需要获取静态、动态库
# 4.--disable-avdevice 关闭avdevice模块,此模块在android中无用
# 5.--disable-encoders 关闭所有编码器(播放不需要编码)
# 6.--disable-muxers 关闭所有复用器(封装器),不需要生成mp4这样的文件,所有关闭
# 7.--disable-filters 关闭所有滤镜
# 8.--enable-cross-compile 开启交叉编译(ffmpeg是跨平台的,注意:并不是所有库都有这么happy的选项)
# 9.--cross-prefix 看右边的值就知道是干嘛的,gcc的前缀..
# 10.disable-shared / enable-static 这个不写也可以,默认就是这样的,(代表关闭动态库,开启静态库)
# 11.--sysroot
# 12.--extra-cflags 会传给gcc的参数
# 13.--arch --target-os ./configure \
--prefix=$PREFIX \
--enable-small \
--disable-programs \
--disable-avdevice \
--disable-encoders \
--disable-muxers \
--disable-filters \
--enable-cross-compile \
--cross-prefix=$TOOLCHAIN/bin/aarch64-linux-android- \
--disable-shared \
--enable-static \
--sysroot=$NDK_ROOT/platforms/android-21/arch-arm64 \
--extra-cflags="$FLAGS $INCLUDES" \
--extra-cflags="-isysroot $NDK_ROOT/sysroot/" \
--arch=arm64 \
--target-os=androidmake cleanmake install
只需要吧NDK地址换成自己的即可:
sh build64.sh
最终编译后,这就是编译结果:
导入到android中使用:
调用看看结果:
我将本篇用到的所有命令已经全局配置放到项目中了,有需要的亲下载源码自取!
完整代码
原创不易,您的点赞就是对我最大的支持!
往期文章:
-
android kotlin 协程(六) 源码浅析
-
android kotlin 协程(五) suspend与continuation
-
android kotlin 协程(四) 协程间的通信
-
android 自定义View:仿QQ拖拽效果
-
android MD 进阶[五] CoordinatorLayout 从源码到实战…