在上一篇Android开发实践之《NDK和JNI极速初体验》,留了一个坑:
独立工具链因官方已弃用不再介绍, ndk-build下一篇文章单独介绍,本文使用AndroidStudio新建的Native C++工程采用的是CMake的编译方式,因此只单独介绍Cmake。
这一篇,就介绍如何使用ndk-build编译rtmpdump移植到Android平台上使用。注意,笔者是在Linux平台进行的编译,Window平台下,建议使用虚拟机安装Linux系统或使用Docker进行操作。
nkd-build脚本
ndk-build 脚本使用 NDK 的基于 Make 的构建系统构建项目。Android官网络针对 ndk-build 使用的 Android.mk 和 Application.mk 配置提供了更具体的文档。
克隆rtmpdump源码
rtmpdump官网:RTMPDump
git clone git://git.ffmpeg.org/rtmpdump
笔者是本地目录/work/librtmp/demo中克隆的源码:
修改MakeFile支持Android平台编译
网上的教程,都会告诉你设置SYS=android,但如果你克隆的是rtmpdump官方的源码(没有用别人提供的rtmpdump源码),编译后会出现几个莫名其妙的报错,实际上如果你看过rtmpdump的MakeFile,会发现rtmpdump默认只支持以下平台的编译:
LIBS_posix= LIBS_darwin= LIBS_mingw=-lws2_32 -lwinmm -lgdi32
包括README文档里,也仅仅列举了对以上平台的参数支持。
To compile type "make" with SYS=<platform name>, e.g. $ make SYS=posix for Linux, Unix, etc. or $ make SYS=darwin for MacOSX or $ make SYS=mingw for Windows.
笔者一开始也不知其所以然,后来在这篇博客Android移植librtmp中找到了答案,感谢博主:blueberry_mu,感谢这位热心的大兄弟我是asha的回复。
我是asha:根据您给的方法,终于交叉编译出来了 有两个坑我来填一下 1) 新版本NDK需要加入参数 -D__ANDROID_API__=$API参数
make SYS=android CROSS_COMPILE=arm-linux-androideabi- INC=”-I/Users/asha/Library/Android/arm/sysroot/usr/include -D__ANDROID_API__=14″
参考NDK: Unified Headers issue · Issue #445 · android/ndk · GitHub 2) 博主定义了SYS=android,就需要在/librtmp/Makefile文件对应的地方增加android平台相关的定义补充下install
SOX_android=so
SODIR_android=$(LIBDIR)
SO_LDFLAGS_android=-shared -Wl,-soname,$@
INSTALL_IMPLIB_android=
make install SYS=android DESTDIR=/path/to/out
按照以上博客的指引,MakeFile修改支持SYS=android编译,完整内容如下,修改的部分增加了注释说明。
VERSION=v2.4 prefix=/usr/local incdir=$(prefix)/include/librtmp bindir=$(prefix)/bin libdir=$(prefix)/lib mandir=$(prefix)/man BINDIR=$(DESTDIR)$(bindir) INCDIR=$(DESTDIR)$(incdir) LIBDIR=$(DESTDIR)$(libdir) MANDIR=$(DESTDIR)$(mandir) CC=$(CROSS_COMPILE)gcc LD=$(CROSS_COMPILE)ld AR=$(CROSS_COMPILE)ar SYS=posix CRYPTO=OPENSSL #CRYPTO=GNUTLS DEF_POLARSSL=-DUSE_POLARSSL DEF_OPENSSL=-DUSE_OPENSSL DEF_GNUTLS=-DUSE_GNUTLS DEF_=-DNO_CRYPTO REQ_GNUTLS=gnutls,hogweed,nettle REQ_OPENSSL=libssl,libcrypto PUB_GNUTLS=-lgmp LIBZ=-lz LIBS_posix= LIBS_darwin= LIBS_mingw=-lws2_32 -lwinmm -lgdi32 LIB_GNUTLS=-lgnutls -lhogweed -lnettle -lgmp $(LIBZ) LIB_OPENSSL=-lssl -lcrypto $(LIBZ) LIB_POLARSSL=-lpolarssl $(LIBZ) PRIVATE_LIBS=$(LIBS_$(SYS)) CRYPTO_LIB=$(LIB_$(CRYPTO)) $(PRIVATE_LIBS) CRYPTO_REQ=$(REQ_$(CRYPTO)) CRYPTO_DEF=$(DEF_$(CRYPTO)) PUBLIC_LIBS=$(PUB_$(CRYPTO)) SO_VERSION=1 SOX_posix=so SOX_darwin=dylib SOX_mingw=dll #原来的MakeFile不支持Android,需要手动加上 SOX_android=so SOX=$(SOX_$(SYS)) SO_posix=.$(SOX).$(SO_VERSION) SO_darwin=.$(SO_VERSION).$(SOX) SO_mingw=-$(SO_VERSION).$(SOX) SO_EXT=$(SO_$(SYS)) SODIR_posix=$(LIBDIR) SODIR_darwin=$(LIBDIR) SODIR_mingw=$(BINDIR) #原来的MakeFile不支持Android,需要手动加上 SODIR_android=$(LIBDIR) SODIR=$(SODIR_$(SYS)) SO_LDFLAGS_posix=-shared -Wl,-soname,$@ SO_LDFLAGS_darwin=-dynamiclib -twolevel_namespace -undefined dynamic_lookup \ -fno-common -headerpad_max_install_names -install_name $(libdir)/$@ SO_LDFLAGS_mingw=-shared -Wl,--out-implib,librtmp.dll.a #原来的MakeFile不支持Android,需要手动加上 SO_LDFLAGS_android=-shared -Wl,-soname,$@ SO_LDFLAGS=$(SO_LDFLAGS_$(SYS)) INSTALL_IMPLIB_posix= INSTALL_IMPLIB_darwin= INSTALL_IMPLIB_mingw=cp librtmp.dll.a $(LIBDIR) #原来的MakeFile不支持Android,需要手动加上 INSTALL_IMPLIB_android= INSTALL_IMPLIB=$(INSTALL_IMPLIB_$(SYS)) SHARED=yes SODEF_yes=-fPIC SOLIB_yes=librtmp$(SO_EXT) SOINST_yes=install_so SO_DEF=$(SODEF_$(SHARED)) SO_LIB=$(SOLIB_$(SHARED)) SO_INST=$(SOINST_$(SHARED)) DEF=-DRTMPDUMP_VERSION=\"$(VERSION)\" $(CRYPTO_DEF) $(XDEF) OPT=-O2 CFLAGS=-Wall $(XCFLAGS) $(INC) $(DEF) $(OPT) $(SO_DEF) LDFLAGS=$(XLDFLAGS) OBJS=rtmp.o log.o amf.o hashswf.o parseurl.o all: librtmp.a $(SO_LIB) clean: rm -f *.o *.a *.$(SOX) *$(SO_EXT) librtmp.pc librtmp.a: $(OBJS) $(AR) rs $@ $? librtmp$(SO_EXT): $(OBJS) $(CC) $(SO_LDFLAGS) $(LDFLAGS) -o $@ $^ $> $(CRYPTO_LIB) ln -sf $@ librtmp.$(SOX) log.o: log.c log.h Makefile rtmp.o: rtmp.c rtmp.h rtmp_sys.h handshake.h dh.h log.h amf.h Makefile amf.o: amf.c amf.h bytes.h log.h Makefile hashswf.o: hashswf.c http.h rtmp.h rtmp_sys.h Makefile parseurl.o: parseurl.c rtmp.h rtmp_sys.h log.h Makefile librtmp.pc: librtmp.pc.in Makefile sed -e "s;@prefix@;$(prefix);" -e "s;@libdir@;$(libdir);" \ -e "s;@VERSION@;$(VERSION);" \ -e "s;@CRYPTO_REQ@;$(CRYPTO_REQ);" \ -e "s;@PUBLIC_LIBS@;$(PUBLIC_LIBS);" \ -e "s;@PRIVATE_LIBS@;$(PRIVATE_LIBS);" librtmp.pc.in > $@ install: install_base $(SO_INST) install_base: librtmp.a librtmp.pc -mkdir -p $(INCDIR) $(LIBDIR)/pkgconfig $(MANDIR)/man3 cp amf.h http.h log.h rtmp.h $(INCDIR) cp librtmp.a $(LIBDIR) cp librtmp.pc $(LIBDIR)/pkgconfig cp librtmp.3 $(MANDIR)/man3 install_so: librtmp$(SO_EXT) -mkdir -p $(SODIR) cp librtmp$(SO_EXT) $(SODIR) $(INSTALL_IMPLIB) cd $(SODIR); ln -sf librtmp$(SO_EXT) librtmp.$(SOX)
注意rtmpdump源码目录里有两个MakeFile文件,这里修改的是librtmp目录里的MakeFile。
下载NDK
NDK官方下载链接:
不受支持的 NDK 下载 | Android NDK | Android Developers
Unsupported Downloads · android/ndk Wiki · GitHub
或者在AndroidStudio的SDKManager里下载:
笔者使用的是android-ndk-r15c,因为项目有其他库必须要使用此版本编译才可以,读者可以根据实际需要使用对应的版本。
编写编译脚本
修改MakeFile支持Android平台编译后,就可以动手写编译脚本了。当然,这个编译脚本不是必须的,但是使用脚本确实很方便。
#!/bin/bash #进入源码目录 cd rtmpdump # 将NDK的路径替换成你自己的NDK路径 NDK=/work/android/android-ndk-r15c/ # 设置工具链mac苹果电脑的是darwin-x86_64 而如果是linux的话则是linux-x86_64,window的是windows-x86_64 TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/ SYSROOT=$NDK/sysroot/usr/ export XCFLAGS="-isysroot $NDK/sysroot -isystem $NDK/sysroot/usr/include/arm-linux-androideabi -D__ANDROID_API__=14 -O0 -fPIC" export XLDFLAGS="--sysroot=${NDK}/platforms/android-14/arch-arm -shared" export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi- make clean # XDEF=-DNO_SSL 的意思是不使用SSL,因为rtmp内部使用了SSL,如果开启的话需要编译链接SSL的库 # https://blog.csdn.net/a992036795/article/details/54377892 需要在MakeFile文件中增加对SYS=android的支持才能正常编译 make install prefix=`pwd`/armlibs SYS=android CRYPTO= XDEF=-DNO_SSL
如果读者编译时遇到问题,可对比参考以上脚本排查是否有其他参数没设置正确。其中export
命令用于声明临时的环境变量,不一定得这么用,读者也可以可以选择修改/etc/profile设置永久的环境变量(具体如何设置不同的系统有些差别,请自行搜索学习相关教程)或者make的时候传入参数。
注意:android-14和 -D__ANDROID_API__=14要对应,当然版本不一定得是14,看你的需求
如果你看了rtmpdump的README文档,会发现文档是这么写的:
You can cross-compile for other platforms using the CROSS_COMPILE variable: $ make CROSS_COMPILE=arm-none-linux- INC=-I/my/cross/includes Please read the Makefile to see what other make variables are used.
CROSS_COMPILE参数怎么放到make 后面了,为啥脚本用的是export CROSS_COMPILE
?因为MakeFile支持直接引用系统环境变量,好处就是环境变量定义好以后了不用每次都传参,使得MakeFile可以写的更简洁。坏处就是,万一环境变量被改了,编译可能会遇到莫名其妙的问题。
更多了解,点击学习MakeFile隐藏规则
编译生成librtmp.so
准备工作做好就可以开始编译了。进入脚本所在目录执行 sh make_rtmp.sh,即可开始编译。
顺利的话就可以在/demo/rtmpdump/armlibs目录看到生成的编译产物,其中重要的librtmp.so动态链接库在/work/librtmp/demo/rtmpdump/armlibs/lib目录中。为什么是armlibs目录,因为脚本里通过install指定了此目录:
make install prefix=`pwd`/armlibs SYS=android CRYPTO= XDEF=-DNO_SSL
armlibs目录一览如下, include/librtmp中rtmp.h头文件等也是我们需要的。
到此,rtmpdump库的编译就结束了,可以愉快开始JNI开发了。当然,使用CMake编译是可以的,对于Android开发人员来说,使用CMake可能是更好的选择。
可能遇到的问题
1.mkdir: missing operand
mkdir -p mkdir: missing operand Try 'mkdir --help' for more information. Makefile:118: recipe for target 'install_so' failed make[1]: [install_so] Error 1 (ignored) cp librtmp cp: missing destination file operand after 'librtmp' Try 'cp --help' for more information. Makefile:118: recipe for target 'install_so' failed make[1]: *** [install_so] Error 1 make[1]: Leaving directory '/home/work/librtmp/demo/rtmpdump/librtmp' Makefile:62: recipe for target 'install' failed make: *** [install] Error 2
这个问题主要是MakeFile没有支持SYS=android
,导致MakeFile 执行到以下代码时出现参数丢失:
install_so: librtmp$(SO_EXT) -mkdir -p $(SODIR) cp librtmp$(SO_EXT) $(SODIR) $(INSTALL_IMPLIB) cd $(SODIR); ln -sf librtmp$(SO_EXT) librtmp.$(SOX)
丢失的是$(SODIR)
,默认是不支持SODIR_android
的,加上就正常了。具体见上面提到的修改MakeFile支持Android平台编译内容。
SODIR_posix=$(LIBDIR) SODIR_darwin=$(LIBDIR) SODIR_mingw=$(BINDIR) SODIR=$(SODIR_$(SYS))
2.dereferencing pointer to incomplete type ‘DH’ {aka ‘struct dh_st’}
xiangang@xiangang-deepin:/media/xiangang/4418F6B718F6A6D8/work/project/c$ cd rtmpdump/xiangang@xiangang-deepin:/media/xiangang/4418F6B718F6A6D8/work/project/c/rtmpdump$ makemake[1]: 进入目录“/media/xiangang/4418F6B718F6A6D8/work/project/c/rtmpdump/librtmp”gcc -Wall -DRTMPDUMP_VERSION=\"v2.4\" -DUSE_OPENSSL -O2 -fPIC -c -o rtmp.o rtmp.cIn file included from handshake.h:86, from rtmp.c:152:dh.h: In function ‘DHInit’:dh.h:256:12: error: dereferencing pointer to incomplete type ‘DH’ {aka ‘struct dh_st’} MP_new(dh->g); ^~dh.h:171:19: note: in definition of macro ‘MP_new’ #define MP_new(m) m = BN_new() ^In file included from rtmp.c:152:handshake.h: In function ‘InitRC4Encryption’:handshake.h:120:12: error: storage size of ‘ctx’ isn’t known HMAC_CTX ctx; ^~~In file included from rtmp.c:152:handshake.h:72:35: warning: implicit declaration of function ‘HMAC_CTX_init’; did you mean ‘HMAC_CTX_new’? [-Wimplicit-function-declaration] #define HMAC_setup(ctx, key, len) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, key, len, EVP_sha256(), 0) ^~~~~~~~~~~~~handshake.h:125:3: note: in expansion of macro ‘HMAC_setup’ HMAC_setup(ctx, secretKey, 128); ^~~~~~~~~~handshake.h:74:67: warning: implicit declaration of function ‘HMAC_CTX_cleanup’; did you mean ‘HMAC_CTX_get_md’? [-Wimplicit-function-declaration] #define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, dig, &dlen); HMAC_CTX_cleanup(&ctx) ^~~~~~~~~~~~~~~~handshake.h:127:3: note: in expansion of macro ‘HMAC_finish’ HMAC_finish(ctx, digest, digestLen); ^~~~~~~~~~~In file included from rtmp.c:152:handshake.h:120:12: warning: unused variable ‘ctx’ [-Wunused-variable] HMAC_CTX ctx; ^~~handshake.h: In function ‘HMACsha256’:handshake.h:269:12: error: storage size of ‘ctx’ isn’t known HMAC_CTX ctx; ^~~handshake.h:269:12: warning: unused variable ‘ctx’ [-Wunused-variable]rtmp.c: In function ‘RTMP_ReadPacket’:rtmp.c:3555:7: warning: variable ‘didAlloc’ set but not used [-Wunused-but-set-variable] int didAlloc = FALSE; ^~~~~~~~At top level:rtmp.c:2907:19: warning: ‘av_NetConnection_Connect_Rejected’ defined but not used [-Wunused-const-variable=] static const AVal av_NetConnection_Connect_Rejected = ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~rtmp.c:1559:35: warning: ‘av_record’ defined but not used [-Wunused-const-variable=] #define SAVC(x) static const AVal av_##x = AVC(#x) ^~~rtmp.c:1905:1: note: in expansion of macro ‘SAVC’ SAVC(record); ^~~~make[1]: *** [<内置>:rtmp.o] 错误 1make[1]: 离开目录“/media/xiangang/4418F6B718F6A6D8/work/project/c/rtmpdump/librtmp”make: *** [Makefile:76:librtmp/librtmp.a] 错误 2xiangang@xiangang-deepin:/media/xiangang/4418F6B718F6A6D8/work/project/c/rtmpdump$
这个问题就比较麻烦了,搜索得到结果基本都是时候openssl的版本有问题,需要更换或者制指定openssl的编译版本,但如果只是为了编译使用,怎可以通过 CRYPTO= XDEF=-DNO_SSL
不适用SSL。
参考资料
Android移植librtmp_BlueBerry的专栏-CSDN博客_android librtmp
PolarSSL 1.2.14 released – Tech Updates – Mbed TLS (Previously PolarSSL)
PolarSSL 1.3.9 released – Tech Updates – Mbed TLS (Previously PolarSSL)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/116894.html