–extra-cflags=“-isysroot $SYSROOT”
-isysroot
的作用就是,把后面的路径设置为默认的头文件搜索路径,这时候,前面 sysroot
配置路径就不再作为 头文件
默认的搜索路径了,不过依然是 库文件
默认的搜索路径。
可以看到,这两个配置从某种程度上说是一样的:
–extra-cflags=“-I$SYSROOT/usr/include”
约等于
–extra-cflags=“-isysroot $SYSROOT”
- extra-ldflags
这个和上面的 extra-cflags
作用是类似的,不过是用于配置额外的 库文件
搜索路径,如
–extra-ldflags=“-L$SYSROOT/usr/lib”
其中 -L 用于区分不同的路径
可以看到 extra-cflags
extra-ldflags
结合起来可以替代 sysroot
。
- cross-prefix
这个选项直译为 交叉编译前缀
,指的是交叉编译工具的前缀。
这个选项经常和另外一个选项 cc
一起出现搭配使用。
这是什么意思呢?网上有的文章对于 cc
这个选项经常出现两种配置方式:
一种是只配置 cross-prefix
,没有配置 cc
,比如本文。
另一种是既配置 cross-prefix
,又配置 cc
。
比如:
–cc= T O O L C H A I N / b i n / a r m − l i n u x − a n d r o i d e a b i − g c c − − c r o s s − p r e f i x = TOOLCHAIN/bin/arm-linux-androideabi-gcc \ --cross-prefix= TOOLCHAIN/bin/arm−linux−androideabi−gcc −−cross−prefix=TOOLCHAIN/bin/arm-linux-androideabi- \
这是两种完全不同的配置方式,但是很神奇的是有时候他们都能成功编译,有时候又会出现找不到编译链工具的错误。
为了搞明白 cross-prefix
cc
这两个选项的配置到底有什么影响,到底应该怎么使用这两个配置,我特地仔细的去看了 FFmpeg
根目录下的 configure
配置脚本,找到了一些蛛丝马迹。
分析 configure 配置脚本
注:以下分析基于ffmpeg-4.2.2版本,其他版本可能有所不同,掌握基本原理即可。
- 获取用户配置选项
打开(注:不是双击运行)configure
shell脚本,首先来看看 configure 是如何获取用户配置的编译选项的。
搜索 for opt do
,可以找到以下代码
for opt do
optval=“KaTeX parse error: Expected '}', got '#' at position 5: {opt#̲*=}" case "opt” in
–extra-ldflags=)
add_ldflags $optval
;;
–extra-ldexeflags=)
add_ldexeflags $optval
;;
–extra-ldsoflags=)
add_ldsoflags $optval
;;
–extra-ldlibflags=)
warn “The --extra-ldlibflags option is only provided for compatibility and will be\n”
“removed in the future. Use --extra-ldsoflags instead.”
add_ldsoflags $optval
;;
–extra-libs=*)
add_extralibs $optval
;;
–disable-devices)
disable $INDEV_LIST
O
U
T
D
E
V
L
I
S
T
;
;
−
−
e
n
a
b
l
e
−
d
e
b
u
g
=
∗
)
d
e
b
u
g
l
e
v
e
l
=
"
OUTDEV_LIST ;; --enable-debug=*) debuglevel="
OUTDEVLIST;;−−enable−debug=∗)debuglevel="optval"
;;
省略中间一些代码…
*)
optname=“KaTeX parse error: Expected '}', got 'EOF' at end of input: …%=*}" optname="{optname#–}”
optname=
(
e
c
h
o
"
(echo "
(echo"optname" | sed ‘s/-/_/g’)
if is_in $optname $CMDLINE_SET; then
eval
o
p
t
n
a
m
e
=
′
optname='
optname=′optval’
elif is_in $optname $CMDLINE_APPEND; then
append
o
p
t
n
a
m
e
"
optname "
optname"optval"
else
die_unknown $opt
fi
;;
esac
done
这个shell脚本的代码有很多特有的语法,也不用钻牛角尖,能大概看明白就可以了。
for循环的首行 通过分割 =
获取到用户设置的选项值 optval
。
下面除了一些特殊的选项,我们看看最后的通配符 *)
,这段代码的目的,其实就是把用户配置的选项和值关联起来。
比如 --cpu=armv7-a
,前面三行就是把 cpu
分割出来,赋值给 optname
,再把 optval
赋值给 cpu
,说白了就是初始化了 cpu
这个变量为 armv7-a
。
- Android相关的配置
搜索 android
关键字,可以找到以下代码
ffmpeg-4.2.2/configure
if test “$target_os” = android; then
cc_default=“clang”
fi
ar_default=“
c
r
o
s
s
p
r
e
f
i
x
{cross_prefix}
crossprefix{ar_default}”
cc_default=“
c
r
o
s
s
p
r
e
f
i
x
{cross_prefix}
crossprefix{cc_default}”
cxx_default=“
c
r
o
s
s
p
r
e
f
i
x
{cross_prefix}
crossprefix{cxx_default}”
nm_default=“
c
r
o
s
s
p
r
e
f
i
x
{cross_prefix}
crossprefix{nm_default}”
pkg_config_default=“
c
r
o
s
s
p
r
e
f
i
x
{cross_prefix}
crossprefix{pkg_config_default}”
当你配置了 --target-os=android
的时候,FFmpeg默认的编译工具为 clang
。
cc_default
其实就是配置项 cc
的默认值,可以看到 cc_default
在这里和 cross_prefix
做了拼接。这里就是为什么说 cross_prefix
是交叉编译工具前缀。
拼接完是这样的:
cc_defalut= T O O L C H A I N / b i n / a r m − l i n u x − a n d r o i d e a b i − TOOLCHAIN/bin/arm-linux-androideabi- TOOLCHAIN/bin/arm−linux−androideabi−cc
看下 ar_default
cc_default
cxx_default
这些默认值是什么。
搜索 cc_default
可以找到以下代码
ffmpeg-4.2.2/configure
ar_default=“ar”
cc_default=“gcc”
cxx_default=“g++”
host_cc_default=“gcc”
可以看到,FFmpeg 默认的编译工具是 GCC
。
当你编译 Android 平台的库时,由于 configure
强制设置 cc_default="clang"
,所以:
-
当你使用
GCC
作为编译工具时,必须配置cc
选项,或修改configure
中的cc_default="clang"
为cc_default="gcc"
; -
当你使用
CLANG
作为编译工具时,可以不配置cc
选项。
仔细想想会发现,为什么当 cc
配置为下边的值时,也可以正常编译呢?
–cc=$TOOLCHAIN/bin/arm-linux-androideabi-gcc
这时 cc_defalut
不就等于
cc_defalut= T O O L C H A I N / b i n / a r m − l i n u x − a n d r o i d e a b i − TOOLCHAIN/bin/arm-linux-androideabi- TOOLCHAIN/bin/arm−linux−androideabi−TOOLCHAIN/bin/arm-linux-androideabi-gcc
这个路径肯定是错的啊!
这就要来看到底 cc_default
是怎么使用的了。
- 初始化变量
搜索 set_default arch
,可以看到以下代码,在这里 configure
重新设置了 cc
的默认值。
set_default arch cc cxx doxygen pkg_config ranlib strip sysinclude
target_exec x86asmexe nvcc
这里调用了一个叫 set_default
的函数,来看看这个函数的实现
set_default(){
for opt; do
eval : ${$opt:=?{opt}_default}
done
}
这也是一个看不太懂的shell语法,大概的意思就是:for循环获取所有的输入参数变量,然后给这个变量赋值。
比如 set_default cc
,意思就是 cc=cc_default
,不过有一点要注意的是中间这个符号 :=
。
这个符号类似Java中的三目运算符:
opt != null? opt:opt_defalut
也就是说,如果参数为空,将 xx_default
赋值给 xx
。
这就可以解释上面的疑问了。
- 当配置
–cc=$TOOLCHAIN/bin/arm-linux-androideabi-gcc
set_default cc
等于没有用了。因为经过 for
循环获取了用户的配置以后, cc
不为空。 set_default
后,cc
的值是不会改变的。
-
当
cc
不配置的时候,FFmpeg 根据默认的拼接方式,把拼接好的路径设置给cc
。 -
但是,不能配置
cc=gcc
这种,这样,最后cc
的值就只有gcc
,肯定是不能正确找到编译工具的。
- 为什么要加入
corss-prefix-clang
这个选项
现在可以来解释为什么前面需要修改 configure
配置脚本了。
原始的配置是这样的
ar_default=“
c
r
o
s
s
p
r
e
f
i
x
{cross_prefix}
crossprefix{ar_default}”
cc_default=“
c
r
o
s
s
p
r
e
f
i
x
{cross_prefix}
crossprefix{cc_default}”
cxx_default=“
c
r
o
s
s
p
r
e
f
i
x
{cross_prefix}
crossprefix{cxx_default}”
nm_default=“
c
r
o
s
s
p
r
e
f
i
x
{cross_prefix}
crossprefix{nm_default}”
pkg_config_default=“
c
r
o
s
s
p
r
e
f
i
x
{cross_prefix}
crossprefix{pkg_config_default}”
也就是说,默认的 cc
ar
nm
路径前缀是一样的,但是 Android NDK
的路径却是这样的
看到了不?ar
/nm
和 cc
的前缀是不一样的,前者是 arm-linux-androideabi-
, 后者是 armv7a-linux-androideabi16-
。
因此,需要对 cc
和 cxx
两个前缀进行修改,为此新加了 cross_prefix_clange
来进行单独配置。
这里只是针对 NDK r20b 的情况,不同的 NDK 版本可能有所不同,根据这个原理去设置即可。
综上,解释了一些编译 FFmpeg 常用的配置选项,并且从原理上弄明白为何要这样配置,基本上搞清楚了这些,想要组合两个不同版本的FFmpeg和NDK来编译,都会比较容易实现。
启动编译
打开cmd终端,cd 到 FFmpeg 所在目录
输入 ./build_android_clang.sh
等待编译完成,将会在 ffmpeg/android/armv7-a目录下得到 include
和 lib
两个目录,分别是 头文件
和 so库文件
五、使用 GCC 编译FFmpeg
目前大部分网上的文章都是使用 GCC
来编译 FFmpeg
的,下面就来看看如何配置 GCC
的编译参数。
下载 Android NDK r17b
前面就说过,NDK r17c 以后,Googole 就移除了 GCC,所以要使用 GCC 只能下载 r17c 及以前的版本,本文使用 r17c 来编译。
根据自己编译平台选择对应的版本:NDK r17c
本文选择的是 Mac 版本:Mac OS X。
NDK 相关的环境路径
和 NDK r20b
相比,NDK r17c
的目录稍微有些变化。
- 交叉编译环境路径
库文件路径
android-ndk-r17c/platforms/android-21/arch-arm/usr/lib
头文件路径
android-ndk-r17c/sysroot/usr/include
- GCC 工具链路径
android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin
可以看到,Google 将 头文件
和 库文件
分离了,这也是很多新手在编译的时候一直没有配对路径,导致编译失败的原因。
新建编译配置脚本
FFmpeg 的版本依然是使用上面的 ffmpeg-4.2.2
, 当然,这次不需要修改 configure
了。
根据前面介绍的知识,很容易就能写出编译配置了
在 ffmpeg-4.2.2
根目录新建脚本: build_android_gcc.sh
#!/bin/bash
set -x
API=21
CPU=armv7-a
#so库输出目录
OUTPUT=/Users/cxp/Desktop/FFmpeg/ffmpeg-4.2.2/android/$CPU
NDK的路径,根据自己的安装位置进行设置
NDK=/Users/cxp/Desktop/FFmpeg/android-ndk-r17c
库文件
SYSROOT= N D K / p l a t f o r m s / a n d r o i d − NDK/platforms/android- NDK/platforms/android−API/arch-arm
头文件
ISYSROOT=$NDK/sysroot/usr/include
汇编头文件
ASM= I S Y S R O O T / a r m − l i n u x − a n d r o i d e a b i T O O L C H A I N = ISYSROOT/arm-linux-androideabi TOOLCHAIN= ISYSROOT/arm−linux−androideabiTOOLCHAIN=NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
function build
{
./configure
–prefix=
O
U
T
P
U
T
−
−
t
a
r
g
e
t
−
o
s
=
a
n
d
r
o
i
d
−
−
a
r
c
h
=
a
r
m
−
−
c
p
u
=
a
r
m
v
7
−
a
−
−
e
n
a
b
l
e
−
a
s
m
−
−
e
n
a
b
l
e
−
c
r
o
s
s
−
c
o
m
p
i
l
e
−
−
e
n
a
b
l
e
−
s
h
a
r
e
d
−
−
d
i
s
a
b
l
e
−
s
t
a
t
i
c
−
−
d
i
s
a
b
l
e
−
d
o
c
−
−
d
i
s
a
b
l
e
−
f
f
p
l
a
y
−
−
d
i
s
a
b
l
e
−
f
f
p
r
o
b
e
−
−
d
i
s
a
b
l
e
−
s
y
m
v
e
r
−
−
d
i
s
a
b
l
e
−
f
f
m
p
e
g
−
−
s
y
s
r
o
o
t
=
OUTPUT \ --target-os=android \ --arch=arm \ --cpu=armv7-a \ --enable-asm \ --enable-cross-compile \ --enable-shared \ --disable-static \ --disable-doc \ --disable-ffplay \ --disable-ffprobe \ --disable-symver \ --disable-ffmpeg \ --sysroot=
OUTPUT −−target−os=android −−arch=arm −−cpu=armv7−a −−enable−asm −−enable−cross−compile −−enable−shared −−disable−static −−disable−doc −−disable−ffplay −−disable−ffprobe −−disable−symver −−disable−ffmpeg −−sysroot=SYSROOT
–cc=
T
O
O
L
C
H
A
I
N
/
b
i
n
/
a
r
m
−
l
i
n
u
x
−
a
n
d
r
o
i
d
e
a
b
i
−
g
c
c
−
−
c
r
o
s
s
−
p
r
e
f
i
x
=
TOOLCHAIN/bin/arm-linux-androideabi-gcc \ --cross-prefix=
TOOLCHAIN/bin/arm−linux−androideabi−gcc −−cross−prefix=TOOLCHAIN/bin/arm-linux-androideabi-
–extra-cflags=“-I
I
S
Y
S
R
O
O
T
−
I
ISYSROOT -I
ISYSROOT−IASM -fPIC”
make clean all
这里是定义用几个CPU编译
make -j12
make
make install
}
build
可以看到,在基本上配置和使用 CLANG
进行编译差不多。
有以下不同:
- 多了
cc
配置。因为如果不配置cc
默认为clang
(参考前文的分析); - 多了
extra-cflags
的配置,因为SYSROOT
中只包含了库文件
,需要额外配置头文件
的搜索路径;汇编头文件
的路径也不在SYSROOT
中,也需要额外配置ASM
。
启动编译
打开 cmd 终端,cd 到 ffmpeg-4.2.2 目录
执行 ./build_android_gcc.sh
六、总结
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
学习福利
【Android 详细知识点思维脑图(技能树)】
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。
这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
由于篇幅有限,这里以图片的形式给大家展示一小部分。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。
这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
由于篇幅有限,这里以图片的形式给大家展示一小部分。
[外链图片转存中…(img-TOIE9idy-1711590859875)]
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。