前言
本文基于 FFmpeg 5.1.4 版本,详细讲述了使用 Android.bp 构建 FFmpeg 库的过程,旨在简化 Android 源码中 FFmpeg 库的构建以及集成过程。
- 构建过程需使用 NDK 工具辅助,请参考该专栏之前文章:使用 NDK 编译 FFmpeg
- github 仓库:FFmpegDroidBuilder
搭建编译框架
以编译 libavutil、libavcodec、libavformat、libswresample、libswscale 模块为例,在 FFmpeg 源码根目录下,创建以下 7 个文件:
Android.bp
├── compat_files.bp
├── avutil_files.bp
├── avcodec_files.bp
├── avformat_files.bp
├── swresample_files.bp
└── swscale_files.bp
Android.bp 作为编译入口,定义整体编译架构,其余 6 个 bp 文件作为 Android.bp 的依赖项,声明 FFmpeg 各模块需编译的源文件:
- compat_files.bp 声明 compat 目录下需编译的源码文件,该目录中存在很多为兼容各种平台而创建的头文件
- 其他 bp 文件则分别声明 libavutil、libavcodec、libavformat、libswresample、libswscale 目录下需编译的源文件
1. 创建 Android.bp 文件
首先,搭建整体编译框架,后续章节再补充具体编译参数。
// 依赖项
build = [
"compat_files.bp",
"avutil_files.bp",
"avcodec_files.bp",
"avformat_files.bp",
"swresample_files.bp",
"swscale_files.bp",
]
// 编译选项默认值
cc_defaults {
name: "libFFmpeg_defaults",
cflags: [
],
arch: {
arm: {
cflags: [
],
asflags: [
],
},
arm64: {
cflags: [
],
asflags: [
],
},
},
}
cc_library {
name: "libFFmpeg",
defaults: ["libFFmpeg_defaults"],
local_include_dirs: [
],
vendor_available: true,
export_include_dirs: [
".",
],
// android 10 平台需设置该选项为 false 以停用 XOM
// xom: false,
// 默认同时编译 32 和 64 位的代码。若放开下列注释,则只编译 64 位的代码
// compile_multilib: "64",
arch: {
arm: {
instruction_set: "arm",
srcs: [
":avutil-src-arm",
":avcodec-src-arm",
":swresample-src-arm",
":swrscale-src-arm",
],
// ARM 架构编译选项
cflags: [
],
// ARM 架构链接选项
ldflags: [
],
// ARM 架构汇编选项
asflags: [
]
},
arm64: {
srcs: [
":avutil-src-arm64",
":avcodec-src-arm64",
":swresample-src-arm64",
":swrscale-src-arm64",
],
// ARM64 架构编译选项
cflags: [
],
// ARM64 架构链接选项
ldflags: [
],
// ARM64 架构汇编选项
asflags: [
],
},
},
srcs: [
":compat-src",
":avutil-src-common",
":avcodec-src-common",
":swresample-src-common",
":swrscale-src-common",
":avformat-src",
],
shared_libs: [
],
static_libs: [
],
// 全局编译选项
cflags: [
],
// 全局链接选项
ldflags: [
],
// 全局汇编选项
asflags: [
],
}
-
XOM 官当说明:AArch64 二进制文件的只执行内存 (XOM)
-
Android.bp 中定义的模块间依赖关系如下:
Android.bp
└── libFFmpeg
├── libFFmpeg_defaults
│
├── compat-src
│
├── avutil-src-common
├── avutil-src-arm
├── avutil-src-arm64
│
├── avcodec-src-common
├── avcodec-src-arm
├── avcodec-src-arm64
│
├── swresample-src-common
├── swresample-src-arm
├── swresample-src-arm64
│
├── swrscale-src-common
├── swrscale-src-arm
├── swrscale-src-arm64
│
└── avformat-src
2. 创建 avutil_files.bp
声明 avutil-src-arm、avutil-src-arm64、avutil-src-common 模块,后续章节再补充这些模块所依赖的源文件。
filegroup {
name: "avutil-src-arm",
// ARM 架构代码
srcs: [
],
}
filegroup {
name: "avutil-src-arm64",
// ARM64 架构代码
srcs: [
],
}
filegroup {
name: "avutil-src-common",
// 多平台共用代码
srcs: [
],
}
3. 创建其他 bp 文件
参考 avutil_files.bp,创建 compat_files.bp、avcodec_files.bp、avformat_files.bp、swresample_files.bp、swscale_files.bp 文件,并添加相应的模块声明。
- compat_files.bp
filegroup {
name: "compat-src",
// 无需区分 ARM 和 ARM64 架构
srcs: [
],
}
- avcodec_files.bp
filegroup {
name: "avcodec-src-arm",
// ARM 架构代码
srcs: [
],
}
filegroup {
name: "avcodec-src-arm64",
// ARM64 架构代码
srcs: [
],
}
filegroup {
name: "avcodec-src-common",
// 多平台共用代码
srcs: [
],
}
- avformat_files.bp
filegroup {
name: "avformat-src",
srcs: [
]
}
- swresample_files.bp
filegroup {
name: "swresample-src-arm",
// ARM 架构代码
srcs: [
],
}
filegroup {
name: "swresample-src-arm64",
// ARM64 架构代码
srcs: [
],
}
filegroup {
name: "swresample-src-common",
// 多平台共用代码
srcs: [
],
}
- swscale_files.bp
filegroup {
name: "swrscale-src-arm",
// ARM 架构代码
srcs: [
],
}
filegroup {
name: "swrscale-src-arm64",
// ARM64 架构代码
srcs: [
],
}
filegroup {
name: "swrscale-src-common",
// 多平台共用代码
srcs: [
],
}
armv8-a 编译参数
1. configure 源代码
cpu 架构配置为 armv8-a,参考专栏之前的文章:使用 NDK 编译 FFmpeg。
2. 查看编译参数
configure 指令执行完成后,在生成的 ffbuild/config.mak 文件中,可查看编译过程各阶段所需参数:
CPPFLAGS:预处理标志
CFLAGS:C编译标志
CXXFLAGS:C++编译标志
ASFLAGS:汇编标志
LDFLAGS:链接标志
LDEXEFLAGS:用于链接可执行文件的标志
3. 添加编译参数
前一小节 CPPFLAGS、CFLAGS、CXXFLAGS 中的 flags 可分为以下两类:
- cpu 特定 flags:如 -mfpu=neon、-march=armv8-a
- 多平台共用 flags:如 -fPIC、-fPIE
FFmpeg 源码根目录下,执行 vim find_flags.sh
编辑脚本文件,用于查找 ffbuild/config.mak 中的编译参数:
#!/bin/bash
#if [ "$#" -ne 1 ]; then
# echo "Usage: $0 config_file"
# exit 1
#fi
# 获取配置文件
#config_file=$1
config_file=ffbuild/config.mak
# 定义需要查找的变量
vars=("CPPFLAGS" "CFLAGS" "CXXFLAGS" "ASFLAGS" "LDFLAGS")
# 定义需打印的 cpu 特定参数
cpu_specific_prefixes=("-mfpu" "-march")
# 定义需要过滤的标志
filter_prefixes=("CPPFLAGS" "CFLAGS" "CXXFLAGS" "ASFLAGS" "LDFLAGS" "--sysroot" "-mfpu" "-march" "-Wl,-rpath-link")
print_flags() {
# 将参数转换为数组
local -a local_filter_prefixes="($(echo "$1"))"
local local_should_print=$2
local var_prefix=$3
for var in "${vars[@]}"; do
flags=$(grep "^$var" "$config_file")
# 分割参数
IFS=' ' read -r -a flagarray <<< "$flags"
# 过滤重复参数参数
flagarray=($(printf "%s\n" "${flagarray[@]}" | sort -u))
echo "$3_$var: {"
for flag in "${flagarray[@]}"; do
# 检查参数是否需要过滤
if [ "$local_should_print" = true ] ; then
should_print=false
else
should_print=true
fi
for prefix in "${local_filter_prefixes[@]}"; do
if [[ "$flag" == $prefix* ]]; then
should_print=$local_should_print
fi
done
# 如果不需要过滤,则打印
if $should_print ; then
echo " \"$flag\","
fi
done
echo "}"
done
}
# 将数组转换为字符串并传递给函数
print_flags "${filter_prefixes[*]}" false common
echo -e "\n"
print_flags "${cpu_specific_prefixes[*]}" true cpu_specific
执行 ./find_flags.sh
指令,查找编译参数:
common_CPPFLAGS: {
"-D_FILE_OFFSET_BITS=64",
"-D_ISOC99_SOURCE",
"-D_LARGEFILE_SOURCE",
"-DPIC",
"-Dstrtod=avpriv_strtod",
"-DZLIB_CONST",
}
common_CFLAGS: {
"-fno-math-errno",
"-fno-signed-zeros",
"-fomit-frame-pointer",
"-fpic",
"-fPIC",
"-fPIE",
"-g",
"-mno-stackrealign",
"-mstack-alignment=16",
"-Os",
"-Oz",
"-pthread",
"-Qunused-arguments",
"-std=c11",
"-Wall",
"-Wdeclaration-after-statement",
"-Wdisabled-optimization",
"-Wempty-body",
"-Werror=implicit-function-declaration",
"-Werror=missing-prototypes",
"-Werror=return-type",
"-Wmissing-prototypes",
"-Wno-bool-operation",
"-Wno-char-subscripts",
"-Wno-format-zero-length",
"-Wno-parentheses",
"-Wno-pointer-sign",
"-Wno-switch",
"-Wno-unused-const-variable",
"-Wpointer-arith",
"-Wredundant-decls",
"-Wstrict-prototypes",
"-Wtype-limits",
"-Wundef",
"-Wwrite-strings",
}
common_CXXFLAGS: {
"-D__STDC_CONSTANT_MACROS",
"-std=c++11",
}
common_ASFLAGS: {
"-fpic",
"-fPIC",
"-g",
"-mno-stackrealign",
"-Os",
"-Qunused-arguments",
}
common_LDFLAGS: {
"-Qunused-arguments",
"-Wl,--as-needed",
"-Wl,--warn-common",
"-Wl,-z,noexecstack",
}
cpu_specific_CPPFLAGS: {
}
cpu_specific_CFLAGS: {
"-march=armv8-a",
"-mfpu=neon",
}
cpu_specific_CXXFLAGS: {
}
cpu_specific_ASFLAGS: {
"-march=armv8-a",
"-mfpu=neon",
}
cpu_specific_LDFLAGS: {
"-march=armv8-a",
}
将上述 cpu_specific_CPPFLAGS、cpu_specific_CFLAGS、cpu_specific_CXXFLAGS
添加到 Android.bp libFFmpeg 模块 arm64 cflags 中,common_CPPFLAGS、common_CFLAGS、common_CXXFLAGS
添加到全局编译选项中:
cc_library {
name: "libFFmpeg",
... 省略部分代码
arch: {
arm: {
... 省略部分代码
},
arm64: {
srcs: [
":avutil-src-arm64",
":avcodec-src-arm64",
":swresample-src-arm64",
":swrscale-src-arm64",
],
// ARM64 架构编译选项
cflags: [
"-mfpu=neon",
"-march=armv8-a",
],
// ARM64 架构链接选项
ldflags: [
],
// ARM64 架构汇编选项
asflags: [
],
},
},
... 省略部分代码
srcs: [
":compat-src",
":avutil-src-common",
":avcodec-src-common",
":swresample-src-common",
":swrscale-src-common",
":avformat-src",
],
// 全局编译选项
cflags: [
// CPPFLAGS
"-D_FILE_OFFSET_BITS=64",
"-D_ISOC99_SOURCE",
"-D_LARGEFILE_SOURCE",
"-DPIC",
"-Dstrtod=avpriv_strtod",
"-DZLIB_CONST",
// CFLAGS
"-fno-math-errno",
"-fno-signed-zeros",
"-fomit-frame-pointer",
"-fpic",
"-fPIC",
"-fPIE",
"-g",
"-mno-stackrealign",
"-mstack-alignment=16",
"-Os",
"-Oz",
"-pthread",
"-Qunused-arguments",
"-std=c11",
"-Wall",
"-Wdeclaration-after-statement",
"-Wdisabled-optimization",
"-Wempty-body",
"-Werror=implicit-function-declaration",
"-Werror=missing-prototypes",
"-Werror=return-type",
"-Wmissing-prototypes",
"-Wno-bool-operation",
"-Wno-char-subscripts",
"-Wno-format-zero-length",
"-Wno-parentheses",
"-Wno-pointer-sign",
"-Wno-switch",
"-Wno-unused-const-variable",
"-Wpointer-arith",
"-Wredundant-decls",
"-Wstrict-prototypes",
"-Wtype-limits",
"-Wundef",
"-Wwrite-strings",
// CXXFLAGS
"-D__STDC_CONSTANT_MACROS",
"-std=c++11",
],
// 全局链接选项
ldflags: [
],
// 全局汇编选项
asflags: [
],
}
4. 添加汇编参数
上一章节编译参数中的汇编参数,cpu_specific_ASFLAGS
添加到 Android.bp libFFmpeg 模块 arm64 asflags 中,common_ASFLAGS
添加到全局汇编选项中:
cc_library {
name: "libFFmpeg",
... 省略部分代码
arch: {
arm: {
... 省略部分代码
},
arm64: {
... 省略部分代码
// ARM64 架构链接选项
ldflags: [
],
// ARM64 架构汇编选项
asflags: [
"-march=armv8-a",
"-mfpu=neon",
],
},
},
... 省略部分代码
// 全局链接选项
ldflags: [
],
// 全局汇编选项
asflags: [
"-fpic",
"-fPIC",
"-g",
"-mno-stackrealign",
"-Os",
"-Qunused-arguments",
],
}
5. 添加链接参数
参考前面章节内容,过程不在赘述,部分代码如下:
cc_library {
name: "libFFmpeg",
... 省略部分代码
arch: {
arm: {
... 省略部分代码
},
arm64: {
... 省略部分代码
// ARM64 架构汇编选项
ldflags: [
"-march=armv8-a",
],
... 省略部分代码
},
},
... 省略部分代码
// 全局链接选项
ldflags: [
"-Qunused-arguments",
"-Wl,--as-needed",
"-Wl,--warn-common",
"-Wl,-z,noexecstack",
],
... 省略部分代码
}
6. HAVE_AV_CONFIG_H 参数
FFmpeg 源码执行完 configure 指定后,根目录下会生成一个 config.h 文件。该文件包含了编译 FFmpeg 时配置的所有配置选项。config.h 文件中的每个宏都代表一个特定的配置选项,例如是否启用某个库(如 libavcodec、libavformat 等),是否启用某个功能(如硬件加速),以及一些特定的编译选项(如优化级别、调试选项等)。
FFmpeg 源码编译时,会根据 HAVE_AV_CONFIG_H 宏来确认是否引用 config.h 文件,如 libavutil/bswap.h 中的代码:
#ifdef HAVE_AV_CONFIG_H
#include "config.h"
#endif
因此我们需要将 -DHAVE_AV_CONFIG_H 添加到编译选项中。在 Android.bp 文件中,我们将该选项添加到 libFFmpeg_defaults 模块:
// 编译选项默认值
cc_defaults {
name: "libFFmpeg_defaults",
cflags: [
"-DHAVE_AV_CONFIG_H",
],
... 省略部分代码
}
armv7-a 编译参数
1. 查看编译参数
参考前面章节 armv8-a 编译参数查找流程:
- configure FFmpeg 源代码,指定 cpu 架构为 armv8-a
- 使用 find_flags.sh 脚本查找编译参数
首先,比较 armv7-a 和 armv8-a 编译参数差异:
- 编译参数差异
- 汇编参数差异
- cpu 特定参数差异
2. 添加编译、汇编、链接参数
根据上一章节 armv7-a 和 armv8-a 编译参数差异,两种 cpu 架构绝大部分编译参数可复用,我们仅需将上述 armv7-a 特定参数添加到 Android.bp arm 架构特定编译选项中:
cc_library {
name: "libFFmpeg",
... 省略部分代码
arch: {
arm: {
instruction_set: "arm",
... 省略部分代码
// ARM 架构编译选项
cflags: [
"-marm",
"-mfloat-abi=softfp",
"-march=armv7-a",
"-mfpu=neon",
],
// ARM 架构链接选项
ldflags: [
"-march=armv7-a",
],
// ARM 架构汇编选项
asflags: [
"-mfloat-abi=softfp",
"-march=armv7-a",
"-mfpu=neon",
]
},
},
... 省略部分代码
}
添加 armv8-a 源文件
首先,我们需要使用 NDK 对源码进行编译,参考专栏之前的文章:使用 NDK 编译 FFmpeg 。然后根据编译后的 .o 目标文件,从源码中匹配对应的源文件(.c 或 .S 文件)。
1. 查找需编译的源文件
配置 cpu 架构为 armv8-a,并编译 FFmpeg 源代码。然后,在 FFmpeg 源码目录下,执行 vim find_srcs.sh
,编辑脚本文件如下:
#!/bin/bash
# 打印帮助提示
if [ "$#" -eq 0 ] || [ "$1" == "-h" ] || [ "$1" == "--help" ]; then
echo "Usage: $0 dir1 [dir2 ...]"
echo "Example: $0 libavutil libavutil/aarch64"
exit 0
fi
# 定义查找源文件的函数
findSrc() {
local dir=$1
local f=`ls $dir/*.o 2>/dev/null`
echo "$dir: {"
for d in $f
do
local s=${d//.o/.c}
if [ -f "$s" ]; then
echo " \"${d//.o/.c}\","
else
echo " \"${d//.o/.S}\","
fi
done
echo "}"
}
# 遍历每个参数并查找源文件
for dir in "$@"; do
echo "Source files in $dir:"
findSrc $dir
done
在源码目录下执行该脚本,查找需编译的源文件,示例如下:
- 执行
./find_srcs.sh compat
,查找 compat 目录
Source files in compat:
compat: {
"compat/strtod.c",
}
- 执行
./find_srcs.sh libavutil libavutil/aarch64
,查找 libavutil、libavutil/aarch64 目录
Source files in libavutil:
libavutil: {
"libavutil/adler32.c",
"libavutil/aes_ctr.c",
"libavutil/aes.c",
"libavutil/audio_fifo.c",
"libavutil/avsscanf.c",
"libavutil/avstring.c",
"libavutil/base64.c",
"libavutil/blowfish.c",
"libavutil/bprint.c",
"libavutil/buffer.c",
"libavutil/camellia.c",
"libavutil/cast5.c",
"libavutil/channel_layout.c",
"libavutil/color_utils.c",
"libavutil/cpu.c",
"libavutil/crc.c",
"libavutil/csp.c",
"libavutil/des.c",
"libavutil/detection_bbox.c",
"libavutil/dict.c",
"libavutil/display.c",
"libavutil/dovi_meta.c",
"libavutil/downmix_info.c",
"libavutil/encryption_info.c",
"libavutil/error.c",
"libavutil/eval.c",
"libavutil/fifo.c",
"libavutil/file.c",
"libavutil/file_open.c",
"libavutil/film_grain_params.c",
"libavutil/fixed_dsp.c",
"libavutil/float_dsp.c",
"libavutil/frame.c",
"libavutil/hash.c",
"libavutil/hdr_dynamic_metadata.c",
"libavutil/hdr_dynamic_vivid_metadata.c",
"libavutil/hmac.c",
"libavutil/hwcontext.c",
"libavutil/hwcontext_stub.c",
"libavutil/imgutils.c",
"libavutil/integer.c",
"libavutil/intmath.c",
"libavutil/lfg.c",
"libavutil/lls.c",
"libavutil/log2_tab.c",
"libavutil/log.c",
"libavutil/lzo.c",
"libavutil/mastering_display_metadata.c",
"libavutil/mathematics.c",
"libavutil/md5.c",
"libavutil/mem.c",
"libavutil/murmur3.c",
"libavutil/opt.c",
"libavutil/parseutils.c",
"libavutil/pixdesc.c",
"libavutil/pixelutils.c",
"libavutil/random_seed.c",
"libavutil/rational.c",
"libavutil/rc4.c",
"libavutil/reverse.c",
"libavutil/ripemd.c",
"libavutil/samplefmt.c",
"libavutil/sha512.c",
"libavutil/sha.c",
"libavutil/slicethread.c",
"libavutil/spherical.c",
"libavutil/stereo3d.c",
"libavutil/tea.c",
"libavutil/threadmessage.c",
"libavutil/timecode.c",
"libavutil/time.c",
"libavutil/tree.c",
"libavutil/twofish.c",
"libavutil/tx_double.c",
"libavutil/tx_float.c",
"libavutil/tx_int32.c",
"libavutil/tx.c",
"libavutil/utils.c",
"libavutil/uuid.c",
"libavutil/version.c",
"libavutil/video_enc_params.c",
"libavutil/xga_font_data.c",
"libavutil/xtea.c",
}
Source files in libavutil/aarch64:
libavutil/aarch64: {
"libavutil/aarch64/cpu.c",
"libavutil/aarch64/float_dsp_init.c",
"libavutil/aarch64/float_dsp_neon.S",
}
2. 添加 compat 源文件
编辑 compat_files.bp 文件,添加上一章节中查找到的 compat 源文件:
filegroup {
name: "compat-src",
srcs: [
"compat/strtod.c",
],
}
3. 添加 libavutil 源文件
编辑 avutil_files.bp 文件,添加上一章节中查找到的 libavutil 源文件。libavutil/aarch64、libavutil 目录下源文件分别添加到 avutil-src-arm64、avutil-src-common 目录下:
filegroup {
name: "avutil-src-arm",
// ARM 架构代码
srcs: [
],
}
filegroup {
name: "avutil-src-arm64",
// ARM64 架构代码
srcs: [
"libavutil/aarch64/cpu.c",
"libavutil/aarch64/float_dsp_init.c",
"libavutil/aarch64/float_dsp_neon.S",
],
}
filegroup {
name: "avutil-src-common",
// 多平台共用代码
srcs: [
"libavutil/adler32.c",
"libavutil/aes_ctr.c",
"libavutil/aes.c",
"libavutil/audio_fifo.c",
"libavutil/avsscanf.c",
"libavutil/avstring.c",
"libavutil/base64.c",
"libavutil/blowfish.c",
"libavutil/bprint.c",
"libavutil/buffer.c",
"libavutil/camellia.c",
"libavutil/cast5.c",
"libavutil/channel_layout.c",
"libavutil/color_utils.c",
"libavutil/cpu.c",
"libavutil/crc.c",
"libavutil/csp.c",
"libavutil/des.c",
"libavutil/detection_bbox.c",
"libavutil/dict.c",
"libavutil/display.c",
"libavutil/dovi_meta.c",
"libavutil/downmix_info.c",
"libavutil/encryption_info.c",
"libavutil/error.c",
"libavutil/eval.c",
"libavutil/fifo.c",
"libavutil/file.c",
"libavutil/file_open.c",
"libavutil/film_grain_params.c",
"libavutil/fixed_dsp.c",
"libavutil/float_dsp.c",
"libavutil/frame.c",
"libavutil/hash.c",
"libavutil/hdr_dynamic_metadata.c",
"libavutil/hdr_dynamic_vivid_metadata.c",
"libavutil/hmac.c",
"libavutil/hwcontext.c",
"libavutil/hwcontext_stub.c",
"libavutil/imgutils.c",
"libavutil/integer.c",
"libavutil/intmath.c",
"libavutil/lfg.c",
"libavutil/lls.c",
"libavutil/log2_tab.c",
"libavutil/log.c",
"libavutil/lzo.c",
"libavutil/mastering_display_metadata.c",
"libavutil/mathematics.c",
"libavutil/md5.c",
"libavutil/mem.c",
"libavutil/murmur3.c",
"libavutil/opt.c",
"libavutil/parseutils.c",
"libavutil/pixdesc.c",
"libavutil/pixelutils.c",
"libavutil/random_seed.c",
"libavutil/rational.c",
"libavutil/rc4.c",
"libavutil/reverse.c",
"libavutil/ripemd.c",
"libavutil/samplefmt.c",
"libavutil/sha512.c",
"libavutil/sha.c",
"libavutil/slicethread.c",
"libavutil/spherical.c",
"libavutil/stereo3d.c",
"libavutil/tea.c",
"libavutil/threadmessage.c",
"libavutil/timecode.c",
"libavutil/time.c",
"libavutil/tree.c",
"libavutil/twofish.c",
"libavutil/tx_double.c",
"libavutil/tx_float.c",
"libavutil/tx_int32.c",
"libavutil/tx.c",
"libavutil/utils.c",
"libavutil/uuid.c",
"libavutil/version.c",
"libavutil/video_enc_params.c",
"libavutil/xga_font_data.c",
"libavutil/xtea.c",
],
}
4. 添加其他模块源文件
从 ffbuild/config.sh 中查看模块间依赖关系:
swscale_deps="avutil"
avformat_deps="avcodec swresample avutil"
avcodec_deps="swresample avutil"
swresample_deps="avutil"
根据上述依赖关系依次添加 libswscale、libswresample、libavcodec、libavformat 模块源文件(参考前面章节添加 compat 和 libavutil 源文件的过程,本章节不再赘述)。需要查找的目录分为以下几类,完整的 bp 文件请查找后续章节代码仓库:
- libxxx/arm、libxxx/aarch64:平台相关源文件路径
- libavxxx、libswxxx:公共源文件路径
- libxxx/neon:neon 相关源文件路径
添加 armv7-a 源文件
除平台相关代码(位于 libxxx/arm,如 libavtuil/arm),其余代码可与 armv8-a 复用。
1. 重新编写 config.h 文件
为了同时兼容 armv8-a 和 armv7-a,编译时需根据 cpu 架构,引用不同的 config.h。因此,我们需要重新编写 config.h 文件:
- 配置 cpu 为 armv8-a,将 config.h 文件重命名为 config_arm64.h
- 执行
make clean
,配置 cpu 架构为 armv7-a 并重新编译 FFmpeg 源码 - 将生成的 config.h 重命名为 config_arm.h
- 对比 config_arm64.h 和 config_arm.h,发现 config.h 使用了 ARCH_AARCH64 和 ARCH_ARM 来区分不同的 cpu 架构
- 将 ARCH_AARCH64、ARCH_ARM 宏定义移到 Android.bp 的 libFFmpeg_defaults 模块编译和汇编选项中
// 编译选项默认值
cc_defaults {
name: "libFFmpeg_defaults",
cflags: [
"-DHAVE_AV_CONFIG_H",
],
arch: {
arm: {
cflags: [
"-DARCH_ARM=1",
"-DARCH_AARCH64=0",
],
asflags: [
"-DARCH_ARM=1",
"-DARCH_AARCH64=0",
],
},
arm64: {
cflags: [
"-DARCH_AARCH64=1",
"-DARCH_ARM=0",
],
asflags: [
"-DARCH_AARCH64=1",
"-DARCH_ARM=0",
],
},
},
}
- 重新创建和编辑 config.h 文件,根据 cpu 架构使用不同的配置
#if ARCH_AARCH64
#include "config_arm64.h"
#elif ARCH_ARM
#include "config_arm.h"
#else
#error "Unsupported CPU architecture"
#endif
2. 查找和添加源文件
参考前面 添加 armv8-a 源文件 章节的内容,查找 libavutil/arm、libswscale/arm、libswresample/arm、libavcodec/arm 目录下 armv7-a 平台相关源文件,并添加到对应 bp 文件中,以 libavutil/arm 为例:
- 执行
./find_srcs.sh libavutil/arm
,查找 libavutil/arm 目录下需编译的源文件
Source files in libavutil/arm:
libavutil/arm: {
"libavutil/arm/cpu.c",
"libavutil/arm/float_dsp_init_arm.c",
"libavutil/arm/float_dsp_init_neon.c",
"libavutil/arm/float_dsp_init_vfp.c",
"libavutil/arm/float_dsp_neon.S",
"libavutil/arm/float_dsp_vfp.S",
}
- 添加源文件到 avutil_files.bp 中
filegroup {
name: "avutil-src-arm",
// ARM 架构代码
srcs: [
"libavutil/arm/cpu.c",
"libavutil/arm/float_dsp_init_arm.c",
"libavutil/arm/float_dsp_init_neon.c",
"libavutil/arm/float_dsp_init_vfp.c",
"libavutil/arm/float_dsp_neon.S",
"libavutil/arm/float_dsp_vfp.S",
],
}
参考上述流程,依次添加源文件到其余 bp 文件,过程不再赘述。完整的 bp 文件请查找后续章节代码仓库。
编译错误处理
- 各模块中存在一些相同的源文件。如
llibavutil/log2_tab.c
,只能添加在 avutil_files.bp 中,否则会导致编译错误ld.lld: error: duplicate symbol: ff_log2_tab
。使用cat ./libavformat/log2_tab.c
指令,可以发现 libavformat 等模块其实是引用了 llibavutil 目录中的 log2_tab.c 文件,#include "libavutil/log2_tab.c"
./libavformat/log2_tab.c
./libavutil/log2_tab.c
./libavcodec/log2_tab.c
./libswscale/log2_tab.c
./libswresample/log2_tab.c
./libavfilter/log2_tab.c
类似的源文件还有
libavutil/reverse.c
libavcodec/mpegaudiotabs.c
libavcodec/to_upper4.c
libavcodec/mpeg4audio_sample_rates.c
libavcodec/jpegtables.c
libavcodec/ac3_channel_layout_tab.c
libavcodec/dca_sample_rate_tab.c
libavcodec/golomb.c(在 libavformat 中被 libavformat/golomb_tab.c 引用)
- 添加
-Wl,-Bsymbolic
到全局ldflags
中处理以下类型的链接错误,详细说明请查阅最后章节中参考资料
ld.lld: error: relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol ff_cos_32; recompile with -fPIC
编译源代码
至此,我们已搭建好构建 FFmpeg 库的完整框架,将代码拷贝到 Android 源码路径下,例如:vendor/qcom/packages/apps/ffmpeg-5.1.4,进入源码目录,执行 Android 编译指令 mma
,即可编译 FFmpeg 源码。当前代码已在 Android 10 和 Android 11 平台编译测试通过。编译完成后,在 Android 源码 out 路径下执行 find . -name "libFFmpeg.so"
,即可看到已生成适用于 arm 及 arm64 平台的库文件:
./target/product/msmnile_gvmq/system/product/lib/libFFmpeg.so
./target/product/msmnile_gvmq/system/product/lib64/libFFmpeg.so
./target/product/msmnile_gvmq/system/lib/libFFmpeg.so
./target/product/msmnile_gvmq/system/lib64/libFFmpeg.so
./target/product/msmnile_gvmq/vendor/lib/libFFmpeg.so
./target/product/msmnile_gvmq/vendor/lib64/libFFmpeg.so文章来源:https://www.toymoban.com/news/detail-841232.html
整体目录结构
下图左侧为 FFmpeg 未修改过的源码,右侧为修改且 configure 后的源码:
绿框部分为我们新创建的文件,黄框为修改后的 config.h 配置文件,其余高亮部分由 configure 指令生成文件。文章来源地址https://www.toymoban.com/news/detail-841232.html
源码仓库
- github 仓库:FFmpegDroidBuilder
参考资料
- C语言解决动态库符号冲突_-wl,-bsymbolic
到了这里,关于Android.bp 构建 FFmpeg 库:从搭建编译框架到处理编译错误的全过程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!