庫(kù):zlib 與 OpenSSL 依賴配置實(shí)戰(zhàn))
簡(jiǎn)介本資源面向需要在 Windows 10 與 VS2017 環(huán)境下使用 RTMP 協(xié)議的 C/C 開(kāi)發(fā)者提供可直接編譯的 librtmp.lib 靜態(tài)庫(kù)工程解決自行編譯時(shí)依賴庫(kù)難找、配置繁瑣的問(wèn)題。壓縮包共 238 個(gè)文件約 49.12MB以 163 個(gè) h 頭文件、28 個(gè) dll 動(dòng)態(tài)庫(kù)、8 個(gè) lib 庫(kù)文件及 7 個(gè) c 源文件為主另含 sln 解決方案、vcxproj 工程與編譯日志等目錄按 lib、librtmp、openssl-1.0.1c、zlib-1.2.8、vs2017 分層組織依賴關(guān)系一目了然。已有 637 人學(xué)習(xí)下載。讀者可直接打開(kāi) librtmp.sln 編譯出 librtmp.lib省去 OpenSSL 與 zlib 的交叉編譯環(huán)節(jié)同時(shí)獲得完整源碼與引用庫(kù)便于二次開(kāi)發(fā)、調(diào)試與集成到推流播放項(xiàng)目中是 RTMP 客戶端開(kāi)發(fā)與流媒體學(xué)習(xí)的實(shí)用參考。1. 為什么 2024 年還在折騰 librtmp.libVS2017 編譯這件事的真實(shí)門(mén)檻如果你手頭有一個(gè)老項(xiàng)目需要在 Windows 上做 RTMP 推流或者維護(hù)一套安防、直播、錄播類的 C 客戶端那你大概率繞不開(kāi) librtmp。它不是什么新東西但勝在輕、穩(wěn)、依賴少很多工業(yè)現(xiàn)場(chǎng)的設(shè)備端程序至今還在用它。問(wèn)題在于網(wǎng)上能搜到的 librtmp 編譯教程十篇里有八篇是 MinGW 或者 Linux 下的剩下兩篇 VS 的還停留在 VS2010 時(shí)代照著做不是缺頭文件就是鏈接報(bào)錯(cuò)。這個(gè)標(biāo)題真正要解決的事情很具體在 Visual Studio 2017 環(huán)境下把 librtmp 編譯成.lib靜態(tài)庫(kù)同時(shí)把 zlib 和 OpenSSL 這兩個(gè)引用庫(kù)一起配好最后能直接在自己的工程里#include加鏈接就能用。它適合兩類人一類是接手了老代碼、必須用 VS2017 工具鏈的維護(hù)型工程師另一類是想在 Windows 上做 RTMP 推流、又不想引入 FFmpeg 這種大塊頭的開(kāi)發(fā)者。我自己的血淚經(jīng)驗(yàn)是librtmp 的編譯難點(diǎn)從來(lái)不在 librtmp 本身而在它的兩個(gè)依賴zlib 和 OpenSSL。VS2017 的 MSVC 工具鏈對(duì) C99 的支持、對(duì)ssize_t這類 POSIX 類型的處理都會(huì)讓直接拿 Linux 源碼來(lái)編的人翻車。所以這篇東西的重點(diǎn)是把「引用庫(kù) 源代碼 編譯配置」這條鏈路講透而不是只丟一個(gè).sln給你。2. 編譯前必須搞清楚的依賴關(guān)系與目錄規(guī)劃2.1 librtmp 到底依賴了什么為什么不能只編它一個(gè)librtmp 的源碼結(jié)構(gòu)其實(shí)很清晰核心就是rtmp.c、log.c、parseurl.c、hashswf.c這幾個(gè)文件。但它默認(rèn)會(huì)用到兩個(gè)外部能力一個(gè)是壓縮一個(gè)是加密。壓縮對(duì)應(yīng) zlib用來(lái)處理 RTMP 握手階段的 HMAC 和部分?jǐn)?shù)據(jù)壓縮加密對(duì)應(yīng) OpenSSL用來(lái)做 RTMPE 的握手和 DH 密鑰交換。如果你只做普通 RTMP 推流理論上可以關(guān)掉這兩個(gè)依賴但實(shí)際項(xiàng)目里幾乎沒(méi)人這么干因?yàn)橐坏┓?wù)端要求 RTMPE 或者你后續(xù)要加鑒權(quán)沒(méi)有 OpenSSL 就得推倒重來(lái)。所以我的建議是一步到位把 zlib 和 OpenSSL 都編成靜態(tài)庫(kù)再讓 librtmp 去鏈接它們。這里有個(gè)選型上的坑OpenSSL 版本不要選太新的。VS2017 的 MSVC 版本是 19.1xOpenSSL 1.1.1 系列是最后一個(gè)對(duì)老工具鏈友好的長(zhǎng)期支持版本3.x 系列在 VS2017 下編譯 Perl 配置腳本容易出問(wèn)題。我一般會(huì)鎖定 OpenSSL 1.1.1w 這個(gè)版本zlib 用 1.2.13這兩個(gè)組合在 VS2017 下驗(yàn)證過(guò)很多次穩(wěn)定。2.2 目錄結(jié)構(gòu)怎么擺決定了你后面少踩多少坑很多人編譯失敗不是代碼問(wèn)題是目錄太亂導(dǎo)致 include 路徑和 lib 路徑對(duì)不上。我習(xí)慣在磁盤(pán)上建一個(gè)干凈的根目錄比如D:\rtmp_build下面按依賴層級(jí)分開(kāi)放D:\rtmp_build\ ├── zlib-1.2.13\ # zlib 源碼 │ ├── build_vs2017\ # zlib 的 VS 工程輸出 ├── openssl-1.1.1w\ # OpenSSL 源碼 │ ├── build_vs2017\ # OpenSSL 編譯輸出 ├── librtmp\ # librtmp 源碼 │ ├── src\ # rtmp.c 等源文件 │ ├── include\ # rtmp.h 等頭文件 │ └── build_vs2017\ # librtmp 的 VS 工程 └── output\ # 最終統(tǒng)一存放 .lib 和 .h ├── include\ └── lib\這樣擺的好處是后面在 VS2017 里配附加包含目錄和附加庫(kù)目錄時(shí)路徑是固定的不會(huì)因?yàn)槟硞€(gè)庫(kù)換了位置就全盤(pán)重來(lái)。output目錄是最終交付物別人拿到這個(gè)目錄就能直接集成不用再關(guān)心你中間怎么編的。提示路徑里不要帶中文和空格VS2017 的 MSBuild 在某些情況下對(duì)空格路徑處理有問(wèn)題尤其是自定義生成步驟里調(diào)用 Perl 或 NASM 的時(shí)候。3. 用 VS2017 編譯 zlib 和 OpenSSL 引用庫(kù)的完整命令3.1 zlib 1.2.13 在 VS2017 下的編譯步驟zlib 官方源碼包里自帶了一個(gè)win32目錄里面有Makefile.msc但那個(gè)是給 nmake 用的在 VS2017 下直接跑容易因?yàn)?SDK 版本問(wèn)題報(bào)錯(cuò)。更穩(wěn)的做法是用 CMake 生成 VS2017 工程然后命令行編譯。先確認(rèn)你裝了 CMake并且cmake --version能看到 3.10 以上。然后在D:\rtmp_build\zlib-1.2.13下打開(kāi) VS2017 的 x64 本機(jī)工具命令提示符執(zhí)行mkdir build_vs2017 cd build_vs2017 cmake -G Visual Studio 15 2017 Win64 -DCMAKE_INSTALL_PREFIXD:/rtmp_build/output .. cmake --build . --config Release --target INSTALL這三條命令的邏輯是第一條用 VS2017 的 64 位生成器創(chuàng)建工程同時(shí)指定安裝前綴到output目錄第二條執(zhí)行 Release 編譯并把頭文件和zlib.lib安裝到output。編譯完成后你會(huì)在D:\rtmp_build\output\include下看到zlib.h和zconf.h在D:\rtmp_build\output\lib下看到zlib.lib。參數(shù)上唯一需要留意的是CMAKE_INSTALL_PREFIX它決定了后面 librtmp 去哪里找 zlib。如果你只想編 32 位把Win64去掉生成器換成Visual Studio 15 2017就行但要注意后面所有庫(kù)的位數(shù)必須一致混用 32 位和 64 位是鏈接報(bào)錯(cuò)的重災(zāi)區(qū)。3.2 OpenSSL 1.1.1w 的 Perl 配置與 nmake 編譯OpenSSL 在 Windows 下的編譯依賴 Perl 和 NASM。Perl 我推薦用 Strawberry Perl裝完之后perl -v能輸出版本號(hào)NASM 裝完后要把它的目錄加到系統(tǒng) PATH否則 OpenSSL 的匯編優(yōu)化模塊編不過(guò)。進(jìn)入D:\rtmp_build\openssl-1.1.1w用 VS2017 x64 本機(jī)工具命令提示符執(zhí)行perl Configure VC-WIN64A no-shared no-tests --prefixD:\rtmp_build\output --openssldirD:\rtmp_build\output\ssl nmake nmake installVC-WIN64A是告訴 OpenSSL 用 VS 的 64 位匯編器no-shared是關(guān)鍵它讓 OpenSSL 只生成靜態(tài)庫(kù)libssl.lib和libcrypto.lib不生成 DLL這樣后面 librtmp 鏈接進(jìn)去就是純靜態(tài)的部署時(shí)不用帶一堆 dllno-tests是跳過(guò)測(cè)試程序編譯能省不少時(shí)間。--prefix和--openssldir都指向output這樣nmake install之后頭文件會(huì)進(jìn)output\include庫(kù)文件會(huì)進(jìn)output\lib。如果你在這一步遇到nasm not found檢查 PATH如果遇到cl.exe not found說(shuō)明你沒(méi)在 VS2017 的命令行環(huán)境里執(zhí)行重新從開(kāi)始菜單打開(kāi)「x64 本機(jī)工具命令提示符」。3.3 驗(yàn)證引用庫(kù)是否可用一個(gè)最小鏈接測(cè)試在正式編 librtmp 之前我習(xí)慣先寫(xiě)一個(gè)幾行的測(cè)試程序確認(rèn) zlib 和 OpenSSL 的靜態(tài)庫(kù)能被 VS2017 正常鏈接。新建一個(gè)空的控制臺(tái)工程把output\include加到附加包含目錄output\lib加到附加庫(kù)目錄然后在附加依賴項(xiàng)里填zlib.lib;libssl.lib;libcrypto.lib;ws2_32.lib;crypt32.lib;。#include zlib.h #include openssl/ssl.h #include iostream int main() { std::cout zlib version: zlibVersion() std::endl; std::cout openssl version: OpenSSL_version(OPENSSL_VERSION) std::endl; return 0; }這段代碼能編過(guò)并且運(yùn)行輸出兩個(gè)版本號(hào)說(shuō)明引用庫(kù)的路徑、位數(shù)、運(yùn)行時(shí)庫(kù)配置都對(duì)上了。如果報(bào)LNK2019未解析的外部符號(hào)九成是位數(shù)不匹配或者附加依賴項(xiàng)漏了ws2_32.lib。這一步看起來(lái)多余但它能幫你把引用庫(kù)的問(wèn)題和 librtmp 的問(wèn)題隔離開(kāi)后面排錯(cuò)會(huì)輕松很多。4. 把 librtmp 源碼編成 librtmp.lib 的工程配置4.1 源碼文件清單與 VS2017 工程創(chuàng)建librtmp 的源碼通常散落在src和include兩個(gè)目錄里。你需要加入編譯的文件其實(shí)不多核心是這幾個(gè)文件作用是否必須rtmp.cRTMP 協(xié)議主實(shí)現(xiàn)必須log.c日志輸出必須parseurl.cURL 解析必須hashswf.cSWF 校驗(yàn)哈希推流到某些服務(wù)端時(shí)必須handshake.c握手?jǐn)U展視服務(wù)端要求在 VS2017 里新建一個(gè)「靜態(tài)庫(kù)」工程把上述.c文件添加進(jìn)去頭文件目錄指向 librtmp 的include。然后右鍵工程屬性把配置類型確認(rèn)為「靜態(tài)庫(kù) (.lib)」目標(biāo)文件名改成librtmp。這里有個(gè)細(xì)節(jié)librtmp 的源碼里會(huì)#include zlib.h和#include openssl/ssl.h所以附加包含目錄里必須同時(shí)有output\include和 librtmp 自己的include。順序上把output\include放在前面避免源碼目錄里有同名頭文件造成沖突。4.2 預(yù)處理宏與運(yùn)行時(shí)庫(kù)的關(guān)鍵設(shè)置librtmp 在 Windows 下編譯必須定義幾個(gè)宏否則會(huì)報(bào)一堆類型未定義或者函數(shù)找不到。在「C/C → 預(yù)處理器 → 預(yù)處理器定義」里加上_CRT_SECURE_NO_WARNINGS WIN32 _WINDOWS NO_CRYPTO等一下NO_CRYPTO這個(gè)宏要特別說(shuō)明。如果你確定不需要 RTMPE 加密加上它可以少鏈接 OpenSSL編譯會(huì)簡(jiǎn)單很多。但如果你后面要用 RTMPE這個(gè)宏絕對(duì)不能加否則rtmp.c里跟加密相關(guān)的代碼會(huì)被條件編譯掉運(yùn)行時(shí)握手直接失敗。我一般是不加NO_CRYPTO老老實(shí)實(shí)把 OpenSSL 鏈進(jìn)去避免以后返工。運(yùn)行時(shí)庫(kù)的設(shè)置也容易翻車。VS2017 默認(rèn)新建靜態(tài)庫(kù)工程用的是「多線程 DLL (/MD)」但如果你希望最終交付一個(gè)不依賴 VC 運(yùn)行時(shí)的獨(dú)立庫(kù)應(yīng)該改成「多線程 (/MT)」。注意這個(gè)設(shè)置必須和 zlib、OpenSSL 的運(yùn)行時(shí)庫(kù)設(shè)置一致。前面用 CMake 編 zlib 時(shí)默認(rèn)也是/MD如果你要改/MTzlib 和 OpenSSL 都得重新編一遍否則鏈接時(shí)會(huì)報(bào)RuntimeLibrary不匹配的 LNK2038 錯(cuò)誤。4.3 鏈接引用庫(kù)并生成 librtmp.lib在靜態(tài)庫(kù)工程的「鏈接器 → 輸入 → 附加依賴項(xiàng)」里填入zlib.lib libssl.lib libcrypto.lib ws2_32.lib crypt32.lib同時(shí)在「鏈接器 → 常規(guī) → 附加庫(kù)目錄」里加上D:\rtmp_build\output\lib。然后直接生成解決方案如果前面 zlib 和 OpenSSL 都編對(duì)了這一步應(yīng)該能順利產(chǎn)出librtmp.lib。生成之后我建議做一個(gè)「安裝」動(dòng)作把librtmp.lib復(fù)制到output\lib把rtmp.h、log.h等頭文件復(fù)制到output\include。這樣output目錄就是一個(gè)完整的 SDK別人拿到之后只需要配一個(gè)包含目錄和一個(gè)庫(kù)目錄就能用。注意如果你在鏈接階段看到unresolved external symbol __imp_SSL_CTX_new這類帶__imp_前綴的符號(hào)說(shuō)明你鏈接的是 OpenSSL 的導(dǎo)入庫(kù)而不是靜態(tài)庫(kù)。檢查output\lib下是不是混進(jìn)了libssl.lib的 DLL 版本或者no-shared沒(méi)生效。5. 編譯 librtmp 時(shí)最容易翻車的幾個(gè)地方5.1 現(xiàn)象報(bào)ssize_t未定義一堆紅字原因ssize_t是 POSIX 類型MSVC 默認(rèn)不提供。librtmp 源碼里有些地方直接用了它在 Linux 下沒(méi)問(wèn)題在 VS 下就炸。解決在rtmp.h或者工程的預(yù)處理器里加一個(gè)類型定義。最穩(wěn)妥的做法是在包含 librtmp 頭文件之前自己補(bǔ)一個(gè)#ifdef _MSC_VER #include basetsd.h typedef SSIZE_T ssize_t; #endif或者直接在 VS 工程里加預(yù)處理器宏_SSIZE_T_DEFINED然后確保basetsd.h被包含。我一般選前者因?yàn)楦墓こ毯暧袝r(shí)候會(huì)被其他頭文件的包含順序干擾。5.2 現(xiàn)象鏈接時(shí)報(bào)LNK2038說(shuō) RuntimeLibrary 不匹配原因zlib、OpenSSL、librtmp 三個(gè)工程的運(yùn)行時(shí)庫(kù)設(shè)置不一致。有的是/MD有的是/MT鏈接器檢測(cè)到?jīng)_突就拒絕生成。解決統(tǒng)一改成/MT或者統(tǒng)一/MD。如果你要交付獨(dú)立庫(kù)選/MT如果你只是自己項(xiàng)目?jī)?nèi)部用/MD也行但部署時(shí)要帶 VC 運(yùn)行時(shí)。改的時(shí)候三個(gè)庫(kù)都要重新編譯不能只改一個(gè)。5.3 現(xiàn)象編譯通過(guò)但運(yùn)行時(shí)RTMP_Connect返回失敗日志顯示握手錯(cuò)誤原因大概率是 OpenSSL 的加密部分沒(méi)鏈進(jìn)去或者NO_CRYPTO宏被誤加了。也有可能是 OpenSSL 版本和 librtmp 源碼不兼容比如用了 OpenSSL 3.x 的 API而 librtmp 里還是 1.1.1 的調(diào)用方式。解決先確認(rèn)NO_CRYPTO沒(méi)定義再確認(rèn)鏈接的是libssl.lib和libcrypto.lib而不是 DLL 導(dǎo)入庫(kù)。如果還不行抓包看握手階段服務(wù)端返回的具體錯(cuò)誤碼RTMPE 握手失敗通常是 DH 參數(shù)不匹配換 OpenSSL 1.1.1w 基本能解決。5.4 現(xiàn)象32 位工程鏈接 64 位庫(kù)報(bào)LNK1112模塊計(jì)算機(jī)類型沖突原因zlib 或 OpenSSL 編的是 64 位但 librtmp 工程建的是 32 位或者反過(guò)來(lái)。解決統(tǒng)一位數(shù)。檢查方法是在 VS 里看工程屬性「平臺(tái)」是 Win32 還是 x64然后確認(rèn)output\lib下的.lib是用哪個(gè)位數(shù)編的。可以用dumpbin /headers zlib.lib | findstr machine來(lái)看x64 會(huì)顯示machine (x64)x86 顯示machine (x86)。5.5 現(xiàn)象nmake install之后output\include里沒(méi)有openssl目錄原因OpenSSL 的--openssldir和--prefix設(shè)置不對(duì)或者nmake install中途報(bào)錯(cuò)但被忽略了。解決重新執(zhí)行nmake install并且把輸出完整看一遍。正常情況下output\include\openssl下應(yīng)該有幾十個(gè)頭文件。如果沒(méi)有檢查--prefix路徑是否可寫(xiě)以及 Perl 配置階段有沒(méi)有報(bào) warning。6. 交付一個(gè)可直接集成的 librtmp SDK目錄規(guī)范與驗(yàn)證技巧編完不是終點(diǎn)能讓人直接拿去用才是。我習(xí)慣在output目錄里再補(bǔ)一個(gè)librtmp_sdk文件夾結(jié)構(gòu)如下librtmp_sdk\ ├── include\ │ ├── rtmp.h │ ├── log.h │ ├── zlib.h │ ├── zconf.h │ └── openssl\ # 整個(gè) openssl 頭文件目錄 ├── lib\ │ ├── librtmp.lib │ ├── zlib.lib │ ├── libssl.lib │ └── libcrypto.lib └── README.txt # 寫(xiě)清楚 VS2017 集成時(shí)的附加依賴項(xiàng)README.txt里我一般只寫(xiě)三行關(guān)鍵信息附加包含目錄填include附加庫(kù)目錄填lib附加依賴項(xiàng)填librtmp.lib;zlib.lib;libssl.lib;libcrypto.lib;ws2_32.lib;crypt32.lib;。這樣別人拿到之后五分鐘就能在自己的工程里跑起來(lái)。驗(yàn)證 SDK 是否完整我有個(gè)偷懶但有效的辦法新建一個(gè)空的 VS2017 控制臺(tái)工程只加一個(gè)main.cpp里面調(diào)用RTMP_Init和RTMP_SetupURL然后鏈接這個(gè) SDK。如果編過(guò)并且運(yùn)行不崩說(shuō)明頭文件和庫(kù)文件都是齊的。這個(gè)測(cè)試不需要真的連服務(wù)器只是驗(yàn)證編譯鏈接鏈路。#include rtmp.h #include iostream int main() { RTMP* rtmp RTMP_Alloc(); if (!rtmp) { std::cerr RTMP_Alloc failed std::endl; return -1; } RTMP_Init(rtmp); std::cout librtmp init ok std::endl; RTMP_Free(rtmp); return 0; }這段代碼能編過(guò)并輸出librtmp init ok就說(shuō)明你的librtmp.lib是真正可用的不是那種編出來(lái)但符號(hào)不全的半成品。最后說(shuō)一個(gè)我自己的習(xí)慣每次編完這套東西我會(huì)把output目錄整個(gè)打包文件名帶上日期和 VS 版本比如librtmp_vs2017_x64_202406.zip。下次再遇到需要這個(gè)庫(kù)的項(xiàng)目直接解壓配路徑不用重新走一遍編譯流程。這個(gè)習(xí)慣幫我省過(guò)至少兩次通宵排錯(cuò)的時(shí)間希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取