1. 程式人生 > >WebRTC Windows版編譯(支援H264+OpenSSL)

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_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檔案中。
--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。