本文介绍FFmpeg穿插编译中的一些概念以及编译的详细进程,协助你跳过一些穿插编译的坑。

  • 系统:Ubuntu 22.04
  • FFmpeg:5.0.1、5.1.2
  • NDK:r21,r22,r23,r24,r25

NDK直到r18才正式移除GCC(原因检查此issue),尽管此前很长一段时刻内筹划将Clang/LLVM作为GCC的代替者。接下来补充GCC和Clang/LLVM一些知识。

GCC 和 Clang/LLVM

GCCClang/LLVM与编译器联络甚密,它们完成编译器的理念根本遵从最经典的三段式架构(编译器详细实的理论基础能够检查编译原理或许龙书)。

  • 编译器: Compiler,一种将高档言语编写的源代码翻译成低级言语的软件,注意: 这儿的低级言语有许多类型,能够是汇编言语也能够是机器言语,一些编译器只担任将高档言语翻译成汇编言语而不是直接翻译成机器言语,至于翻译成机器言语的工作交给汇编器处理
  • 三段式设计: 将源码编译生成目标言语的进程划分成前端中端(也称优化器)以及后端三个阶段:

1)前端(front end):担任词法、语法和语义分析,随后将源代码转换为中间代码。

2)中端(middle end或optimizer ):对中间代码进行优化。

3)后端(back end):担任将中端产品转化为针对各自平台的机器码(能够是汇编也能够是目标代码)。

GCC

1987年3月22日理查德马修斯托曼发布GCC第一个beta测验版(其时被称为GNU C Compiler,也便是GNU C编译器)。1999年4月,经过长期的商洽EGCS终究成为GCC(此时改名为GNU Compiler Collection,GNU 编译器调集)的官方版别,并于当年7月31号发布统一后的第一个GCC版别2.95,之后由GCC辅导委员会的辅导下不断迭代更新。GCC中编译器也遵从三段式设计(如下图中所示,详见GCC内部课程),有前端、中端以及后端。

Android之FFmpeg 5.0起航

Clang/LLVM

介绍Clang之前不得不说一下LLVM(原来被称为Low Level Virtual Machine,其实它与传统虚拟机一点关系也没有,准确说它是LLVM Project的全名),它源于2000年美国UIUC大学的一个研讨项目,意图是供给一种现代的、依据 SSA 的编译策略,并能支撑任意编程言语的静态和动态编译(参阅内容),开端由该大学的 Vikram AdveChris Lattner开发(风趣的是Chris Lattner主页中称自己是LLVM的原作者,理论还是实践这儿就不清楚了)。现在它现已开展成为一个由多个子项目组成的巨型项目,原来的界说现已不适合,更为切当的描绘是LLVM Project是模块化和可重用的编译器和东西链技术的调集。下图描绘的是LLVM三段式设计完成,源于Chris Lattner在The Architecture of Open Source Applications一书中的介绍。

Android之FFmpeg 5.0起航

Clang(a C language family frontend for LLVM,LLVM中C宗族言语的前端)诞生背景也是很有意思,2005年Chris Lattner进入苹果公司,并致力于LLVM在苹果开发系统中运用,自此苹果公司开端在多个商业产品中广泛运用LLVM,包含运用修正版的GCC(llvm-gcc) 作为Objective-C、C、C++等言语的编译器前端,然并卵,受限于GCC社区的支撑以及答应协议的约束(GCC 4.2.1是最终一个GPL v2答应证下的版别),苹果公司挑选开发Clang,代替llvm-gcc作为LLVM的一个前端,并于2007年7月11开源。

小结

GCC是编译器的调集;LLVM是编译器+东西链的调集;Clang仅是用于代替当初llvm-gcc补足LLVM中C宗族言语编译器缺失的前端。假如仅从代码库动身考虑,GCC便是烤全羊,LLVM便是全羊宴,Clang便是全羊宴中的一道菜,你要说谁更好吃,那就见仁见智了。

穿插编译

穿插编译实质上是在本机上为其它平台生成库或许可履行代程序,在这个进程中涉及运用东西链(toolchain,一组专用东西,一般包含编译器、汇编器以及链接器,能够检查此处),它们一般一个接一个的调用,上一个的输出即是下一个的输入。

NDK去GNU化

NDK存在两套东西链如下:

  1. gcc/g++和GNU Binutils
  2. clang/clang++和LLVM tools(LLVM Binutils)

