WebRTC Windows版編譯(支援H264+OpenSSL)
摘要
本文介紹了在Windows下編譯WebRTC的方法,WebRTC預設支援VP8、VP9(谷歌自己的編碼)和BoringSSL
(谷歌的OpenSSL分支,主要修復一些OpenSSL主線的漏洞),本文將介紹在Windows下讓WebRTC支援使用更廣泛的H264、OpenSSL的方法。
版本
本文使用的版本是57,我下載WebRTC程式碼的時候最新版本是62,但是為了方便編譯切到57版本,57版本在Windows下編譯需要Visual Studio 2015。後來我下載了最新的65版本的程式碼,其編譯工具要求Visual Studio 2017,所以這裡需要注意,WebRTC Windows版不同版本的編譯可能會要求不同的Visual Studio版本。
為什麼要支援H264
很簡單,為了相容性。VP8、VP9是谷歌自己的編碼,在IOS上目前支援並不好,而H264是事實上的標準,在各個平臺支援的很好。但是由於H264並不是谷歌親生的,所以在傳輸上與WebRTC本身的程式碼結合得還不是很完美,例如H264下FEC無法使用,弱網情況下VP8比H264的表現的確要好一些,詳細可以參考:https://blog.csdn.net/volvet/article/details/53700049。
為什麼要支援OpenSSL
OpenSSL當年曝出HeartBleed等漏洞,谷歌便開啟了OpenSSL的BoringSSL分支用於解決這些漏洞,這些漏洞大多被提交到官方的OpenSSL中。目前業內做SSL/TLS應用大多選擇OpenSSL,而BoringSSL除了修改了部分漏洞外,介面與OpenSSL是完全一致的,這就導致了大量重複的符號。如果有兩個靜態庫,一個使用OpenSSL,一個使用BoringSSL(例如WebRTC),在連結時由於符號衝突會出現連結錯誤。如果使用動態庫,連結期間不會報錯,但是執行期間程式是執行OpenSSL的符號還是BoringSSL的符號不可知,程式的行為無法預計。現在谷歌在很多專案中已經開始使用BoringSSL,其宣稱並不想取代OpenSSL,而事實上BoringSSL具有傳染性,因為你一旦用了他包含BoringSSL的一個庫,其他使用OpenSSL的地方只好捨棄OpenSSL採用BoringSSL。就目前來講,OpenSSL使用得更廣泛(雖然有已知的漏洞),如果你提供一個SDK給別人整合,那麼希望相容性更好,那麼有可能需要將BoringSSL改為OpenSSL。
編譯準備
- 已經編譯好的OpenSSL,參考我的另外一篇博文:https://blog.csdn.net/sonysuqin/article/details/79674854;
- 安裝Cygwin,主要用於執行補丁指令碼;
- VPN,下載WebRTC程式碼最好直接下官網的,需要FAN-QIANG,也有國內人做的映象,但有可能會缺一些依賴;
編譯
主要參考:https://github.com/ipop-project/ipop-project.github.io/wiki/Build-WebRTC-Libraries-for-Windows這裡搬運一下,基本步驟大同小異:
下載依賴
- 安裝Visual Studio 2015 Update 2以上版本,安裝的時候需要選擇MFC、通用Windows開發工具、 Windows 10 SDK (10.0.10586);
- 安裝Chromium depot tools,主要就是下載depot_tools.zip,解壓後,將其路徑加入PATH環境變數;
- 開啟CMD,執行一次gclient,會自動下載一些工具,例如git、python等;
下載WebRTC原始碼
- 建立WebRTC程式碼目錄並進入
mkdir webrtc-checkout
cd webrtc-checkout - 下載程式碼
fetch --nohooks webrtc - 切換到57版本
git branch -r
git checkout branch-heads/57 - 同步,下載依賴
gclient sync
生成工程
gn gen out/release_vs_h264_openssl --args="is_debug=false target_os=\"win\" target_cpu=\"x86\" is_component_build=false proprietary_codecs=true rtc_use_h264=true ffmpeg_branding=\"Chrome\" rtc_build_ssl=false rtc_ssl_root=\"D:\work\mediasdk\third_party\openssl\win32\include\"" --ide=vs2015
這個命令會在out目錄下生成一個工作目錄release_vs_h264_openssl。
is_debug | 是否是Debug版,這裡取false,表示編譯Release版。 |
target_os | 平臺型別,可以取值win、android、ios、linux等,這裡取win,表示Windows平臺。 |
target_cpu | cpu型別,Windows下可以取x86、x64,這裡取x86,對應32位版本。 |
is_component_build | 是否使用動態執行期庫,這裡取false,使用靜態執行期庫,Release版本將對應MT,Debug版將對應MTd。 |
proprietary_codecs | 是否使用版權編碼,也就是H264,這裡取true。 |
rtc_use_h264 | 是否使用H264,這裡取true,注意Windows平臺編碼使用OpenH264,解碼使用ffmpeg。 |
ffmpeg_branding | ffmpeg的分支名,這裡採用Chrome的分支。 |
rtc_build_ssl | 是否編譯BoringSSL,這裡取false,因為後面我們要替換成OpenSSL。 |
rtc_ssl_root | OpenSSL的標頭檔案路徑,會被寫到生成的ninja檔案中。 |
這個引數在使用Visual Studio 2015除錯的時候需要設定,如果只是編譯不除錯則沒有必要 設定。無論有沒有這個引數,WebRTC都是用ninja編譯系統來編譯,也就是說即使用Visual Studio 2015打開了工程編譯,也是呼叫ninja編譯。
obj目錄下每個.ninja檔案都相當於一個Makefile或者說一個工程檔案,對應每個模組的編譯、連結設定, 而主工程obj/webrtc/webrtc.ninja生成的webrtc.lib是我們將要使用的庫。
手動打的補丁
主要是一次性永久生效的修改,主要修改兩個檔案:
- webrtc/BUILD.gn,修改前生成的靜態庫webrtc.lib只有幾K,修改後會生成完整的靜態庫;
- webrtc/base/opensslstreamadapter.cc,修改了標頭檔案的順序,否則會報編譯錯誤。
自動打的補丁
每生成一次工程都要打的補丁,下面貼一個指令碼(必須放到工程的obj目錄下),用於
- 加入OpenSSL的支援;
- 加入field_trial和metrics這兩個庫,預設webrtc.ninja沒有把這兩個庫加入,只連結webrtc.lib時可能會報連結錯誤;
- 刪除video_capture_external、device_info_external這兩個模組,因為預設webrtc.ninja把video_capture_external、device_info_external、video_capture_internal_impl等模組一起連結進去,符號是重複的,編譯的時候會報警,呼叫的時候可能調到video_capture_external(需要上層實現),實際上video_capture_internal_impl是WebRTC對攝像頭的內部實現,如果要自己實現外部的攝像頭模組,則應刪除video_capture_internal_impl。
#!/bin/bash
boringssl_include="..\/..\/third_party\/boringssl\/src\/include"
boringssl_libs="obj\/third_party\/boringssl\/boringssl.lib\ obj\/third_party\/boringssl\/boringssl_asm.lib"
boringssl_asm_stamp="obj\/third_party\/boringssl\/boringssl_asm_action.stamp"
openssl_include="D$:\/work\/mediasdk\/third_party\/openssl\/win32\/include"
openssl_libs="D$:\/work\/mediasdk\/third_party\/openssl\/win32\/lib\/ssleay32.lib D$:\/work\/mediasdk\/third_party\/openssl\/win32\/lib\/libeay32.lib"
openssl_libdir="D$:\/work\/mediasdk\/third_party\/openssl\/win32\/lib"
openssl_ld="ssleay32.lib\ libeay32.lib"
additional_obj="obj\/webrtc\/system_wrappers\/field_trial_default\/field_trial_default.obj obj\/webrtc\/system_wrappers\/metrics_default\/metrics_default.obj"
patch() {
local file_name=$1
echo process $file_name
has_boringssl_header=`grep $boringssl_include $file_name`
if [ -n "$has_boringssl_header" ];then
#echo Found boringssl include dir,replace it and add openssl macro.
#Replace include
sed -i "s/$boringssl_include/$openssl_include/g" $file_name
#Append openssl macro.
sed -i "/^defines =/s/$/ -DSSL_USE_OPENSSL -DHAVE_OPENSSL_SSL_H -DFEATURE_ENABLE_SSL/" $file_name
fi
#Replace boringssl libraries to openssl libraries.
sed -i "s/$boringssl_libs/$openssl_libs/g" $file_name
#Delete boringssl asm stamp
sed -i "s/$boringssl_asm_stamp//g" $file_name
#Delete boringssl objects.
sed -i "s/obj\/third_party\/boringssl\/boringssl\/err_data.obj.*obj\/third_party\/boringssl\/boringssl_asm\/sha512-586.o //g" $file_name
}
add_openssl_libs() {
local file_name=$1
echo Add openssl lib to $file_name
#Append openssl library path.
has_ssl_ld=`grep openssl\/win32\/lib $file_name`
if [ -z "$has_ssl_ld" ];then
sed -i "/ldflags =/s/$/ \/LIBPATH:\"$openssl_libdir\"/" $file_name
fi
has_ssl_lib=`grep ssleay32.lib $file_name`
if [ -z "$has_ssl_lib" ];then
sed -i "s/libs =/& $openssl_ld/" $file_name
fi
}
grep -r boringssl . | grep -v -e boringssl.ninja -e boringssl_asm.ninja -e boringssl.vcxproj -e boringssl_asm.vcxproj | grep ninja | awk -F : '{print $1}' | sort | uniq | while read line
do
patch $line
done
fs=('./webrtc/examples/stun_prober.ninja' './webrtc/rtc_unittests.ninja' './webrtc/webrtc_nonparallel_tests.ninja' './webrtc/stats/rtc_stats_unittests.ninja' './webrtc/modules/modules_unittests.ninja')
for f in ${fs[@]};do
add_openssl_libs $f
done
file_name=./webrtc/webrtc.ninja
#Add field_trial and metrics
has_field_trial=`grep field_trial_default $file_name`
if [ -z "$has_field_trial" ];then
echo Add field_trial and metrics
sed -i "s/alink/& $additional_obj/" $file_name
fi
#Delete external capture
echo Delete external capture
sed -i "s/obj\/webrtc\/modules\/video_capture\/video_capture\/device_info_external.obj obj\/webrtc\/modules\/video_capture\/video_capture\/video_capture_external.obj //g" $file_name
執行該指令碼需要安裝Cygwin,該指令碼會先搜尋工程目錄下所有的ninja檔案,找到BoringSSL的標頭檔案、庫,替換成OpenSSL的巨集、標頭檔案、庫,同時該指令碼還將修改webrtc.ninja,加入field_trial_default、metrics_default這兩個模組,並刪除video_capture_external、device_info_external這兩個模組。
執行編譯
在src目錄下,執行
ninja -C out\release_vs_h264_openssl
會進行完整編譯,也可以用Visual Studio 2015開啟all.sln進行編譯。
在obj/webrtc目錄下生成的webrtc.lib就是我們要使用的支援H264、OpenSSL的WebRTC靜態庫。
測試H264
完整編譯後會在out\release_vs_h264_openssl目錄下生成一個video_loopback工具,用於進行loopback播放。如果不完整編譯,也可以單獨執行以下命令來單獨編譯video_loopback,相應的依賴也將被編譯。
ninja -C out\release_vs_h264_openssl video_loopback
video_loopback會呼叫WebRTC採集攝像頭資料、編碼、解碼、渲染,進入out\release_vs_h264_openssl目錄,執行以下命令會看到效果:
video_loopback --codec=H264
注:以上是Debug版的截圖,Release版並不列印這些log。