RTMPDump Android 端集成

2,611 阅读3分钟

RTMPDump 用来处理 RTMP 流媒体的开源工具包。能够单独使用 RTMP 通信,也可以集成到 FFmpeg 中通过 FFmpeg 接口来使用 RTMPDump。

下载地址:http://rtmpdump.mplayerhq.hu/

介绍两种方式引入 RTMPDump 的方式,一种使用 NDK 提供的交叉编译工具来进行编译成静态库再引入,第二种源码集成。实际场景中可自行选择,如果源码不多的情况下推荐使用源码集成的方式。不过有时候我们需要大量引入 C/C++ 库,这个时候还是考虑使用静态库的方式,这样源文件就不显得很多。

交叉编译工具

使用 NDK 提供的交叉编译工具来生成静态库,首先我们下载源码,可以查看一下源码。一般开源项目都会提供 Makefile 或 CMake 编译脚本。RTMPDump 提供了 Makefile,不过它有两个,一个是根目录下,另一个在 librtmp 下。 先来看根目录下:

....

# 编译工具 gcc, 但是我在用 mac 时会使用 clang 替代,具体看我后面编译脚本。
CC=$(CROSS_COMPILE)gcc
# 链接器
LD=$(CROSS_COMPILE)ld

# 目标系统
SYS=posix
#SYS=mingw

# Openssl 加密,因为 RTMP 流媒体协议支持双向加密
CRYPTO=OPENSSL
#CRYPTO=POLARSSL
#CRYPTO=GNUTLS
LIBZ=-lz
LIB_GNUTLS=-lgnutls -lhogweed -lnettle -lgmp $(LIBZ)
LIB_OPENSSL=-lssl -lcrypto $(LIBZ)
LIB_POLARSSL=-lpolarssl $(LIBZ)
CRYPTO_LIB=$(LIB_$(CRYPTO))
DEF_=-DNO_CRYPTO
CRYPTO_DEF=$(DEF_$(CRYPTO))

DEF=-DRTMPDUMP_VERSION=\"$(VERSION)\" $(CRYPTO_DEF) $(XDEF)
OPT=-O2
# 编译参数 XCFLAGS 会从外部指定.
CFLAGS=-Wall $(XCFLAGS) $(INC) $(DEF) $(OPT)
LDFLAGS=-Wall $(XLDFLAGS)

......

LIBRTMP=librtmp/librtmp.a
INCRTMP=librtmp/rtmp_sys.h librtmp/rtmp.h librtmp/log.h librtmp/amf.h

......

PROGS=rtmpdump rtmpgw rtmpsrv rtmpsuck

# 依赖两个目标:LIBRTMP 和 PROGS, 由于我们生成的是 Android 平台,所以这里我们不关心 PROGS,后面我会删掉这个目标。
all:	$(LIBRTMP) $(PROGS)

$(PROGS): $(LIBRTMP)

# 安装,创建文件、复制文件到指定目录
install:	$(PROGS)
	-mkdir -p $(BINDIR) $(SBINDIR) $(MANDIR)/man1 $(MANDIR)/man8
	cp rtmpdump$(EXT) $(BINDIR)
	cp rtmpgw$(EXT) rtmpsrv$(EXT) rtmpsuck$(EXT) $(SBINDIR)
	cp rtmpdump.1 $(MANDIR)/man1
	cp rtmpgw.8 $(MANDIR)/man8
	@cd librtmp; $(MAKE) install

clean:
	rm -f *.o rtmpdump$(EXT) rtmpgw$(EXT) rtmpsrv$(EXT) rtmpsuck$(EXT)
	@cd librtmp; $(MAKE) clean

FORCE:

# 执行 librtmp 目录下的 Makefile 文件.
$(LIBRTMP): FORCE
	@cd librtmp; $(MAKE) all

# 调用编译器编译生成 rtmpdump.o
rtmpdump: rtmpdump.o
	$(CC) $(LDFLAGS) -o $@$(EXT) $@.o $(LIBS)

....

librtmp 目录下的 Makefile.

......

# CC 编译器
CC=$(CROSS_COMPILE)gcc
# 链接器
LD=$(CROSS_COMPILE)ld
# ar 打包
AR=$(CROSS_COMPILE)ar

# 目标系统
SYS=posix
# OPENSSL 加密.
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))

......

SHARED=yes
......

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

# 生成 librtmp.a 以及动态库
all:	librtmp.a $(SO_LIB)

clean:
	rm -f *.o *.a *.$(SOX) *$(SO_EXT) librtmp.pc

