Android-NDK-clang 编译 FFmpeg

news2025/1/16 13:41:31

Android-NDK-clang 编译 FFmpeg

Android-NDK-clang 编译 FFmpeg

Android-NDK-clang 编译 FFmpeg

Android-NDK-clang 编译 FFmpeg - 知乎 (zhihu.com)

前期准备

  1. 下载 Android-NDK
  2. 下载 FFmpeg 源码 注意:笔者用的是 NDK-21 和 ffmpeg-4.4 进行编译,如果版本不同可能会有所不同。
    测试:mac 与 ubuntu 下的NDK20 - NDK22 和 ffmpeg 4.0 - ffmpeg 4.4,均可使用。


本文你可以了解到

  • NDK20 - NDK22 提供的交叉编译工具链主要目录
  • 使用 clang 交叉编译出 Android 平台可以使用的 libffmpeg.so 库
  • 部分编译细节

一、NDK 提供的交叉编译工具链主要目录与文件

从 NDK20 - NDK22 编译工具链目录结构基本没变,这里以 NDK21 作为演示( NDK 在 windows、linux、mac 中的目录基本一样)

如上图,主要用的就是这几个目录,其中编译 FFmpeg 需要用到的 gcc 库就在 aarch64、arm、x86_64、x86 这几个文件夹中,这里先介绍一下这几个名字在 Android 中的不同平台库的联系。

aarch64:带这个前缀的目录都是与 arm64-v8a 库相关
arm:带这个前缀的目录都是与 armeabi-v7a 库相关
x86_64:带这个前缀的目录都是与 x86_64 库相关
x86:带这个前缀的目录都是与 x86 库相关

1.clang 编译工具

进入目录 llvm->prebuilt->darwin-x86_64->bin 里面都是与交叉编译相关的文件,我们以 clang 进行编译,所以主要关注的是以 clang、clang++ 结尾的文件,clang 用于编译 c 文件clang++ 用于编译 c++ 文件。
 

  • 这里需要注意的是:21 和 i686
  • 21:表示编译出的库支持的最低 Android 版本
  • i686:表示编译出 x86 库平台的编译工具

上面的 NDK 目录名在各系统上的对应形式:
mac:darwin-x86_64
linux:linux-x86_64
windows:windows-x86_64
注意:下面都以 mac 系统下的 NDK 目录进行介绍

2.编译环境,需要用到的库

库和头文件所在的目录在 darwin-x86_64 下的 sysroot 目录,其中头文件在 include 目录,库在 lib 目录,了解完这些,就可以开始编译了。

二、使用 clang 交叉编译出 Android 平台可以使用的 libffmpeg.so 库

进入 FFmpeg 源码根目录

1.创建编译脚本:build_ffmpeg_android.sh

脚本的主要内容如下:

#!/bin/sh
# NDK 所在的路径
NDK=/Users/mac/Library/Android/sdk/ndk/21.4.7075529
# 需要编译出的平台,这里是 arm64-v8a
ARCH=aarch64
# 支持的最低 Android API
API=21
# 编译后输出目录,在 ffmpeg 源码目录下的 /android/arm64-v8a
OUTPUT=$(pwd)/android/arm64-v8a
# NDK 交叉编译工具链所在路径
TOOLCHAIN=/Users/mac/Library/Android/sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/darwin-x86_64

build() {
  ./configure \
  --target-os=android \
  --prefix=$OUTPUT \
  --arch=$ARCH \
  --sysroot=$TOOLCHAIN/sysroot \
  --disable-static \
  --disable-ffmpeg \
  --disable-ffplay \
  --disable-ffprobe \
  --disable-debug \
  --disable-doc \
  --disable-avdevice \
  --enable-shared \
  --enable-cross-compile \
  --cross-prefix=$TOOLCHAIN/bin/aarch64-linux-android- \
  --cc=$TOOLCHAIN/bin/aarch64-linux-android$API-clang \
  --cxx=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++ \
  --extra-cflags="-fpic"

  make clean all
  make -j12
  make install
}

build

