使用 clang 编译 FFmpeg

一、 准备工作

  • 下载 FFmpeg、 最新版 ndk(上篇文章已经提及)
  • 整理下文件(当然用你喜欢的就行, 只要配置的路径对就没问题)

二、 明确使用 target-os=android, 使用 clang 进行编译

2.1 明确 clang 编译环境的位置

1
2
# 在相应 ndk 的这个目录下
ndk/toolchains/llvm/prebuilt/darwin-x86_64

让我们看看 clang 等编译工具的内容(bin目录)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
aarch64-linux-android-addr2line		i686-linux-android-gprof
aarch64-linux-android-ar i686-linux-android-ld
aarch64-linux-android-as i686-linux-android-ld.bfd
aarch64-linux-android-c++filt i686-linux-android-ld.gold
aarch64-linux-android-dwp i686-linux-android-nm
aarch64-linux-android-elfedit i686-linux-android-objcopy
aarch64-linux-android-gprof i686-linux-android-objdump
aarch64-linux-android-ld i686-linux-android-ranlib
aarch64-linux-android-ld.bfd i686-linux-android-readelf
aarch64-linux-android-ld.gold i686-linux-android-size
aarch64-linux-android-nm i686-linux-android-strings
aarch64-linux-android-objcopy i686-linux-android-strip
aarch64-linux-android-objdump i686-linux-android16-clang
aarch64-linux-android-ranlib i686-linux-android16-clang++
aarch64-linux-android-readelf i686-linux-android17-clang
aarch64-linux-android-size i686-linux-android17-clang++
aarch64-linux-android-strings i686-linux-android18-clang
aarch64-linux-android-strip i686-linux-android18-clang++
aarch64-linux-android21-clang i686-linux-android19-clang
aarch64-linux-android21-clang++ i686-linux-android19-clang++
aarch64-linux-android22-clang i686-linux-android21-clang
aarch64-linux-android22-clang++ i686-linux-android21-clang++
aarch64-linux-android23-clang i686-linux-android22-clang
aarch64-linux-android23-clang++ i686-linux-android22-clang++
aarch64-linux-android24-clang i686-linux-android23-clang
aarch64-linux-android24-clang++ i686-linux-android23-clang++
aarch64-linux-android26-clang i686-linux-android24-clang
aarch64-linux-android26-clang++ i686-linux-android24-clang++
aarch64-linux-android27-clang i686-linux-android26-clang
aarch64-linux-android27-clang++ i686-linux-android26-clang++
aarch64-linux-android28-clang i686-linux-android27-clang
aarch64-linux-android28-clang++ i686-linux-android27-clang++
aarch64-linux-android29-clang i686-linux-android28-clang
aarch64-linux-android29-clang++ i686-linux-android28-clang++
arm-linux-androideabi-addr2line i686-linux-android29-clang
arm-linux-androideabi-ar i686-linux-android29-clang++
arm-linux-androideabi-as ld.lld
arm-linux-androideabi-c++filt llvm-ar
arm-linux-androideabi-dwp llvm-as
arm-linux-androideabi-elfedit llvm-config
arm-linux-androideabi-gprof llvm-cov
arm-linux-androideabi-ld llvm-dis
arm-linux-androideabi-ld.bfd llvm-link
arm-linux-androideabi-ld.gold llvm-modextract
arm-linux-androideabi-nm llvm-nm
arm-linux-androideabi-objcopy llvm-objcopy
arm-linux-androideabi-objdump llvm-profdata
arm-linux-androideabi-ranlib llvm-readobj
arm-linux-androideabi-readelf llvm-strip
arm-linux-androideabi-size llvm-symbolizer
arm-linux-androideabi-strings pkg-config
arm-linux-androideabi-strip sancov
armv7a-linux-androideabi16-clang sanstats
armv7a-linux-androideabi16-clang++ scan-build
armv7a-linux-androideabi17-clang scan-view
armv7a-linux-androideabi17-clang++ x86_64-linux-android-addr2line
armv7a-linux-androideabi18-clang x86_64-linux-android-ar
armv7a-linux-androideabi18-clang++ x86_64-linux-android-as
armv7a-linux-androideabi19-clang x86_64-linux-android-c++filt
armv7a-linux-androideabi19-clang++ x86_64-linux-android-dwp
armv7a-linux-androideabi21-clang x86_64-linux-android-elfedit
armv7a-linux-androideabi21-clang++ x86_64-linux-android-gprof
armv7a-linux-androideabi22-clang x86_64-linux-android-ld
armv7a-linux-androideabi22-clang++ x86_64-linux-android-ld.bfd
armv7a-linux-androideabi23-clang x86_64-linux-android-ld.gold
armv7a-linux-androideabi23-clang++ x86_64-linux-android-nm
armv7a-linux-androideabi24-clang x86_64-linux-android-objcopy
armv7a-linux-androideabi24-clang++ x86_64-linux-android-objdump
armv7a-linux-androideabi26-clang x86_64-linux-android-ranlib
armv7a-linux-androideabi26-clang++ x86_64-linux-android-readelf
armv7a-linux-androideabi27-clang x86_64-linux-android-size
armv7a-linux-androideabi27-clang++ x86_64-linux-android-strings
armv7a-linux-androideabi28-clang x86_64-linux-android-strip
armv7a-linux-androideabi28-clang++ x86_64-linux-android21-clang
armv7a-linux-androideabi29-clang x86_64-linux-android21-clang++
armv7a-linux-androideabi29-clang++ x86_64-linux-android22-clang
bisect_driver.py x86_64-linux-android22-clang++
clang x86_64-linux-android23-clang
clang++ x86_64-linux-android23-clang++
clang-check x86_64-linux-android24-clang
clang-format x86_64-linux-android24-clang++
clang-tidy x86_64-linux-android26-clang
clang-tidy.real x86_64-linux-android26-clang++
git-clang-format x86_64-linux-android27-clang
i686-linux-android-addr2line x86_64-linux-android27-clang++
i686-linux-android-ar x86_64-linux-android28-clang
i686-linux-android-as x86_64-linux-android28-clang++
i686-linux-android-c++filt x86_64-linux-android29-clang
i686-linux-android-dwp x86_64-linux-android29-clang++
i686-linux-android-elfedit yasm