早期东西链1能够投入实践出产环境,东西链2还处于预研阶段,中期逐渐将东西链2中的东西替换东西链1中的东西,后期东西链2完全投入出产环境,抛弃东西链1。

  • NDK r19之前东西链1坐落$NDK/toolchains/文件夹下后缀为-4.9的子文件夹下,东西链2坐落$NDK/toolchains/llvm/prebuilt/<host-tag>/bin/文件夹下。
  • NDK r18时,移除GCC,尽管东西链中还有名为gcc/g++的脚本,可是已被强制指向clang/clang++。
  • NDK r19进一步将东西链中的GNU Binutils移动到$NDK/toolchains/llvm/prebuilt/<host-tag>下,自此去GNU化拉开帷幕。
  • NDK r22时,运用LLD替换ld。
  • NDK r23时GNU Binutils中除了GNU汇编器(一般被称为gas)之外都被移除。
  • NDK r24时移除gas(代替者不是llvm-as),自此完成去GNU化,这也意味着自NDK r24开端NDK中只要一套东西链,即东西链2。

关于NDK中Binutils大致改变能够检查BulildSystemMaintainers#binutils,更多细节请检查NDK各个版别对应的Changelog。

东西链改变:

因为NDK去GUN化,需求东西链2中的东西兼容东西链1中的功能,所以一些东西并不直接对应于LLVM中的东西,如下表中所示:

GNU LLVM 介绍
gcc clang C编译器
g++ clang++ c++编译器
as clang(llvm-as处理的是LLVM IR,不是汇编源码) 汇编器,将汇编代码生成目标文件
ld lld、clang(也能用于链接) 链接器,将一切的目标文件以及依靠的库文件进行链接
addr2line llvm-addr2line 查找对应方位的源码信息,一般用于调试
ar llvm-ar 用于创立、修正和从档案中提取的实用程序
nm llvm-nm 列出object文件中的符号。
ranlib llvm-ranlib 生成存档内容的索引
strip llvm-strip 裁剪符号表

需求注意表中是clang不是Clang,clang是 一个依据Clang和LLVM的C宗族言语编译器,它涉及预处理、解析、优化、代码生成、汇编和链接,你能够经过传递参数操控它在履行完整链接之前的任何阶段停止;你也能够将clang了解成驱动器 Clang Driver,由它操控比方编译器、汇编器和链接器的履行,完成编译进程。关于clang的指令参数能够检查对应版别的文档或许直接在终端运用–help检查,clang一起也兼容gcc中的一些指令参数,修正脚本时能够仔细阅览。

链接器GNU ld的代替者是lld或许clang,为什么不是llvm-ld呢?其实llvm-ld也是存在的,只不过在LLVM 3.2中被移除,它的部分功能被其它程序取代。

llvm-ld and llvm-stub have been removed, llvm-ld functionality can be partially replaced by llvm-link | opt | {llc | as, llc -filetype=obj} | ld, or fully replaced by Clang.

lld是LLVM Linker的缩写,llvm-link是LLVM bitcode linker的缩写,这样就很简略区分lld和llvm-link,二者都是链接器,前者GNU链接器的直接代替,接受与GNU相同的指令行参数和链接脚本;后者用于处理多个LLVM bitcode文件,并将它们链接在一起构成一个LLVM bitcode文件。上述内容能够LLVM Download Page页面中查各个版别的release notes 或许 docs,如下图所示。关于llvm tools的一些指令描绘文档能够从docs中检查,例如Clang的指令行参数描绘就能够从docs的index界面中的如下内容中找到:

  • Clang Compiler User’s Manual(Clang编译器的用户手册)
  • Clang command line argument reference(Clang指令行参数引证)
  • Clang “man” pages

Android之FFmpeg 5.0起航

FFmpeg穿插编译

一般过程如下:

  1. 创立并发动Ubuntu虚拟机
  2. 建立NDK环境
  3. 装置必要软件
  4. 下载并解压FFmpeg源码包
  5. 将脚本放置于FFmpeg解压包的根途径
  6. 履行脚本,等候编译完毕

过程1能够参阅《VMware虚拟机装置Ubuntu20.04详细图文教程》,不再赘述;接下来对2-6过程中一些要害部分进行逐个介绍。

NDK环境建立

下载NDK并将它解压至usr/local/途径下,在终端中进行如下操作,进行环境变量装备(能够参阅超详干货!Linux 环境变量装备全攻略,当然你还能够在/etc/profile.d/目录下增加脚本文件)。

  • 履行 sudo vim ~/.bashrc,假如未装置vim软件,运用sudo apt install vim指令进行装置。
  • 键盘输入i进入修正形式,并在文件结尾顺次增加export NDK=/usr/local/android-ndk-r23bexport PATH=$PATH:$NDK,如下图所示:

Android之FFmpeg 5.0起航

  • 键盘敲击Esc键,退出修正形式后,键盘输入:wq保存并退出vim软件。
  • 继续履行source ~/.bashrc更新装备或许翻开新的终端,确保NDK的环境变量装备生效
  • 输入ndk-build -v验证是NDK的环境变量装备是否成功,成功成果如下图所示:

Android之FFmpeg 5.0起航

装置必要软件

