Android 音视频开发实践系列-01-ndk-build编译rtmpdump

导读:本篇文章讲解 Android 音视频开发实践系列-01-ndk-build编译rtmpdump,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

在上一篇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.mkApplication.mk 配置提供了更具体的文档。

克隆rtmpdump源码

rtmpdump官网:RTMPDump

git clone git://git.ffmpeg.org/rtmpdump

笔者是本地目录/work/librtmp/demo中克隆的源码:

Android 音视频开发实践系列-01-ndk-build编译rtmpdump

修改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。

Android 音视频开发实践系列-01-ndk-build编译rtmpdump

下载NDK

NDK官方下载链接:

不受支持的 NDK 下载  |  Android NDK  |  Android Developers

Unsupported Downloads · android/ndk Wiki · GitHub

或者在AndroidStudio的SDKManager里下载:

Android 音视频开发实践系列-01-ndk-build编译rtmpdump

笔者使用的是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,即可开始编译。

Android 音视频开发实践系列-01-ndk-build编译rtmpdump

顺利的话就可以在/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头文件等也是我们需要的。

Android 音视频开发实践系列-01-ndk-build编译rtmpdump

到此,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

(0)
seven_的头像seven_bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!