这个shell脚本,大体上其实还是很容易懂的,比如
--disabble-static 禁止输出静态库
--enable-shared 输出动态库
--arch 用于配置输出的so库是什么架构的
--prefix 用于配置输出的so库的存放路径
enable-cross-compile 开启多平台编译,也就是可以编译多个平台的库
更多的选项可以查看官网的介绍,这里不再多说。

接下来重点来讲一下几个选项:

  • target-os --target-os=android:在旧版本的 FFmpeg 中,对Android平台的支持并不是很完善,并没有 android 这个target,所以在一些比较老的文章中都会提到,编译Android平台的so库,需要对 configure 做修改,否则会按照 linux 标准的方式输出so库,其命名方式和Android的so不一样,Android是无法加载的,所以编译时,FFmpeg 源码版本最好选和笔者的一致。

问题一:Linux 下输出的 so 库,Android 下无法加载

  • sysroot --sysroot=$TOOLCHAIN/sysroot: 用于配置交叉编译环境的 根路径 ,编译的时候会默认从这个路径下去寻找 usr/includeusr/lib 这两个路径,进而找到相关的头文件和库文件。
    NDK20-NDK22 系统的头文件和库文件就是在 $SYSYROOT/usr/include 和 $SYSYROOT/usr/lib 中。

extra-cflags 给编译器指定一些编译标志,例如:
设置头文件路径:格式 -I头文件路径
设置编译出的二进制文件为位置无关码文件:格式 -fpic
至于为什么需要编译出位置无关码文件,就是因为 打包 出的 so 库就是由多个为位置无关码的二进制文件组成的。

extra-ldflags 给链接器指定一些链接标志,例如:
设置需要链接的库的路径:格式 -L库文件路径
输出库并设置名字:格式 -o 库名
设置需要链接的库:格式 -l库名
这里需要注意:
假设库名为:a
-o 库名 需要带 lib 前缀,与 .so/.a 后缀的部分,如 -o liba.so
-l库名 是不带 lib 前缀,与 .so/.a 后缀的部分,如 -la

关于编译与链接标志的问题,想了解详情可以查看这里。

  • cross-prefix 配置交叉编译的编译工具的前缀,就是上面介绍的交叉编译相关的文件所在的目录内的文件名的前缀,如:编译 arm64-v8a 平台的就是 aarch64-linux-android-,而编译 armeabi-v7a 平台的就是 arm-linux-androideabi-,具体是什么,到 交叉编译工具链目录下的 bin 目录查看即可。
  • cc
  • cxx 这两项就是配置上面说的使用 Android 自带的 clang 工具的具体路径

2.开始编译

通过终端进入到 FFmpeg 源码根目录,并运行刚刚写好的编译脚本,

sh build_ffmpeg_android.sh

运行结果如下

如上图,红框内的就是我们编译出的所以文件,但是这么多个 so 文件,用起来也麻烦,所以我们要把它们打包成一个 so 文件。

3.将多个库打包成一个库

  • 修改编译脚本,修改后如下
#!/bin/sh

# ...省略了不变的部分
SYSROOT_L=$TOOLCHAIN/sysroot/usr/lib/aarch64-linux-android
GCC_L=$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/lib/gcc/aarch64-linux-android/4.9.x

build() {
  # ...省略了不变的部分
  --disable-shared \
  --enable-static \
  --extra-cflags="-fpic -I$OUTPUT/include" \
  --extra-ldflags="-lc -ldl -lm -lz -llog -lgcc -L$OUTPUT/lib"
  
  # ...省略了不变的部分
}

package_library() {
  $TOOLCHAIN/bin/aarch64-linux-android-ld -L$OUTPUT/lib -L$GCC_L \
    -rpath-link=$SYSROOT_L/$API -L$SYSROOT_L/$API -soname libffmpeg.so \
    -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o $OUTPUT/libffmpeg.so \
    -lavcodec -lpostproc -lavfilter -lswresample -lavformat -lavutil -lswscale -lgcc \
    -lc -ldl -lm -lz -llog \
    --dynamic-linker=/system/bin/linker
    # 设置动态链接器,不同平台的不同,android 使用的是/system/bin/linker
}

build
package_library

上面列出的只是片段代码,只列出了做出了改变的部分,大致流程就是:
clang 编译出静态库 -> 使用 Android 自带的链接器将编译出的静态库打包成一个动态库

