webrtc最佳实践

webrtc下载+编译+运行

本机环境
本机为Mac M2 os14.4版本,iphone12mini-iOS16.4,vpn

下载

首先python2 和 python3 都得安装。去官网下载安装包。

  • macos 自带Python3,默认安装在 /usr/bin/pyhon3,手动下载的Python2安装包,最终会把Python2安装在 /usr/local/bin/python
  • 下载depot_tools

    • git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
  • 设置环境变量

    • export PATH=$PWD/depot_tools:$PATH
  • 下载webrtc源码

    • mkdir webrtc
    • cd webrtc
    • fetch --nohooks webrtc_ios 时间很长,需要上外网
    • gclient sync -D 时间也不短,这几句命令过后下载的就是webrtc的最新代码
  • 官网查看最新源码

  • 查看所有 release 版本

  • 碰到编译失败的情况可现在官网搜一下官网搜issue

  • webrtc 从 m79 版本之后不再以此种形式命名,m79 最后一笔提交已经是五年前了:2019 年 11 月。M79。我用的是[6261]即 M122 版本:2024 年 2 月

  • 查看可用版本分支 git branch -r
  • 切换到某个分支,比如m79分支 git checkout branch-heads/m79gclient sync -D
  • 或者强制切换到指定commit(b484ec0082948ae086c2ba4142b4d2bf8bc4dd4b是m79最后一次提交的commit id)
    gclient sync -r b484ec0082948ae086c2ba4142b4d2bf8bc4dd4b --force

执行 fetch –nohooks webrtc_ios可能出现的错误

  • .cipd_bin/vpython3: No such file or directory

是代理的原因,终端执行如下命令:

  • export http_proxy=”http://127.0.0.1:7890
  • export https_proxy=”http://127.0.0.1:7890
    • 其中端口就是你vpn的端口,打开你vpn的设置去找端口
    • 比如这样
  • 查看设置的代理
    • echo $http_proxy
  • 取消设置的代理
    • unset http_proxy
    • unset https_proxy

执行gclient sync时可能出现的错误:

把update.py脚本用PyCharm IDE打开跑一下,发现报错如下:


使用如下命令可以解决

  • python3 -m pip install certifi
  • 再打开spot light输入Install Certificates.command然后启动
  • 启动的是一个进程,加载完后是这样:

这个命令就会更新ssl 的证书到一个目录下:

/Library/Frameworks/Python.framework/Versions/3.11/etc/openssl 在执行Install Certificates.command命令前,该目录下是空的。执行命令后就会看到一个cert.pem

解决后再继续gclient sync就可以跑完了,跑完后的样子是这样:

编译

mac平台的支持h264的debug版本

下载完后的目录结构是这样:

开始执行编译命令:

  • cd src

  • 编译一个mac平台的支持h264的debug版本

    • gn gen out/mac-debug –args=’target_os=”mac” target_cpu=”x64” is_debug=true use_rtti=true is_component_build=false rtc_use_h264=true rtc_include_tests=false’ –ide=xcode
    • 其中out/mac-debug表示会在src/out目录下创建一个mac-debug目录
    • 执行完成后的样子:
  • 生成一个可以运行调试的工程

    • ninja -C out/mac-debug
    • 该命令执行如下所示
    • 执行完成如下所示:

    目录如下

    其中AppRTCMobile.app可以直接打开,也可以打开all工程调试代码了。

运行 Mac 的 AppRTCMobile 工程
  • 首先需要给该demo添加plist文件:

    • plist文件目录在webrtc/src/examples/objc/AppRTCMobile/mac/Info.plist
    • 在build settings中搜索info.plist,默认显示info.plist目录时空的,把上面的目录填上,如下图
  • 修改info.plist的bundle ID

    • 需要注意的是info.plist的bundle ID必须和all-AppRTCMobile-General中的bundle id一致,否则编译出错。

ios平台的不支持h264的release版本

编译:

gn gen out/ios-release –args=’target_os=”ios” target_cpu=”arm64” is_debug=false use_rtti=true is_component_build=false ios_enable_code_signing=false proprietary_codecs=false rtc_use_h264=false rtc_include_tests=false’ –ide=xcode

ninja -C out/ios-release


ios平台的支持h264的release code signing版本

  • 获取本机Xcode带有的所有可用的开发者账号
    • security find-identity -v -p codesigning 打印如下:
  • gn gen out/ios-release-sign --args='target_os="ios" target_cpu="arm64" is_debug=false use_rtti=true is_component_build=false ios_code_signing_identity_description="2417xxxx" proprietary_codecs=false rtc_use_h264=true rtc_include_tests=false' --ide=xcode
  • ios_code_signing_identity_description 用来指定用哪个开发者账号。只能是开发者账号全称字符串的子串。

编译出mac端的sdk

目录结构

1
2
3
4
5
.
--depot_tools
--webrtc
----src
----build.sh

脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/bin/bash

set -e

# 进入 src 目录,这是 gn 需要的 source root
cd src

# 1. 生成ninja构建文件
# 输出目录会是 webrtc/src/out/mac_arm64_static
gn gen out/mac_arm64_static --args='
target_os="mac"
target_cpu="arm64"
is_component_build=false
rtc_use_h264=true
is_debug=false
rtc_libvpx_build_vp9=true
enable_stripping=true
rtc_enable_protobuf=false
'

# 2. 编译webrtc静态库
ninja -C out/mac_arm64_static webrtc

# 编译完成后,返回到项目根目录
cd ..

# 3. 创建最终的输出目录
OUTPUT_DIR="webrtc_mac_arm64"
rm -rf "$OUTPUT_DIR"
mkdir -p "$OUTPUT_DIR/lib"
mkdir -p "$OUTPUT_DIR/include"

# 4. 拷贝静态库
# 路径需要从根目录开始,所以是 src/out/...
cp src/out/mac_arm64_static/obj/libwebrtc.a "$OUTPUT_DIR/lib/"

# 5. 拷贝头文件
# 使用子shell进入src目录执行find,避免影响当前目录
# rsync的-R会保留目录结构,目标路径 ../"$OUTPUT_DIR/include/" 是相对于src目录的
(cd src && find api \
rtc_base \
modules \
common_audio \
common_video \
p2p \
pc \
media \
system_wrappers \
-name "*.h" \
-exec rsync -R {} ../"$OUTPUT_DIR/include/" \;)

echo "✅ Build finished successfully. Output in $OUTPUT_DIR"

编译出Linux端的sdk

目录结构

