乐趣区

关于android:音视频学习之NDK交叉编译基础

我收集了一些学习用的材料,其中蕴含了很多学习,面试,中高进阶 fluuter 材料,还有很多视频详解,如果有同学想进一步理解,详情请看文末。也欢送各路大神门前来装 X。

穿插编译

穿插编译就是程序的编译环境和理论运行环境不统一,即在一个平台上生成另一个平台上的可执行代码。

比方 NDK,你在 Mac、Win 或者 Linux 上生成的 C /C++ 的代码要在 Android 平台上运行,就须要应用到穿插编译了。

艰深点说就是你的电脑和手机应用的 CPU 不同,所以 CPU 的指令集就不同,比方 arm 的指令集在 X86 上就不能运行。

罕用的编译工具链

gcc

GNU C 编译器。本来只能解决 C 语言,很快扩大,变得可解决 C ++。(GNU 打算,又称革奴打算。指标是创立一套齐全自在的操作系统)

Android 在之后彻底移除了 gcc,默认应用 clang 编译,所以应用不同版本的 ndk 对 ffmpeg 进行穿插编译时会呈现同样的脚本在旧版的 ndk 能编译通过,然而旧版的就不编译不通过的问题。

笔者会在前面的学习过程中应用最新的 ndk 对最新版的 ffmpeg 进行穿插编译,并且会通过文章记录学习过程,感兴趣的同学能够继续关注。

g++

GNU c++ 编译器

gcc 和 g ++ 都可能编译 c /c++,然而编译时候行为不同。

对于 gcc 与 g ++ 会有以下区别:

后缀为.c 的源文件,gcc 把它当作是 C 程序,而 g ++ 当作是 C ++ 程序;后缀为.cpp 的,两者都会认为是 c ++ 程序

g++ 会主动链接 c ++ 规范库 stl,gcc 不会

gcc 不会定义__cplusplus 宏,而 g ++ 会

clang

clang 是一个 C、C++、Object- C 的轻量级编译器。基于 LLVM(LLVM 是以 C ++ 编写而成的构架编译器的框架零碎,能够说是一个用于开发编译器相干的库)

比照 gcc,clang 具备编译速度更快、编译产出更小等长处,然而某些软件在应用 clang 编译时候因为源码中内容的问题会呈现谬误。

另外 clang++ 也是一个编译器,clang++ 与 clang 就相当于 gcc 与 g ++ 的区别。

动态库和动静库

动态库是指编译链接时,把库文件的代码全副退出到可执行文件中,因而生成的文件比拟大,但在运行时也就不再须要库文件了。Linux 中后缀名为”.a”。

动静库则与动态库相同,在编译链接时并没有把库文件的代码退出到可执行文件中,而是在程序执行时由运行时链接文件加载库。Linux 中后缀名为”.so”。gcc 在编译时默认应用动静库。

总结起来就是动态库节俭运行工夫,动静库节俭运行空间,典型的工夫换空间,在开发过程中可依据状况自行抉择。

Java 中在不通过封装的状况下只能间接应用动静库。

编译器过程

一个 C /C++ 文件要通过预处理 (preprocessing)、编译(compilation)、汇编(assembly)、和连贯(linking) 能力变成可执行文件。

咱们以最简略的一个 c 语言程序来做一个例子:

预处理

gcc -E main.c -o main.i

- E 的作用是让 gcc 在预处理完结后进行编译。

预处理阶段次要解决 include 和 define 等。它把 #include 蕴含进来的.h 文件插入到 #include 所在的地位,把源程序中应用到的用#define 定义的宏用理论的字符串代替。

编译阶段

gcc -S main.i -o main.s

- S 的作用是编译后完结,编译生成了汇编文件。

在这个阶段中,gcc 首先要查看代码的规范性、是否有语法错误等,以确定代码的理论要做的工作,在查看无误后,gcc 把代码翻译成汇编语言。

汇编阶段

gcc -c main.s -o main.o

汇编阶段把 .s 文件翻译成二进制机器指令文件.o, 这个阶段接管.c, .i, .s 的文件都没有问题。