接下来重点来讲一下几个选项:

  • -rpath-link
When using ELF or SunOS, one shared library may require another. This happens when
an `"ld -shared"` link includes a shared library as one of the input files.

When the linker encounters such a dependency when doing a non-shared, non-relocatable 
link, it will automatically try to locate the required shared library and include it in 
the link, if it is not included explicitly. In such a case, the **-rpath-link** option 
specifies the first set of directories to search. The **-rpath-link** option may 
specify a sequence of directory names either by specifying a list of names separated by 
colons, or by appearing multiple times.

上面是官方给出的介绍,这里我斗胆用一句话概括一下,可能不太准确,但是能理解就行了:
这是传递给链接器的一个标志,当我们使用的库有依赖关系时,打包就需要按照依赖关系进行,否则会报错,用了这标志,我们只需要设置库的目录,不需要管依赖关系,链接时链接器帮我们处理。

  • -soname 这个选项的解释为:给库添加一个别名,也就是可以通过别名引用库,为什么要起别名?因为我们一开始是先打出了多个静态库,静态库已经有它自己的名字了,如果不对它们统一的做别名映射,你会发现你加载库的时候一直报错,说找不到你指定的库文件,不信你可以尝试一下哦^^。
  • --dynamic-linker 这个选项用于设置动态链接器,还记得上面提出的问题一吗,为了解决这个问题,这里设置成 Android 使用的链接器,就是/system/bin/linker,关于这点,可以看看这里

4.再次编译

运行结果如下

至此,我们就完成了 FFmpeg 的编译工作。

三、脚本使用介绍
笔者把编译脚本封装了一下,以适应方便的编译出 Android 各个平台的 so 库,脚本链接在文章开头,下面介绍使用步骤:

  1. 将脚本放在 FFmpeg 源码根目录
  2. 以文本方式打开脚本,简单的修改下面列出的几个参数
# 构建的最低支持 API 等级
API=21
# 在什么系统上构建,mac:darwin,linux:linux,windows:windows
OS_TYPE=darwin
# 自己本机 NDK 所在目录
NDK=/Users/mac/Library/Android/sdk/ndk/21.4.7075529
# 目标文件输出目录,默认是当前目录下的 android 目录
OUTPUT=$(pwd)/android/$ABI

  1. 打开终端,进入到 FFmpeg 源码目录,执行脚本:sh build_ffmpeg_android.sh 1

执行规则
sh build_ffmpeg_android.sh 后可以附带 1、2、3、4 这四项,下面说明这四项的意义
1:构建出 arm64-v8a 架构的库文件
2:构建出 armeabi-v7a 架构的库文件
3:构建出 x86_64 架构的库文件
4:构建出 x86 架构的库文件
如果想要构建多个平台的,可以附带多项,中间通过空格分隔开即可,如构建全平台:
sh build_ffmpeg_android.sh 1 2 3 4

四、遇到的问题

  1. 运行脚本,显示没权限
修改文件权限,再次运行即可:chmod 777 build_ffmpeg_android.sh

  1. 运行脚本,显示脚本中存在无法识别的字符不能运行

解决方法一:
Visual Studio Code 代替记事本,重新编辑
解决方法二:
安装 dos2unix 软件
mac 下:brew install dos2unix
ubuntu 下:sudo apt install dos2unix
使用:dos2unix build_ffmpeg_android.sh
然后再次运行即可

编译x86库的时候报错,错误如下

这个时候只需要在 ./configure 后加上:--disable-asm 即可,然后重新编译就没问题了,因为 x86 平台移除了寄存器,如果不禁用这一项就会报错,详细原因在这。

  1. 将多个库打包时用到的 gcc 的库在别的目录也有

这里容许我吐槽一下,我认为是一个巨坑... 因为我打包的时候一开始用的是别的目录的 gcc ,部分平台的打包 是正常的,但是 armeabi-v7a 平台的一直打包不成功,试了很久才发现现在用的目录也有,并且没问题。如果你 也遇到了同样的问题,那就换成我介绍的目录的 gcc 就没问题了。

