happyj2me
4/18/2019 - 3:34 AM

webrtc-compile

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函数表现异常,挂掉