# 目标依赖于 OBJS
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 $(SODIR)
	cp amf.h http.h log.h rtmp.h $(INCDIR)
	cp librtmp.a $(LIBDIR)
	cp librtmp.pc $(LIBDIR)/pkgconfig
	cp librtmp.3 $(MANDIR)/man3
	
......

整体流程大概分析一下,告诉了我们编译规则和依赖,以及安装目录。那么接下来编写 NDK 交叉编译脚本,基于 Mac 平台编写,linux 更改一下工具链位置。

# 交叉编译工具链,基于 ndk-r17c
# 你的交叉编译工具链位置,这里使用的是独立工具链生成.
TOOLCHAIN= /XX/standalone-toolchain/armv7a-standalone-toolchain

# 加入到系统路径
export PATH=$PATH:$TOOLCHAIN/bin

# 目标
target_host=arm-linux-androideabi

# 编译目标品台 api 版本
ANDROID_API=21

# 指定编译器 cflags 编译参数
export XCFLAGS="-isysroot ${TOOLCHAIN}/sysroot -isystem ${TOOLCHAIN}/sysroot/usr/include/${target_host} -D__ANDROID_API__=${ANDRO$

# 交叉编译工具,使用的是 NDK 提供的
export CROSS_COMPILE=${TOOLCHAIN}/bin${target_host}-

# 使用 clang 编译器
CC=$target_host-clang
AR=$target_host-ar
LD=$target_host-ld

make clean

# 安装,指定 SYS 为 android, prefix 安装路径, 不指定 CRYPTO=, 不生成共享库 SHARED ,去掉 ssl 加密
make install SYS=android prefix=`pwd`/install CC=$CC AR=$AR LD=$LD CRYPTO= SHARED= XDEF=-DNO_SSL

chmod +x librtmp_build.sh
./librtmp_build.sh

生成的目录

在 Android Studio 中使用

新建一个 CXX 工程,然后将编译出来头文件和静态库文件拷贝到项目中,如图

CMakeLists.txt 配置文件

cmake_minimum_required(VERSION 3.4.1)

include_directories(${CMAKE_SOURCE_DIR}/include/librtmp)

set(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI}")

add_library(
        native-lib
        SHARED
        native-lib.cpp )

target_link_libraries(
        native-lib
        log
        rtmp
)

测试获取 RTMPDump 版本

#include <jni.h>
#include <string>
#include "rtmp.h"

extern "C" JNIEXPORT jstring JNICALL
Java_com_hxj_rtmpdumpdemo_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    char *str = "hello xxx";

    char version[100];
    sprintf(version, "rtmpdump version: %d", RTMP_LibVersion());

    return env->NewStringUTF(version);
}

引入源码的方式

前面大概介绍了下 Makefile 规则,引入源码的只需要将 librtmp 目录下的源码引入,根目录下生成一些工具,而我们不必关心。

# librtmp 目录下的 Makefile.

# 依赖的目标.
OBJS=rtmp.o log.o amf.o hashswf.o parseurl.o

# 生成 librtmp.a 以及动态库
all:	librtmp.a $(SO_LIB)

# 目标依赖于 OBJS
librtmp.a: $(OBJS)
	$(AR) rs $@ $?

# 生成目标的规则.
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

可以知道我们需要那些文件,这里其实就是所有的.h 和 .c 文件。 这里我们新建一个目录 sources 用来存放.h 和 .c 文件,可以使用如下命令拷贝

注意 librtmp 路径
cp -rf ../librtmp/*.h ../librtmp/*.c ./sources

在工程新建一个目录 librtmp,然后拷贝源文件进入

修改 CMakeLists.txt 文件

cmake_minimum_required(VERSION 3.4.1)

include_directories(${CMAKE_SOURCE_DIR}/librtmp)
set(src_dir ${CMAKE_SOURCE_DIR}/librtmp)
file(GLOB srcs ${src_dir}/*.c)

# 或者使用 aux_source_directory 替代 set 和 file 两个指令.
# aux_source_directory(${CMAKE_SOURCE_DIR}/librtmp srcs)

set(CMAKE_C_FLAGS  "${CMAKE_C_FLAGS} -DNO_CRYPTO")

add_library(
        native-lib
        SHARED
        native-lib.cpp
        ${srcs}
)

target_link_libraries(
        native-lib
        log
)

由于 RTMPDump 使用到了 openssl, 我这里没有选择使用所以通过编译指令去掉。否则会报错,读者可以去掉试试。

set(CMAKE_C_FLAGS  "${CMAKE_C_FLAGS} -DNO_CRYPTO")