ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

VS2017下编译librtmp静态库:从零到可用的完整指南

VS2017下编译librtmp静态库:从零到可用的完整指南 简介面向需要在 Windows 10 下使用 VS2017 自行编译 librtmp 库的开发者这是一份可直接编译的完整资源包专门解决 RTMP 推拉流开发中 librtmp、OpenSSL 与 zlib 依赖难匹配的问题。包内包含 librtmp 源码、openssl-1.0.1c 与 zlib-1.2.8 依赖库并整理出 lib、librtmp、vs2017 等独立目录和 librtmp.sln 解决方案其中 lib 目录用于放置生成的库文件openssl-1.0.1c 与 zlib-1.2.8 分别为加密和压缩模块提供头文件与运行支持vs2017 目录保存 Visual Studio 工程配置便于按需调整编译选项。压缩包共 238 个文件以 163 个 .h 头文件、28 个 .dll 动态库、8 个 .lib 静态库、7 个 .c 源文件为主另有 .sln/.vcxproj/.tlog/.obj/.pdb 等工程与编译中间文件整体约 49.12MB结构清楚可直接打开 librtmp.sln 生成 .lib。已有 635 人学习下载适合流媒体开发初学者快速上手也对进行 RTMP 二次开发、链接阶段排查 OpenSSL/zlib 符号问题的中高级开发者有实际帮助。借助预置的依赖与工程配置可减少编译报错提升集成效率目录划分也有助于二次开发和链接排查。 做直播相关的项目尤其是Windows桌面端需要自己推流或者拉流的时候RTMP几乎是绕不开的协议。但问题在于librtmp这个库在Linux下很好编译到了Windows上就变成了一个老大难官方仓库只提供Makefile没有现成的VS工程依赖的OpenSSL和zlib还得自己折腾。很多人在这一步就被劝退了只能去网上各种找预编译好的库又担心版本不匹配。如果你用的是VS2017正在为编译librtmp.lib发愁这篇文章应该能帮你省下不少时间。我会从源码准备、依赖处理、编译命令到常见报错把整个流程完整走一遍顺便说清楚每一步为什么这么做。1. 项目背景与版本选型1.1 为什么要在Windows下自己编译librtmp先聊点背景。librtmp是rtmpdump项目里的核心库负责RTMP协议的握手、AMF编码、分块传输这些底层逻辑。很多开源播放器、推流软件虽然内部实现了RTMP但如果你想自己写一个采集推流工具或者要在现有C项目里集成RTMP直播功能librtmp依然是最省事的方案。问题是这个项目对Windows的支持一直很“随缘”。官方代码仓库里只有Linux下的Makefile和configure脚本没有提供Visual Studio的解决方案文件。你打开源码看一眼就能发现里面的网络层是直接用socket写的依赖POSIX风格的接口Windows下虽然也有一套Winsock但是初始化方式、类型定义都有差异直接拿源码丢进VS2017里编报错会多到让你怀疑人生。所以网上才会出现各种“编译好的librtmp.lib”下载但这类文件往往存在两个问题一是不知道他用哪个版本的VS编的二是不知道依赖的OpenSSL版本和你项目里用的是否一致。这两个问题会导致链接时出现一堆莫名其妙的LNK2019排查起来的痛苦程度不亚于自己编译一次。结论就是有能力的项目还是建议自己把源码编译一遍用起来心里有底。1.2 版本怎么选VS2017、OpenSSL、zlib的三方匹配这个zip包选择VS2017作为编译环境是有讲究的。VS2015、VS2017、VS2019的工具集虽然都可以编译C代码但生成的lib文件在不同版本之间混用有时候会提示“_MSC_VER不匹配”之类的错误。在直播推流这个场景下很多人用的是老项目停留在VS2015或者VS2017上所以用VS2017编译出来的库兼容性覆盖是最广的既能给VS2017的项目用也能给VS2015的项目用向上兼容到VS2019、VS2022问题也不大。然后是依赖版本的问题。librtmp的完整编译需要OpenSSL和zlib其中OpenSSL用于RTMPE加密和SWF验证zlib用于SWF文件的解压校验。OpenSSL的版本选择很关键1.0.x系列生成的库文件名是libeay32.lib和ssleay32.lib1.1.x及以后改成了libcrypto.lib和libssl.lib这不只是改个名字那么简单函数接口也有调整。这个zip包里做的是直接提供引用库的方式也就是把OpenSSL和zlib的include目录和lib文件提前放好编译librtmp的时候直接引用省掉了自己编译这两大依赖的步骤这也是标题里“可直接编译”的底气所在。这里我强烈建议如果你要自己动手从零编译整套依赖OpenSSL选1.0.2的最后一个版本和1.1.1的长期支持版都可以但一定要保证librtmp源码和你手里的OpenSSL头文件能对得上。怎么验证打开OpenSSL的include/openssl/opensslv.h文件看OPENSSL_VERSION_NUMBER这个宏如果是0x10101000L左右说明是1.1.x系列链接时就用libssl.lib和libcrypto.lib如果是0x10002000L那就是1.0.2系列对应libeay32.lib和ssleay32.lib。2. 工程结构与环境准备2.1 这个zip里到底有什么拿到这个zip包之后先不要急着解压编译建议花两分钟把目录结构看清楚。一般来说打包的人会把目录整理成这几个部分src目录librtmp的完整源码包括rtmp.c、amf.c、hashswf.c、log.c、parseurl.c这几个核心的C文件以及对应的头文件rtmp.h、amf.h、log.h等。include目录librtmp对外暴露的头文件这个目录是要拷贝到你的项目里的编译时会用到。lib目录或者叫libs、thirdparty目录OpenSSL和zlib的引用库文件通常还会带上它们各自的头文件子目录。在动手之前我习惯先确认两件事。第一lib目录下到底是哪些库文件确认OpenSSL是1.0.x还是1.1.x确定链接时该写哪几个lib名字第二看下有没有现成的README或者编译说明txt文件很多打包者会在里面写清楚自己用的编译命令、宏定义甚至有现成的编译脚本这些信息比你重新摸索快得多。还要提醒一点注意lib目录下的文件名有没有带d后缀比如librtmpd.lib和librtmp.lib的区别前者通常是Debug版后者是Release版。如果你在项目里不小心把Debug版的lib链接进了Release配置编译时能过但运行时大概率会出现“未定义行为”或者崩溃这种坑隐蔽性极强。2.2 检查依赖库的位数与版本接下来是关键一步确认引用库的位数。用VS2017编译的时候x86和x64完全是两套环境lib文件不能混用。你在命令行编译也好在VS的工程属性里配置也好必须先确定目标平台是Win32还是x64然后到lib目录里看对应的子目录。这个zip包如果做得好一般会区分x86和x64两个文件夹。如果没有区分你可以用一个小技巧来判断用一个能查看COFF文件头的工具或者直接在VS2017的开发者命令行里执行dumpbin /headers openssl的lib文件输出信息里会明确标出机器类型比如14C机器代表x6414C的上一行如果显示“machine (x64)”就是64位库显示“machine (x86)”就是32位。如果你懒得敲命令还有个笨办法打开lib文件看一眼文件大小一般64位库会比32位库大一些但这个方法不准不推荐。确定完位数之后再看一下Debug和Release。Debug版的OpenSSL库通常会用MDd或者MTd运行时Release版则是MD或者MT。librtmp编译出来之后最终要链接到你自己的项目里运行时库的选择必须和你的项目保持一致这一块放到后面详细说但是检查依赖这步无论怎么强调都不过分。3. 一步步把librtmp编译成静态库3.1 方案A用VS2017命令行直接编译如果你喜欢命令行操作用VS2017自带的开发者命令提示符是最快的。打开“开始菜单”找到“VS2017”文件夹里面有个“x64 Native Tools Command Prompt for VS2017”64位或者“x86 Native Tools Command Prompt for VS2017”32位根据自己的目标平台选一个。启动之后先cd到librtmp源码的src目录然后执行下面这组命令cl /nologo /c /O2 /MT /DWIN32 /D_CRT_SECURE_NO_WARNINGS /D_USE_OPENSSL /I. /I..\include /I..\lib\openssl\include rtmp.c amf.c hashswf.c log.c parseurl.c如果编译顺利你会看到生成了rtmp.obj、amf.obj、hashswf.obj、log.obj、parseurl.obj这几个目标文件。这里逐个解释一下我用的编译参数/O2是速度优化Release编译的标准配置。/MT是静态链接运行时库这个很关键如果你要在一个不依赖VC运行时的环境里部署程序就必须用/MT但前提是后面链接的OpenSSL库也得是/MT编的。/DWIN32是定义Windows平台的宏。/D_CRT_SECURE_NO_WARNINGS是屏蔽掉_CRT_SECURE_NO_WARNINGS相关的C4996告警因为librtmp源码里大量使用了strcpy、sprintf这类函数在VS2017下会报安全告警。/D_USE_OPENSSL是告诉编译器启用OpenSSL加密支持。然后执行lib命令把这几个obj打包成静态库lib /nologo /OUT:..\lib\librtmp.lib rtmp.obj amf.obj hashswf.obj log.obj parseurl.obj这样一个librtmp.lib就新鲜出炉了。注意第5个源文件parseurl.c它是librtmp中解析URL地址的功能模块。如果你不需要SWF验证相关的功能也可以不编译hashswf.c但为了完整性我还是保留它因为去掉之后如果代码里调用了相关接口链接阶段会报错。3.2 方案B用VS2017静态库工程编译命令行的方式虽然直接但很多习惯了IDE的人还是更倾向于用工程方式管理尤其是项目需要长期维护后续可能要改librtmp源码做定制这种情况下建一个VS工程是最合理的。创建工程的方法很简单VS2017里新建一个“静态库”项目然后把src目录下的所有.c文件添加进“源文件”文件夹把include目录下的.h文件添加进“头文件”文件夹。接下来要改几个地方第一“项目属性 - C/C - 常规 - 附加包含目录”把librtmp的include目录、OpenSSL的include目录、zlib的include目录都加进去第二“项目属性 - C/C - 预处理器 - 预处理器定义”加上WIN32和_CRT_SECURE_NO_WARNINGS这两个必须加否则编译会报错第三“项目属性 - C/C - 代码生成 - 运行库”这里选“多线程(/MT)”还是“多线程调试(/MTd)”要和你的使用场景匹配后面细说。如果你不想改这些配置还有一个更取巧的办法在VS2017里建一个空白控制台应用程序把.c文件拖进去然后按F7编译VS会自动帮你调用C编译器并按C语言规则编译。编译完成后到输出目录找.obj文件再用lib命令把它们打包成.lib。我个人的习惯是方案A和方案B混着来源码没改的情况下用命令行快编需要调试定位问题的时候用工程方式。两种方式生成的lib在功能上没有区别。3.3 编译期间必须处理的宏与编译选项这个部分值得单独拿出来说因为很多报错其实不是源码问题而是编译选项没配对。先说宏定义。librtmp源码里有几个条件编译的开关最重要的是USE_OPENSSL。如果你在编译时不加这个宏定义rtmp.c里和加密握手相关的代码会被预处理掉编译出来的库虽然也能跑但是不支持RTMPE这类加密流某些直播平台可能拉不下来流。所以在VS工程里一定要确认预处理定义那里有USE_OPENSSL除非你确定自己不需要加密功能。还有一个容易踩的坑是_WINDOWS宏。有些人习惯在VS项目里加上_WINDOWS宏但librtmp源码里并没有按照_WINDOWS来区分配置它用的是_WIN32。如果你在64位平台编译记得VS默认会定义_WIN64而_WIN32在64位编译时其实也会被定义虽然名字带32但这是微软的历史遗留习惯不用纠结直接用就行。编译选项里另一个重点是语言标准。librtmp是纯C代码VS2017默认会把.c文件按C89标准来编译但源码里又有一些C99风格的写法比如在for循环里声明变量。这种情况下你需要在工程属性里把“C/C - 语言 - C语言标准”改成“ISO C99”或者“默认”否则编译会报C2143之类的语法错误。有一种更省事的做法是把.c文件用/TP参数按C编译但我不推荐因为librtmp源码里有些结构体的类型转换在C编译器下会报错到时候你会被各种重载和类型安全检查折磨得很惨。4. 链接集成与运行时常见问题4.1 链接时常见错误速查与解决编译只是第一步真正接入项目的时候才是问题集中爆发的地带。下面是我见过最多的几个链接错误整理成表方便排查。报错信息大概率原因解决办法LNK2019无法解析的外部符号__imp_SSL_CTX_new没有链接OpenSSL的库或者OpenSSL版本不对Release下链接libssl.lib和libcrypto.lib同时检查库路径是否配置正确LNK2019无法解析的外部符号__imp_inflate没有链接zlib库在附加依赖项里加上zlib的lib文件注意是zlib.lib还是libzlib.lib取决于包内命名LNK2005 _sprintf已经定义运行时库冲突检查是否混用了/MT和/MD统一所有依赖库和主项目的运行时库LNK4098默认库“LIBCMT”与其他库的使用冲突同上/MT与/MD混用把项目里所有模块的运行时库改成一致建议全部用/MTLNK2038检测到“RuntimeLibrary”的不匹配项一部分代码用/MT一部分用/MD在“代码生成 - 运行库”里统一设置其中最常见也最隐蔽的是最后一个RuntimeLibrary不匹配。这个问题的外在表现是链接时提示“检测到RuntimeLibrary的不匹配项”但有时候这个告警会被VS当成warning而不是error项目照样能生成exe等到运行时在极早期的初始化阶段就崩掉排查起来非常费劲。为了避免这个问题我建议在接入librtmp之前先明确一件事你的最终exe打算怎么部署。如果是常规的Windows应用事先装好了VC运行库那所有模块都用/MD动态CRT最省心如果是一个绿色免安装的软件希望复制到任何机器都能跑那所有模块都用/MT静态CRT。统一之后这类问题基本不会再出现。4.2 C4996、C4819这些告警要怎么办编译librtmp的时候第一次看到C4996会吓一跳。因为源码里大量的strcpy、sprintf、strcat都是老代码了VS2017对这些“不安全”的C标准库函数默认会给出编译告警有时候甚至会直接当成错误。我做过测试librtmp源码编译时C4996告警少说也有几十条每一行都是“this function or variable may be unsafe”。解决办法不是什么函数都改成strcpy_s那样改动量太大而且容易改出错。最干净的方式是加_CRT_SECURE_NO_WARNINGS这个预处理器定义一行代码解决所有问题。这也是我在前面反复提到这个宏的原因。还有一个C4819告警通常出现在中文Windows环境下提示文件包含无法在当前代码页表示的字符。这个实际上是源码文件的编码问题librtmp源码里有些注释是UTF-8编码但系统默认代码页是936GBKVS就报警了。处理办法也很简单在工程属性里把“C/C - 命令行 - 其他选项”加上/utf-8让编译器按UTF-8解析源码文件。如果你不嫌麻烦也可以把源码文件另存为带BOM的UTF-8格式效果一样。4.3 运行时库MT/MD混乱带来的隐蔽问题这一节我要展开讲一下MT和MD的问题因为它是librtmp集成过程中最容易出问题的地方而且出的问题通常还不是链接报错而是运行时的内存错误。先解释一下区别。MT表示静态链接运行时库生成的exe里包含CRTC运行时库的代码不依赖系统的MSVCRT.dll或者VCRUNTIME.dllMD表示动态链接运行时库程序启动的时候需要系统里装有对应的VC Redistributable。这两种模式在内存管理模型上是不同的/MT模式下malloc在CRT内部维护的堆上分配/MD模式下malloc调用的是系统堆上的API。如果librtmp.lib是用/MT编译的而你的主程序是/MD编译的那么在librtmp内部malloc的内存在主程序里free或者反过来就有可能导致堆损坏。这类问题在Debug版里偶尔会弹出一个“检测到堆损坏”的assert在Release版里则是随机的崩溃或者内存越界非常难查。我见过最离谱的一个案例程序大概每运行30秒左右就会随机崩溃一次加了各种日志都找不到规律后来用了Application Verifier一测才发现是librtmp的malloc/free跨模块了。所以如果你正在烦恼某个“无缘无故的内存错误”先检查一下所有依赖库和主工程的运行时库是否完全一致。这里说的“一致”不只是Debug和Release还包括静态/动态。5. 用一段测试代码验证库存是否能用5.1 测试代码与项目配置库编译完之后我建议不要直接丢到大型项目里先写一个最小验证程序确保库本身没问题。这样可以隔离问题源避免后面出问题时分不清到底是你自己的代码bug还是librtmp的问题。新建一个控制台项目包含目录指向librtmp的include库目录指向lib附加依赖项加上librtmp.lib、libssl.lib、libcrypto.lib、zlib.lib以及ws2_32.lib。然后粘贴下面这段代码#include stdio.h #include winsock2.h #include rtmp.h #pragma comment(lib, ws2_32.lib) int main() { WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), wsaData); RTMP *rtmp RTMP_Alloc(); if (!rtmp) { printf(RTMP_Alloc failed\n); return -1; } RTMP_Init(rtmp); if (RTMP_SetupURL(rtmp, rtmp://127.0.0.1:1935/live/test)) { RTMP_EnableWrite(rtmp); printf(SetupURL ok, starting connect...\n); if (RTMP_Connect(rtmp, NULL)) { printf(RTMP Connect ok\n); } else { printf(RTMP Connect failed\n); } } else { printf(RTMP_SetupURL failed\n); } RTMP_Free(rtmp); WSACleanup(); return 0; }这段代码的作用是先初始化Winsock然后创建RTMP对象解析URL并尝试建立连接。这里连的肯定是本地地址所以RTMP_Connect大概率会失败但只要走到“RTMP Connect failed”这一步就说明库的链接、初始化、URL解析这些核心功能都是正常的。如果连RTMP_SetupURL都失败了说明库在解析URL的parseurl模块已经出了问题。5.2 实测结论与使用体会我在VS2017下用这套流程编译过多个版本的librtmp整体体验是只要前置的依赖库路径和运行时库设置没问题编译本身大概就是一两分钟的事真正的坑不在编译器而在链接和部署。个人体会比较深的一点是librtmp这个库虽然老但稳定性和兼容性都相当靠谱在Windows下用VS2017编出来的静态库不管以后是接到Qt项目里做推流还是写一个纯Win32的工具单独使用都能正常干活。比起费劲去找网上各种来路不明的预编译版本自己编译一次后面省心很多。最后再分享一个使用途中的小技巧如果你要同时给多个不同配置的项目比如x86/x64、Debug/Release提供librtmp可以在编译的时候给lib文件区分好名字比如librtmp_mt.lib、librtmp_mtd.lib、librtmp_md.lib、librtmp_mdd.lib这样在CMake或者VS工程里配置起来一目了然不会出现拿错文件这种低级又耗时的失误。本文还有配套的精品资源点击获取
返回列表