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。

編譯準備

  1. 已經編譯好的OpenSSL,參考我的另外一篇博文:https://blog.csdn.net/sonysuqin/article/details/79674854;
  2. 安裝Cygwin,主要用於執行補丁腳本;
  3. VPN,下載WebRTC代碼最好直接下官網的,需要FAN-QIANG,也有國內人做的鏡像,但有可能會缺一些依賴;

編譯

    主要參考:https://github.com/ipop-project/ipop-project.github.io/wiki/Build-WebRTC-Libraries-for-Windows
    這裏搬運一下,基本步驟大同小異:
下載依賴

  1. 安裝Visual Studio 2015 Update 2以上版本,安裝的時候需要選擇MFC、通用Windows開發工具、 Windows 10 SDK (10.0.10586);
  2. 安裝Chromium depot tools,主要就是下載depot_tools.zip,解壓後,將其路徑加入PATH環境變量;
  3. 打開CMD,執行一次gclient,會自動下載一些工具,例如git、python等;
下載WebRTC源碼

  1. 創建WebRTC代碼目錄並進入
    mkdir webrtc-checkout
    cd webrtc-checkout
  2. 下載代碼
    fetch --nohooks webrtc
  3. 切換到57版本
    git branch -r
    git checkout branch-heads/57
  4. 同步,下載依賴
    gclient sync
    最好預留20G的空間。
生成工程
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。

--args內部參數的意義:
is_debug   是否是Debug版,這裏取false,表示編譯Release版。
target_os平臺類型,可以取值win、android、ios、linux等,這裏取win,表示Windows平臺。
target_cpucpu類型,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_brandingffmpeg的分支名,這裏採用Chrome的分支。
rtc_build_ssl是否編譯BoringSSL,這裏取false,因爲後面我們要替換成OpenSSL。
rtc_ssl_rootOpenSSL的頭文件路徑,會被寫到生成的ninja文件中。
--ide=vs2015
    這個參數在使用Visual Studio 2015調試的時候需要設置,如果只是編譯不調試則沒有必要設置。無論有沒有這個參數,WebRTC都是用ninja編譯系統來編譯,也就是說即使用Visual Studio 2015打開了工程編譯,也是調用ninja編譯。
    obj目錄下每個.ninja文件都相當於一個Makefile或者說一個工程文件,對應每個模塊的編譯、鏈接設置,而主工程obj/webrtc/webrtc.ninja生成的webrtc.lib是我們將要使用的庫
手動打的補丁

    主要是一次性永久生效的修改,主要修改兩個文件:

  1.  webrtc/BUILD.gn,修改前生成的靜態庫webrtc.lib只有幾K,修改後會生成完整的靜態庫;
  2. webrtc/base/opensslstreamadapter.cc,修改了頭文件的順序,否則會報編譯錯誤。


自動打的補丁

    每生成一次工程都要打的補丁,下面貼一個腳本(必須放到工程的obj目錄下),用於

  1. 加入OpenSSL的支持;
  2. 加入field_trial和metrics這兩個庫,默認webrtc.ninja沒有把這兩個庫加入,只鏈接webrtc.lib時可能會報鏈接錯誤;
  3. 刪除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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章