戰(zhàn):老系統(tǒng)視頻處理方案)
簡(jiǎn)介利用 VS2015 在 32 位 Windows 環(huán)境下編譯 FFmpeg 6.0.1 后打包的資源面向需要在 Win32 平臺(tái)做音視頻開(kāi)發(fā)、二次封裝或功能裁剪的技術(shù)人員。下載后可直接獲得可用的 DLL、頭文件和導(dǎo)入庫(kù)經(jīng)過(guò)實(shí)測(cè)能夠正常調(diào)用省去手動(dòng)編譯和依賴(lài)配置的時(shí)間。壓縮包共 222 個(gè)文件大小約 10.89MB包含 139 個(gè)頭文件、24 個(gè) C 源碼文件以及 7 個(gè) DLL 動(dòng)態(tài)庫(kù)、7 個(gè) LIB 導(dǎo)入庫(kù)和 7 個(gè) DEF 導(dǎo)出定義文件同時(shí)附帶 pkg-config 所需的 .pc 文件、FFmpeg 可執(zhí)行程序、預(yù)設(shè)參數(shù)文件和說(shuō)明文檔。文件組織清晰動(dòng)態(tài)庫(kù)可直接用于工程鏈接源碼和頭文件便于查看內(nèi)部實(shí)現(xiàn)或按需裁剪man 手冊(cè)頁(yè)則覆蓋過(guò)濾器、編解碼器、格式、設(shè)備與協(xié)議等模塊適合開(kāi)發(fā)時(shí)查詢(xún)接口和參數(shù)。目前已有 414 人學(xué)習(xí)下載對(duì)于想在 Win32 下快速搭建 FFmpeg 開(kāi)發(fā)環(huán)境、兼顧源碼閱讀和文檔查閱的開(kāi)發(fā)者是一份很實(shí)際的資源。1. 都到6.x了為什么還有人在找32位ffmpeg先說(shuō)結(jié)論ffmpeg 6.0.1的32位版本沒(méi)有過(guò)時(shí)它只是服務(wù)的人群不再發(fā)聲了。我自己是在給一臺(tái)老工控機(jī)做視頻采集方案時(shí)被迫回頭折騰32位編譯的后來(lái)發(fā)現(xiàn)這個(gè)需求遠(yuǎn)比想象中普遍只是平時(shí)大家不太會(huì)主動(dòng)聊。1.1 存量老機(jī)器的現(xiàn)實(shí)約束Windows XP、Win7 32位、老款嵌入式主板、工控機(jī)、POS機(jī)、車(chē)載中控這些設(shè)備到今天依然在大量運(yùn)行。它們的CPU架構(gòu)早就被官方和各大軟件廠商放棄但生產(chǎn)環(huán)境里不可能說(shuō)換就換。如果你需要在這些設(shè)備上做視頻推流、RTSP拉流、截圖分析ffmpeg 6.0.1的32位版本就是少數(shù)還能拿到新編碼器和新協(xié)議修復(fù)的選擇。很多人會(huì)問(wèn)為什么不直接用老版本ffmpeg 3.x因?yàn)槔习姹緦?duì)H.265、AV1、HEVC的硬解支持、對(duì)某些RTSP廠商私有協(xié)議的處理、對(duì)m3u8加密流的兼容性都有明顯短板。視頻編碼領(lǐng)域這十幾年變化太快哪怕是2023年發(fā)布的6.0.1在某些新設(shè)備推流場(chǎng)景下也都才剛追平。所以與其找一個(gè)十年前的老版本將就不如自己在新的源碼樹(shù)上編一個(gè)32位版本出來(lái)。1.2 集成到32位宿主程序的無(wú)奈另一個(gè)很典型的需求是把自己寫(xiě)的程序或第三方SDK與ffmpeg做靜態(tài)集成。很多商業(yè)SDK、老項(xiàng)目、銀行柜臺(tái)程序、醫(yī)療影像軟件底層還是32位的PE文件或者32位ELF。你的宿主進(jìn)程是32位的就沒(méi)辦法直接LoadLibrary一個(gè)64位的ffmpeg哪怕你的操作系統(tǒng)是64位的Windows 10或Linux服務(wù)器。這種情況下編譯一個(gè)32位版本的ffmpeg動(dòng)態(tài)庫(kù)或靜態(tài)庫(kù)然后把你的模塊一起鏈進(jìn)去是最省事的路線。架構(gòu)不匹配不是靠“換個(gè)路徑”或者“改個(gè)權(quán)限”能繞過(guò)去的必須老老實(shí)實(shí)準(zhǔn)備32位目標(biāo)文件。1.3 測(cè)試矩陣?yán)锏募嫒菪孕枨筮€有一種場(chǎng)景你可能想不到做商業(yè)化軟件分發(fā)QA測(cè)試矩陣?yán)锉仨毟采w32位系統(tǒng)環(huán)境否則測(cè)試報(bào)告就不完整。有些產(chǎn)品的用戶畫(huà)像就是老設(shè)備你的CI/CD流水線里就得有一個(gè)job專(zhuān)門(mén)編32位ffmpeg、跑32位回歸。我自己就碰到過(guò)這種情況編譯環(huán)境全是新的容器和新的工具鏈但客戶現(xiàn)場(chǎng)反饋說(shuō)“在32位系統(tǒng)上跑不起來(lái)”一查果然是ffmpeg編成了64位。從那兒之后我的發(fā)布腳本里永遠(yuǎn)會(huì)保留一個(gè)32位構(gòu)建產(chǎn)物哪怕平時(shí)根本用不到。2. 獲取32位ffmpeg 6.0.1的現(xiàn)成渠道如果不太想自己編譯先看看現(xiàn)成輪子。這里分幾類(lèi)渠道說(shuō)清楚免得你花一晚上踩我踩過(guò)的坑。2.1 官方?jīng)]提供Windows二進(jìn)制別白等FFmpeg官方項(xiàng)目本身是不提供Windows預(yù)編譯二進(jìn)制的官網(wǎng)只給Linux源碼包。所以你在網(wǎng)上搜“ffmpeg-6.0.1 win32下載”能搜到的基本全是第三方構(gòu)建站點(diǎn)或個(gè)人打包。如果你看到某個(gè)名氣不大的下載站掛著“官方32位版”字樣心里要打個(gè)問(wèn)號(hào)官方從來(lái)沒(méi)發(fā)過(guò)Windows二進(jìn)制哪來(lái)的官方版這里不是說(shuō)第三方一定不安全而是提醒你別下載來(lái)歷不明的exe尤其要小心被捆綁了多余的東西。2.2 gyan.dev / BtbN 等第三方構(gòu)建怎么選目前靠譜的第三方構(gòu)建主要是兩個(gè)渠道gyan.dev提供Windows的 release 和 full 兩種版本full版本編碼器更全release版本更精簡(jiǎn)。它的下載頁(yè)里能選32位版本我記得是帶“win32”字樣。BtbNGitHub Actions自動(dòng)構(gòu)建提供ffmpeg-master-latest-win32-gpl.zip這類(lèi)產(chǎn)物。它是持續(xù)集成自動(dòng)編的基本可以認(rèn)為是當(dāng)前源碼的滾動(dòng)構(gòu)建不一定恰好是6.0.1這個(gè)tag但如果你只需要“6.x級(jí)別的功能”完全夠用。如果項(xiàng)目里有嚴(yán)格版本要求比如你們內(nèi)部依賴(lài)某個(gè)特定commit或必須鎖6.0.1的API行為那第三方滾動(dòng)構(gòu)建就幫不上忙了還是得回到源碼自編譯。2.3 拿到文件后立刻做的架構(gòu)驗(yàn)證下載完別急著放進(jìn)生產(chǎn)環(huán)境先驗(yàn)證一下這個(gè)文件到底是32位還是64位。Windows下用命令行dumpbin /headers ffmpeg.exe找輸出里的“machine”字段x86表示32位x64表示64位。如果沒(méi)有dumpbin用Git Bash或MSYS2自帶的file命令也行file ffmpeg.exe # 輸出示例PE32 executable (console) Intel 80386, for MS WindowsPE32且Intel 80386就是32位。如果是PE32那是64位。這個(gè)檢查十秒鐘的事能幫你避免把整個(gè)測(cè)試環(huán)境帶偏。3. Windows環(huán)境親手編譯32位版MSYS2路線實(shí)操如果你必須鎖死6.0.1版本或者需要裁剪模塊那就到了自己編譯這一步。Windows環(huán)境下我最推薦的是MSYS2 MinGW-w64路線不要拿Visual Studio去硬碰ffmpeg那套configure腳本編起來(lái)痛苦得多。3.1 安裝MINGW32環(huán)境時(shí)的兩個(gè)細(xì)節(jié)先裝MSYS2然后打開(kāi)“MSYS2 MINGW32”這個(gè)shell注意名字里帶32不是MSYS2 MSYS也不是MINGW64執(zhí)行pacman -S mingw-w64-i686-toolchain mingw-w64-i686-yasm這里有兩個(gè)細(xì)節(jié)值得注意必須裝mingw-w64-i686前綴的包不要裝mingw-w64-x86_64。前綴決定目標(biāo)架構(gòu)裝錯(cuò)了編出來(lái)的還是64位。ffmpeg的configure在生成匯編優(yōu)化時(shí)依賴(lài)yasm或nasmWindows上更常用yasm。不裝也能編但很多SIMD優(yōu)化會(huì)被跳過(guò)性能差距能達(dá)到20%以上。依賴(lài)庫(kù)libx264、libmp3lame、libvpx等同樣用i686前綴裝比如pacman -S mingw-w64-i686-libx264 mingw-w64-i686-libmp3lame3.2 configure參數(shù)怎么給才算是真正的32位解壓ffmpeg-6.0.1源碼后在MINGW32 shell里執(zhí)行configure我這里給一套經(jīng)過(guò)驗(yàn)證的參數(shù)./configure \ --archx86 \ --target-osmingw32 \ --cross-prefixi686-w64-mingw32- \ --enable-cross-compile \ --disable-doc \ --disable-debug \ --enable-gpl \ --enable-libx264 \ --enable-libmp3lame \ --extra-cflags-m32 \ --extra-ldflags-m32我在MINGW32 shell下實(shí)測(cè)--archx86和--target-osmingw32是定位32位架構(gòu)的關(guān)鍵。--cross-prefix指定i686的交叉編譯前綴這樣configure能找到32位的gcc。--enable-cross-compile是必須打開(kāi)的否則configure會(huì)嘗試在本地跑編譯產(chǎn)物來(lái)探測(cè)運(yùn)行行為而本地shell是32位的、編出來(lái)的程序也是32位的邏輯上沒(méi)問(wèn)題但configure對(duì)它自己“跨平臺(tái)”這件事特別敏感不聲明的話容易報(bào)錯(cuò)。如果你不想要GPL組件把--enable-gpl和--enable-libx264去掉即可靜態(tài)鏈接下許可證問(wèn)題值得注意公司內(nèi)部用無(wú)所謂對(duì)外分發(fā)要慎重。配置完成后直接make -j8i7級(jí)別機(jī)器完整編一遍大概五六分鐘。編完在源碼目錄下找ffmpeg.exe和ffprobe.exe。3.3 編譯完成的驗(yàn)證與打包驗(yàn)證一定不要省file ffmpeg.exe ./ffmpeg.exe -versionfile確認(rèn)是PE32架構(gòu)-version確認(rèn)版本號(hào)是6.0.1再順手轉(zhuǎn)一個(gè)測(cè)試視頻確認(rèn)編碼器能工作./ffmpeg.exe -i test.mp4 -c:v libx264 -preset fast test_out.mp4如果要用到dll形式的運(yùn)行庫(kù)別忘了把MinGW32的bin目錄下對(duì)應(yīng)的32位dll一起拷出去。最簡(jiǎn)單的辦法是編成靜態(tài)版本即configure時(shí)不加--enable-shared讓編出來(lái)的exe盡量自包含會(huì)省掉很多部署麻煩。4. Linux下編譯32位ffmpeg 6.0.1容器最省心Linux下的32位編譯我強(qiáng)烈建議用容器隔離不要在開(kāi)發(fā)機(jī)上直接搞因?yàn)槎嗉軜?gòu)依賴(lài)很容易把系統(tǒng)環(huán)境搞亂。4.1 docker跑386容器避免環(huán)境污染用linux/386平臺(tái)起一個(gè)干凈的Debian或Ubuntu容器一步到位docker run --rm -it --platform linux/386 debian:bullseye bash進(jìn)容器后先裝編譯工具鏈和依賴(lài)apt update apt install -y build-essential yasm pkg-config libx264-dev注意容器已經(jīng)是386平臺(tái)理論上不需要再加-m32直接configure就行./configure --disable-doc --disable-debug --enable-gpl --enable-libx264 make -j$(nproc)這種方式最大的好處是容器內(nèi)的libx264.so、libmp3lame.so自動(dòng)是32位的不會(huì)出現(xiàn)“編譯器是32位、庫(kù)卻是64位”的奇葩鏈接錯(cuò)誤。我第一次自己搞的時(shí)候就是在64位宿主機(jī)上硬編被各種dso參數(shù)坑了整整一個(gè)下午。4.2 本機(jī)直接-m32編譯的依賴(lài)坑如果你不想用容器堅(jiān)持在64位Linux宿主機(jī)上編譯32位目標(biāo)需要做兩件額外的事安裝multilib支持apt install gcc-multilib g-multilib沒(méi)有這個(gè)包-m32參數(shù)會(huì)直接報(bào)找不到bits/predefs.h之類(lèi)的頭文件。32位開(kāi)發(fā)庫(kù)64位系統(tǒng)默認(rèn)只裝了64位的libx264-dev想鏈接32位版本要么換裝:i386架構(gòu)包要么重新編譯一套32位依賴(lài)庫(kù)放進(jìn)自定義前綴目錄然后用--extra-ldflags-L/你的32位庫(kù)路徑強(qiáng)制指定。說(shuō)句實(shí)話這套流程在純手工操作下非常容易翻車(chē)因?yàn)橐蕾?lài)庫(kù)之間的版本匹配、路徑匹配、pkg-config路徑匹配都要自己維護(hù)。非必要不推薦。4.3 靜態(tài)鏈接與動(dòng)態(tài)鏈接的取舍Linux上這步的取舍比Windows更明顯。動(dòng)態(tài)鏈接的話發(fā)布時(shí)要跟著帶上一堆.so.6、.so.7你無(wú)法預(yù)知用戶系統(tǒng)里到底裝了哪一版依賴(lài)。ffmpeg 6.0.1對(duì)庫(kù)的SONAME有要求版本不匹配經(jīng)常啟動(dòng)就報(bào)undefined symbol。我更推薦靜態(tài)鏈接部署configure時(shí)加上--disable-shared --enable-static編譯選項(xiàng)里用-static或-static-libgcc -static-libstdc得到的就是一個(gè)能扔到任何相同架構(gòu)Linux上直接跑的裸二進(jìn)制。唯一要注意的是靜態(tài)鏈接GPL庫(kù)后分發(fā)二進(jìn)制時(shí)必須提供對(duì)應(yīng)的源碼獲取途徑這是GPL條款的要求商用場(chǎng)景下尤其別忽略。5. 運(yùn)行時(shí)的匹配問(wèn)題dll、so與庫(kù)檢查方法很多人在這一步卡住ffmpeg.exe明明就在當(dāng)前目錄雙擊卻提示“找不到libx264-168.dll”或者“不是有效的Win32應(yīng)用程序”。這里把排查手段講透。5.1 32位程序在64位系統(tǒng)上的加載機(jī)制Windows 64位系統(tǒng)通過(guò)WoW64層運(yùn)行32位程序正常情況下32位exe能正常運(yùn)行。但如果缺失32位依賴(lài)dll錯(cuò)誤提示五花八門(mén)最常見(jiàn)的是“找不到XXX.dll”和“應(yīng)用程序無(wú)法啟動(dòng)”。核心原則32位進(jìn)程只能加載32位dll64位進(jìn)程只能加載64位dll。別指望把64位的libx264.dll改名放到syswow64目錄就能騙過(guò)加載器它不看文件名看PE頭里的機(jī)器類(lèi)型。所以如果你下載的ffmpeg是32位完整的full版理論上自帶所有dll但如果只拷貝了exe而漏了dll啟動(dòng)就會(huì)失敗。5.2 Windows與Linux下的架構(gòu)檢查命令Windows排查依賴(lài)與架構(gòu)我常用這幾個(gè)手段查看exe/dll架構(gòu)dumpbin /headers查看exe依賴(lài)了哪些dll和它們的位置用Process Explorer或dumpbin /dependents32位dll不見(jiàn)得存放在System32里很多第三方組件裝到應(yīng)用目錄或SysWOW64別只盯著一個(gè)目錄找Linux下更直接file ffmpeg ldd ffmpeg$ file ffmpeg ffmpeg: ELF 32-bit LSB executable, Intel 80386, dynamically linked, ... $ ldd ffmpeg linux-gate.so.1 (0xf7f7a000) libx264.so.164 /usr/lib/i386-linux-gnu/libx264.so.164file輸出里能看到“ELF 32-bit”字樣ldd列出的依賴(lài)庫(kù)路徑如果指向i386-linux-gnu目錄說(shuō)明依賴(lài)也是32位的鏈路沒(méi)問(wèn)題。另外查看.a靜態(tài)庫(kù)是32位還是64位也是靠filefile libavcodec.a輸出顯示i386是32位x86-64是64位。這個(gè)排查在集成SDK時(shí)非常常用。5.3 常見(jiàn)運(yùn)行錯(cuò)誤與排查幾個(gè)我在實(shí)際運(yùn)行中反復(fù)踩過(guò)的坑列出來(lái)你對(duì)照看“不是有效的Win32應(yīng)用程序”你拿64位exe往32位系統(tǒng)或32位進(jìn)程里塞或者反之。檢查exe架構(gòu)即可。“找不到dll”依賴(lài)沒(méi)帶全用dumpbin /dependents看依賴(lài)列表把對(duì)應(yīng)32位dll補(bǔ)齊。Linux下報(bào)cannot execute binary file: Exec format error架構(gòu)不匹配可能拿32位二進(jìn)制往64位系統(tǒng)上放但沒(méi)開(kāi)multiarch支持。64位系統(tǒng)默認(rèn)能跑32位用戶態(tài)程序但缺32位動(dòng)態(tài)鏈接器ld-linux.so.2時(shí)就會(huì)報(bào)這個(gè)錯(cuò)裝libc6:i386解決。6. 32位版日常操作命令截圖、合并、推流一次說(shuō)清拿到32位ffmpeg后日常最常見(jiàn)的幾個(gè)操作這里把命令和參數(shù)邏輯一并寫(xiě)清楚。6.1 截圖與基礎(chǔ)轉(zhuǎn)碼視頻里截一幀出來(lái)做封面、做預(yù)覽是使用頻率最高的操作ffmpeg -i input.mp4 -vframes 1 output.png-vframes 1的意思是只處理一幀。熱詞里提到加了這個(gè)參數(shù)還是報(bào)“the specified filename”錯(cuò)誤多半是輸出路徑不存在或者文件名里帶了非法字符Windows下尤其注意別用反斜杠結(jié)尾?;A(chǔ)轉(zhuǎn)碼相對(duì)直觀ffmpeg -i input.mp4 -c:v libx264 -preset fast -crf 23 -c:a aac output.mp4-crf 23是H.264的默認(rèn)質(zhì)量值越小越清晰、文件越大。個(gè)人經(jīng)驗(yàn)是19到23之間日常夠用真正要批量處理時(shí)先用一個(gè)小段測(cè)試找到均衡點(diǎn)再全量跑。6.2 合并ts與m3u8轉(zhuǎn)mp4很多流媒體緩存放下來(lái)是一段段ts切片合并用concat協(xié)議最簡(jiǎn)單ffmpeg -f concat -safe 0 -i list.txt -c copy output.tslist.txt內(nèi)容格式file seg1.ts file seg2.ts file seg3.ts注意-safe 0要放在-i之前不然安全模式默認(rèn)不允許絕對(duì)路徑。合并完再轉(zhuǎn)成mp4或者直接對(duì)m3u8索引文件操作ffmpeg -i playlist.m3u8 -c copy output.mp4如果服務(wù)器帶寬不穩(wěn)定建議先完整下載切片到本地再合并直接-i指定m3u8容易因?yàn)榫W(wǎng)絡(luò)抖動(dòng)中途失敗。6.3 推流參數(shù)中的-y、-re到底什么意思這兩個(gè)參數(shù)幾乎每次推流都會(huì)出現(xiàn)但很多人只是照抄-y覆蓋輸出文件。推流時(shí)輸出是一個(gè)RTMP/RTSP地址原本不存在“覆蓋”的問(wèn)題但如果你在調(diào)試階段輸出到本地文件不加-y時(shí)每次都會(huì)被詢(xún)問(wèn)是否覆蓋腳本里就會(huì)卡住。-re按原始幀率讀取輸入。它的作用是讓ffmpeg以“實(shí)時(shí)”速度讀文件而不是一口氣讀完。推流場(chǎng)景必須加否則ffmpeg會(huì)以最快速度把視頻推完導(dǎo)致畫(huà)面像開(kāi)了倍速直播流瞬間就結(jié)束。典型推流命令ffmpeg -re -i input.mp4 -c copy -f flv rtmp://your-server/live/stream-c copy表示不做轉(zhuǎn)碼直接把原始編碼數(shù)據(jù)打包進(jìn)FLV。如果你要推給不支持H.265的流媒體服務(wù)需要先把輸入轉(zhuǎn)成H.264ffmpeg -re -i input.mp4 -c:v libx264 -preset veryfast -c:a aac -f flv rtmp://your-server/live/stream32位ffmpeg在轉(zhuǎn)碼和推流上的性能和64位版本差距其實(shí)沒(méi)有想象中那么大主要差異出在硬件加速編解碼器上某些GPU硬編庫(kù)沒(méi)有32位版本這就只能軟編頂上了。說(shuō)到最后我還是想提醒一句如果你只是普通使用直接去下載現(xiàn)成的32位靜態(tài)構(gòu)建就行別為了折騰而折騰。但如果你要鎖定6.0.1、要裁剪模塊、要集成進(jìn)自己的32位程序那就按照上面容器或MSYS2的流程自己編譯這份源碼樹(shù)是干凈的產(chǎn)物也完全可控。整個(gè)過(guò)程里最容易忽略的從來(lái)不是configure參數(shù)而是架構(gòu)驗(yàn)證和依賴(lài)庫(kù)的架構(gòu)匹配——文件到手、庫(kù)鏈接好之后記得先跑一遍file和對(duì)應(yīng)系統(tǒng)的依賴(lài)檢查命令再放進(jìn)真實(shí)環(huán)境驗(yàn)證一遍轉(zhuǎn)碼、截圖、推流三條主流程這樣才算是真正把32位ffmpeg用踏實(shí)了。本文還有配套的精品資源點(diǎn)擊獲取