FFmpeg编译so库文件
FFmpeg是一个音视频库,是使用C语言开发的,目前在各个行业都应用很广。在Android中,也通常都是使用其作为音视频播放和处理方案,当然为了更适配对应的平台,我们可能很少直接使用FFmpeg,而是使用其他的对FFmpeg进行二次封装的库,如ijkPlayer等,但了解FFmpeg会让我们在遇到难题时更易找到解决方案。
FFmpeg的编译
如果要在Android中使用,我们通常会使用交叉编译的方式编出对应的so文件,然后在引入到Android中进行使用,这里最好使用在Linux环境中进行编译。
- 创建一个编译目录:
1 | |
- 下载
ndk:
1 | |
此时在目录中会有一个android-ndk-r27的目录,这个目录就是ndk的目录。如果不想使用命令的方式,直接在浏览器中访问官网更好,因为可能下载到最新的ndk版本,地址是https://developer.android.google.cn/ndk/downloads/index.html?hl=uk
- 下载
ffmpeg源码:
1 | |
此时目录中会有一个ffmpeg-7.1.1的目录,这个目录中存储的就是ffmpeg的源码。如果不想使用命令的方式,直接在浏览器中访问官网更好,因为可以下载到最新的版本或者你想要的版本。地址是:https://ffmpeg.org/download.html#releases
- 编译链接库
在FFmpeg中有一个configure文件,该文件是一个脚本文件,用来控制如何进行编译的,如果是直接编译的话,直接执行configure文件即可,但我们需要在Android中使用,因此需要将ndk配置进去。
1 | |
我们进入到了ffmpeg目录,然后创建了一个build.sh文件,这个文件就是最终的编译脚本,我们会在其中调用configure文件从而达成编译的目的,文件内容如下:
1 | |
在然后就是进行构建了,这里编了三个不同架构的so库,一个是32位的armv7a,一个是64位的arm64的,还有一个模拟器的x86_64的,根据自己实际的项目进行选择,想要编译直接调用该脚本即可。
1 | |
在ffmpeg-7.1.1/output目录下,是我们两次的编译结果,我们需要的就是对应的include中的头文件,以及lib中的so库文件。
参数说明
前面的编译脚本build.sh,实际看下就知道它最终还是通过configure脚本来实现的,只是通过不同的参数进行控制的。接下来先看下前面的参数:
1 | |
--prefix,它代表着输出产物的前缀,该目录可以随意设置,这里因为我们编译了两份产物,因此在前缀并不是相同的,避免文件重复。--cross-prefix,它代表着交叉编译的前缀。编译的过程会涉及到很多的命令,如nm、ar等,正常编译会直接找到系统命令去进行编译,但我们编的不是当前平台,而是android平台,因此不能直接找系统中的这些命令,而是从ndk中查找。android-ndk-r27c/toolchains/llvm/prebuilt/linux-x86_64/bin这个目录中就是我们需要的命令,可以看到除了clang和clang++与平台版本有差异外,其他的都没有差异,而是都加了llvm-的前缀,因此这里的参数我们也需要设置成llvm-。--target-os,目标平台直接填android就行。--arch,目标平台的架构,常见的arm是32位的架构,最通用的架构,老机型用的多;aarch64是64位的架构,目前的主流架构;x86_64模拟器上使用。sysroot系统查找目录,包含了头文件以及对应的链接库文件,对应的是android-ndk-r27c/toolchains/llvm/prebuilt/linux-x86_64/sysroot目录--cc和--cxx,编译c和c++的命令,对应着llvm中的clang和clang++。注意这里是有区分的,需要区分不同的架构和安卓版本号,如果我们不指定这两个参数的话,它会使用前面设置的prefix来查找命令,如它会去找llvm-clang命令,结果当然是找不到的。因此这里我们给他手动指定前缀,具体前缀可以在android-ndk-r27c/toolchains/llvm/prebuilt/linux-x86_64/bin目录中找到clang命令,然后查看规律在设置就行了,如armv7a,安卓30对应的是armv7a-linux-androideabi30-clang命令,armv8a安卓30对应的是aarch64-linux-android30-clang命令。--pkg-config,这个是属于系统命令,需要确保系统中有安装pkg-config,可以通过sudo apt install pkg-config进行安装,为什么要明确指定呢,因为前面我们有设置--cross-prefix,该属性应用后会导致命令变成llvm-pkg-config,导致找不到对应的命令,因此我们才需要重新指定下。
前面的参数都是交叉编译所涉及的一些参数,不涉及到具体的功能相关,而后续的参数则是与FFmpeg相关的:
1 | |
--disable-static:关闭静态链接库,通常生成的是.a文件,在编译时会直接编在文件中--enable-shared:打开共享库,通常生成的是.so文件,也是我们的目标文件ffmpeg、ffplay、ffrpobe是FFmpeg的命令行工具,因为我们不需要它们,因此直接disable即可
更多的参数涉及到了封装、编码等相关功能,实际上基本所有的功能都是通过disable和enable命令来打开和关闭的,因此需要我们自己衡量并进行裁剪。因为FFmpeg的功能是非常多的,如果我们不做任何裁剪的话,生成的so也会很大,如果只是作为学习的话,就不用去裁剪了,完整版的才是最好的。具体的功能配置在ffmpeg-7.1.1/configure文件中都有说明。
引入到安卓中
如果先麻烦可以直接新建一个native C++项目,如果是已有项目则可以新建一个Module,然后将其配置成支持C++,如果不会配置的话,直接创建一个新的native C++项目,然后抄一下gradle的写法即可。
然后就是引入我们编译好的so文件,首先修改gradle:
1 | |
主要就是修改abiFilters,因为我们只编译了这两种so。接着就是将so放在项目中,在该module的src/main目录下新建一个目录,注意如果AGP版本低于4.0的话,新建的目录名称需要是jniLibs,高于4.0就随意命名了,这里我命名的是libs目录。然后在该目录下新建两个目录分别为arm64-v8a和armeabi-v7a,然后将前面生成的so分别放在对应的目录中,大概的层级如下:
1 | |
接下来修改项目中的CMakeLists.txt,将我们的so和头文件包含进去。首先是将FFmpeg的头文件放在cpp的目录中,这里为了区分新建了一个文件夹来放头文件,目录如下:
1 | |
然后修改CMakeLists.txt,将头文件包含进来,然后在引入so文件:
1 | |
到这里就已经引入完成了,我们直接编译一个apk,然后将其拖入到as中,就能看到apk中已经包含了我们引入的FFmpeg的so。
我们可以直接修改native-lib.cpp中返回字串的函数,然后调用FFmpeg的方法查看我们编译FFmpeg时的配置信息:
1 | |
直接运行就能看到输出的信息确实是我们编译时的configure信息。