五、总结

  1. 在 Android 端的编译问题很多时候是因为对编译工具链的目录不熟,找不到对应的库
  2. 编译时,如果遇到缺哪个库,去上文介绍的目录找到并且在编译时加进去就可以了
  3. 多动手实践,你会发现“书上得来终觉浅”这句话的真谛 最后,如果你觉得这篇文章对你有所帮助,那就点个赞呗

参考文章

FFmpeg so库编译
如何跨平台编译能执行在 Android 上的文件
FFmpeg x86 编译问题
链接器的-rpath介绍
将FFmpeg编译成一个libffmpeg.so库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1108010.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【网络爬虫】2 初探网络爬虫

爬虫练手 把豆瓣的书评list页爬取下来,并获取其书名,和detail的连接地址 豆瓣的书评list的url地址, start1,2,3,4…是其地址页 https://book.douban.com/top250?start1 f12 观察其html结构 思路 按照找到的list的页面地址: 1.获取list页…

训练ChatGPT提示词,实现Excel函数操作

Excel常用表格数据处理都会离不开函数的应用。 在数据处理,数据汇总,数据展示的过程中经常需要各类函数的使用如Vlookup,Sumifs,IF 等。 例如有一份数据我们想根据销售经理的名字,查找对应的销售额。 我们先简单描述一下我们的需求: 帮我写个Excel函数,要求查找出任意销…

Electron之集成vue+vite开发桌面程序

在electron中集成vue开发桌面程序 使用我们之前创建的electron项目 创建vue 项目 命令行进入electron根目录 执行下面命令 npm create vitelatest vue -- --template vue这样就创建了一个vue项目,文件名是vue,命令行进入vue下,执行下面命…

react-router-dom v6版本实现Tabs路由缓存切换

目录 文章目录 概要 效果 完整代码 概要 摆了半年摊,好久没写代码了,今天有人问我怎么实现React-Router-dom类似标签页缓存。后面看了一下router的官网。很久以前用的是react-router v5那个比较容易实现。v6变化挺大,但了解react的机制和rea…

工业交换机定制化解决方案:光路科技的工业PoE交换机

PoE交换机能代替普通工业交换机使用吗? Power over Ethernet (PoE)交换机和普通工业交换机在某些情况下可以互相替代,但有些情况下则不太适合。PoE交换机具有额外的供电能力,用于同时为网络设备提供数据和电力。如果您的应用中只需要传输数据…

python web开发(四): Bootstrap

1.初步了解 别人已经写好的CSS样式&#xff0c;我们可以直接引用 下载 Link-BootStrap 解压&#xff0c;并放入到当前项目中 引用 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</tit…

IT售前“楠“知识之这!就是售前-尚文网络xUP楠哥

进Q群11372462领取专属报名福利 &#xff01;&#xff01;&#xff01; # 何为售前工程师 售前工程师在一个IT信息化团队中起到了呈上启下的绝对重要作用&#xff01;站在销售团队的视角&#xff0c;需要售前工程师从技术维度支持销售业务的开展&#xff1b;站在对立面用户的…

一些bug总结

今天被几个小问题和bug折磨了一天&#xff0c;来总结一下… 权限问题 用vscode连接服务器&#xff0c;如果是在root用户连接的情况下新建的文件/文件夹&#xff0c;然后切换到别的用户的时候去写的代码 可能会遇到各种问题 解决方案是更改文件或文件夹的所有权。这可以通过使用…

pymysql连接Mariadb/Mysql出现错误(配置正确情况下)解决办法

场景&#xff1a;在kali中使用python中pymysql对Mariadb进行连接&#xff0c;在整个过程中配置全部正确&#xff0c;但是就是无法进行连接&#xff0c;提示结果如下&#xff1a; Access denied for user rootlocalhost解决办法&#xff1a;进入数据库中&#xff0c;将默认密码…

一步一步分析ChatGPT,1 粘性,2 传染性, 3 双边网络效应

请按照以下三个维度一步一步分析ChatGPT&#xff0c;1 粘性&#xff0c;2 传染性&#xff0c; 3 双边网络效应&#xff0c;比如亚马逊的买家和商家的关系 ChatGPT的分析 1.1. 粘性 (Stickiness) 定义&#xff1a; 粘性是指产品或服务对用户的吸引力&#xff0c;即用户在使用…

