编译ffmpeg安卓库(clang篇),含armeabi-v7a , arm64-v8a, x86, x86_64

    安卓新的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),理论上应该是可以直接进入后面的编译步骤的,不过我没尝试过

1、msys2安装 

         在清华大学镜像网站下载并安装 msys2 最新版本 ,此外还有中科大,北京理工大学, 上海交大等镜像源,64位系统建议下载x86_64版本,安装一路点击下一步就行

2、msys2配置

        将默认源地址替换为清华大学镜像地址,类似于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包管理工具命令开始编译

3、安装make

    在msys2终端执行pacman -S make,安装make工具

pacman -S make

编译ffmpeg

    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可跟踪编译情况

编译ffmpeg安卓库(clang篇),含armeabi-v7a , arm64-v8a, x86, x86_64_第1张图片

#!/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

问题总结

1、编译x86cpu时内联汇编报错

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编译选项

2、使用ffmpeg动态库,编译时提示找不到动态库

在app下build.gradle 文件android内添加如下代码

sourceSets.main{
    jniLibs.srcDirs=['libs']
}

3、使用模拟器调试时,提示java.lang.UnsatisfiedLinkError: dlopen failed: "xxx.so" has text relocations(https://android.googlesource.com/platform/bionic/+/master/android-changes-for-ndk-developers.md#Text-Relocations-Enforced-for-API-level-23)

模拟器基本都是x86cpu,网上说只有6.0以上才有这个问题,网上的方案都是把aap下build.gradle中的targetSdkVersion设置为23以下,但是我用模拟器调试时似乎不能这么操作,修改后编不出APK,我是搞安卓framework的,应用相关的不是很清楚,所以我直接使用静态库了,或者使用手机

4,提示找不到ysam或版本过低

首先在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环境变量

5、在编译armv7-a时,汇编文件报错

libswscale/arm/rgb2yuv_neon_common.S:251:11: error: unexpected token in argument list
CO_BV .dn d2.s16[2]
          ^
:4:5: error: invalid instruction
    vmul y16x16_l, n16x16_l, CO_RY
    ^
我在编译ffmpeg4.3.1时没有这个问题,但是编译ffmpeg3.3.9时报错,简单粗暴的操作就是在ffmpeg configure选项中添加--disable-asm禁用汇编,但是我们要斯文一点,可以选择只禁用neon,即在armv7-a的ffmpeg configure选项中加入--disable-neon

6、将ffmpeg所有的库编译为一个libffmpeg.so库

目前只编译出静态库,动态库编译编译过程遇到了些问题,后面尝试解决

将多个静态库合并为一个静态库(使用安卓NDK编译链)_mvp_Dawn的博客-CSDN博客_多个静态库打包一个静态库

你可能感兴趣的:(ffmpeg,ffmpeg,android,音视频,ndk)