尽管一般状况下仅需装置NDK+pkg-config+make就能够进行穿插编译,可是FFmpeg对外供给的编译装备中有一些特别优化,比方enable-asm时,或许需求装置额定的软件包。

  • pkg-config:供给库的一些信息,终端输入sudo apt install pkg-config进行装置
  • make:用于解说Makefile文件中的规矩,终端输入sudo apt install make进行装置

装置yasm/nasm条件

FFmpeg中x86/x86_64架构对应的汇编代码选用了Intel语法风格的x86汇编言语(x86汇编言语有两种语法风格(Intel和AT&T ) ),敞开汇编优化时需求支撑Intel语法风格的汇编器。因为gas或许llvm-as只支撑AT&T语法风格,因而装置yasm/nasm。

yasm:尽管在Github上的库房时有更新,但最新版别停留在了2014年8月10号。终端中输入sudo apt install yasm

nasm:一向在保护更新,能够检查changelog。终端输入sudo apt install nasm

下载并解压FFmpeg源码包

源码包能够github.com/FFmpeg/FFmp… 库房中挑选对应版别下载,当然也可运用FFmpeg-n5.0.1.zip。原先即使运用最新版别的 open-vm-tools-desktop (2:11.3.5-1ubuntu4,终端中输入dpkg -l | grep open-vm可检查版别)也无法从windows直接拷贝文件到虚拟机。(已被修正,仅做记载)

编写脚本

Tip:这部分内容涉及Shell脚本的语法规矩非常简略,花费一点时刻就能掌握,阅览Shell教程

FFmpeg遵从与大多数Unix软件相同的构建办法,用到如下三条指令:

  1. ./configure+参数,向configure脚本传递参数并履行,并生成相应的Makefile文件或与编译相关文件(装备或许与其它项目中的参数不一致,运用./configure --help检查更多参数,当然也能够运用./configure --help > configure_help.txt将协助文档的内容输入到txt文件中检查)。
  2. make,运用指令make编译,make指令会自动查找上一步生成的Makefile文件并依据Makefile中的内容履行编译。
  3. make install,将编译生成的库文件、装备文件、头文件等等拷贝到指定的装置目录。

除了上述指令外还有或许用到make cleanmake distcleanmake uninstall等指令合作运用,详细介绍能够检查make、make clean、make install、make uninstall、make dist、make distcheck和make distclean。

装备./configure+参数

shell脚本中的字符串能够用单引号,也能够用双引号,也能够不用引号

下面是一个根本的架子:

Android之FFmpeg 5.0起航

接下来对上面脚本中运用到的FFmpeg Toolchanain option和自界说变量逐行介绍:

Android之FFmpeg 5.0起航

Tip:为了便利阅览,将变量的界说和赋值的内容放到下面介绍

补充上述变量的界说与值

CONFIGURATION:封装一些向configure脚本传递的参数。

CFLAGSLDFLAGS:向编译器或链接器传递参数,辅助编译。

其它变量:用于确认文件方位,如下:

NDK="/usr/local/android-ndk-r25"
HOST="linux-x86_64"
SYSROOT="$NDK/toolchains/llvm/prebuilt/$HOST/sysroot"
LLVM_TOOLCHAIN="$NDK/toolchains/llvm/prebuilt/$HOST/bin"
PKG_CONFIG="/usr/bin/pkg-config"
#省掉了一些代码,详细检查脚本
PREFIX="$(pwd)/android/$ARCH"
CC=$LLVM_TOOLCHAIN/$SPECIAL_TARGET$API-clang
CXX=$LLVM_TOOLCHAIN/$SPECIAL_TARGET$API-clang++
AS=$LLVM_TOOLCHAIN/$SPECIAL_TARGET$API-clang
LD=$LLVM_TOOLCHAIN/$SPECIAL_TARGET$API-clang
AR=$LLVM_TOOLCHAIN/llvm-ar
NM=$LLVM_TOOLCHAIN/llvm-nm
STRIP=$LLVM_TOOLCHAIN/llvm-strip
RUNLIB=$LLVM_TOOLCHAIN/llvm-ranlib

接下来要点介绍CONFIGURATIONCFLAGS以及LDFLAGS的值:

$CONFIGURATION

FFmpeg configure可用以装备的选项有许多,如图configure_help.svg,网上有许多这方面的解说,能够自行查找,这儿不做逐个介绍。

注意:在前缀为--disable---enable-的选项中,disable表明封闭 ,enable 代表敞开。关于变量COMPONENT_LIST和变量CMDLINE_SELECT中的选项来说,能够自由挑选设置--disable---enable-作为前缀(详见下方Configure脚本分析)。

