Android 端IjkPlayer交叉编译的逻辑分析
- 交叉编译的原理
- 为什么要交叉编译
- 交叉编译的工具
- Android NDK 支持的编译器
- Android NDK 提供了两种编译器: GCC、Clang
- Android平台的交叉编译
- Android NDK 的目录结构
- 高版本NDK toolchiains目录:
- GCC 编译 FFmpeg
- Clang 编译 FFmpeg
- IjkPlayer的交叉编译
- ijkplayer 交叉编译分析
交叉编译的原理
所谓交叉编译,就是一个平台(如PC)上生成另外一个平台(Android,iOS或者其他嵌入式设备)的可执行代码。
为什么要交叉编译
在一般的嵌入式设备开发系统中(Android,ios),运行程序的目标平台其存储空间和运算能力都是有限的。
交叉编译的工具
无论是PC机上的编译还是嵌入式设备中的编译,它们都会提供以下几个工具:CC、AS、AR、LD、NM、GDB。那么,这几个工具到底是做什么用的呢?下面就逐一解释下。
CC:编译器,对C源文件进行编译处理,生成汇编语言。
AS:将汇编语言生成目标文件.
AR:打包器,用于库操作,通过该工具从一个库中删除或者增加代码模块.
LD:连接器,将多个目标文件链接成一个库或者可执行文件.
GDB:调试工具.
STRIP:以最终可执行的文件或者库文件作为输入,然后消除掉其中的源码.
NM:查看静态库中的符号表.
Objdump:查看静态库或者动态库的方法签名.
Android NDK 支持的编译器
Android NDK 提供了两种编译器: GCC、Clang
Android NDK从r11开始建议大家切换到clang。并且把GCC标记为deprecated,将GCC版本锁定在GCC 4.9不再更新。
- PSA: Everyone should be switching to Clang.
- Clang has been updated to 3.8svn (r243773, build 2481030).
-
Time to start using Clang if you haven’t already. If you have
problems with Clang, please file bugs! -
The NDK will not be upgrading to , nor will we be accepting non-critical backports.
Maintenance for miscompiles and internal compiler errors in 4.9 will be handled on a case by case basis.
Note that this is now a nearly pure upstream clang.
Also note that Clang packaged in the Windows 64 NDK is actually 32-bit.
GCC in the NDK is now deprecated. -
GCC 4.8 has been removed. All targets now use GCC 4.9.
-
Synchronized with google/gcc-4_9 to r224707 (from r214835).
-
Android平台的交叉编译
Android NDK 的目录结构
ndk-build: 该shell脚本用来构建项目动态库
ndk-gdb: 该shell脚本用GUN调试器调试Native代码
ndk-stack: 该shell脚本分析Native代码崩溃是的堆栈信息
build: 该目录包含NDK构建系统的所有模块
platform: 该目录包含支持不同Android目标版本的头文件和库文件,NDK构建系统会根据具体的配置来引用指定平台下的头文件和库文件
toolchiains:该目录包含目前NDK所支持的不同平台下的交叉编译器------ARM、x86、MIPS
高版本NDK toolchiains目录:
Clang 主要使用llvm 实现交叉编译
GCC 编译 FFmpeg
Android交叉编译主要使用android NDK目录结构中platform、toolchiains;
现在我主要讲解利用GCC编译器实现交叉编译,Clang编译器目前主要是在高版本的NDK中才支持,后面讲解如何利用Clang编译器交叉编译FFmpeg 4.0+;
建议GCC编译FFmpeg 4.0以下的版本,用Clang编译FFmpeg4.0以上的版本,否则你会发现用GCC编译FFmpeg4.0以上的版本会出现各种错误;
-
FFmpeg 下载 : / (下载3.2.14)
修改FFmpeg的configure,下载FFmpeg源代码之后,首先需要对源代码中的 - configure文件进行修改。由于编译出来的动态库文件名的版本号在.so之后(例如“.5.100.1”),而android平台不能识别这样文件名,所以需要修改这种文件名。在configure文件中找到下面几行代码:
SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR)$(SLIBNAME)'
- 1
- 2
- 3
- 4
替换为下面内容:
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'
- 1
- 2
- 3
- 4
- 生成FFmpeg的头文件和类库文件
cd ffmpeg-3.2.14
export NDK=/Users/videopls/android/android-ndk-r14b
export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt
export PLATFORM=$NDK/platforms/android-9/arch-arm
export PREFIX=../ffmpeg_3.2.14_lib
build_one(){
./configure --target-os=linux --prefix=$PREFIX \
--enable-cross-compile \
--enable-runtime-cpudetect \
--disable-asm \
--arch=arm \
--cc=$PREBUILT/darwin-x86_64/bin/arm-linux-androideabi-gcc \
--cross-prefix=$PREBUILT/darwin-x86_64/bin/arm-linux-androideabi- \
--disable-stripping \
--nm=$PREBUILT/darwin-x86_64/bin/arm-linux-androideabi-nm \
--sysroot=$PLATFORM \
--enable-gpl --enable-shared --disable-static --enable-small \
--disable-ffprobe --disable-ffplay --disable-ffmpeg --disable-debug --disable-ffserver \
--extra-cflags="-fPIC -DANDROID -D__thumb__ -mthumb -Wfatal-errors -Wno-deprecated -mfloat-abi=softfp -marm -march=armv7-a"
}
build_one
make clean
make -j8
make install
cd ..
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- FFmpeg 的裁剪及链接x264、fdk_aac等第三方库这里就不讲解了,如果想了解的可以通过 ./configure -h
查看每个参数的具体含义及网上搜索资料;
Clang 编译 FFmpeg
Clang 主要使用NDK → toolchiains → llvm 中的编译工具实现交叉编译 ;
- clang 编译环境的位置: toolchains/llvm/prebuilt/darwin-x86_64
- 编译工具的内容:toolchains/llvm/prebuilt/darwin-x86_64/bin
aarch64-linux-android-addr2line
aarch64-linux-android-ar
aarch64-linux-android-as
aarch64-linux-android-c++filt
aarch64-linux-android-dwp
aarch64-linux-android-elfedit
aarch64-linux-android-gprof
aarch64-linux-android-ld
aarch64-linux-android-ld.bfd
aarch64-linux-android-ld.gold
aarch64-linux-android-nm
aarch64-linux-android-objcopy
aarch64-linux-android-objdump
aarch64-linux-android-ranlib
aarch64-linux-android-readelf
aarch64-linux-android-size
aarch64-linux-android-strings
aarch64-linux-android-strip
aarch64-linux-android21-clang
aarch64-linux-android21-clang++
aarch64-linux-android22-clang
aarch64-linux-android22-clang++
aarch64-linux-android23-clang
aarch64-linux-android23-clang++
aarch64-linux-android24-clang
aarch64-linux-android24-clang++
aarch64-linux-android26-clang
aarch64-linux-android26-clang++
aarch64-linux-android27-clang
aarch64-linux-android27-clang++
aarch64-linux-android28-clang
aarch64-linux-android28-clang++
arm-linux-androideabi-addr2line
arm-linux-androideabi-ar
arm-linux-androideabi-as
arm-linux-androideabi-c++filt
arm-linux-androideabi-dwp
arm-linux-androideabi-elfedit
arm-linux-androideabi-gprof
arm-linux-androideabi-ld
arm-linux-androideabi-ld.bfd
arm-linux-androideabi-ld.gold
arm-linux-androideabi-nm
arm-linux-androideabi-objcopy
arm-linux-androideabi-objdump
arm-linux-androideabi-ranlib
arm-linux-androideabi-readelf
arm-linux-androideabi-size
arm-linux-androideabi-strings
arm-linux-androideabi-strip
armv7a-linux-androideabi16-clang
armv7a-linux-androideabi16-clang++
armv7a-linux-androideabi17-clang
armv7a-linux-androideabi17-clang++
armv7a-linux-androideabi18-clang
armv7a-linux-androideabi18-clang++
armv7a-linux-androideabi19-clang
armv7a-linux-androideabi19-clang++
armv7a-linux-androideabi21-clang
armv7a-linux-androideabi21-clang++
armv7a-linux-androideabi22-clang
armv7a-linux-androideabi22-clang++
armv7a-linux-androideabi23-clang
armv7a-linux-androideabi23-clang++
armv7a-linux-androideabi24-clang
armv7a-linux-androideabi24-clang++
armv7a-linux-androideabi26-clang
armv7a-linux-androideabi26-clang++
armv7a-linux-androideabi27-clang
armv7a-linux-androideabi27-clang++
armv7a-linux-androideabi28-clang
armv7a-linux-androideabi28-clang++
i686-linux-android-addr2line
i686-linux-android-ar
i686-linux-android-as
i686-linux-android-c++filt
i686-linux-android-dwp
i686-linux-android-elfedit
i686-linux-android-gprof
i686-linux-android-ld
i686-linux-android-ld.bfd
i686-linux-android-ld.gold
i686-linux-android-nm
i686-linux-android-objcopy
i686-linux-android-objdump
i686-linux-android-ranlib
i686-linux-android-readelf
i686-linux-android-size
i686-linux-android-strings
i686-linux-android-strip
i686-linux-android16-clang
i686-linux-android16-clang++
i686-linux-android17-clang
i686-linux-android17-clang++
i686-linux-android18-clang
i686-linux-android18-clang++
i686-linux-android19-clang
i686-linux-android19-clang++
i686-linux-android21-clang
i686-linux-android21-clang++
i686-linux-android22-clang
i686-linux-android22-clang++
i686-linux-android23-clang
i686-linux-android23-clang++
i686-linux-android24-clang
i686-linux-android24-clang++
i686-linux-android26-clang
i686-linux-android26-clang++
i686-linux-android27-clang
i686-linux-android27-clang++
i686-linux-android28-clang
i686-linux-android28-clang++
x86_64-linux-android-addr2line
x86_64-linux-android-ar
x86_64-linux-android-as
x86_64-linux-android-c++filt
x86_64-linux-android-dwp
x86_64-linux-android-elfedit
x86_64-linux-android-gprof
x86_64-linux-android-ld
x86_64-linux-android-ld.bfd
x86_64-linux-android-ld.gold
x86_64-linux-android-nm
x86_64-linux-android-objcopy
x86_64-linux-android-objdump
x86_64-linux-android-ranlib
x86_64-linux-android-readelf
x86_64-linux-android-size
x86_64-linux-android-strings
x86_64-linux-android-strip
x86_64-linux-android21-clang
x86_64-linux-android21-clang++
x86_64-linux-android22-clang
x86_64-linux-android22-clang++
x86_64-linux-android23-clang
x86_64-linux-android23-clang++
x86_64-linux-android24-clang
x86_64-linux-android24-clang++
x86_64-linux-android26-clang
x86_64-linux-android26-clang++
x86_64-linux-android27-clang
x86_64-linux-android27-clang++
x86_64-linux-android28-clang
x86_64-linux-android28-clang++
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 通过观察目录可以发现, toolchains/llvm/prebuilt/darwin-x86_64/bin中包含了aarch64、armv7a 、armv7a 、x86_64等CPU架构的编译工具;通过该工具可以编译出我们期望的库文件;
- clang、 clang++ 是不仅分架构还分 Android 版本的,而其他工具只分架构
armv7a-linux-androideabi16-clang
armv7a-linux-androideabi16-clang++
armv7a-linux-androideabi17-clang
armv7a-linux-androideabi17-clang++
armv7a-linux-androideabi18-clang
armv7a-linux-androideabi18-clang++
armv7a-linux-androideabi19-clang
armv7a-linux-androideabi19-clang++
armv7a-linux-androideabi21-clang
armv7a-linux-androideabi21-clang++
armv7a-linux-androideabi22-clang
armv7a-linux-androideabi22-clang++
armv7a-linux-androideabi23-clang
armv7a-linux-androideabi23-clang++
armv7a-linux-androideabi24-clang
armv7a-linux-androideabi24-clang++
armv7a-linux-androideabi26-clang
armv7a-linux-androideabi26-clang++
armv7a-linux-androideabi27-clang
armv7a-linux-androideabi27-clang++
armv7a-linux-androideabi28-clang
armv7a-linux-androideabi28-clang++
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- clang 编译FFmpeg 4.2.2(最新版)
cd ffmpeg-4.2.2
export NDK=/Users/videopls/Library/Android/sdk/ndk-bundle
export API=22
export ARCH=aarch64
export PLATFORM=aarch64
export TARGET=$PLATFORM-linux-android
export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/darwin-x86_64/bin
#正确的sysroot
export SYSROOT=$NDK/toolchains/llvm/prebuilt/darwin-x86_64/sysroot
export CPU=aarch64
export PREFIX=../ffmpeg-4.2.2-lib
export CFLAG="-D__ANDROID_API__=$API -Os -fPIC -DANDROID "
./configure \
--prefix=$PREFIX \
--cc=$TOOLCHAIN/$TARGET$API-clang \
--cxx=$TOOLCHAIN/$TARGET$API-clang++ \
--target-os=android \
--arch=$ARCH \
--cross-prefix=$TOOLCHAIN/$ARCH-linux-android- \
--disable-asm \
--enable-cross-compile \
--disable-shared \
--disable-doc \
--enable-runtime-cpudetect \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-decoders \
--disable-encoders \
--disable-devices \
--sysroot=$SYSROOT \
--extra-cflags="$CFLAG" \
--extra-ldflags=""
make clean
make -j8
make install
cd ..
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
IjkPlayer的交叉编译
- ijkplayer github 地址: /bilibili/ijkplayer
- Before Build
# install homebrew, git, yasm
ruby -e "$(curl -fsSL /Homebrew/install/master/install)"
brew install git
brew install yasm
# add these lines to your ~/.bash_profile or ~/.profile
# export ANDROID_SDK=<your sdk path>
# export ANDROID_NDK=<your ndk path>
# on Cygwin (unmaintained)
# install git, make, yasm
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- Build Android
git clone https://github.com/Bilibili/ijkplayer.git ijkplayer-android
cd ijkplayer-android
git checkout -B latest k0.8.8
./init-android.sh
cd android/contrib
./compile-ffmpeg.sh clean
./compile-ffmpeg.sh all
cd ..
./compile-ijk.sh all
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
ijkplayer 交叉编译分析
通过上面简简单单的几行代码,你会得到你想要的ijkplayer的库文件.但是如果我们对ijkplayer 有下面几个需求怎么办?
- 修改FFmpeg 的版本
- 对FFmpeg 进行裁剪
- FFmpeg 引入 libx264 、fdk_aac
这个时候你就会感到迷茫,所以我们必须了解ijkplayer的编译逻辑,等了解清楚了我们就可以对它进行定制的编译符合自己的库文件.
ijkplayer编译脚本:
./init-android.sh
cd android/contrib
./compile-ffmpeg.sh clean
./compile-ffmpeg.sh all
cd ..
./compile-ijk.sh all
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
1. 把https://github.com/Bilibili/FFmpeg.git clone 到本地extra/ffmpeg;(所以如果修改FFmpeg源码,需要需要修改extra/ffmpeg里面的)
2. 把本地extra/ffmpeg更新到android/contrib/ffmpeg-armv5/armv7a/arm64/x86/x86_64中
3.
./init-config.sh
./init-android-libyuv.sh
./init-android-soundtouch.sh
3.1 init-config.sh 配置ffmpeg的裁剪,我们可以通过修改module.sh进行对ffmpeg的裁剪;
3.2 init-android-libyuv.sh 把远程库 https://github.com/Bilibili/libyuv.git clone 到本地extra/libyuv 然后把本地extra/libyuv更新到ijkmedia/ijkyuv中
3.3 init-android-soundtouch.sh 把远程库 https://github.com/Bilibili/soundtouch.git clone 到本地extra/soundtouch 然后把 本地extra/soundtouch更新到ijkmedia/ijksoundtouch中
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
1.开发环境检测,根据不同的PC环境获取不同的编译目录;不同的PC环境对应的的NDK交叉编译工具的目录是不一样的;
MAC: NDK → toolchains → arm-linux-androideabi-4.9 → prebuild → darwin-x86_64
Linux: NDK → toolchains → arm-linux-androideabi-4.9 → prebuild → linux-x86_64
Window: NDK → toolchains → arm-linux-androideabi-4.9 → prebuild →windows-x86_64
2.根据CPU架构配置不同的编译参数
3.GCC 编译FFmpeg (静态库)
4.把静态库打包成一个动态库
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- ./
1. cd ijkplayer/ijkplayer-$ARCH/src/main/jni
2. 执行 ndk-build, 会在ijkplayer/ijkplayer-$ARCH/src/main/jni/libs生成我
们需要的库文件.
- 1
- 2
- 3
- 4
- 5