摘要
本文介紹了在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。