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