我们可以在这里发现不同 Android 版本和架构的编译工具。 通过查看里面的编译工具我们发现, clang、 clang++ 是不但分架构还分 Android 版本的, 而其他工具只分架构。 比如我选几个哈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
armv7a-linux-androideabi16-clang 
armv7a-linux-androideabi16-clang++
armv7a-linux-androideabi29-clang
armv7a-linux-androideabi29-clang++

arm-linux-androideabi-addr2line
arm-linux-androideabi-ar
arm-linux-androideabi-as
arm-linux-androideabi-c++filt
arm-linux-androideabi-dwp
arm-linux-androideabi-elfedit
arm-linux-androideabi-gprof
arm-linux-androideabi-ld
arm-linux-androideabi-ld.bfd
arm-linux-androideabi-ld.gold
arm-linux-androideabi-nm
arm-linux-androideabi-objcopy
arm-linux-androideabi-objdump
arm-linux-androideabi-ranlib
arm-linux-androideabi-readelf
arm-linux-androideabi-size
arm-linux-androideabi-strings
arm-linux-androideabi-strip

发现问题了吗, 前缀不一样耶。 这个问题在配置 configure 时需要特别注意。(这里知道前缀不一样就好了)

2.2 对比 configure 源码明确问题所在

在查看 configure 时 发现如下代码:

1
2
3
4
5
6
7
8
9
10
set_default target_os
if test "$target_os" = android; then
cc_default="clang"
fi

ar_default="${cross_prefix}${ar_default}"
cc_default="${cross_prefix}${cc_default}"
cxx_default="${cross_prefix}${cxx_default}"
nm_default="${cross_prefix}${nm_default}"
pkg_config_default="${cross_prefix}${pkg_config_default}"

发现问题了吗?

