从“win10+vs2019编译 webrtc”这个标题说起,我猜你多半是被工作逼到了这一步:要么需要给产线接入WebRTC做音视频通信,要么想在老项目里集成实时传输能力,要么想在源码层面改点东西但官方预编译包不开放。无论哪一种,只要你在Windows上点开过WebRTC的源码仓库,就会明白“下载容易编译难”这句话不是开玩笑的。这篇文章把我自己在Win10上用VS2019把WebRTC从源码编译成可用的库和Demo的完整过程记录下来,包括环境准备、工具链配置、同步代码、生成工程、踩坑记录和产物验证,给正准备动手或已经卡在半路的你一个能直接参考的路径。
顺便先说结论:这套组合编译WebRTC是完全可行的,而且VS2019对老项目的兼容性比VS2022更友好,很多企业级代码库还停留在v142工具集,用VS2019编译出来的库能直接接入既有工程,省去升级整个解决方案的麻烦。但整个流程里有不少细节坑,错一个可能就白等几个小时,下面我按实际操作顺序把关键点都拆开讲。
1. 为什么要自己编译WebRTC,而不是用现成的包
1.1 预编译包满足不了你的几个典型场景
WebRTC虽然开源,但官方对外的二进制包更新节奏和分发渠道都比较受限。你在实际项目里如果遇到下面这些情况,基本就只能走自己编译这条路:
- 需要开启或关闭特定的编译宏,比如h264编解码支持、音频设备模块自定义、日志等级调整、部分平台特性开关等。官方预编译包的参数是固定的,你没法按自己的业务场景裁剪。
- 需要把WebRTC作为静态库链接进公司内部的一套旧架构里,而旧架构用的是VS2019的v142工具链,预编译包和你的工程格式对不上。
- 需要调试音视频底层逻辑,比如自己改RTP打包策略、调拥塞控制算法、替换加密模块。不编译出带调试符号的版本,你在源码里下的断点根本进不去。
- 需要把特定版本固定进自己的发布流程里,保证所有开发者用的是同一套代码和同样的编译参数。自己编译才能做到版本可控、参数可控、发布可控。
1.2 这套组合对很多团队其实是“最优解”
先说Win10。虽然Mac和Linux编译WebRTC会省掉一些Windows特有的坑,但对绝大多数做客户端开发的团队来说,主力开发机就是Windows,尤其是做音视频SDK、游戏语音、视频会议、安防监控的团队,产出物最终要跑在Windows平台的占比很高。直接用Windows编译,至少你调试的时候不需要在虚拟机里来回切。
再说VS2019。WebRTC官方文档倾向于支持较新的Visual Studio版本,但VS2019在国内企业环境里存量巨大,很多公司的C++工程还没迁移到VS2022。用VS2019编译出来的WebRTC库,接入这类老工程时不会遇到“工具集版本不匹配”这种让人头大的问题。我自己实测下来,VS2019 v16.11系列配合对应的Windows 10 SDK是稳定的,重点是要把组件装对,这个后面细说。
1.3 在动手之前先想清楚三件事
第一,磁盘空间。WebRTC源码同步下来大概20GB往上,编译过程中的中间文件和产物还会再占几十GB。我建议你至少预留80GB可用空间,并且把工作目录放到NTFS分区。如果你只有一块256GB的固态,那编译前先清一清盘,否则中途磁盘满报错会让你欲哭无泪。
第二,内存和CPU。全量编译WebRTC是个重活,8核16线程的CPU加16GB内存属于“能跑但慢”,32GB内存加多核处理器会舒服很多。编译的时候开太多并行任务,内存不够会直接触发编译器崩溃,这个后面在常见问题里会提到。
第三,网络稳定性。WebRTC源码仓库非常大,同步过程要拉取几万个历史提交,中途断网不处理的话,gclient同步出来的目录状态会非常“脏”。建议在开始之前连一个稳定的网络,并且做好“失败重试几次”的心理准备,这不丢人,我自己第一次同步也重试了四次才成功。
2. 环境准备:先把这5个环节装对,后面少踩一半坑
2.1 Win10系统版本与基础环境
理论上Win10 1809之后的版本都可以,但为了兼容性,我建议至少用Win10 1903以上的版本,最好更新到最新的补丁。这里有个容易忽略的点:Windows的“开发者模式”建议打开,它在处理符号链接、长路径、部分开发工具权限时能省去很多麻烦。
另外确认你的系统用户名是英文,路径里不要带中文和空格。depot_tools和WebRTC的构建脚本对路径空格处理得很差,我见过有人在“C:\Users\张三\webrtc”这个路径下编译,结果gn生成工程阶段反复报奇怪的错误。工作目录建议直接放在盘符根目录下,比如“D:\webrtc”,干净利落。
2.2 VS2019安装时必修的组件清单
VS2019的安装界面组件很多,但编译WebRTC真正必需的是下面这几个:
- 使用C++的桌面开发(这是主工作负载,必须选)
- MSVC v142 - VS 2019 C++ x64/x86生成工具
- Windows 10 SDK,建议选10.0.19041.0或更高版本
- 适用于最新v142生成工具的C++ ATL,有些旧工程会用到,装了不亏
- 单个组件里找一下“Windows SDK”相关的,确认选中的版本包含“Desktop Apps”那套头文件
安装完成之后,我强烈建议你在命令行里敲一下“cl”看有没有输出。如果提示找不到,说明环境变量还没生效,或者你需要打开“开发者命令提示符”而不是普通的cmd。很多人卡在“gn找不到VS”这一步,就是因为VS装了但C++工具链没装全,或者只装了VSCode没装完整的Visual Studio。
2.3 depot_tools:WebRTC编译的“总开关”
depot_tools是Chromium/WebRTC体系下的工具集,负责拉代码、同步依赖、调用gn和ninja。它本质上是一堆Python脚本加几个可执行文件,你需要做的是:
- 从官方Git仓库把depot_tools克隆到本地,比如“D:\depot_tools”。
- 把“D:\depot_tools”加到系统PATH环境变量里,而且必须放在最前面,排在Python和Git之前。
- 创建系统环境变量DEPOT_TOOLS_WIN_TOOLCHAIN,值设为0。这个变量的作用是告诉工具链不要走自动下载的配置,直接用你本机VS2019安装的组件。如果你不设这个变量,它默认会尝试下载一个内部工具链,那在非官方网络环境下基本是下不动的。
depot_tools对Python有依赖,WebRTC各版本要求的Python版本不一样。用VS2019编译时,老分支要求Python 2.7,新分支要求Python 3.x。我的建议是直接找一个Python 3.8或3.9的64位版本装上,并且在PATH里把Python路径放在depot_tools后面。怎么确认当前分支要哪个版本?在同步完代码后,depot_tools会在运行时明确提示你。
2.4 Git配置的两个关键设置
WebRTC的同步和提交过程依赖Git,但Windows上的Git有两个设置必须提前改:
- 全局关闭自动换行转换:git config --global core.autocrlf false。如果开着自动转换,WebRTC里大量带Unix换行符的脚本文件会在checkout时被改坏,直接导致后面的gn和ninja行为异常。
- 调大HTTP缓存和低速度限制:git config --global http.postBuffer 524288000,这个值能降低大仓库同步时的HTTP错误概率。另外可以设置git config --global http.lowSpeedLimit 1000和http.lowSpeedTime 60,让Git在网络极端缓慢时快速失败而不是无限期挂起,这样你能及时发现问题并重试。
2.5 确认你的磁盘是NTFS
WebRTC的源码目录里有不少符号链接,老旧的FAT32格式既不支持符号链接,也扛不住大文件。检查方法很简单:右键工作目录所在盘符,看属性里的文件系统,是NTFS就没问题,是FAT32就趁早格式化或换盘。这个事在编译中后期才暴露的话,处理成本极高,因为符号链接文件的创建失败不会立刻报错,而是会静默地产生一堆坏文件,等到链接阶段才到处爆炸。
3. 源码同步:用对命令,少走冤枉路
3.1 初始化并拉取WebRTC源码
depot_tools装好后,在工作目录下执行:
fetch --nohooks webrtc这个命令会做两件事:生成一个叫“.gclient”的配置文件,然后把WebRTC主仓克隆到本地。参数“--nohooks”的意思是先不执行hooks,因为hooks要等依赖同步完才能跑,拆开做更稳妥。
等fetch命令结束,你会看到src目录出现。然后进入src目录,执行:
gclient sync --with_branch_heads --with_tags这一步会把WebRTC的大量子模块依赖都拉下来,包括很多第三方库、音频处理库、编解码库、测试框架等。整个同步过程在普通网络下可能需要几个小时,取决于你的带宽和磁盘速度。
这里有个小技巧:如果你只想要某个特定的release分支,建议先fetch完整主线,再用“git checkout”切到目标分支。因为WebRTC的分支基于完整历史,直接拉分支容易漏掉一些中间commit,导致后续sync失败。先全量同步再切分支,后面依赖的版本反而更稳定。
3.2 搞清楚你同步的是哪个版本的代码
同步完成后,在src目录下执行:
git log -1 --oneline你会看到一个很长的commit号,比如“xxxxxxx Revert '...这类提交信息”。这个commit号就是你的代码版本。如果你之后要把编译产物发布给团队其他人,必须把这个commit号固定下来,因为WebRTC是滚动发布的,不同commit的API和编译参数差异可能很大。
在git分支列表里,release分支一般长这样:branch-heads/xxx。如果你想用某个稳定版本,建议切到branch-heads版本分支,而不是用主干的某个commit。主干是每天都在变的,API可能今天还能编译过,下周就废了。
3.3 同步失败和中断怎么处理
同步过程中最常见的报错是网络超时和断线。第一次遇到时不用慌,重新执行:
gclient sync它能断点续传,已经下载的部分不会重新拉。如果反复在同一个地方失败,可以试试加参数:
gclient sync --force --delete_unversioned_trees这个命令会对未纳入版本管理的文件做清理,同时重新校验依赖状态。但要注意,“--delete_unversioned_trees”会删掉你不小心放进源码目录的文件,所以如果在src下面放了自己写的测试脚本,执行之前先备份。
另一个常见问题是hooks执行失败。如果sync的最后阶段提示hooks相关错误,可以单独执行:
gclient runhooks把所有依赖同步到位后手动补跑hooks,通常能解决这类问题。
3.4 同步后检查两件事
第一,确认第三 pihak目录齐全。WebRTC源码里有很多submodule,如果同步不完整,src/third_party下会缺目录或只有空壳。缺依赖的后果是gn生成工程时各类“找不到头文件”的报错,而且这种报错经常是连环的,看起来是编译错误,实际是同步没完成。
第二,确认bin目录下有vpython和python等工具。depot_tools在同步后会在src目录下生成环境相关的文件,如果这些基础工具缺失,后续gn命令会直接失败。检查方式是直接输入“python --version”看输出是否正常,以及“gn --version”看工具是否可用。
4. 生成VS2019工程:gn参数决定你后面几个小时的命运
4.1 理解gn和ninja的关系
gn是Chromium体系下的构建系统生成器,它读取你写好的构建参数,生成ninja构建文件。ninja是实际调用编译器、链接器干活的东西。我们用VS2019编译WebRTC,其实是让gn生成一个适配VS2019的构建目录,然后用ninja在后台编译。VS的IDE只是壳,真正干苦力的是ninja和cl.exe。
这套方式的核心优势是编译速度快。VS的原生工程格式在项目规模巨大时加载和分析非常慢,而gn生成的文件为ninja做了高度优化,增量编译时只重新构建真正变化的文件,节省的时间非常可观。
4.2 第一个gn命令:生成VS2019解决方案
进入src目录后,执行:
gn gen out/win --ide=vs2019这条命令会在src/out/win下生成构建目录,同时生成一个名为“all.sln”的VS解决方案文件。打开这个sln,你能在VS里看到WebRTC的工程全貌,但注意,这个sln不是让你“点生成解决方案”来用的,在VS里编译全量会非常慢,而且VS的MSBuild对WebRTC这种规模的工程支持并不好。
4.3 必配参数:gn args的精简配置
裸跑gn gen只能得到默认Debug参数,你还需要用gn args调整关键选项。打开“out/win/args.gn”文件,加入下面几行:
is_debug = false target_cpu = "x64" is_component_build = false enable_iterator_debugging = false rtc_use_h264 = true proprietary_codecs = true ffmpeg_branding = "Chrome" symbol_level = 1逐条说下为什么这么配:
- is_debug = false:发布Release版,编译耗时比Debug短,生成的库运行时效率也高。你在调试自己业务代码时,不需要WebRTC内部带完整调试符号,Release加外部符号足够。
- target_cpu = "x64":如果你只做64位应用,就设x64。需要32位就设“x86”,但建议不要默认出一次就编译两个架构,机器扛不住。
- is_component_build = false:产出静态库。组件模式下生成一堆dll,调试时部署麻烦,发布时也要带一堆运行库。静态库模式下生成一个大webrtc.lib或若干静态库,虽然在链接环节慢一点,但发布最省心。
- enable_iterator_debugging = false:关闭迭代器调试,这个必须关,不然链接自己的工程时会有无数“迭代器调试级别不一致”的坑。
- rtc_use_h264和proprietary_codecs、ffmpeg_branding:这三件套是开启H264编解码支持的。WebRTC默认不带H264(专利问题),但国内做音视频的几乎没有不用H264的,所以打开。
- symbol_level = 1:只生成关键符号,不生成全部调试信息。能显著减小产物体积,同时对正常调试影响不大。
4.4 生成构建文件并检查输出
写好args.gn后,回到命令行执行:
gn gen out/win它会自动读取参数、对比源码状态、生成ninja文件。如果参数有冲突或者系统工具缺失,这里会直接报错。常见的“ERROR at //build/config/win/BUILD.gn”多半就是VS组件缺失。
执行成功后,用“gn args out/win --list”可以查看当前生效的所有参数,这是一个很好的自查手段。我第一次配置时对某个参数不确定,就是用这个命令确认的,它能省去大量翻阅文档的时间。
5. 编译过程:从第一次忍受到增量编译的快乐
5.1 建议用ninja而不是IDE编译
既然你是用VS2019,可能会习惯性地打开all.sln点“生成”。我的建议是:可以打开看工程结构,但真正编译请用ninja。在src目录下执行:
autoninja -C out/winautoninja是ninja的封装,能自动判断并行任务数。如果没有autoninja,直接执行“ninja -C out/win”也可以,但要手动加-j参数控制并行度。比如你的机器是8核16线程,建议用“ninja -C out/win -j 8”,留一些线程给系统响应,避免整机卡死。
第一次全量编译的时间取决于机器配置,快的一小时,慢的四五小时都有可能。中途别盲目打断,除非你确认编译卡死,而不是单纯在编译某个巨型文件。
5.2 编译过程中这些现象都是正常的
编译日志里会有一堆warning,比如“warning C4244”“warning C4305”这类类型转换警告。只要不报error,就可以放心让它跑。WebRTC自身代码量巨大,跨平台宏满天飞,有warning太正常了。不要手贱去开“把警告当错误”,否则你会被上千个warning淹没。
你还可能看到一些编译命令在反复执行同一个文件,这是ninja的依赖机制在检查头文件变化,属正常行为。另外,某些第三方库(比如音频处理库、FFmpeg)在编译时会调用额外脚本生成配置头文件,中途会有短暂的看似停顿,其实是在跑生成器。
5.3 增量编译的使用姿势
WebRTC全量编译一次很贵,但增量编译非常快。你修改一个源文件后重新执行autoninja,通常几十秒到几分钟就能完成。这让你可以放心在源码上做实验,改坏了就改回来重编,成本可控。
我在实际开发中喜欢做这样一套流程:先用gn gen生成一个默认的out/win,编译通过后,再复制一份out/win_custom,只修改自定义参数。这样既保留了原始可用版本,又不会因为参数改错毁了整个构建目录。复制构建目录记得用“gn gen”重新生成一次,不要直接拷贝ninja文件,因为里面都是绝对路径。
5.4 编译产物在哪里
编译完成后,所有可执行文件和库文件都在out/win目录下。常见的有:
- webrtc.lib:WebRTC主静态库,集成时核心链接目标。
- peerconnection_client.exe:示例客户端,可以拿来跑一下验证WebRTC功能。
- peerconnection_server.exe:示例信令服务器,配合上面的客户端做双人通话演示。
- 各类测试可执行文件,比如audio_device_test.exe等,用于验证特定模块。
链接库时要注意,静态库不是只链一个webrtc.lib就够了,WebRTC还依赖一堆系统库和第三方库,比如Winmm.lib、Secur32.lib、d3d11.lib等。你需要在工程配置里把这些系统库都加上,具体清单在后续章节给一份参考。
6. 链接自己的工程:静态库集成实操
6.1 配置头文件和库目录
在自己的VS2019工程里,需要把WebRTC的src目录和out/win目录加入包含路径。一般这么配:
- C/C++ -> 常规 -> 附加包含目录:D:\webrtc\src、D:\webrtc\src\out\win
- 链接器 -> 常规 -> 附加库目录:D:\webrtc\src\out\win\obj
注意,WebRTC的头文件目录是分散的,但核心头文件在src/api、src/rtc_base、src/modules等子目录下。gn生成构建目录时会把很多生成的配置头文件放在out/win目录里,所以这两个路径缺一不可。
6.2 链接阶段要补的系统依赖
我第一次集成时只加了webrtc.lib,结果链接器疯狂报“无法解析的外部符号”。后来挨个补依赖,才把符号全部对上。这里给一份常见的系统库清单:
webrtc.lib Winmm.lib Secur32.lib Dnsapi.lib Iphlpapi.lib ws2_32.lib Advapi32.lib Ole32.lib OleAut32.lib User32.lib Gdi32.lib Crypt32.lib d3d11.lib dxgi.lib如果你的业务代码用到了音频设备相关模块,可能还要加winmm、ole32等库。具体怎么判断?链接时报哪个符号找不到,就搜一下它属于哪个系统库,然后补上就行。这个过程不算难,但需要耐心。
6.3 在C++代码里初始化WebRTC
代码接入时,核心就几个步骤:创建PeerConnectionFactory、创建PeerConnection、添加音视频轨、处理信令。WebRTC的API比较稳定,关键是要注意线程模型。在Windows上,很多回调会发生在WebRTC内部的工作线程里,你更新UI或操作其他COM组件时,需要自己切回UI线程。
还有一个容易踩的坑:不要在你的主线程里做阻塞操作。WebRTC内部有大量锁和等待逻辑,你在主线程里卡住,很可能连带把WebRTC的工作线程带崩,表现就是画面卡死、音频断断续续或者直接触发断言。
6.4 用webrtc自带的Demo验证环境
在开始写自己的业务代码之前,先用编译出来的peerconnection_client和peerconnection_server跑一次通话Demo,这是验证整套工具链是否可用的最快方式。跑起来看到两个窗口互相显示对方画面,说明你的编译是完整可用的,后面写业务代码时出了bug,可以先排除编译环境问题。
7. 常见问题与排查技巧实录
7.1 源码同步阶段的问题速查
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| gclient sync卡住不动 | 网络波动,连接挂起 | Ctrl+C中断后重新执行sync,利用断点续传 |
| 报错“fatal: unable to access...” | 网络不稳定或仓库地址异常 | 重试,或者检查本地Git配置是否正常 |
| hooks执行失败 | 依赖子模块没同步完整 | gclient sync --force 后执行 gclient runhooks |
| 同步后third_party下某个目录为空 | 子模块缺失 | 用gclient sync重新迭代同步,不要手动补文件 |
| depot_tools和Python版本冲突 | PATH里Python版本不对 | 调整PATH顺序,让depot_tools优先,或安装指定Python版本 |
7.2 gn生成阶段的问题速查
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| ERROR: Unable to find a Visual Studio installation | VS组件没装全,或环境变量不对 | 确认C++桌面开发组件,确认DEPOT_TOOLS_WIN_TOOLCHAIN=0 |
| 找不到Windows SDK | SDK版本过旧或不匹配 | 安装10.0.19041或更高版本,并在gn args里用windows_sdk_version指定 |
| 报Python相关错误 | Python版本和分支要求不符 | 检查当前分支要求,安装对应Python版本 |
| args.gn报语法错误 | 参数名拼写错误或值格式不对 | gn args --list查合法参数名,对照修改 |
7.3 编译链接阶段的问题速查
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 编译到某个文件时内存不足崩溃 | 并行度太高,32位编译内存爆掉 | 减少-j并行数,关掉占用内存的软件,尽量用64位编译 |
| 链接器LNK1104无法打开文件 | 杀毒软件锁文件,或磁盘满 | 关掉杀毒实时监控,清理磁盘空间 |
| 大量LNK2001未解析的外部符号 | 依赖库缺失,或SDK版本不统一 | 按6.2清单补齐系统库,检查所有工程使用同一套SDK |
| 编译时rc.exe报错 | Windows SDK的rc.exe路径未加入PATH | 在PATH中加入Windows SDK的bin目录,或在VS开发者命令行中操作 |
| 编译过程中系统假死 | 并行编译任务太多 | 用autoninja或手动控制-j参数,给系统留余量 |
| 生成工程报“Invalid character” | args.gn里包含了注释之外的非法字符 | 检查参数文件,删除BOM头和不可见字符 |
7.4 一个让我浪费两个小时的坑:符号链接权限
WebRTC源码里存在符号链接,Windows上创建符号链接默认需要管理员权限。如果你的开发命令行不是管理员权限,某些符号链接创建失败,gn生成阶段可能不报错,但编译到某一个模块时会突然报“找不到头文件”。排查了很久才发现是符号链接的锅。解决办法是:用管理员身份打开命令行执行gn和ninja,或者把“开发者模式”打开,让普通权限进程也能创建符号链接。
7.5 另一个隐形坑:重复编译不同架构
有人图省事,想一次性编译x86和x64两个版本,手动改target_cpu后重新gn gen。结果两个构建目录相互干扰,最后两个版本都不稳定。正确做法是建立两个独立的输出目录,比如out/win_x64和out/win_x86,分别设置target_cpu,各自gn gen和编译。构建目录之间的隔离性非常重要,永远不要在同一个构建目录里反复横跳改架构参数。
8. 后续扩展:从“编译通过”到“能上生产”
8.1 编译通过不是终点,验证接入才是关键
很多人把“编译通过”当成大功告成,结果集成了两周还没跑通音视频通话。问题往往不在于WebRTC本身,而在于信令流程、NAT穿透、ICE配置这些外围环节。所以我建议你在编译通过的兴奋劲过后,立刻做一个最小可用的Demo:两端建立连接、互相推流、能看到画面。这个最小闭环跑通了,后面加业务逻辑才有底气。
8.2 性能优化和产物体积控制
如果要把WebRTC集成到正式产品里,Release编译只是第一步。你还可以关注几个参数来优化产物:
- symbol_level改成0可以进一步减小体积,但调试难度会上升。
- rtc_include_tests = false可以剔除大量测试代码,减小库体积。
- rtc_build_examples = false不生成示例工程,如果你不需要Demo,可以省掉这部分编译时间。
- 可以尝试is_component_build = true在调试阶段加速链接,但发布时必须改回false。
这些参数在args.gn里改完,重新gn gen再编译即可。注意参数变更后最好执行一次“ninja -C out/win -t clean”清理旧产物,否则可能出现改一个参数但部分旧对象文件残留的情况。
8.3 如何跟上WebRTC的版本迭代
WebRTC版本迭代很快,API也在持续变化。如果你们的项目长期维护,建议固定一个release分支,不要追主干。每个季度评估一次要不要升级,升级时先在测试环境完整验证一遍音视频效果、内存占用、CPU占用,再决定是否合并到主线。这个策略能避免“升级一时爽,回归火葬场”的尴尬局面。
我个人在实际操作中的体会是:第一次编译WebRTC时,几乎每个人都会在某一步卡住,这不是能力问题,而是这个工具链的复杂性超出常规C++项目太多。但当你完整走过一遍这套流程后,再去接触其他大型C++开源项目,会感觉从容很多。如果时间紧迫,强烈建议照着我上面的配置先跑通一个最小配置,不要着一开始就加各种自定义参数。先编译出能跑的版本,再考虑裁剪和优化。
最后分享一个小技巧:把编译用的命令行和参数都整理成一个bat或PowerShell脚本,存到项目仓库里。这样团队里新同学入职时,照着脚本敲一遍就能复现编译环境,不用每次靠文档考古。我自己就是踩过“环境配置只存在于某个人脑子里”这种坑之后,才养成了这个习惯。希望对你有帮助。