#坐落4105行,不同版别或许有改变。
#关于for要害字,能够在终端中输入help for检查文档,这儿有一些特别,没有指定[in WORDS],
#因而按照文档中说法,便是默许运用了in "$@",
#相当于遍历一个由指令行的一切参数分别填充的字符串数组,["opt0","opt1","opt2"...].
for opt do
    # ${opt#*=}表明将opt字符串中=右边的字符串取出,详见参阅内容《shell变量操作${}详细用法》
    optval="${opt#*=}"
    case "$opt" in
    # 省掉部分分支
        #匹配以--enable-或许--disable-开头的选项,当然有一些现已提前处理
        --enable-?*|--disable-?*)
           #eval指令会将参数组合成一个字符串,并将成果作为shell的输入,然后履行。
           #1. echo "--enable-xxx" | sed 's/--/action=/;s/-/ option=/;s/-/_/g'
           #2.履行上述指令,打印action=enable option=xxx
            eval $(echo "$opt" | sed 's/--/action=/;s/-/ option=/;s/-/_/g')
            # is_in函数相当于ArrayList#contains办法,在后边解析 
            if is_in $option $COMPONENT_LIST; then
            # 关于COMPONENT_LIST变量中的参数来说,假如设置disable,那么重新将action设置为unset
                test $action = disable && action=unset
                # 假定$option=bsfs,
                # bsfs去掉s,并转换成大写;终究履行unset $BSF_LIST或许enable $BSF_LIST
                eval $action $$(toupper ${option%s})_LIST
            elif is_in $option $CMDLINE_SELECT; then
            # 关于CMDLINE_SELECT变量中的参数能够自由设置disable还是enable
                $action $option
            else
                die_unknown $opt
            fi
        ;;
    # 省掉部分判别
    esac
done
#假定 is_in $option $COMPONENT_LIST
is_in(){
    # $1的值便是$option
    value=$1
    # 确保后边$*获取的参数中移除$1(即$option)
    shift
    # 此处遍历$*,假如$option在$COMPONENT_LIST中,那么回来0,shell中0表明true.
    for var in $*; do
        [ $var = $value ] && return 0
    done
    return 1
}

这儿介绍一些常用的。

Configuration options

–disable-static 不构建静态库
–enable-shared 编译生成动态库
–enable-small 降低库体积

Program options

–disable-programs 不构建指令行程序(指ffmpeg、ffplay以及ffprobe)
–disable-ffmpeg 不构建ffmpeg程序(转换器)
–disable-ffplay 不构建ffplay程序(播放器)
–disable-ffprobe 不构建ffprobe程序(检查多媒体文件的信息)

Documentation options

–disable-doc 不产生文档
–disable-htmlpages 不产生 HTML类型的文档
–disable-manpages 不产生 manpage类型的文档
–disable-podpages 不产生 POD 类型的文档
–disable-txtpages 不产生 text 类型的文档

Component options

  • –disable-avdevice:不构建libavdevice库,libavdevice is a library containing input and output devices for grabbing from and rendering to many common multimedia input/output software frameworks, including Video4Linux, Video4Linux2, VfW, and ALSA.
  • –disable-avcodec:不构建libavcodec库,libavcodec is a library containing decoders and encoders for audio/video codecs.
  • –disable-avformat:不构建libavformat库,libavformat is a library containing demuxers and muxers for multimedia container formats.
  • –disable-swresample:不构建libswresample库,libswresample is a library performing highly optimized audio resampling, rematrixing and sample format conversion operations.
  • –disable-swscale:不构建libswcale库,libswscale is a library performing highly optimized image scaling and color space/pixel format conversion operations.

Toolchain options

–enable-cross-compile 敞开穿插编译
–enable-pic 生成方位无关代码

Optimization options

  • –disable-asm:制止一切汇编优化

注:脚本中在7281行中有enabled asm || { arch=c; disable $ARCH_LIST $ARCH_EXT_LIST; }这样的代码,意为着当检测到asm封闭时,会封闭ARCH_LISTARCH_EXT_LIST中的选项。趁便提一下,当封闭asm时,neon也被封闭,asm默许被敞开的,详细能够检查脚本。

# 这儿已被调整,只做参阅,详细详见关于版别中的configure脚本
ARCH_LIST=" aarch64 alpha arm avr32 avr32_ap avr32_uc bfin ia64 loongarch loongarch32 loongarch64
m68k mips mips64 parisc ppc ppc64 riscv s390 sh4 sparc sparc64 tilegx tilepro tomi x86 x86_32 x86_64"
ARCH_EXT_LIST=" $ARCH_EXT_LIST_ARM $ARCH_EXT_LIST_PPC
    $ARCH_EXT_LIST_X86 $ARCH_EXT_LIST_MIPS $ARCH_EXT_LIST_LOONGSON"
ARCH_EXT_LIST_ARM=" armv5te armv6 armv6t2 armv8 neon vfp vfpv3 setend"
ARCH_EXT_LIST_X86="
    $ARCH_EXT_LIST_X86_SIMD
    cpunop
    i686
"
ARCH_EXT_LIST_X86_SIMD="aesni amd3dnow amd3dnowext avx avx2 avx512 fma3 
fma4 mmx mmxext sse sse2 sse3 sse4 sse42 ssse3 xop"
  • –disable-neon:制止NEON优化

