简介:librtmp库作为rtmpdump工具的核心组件,长期用于RTMP协议处理与实时流媒体开发。该librtmp库实测可在Linux环境下完成交叉编译,面向需要在x86开发机上为ARM等嵌入式平台构建RTMP库的开发者,可帮助解决编译链配置与Makefile调整等常见问题。压缩包共106个文件,以86个.h头文件为主,另含6个.lib库文件、5个.c源码、Makefile及相关工程配置文件,整体大小4.35MB,便于直接对照学习。目前已有1421人学习下载,具备一定参考价值。资源中包含librtmp、amf、hashswf、parseurl、log等关键模块实现,从RTMP握手、AMF编解码到数据收发均有涉及,读者既能借此厘清协议细节,也能利用预编译库快速集成,或按需交叉编译到目标平台,尤其适合嵌入式流媒体项目前期预研与实战排错参考。对于需要移植到嵌入式场景的团队而言,这份资源提供了完整的编译参考和模块划分,可大幅减少重复排查时间。 做嵌入式流媒体传输的朋友,应该都绕不过 librtmp 这个库。网络摄像头、机顶盒、ARM 开发板,只要是做 RTMP 推流或者拉流,这套开源的 RTMP 客户端库几乎是默认首选。最近我手头一个飞腾 ARM 平台的视频推送项目,需要把原来在 x86 环境里跑得好好的 RTMP 客户端整体迁移过去,于是把 librtmp 在 Linux 下交叉编译的完整流程走了一遍。整个过程比预想中要绕不少,主要卡在依赖库的交叉编译和工具链的细节上,这里把实操过程和踩坑记录整理出来,希望能帮你省下半天时间。
1. 交叉编译 librtmp 之前先想清楚这几件事
1.1 交叉编译的基本逻辑
所谓交叉编译,就是在当前这台 x86_64 的 Linux 编译机上,用一套目标平台的工具链,编译出在 ARM 架构处理器上运行的二进制文件。之所以不能直接在开发板上编译,一是大多数嵌入式设备的算力、内存都比较有限,编译大型库会非常慢;二是很多产品的主控上根本没有完整的编译环境,只有精简的运行时。我自己的习惯是主机用 Ubuntu 20.04 或 22.04,目标平台是 64 位 ARM 的用 aarch64 工具链,32 位 ARM 的用 arm-linux-gnueabihf 工具链。
这里要注意硬浮点和软浮点的区别,现在绝大多数新的 ARM 平台都是硬浮点,选工具链时带hf的基本不会错。但如果是老内核、老根文件系统的设备,还是要先确认目标板的 ABI 和浮点参数,否则编译出来的程序一运行就直接报 Illegal instruction 或者找不到加载器,这种问题排查起来非常痛苦,因为编译阶段完全察觉不到。
1.2 librtmp 的依赖关系决定了编译顺序
很多人以为编译 librtmp 就是进到源码目录一条 make 的事,实际不是。librtmp 的底层依赖 OpenSSL 做 TLS 加密握手,依赖 zlib 处理压缩数据,所以编译顺序必须从底层依赖往上排:先编 zlib,再编 OpenSSL,最后编 librtmp。反过来先编 librtmp,编译器会直接告诉你头文件找不到,而且报错位置很迷惑,有时候是 ssl.h 找不到,有时候是 zlib.h 的问题,新手很容易误以为 librtmp 源码有问题。
这个依赖关系也解释了为什么官方仓库里提供的预编译二进制包不能直接用:目标板上如果没有对应的.so,程序跑起来会报error while loading shared libraries。所以我建议一开始就把三个库的 install 目录规划好,统一放在一个目录里,后面写 Makefile 和链接参数都会省事很多。
2. 环境准备与工具链选型
2.1 主机环境与工具链安装
我这次的主机是 Ubuntu 22.04,x86_64 架构,目标板是飞腾 FT-2000/4,64 位 ARM 架构。工具链我直接用了 Ubuntu 自带的交叉编译包,没有额外去装 Linaro。安装命令很简单:
# 64位 ARM sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu # 32位 ARM(根据目标平台选择) sudo apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf安装完记得验证一下工具链版本,确保环境变量和 PATH 没有问题:
aarch64-linux-gnu-gcc -v如果项目里明确指定要用 Linaro 的交叉编译工具链,那就需要去官方镜像站下载对应版本的压缩包,解压后把bin目录加入 PATH。相比 Ubuntu 仓库里的版本,Linaro 工具链在某些老内核目标板上的兼容性确实更好,但引入方式更繁琐,交叉编译时所有路径都要手动指定,很考验环境配置的细心程度。我自己实际跑下来,如果目标板内核版本不是特别老(4.x 以上),直接用 Ubuntu 自带的 aarch64-linux-gnu 完全够用。
2.2 整理目录结构
我习惯把所有交叉编译产物放到一个独立的目录里,避免污染主机环境,也方便后面打包发给其他同事。这里给一个参考结构:
export ROOT_DIR=$HOME/cross_build export SYSROOT=$ROOT_DIR/sysroot-aarch64 mkdir -p $SYSROOT/{lib,include} $ROOT_DIR/{zlib,openssl,librtmp}后面所有库的 install 路径都指向$SYSROOT,最后把整个sysroot-aarch64目录拷到开发板或者打包进根文件系统,依赖关系就非常清晰了。很多人交叉编译习惯把库装到默认的/usr/local,这样虽然能编过,但产物里全是主机路径,部署到板子上就必须重新整理一遍,反而更浪费时间。
3. 依赖库的交叉编译完整流程
3.1 第一个依赖:zlib
zlib 的交叉编译是所有依赖里最简单的,它没有那种复杂的 configure 检测脚本,主要就是设置 CC 和 AR 环境变量,然后走 Makefile 的交叉编译分支。我使用的是 zlib 1.2.13,目前 1.2.x 系列里比较新的版本,和 librtmp、OpenSSL 1.1.1 都兼容。
cd $ROOT_DIR/zlib wget https://zlib.net/zlib-1.2.13.tar.gz tar zxf zlib-1.2.13.tar.gz && cd zlib-1.2.13 export CC=aarch64-linux-gnu-gcc export AR=aarch64-linux-gnu-ar ./configure --prefix=$SYSROOT --static make -j$(nproc) make install这里我特意加了--static,是因为 librtmp 链接 zlib 时用静态库可以减少目标板上动态库的数量。不过要注意,--static只对 zlib 生效,OpenSSL 那边不建议全静态编译,原因后面会细说。另外提醒一句,make install完成后最好把环境变量 CC、AR 清理掉,避免影响后面 OpenSSL 的配置。
3.2 第二个依赖:OpenSSL 1.1.1
OpenSSL 的交叉编译比 zlib 容易踩坑。我强烈建议用 OpenSSL 1.1.1 系列的最新版,比如 1.1.1w,不要用 OpenSSL 3.x。原因很简单:librtmp 的老代码大量使用 OpenSSL 1.1 的 API,3.x 里不少接口废弃或者改成了宏,编译时会有一堆函数不可用的报错,就算强行编过,代码里也要额外加兼容层,完全没有必要。
编译命令如下:
cd $ROOT_DIR/openssl wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz tar zxf openssl-1.1.1w.tar.gz && cd openssl-1.1.1w ./Configure linux-aarch64 \ --prefix=$SYSROOT \ --cross-compile-prefix=aarch64-linux-gnu- \ no-asm \ shared \ -fPIC make -j$(nproc) make install几个关键参数的作用我逐个说一下。
linux-aarch64是 OpenSSL 预定义的目标平台配置名称,32 位 ARM 要用linux-armv4,这个配置决定了 OpenSSL 内部编译哪些架构相关的代码。--cross-compile-prefix指定交叉工具链前缀,后面 make 时 OpenSSL 会自动拼接出aarch64-linux-gnu-gcc这样的完整命令。no-asm关闭汇编优化。这一步是我最容易漏的:嵌入式目标板的汇编指令集和 x86 完全不同,OpenSSL 的 asm 代码在某些精简工具链上会因为汇编器版本过老或者指令不匹配而失败。关了之后只是加解密性能略有下降,对 RTMP 推流这种场景完全感知不到。shared生成动态库,同时加-fPIC,确保后续链接进 librtmp 的.so时不会报位置无关代码相关的错误。
3.3 第三个步骤:librtmp 本身
librtmp 的源码在 rtmpdump 项目仓库里,clone 或者下载 release 包都可以:
cd $ROOT_DIR/librtmp git clone https://git.ffmpeg.org/rtmpdump cd rtmpdump/librtmplibrtmp 的 Makefile 支持CROSS_COMPILE变量,不需要手动去改源码,把编译参数一次性传进去就行。我最后的编译命令长这样:
make clean make \ CROSS_COMPILE=aarch64-linux-gnu- \ CRYPTO=OPENSSL \ XCFLAGS="-I$SYSROOT/include" \ XLDFLAGS="-L$SYSROOT/lib" \ prefix=$SYSROOT \ SHARED=yes \ -j$(nproc)编译完执行:
make install参数含义解释一下:
CRYPTO=OPENSSL:告诉 librtmp 使用 OpenSSL 作为加密后端。如果你用的是 mbedTLS,可以改成CRYPTO=MBEDTLS,但要重新配置 XCFLAGS 和 XLDFLAGS。XCFLAGS="-I$SYSROOT/include":让编译时能找到 zlib 和 OpenSSL 的头文件。XLDFLAGS="-L$SYSROOT/lib":让链接时能找到 zlib 和 OpenSSL 的库文件。SHARED=yes:生成动态库librtmp.so。如果目标板内存特别紧张,也可以SHARED=no只编静态库,按需选择。
编完之后验证一下产物架构:
file $SYSROOT/lib/librtmp.so看到输出里有ARM aarch64字样,就说明交叉编译这一步成功了。
4. 把编译结果集成到项目里
4.1 准备一个测试程序验证
只编译出库还不够,至少要写一个小的测试程序跑通推流或拉流链路,才能确认 librtmp 的 API 调用、OpenSSL 的链接、目标板的动态加载都没有问题。
我在实际项目里会先写一个最简单的 RTMP 拉流程序,核心就三个接口:RTMP_Init、RTMP_Connect、RTMP_Read。编译时直接用交叉编译器链接:
aarch64-linux-gnu-gcc -o rtmp_test rtmp_test.c \ -I$SYSROOT/include \ -L$SYSROOT/lib \ -lrtmp -lssl -lcrypto -lz -ldl -lpthread这里特别要注意链接顺序:要把-lrtmp写在依赖库前面。链接器是从左往右扫描的,如果-lrtmp写在最后,一旦 librtmp 里有引用 OpenSSL 的符号,链接器可能已经错过了 OpenSSL 的库,就会报一堆 undefined reference,这种问题定位起来很迷惑,其实是链接顺序不对。
4.2 部署到开发板
测试程序编译好之后,用 scp、U 盘或者 adb 拷到开发板上。动态库也要一并拷贝,我把$SYSROOT/lib下的libz.so、libssl.so、libcrypto.so、librtmp.so都放到了开发板的/usr/lib或者自定义的库目录,然后设置LD_LIBRARY_PATH指向该目录。如果目标板上已经有系统自带的 OpenSSL,最好先看一下版本,老版本系统里通常还是 1.0.2 或者 1.1.1,和编译时如果一致可以尽量复用,不一致就直接用我们编好的.so,避免加载混乱。
4.3 在开发板上验证
跑一下测试程序:
./rtmp_test rtmp://192.168.1.100/live/test如果库路径没问题,程序就能正常连接 RTMP 服务器并拉取数据。如果报symbol lookup error,通常是你目标板上的 OpenSSL 版本和编译时的不一致,需要把配套的.so一起覆盖过去,不要让程序去加载系统自带的旧版本 OpenSSL。如果报No such file or directory,那基本就是动态加载器版本不匹配的问题,这个后面详细说。
5. 交叉编译高频问题与排查实录
5.1 openssl/ssl.h 头文件找不到
这个报错是头文件路径没指对。先确认 OpenSSL 是否安装到了$SYSROOT/include,再看 Makefile 里的XCFLAGS是否包含了-I$SYSROOT/include。如果路径写对了还找不到,用find $SYSROOT -name ssl.h查一下头文件实际位置,OpenSSL 的 install 路径有时候会因为 Configure 参数差异落在意外的地方。
5.2 链接时大量 undefined reference
这是我遇到最多的坑,几乎都是 OpenSSL 3.x 导致。报错信息类似下面这样:
undefined reference to `SSL_library_init' undefined reference to `OpenSSL_add_all_algorithms'SSL_library_init在 OpenSSL 1.1 里已经变成宏,3.x 里直接废弃了。librtmp 老版本代码调用的还是旧 API。解决办法只有一个:换回 OpenSSL 1.1.1 重新编译,不要尝试改 librtmp 源码去适配 3.x。真要适配 3.x,牵涉的接口改动远不止这一两处,成本完全不值得。
5.3 编译 OpenSSL 时汇编语法错误
如果碰到类似.arch armv8-a+crypto的汇编指令报错,十有八九是新工具链编旧源码时 OpenSSL 的 asm 优化触发了兼容性问题。解决方式就是在 Configure 阶段加no-asm,其他参数都不用动。
5.4 程序放到开发板报 /lib/ld-linux-aarch64.so.1: No such file or directory
这个报错说明你编译用的工具链对应的 glibc 版本比目标板根文件系统的 glibc 高,程序在板子上找不到匹配的动态加载器。处理方式有两个:一是换成与目标板系统匹配的旧版工具链,二是升级目标板的根文件系统。如果产品已经量产,改根文件系统不现实,那只能老老实实找和老系统 glibc 匹配的工具链版本。排查命令用 readelf 看程序的动态加载器:
aarch64-linux-gnu-readelf -l rtmp_test | grep interpreter然后对比板子上/lib下实际存在的加载器文件。
5.5 编译时报需要重新编译带 -fPIC 的错误
编译 librtmp 时如果收到类似recompile with -fPIC的错误,说明依赖库编译时没有加-fPIC,导致它们无法被合并进 librtmp 的共享库。解决方法是把 zlib、OpenSSL 的编译选项里加上-fPIC重新编译。很多人在这里反复折腾,其实就是最开始没给依赖库加位置无关代码参数。
6. 实践中的几个额外建议
除了上面的具体步骤,我再补充几个实践中的判断依据。
工具链方面,不用过度迷信某个特定版本。我建议先用 Ubuntu 自带的交叉工具链快速跑通全流程,确认代码能编译、板子能运行,再回头考虑是不是要换 Linaro 或其他定制工具链。先用最简单的方案验证,比一开始就纠结工具链版本要高效得多。
依赖库版本方面,zlib 选 1.2.12 或 1.2.13 都可以,OpenSSL 尽量用 1.1.1 系列的最新小版本。版本太老会有安全告警,版本太新和 librtmp 的 API 兼容性又容易出问题,1.1.1 是当前最稳妥的选择。至于 librtmp 本身,直接拉 rtmpdump 仓库的 master 分支一般没问题,这是社区长期维护的活跃仓库,比找几个镜像站的老 release 包更可靠。
集成方面,如果你的项目里还要用 Qt 或者 ffmpeg,思路是一样的:先保证这三个库的交叉编译产物齐全,再向外扩展。librtmp 只是整个链路的一环,把依赖和工具链理清楚,后面编什么都顺。
最后再分享一个我特别推荐的检查习惯:编译完不管是什么架构的库和程序,都先用file和readelf确认当前架构和依赖,拷贝到板子之前就排查掉一半的问题。等上了板子再排查,调试成本高出好几倍不说,还容易让人怀疑是硬件或系统层面的问题,白白浪费时间。
本文还有配套的精品资源,点击获取