本文最后更新于 2026-04-23T17:56:32+08:00
webrtc下载+编译+运行 本机环境 本机为Mac M2 os14.4版本,iphone12mini-iOS16.4,vpn
下载 首先python2 和 python3 都得安装。去官网下载 安装包。
macos 自带Python3,默认安装在 /usr/bin/pyhon3,手动下载的Python2安装包,最终会把Python2安装在 /usr/local/bin/python
查看可用版本分支 git branch -r
切换到某个分支,比如m79分支 git checkout branch-heads/m79 再 gclient sync -D
或者强制切换到指定commit(b484ec0082948ae086c2ba4142b4d2bf8bc4dd4b是m79最后一次提交的commit id)gclient sync -r b484ec0082948ae086c2ba4142b4d2bf8bc4dd4b --force
执行 fetch –nohooks webrtc_ios可能出现的错误
.cipd_bin/vpython3: No such file or directory
是代理的原因,终端执行如下命令:
执行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版本 下载完后的目录结构是这样:
开始执行编译命令:
运行 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 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=noninteractiveRUN 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/* RUN git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git /opt/depot_tools ENV PATH="/opt/depot_tools:${PATH}" RUN pip3 install httplib2 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 镜像
仓库地址:https://gitee.com/damn_code/apprtc-server.git 这是 fork 的谷歌apprtc server仓库。我的仓库已经对仓库中的部分源代码文件进行了修改简化。
进入到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_BUSTERRUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories 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 /goENV GO111MODULE offRUN 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 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"
修改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' } ]
修改电脑hosts文件,将客户端源代码中的appr.tc域名重定向到本地127.0.0.1。在本地hosts文件内新增下面一行。hosts文件在:/etc/hosts
1 2 # webrtc 源码使用的域名,全部转为本地的nmrtc_docker服务127.0.0.1 appr.tc
每次脚本运行完成,都得需要双击证书,在钥匙串里设置信任。
保存镜像 如何打包并分享您的 nmrtc_docker 镜像 这个过程分为两步:在您当前的电脑上 保存 镜像,以及在您的另一台电脑上 加载并运行
保存镜像 :执行以下命令,将我们已经构建好的 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 盘、移动硬盘、或者网络,将它拷贝到您的任何一台其他电脑上。
第二步:在您的另一台电脑上,加载并运行镜像 在您的另一台电脑上,您 不需要 再下载我们的 Gitee 仓库,也 不需要 再运行 build.sh 脚本。您只需要:1 docker load -i nmrtc_docker.tar
运行容器 :执行以下命令,用这个刚刚加载的镜像,来启动一个和您当前电脑上 一模一样 的 AppRTC 服务器容器。1 docker run -d -p 443 :443 -p 8089 :8089 --name apprtc-server nmrtc_docker:latest
-d 代表在后台运行。
-p 代表端口映射。
–name 给容器命名,方便管理。
拷贝并信任证书 :为了让新电脑上的客户端能够连接,我们还需要从刚刚启动的容器中,把证书拷贝出来。1 docker cp apprtc-server:/cert/cert.crt .
这个命令执行后,您会在当前目录下,看到一个 cert.crt 文件。
请您像之前一样,在您的新电脑上,将这个证书添加到“钥匙串访问”中,并设置为“始终信任”。
至此,所有步骤都已完成!