前言
在iOS设备上进行音视频的处理,往往要使用市面上比较流行的音视频相关库,比如音频编码的Lame,处理视频的FFmpeg,处理图片的OpenCv等.
而要能够运行这些库,我们必须将它们编译为能够在iOS设备上运行的文件.这就需要我们进行交叉编译.交叉编译是iOS通向音视频处理的第一道拦路虎,非常容易出错.本文最后以编译Lame库为例,介绍交叉编译的完整流程.
1 交叉编译
1.1 CPU的类型和指令集
对于一台电脑或者手机来说,核心部件是中央处理器CPU,所有指令的执行,最终都是依靠CPU来完成的.
指令集,是CPU能够执行的指令的集合,每种类型CPU都有自己的指令集,或者架构.
如果是不同系列CPU,指令集是互不兼容的,在指令集为A的CpuA上运行的代码,不能在指令集为B的CpuB上执行.
如果是相同系列的CPU,指令集向下兼容,低版本指令集A1的CpuA1上运行的代码,也可以在高版本指令集为A2的CpuA2上运行,但是不能够充分发挥CpuA2的性能.
1.2 iOS设备架构列表
iOS设备都虽然都是ARM系列的CPU,但是有不同的指令集:
armv6:iPhone,iPhone2,iPhone 3G
armv7:iPhone 4,iPhone 4S
armv7s:iPhone 5,iPhone 5S
arm64:iPhone6(P),iPhone7(P)
arm64e:iPhone XS
i386:32位模拟器
x64:64位模拟器
1.3 什么是交叉编译
编译型语言的源代码,比如C源代码,要能在PC上运行,需要经过编译,链接,成为PC可执行的二进制文件,然后才能在PC上运行.
同理,如果要在其他机器上运行,就必须编译,链接成为可以在其他机器上运行的代码.
源代码在机器A上的编译链接得到机器B上运行的代码.
如果机器B==机器A,那么就是本机编译.
如果机器B!=机器A,就是交叉编译.
比如,机器A是Mac,机器B是iPhone,那么就是在Mac上交叉编译生成iPhone的代码.
那么为什么不直接在iPhone上本机编译呢,因为iPhone上没有成熟的编译工具,以及足够的硬件条件去编译源代码.
1.4 iOS关于架构的设置
iOS中,有3个关于架构的设置:
BuildSettings|Architectures|Architectures
指定工程被编译成可支持哪些指令集类型,而支持的指令集越多,就会编译出包含多个指令集代码的数据包,对应生成二进制包就越大,也就是ipa包会变大。
BuildSettings|Architectures|Valid Architectures
限制可能被支持的指令集的范围,也就是Xcode编译出来的二进制包类型最终从这些类型产生,而编译出哪种指令集的包,将由Architectures与Valid Architectures(因此这个不能为空)的交集来确定.
BuildSettings|Architectures|Build Active Architecture Only
是否只生成支持当前连接设备的指令集的包.
一般Debug模式下设置为YES,加快编译速度;而Release模式下设置为NO,更好地适配各种机型.
一般情况下,我们编译armv7s和arm64的包就可以适配大部分机型了.
2 交叉编译Lame库
2.1 Lame库
Lame是一款效果很好的开源Mp3编码器,是在iOS设备上将音频编码为Mp3的最佳选择.
本章用最新版本3.100进行编译.
2.2 configure命令
这里先提一个文件,configure文件:
每一个符合GNU标准的软件包都会包含该命令.
交叉编译的整个流程,做了很多事情,我也不是很清楚具体都做了什么.我们需要关心的是,我们需要做什么:
我们需要通过以合适的方式运行configure命令,改名令执行的结果会生成合适的Makefile文件.
然后利用make和make install命令编译和安装整个库.
所以,我们在交叉编译中需要做的重点,就是用合适的参数去运行configure命令.
2.2 configure命令常用参数
我们输入命令./configure -h
可以查看configure的帮助文档,会显示当前软件包的全部可选项配置.
下面介绍几个主要的配置:
-
指定执行编译任务的主机的架构
--build={Build}
build: 执行代码编译的主机的架构,正常的话就是你的主机系统。
这个参数一般由config.guess来猜就可以。当然自己指定也可以。 -
指定输出位置
--prefix={PREFIX}, 指定编译好的库放在哪个目录下,这是GNU大部分库的标准配置. -
指定要运行库的架构
--host={HOST},指定要交叉编译出来的库,最终运行的平台,不同的架构CPU有不同的值:
arm64:
arm-apple-darwin
armv7:
arm-apple-darwin
i386:
i386-apple-darwin
x86_64:
x86_64-apple-darwin -
工具链的路径
CC,交叉编译工具链的路径,其实就是gcc编译器的路径.不同架构的CPU有不同的值:
x86_64:
xcrun -sdk iphonesimulator clang -arch x86_64
i386:
xcrun -sdk iphonesimulator clang -arch i386
arm64:
xcrun -sdk iphoneos clang -arch arm64
armv7s:
xcrun -sdk iphoneos clang -arch armv7s -
编译参数
CFLAGS,编译时需要带的参数,不同架构的CPU有不同的值:
x86_64:
-arch x86_64 -fembed-bitcode -miphoneos-version-min=7.0
i386:
-arch i386 -fembed-bitcode -miphoneos-version-min=7.0
arm64:
-arch arm64 -fembed-bitcode -miphoneos-version-min=7.0
armv7s:
-arch armv7s -fembed-bitcode -miphoneos-version-min=7.0
其中,-fembed-bitcode表示该静态库使用了bitcode,关于bitcode,请参考后面的bitcode一节. -
链接参数
LDFLAGS,链接时的参数,和CFLAGS保持一致. -
指定关闭动态链接
--disable-shared:关闭动态链接,以便工具可以单独使用. -
指定不编译可执行文件.
--disable-frontend,指定不编译LAME的可执行文件. -
指定创建静态库
--enable-static,创建静态库,默认为YES.
软件工程师的职责之一,就是要把重复性的东西做成工具,让工作变得简单,从而创造快乐和价值.在本文末尾,会给出一个编译脚本.利用该脚本,可以方便地编译出i386,x86_64,armv7s,arm64架构的库.
2.3 查看编译成果物
每一个指令集的包中,有lib,include,share3个目录:
lib目录,存放的是我们的静态库文件,libmp3lame.a
include目录,是工程中需要使用的头文件,每个指令集对应的目录下的头文件是一样的.
share目录,包含了使用手册.
2.4 合并静态库
如果我们多个指令集的静态库,需要把他们合并才能使用.
如果这几个静态库的文件路径是libPath1,libPath2
使用以下命令合并:
$ lipo -create libPath1 libPath2 -output outputFilePath
合并后的库文件的目录为outputFilePath
验证是否合并成功,可使用file命令或者lipo -info命令:
$ file libmp3lame.a
libmp3lame.a: Mach-O universal binary with 4 architectures: [i386:current ar archive random library] [arm64]
libmp3lame.a (for architecture i386): current ar archive random library
libmp3lame.a (for architecture armv7s): current ar archive random library
libmp3lame.a (for architecture x86_64): current ar archive random library
libmp3lame.a (for architecture arm64): current ar archive random library
或者:
$ lipo -info libmp3lame.a
Architectures in the fat file: libmp3lame.a are: i386 armv7s x86_64 arm64
2.5 bitcode
如果开启了bitcode
1,Xcode会将程序编译为一个中间表现形式(bitcode)
2,AppStore会将该中间形式的代码进行优化
总体来说,开启bitcode可以优化app的性能;
一个项目要使用bitcode,必须满足该项目中所有使用的第三方静态库都在编译时打开了bitcode.否则,只能关闭bitcode,从而得不到性能上的优化.
3 编译脚本的使用
编译脚本是一个sh文件,使用时先将脚本放到LAME库文件的根目录下.
然后在命令行中,切换到LAME库文件的目录下,然后执行:
sh ./LameBuilderForiOS.sh
该脚本会编译i386 x86_64 arm64 armv7s四个平台的库文件,输出在{LAME库目录}/thin文件夹中,然后将这四个库文件合并起来,输出在{LAME库目录}/fat文件夹中.
编译脚本地址和编译好的Lame库地址:
https://github.com/GikkiAres/LameBuilder
网友评论