番外8.1 配置+管理文件系统

Task01: Linux 文件系统结构&#xff1b; 可以进行Linux操作系统的文件权限管理与方式切换&#xff0c;可以应用磁盘与文件权限管理工具&#xff1b; 01&#xff1a;常见文件系统类型&#xff08;Ext4[rhel6默认文件管理系统], 存储容量1 EB1073741824 GB; XFS[rhel 7/8默认的文…

HugeGraph 部署和Hubble1.0.0的数据导入Bug修复

背景 HugeGraph 安装部署了最新版本1.0.0&#xff0c;发现它的 Web 工具 Hubble 有一个大 Bug。数据导入的时候&#xff0c;配置节点属性映射这个选项时&#xff0c;下拉框只有一个选项&#xff0c;但实际上&#xff0c;元数据配置中的属性有3个&#xff0c;这个 Bug 是怎么产…

黑客(自学方法)技术——网络安全

如果你想自学网络安全&#xff0c;首先你必须了解什么是网络安全&#xff01;&#xff0c;什么是黑客&#xff01;&#xff01; 1.无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面性&#xff0c;例如 Web 安全技术&#xff0c;既有 Web 渗透2.也有 Web 防…

项目管理之5W2H项目定义法

在项目管理中&#xff0c;为了确保项目的成功实施&#xff0c;项目团队需要全面了解项目的各个方面&#xff0c;并制定相应的计划和措施。为此&#xff0c;可以采用一种被称为5W2H的项目定义方法。这种方法可以帮助项目团队更好地了解项目的需求、干系人、实施地点、交付物、时…

环境变量【使用命令行参数引出环境变量】

前提&#xff1a;命令行参数 大家在写C/C程序的时候肯定见过下面这种情况&#xff1a; main函数里面携带的参数&#xff0c;平常写代码过程中很少用到这两个参数&#xff0c;接下来我们就研究一下 我们也不知道 指针数组argv里面到底保存的是什么&#xff0c;也不知道这个a…

Java实现业务异步的几种方案

背景&#xff1a; 在java中异步线程很重要&#xff0c;比如在业务流处理时&#xff0c;需要通知硬件设备&#xff0c;发短信通知用户&#xff0c;或者需要上传一些图片资源到其他服务器这种耗时的操作&#xff0c;在主线程里处理会阻塞整理流程&#xff0c;而且我们也不需要等…

软考系列(系统架构师)- 2017年系统架构师软考案例分析考点

试题一 软件架构&#xff08;质量属性效用树、架构风险、敏感点、权衡点&#xff09; 系统架构风险、敏感点和权衡点的定义 【问题2】&#xff08;13分&#xff09; 在架构评估过程中&#xff0c;需要正确识别系统的架构风险、敏感点和权衡点&#xff0c;并进行合理的架构决策…

ubuntu20.04 nvidia显卡驱动掉了,变成开源驱动,在软件与更新里选择专有驱动,下载出错,调整ubuntu镜像源之后成功修复

驱动配置好&#xff0c;环境隔了一段时间&#xff0c;打开Ubuntu发现装好的驱动又掉了&#xff0c;软件与更新 那里&#xff0c;附加驱动&#xff0c;显示开源驱动&#xff0c;命令行输入 nvidia-smi 命令查找不到驱动。 点击上面的 nvidia-driver-470&#xff08;专有&#x…

Spring 国际化:i18n

文章目录 i18n概述Java国际化Spring6国际化MessageSource接口使用Spring6国际化 i18n概述 国际化也称作i18n&#xff0c;其来源是英文单词 internationalization的首末字符i和n&#xff0c;18为中间的字符数。由于软件发行可能面向多个国家&#xff0c;对于不同国家的用户&…

【AI视野·今日CV 计算机视觉论文速览 第268期】Mon, 16 Oct 2023

AI视野今日CS.CV 计算机视觉论文速览 Mon, 16 Oct 2023 Totally 61 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computer Vision Papers Vision-by-Language for Training-Free Compositional Image Retrieval Authors Shyamgopal Karthik, Karsten Roth, Massi…