Demystifying ARM Floating Point Compiler Options一文中对NEON、float-abi、fpu有详细解说,能够处理许多疑问。

一般状况下关于ARM架构引荐敞开NEON优化,原因:1.绝大多数ARM 安卓设备支撑NEON,特别是依据ARMv8的设备都支撑NEON。2.NDK r23是最终一个支撑non-Neon的版别,原因是极少数非常旧的设备不支撑Neon。3.功能优化很可观。本文中关于x86架构封闭NEON优化的原因:x86架构不兼容NEON指令。假如你需求在依据x86架构的安卓设备(比方Window11上的android子系统)支撑NEON指令,能够参阅From ARM NEON to Intel SSE-the automatic porting solution, tips and tricks。一些拓宽链接error:’neon_vector_type’attribute is not suppoted for this target #420 Neon for x86 not enabled in Clang #260

Developer options

  • –disable-debug:制止调试符号,脚本中默许敞开。
$CFLAGS

这儿介绍clang/clang++常见参数。

操控代码优化程度,-O0, -O1, -O2, -O3, -Ofast, -Os, -Oz, -Og, -O, -O4

-O0, -O1, -O2, -O3, -Ofast, -Os, -Oz, -Og, -O, -O4

Specify which optimization level to use:

-O0 Means “no optimization”: this level compiles the fastest and generates the most debuggable code.

-O1 Somewhere between -O0 and -O2.

-O2 Moderate level of optimization which enables most optimizations.

-O3 Like -O2, except that it enables optimizations that take longer to perform or that may generate larger code (in an attempt to make the program run faster).

-Ofast Enables all the optimizations from -O3 along with other aggressive optimizations that may violate strict compliance with language standards.

-Os Like -O2 with extra optimizations to reduce code size.

-Oz Like -Os (and thus -O2), but reduces code size further.

-Og Like -O1. In future versions, this option might disable different optimizations in order to improve debuggability.

-O Equivalent to -O1.

-O4 and higher

Currently equivalent to -O3

-D <macro>=<value> :Define <macro> to <value> (or 1 if <value> omitted)

相当于C言语中的#define macro=value

-Wl,<arg> :Pass the comma separated arguments in to the linker

向linker传递参数

-I <dir> :Add directory to the end of the list of include search paths

寻觅头文件时会在dir途径中寻觅

-L <dir> : Add directory to library search path

$LDFLAGS

-L <dir> :Add a directory to the library search path

-l <libName> :Root name of library to use

–nostdlib: Only search directories specified on the command line

修正config.h中的内容

运用sed指令修正config.h中的值,config.h文件会在configure脚本履行完成后自动生成,里面记载一些装备信息,比方是否敞开了asm、是否敞开neon等等。因而也能够经过config.h文件确认装备是否与预期相符。

Android之FFmpeg 5.0起航

编译&装置

Android之FFmpeg 5.0起航

编译

供给脚本:build_shared_asm.sh,build_static_asm.sh

你能够运用上述供给的脚本进行编译(你需求依据需求调整参数的编译出最合适的库),注:现已测验NDKr21及以上版别,你只需调整NDK版别或许途径。

将脚本放入FFmpeg源码解压包的根目录下,在终端中履行脚本,等候完毕,如图:

Android之FFmpeg 5.0起航

编译成果如下:

Android之FFmpeg 5.0起航

Android之FFmpeg 5.0起航

问题记载

编译进程中呈现

GNU assembler not found, install/update gas-preprocessor

原来脚本中汇编器运用的GAS,因为未敞开汇编优化,所以一向没有发现脚本中的过错。当运用NDK中的GAS编译Arm架构对应的汇编代码时,呈现此问题。

处理办法:

依据提示装置gas-preprocessor(没什么用,究竟gas-preprocessor是Perl脚本,用于完成一个 Apple’s as没有的gas preprocessor的子集):

  • 下载gas-preprocessor.pl文件。
  • 运用 sudo cp gas-preprocessor.pl /usr/local/bin/将其拷贝至usr/local/bin途径下,并经过sudo chmod 777 /usr/local/bin/gas-preprocessor.pl 赋予权限。

办法1:clang内置汇编器,用于削减生成.s文件以及调用汇编器的时刻。向clang传递-fno-integrated-as参数,禁用内置汇编器,默许运用/usr/bin/as代替。经实践,办法1不适合当前版别,会报如下过错(NDK issues),NDK r23中直到r23c中被修正,尽管之前版别依旧能运用,可是NDK r24中此参数已被移除。

