简介面向需要在 Windows 10 与 VS2017 环境下使用 RTMP 协议进行流媒体开发的 C/C 工程师这份资源提供了一套可直接编译的 librtmp 静态库工程省去自行移植与配置依赖的繁琐过程。压缩包共 238 个文件约 49.12MB以 163 个头文件、28 个动态库、8 个静态库及少量 C 源文件为主并附带 openssl-1.0.1c、zlib-1.2.8 等依赖源码与预编译产物以及 librtmp.sln 解决方案和 VS 工程配置文件目录按 lib、librtmp、vs2017 等模块划分结构清晰便于检索。已有 640 人学习下载适合需要快速搭建 RTMP 推流或拉流基础环境、研究 librtmp 内部实现的中级开发者参考可据此直接编译出库文件并接入自有项目减少环境适配与依赖排查的时间成本。1. 拿到一个能直接编译的 librtmp 静态库先别急着双击 sln前阵子帮一个做流媒体采集的同事排查推流失败的问题最后定位到 RTMP 握手阶段需要把 librtmp 单独编出来做日志跟踪。网上流传的源码包大多只给一堆 .c 和 .h缺 Makefile、缺 openssl 依赖、缺 zlib 头文件路径在 win10 vs2017 这套组合上折腾半天是常态。这次拿到的这个包不一样它把 librtmp.lib 的完整工程、预编译引用库和源代码一起打包解压后直接能用 VS2017 打开编译不用再自己去配 OpenSSL 和 zlib。适合两类人一是需要在 Windows 桌面端做 RTMP 推拉流、又不想引入 FFmpeg 全量依赖的开发者二是想读 librtmp 源码、改握手逻辑或加自定义鉴权字段的二次开发者。下面按「这包是什么 → 怎么编起来 → 工程结构怎么读 → 核心源码怎么改 → 踩过哪些坑」的顺序拆。2. 编译落地从解压到拿到 librtmp.lib 的完整链路2.1 解压后的目录长什么样这个包的结构是典型的「源码 依赖 工程文件」三件套解压后大致是这么几层librtmp-vs2017/ ├── librtmp.sln # VS2017 解决方案入口 ├── librtmp/ │ ├── librtmp.vcxproj # 静态库工程 │ ├── rtmp.c # 协议主实现 │ ├── rtmp.h # 对外 API 头 │ ├── handshake.c/h # 握手流程 │ ├── amf.c/h # AMF0/AMF3 编解码 │ ├── hashswf.c/h # SWF 校验部分场景用 │ ├── log.c/h # 日志回调 │ └── ... ├── deps/ │ ├── openssl/ # 头文件 libx86/x64 各一套 │ └── zlib/ # 头文件 lib └── include/ # 对外暴露的头文件副本关键点在于deps目录librtmp 本身依赖 OpenSSL 做 RTMPS 和 digest 认证依赖 zlib 做压缩这两个依赖如果让使用者自己找版本一错就是一堆 LNK2019。包里已经把 x86 和 x64 两套预编译库放好了工程属性里的附加包含目录和附加库目录都指向deps所以打开就能编。2.2 用 VS2017 编译的步骤打开librtmp.sln后先确认两件事平台选对Win32 还是 x64要和你的主工程一致配置选 Release 或 Debug。然后直接生成解决方案即可。命令行方式更适合集成到 CI# 在 VS2017 开发者命令提示符下执行注意平台参数要和依赖库匹配 msbuild librtmp.sln /p:ConfigurationRelease /p:Platformx64 /t:Rebuild/t:Rebuild强制全量重编避免增量编译残留旧 obj 导致符号对不上。/p:Platform必须和deps/openssl下你选的那套库一致——包里 x86 和 x64 是分开放的选错平台会在链接阶段报LNK2038或直接找不到 lib。编译成功后产物在librtmp/x64/Release/librtmp.lib头文件用include/下那份。2.3 工程属性里几个必须核对的配置项即使包已经配好换机器或换 VS 版本后仍要核对这几项否则编译能过、链接会挂配置项应设值作用配置类型静态库 (.lib)决定产物形态C/C → 附加包含目录..\deps\openssl\include;..\deps\zlib\include找到 ssl.h、zlib.h链接器 → 附加库目录..\deps\openssl\lib\x64;..\deps\zlib\lib\x64找到 .lib链接器 → 附加依赖项libssl.lib;libcrypto.lib;zlib.lib解析外部符号C/C → 预处理器_CRT_SECURE_NO_WARNINGS;WIN32关掉安全函数告警运行库与主工程一致MD 或 MT避免 CRT 冲突运行库这一项是最容易被忽略的静态库本身不链接 CRT但主工程如果用的是/MT而 librtmp 编译时用了/MD混用会在最终链接时报重复符号。常见做法是让 librtmp 工程的运行库设置跟随主工程改完重新生成一次。提示如果只需要纯 RTMP不涉及 RTMPS 和 digest 认证可以在预处理器里去掉CRYPTO宏这样连 OpenSSL 都不用链产物更小但会失去部分服务器的鉴权兼容性。3. 工程结构拆解librtmp 的模块划分与调用链3.1 从 API 入口看整体分层librtmp 对外暴露的接口集中在rtmp.h核心是RTMP_Alloc/RTMP_Init/RTMP_SetupURL/RTMP_Connect/RTMP_ConnectStream这一串。内部实现分三层最上面是rtmp.c里的会话管理中间是handshake.c的握手状态机和amf.c的消息编解码最下面是rtmpproto.c负责具体协议包chunk的收发。这个分层在源码里体现得很清楚——rtmp.c基本不碰字节流细节只做状态流转和回调分发。调用链大致是RTMP_Connect内部先走HandShake三次握手C0/C1/C2 和 S0/S1/S2握手成功后RTMP_ConnectStream发送connect命令AMF0 编码等服务器回_result后才算真正建立会话。理解这条链改代码时才知道该动哪个文件想加自定义连接参数就改rtmp.c里拼 AMF 的那段想改握手行为就进handshake.c。3.2 握手模块的关键实现握手是 RTMP 最容易出问题的地方handshake.c里HandShake函数负责整个流程。核心逻辑是构造 C1 包时间戳 零填充 随机数发出去后读 S0/S1校验版本号再回 C2。下面这段是 C1 构造的简化示意实际源码里还带 digest 计算// 构造 C14 字节时间戳 4 字节零 1528 字节随机 char C1[1536]; memset(C1, 0, sizeof(C1)); uint32_t uptime htonl((uint32_t)(RTMP_GetTime() - 0)); memcpy(C1, uptime, 4); // 时间戳服务器一般不校验 // C1[4..7] 保持为 0 for (int i 8; i 1536; i) { C1[i] (char)(rand() 0xff); // 随机填充digest 模式下会被覆盖 }时间戳字段用htonl转网络序这是跨平台必须做的随机填充部分在简单握手模式下无所谓但走 digest 认证时会被HMACsha256的结果替换掉特定偏移的 32 字节。改这里最常见的需求是兼容某些服务器对 C1 随机性的校验把rand()换成更均匀的随机源即可但要注意别动 digest 覆盖的那段偏移否则认证必失败。3.3 AMF 编解码与命令发送amf.c实现 AMF0 的类型标记读写rtmp.c里的SendConnectPacket用它拼connect命令。一个典型的 connect 包包含 app 名、tcUrl、flashVer 等字段。下面是从源码提炼的拼包逻辑// 发送 connect 命令pkg 是 AMF 编码缓冲区 RTMPPacket packet; char *p NULL; packet.m_nChannel 0x03; // 命令走 chunk stream 3 packet.m_headerType RTMP_PACKET_SIZE_LARGE; packet.m_packetType RTMP_PACKET_TYPE_INVOKE; // 0x14 命令消息 packet.m_nInfoField2 0; // AMF0: 字符串 connect 数字 1事务 ID 对象参数 p packet.m_body; p AMF_EncodeString(p, end, connect); p AMF_EncodeNumber(p, end, 1.0); p AMF_EncodeString(p, end, app); p AMF_EncodeString(p, end, app); // 推流地址里的 app 段m_nChannel 0x03是协议规定的命令通道不能随便改事务 ID 用 1.0 是惯例服务器回_result时会带回同一个值用于匹配。要加自定义字段比如某些平台的 token就在对象编码段追加一对AMF_EncodeString 值位置放在标准字段之后服务器一般会忽略不认识的键。4. 避坑与排查编译和运行阶段的高频翻车点4.1 链接报 LNK2019 找不到 SSL 符号现象编译通过链接主工程时报unresolved external symbol SSL_new之类。原因通常是主工程没把libssl.lib、libcrypto.lib加进附加依赖项或者平台选错x64 工程链了 x86 的库。解决核对主工程的附加库目录指向deps/openssl/lib/x64附加依赖项补齐三个 lib如果主工程本身也用了别的 OpenSSL 版本会出现符号冲突这时要么统一版本要么把 librtmp 的 OpenSSL 调用隔离到独立 DLL 里。4.2 运行库不匹配导致重复定义现象链接期报LNK2005: _malloc 已经在 libcmt.lib 中定义。原因是 librtmp 用/MT编主工程用/MD两套 CRT 混链。解决把 librtmp 工程的「C/C → 代码生成 → 运行库」改成和主工程一致重新生成。这个坑在把静态库分发给别人时特别常见血泪经验是交付前一定问清楚对方的运行库设置。4.3 握手成功但 connect 超时现象RTMP_Connect返回成功RTMP_ConnectStream卡住直到超时。原因多半是 tcUrl 拼错或 app 名不对服务器收到 connect 后不回_result。排查方法打开 librtmp 的日志回调RTMP_LogSetLevel(RTMP_LOGDEBUG)看发出的 connect 包里 app 和 tcUrl 字段是否和推流地址一致。常见错误是把rtmp://host/live/stream里的live当成 app、stream当成 playpath实际 app 是live、playpath 是stream但有些服务器要求 app 带查询参数得按服务端文档来。4.4 x64 下指针截断引发崩溃现象Debug 下正常Release x64 下随机崩溃。原因是老版本 librtmp 里有把指针强转int的地方历史遗留x64 下高 32 位被截断。解决全局搜(int)强转指针的写法改成intptr_t这个包里如果已经修过就不用管但二次开发时自己加代码别犯同样的错。4.5 静态库被多个模块引用导致符号重复现象主工程和某个插件 DLL 都静态链接了 librtmp运行时报重复初始化或全局状态错乱。原因是 librtmp 内部有全局变量如日志级别、缓冲区静态链接进多个模块后各有一份。解决把 librtmp 编成 DLL 供多方共享或者确保只有一个模块持有 RTMP 会话其他模块通过接口调用。5. 二次开发改握手、加鉴权、做日志追踪的实操思路拿到能编译的库只是起点真正落地往往要动源码。最常见的三类改造一是加自定义鉴权字段二是替换日志输出到自己的框架三是裁剪不需要的模块减小体积。加鉴权字段的改法在SendConnectPacket里 AMF 对象编码段追加键值对比如某些服务端要求带token。改完要验证——用 Wireshark 抓包看 connect 包的 AMF 内容或者开RTMP_LOGDEBUG看发出的字段。这里有个容易翻车的点AMF 编码缓冲区大小是固定的追加字段前要确认packet.m_nBodySize够用不够就调大RTMPPacket的 body 分配否则会越界写。替换日志的做法更简单log.c里RTMP_Log是统一出口把它重定向到自己的日志宏即可但要注意它可能被多线程调用重定向的目标得是线程安全的。裁剪模块则是把hashswf.c这类用不到的文件从工程里移除同时去掉对应的宏能省几 KB对嵌入式场景有意义。验证改造是否成功我一般走三步先用官方示例地址跑通推流确认基础链路没坏再抓包对比改造前后的 connect 包差异最后在目标服务器上实测因为不同服务端对未知字段的容忍度不一样。从那以后我每次改协议层代码都强制先抓一次包再提交省得后面排查时连黑匣子都没有。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?