安卓新的NDK编译工具替换成了clang,所以之前的编译脚本需要做相应调整,本文总结了编译过程中遇到的诸多问题,如果编译过程中遇到问题,或编出的库不符合需求,可参考后面的问题总结,希望能帮到你
编译环境:win10 + msys2
DNK版本:android-ndk-21.2.6472646/ android-ndk-21.0.6113669/android-ndk-r20
ffmpeg版本: ffmpeg3.3.9/ffmpeg4.3.1
使用本文的编译脚本,测试了linux和window10,android-ndk-21.2.6472646/ android-ndk-21.0.6113669/android-ndk-r20,编译了 ffmpeg3.3.9/ffmpeg4.3.1,都能成功编译
网上许多安装类unix运行环境都需要,要么安装速度很慢,本文使用国内源不需要,如果你安装了其他的类unix运行环境(如cywin, MinGW),理论上应该是可以直接进入后面的编译步骤的,不过我没尝试过
在清华大学镜像网站下载并安装 msys2 最新版本 ,此外还有中科大,北京理工大学, 上海交大等镜像源,64位系统建议下载x86_64版本,安装一路点击下一步就行
将默认源地址替换为清华大学镜像地址,类似于Ubuntu设置镜像源,这样安装其他工具速度会快很多,配置步骤如下
见清华大学镜像网站msys2帮助文档,我下载的最新版本默认有许多国内镜像源在配置文件中,其他镜像源的没有试过,建议把国内的源放前面,我的配置顺序如下
#清华大学
Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/i686/
#中科大
Server = http://mirrors.ustc.edu.cn/msys2/mingw/i686/
#北京理工大学
Server = http://mirror.bit.edu.cn/msys2/mingw/i686/
Server = https://mirror.selfnet.de/msys2/mingw/i686/
#上海交大
Server = https://mirrors.sjtug.sjtu.edu.cn/msys2/mingw/i686/
Server = https://mirror.yandex.ru/mirrors/msys2/mingw/i686/
Server = https://sourceforge.net/projects/msys2/files/REPOS/MINGW/i686/
Server = https://www2.futureware.at/~nickoe/msys2-mirror/mingw/i686/
配置完打开安装路径下msys2.exe(或按win键在程序列表中打开),执行 pacman -Sy
刷新软件包数据即可。
然后执行安装路径下msys2_shell.cmd打开msys终端,执行"pacman -Syuu"命令,中途可能会提示关闭终端,此时关闭msys,重新 打 开,再次执行"pacman -Syuu"命令,更新软件包
这里顺便附一个pacman包管理工具命令开始编译
在msys2终端执行pacman -S make,安装make工具
pacman -S make
msys2安装目录下打开mingw64.exe, 切换到ffmpeg源码路径,windows D:盘被挂载为/d/,以此类推,可用 df -h命令确认,不过盘符占用比较多时相应可能比较慢
新建一个buid.sh,将以下内容粘贴到文本,根据需求替换前三个变量,如果使用windows文本编辑器,切记保存为unix格式,否则可能会由于结束符不一致问题,报有语法错误,脚本加了一些辅助调试信息,和交互选项,所以比较长,将脚本保存到ffmpeg源码路径下,执行./build 5 ,可编译armeabi-v7a , arm64-v8a, x86, x86_64 所有动态和静态库,如果只编译某个版本的库执行./build可以看到编译选择,选择相应版本编号即可,输出路径下build.log可跟踪编译情况
#!/bin/bash
#ffmpeg 版本
FFMPEG_VERSION=4.3.1
#NDK 路径, 根据情况替换
export NDK=/d/Green_Sorft/Android/Sdk/ndk/21.2.6472646
#编译输出路径 根据需求替换替换,默认在脚本上级目录ffmpeg_for_android目录下
OUT_DIR="`dirname $PWD`/ffmpeg_for_android/ffmpeg_${FFMPEG_VERSION}_lib"
#编译工具路径,linux版本需根据情况替换
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/windows-x86_64
export TMPDIR=$OUT_DIR/tmp
mkdir -p $TMPDIR
#将ndk下yasm所在路径添加到PATH环境变量
PATH=$PATH:$TOOLCHAIN/bin
#创建/清空编译日志
> $OUT_DIR/build.log
function build_start() {
echo "build FFmpeg for $ABI"
./configure \
--prefix=$OUT_DIR/$ABI \
--disable-gpl \
--enable-shared \
--enable-static \
--disable-doc \
--disable-programs \
--disable-avdevice \
--disable-doc \
--disable-symver \
--cross-prefix=$CROSS_PREFIX \
--target-os=android \
--enable-cross-compile \
--arch=$ARCH \
--cpu=$CPU \
--cc=$CC \
--cxx=$CXX \
--sysroot=$TOOLCHAIN/sysroot \
--extra-cflags="-Os -fPIC $OPTIMIZE_CFLAGS" \
configRet=$?
echo "* config for $ABI ret:$configRet" >> $OUT_DIR/build.log
if [ $configRet -eq 0 ]; then
echo "start make $ABI"
make clean
make -j64
makeRet=$?
echo "* make for $ABI return:$makeRet" >> $OUT_DIR/build.log
if [ $makeRet -eq 0 ]; then
make install
mv_libs
fi
else
echo "The config of FFmpeg for $ABI is failded"
echo "* config for $ABI failed !!" >> $OUT_DIR/build.log
fi
echo "The Compilation of FFmpeg for $ABI is completed"
echo "*************************" >> $OUT_DIR/build.log
}
function mv_libs() {
mkdir -p $OUT_DIR/shared-libs/$ABI
mv $OUT_DIR/$ABI/lib/*.so $OUT_DIR/shared-libs/$ABI
mkdir -p $OUT_DIR/static-libs/$ABI
mv $OUT_DIR/$ABI/lib/*.a $OUT_DIR/static-libs/$ABI
cp -rf $OUT_DIR/$ABI/include $OUT_DIR/
cp -rf $OUT_DIR/$ABI/share/ffmpeg/examples $OUT_DIR/
rm -rf $OUT_DIR/$ABI/
}
#armv8-a aarch64
function build_arm64() {
API=21
ABI=arm64-v8a
ARCH=arm64
CPU=armv8-a
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
OPTIMIZE_CFLAGS="-march=$CPU"
echo "* buidl for $ABI start !!" >> $OUT_DIR/build.log
build_start
echo "* buidl for $ABI end !!" >> $OUT_DIR/build.log
echo "*************************" >> $OUT_DIR/build.log
}
#armv7-a
function build_arm() {
API=16
ABI=armeabi-v7a
ARCH=arm
CPU=armv7-a
CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang
CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++
CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi-
OPTIMIZE_CFLAGS="-march=$CPU -mfloat-abi=softfp -mfpu=vfp -marm"
echo "* buidl for $ABI start !!" >> $OUT_DIR/build.log
build_start
echo "* buidl for $ABI end !!" >> $OUT_DIR/build.log
echo "*************************" >> $OUT_DIR/build.log
}
#x86 i686
function build_x86() {
API=16
ABI=x86
ARCH=x86
CPU=x86
CC=$TOOLCHAIN/bin/i686-linux-android$API-clang
CXX=$TOOLCHAIN/bin/i686-linux-android$API-clang++
CROSS_PREFIX=$TOOLCHAIN/bin/i686-linux-android-
OPTIMIZE_CFLAGS="-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32 -mno-stackrealign"
echo "* buidl for $ABI start !!" >> $OUT_DIR/build.log
build_start
echo "* buidl for $ABI end !!" >> $OUT_DIR/build.log
echo "*************************">> $OUT_DIR/build.log
echo "">> $OUT_DIR/build.log
}
#x86_64
function build_x86_64() {
API=21
ABI=x86_64
ARCH=x86_64
CPU=x86-64
CC=$TOOLCHAIN/bin/x86_64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/x86_64-linux-android$API-clang++
CROSS_PREFIX=$TOOLCHAIN/bin/x86_64-linux-android-
OPTIMIZE_CFLAGS="-march=$CPU -msse4.2 -mpopcnt -m64 -mtune=intel"
echo "* buidl for $ABI start !!" >> $OUT_DIR/build.log
build_start
echo "* buidl for $ABI end !!" >> $OUT_DIR/build.log
echo "*************************">> $OUT_DIR/build.log
}
function read_opt()
{
echo ""
echo "please select onece"
echo " 1、build for ABI=armeabi-v7a CPU=armv7-a"
echo " 2、build for ABI=arm64-v8a CPU=armv8-a"
echo " 3、build for ABI=x86 CPU=x86"
echo " 4、build for ABI=x86_64 CPU=x86_64"
echo " 5、build all"
echo ""
echo "input:"
read option
}
option=$1
if [ "$option" = "" ];then
read_opt
fi
if [ "$option" = "1" ];then
echo "start build ABI=arm64-v8a CPU=armv8-a"
build_arm64
elif [ "$option" = "2" ];then
echo "start build ABI=armeabi-v7a CPU=armv7-a"
build_arm
elif [ "$option" = "3" ];then
echo "start build ABI=x86 CPU=x86"
build_x86
elif [ "$option" = "4" ];then
echo "start build ABI=x86_64 CPU=x86_64"
build_x86_64
elif [ "$option" = "5" ];then
echo "start build all"
build_arm64
build_arm
build_x86
build_x86_64
else
echo "invalid input"
fi
rm -rf $TMPDIR
In file included from libswscale/x86/rgb2rgb.c:102:
libswscale/x86/rgb2rgb_template.c:1666:13: error: inline assembly requires more registers than available
"mov %4, %%"FF_REG_a"\n\t"
^
提供了一下4个解决方案
1、禁用asm,在ffmpeg configure选项中添加--disable-asm禁用汇编,不过可能会影响运行效率
2、在修改ffmpeg路径下config.h,将HAVE_EBP_AVAILABLE 设置为0 #define HAVE_EBP_AVAILABLE 0
3、这是ndk低版本编译链中才存在的问题,可用24及以上版本编译链,及将脚本中x86_build中的API设置为24及以上(可能会影响兼容性)
4、(推荐)在configure --extra-cflags选项中添加 -mno-stackrealign编译选项
在app下build.gradle 文件android内添加如下代码
sourceSets.main{
jniLibs.srcDirs=['libs']
}
模拟器基本都是x86cpu,网上说只有6.0以上才有这个问题,网上的方案都是把aap下build.gradle中的targetSdkVersion设置为23以下,但是我用模拟器调试时似乎不能这么操作,修改后编不出APK,我是搞安卓framework的,应用相关的不是很清楚,所以我直接使用静态库了,或者使用手机
首先在msys终端输入yasm,看是否有这个命令,如果提示找不到命令,则在脚本中将ndk中android-ndk-r20\toolchains\llvm\prebuilt\windows-x86_64\bin\yasm添加到PATH环境变量中,PATH=$PATH:$TOOLCHAIN/bin ,如果有yasm,则用whereis yasm命令,找到电脑中的yasm,将其重命名(记得改回去)或直接删除,然后将ndk的yasm添加到path环境变量
libswscale/arm/rgb2yuv_neon_common.S:251:11: error: unexpected token in argument list
CO_BV .dn d2.s16[2]
^
vmul y16x16_l, n16x16_l, CO_RY
^
我在编译ffmpeg4.3.1时没有这个问题,但是编译ffmpeg3.3.9时报错,简单粗暴的操作就是在ffmpeg configure选项中添加--disable-asm禁用汇编,但是我们要斯文一点,可以选择只禁用neon,即在armv7-a的ffmpeg configure选项中加入--disable-neon
目前只编译出静态库,动态库编译编译过程遇到了些问题,后面尝试解决
将多个静态库合并为一个静态库(使用安卓NDK编译链)_mvp_Dawn的博客-CSDN博客_多个静态库打包一个静态库