test_ld cc
test_cc
BEGIN /tmp/ffconf.MNVahc6Q/test.c
1	int main(void){ return 0; }
END /tmp/ffconf.MNVahc6Q/test.c
/usr/local/android-ndk-r23b/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi21-clang --sysroot=/usr/local/android-ndk-r23b/toolchains/llvm/prebuilt/linux-x86_64/sysroot -I/usr/local/android-ndk-r23b/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/arm-linux-androideabi -Os -fPIC -fno-integrated-as -ferror-limit=0 -DVK_ENABLE_BETA_EXTENSIONS=0 -Wno-implicit-int-float-conversion -Wno-sometimes-uninitialized -Wno-unused-function -Wno-deprecated-declarations -Wno-tautological-constant-out-of-range-compare -Wno-inline-asm -march=armv7-a -c -o /tmp/ffconf.MNVahc6Q/test.o /tmp/ffconf.MNVahc6Q/test.c
/usr/bin/as: unrecognized option '-EL'
clang-12: error: assembler command failed with exit code 1 (use -v to see invocation)
C compiler test failed.

办法2:直接由clang指定汇编器,即--as=$cc

x86架构敞开asm,编译时报错,inline assembly requires more registers than available

libavcodec/x86/cabac.h:200:9: error: inline assembly requires more registers than available
        BRANCHLESS_GET_CABAC("%0", "%q0", "(%4)", "%1", "%w1",
        ^
libavcodec/x86/cabac.h:145:9: note: expanded from macro 'BRANCHLESS_GET_CABAC'
        "movzbl "statep"    , "ret"                                     \n\t"\
        ^
CC	libavcodec/hcom.o
CC	libavcodec/hevc_cabac.o
libavcodec/x86/cabac.h:200:9: error: inline assembly requires more registers than available
libavcodec/x86/cabac.h:145:9: note: expanded from macro 'BRANCHLESS_GET_CABAC'
        "movzbl "statep"    , "ret"                                     \n\t"\
        ^
CC	libavcodec/hevc_data.o
CC	libavcodec/hevc_filter.o
In file included from libavcodec/h264_cabac.c:43:
libavcodec/x86/h264_cabac.c:66:9: error: inline assembly requires more registers than available
        "3:                                     \n\t"
        ^
CC	libavcodec/hevc_mp4toannexb_bsf.o
CC	libavcodec/hevc_mvs.o
CC	libavcodec/hevc_parse.o
libavcodec/hevc_cabac.c:37:21: warning: variable 'num_bins_in_se' is not needed and will not be emitted [-Wunneeded-internal-declaration]
static const int8_t num_bins_in_se[] = {
                    ^
CC	libavcodec/hevc_parser.o
CC	libavcodec/hevc_ps.o
CC	libavcodec/hevc_refs.o
CC	libavcodec/hevc_sei.o
In file included from libavcodec/hevc_cabac.c:27:
In file included from libavcodec/cabac_functions.h:49:
libavcodec/x86/cabac.h:200:9: error: inline assembly requires more registers than available
        BRANCHLESS_GET_CABAC("%0", "%q0", "(%4)", "%1", "%w1",
        ^
libavcodec/x86/cabac.h:145:9: note: expanded from macro 'BRANCHLESS_GET_CABAC'
        "movzbl "statep"    , "ret"                                     \n\t"\
        ^

经过查找和实践ffmepg trac 779、x86 inline assembly code which compiled with r16b does not compile with r17 和android 4.2.2 x86,处理Android NDK编译FFmpeg 4.2.2的x86 cpu版时的问题的内容,向CFLAG中传递-DHAVE_EBP_AVAILABLE=0办法仍会出错,报如下过错:

./config.h:356:9: warning: 'HAVE_EBP_AVAILABLE' macro redefined [-Wmacro-redefined]
warning: 'HAVE_EBP_AVAILABLE' macro redefined [-Wmacro-redefined]

将libavutils/x86/asm.h中#define HAVE_7REGS (ARCH_X86_64 || (HAVE_EBX_AVAILABLE && HAVE_EBP_AVAILABLE))修正为#define HAVE_7REGS (ARCH_X86_64)的办法能够编译经过。

运行进程中呈现

java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol “xxx” referenced by

一般无法定位符号的原因是没有链接对应的库文件。下面是三个架构不同报错:

W/System.err: java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__extenddftf2" referenced by "/data/app/~~5iB4UFPumZmTrJq8DUYFmg==/com.floatWind.snap-2vw43nTh6DG8YL6K3Td2aA==/lib/x86_64/libavutil.so"...
W/System.err:     at java.lang.Runtime.loadLibrary0(Runtime.java:1077)
W/System.err:     at java.lang.Runtime.loadLibrary0(Runtime.java:998)
W/System.err:     at java.lang.System.loadLibrary(System.java:1656)
W/System.err: java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__ashldi3" referenced by "/data/app/com.floatWind.snap-s8RJlycCvRQZYgyulGFC3Q==/lib/x86/libavformat.so"...
W/System.err:     at java.lang.Runtime.loadLibrary0(Runtime.java:1016)
W/System.err:     at java.lang.System.loadLibrary(System.java:1657)
W/System.err:     at com.ffmpeg.VideoSnap.<clinit>(VideoSnap.java:16)
W/System.err:     at com.floatWind.snap.MainActivity$2.run(MainActivity.java:70)
W/System.err: java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__floatunsitf" referenced by "/data/app/com.floatWind.snap-K2FtbiNL0c7eCXN_xanOMg==/lib/arm64/libavutil.so"...
W/System.err:     at java.lang.Runtime.loadLibrary0(Runtime.java:1071)
W/System.err:     at java.lang.Runtime.loadLibrary0(Runtime.java:1007)
W/System.err:     at java.lang.System.loadLibrary(System.java:1668)
W/System.err:     at com.ffmpeg.VideoSnap.<clinit>(VideoSnap.java:16)
W/System.err:     at com.floatWind.snap.MainActivity$2.run(MainActivity.java:70)

extenddftf2、ashldi3、floatunsitf这几个符号皆是用于long double数据类型的浮点运算,它们坐落compiler-rt project中builtins中(原本libgcc也有对应完成,后被移除),它坐落$NDK/toolchains/llvm/prebuilt/$HOST/lib64/clang/*/lib/linux之下。你也可在Building with 128-bit floating-point (long double) support for AArch64 state, with Arm Compiler 6文章了解更多。

原因:运用-nostdlib导致无法链接到compiler-rt

处理办法: 1.将对应库(clang_rt.builtins-XXX-android)的头文件方位和库文件所在方位分别奉告编译器和链接器(NDK r21中此计划不生效)。

2.移除-nostdlib标志

继续盯梢该问题

  • [BUG] with ndk r23 and newer, builtin symbols cannot be found when a program is linked with libtool #1614
  • [Bug]: Several configure&make packages give binaries with cannot locate symbol “__extendsftf2” #8029
  • [FR] switch from libgcc to libclang_rt #1231

undefined reference to ‘inflateInit_’

编译ffmpeg静态产品时链接了libz,本地编写CMakeList.txt未链接libz,导致呈现未界说的引证

fatal error: ‘vulkan_beta.h’ file not found

办法1:装备configure脚本时禁用vulkan: --disable-vulkan

办法2:装备extra-cflags时增加 -DVK_ENABLE_BETA_EXTENSIONS=0,防止引证vulkan_beta.h头文件

ld: error: relocation XXX cannot be used against symbol XXX; recompile with -fPIC

arm64-v8a

尽管能够经过CFlAG中增加-mcmodel=large防止(阅览1、2),可是还有其它过错产生,仅作记载

C/C++: ld: error: relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol 'ff_cos_32'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADD_ABS_LO12_NC cannot be used against symbol 'ff_cos_32'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol 'ff_cos_64'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADD_ABS_LO12_NC cannot be used against symbol 'ff_cos_64'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol 'ff_cos_128'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADD_ABS_LO12_NC cannot be used against symbol 'ff_cos_128'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol 'ff_cos_256'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADD_ABS_LO12_NC cannot be used against symbol 'ff_cos_256'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol 'ff_cos_512'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADD_ABS_LO12_NC cannot be used against symbol 'ff_cos_512'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol 'ff_cos_1024'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADD_ABS_LO12_NC cannot be used against symbol 'ff_cos_1024'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol 'ff_cos_2048'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADD_ABS_LO12_NC cannot be used against symbol 'ff_cos_2048'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol 'ff_cos_4096'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADD_ABS_LO12_NC cannot be used against symbol 'ff_cos_4096'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol 'ff_cos_8192'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADD_ABS_LO12_NC cannot be used against symbol 'ff_cos_8192'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol 'ff_cos_16384'; recompile with -fPIC
C/C++: ld: error: relocation R_AARCH64_ADD_ABS_LO12_NC cannot be used against symbol 'ff_cos_16384'; recompile with -fPIC
C/C++: ld: error: too many errors emitted, stopping now (use -error-limit=0 to see all errors)
x86_64
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_h264_cabac_tables'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_h264_cabac_tables'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_h264_cabac_tables'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_h264_cabac_tables'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_h264_cabac_tables'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_h264_cabac_tables'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_ac3_bap_bits'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_ac3_bap_bits'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_pd_1'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_pd_1'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_pw_5'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_pw_4'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_pw_5'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_pw_4'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_pw_5'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_pw_4'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_pw_5'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_pw_4'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_pw_5'; recompile with -fPIC
C/C++: ld: error: relocation R_X86_64_PC32 cannot be used against symbol 'ff_pw_4'; recompile with -fPIC
C/C++: ld: error: too many errors emitted, stopping now (use -error-limit=0 to see all errors)
x86
C/C++: ld: error: relocation R_386_32 cannot be used against local symbol; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against local symbol; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against local symbol; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against symbol 'ff_ac3_bap_bits'; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against symbol 'ff_ac3_bap_bits'; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against local symbol; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against local symbol; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against symbol 'ff_pd_1'; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against local symbol; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against symbol 'ff_pd_1'; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against local symbol; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against local symbol; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against local symbol; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against symbol 'ff_pw_5'; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against symbol 'ff_pw_4'; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against symbol 'ff_pw_5'; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against symbol 'ff_pw_4'; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against symbol 'ff_pw_5'; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against symbol 'ff_pw_4'; recompile with -fPIC
C/C++: ld: error: relocation R_386_32 cannot be used against symbol 'ff_pw_5'; recompile with -fPIC
C/C++: ld: error: too many errors emitted, stopping now (use -error-limit=0 to see all errors)

经过长期测验各种计划,终究在Unable to compile ffmpeg extension with NDK 23 + CMake-3.22看到处理计划(不是很完美,无法处理x86中的悉数报错,但现已够用),如下图所示,当然你也能够在CMakeLists.txt文件中向CFLAGS中增加-Wl,-Bsymbolic,即向链接器传递-Bsymbolic,强制选用本地的全局变量界说。

Android之FFmpeg 5.0起航

尽管问题处理了,忽然想起FFmpeg官网中Platform Specific Information中Advanced linking configuration中提及(/(ㄒoㄒ)/~~)

If you compiled FFmpeg libraries statically and you want to use them to build your own shared library, you may need to force PIC support (with --enable-pic during FFmpeg configure) and add the following option to your project LDFLAGS:-Wl,-Bsymbolic.

If your target platform requires position independent binaries, you should pass the correct linking flag (e.g. -pie) to --extra-ldexeflags.

dlopen failed: “xxx.so” has text relocations

System.err: java.lang.UnsatisfiedLinkError: dlopen failed: "/data/app/com.floatWind.snap-lMxvRHX5I84nYETkUOhOow==/lib/x86/libavformat.so" has text relocations

经过阅览Android linker changes for NDK developers中Text Relocations一些状况,能够经过Gentoo Textrels guide中的介绍处理文本重定位的问题,下面是一些针对上述详细问题的处理过程:

  1. 文章中用到scanelf指令来自pax-utils 软件包,因而在终端输入sudo apt install pax-utils

  2. 终端中输入scanelf -qT libavformat.so 寻觅出错代码,-q表明只输出一些或许有问题的提示,-T表明文本重定位的方位,scanelf指令其它详细参数的含义能够检查这儿。

  3. 上面的还没研讨透彻,待续。

  4. 经过查询,向链接器传递了”-z notext” 导致衔接时允许text relocations,疏忽了ld: error: can’t create dynamic relocation R_386_32 against local symbol in readonly segment; recompile object files with -fPIC or pass ‘-Wl,-z,notext’ to allow text relocations in the output过错。

  5. libavutil/x86/float_dsp.asm、libavutil/x86/tx_float.asm文件出了问题,需求重新编写。

dlopen failed: cannot locate symbol “ff_tx_codelet_list_float_x86″(x86 & x86_64 )

java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "ff_tx_codelet_list_float_x86" referenced by "/data/app/~~K5KAUCDK4rV3L6O1Qu8t7w==/com.floatWind.snap-_Cs7dWX9r8E_ELZ7aiTP-g==/lib/x86_64/libavutil.so"...

dlopen failed: cannot locate symbol “__aarch64_ldadd8_acq_rel”

java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__aarch64_ldadd8_acq_rel" referenced by "/data/app/com.floatWind.snap--aifSTof5wmQ0hbczZDovw==/lib/arm64/libavformat.so"...

在CFLAGS中增加-mno-outline-atomics(Don’t generate local calls to out-of-line atomic operations,关于Out-of-line Atomics能够检查What is new in LLVM12 for Arm),参阅Building for Android with OpenSSL

参阅内容

  • Compile FFmpeg for Ubuntu, Debian, or Mint

  • The Architecture of Open Source Applications

  • Build System Maintainers Guide

  • Compiling Linux Software from Source Code

  • Linux编译装置中configure、make和make install 指令详解

  • configure、 make、 make install 背后的原理(翻译)

  • GCC编译选项参数

  • Demystifying ARM Floating Point Compiler Options

  • libxxx.so- text relocations问题的终极处理计划

  • Getting Started with LLVM Core Libraries(中文版)

  • LLVM-Driver笔记

  • Android linker changes for NDK developers

  • The magic behind configure, make, make install

  • configure脚本

  • Linux下 config/configure/Configure、make 、make test/make check、sudo make install 的作用

  • Linux 指令详解(三)./configure、make、make install 指令

  • armeabi-v7a arm64-v8a armeabi x86 x86_64区别

  • shell变量操作${}详细用法

  • 怎么高效的调试 ffmpeg