如果 target-os 设置的是 Android 的话, 那么 使用的 clang 去编译呢。
我们先不管 ar_default、cc_default、cxx_default、….等等的默认值,我们先看最后几行各个工具的拼接, 发现都是 cross_prefix 去拼接的。 cross_prefix 就是我们在设置 configure 里配置的 –cross-prefix 前缀。 发现问题没, 发现问题没, 发现问题没, 我们在 2.1 中刚看到, 很明显 clang、 clang++ 的前缀和 其他工具的不一样。 所以这个地方很矛盾呀。 为了通用,我们可以定义一个参数在外部去单独设置 clang 和 clang++ 的前缀(这里我随便命名成–cross-prefix-clang)。跟设置 –cross-prefix 一样。

现在看下 ar_default、 cc_default、 cxx_default、 nm_default、 pkg_config_default等还有其他工具的默认名字, 可以在configure中全局搜索这个名字看默认值, 发现 ar_default 默认值是ar, 如果拼上前缀的话(–cross-prefix + ar),在编译工具中是存在这个工具的, 其他的也一样。 但是 clang 和 clang++ 也不行, 不存在的, 我们看到在上面已经把 cc_default=”clang”了, 但是 clang++ 还没有, 所以在上面需要手动添加一个 cxx_default=”clang++”, 这样的话(–cross-prefix-clang + clang 或者 + clang++) 才存在哦!

2.3 明确所需要的头文件和库

使用了 clang 编译就需要指定 clang 的头文件和库

1
2
使用该目录下的头文件和库, 在编写脚本时, 引入即可
/ndk/toolchains/llvm/prebuilt/darwin-x86_64/sysroot

三、 修改 configure

  • 最新版已经支持 target-os 设置成 android, 所以不用再像以前那样去修改那四个值了。

  • 通过二中我们知道要增加一个变量 –cross-prefix-clang(随便命名)

1
2
3
--cross-prefix=PREFIX    use PREFIX for compilation tools [$cross_prefix]
# 这里添加
--cross-prefix-clang=PREFIX use PREFIX for compilation clang tools [$cross_prefix]

先设置一个帮助信息呢, 规范嘛要

  • 在 CMDLINE_SET 中把新增的变量加入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CMDLINE_SET="
$PATHS_LIST
ar
arch
as
assert_level
build_suffix
cc
objcc
cpu
cross_prefix
# 这里添加
cross_prefix_clang
custom_allocator
cxx
  • 修改 clang++ 编译的默认值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 set_default target_os
if test "$target_os" = android; then
cc_default="clang"
# 这里修改 默认值
cxx_default="clang++"
fi
ar_default="${cross_prefix}${ar_default}"
# 这里修改成我们新定义的前缀
cc_default="${cross_prefix_clang}${cc_default}"
# 这里修改成我们新定义的前缀
cxx_default="${cross_prefix_clang}${cxx_default}"
nm_default="${cross_prefix}${nm_default}"
pkg_config_default="${cross_prefix}${pkg_config_default}"

  • 修改完后重新 ./configure

四、 编写脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 #!/bin/bash
export TMPDIR=../temp
# 定义变量(可以不定义, 直接在下面写死也行, 这样不是更加清晰复用和简易嘛)
SYSROOT=/Users/liushuai/ffmpeg_2/ndk/toolchains/llvm/prebuilt/darwin-x86_64/sysroot
# 定义变量(可以不定义, 直接在下面写死也行, 这样不是更加清晰复用和简易嘛)
PLATFORM=/Users/liushuai/ffmpeg_2/ndk/toolchains/llvm/prebuilt/darwin-x86_64
function build
{
./configure \
--prefix=$PREFIX \
--target-os=android \
--arch=$CPU \
--enable-shared \
--disable-static \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-avdevice \
--disable-symver \
--enable-cross-compile \
--sysroot=$SYSROOT \
--cross-prefix=$PLATFORM/bin/arm-linux-androideabi- \
--cross-prefix-clang=$PLATFORM/bin/armv7a-linux-androideabi16- \
--extra-cflags="-I$SYSROOT/usr/include" \
--extra-ldflags="-L$SYSROOT/usr/lib"
$ADDITIONAL_CONFIGURE_FLAG
make clean
make -j4
make install
}
CPU=armv7-a
PREFIX=../os
build

你会发现直接编译完,动态库生成了!!!, 没有发现中间有任何问题