1.linux平台, webrtc抽取出来静态库和头文件,先用ninja编译完webrtc,然后进入src/out/linux目录(假设src目录下是webrtc相关的代码)
1.1 提取所有的.o生成libwebrtc_full.a
.ninja_log 里面可以找到所有的.a ; .ninja_deps里面可以找到所有的.o, 但建议用.a去构建最终的静态库
cat libwebrtc_full.ar
CREATE libwebrtc_full.a
ADDLIB obj/third_party/boringssl/libboringssl.a
ADDLIB obj/third_party/protobuf/libprotobuf_full.a
ADDLIB obj/libwebrtc.a
SAVE
END
ar -M < libwebrtc.ar
ranlib libwebrtc_full.a
1.2 提取所有的头文件放入out/linxu/lib目录下
find . -path './third_party' -prune -o -type f -name '*.h' -print | xargs -I '{}' cp --parents '{}' out/linux/lib/
find . -name '*.h' -o -name README -o -name LICENSE -o -name COPYING | grep './third_party' | \
grep -E 'boringssl|expat/files|jsoncpp/source/json|libjpeg|libjpeg_turbo|libsrtp|libyuv|libvpx|opus|protobuf|usrsctp/usrsctpout/usrsctpout' | \
xargs -I '{}' cp --parents '{}' out/linux/lib/
1.3 有了上面的头文件和库,我们就可以引入到其他的工程里面去使用了,这是集成webrtc能力到其他项目中的一种很自然的方法
下面思考一个问题,很多时候我们希望不但可以集成webrtc到其他工程,更重要的是希望集成后,可以单步调测webrtc里面的代码,比如在vscode这个IDE里面
具体应该怎么做才最方便呢,很多时候我们要改动webrtc的代码,我们希望改动后直接编译好webrtc就可以生效了,不想再去打包,拷贝头文件到其他工程
因为这些头文件很多,libwebrtc_full.a 有200多M,如果稍微修改下webrtc代码就需要去这么拷贝,太麻烦了,请看下面1.4所用的方法,就是把工程建立到
webrtc/src/目录下,通过工程相关的配置去规避改动webrtc就需要拷贝的问题,同时,最重要的一点,如果希望能顺利调测,生成的可执行程序必须
放到webrtc/src/out/linux目录下,这个目录同时也是我们通过ninja -C src/out/linux xxx 去编译webrtc的时候的输出目录
1.4 假设新建一个transfer实时转码程序,它依赖webrtc库提供的一些能力,我们用vscode这个linux平台的IDE进行开发
首先我们需要考虑transfer这个实时转码程序需要依赖的能力,具体分析如下:
它需要webrtc的能力,因为我们希望合并多路音视频流到一路,webrtc里面有合音的代码可以利用,另外,webrtc代码提供了很多类似线程,网络方面的封装,都是可以利用的
它需要http+json的能力,因为我们转码服务器需要一些配置,比如多路画面如何布局等等,这些都可以通过http+json的配置方式解决(transfer接收http配置请求)
它需要一套信令传输系统,和其他部件进行交互,可以考虑http或者websocket,如果是http,那么它需要http client+ http server的能力
基于以上的考虑,我们集成一下基础开源能力部件,考虑到webrtc代码过于庞大,我们的目的主要是需要调测webrtc代码,所以我们把这些能力集成到webrtc的third_party目录下:
nlohmann_json: 提供json的能力,它的代码放入third_party/json子目录下(libsourcey使用了它提供json的能力)
restclient-cpp: 提供http client的能力,它的库和头文件放入third_party/restclieng-cpp子目录下
webrtc: 提供webrtc端的能力
libsourcy: 它提供了很多基础能力,比如网络,线程,事件分发,还能把这些能力和webrtc结合起来提供视频录制,视频分析等功能,它的库和文件放入third_party/libsourcey子目录下
多提一句,libsourcy的代码结构以及编译系统组织的很好,可以拿出来单独几个模块使用,transfer里面利用它的base,json,webrtc三个模块,具体编译方法查看官网介绍
如果想依赖外部的ffmepg,可以查看libsourcey/cmake/FindFFmpeg.cmake的内容,修改ffmpeg_root的位置
1.4.1 VSCODE-transfer工程目录结构的设计,以webrtc为主:
/home/kevin/webrtc/linux/src -----> webrtc-linux平台下的源码(适用于android以及linux两个平台下的开发)
/home/kevin/webrtc/linux/src/out/linux/compile.sh -----> 如果改动了webrtc相关代码,可以用来重新编译webrtc,打成libwebrtc_full.a
/home/kevin/webrtc/linux/CMakeLists.txt -----> 它调用transfer下的CMakeList.txt
/home/kevin/webrtc/linux/transfer
/home/kevin/webrtc/linux/transfer/CMakeLists.txt -----> 完成transfer相关代码的编译以及连接
/home/kevin/webrtc/linux/transfer/src -----> transfer的源码
/home/kevin/webrtc/linux/transfer/lib -----> transfer依赖的第三方库
/home/kevin/webrtc/linux/transfer/bin -----> 编译后的可执行程序存放目录
/home/kevin/webrtc/linux/src/out/linux/compile.sh:
#!/bin/bash
# $1: The platform
# $2: The list of object file paths to be combined
# $3: The output library name
function combine::static() {
local platform="$1"
local outputdir="$2"
local libname="$3"
echo $libname
pushd $outputdir >/dev/null
rm -f $libname.*
# Find only the libraries we need
if [ $platform = 'win' ]; then
local whitelist="boringssl.dll.lib|protobuf_lite.dll.lib|webrtc\.lib|field_trial_default.lib|metrics_default.lib"
else
local whitelist="boringssl\.a|protobuf_full\.a|webrtc\.a|field_trial_default\.a|metrics_default\.a"
fi
cat .ninja_log | tr '\t' '\n' | grep -E $whitelist | sort -u >$libname.list
# Combine all objects into one static library
case $platform in
win)
# TODO: Support VS 2017
"$VS140COMNTOOLS../../VC/bin/lib" /OUT:$libname.lib @$libname.list
;;
*)
# Combine *.a static libraries
echo "CREATE $libname.a" >$libname.ar
while read a; do
echo "ADDLIB $a" >>$libname.ar
done <$libname.list
echo "SAVE" >>$libname.ar
echo "END" >>$libname.ar
ar -M < $libname.ar
ranlib $libname.a
rm $libname.list
rm $libname.ar
;;
esac
popd >/dev/null
}
ninja -C . webrtc
combine::static "linux" "./" libwebrtc_full
/home/kevin/webrtc/linux/CMakeLists.txt:
cmake_minimum_required(VERSION 3.5.1)
add_subdirectory(src/transfer ${CMAKE_CURRENT_SOURCE_DIR}/transfer/build)
/home/kevin/webrtc/linux/transfer/CMakeLists.txt:
cmake_minimum_required(VERSION 3.5.1)
project(transfer)
# Typically you don't care so much for a third party library's tests to be
# run from your own project's code.
set(JSON_BuildTests OFF CACHE INTERNAL "")
# If you only include this third party in PRIVATE source files, you do not
# need to install it when your main project gets installed.
set(JSON_Install OFF CACHE INTERNAL "")
# Don't use include(nlohmann_json/CMakeLists.txt) since that carries with it
# unintended consequences that will break the build. It's generally
# discouraged (although not necessarily well documented as such) to use
# include(...) for pulling in other CMake projects anyways.
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../src/third_party/json json)
#add_library(foo ...)
#target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json)
set(CMAKE_VERBOSE_MAKEFILE on)
set(CMAKE_CXX_COMPILER "clang++")
set(CMAKE_CXX_FLAGS "-std=c++11 -stdlib=libc++ -fno-rtti ${CMAKE_CXX_FLAGS}")
add_definitions(-DWEBRTC_POSIX)
add_definitions(-DWEBRTC_LINUX)
set(ENV{PKG_CONFIG_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../src/third_party/libsourcey/lib/pkgconfig")
find_package(PkgConfig REQUIRED)
pkg_search_module(PKG_SCY REQUIRED libsourcey)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src/third_party/abseil-cpp)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src/third_party/restclient-cpp/include)
include_directories(${PKG_SCY_INCLUDE_DIRS})
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src/out/linux)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src/third_party/restclient-cpp/lib)
add_executable(transfer src/main.cpp)
target_link_libraries(transfer nlohmann_json::nlohmann_json
restclient-cpp
${PKG_SCY_LDFLAGS}
webrtc_full
pthread)
set_target_properties(transfer PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../src/out/linux)
1.4.2 /home/kevin/webrtc/linux/.vscode目录下的相关配置文件及其内容
c_cpp_properties.json -----> 这个文件主要用来告诉vscode头文件到哪里去查找
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/src/third_party/abseil-cpp",
"${workspaceFolder}/src/third_party/json/single_include",
"${workspaceFolder}/src/third_party/libsourcey/include",
"${workspaceFolder}/src/third_party/restclient-cpp/include"
],
"defines": [],
"compilerPath": "/usr/bin/clang",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "clang-x64",
"compileCommands": "${workspaceFolder}/src/transfer/build/compile_commands.json"
}
],
"version": 4
}
settings.json -----> 相关的配置,这些配置的范围可以是项目级别,也可以是用户级别,这里配置项目级别:
告诉vscode不要去管这些目录的变化情况,因为里面的文件太多了,超过了vscode的关注能力
如果想扩大这个能力,修改/etc/sysctl.conf,在文件最后加一行
fs.inotify.max_user_watches=524288,然后sudo sysctl -p
{
"search.exclude": {
"**/src/examples": true,
"**/src/out": true,
"**/src/third_party": true,
"**/transfer/build": true,
"**/transfer/lib": true
}
}
launch.json -----> 告诉vscode如何运行我们编译好的程序
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/src/out/linux/transfer",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
tasks.json ----->告诉vscode如何去编译我们的程序,因为额外在vscode里面安装了一个cmake插件,所以其实这个文件没有用到
编译的时候是用的cmake插件,但是其实配置下tasks.json一样可以达到编译CMakeLists.txt的目的,待研究
{
"tasks": [
{
"type": "shell",
"label": "clang++-3.8 build active file",
"command": "/usr/bin/clang++-3.8",
"args": [
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "/usr/bin"
}
}
],
"version": "2.0.0"
}
1.4.3 用vscode open /home/kevin/webrtc/linux 这个目录,就可以查看,编译,单步调测相关源码了(包括webrtc)
1.4.4 编译的时候,需要注意,默认webrtc使用libc++标准库,所以先sudo apt-get install libc++-dev,否则会提示说list找不到;
另外,CMakeLists.txt中指定编译选项 -stdlib=libc++ 用clang++去编译,否则会报一堆的symbol undefined错误
其他注意的地方:
1. libsourcey最低要求c++14去编译
2. 所有cpp编写的第三方库的编译使用clang++完成,和webrtc对齐
3. 服务器对显示界面方面没有要求,webrtc要想办法要去掉x11相关的库依赖,附上一份不需要x11的arm64机器上的args.gn
target_cpu = "arm64"
target_os = "linux"
rtc_use_fake_ui=true
rtc_use_sdk_mode=true
rtc_use_external_capturer=true
rtc_use_h264=true
rtc_use_x264=false
rtc_use_uws=true
rtc_build_ssl=false
rtc_ssl_version="arm64"
is_clang=false
use_sysroot=false
linux_use_bundled_binutils=false
use_custom_libcxx=false
use_custom_libcxx_for_host=false
rtc_use_x11=false
rtc_include_internal_audio_device=false
rtc_libvpx_build_vp9=false
rtc_use_new_stat_api=false
treat_warnings_as_errors=false
5. 如果需要x11,那么需要安装
sudo apt-get install libalsa-ocaml-dev libpulse-dev libxcomposite-dev libxrender-dev libxext-dev libxdamage-dev libcairo2-dev
webrtc依赖的c++标准库在 src/build/config/android/BUILD.gn中定义
标准库的位置:third_party/android_tools/ndk/sources/cxx-stl --- 该目录下有很多标准库,webrtc默认使用llvm的libc++_shared.so
交叉编译ARM-LINUX平台(host是ubuntu16.04):
sudo apt get install binutils-aarch64-linux-gnu binutils-arm-linux-gnueabihf --- 分别安装64以及32的交叉编译环境
sudo apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
这些工具的名字记录位置: src/build/toolchain/linux/BUILD.gn 里面
然后:
gn gen out/arm64 --args='target_cpu="arm64" target_os="linux"'
ninja -C out/arm64 peerconnection_client
依赖库脚本安装: src/build/linux/sysroot_scripts/install-sysroot.py --arch=arm64
clang编译webrtc静态库
ninja -C out/linux
得到libwebrtc.a,这个静态库包括webrtc全部的o文件.
直接-lwebrtc, -I${webrtc}/src目录,就可以用native api开发了.
由于webrtc编译时,采用clang进行的编译,所以如果二次开发环境是gcc,会遇到一些库的不兼容
例如:
undefined reference:string<char, char_traits<char>, allocator<char> >::~basic_string()
解决方法:
把webrtc的clang环境,引入到二次开发的工程中,步骤如下.
(1)在${webrtc}/src/out/linux/obj/buildtools/third_party中,找到.o文件,分别打包.
ar -crv libc++.a *.o
ar -crv libc++abi.a *.o
(2)在二次开发的工程中,引入这两个a文件,-lc++,-lc++abi
(3)把g++和gcc替换成clang++和clang,可执行文件在${webrtc}src/third_party/llvm-build/Release+Asserts/bin下面
(4)包含头文件位置:
${webrtc}/src/buildtools/third_party/libc++/trunk/include
${webrtc}/src/third_party/llvm-build/Release+Asserts/lib/clang/6.0.0/include
(5)为防止clang与gcc的标准库冲突,在编译时指定选项,-nostdinc++,表示不要搜索标准系统头文件目录
(6)预定义WEBRTC_POSIX和WEBRTC_LINUX
(7)webrtc关闭了 rtti机制,以减小应用程序大小,所以在继承webrtc的类时,需要在g++中添加 -fno-rtti选项.
总结,用webrtc或者chromium的二次开发,不仅需要引入.a的静态库文件,同时需要引入相应的c++编译环境,包括clang的依赖库,bin和头文件.
openssl vs boringssl
rtc_build_ssl = false && rtc_ssl_root = //third_party/openssl/include , 这样就可以切换到openssl了,注意一下几点:
1. third_party 里面 usrsctp 以及 libsrtp 依赖 boringssl, 就是没有注意修改这两个地方,导致编译进去openssl以后,
DTLSV1_get_timeout函数表现异常,挂掉