因为工作需要在Android上接受RTMP推流,只能想办法架设一个RTMP服务器。调研了一下决定用Nginx配合RTMP模块。不过目前为止,Nginx对交叉编译的支持还不是很友好,而且Android的C库bionic相比GNU的C库glibc还是有些差异(没有glob,没有crypt)。尽管参考了不少博客,一路上还是踩了不少坑,整理记录一下。
2018.7.31更新
- macOS High Sierra 10.13.6, Android Studio 3.1.3 测试可用
- 脚本目前只支持
ndk r15c
(这里下载)
主机环境
系统:macOS Sierra 10.12.5
准备工作
为了方便,直接用的Android Studio 2.3.3,主要是用到了adb
,还需要下好ndk
。
在Android Studio的Preferences里直接搜索sdk,可以找到Android SDK Location
,adb
位于platform-tools目录内,而ndk-bundle就是ndk
目录。
adb
默认目录:$HOME/Library/Android/sdk/platform-tools
ndk
默认目录:$HOME/Library/Android/sdk/ndk-bundle
将adb
所在目录加入环境变量,以便后续使用:1
export PATH="$HOME/Library/Android/sdk/platform-tools:$PATH"
直达总结 >>>
编译
交叉编译OpenSSL
虽然Nginx支持指定OpenSSL源码编译,但并不是交叉编译的,最终链接时会有问题,因此需要先交叉编译OpenSSL。这部分参考OpenSSL的官方wiki。
环境脚本
编辑该文件:
第18行的
_ANDROID_NDK
变量最终用于生成ANDROID_NDK_ROOT
。之前我们已经知道ndk目录了,因此可以直接设置ANDROID_NDK_ROOT
,将第11行(空行)替换为:1
export ANDROID_NDK_ROOT=$HOME/Library/Android/sdk/ndk-bundle
等号右边部分就是ndk目录,按需更改
第25行的
_ANDROID_EABI
用于指定EABI,可以在$ANDROID_NDK_ROOT/toolchains
下找到,常见的Android一般运行在arm架构的CPU上,因此填arm-linux-androideabi对应的那个,我这里是arm-linux-androideabi-4.9
。如果是x86架构的,就应该填x86对应的那个,同时要记得改第30行的_ANDROID_ARCH
变量第39行的
_ANDROID_API
指定Android API等级,可以根据需要改,我改成了21第122行的引号改为括号:
1
ANDROID_TOOLS=(arm-linux-androideabi-gcc arm-linux-androideabi-ranlib arm-linux-androideabi-ld)
否则第132行的for循环会有问题。同样地,x86架构的需要将第125行的引号改为括号
第201行的==改为=
- 把行结束符改为当前系统对应的行结束符(例如在Sublime Text 3中,View > Line Endings可以切换),否则脚本不能正常执行。下载下来的是Windows行结束符。
编辑后完整的脚本见GitHub,在终端中给脚本加上执行权限:1
chmod +x Setenv-android.sh
编译
从官网下载最新LTS版本的OpenSSL源码(当前最新版是1.1.0f),解压到Setenv-android.sh脚本所在目录
依次执行以下命令:1
2
3
4
5
6
7
8
9
10
11
12# 执行环境脚本,第一个句点不能省略
. ./Setenv-android.sh
export OPENSSL_DIR=/usr/local/ssl/$ANDROID_API
# 进入openssl源码目录,版本号按需更改
cd openssl-1.1.0f/
# 生成Makefile
KERNEL_BITS=32 ./config shared no-ssl2 no-ssl3 no-comp no-hw no-engine \
--openssldir=$OPENSSL_DIR --prefix=$OPENSSL_DIR
make depend
make all
# -E 保留当前的环境变量给root用户
sudo -E make install CC=$ANDROID_TOOLCHAIN/arm-linux-androideabi-gcc RANLIB=$ANDROID_TOOLCHAIN/arm-linux-androideabi-ranlib
编译Nginx
准备Nginx源码
下载最新稳定版Nginx的源码、RTMP模块的源码,放在同一目录下。由于Nginx的configure在执行过程中会编译一些测试程序来获取一些信息,而我们交叉编译出来的测试程序不可能在宿主机上运行,会导致获取信息有误,因此要做一些处理,通过adb
在Android上执行测试程序来使其正常工作:
编辑nginx/auto/feature文件
在if [ -x $NGX_AUTOTEST ]; then
和case "$ngx_feature_run" in
之间添加:1
adb push $NGX_AUTOTEST /data/local/tmp 2>&1 >/dev/null
并且在该case结束后(esac之后)添加:
1
adb shell rm /data/local/tmp/$(basename $NGX_AUTOTEST)
再将
/bin/sh -c $NGX_AUTOTEST
和`$NGX_AUTOTEST`
全部替换成:1
adb shell /data/local/tmp/$(basename $NGX_AUTOTEST)
这一步是将测试程序通过
adb
拷贝到Android的/data/local/tmp目录,在Android上执行测试程序,并在结束测试后删除测试程序,因此要事先确保adb
能够正常工作编辑nginx/auto/include文件
将ngx_test="$CC -o $NGX_AUTOTEST $NGX_AUTOTEST.c"
替换成:1
ngx_test="$CC $CC_AUX_FLAGS -o $NGX_AUTOTEST $NGX_AUTOTEST.c"
测试头文件时,要确保--sysroot参数正确,如果不正确就通过设置CC_AUX_FLAGS环境变量来指定,因此这里添加
$CC_AUX_FLAGS
,并且在执行configure之前执行:1
export CC_AUX_FLAGS="--sysroot=$ANDROID_SYSROOT"
编辑nginx/auto/types/sizeof文件
将ngx_size=`$NGX_AUTOTEST`
替换为:1
2
3adb push $NGX_AUTOTEST /data/local/tmp 2>&1 >/dev/null
ngx_size=`adb shell /data/local/tmp/$(basename $NGX_AUTOTEST)`
adb shell rm /data/local/tmp/$(basename $NGX_AUTOTEST)编辑nginx/auto/lib/openssl/conf文件
因为编译openssl时没有用.openssl
子目录,所以要将.openssl/
全部移除编辑nginx/src/os/unix/ngx_user.c源文件
由于Android的C库没有crypt,因此需要修改该文件中对crypt()函数的调用,改成调用OpenSSL中的DES_crypt()方法
引入头文件:1
将调用
value = crypt((char *) key, (char *) salt);
改为DES_crypt():1
value = DES_crypt((char *) key, (char *) salt);
准备glob
由于Android的C库bionic没有glob,需要下载相应源码,glob.h和glob.c
编辑glob.h
删除以下代码:1
2
3
4
5
6
typedef __size_t size_t;编辑glob.c
删除对issetugid()
的调用将
glob.h
和glob.c
文件移动或复制到nginx/src/os/unix目录编辑nginx/auto/sources文件
在UNIX_DEPS
中添加glob.h
的路径:1
src/os/unix/glob.h \
在
UNIX_SRCS
中添加glob.c
的路径:1
src/os/unix/glob.c \
编辑好的glob.h和glob.c见GitHub
设置、导出环境变量
1 | START_DIR=$PWD |
生成Makefile
1 | # 进入nginx目录,版本号按需更改 |
编译
1 | make -j8 |
编译成功的文件位于$DESTDIR/sdcard/nginx目录
总结
为使用方便,整理了相关脚本,见GitHub
执行脚本前,确保adb
和ndk
都正常可以使用,并且下载好相关源码:
- 下载Nginx最新稳定版源码压缩包(
make_nginx.sh
会自动解压) - 下载nginx-rtmp-module最新稳定版源码
- 下载openssl最新稳定LTS版源码、 openssl-fips最新版源码,并解压
- 下载GitHub仓库代码,并组织成如下目录结构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19.
├── Setenv-android.sh
├── glob(glob.c和glob.h文件由make_nginx.sh脚本在执行过程中复制到nginx/src/os/unix目录下)
│ ├── glob.c
│ └── glob.h
├── make_nginx.sh
├── make_openssl.sh
│
├── nginx-1.12.0/(该目录由make_nginx.sh脚本自动解压对应的压缩包生成)
│ └── ...
├── nginx-1.12.0.tar.gz(Nginx源码压缩包,版本号可以不一样)
├── nginx-rtmp-module/
│ └── ...
├── openssl-1.1.0f/(openssl源码,版本号可以不一样)
│ └── ...
├── openssl-fips-2.0.16/(openssl-fips源码,版本号可以不一样)
│ └── ...
└── sdcard(该目录在编译成功后会自动生成)
└── nginx
依次执行脚本即可1
2
3# 第一个句点不能省略,用于保留脚本之间的环境变量
. ./make_openssl.sh
. ./make_nginx.sh