链接阶段

gcc -o main main.s

链接阶段,链接的是函数库。在 main.c 中并没有定义”printf”的函数实现,且在预编译中蕴含进的”stdio.h”中也只有该函数的申明。零碎把这些函数实现都被做到名为 libc.so 的动静库。

一步到位:

gcc main.c -o main

到这里咱们胜利的在 mac 平台生成了可执行文件,运行即可看到输入。试想一下咱们能够将这个可执行文件拷贝到安卓手机上执行吗?

必定是不行的,次要起因是两个平台的 CPU 指令集不一样,基本就无奈辨认指令,这时候穿插编译就排上用场了。

如果你不信能够把 main 可执行文件 push 到手机 /data/local/tmp 外面执行验证一下是否正确输入。

也不肯定必须要是 /data/local/tmp 这个门路,push 到任意一个有可读可写可执行的权限的目录下测试均可。

穿插编译试验

上面咱们应用 ndk 来对 main.c 进行穿插编译,看看编译后的可执行文件是不是真的能在 Android 上运行。

笔者这里以 armeabi 为例,在 mac 平台上进行穿插编译。

既然是 gcc 被移除了,那咱们应用 clang 来进行穿插编译。

首先找到 clang 工具链

执行命令

在 mac 平台能能失常生成可执行文件, 咱们将可执行文件用 push 到这个目录下,而后应用 adb 执行即可看到输入。阐明咱们的穿插编译胜利了。

如果不应用 clang,如何 gcc 进行穿插编译呢?

首先也是先找到 gcc 的工具链

而后执行 gcc 编译命令

咱们发现报错了

找不到 stdio.h 头文件

这种谬误是说在咱们编的时候编译器找不到咱们引入的 stdio.h 头文件,那怎么通知编译器 stdio.h 头文件在哪里呢? 上面知识点阐明怎么指定这些报错的头文件

咱们通过参数通知 gcc 工具链到那个目录上来寻找头文件,传递参数进去再次试一下

还是报错

找不到 types.h 头文件

因为找不到头文件,咱们进去 -isystem 配置的头文件查找目录中发现 aarch64-linux-android 和 arm-linux-androideabi 都存在 asm 的子目录,所以编译器就不晓得用那个了,咱们再指定一下即可。

终于胜利了,咱们将可执行文件 push 到手机的这个目录下,而后应用 adb 执行即可看到输入。

在这里咱们应用了 clang 和 gcc 进行了穿插编译发现 clang 更加的简略,间接找到工具链的门路即可进行编译了,然而 gcc 就比较复杂了,须要指定多个参数。

这里须要须要咱们明确每个参数的意思是什么:

例如 gcc –sysroot= 目录 1 -isysroot 目录 2 -isystem 目录 3 - I 目录 4 main.c

的意思就是 查找 目录 1 /usr/lib 的库文件、

查找目录 2 /usr/include 的头文件、

查找 目录 3 下的头文件、

查找 目录 4 下的头文件。

例如:

gcc - L 目录 1 - l 库名

链接 ndk 的日志库:

gcc -LC:NDK 门路 \platforms\android-21\arch-arm\usr\lib

-llog -lGLESv2

或者是

gcc –sysroot=NDK 门路 \platforms\android-21\arch-arm

-llog -lGLESv2

生成动静库

gcc -fPIC -shared main.c -o libTest.so

或者

clang -fPIC -shared main.c -o libTest.so

即便不加 -fPIC 也能够生成.so 文件,然而对于源文件有要求,

因为不加 fPIC 编译的 so 必须要在加载到用户程序的地址空间时重定向所有指标地址,所以在它外面不能引用其它中央的代码。

要想验证编译进去的 so 动静库能不能失常应用,通过 JNI 调用测试即可。

须要学习更多 Android 进阶,高阶,flutter,面试材料的请戳上面链接获取

https://shimo.im/docs/dYkqrQcyr98jPKYX/《android 学习面试 fulutter 进阶材料收费获取》,可复制链接后用石墨文档 App 或小程序关上。

退出移动版