1
2
3
4
5
6
.
--depot_tools
--webrtc
----src
----build.sh
----Dockerfile

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FROM --platform=linux/amd64 ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
# Add python3-pip to install python packages
RUN apt-get update && apt-get install -y \
git python3 python3-distutils python3-pip build-essential ninja-build pkg-config curl clang \
&& rm -rf /var/lib/apt/lists/*

# Install depot_tools
RUN git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git /opt/depot_tools
ENV PATH="/opt/depot_tools:${PATH}"

# Install python dependencies for depot_tools before running it
RUN pip3 install httplib2

# Bootstrap depot_tools to ensure all helper tools are downloaded
RUN python3 /opt/depot_tools/gclient.py --version

脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#!/usr/bin/env bash
set -e

# 1. 使用 buildx 构建镜像,确保 --platform 标志生效
docker buildx build --platform linux/amd64 -t webrtc-build-env --load .

# 2. 运行编译 (同样指定平台)
docker run --rm --platform linux/amd64 \
-v "$(pwd)":/webrtc \
-v "$(pwd)/output":/output \
webrtc-build-env \
bash -c "
set -e
cd /webrtc

# ------------------- 重要说明:编译模式选择 -------------------
#
# 【模式一:首次编译或更新分支】
# 运行下面的 'gclient sync'。它会完整同步所有依赖,但速度较慢。
# echo 'Syncing dependencies for a full build...'
# gclient sync --with_branch_heads --force

# 【模式二:日常增量编译】
# 为了快速编译,请注释掉上面的 'gclient sync',
# 然后取消下面 'gclient runhooks' 的注释。
# 'runhooks' 会跳过缓慢的依赖检查,只执行必要的环境设置,速度很快。
#
echo 'Running hooks for an incremental build...'
gclient runhooks

# --- 后续步骤都在 /webrtc/src 目录中执行 ---
cd /webrtc/src

# c. 安装 sysroot
echo 'Installing sysroot...'
build/linux/sysroot_scripts/install-sysroot.py --arch=amd64

# d. 编译
echo 'Generating build files...'
gn gen out/linux --args='is_debug=false is_component_build=false is_clang=true rtc_include_tests=false rtc_use_h264=true use_rtti=true use_custom_libcxx=true treat_warnings_as_errors=false target_os=\"linux\" target_cpu=\"x64\"'

echo 'Building WebRTC...'
ninja -C out/linux webrtc

# e. 合并所有 .a 文件成一个大的 libwebrtc.a
echo 'Combining archives...'
# 创建一个 MRI 脚本来执行合并操作
echo \"create /output/libwebrtc.a\" > /tmp/archive.mri
find out/linux/obj -name \"*.a\" -exec echo \"addlib {}\" \; >> /tmp/archive.mri
echo \"save\" >> /tmp/archive.mri
echo \"end\" >> /tmp/archive.mri
ar -M < /tmp/archive.mri

# f. 拷产物
echo 'Copying artifacts...'
mkdir -p /output/include
find api rtc_base modules common_audio common_video p2p pc call video audio logging media system_wrappers -name '*.h' -exec cp --parents -t /output/include/ {} +
"
echo "✅ Linux 版 libwebrtc.a 已生成在 $(pwd)/output/"

1. 创建库文件

静态库和动态库在底层原理上的区别:

  • 生成静态库 ( .a ) :这一步我们称之为 归档 (Archiving) 。 ar 命令做的事情相对“简单”,它就像一个打包工具,把成百上千个编译好的 .o 文件不加处理地塞进一个大文件里。这个过程主要是 磁盘 I/O 密集型 的,因为它需要读取大量文件。
  • 生成动态库 ( .so ) :这一步我们称之为 链接 (Linking) 。链接器( ld )做的事情要复杂得多。它不仅要读取所有的 .o 文件,还需要:
    • 解析符号 :检查所有函数调用和变量引用,确保它们都能找到对应的定义。
    • 重定位 :处理代码和数据的位置,使库可以在运行时被加载到内存的任意地址。
    • 生成元数据 :创建复杂的内部结构,比如符号表、导出表等,供操作系统在加载时使用。这个过程不仅是 磁盘 I/O 密集型 的,更是 CPU 和内存密集型的。
      简单来说 :
  • 创建 .a 文件像把一堆零件( .o 文件)都扔进一个大箱子里。
  • 创建 .so 文件则像把这些零件精密地组装成一台可以运转的引擎。

  • 静态库 ( .a ):只归档,不链接

    • 这一步, ar 命令只是简单地把成百上千个 .o 文件(就像一个个独立的零件)打包成一个 .a 文件(一个大零件盒)。
    • 它 完全不关心 这些零件之间是如何配合的。比如 A 零件调用了 B 零件的一个功能, ar 对此一无所知,也不会去检查。所以这一步非常快,主要是磁盘读写。
    • 链接器在处理静态库时,只会把最终程序用到的那部分代码(以 .o 目标文件为单位)拷贝进去,任何没有被用到的代码都会被舍弃。
    • 这正是静态库的一个巨大优势,通常被称为按需链接或死代码剥离 (Dead Code Stripping)
  • 动态库 ( .so ):归档,并且链接

    • 这一步,链接器 ( ld ) 不仅要把所有 .o 文件打包,还要做大量的“内部整理”工作。
    • 它会检查并 解析所有内部符号 ,确保库内部的函数调用都能找到彼此。它会把这些零件预先组装成一个可以独立运转的“半成品引擎”。
    • 这个过程非常复杂,耗时也更长。

2. 使用库文件,编译你的最终程序时

这是“链接”这个词真正发挥作用的地方,也是静态库和动态库体现出最大不同的时候。

  • 使用静态库 ( .a )
    • 当你编译自己的 main.cpp 并链接 libwebrtc.a 时,链接器会打开 libwebrtc.a 这个大零件盒。
    • 它会仔细分析你的 main.cpp 需要哪些零件( .o 文件),然后只把这些被用到的零件的机器码,原封不动地复制到你的最终可执行文件中 。
    • 结果 :你的可执行文件体积很大,因为它包含了所有它需要的 WebRTC 代码。
  • 使用动态库 ( .so )
    • 当你编译自己的 main.cpp 并链接 libwebrtc.so 时,链接器做的工作非常少。
    • 它只是在你的可执行文件里记下一个“小纸条”:“我需要 libwebrtc.so 这个文件才能运行”。它 完全不复制任何代码。专业上我们称之为 符号存根 (Symbol Stubs) 或 导入表 (Import Table) 。这些“纸条”上写着:“我需要一个叫 webrtc::CreatePeerConnectionFactory 的函数,它在 libwebrtc.so 这个文件里”。
    • 工作交给操作系统 :当你运行你的程序时,操作系统里的动态链接器 (Dynamic Linker/Loader) 会看到这些“小纸条”。它会去系统指定的路径下查找 libwebrtc.so 文件,把它加载到内存里。然后,它会把你程序里所有指向 CreatePeerConnectionFactory 的调用,都正确地指向内存中 libwebrtc.so 里的真实函数地址。
    • 共享与效率 :这种方式最大的好处是共享。如果你的系统上有 10 个程序都用到了 libwebrtc.so ,内存中只需要加载一份 libwebrtc.so 的代码,所有程序都可以共享使用它。这也意味着,如果 libwebrtc.so 有个 bug 需要修复,你只需要更新这一个 .so 文件,所有依赖它的程序就都自动用上新版本了,无需重新编译。
    • 结果 :你的可执行文件体积很小。但它在运行时,必须能找到 libwebrtc.so 文件,由操作系统把它和你的程序一起加载到内存中,这时才真正完成“链接”。

部署AppRTC Server

下载的源码里面可以看到有很多appr.tc的域名

这是demo与appr.tc的后端进行交互,请求stun/turn服务地址,还有加入房间和离开房间等一些房间逻辑。由于官方不再维护appr.tc官网,推荐使用docker本地部署,就可以进行本地调试了。

构建 apprtc server 的 docker 镜像

  1. 仓库地址:https://gitee.com/damn_code/apprtc-server.git 这是 fork 的谷歌apprtc server仓库。我的仓库已经对仓库中的部分源代码文件进行了修改简化。
  2. 进入到apprtc-server目录,修改dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
####### 最终生产版:修复了所有已知问题 #######

FROM golang:1.17.5-alpine3.15

ARG CACHE_BUSTER
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
# (关键修复) 安装 go get 命令所必需的 git 工具。
RUN apk add --no-cache git curl python2 build-base openssl-dev openssl

# --- 缓存重量级依赖 ---
RUN curl https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-367.0.0-linux-x86_64.tar.gz --output gcloud.tar.gz \
&& tar -xf gcloud.tar.gz -C / \
&& rm -f gcloud.tar.gz \
&& /google-cloud-sdk/bin/gcloud components install app-engine-python-extras app-engine-python --quiet
ENV STUNNEL_VERSION 5.60
RUN curl https://www.stunnel.org/archive/5.x/stunnel-${STUNNEL_VERSION}.tar.gz --output /tmp/stunnel.tar.gz\
&& tar -xf /tmp/stunnel.tar.gz -C /tmp \
&& cd /tmp/stunnel-${STUNNEL_VERSION} \
&& ./configure --prefix=/usr && make && make install \
&& rm -rf /tmp/stunnel*

# --- 克隆并构建代码 ---
RUN echo "Cache buster: ${CACHE_BUSTER}" && git clone https://gitee.com/damn_code/apprtc-server.git /apprtc
WORKDIR /apprtc

ENV GOPATH /go
ENV GO111MODULE off
# (关键修复) 在编译前,强制、显式地安装最关键的 websocket 依赖。
RUN go get -v golang.org/x/net/websocket

RUN mkdir -p $GOPATH/src \
&& cp -r src/collider/collider $GOPATH/src/ \
&& cp -r src/collider/collidermain $GOPATH/src/ \
&& cp -r src/collider/collidertest $GOPATH/src/ \
&& cd $GOPATH/src/collidermain \
&& go install .

RUN python build/build_app_engine_package.py src/ out/
RUN curl -L https://webrtc.github.io/adapter/adapter-latest.js --output src/web_app/js/adapter.js
RUN cp src/web_app/js/*.js out/js/

# --- 配置和启动 ---
RUN mkdir /cert
RUN openssl req -x509 -out /cert/cert.crt -keyout /cert/key.pem \
-newkey rsa:2048 -nodes -sha256 \
-subj '/CN=appr.tc' -extensions EXT -config <( \
printf "[dn]\nCN=appr.tc\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:appr.tc\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth") \
&& cat /cert/key.pem > /cert/cert.pem \
&& cat /cert/cert.crt >> /cert/cert.pem \
&& chmod 600 /cert/cert.pem /cert/key.pem /cert/cert.crt

RUN echo 'foreground=yes' > /usr/etc/stunnel/stunnel.conf \
&& echo '[AppRTC GAE]' >> /usr/etc/stunnel/stunnel.conf \
&& echo 'accept=0.0.0.0:443' >> /usr/etc/stunnel/stunnel.conf \
&& echo 'connect=0.0.0.0:8080' >> /usr/etc/stunnel/stunnel.conf \
&& echo 'cert=/cert/cert.pem' >> /usr/etc/stunnel/stunnel.conf

# 恢复为正常的、所有服务都在后台运行的启动脚本。
RUN echo '#!/bin/sh' > /go/start.sh \
&& echo '/google-cloud-sdk/bin/dev_appserver.py --host 0.0.0.0 --addn_host appr.tc /apprtc/out/app.yaml &' >> /go/start.sh \
&& echo '/go/bin/collidermain -port=8089 -tls=false -room-server=http://localhost:8080 &' >> /go/start.sh \
&& echo '/usr/bin/stunnel &' >> /go/start.sh \
&& echo 'wait -n' >> /go/start.sh \
&& echo 'exit $?' >> /go/start.sh \
&& chmod +x /go/start.sh

CMD /go/start.sh
  1. 脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/bin/bash

# 当任何命令失败时,立即退出脚本
set -e

# --- 变量定义 ---
IMAGE_NAME="nmrtc_docker"
CONTAINER_NAME="apprtc-server"

# --- 1. 构建 Docker 镜像 ---
echo "正在构建 Docker 镜像 (强制重新拉取代码)..."
# 我们传递一个每次都不同的 CACHE_BUSTER 参数,来强制 Docker 重新执行 git clone 步骤。
docker build --build-arg CACHE_BUSTER=$(date +%s) . -t $IMAGE_NAME

# --- 2. 停止并移除旧容器 (如果存在) ---
echo "正在清理旧的容器..."
docker stop $CONTAINER_NAME > /dev/null 2>&1 || true
docker rm $CONTAINER_NAME > /dev/null 2>&1 || true

# --- 3. 在后台启动新容器 ---
echo "正在启动新容器: $CONTAINER_NAME..."
docker run -d -p 443:443 -p 8089:8089 --name $CONTAINER_NAME $IMAGE_NAME

# --- 4. 等待容器启动并拷贝新证书 ---
echo "正在等待容器初始化..."
sleep 5 # 等待5秒,确保容器内的服务已启动

echo "正在从容器中拷贝新生成的证书..."
docker cp $CONTAINER_NAME:/cert/cert.crt .

echo ""
echo "--- 脚本执行完毕 ---"
echo "一份全新的证书 'cert.crt' 已被拷贝到当前目录。"
echo "请您手动在 macOS 的“钥匙串访问”中,删除所有旧的 appr.tc 证书,然后添加并信任这个新的证书。"
echo ""
echo "服务正在后台运行。如果您想查看实时日志,请打开新终端并执行:"
echo "docker logs -f $CONTAINER_NAME"
echo ""
echo "当您测试完毕后,请在终端执行以下命令来停止服务:"
echo "docker stop $CONTAINER_NAME"


  1. 修改constants.py文件,使用自己的stun/turn服务器
1
2
3
4
5
6
7
8
ICE_SERVER_OVERRIDE = [
{'urls': ['stun:stun.nmrtc.cn:10086']},
{
'urls': ['turn:turn.nmrtc.cn:10086'],
'username': 'nmrtc',
'credential': 'Fengcheng_1992'
}
]
  1. 修改电脑hosts文件,将客户端源代码中的appr.tc域名重定向到本地127.0.0.1。在本地hosts文件内新增下面一行。hosts文件在:/etc/hosts
1
2
# webrtc 源码使用的域名,全部转为本地的nmrtc_docker服务
127.0.0.1 appr.tc
  1. 每次脚本运行完成,都得需要双击证书,在钥匙串里设置信任。

保存镜像

如何打包并分享您的 nmrtc_docker 镜像

这个过程分为两步:在您当前的电脑上 保存 镜像,以及在您的另一台电脑上 加载并运行

  1. 保存镜像 :执行以下命令,将我们已经构建好的 nmrtc_docker 镜像,保存成一个名为 nmrtc_docker.tar 的文件。
    1
    docker save -o nmrtc_docker.tar nmrtc_docker:latest
    • -o 参数代表“输出 (output)”。
    • 执行完毕后,您会在 /Users/bytedance/Documents/Gitee/apprtc 目录下,看到一个几百兆大小的 nmrtc_docker.tar 文件。
      现在,这个 nmrtc_docker.tar 文件,就是您那个 包含了所有修复、可随处运行的 AppRTC 服务器 。您可以通过 U 盘、移动硬盘、或者网络,将它拷贝到您的任何一台其他电脑上。
  2. 第二步:在您的另一台电脑上,加载并运行镜像
    在您的另一台电脑上,您 不需要 再下载我们的 Gitee 仓库,也 不需要 再运行 build.sh 脚本。您只需要:
    1
    docker load -i nmrtc_docker.tar
    • -i 参数代表“输入 (input)”。
  3. 运行容器 :执行以下命令,用这个刚刚加载的镜像,来启动一个和您当前电脑上 一模一样 的 AppRTC 服务器容器。
    1
    docker run -d -p 443:443 -p 8089:8089 --name apprtc-server nmrtc_docker:latest
    • -d 代表在后台运行。
    • -p 代表端口映射。
    • –name 给容器命名,方便管理。
  4. 拷贝并信任证书 :为了让新电脑上的客户端能够连接,我们还需要从刚刚启动的容器中,把证书拷贝出来。
    1
    docker cp apprtc-server:/cert/cert.crt .
    • 这个命令执行后,您会在当前目录下,看到一个 cert.crt 文件。
    • 请您像之前一样,在您的新电脑上,将这个证书添加到“钥匙串访问”中,并设置为“始终信任”。

至此,所有步骤都已完成!


webrtc最佳实践
http://alberthello.github.io/2026/04/23/webrtc/get-build-run/
作者
fengcheng
发布于
2026年4月23日
更新于
2026年4月23日
许可协议