在 Windows 上编译 baresip 4.12.0:一个 1.3MB 单文件 SIP 软电话的诞生记

在 Windows 上编译 baresip 4.12.0:一个 1.3MB 单文件 SIP 软电话的诞生记

环境:Windows 11 + Visual Studio 2026 Community(MSVC 14.51)
产物:baresip.exe(x64 / 静态 CRT / 无任何第三方 DLL 依赖 / 约 1.3 MB)
用途:对接 FreeSWITCH 做语音调度联调,与 RK3588 板端 baresip 同一套版本


0. 为什么要在 Windows 上自己编译

baresip 官方并不提供 Windows 二进制包,社区流传的旧版要么缺 Opus、要么拖着
一堆 DLL。我们的诉求很简单:

  • 单文件:一个 exe 拷到哪都能跑,不装运行库(静态链接 MSVC 运行时)
  • 带 Opus:和板端、FreeSWITCH 协商编码一致
  • 带 wasapi:用 Windows 原生音频接口,不要 portaudio/sdl 那套

最终交付物验证数据(后文有复现命令):

baresip.exe  1,338,880 字节  (约 1.3 MB)
SHA256: 6b984658f89d1df8933ff1a51062d731618beec664fd76e7484e2bc66baa589f
machine: 8664 (x64)   subsystem: 3 (Windows CUI 控制台程序)
依赖 DLL 仅 8 个系统库:
  qwave.dll / IPHLPAPI.DLL / WSOCK32.dll / WS2_32.dll
  ole32.dll / KERNEL32.dll / SHELL32.dll / ADVAPI32.dll

也就是说:没有 MSVCP140.dll、没有 VCRUNTIME140.dll、没有 libopus-0.dll,
双击即用。


1. 环境准备

组件 版本 说明
Visual Studio 2026 Community(目录名 18) 安装「使用 C++ 的桌面开发」工作负载
CMake VS 自带 4.3.1-msvc1 不需要单独装,路径见下
MSVC 工具链 14.51.36231
源码 baresip 4.12.0 / re 4.12.0 / opus 1.5.2 见下

VS 自带的 cmake 在这个路径(版本号 18 对应 VS2026,老版本是 17):

C:\Program Files\Microsoft Visual Studio\18\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe

提示:可以直接把上面这个 ...\CMake\bin 加进 PATH,后面命令就写 cmake。
用 VS 生成器不需要跑 vcvars 脚本,cmake 会自己找工具链。

源码获取

三个仓库的正式发布 tag:

组件 仓库 tag
opus https://github.com/xiph/opus v1.5.2
re(baresip 的网络协议栈) https://github.com/creytiv/re v4.12.0
baresip https://github.com/baresip/baresip v4.12.0

注意:re 4.12.0 起自带 rem 子目录,不需要再单独编译 rem,三步(opus → re → baresip)即可。

国内网络如果拉 GitHub 慢,可以给地址加 gh-proxy 镜像前缀,
或直接用 tarball(baresip-v4.12.0.tar.gz / re-v4.12.0.tar.gz / opus-1.5.2.tar.gz)。

本文假设的目录布局(保持这个结构,后面命令可直接复制):

C:\Code\baresip-win\
├── src\        # 源码:opus-1.5.2 / re-4.12.0 / baresip-4.12.0
├── build\      # 构建目录:opus / re / baresip
├── deps\       # 安装树:include / lib / bin / share(铃声 wav)
└── deliver\    # 最终交付:exe + 配置文件

2. 三步构建

三步的共同思想:全部装进同一个 deps 前缀,后面的步骤从里面找头文件和库。
命令按顺序复制粘贴即可(在任意终端,bash / cmd 都行——注意第 3 坑的引号差异)。

2.1 第一步:Opus 1.5.2

cmake -S C:/Code/baresip-win/src/opus-1.5.2 ^
      -B C:/Code/baresip-win/build/opus ^
      -DCMAKE_INSTALL_PREFIX=C:/Code/baresip-win/deps ^
      -DOPUS_STATIC_RUNTIME=ON

cmake --build   C:/Code/baresip-win/build/opus --config Release
cmake --install C:/Code/baresip-win/build/opus --config Release

坑①:-DOPUS_STATIC_RUNTIME=ON 不能省。
opus 的 CMakeLists.txt(274-280 行附近)会无条件覆写
CMAKE_MSVC_RUNTIME_LIBRARY——你在外层传 CMAKE_MSVC_RUNTIME_LIBRARY 根本
传不进去。它专门提供了 OPUS_STATIC_RUNTIME 这个开关,打开后 opus 才会用
/MT(静态 CRT);不打开就是 /MD,最后链接时 CRT 不匹配报一堆
LNK2038: RuntimeLibrary 不匹配。

2.2 第二步:re(libre)4.12.0

cmake -S C:/Code/baresip-win/src/re-4.12.0 ^
      -B C:/Code/baresip-win/build/re ^
      -DCMAKE_INSTALL_PREFIX=C:/Code/baresip-win/deps ^
      -DLIBRE_BUILD_SHARED=OFF ^
      -DCMAKE_DISABLE_FIND_PACKAGE_OpenSSL=ON ^
      -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$<CONFIG:Debug>:Debug>"

cmake --build   C:/Code/baresip-win/build/re --config Release
cmake --install C:/Code/baresip-win/build/re --config Release

坑②:OpenSSL 必须用 CMAKE_DISABLE_FIND_PACKAGE_OpenSSL=ON 关掉,
-DUSE_OPENSSL=OFF 是无效的。

re 的 cmake/re-config.cmake 里 find_package(OpenSSL) 是无条件执行的,
机器上没装 OpenSSL 会直接配置失败。USE_OPENSSL=OFF 只是后续的功能开关,
管不住这个 find_package。CMAKE_DISABLE_FIND_PACKAGE_* 是 CMake 的原生机制,
设上之后 find_package(OpenSSL) 会当作"找不到"直接跳过(后面 baresip 那步同理)。

LIBRE_BUILD_SHARED=OFF 则保证 re 编成静态库(re-static.lib),配合 -DSTATIC=ON 才能单文件。

2.3 第三步:baresip 4.12.0

cmake -S C:/Code/baresip-win/src/baresip-4.12.0 ^
      -B C:/Code/baresip-win/build/baresip ^
      -DCMAKE_INSTALL_PREFIX=C:/Code/baresip-win/deps ^
      -DCMAKE_PREFIX_PATH=C:/Code/baresip-win/deps ^
      -DSTATIC=ON ^
      -DMODULES="wasapi;wincons;g711;opus;auconv;auresamp;uuid;stun;turn;ice;account;contact;debug_cmd;menu;netroam;ctrl_tcp" ^
      -DCMAKE_DISABLE_FIND_PACKAGE_OpenSSL=ON ^
      -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$<CONFIG:Debug>:Debug>" ^
      -DRE_INCLUDE_DIR=C:/Code/baresip-win/deps/include/re ^
      -DRE_LIBRARY=C:/Code/baresip-win/deps/lib/re-static.lib ^
      -DOPUS_INCLUDE_DIR=C:/Code/baresip-win/deps/include ^
      -DOPUS_LIBRARY=C:/Code/baresip-win/deps/lib/opus.lib

cmake --build   C:/Code/baresip-win/build/baresip --config Release --target baresip_exe
cmake --install C:/Code/baresip-win/build/baresip --config Release

坑③:可执行目标叫 baresip_exe,不是 baresip。
baresip 这个工程里,baresip 是核心静态库的名字,exe 目标叫 baresip_exe。
直接 --target baresip 会构建出一堆库却看不到 exe。

关于 -DMODULES(16 个模块,按需裁):

模块 作用
wasapi Windows 原生音频(采集/播放/提示音)
wincons Windows 控制台交互(就是让你敲 /dial 的那个界面)
g711 / opus 编码:PCMU/PCMA、Opus
auconv / auresamp 采样格式转换、重采样
uuid UUID 生成(SIP 标识用)
stun / turn / ice NAT 穿透(跨网段互通必备)
account / contact 账户与联系人命令
debug_cmd 调试命令
menu 来电接听/回铃音/按键菜单
netroam 网络变化(换 WiFi 重新注册)
ctrl_tcp 开放 TCP 控制端口(用脚本控 baresip 时有用)

cmake --install 会把 exe 装到 deps\bin\,并把铃声 wav 装到 deps\share\baresip\——
铃声路径是安装时写进配置模板的,所以整目录一起拷才不丢铃声。


3. 大坑复盘:$<$...> 的引号写法

CMAKE_MSVC_RUNTIME_LIBRARY 的值里有个生成器表达式 $<$<CONFIG:Debug>:Debug>,
不同终端的引号写法不一样,写错就被 shell 吞掉:

终端 写法 原因
bash / Git Bash 'MultiThreaded$<$<CONFIG:Debug>:Debug>' 单引号防一切展开;< 在 bash 里是重定向符
cmd.exe "MultiThreaded$<$<CONFIG:Debug>:Debug>" 双引号防 < 被当重定向
PowerShell 'MultiThreaded$<$<CONFIG:Debug>:Debug>' 单引号防 $ 被当变量展开

4. Windows 版的一个先天限制:没有命令行参数

MSVC 环境没有 getopt.h,源码里 #ifdef HAVE_GETOPT 会把全部参数解析逻辑剥掉。
后果:

  • -f 配置目录、-m 模块、-e 之类的命令行选项全部不可用;
  • 配置文件路径是写死的(见下一节),只有一个地方生效;
  • 启动就等于:双击 exe,它自己找配置、自己注册。

这不是开关没打开,是 Windows 构建下的既定行为,不用挣扎。


5. 运行时三件套(Windows 上最容易翻车的部分)

5.1 配置真正生效的位置:%APPDATA%\.baresip

这是最坑的一条:exe 所在目录旁边放 config、accounts 是没用的,
baresip 只认:

%APPDATA%\.baresip\config
%APPDATA%\.baresip\accounts
%APPDATA%\.baresip\contacts

也就是 C:\Users\<用户名>\AppData\Roaming\.baresip\。

实战教学:我们把这个 exe 拷给同事用,他双击后控制台显示
Populated 0 accounts(一个账号都没加载)——因为配置还在我这边,
他那边 %APPDATA%\.baresip 是空的。给别人的正确姿势:
exe + config + accounts 两个文件,让对方放到自己 %APPDATA%\.baresip\ 下,
重启即生效。改完配置也要重启 baresip 才生效(没有热重载)。

一份能用的 accounts 长这样(分机 1002 注册到 FreeSWITCH 5070 端口):

"测试话机" <sip:1002@192.168.86.56:5070>;auth_pass=<你的SIP密码>;regint=60;audio_codecs=opus/48000/2

对应的 config 关键行:

sip_listen        0.0.0.0:5064        # 同机避开 linphone(5060) 和 FreeSWITCH(5070)
audio_player      wasapi,default
audio_source      wasapi,default
audio_alert       wasapi,default
ctrl_tcp_listen   127.0.0.1:4444      # 可选,脚本控制口

同机端口分配经验:一个 Windows 上最多跑一个 SIP 软电话占 5060;
我们的坑是 linphone(通配 0.0.0.0:5060)和 FreeSWITCH(具体 192.168.86.56:5060)
"共存绑定"但收包只命中一方,导致注册包被投回 linphone 自己。
最终方案:FreeSWITCH 5070 / baresip 5064 / linphone 5060,全部错开。

5.2 麦克风权限:不开就"哑巴"

Windows 隐私设置默认可能禁止桌面应用访问麦克风。
不打开时的现象:日志里出现

wasapi/src: IAudioClient_Initialize failed: 0x80070005

0x80070005 = E_ACCESSDENIED(拒绝访问)。baresip 的 wasapi 模块遇到这个
错误时线程直接退出、不重试——表现就是"能接通、对方听不到你"。

需要打开的三处开关(设置 → 隐私和安全性 → 麦克风):

  1. 麦克风访问(总开关)
  2. 允许应用访问你的麦克风
  3. 允许桌面应用访问你的麦克风 ← 最关键,baresip 属于"桌面应用"

实测:三个开关打开后,同样操作,0x80070005 消失,语音双向可通。

5.3 扬声器:默认设备 + "默认通信设备"两个都要设

baresip 的 wasapi 模块选音频设备时,用的不是"默认设备",而是
「默认通信设备」角色(源码 GetDefaultAudioEndpoint(..., eRender, eCommunications),
采集端同理用 eCapture + eCommunications)。

Windows 把音频设备分三个角色:默认、默认通信、默认多媒体——
插拔 HDMI / USB 耳机时三者可能被系统分别改掉。

手动设置方法(两个都设一遍):

Win + R → mmsys.cpl → 「播放」选项卡
→ 右键「扬声器 (Realtek(R) Audio)」→ 设为默认设备
→ 再右键一次 → 设为默认通信设备
(「录制」选项卡里对麦克风做同样操作)

真实翻车故事:有一次我抱着笔记本去同事工位(拔了 HDMI 外接显示器),
回来后重新插上显示器,Windows 悄悄把默认播放设备切成了显示器
(R27q-30 自带的音频输出)。于是我这边"接通了但一点声音都没有"——
声音全送到显示器去了,笔记本扬声器一声不吭。查了半小时才发现
是拔插 HDMI 惹的祸。教训:笔记本上排查"没声音",先看一眼当前默认播放设备
是不是显示器的名字。


6. 产物验证与自测

6.1 静态链接验证

:: 看依赖 DLL(应该只有 8 个系统库)
dumpbin /dependents C:\Code\baresip-win\deliver\baresip.exe

:: 看架构与子系统
dumpbin /headers C:\Code\baresip-win\deliver\baresip.exe | findstr /i "machine subsystem"

:: 校验哈希
certutil -hashfile C:\Code\baresip-win\deliver\baresip.exe SHA256

6.2 回声测试(推荐的自测法)

  1. 确认 FreeSWITCH 已启动(我们是手动跑 FreeSwitchConsole.exe)
  2. 双击 baresip.exe,等到出现 All 1 useragent registered successfully!
  3. 控制台输入(注意长命令必须以 / 开头,不带 / 首字符会被当快捷键吞掉):
/dial 9196 0

9196 是 FreeSWITCH 自带回声测试号,0 表示用第 1 个账号发起。
对着麦克风说话应该能听到自己的回声。

  1. 通话中控制台会持续刷新状态行:
[0:00:56] audio=19702/19667 (bit/s)

audio=发送/接收,两边数值对称说明回环在工作;完全是 0 则媒体流没起来。
挂断按 b,退出按 q。


7. 速查表:症状 → 原因 → 处置

症状 原因 处置
Populated 0 accounts 配置没放对地方 放 %APPDATA%\.baresip\accounts
IAudioClient_Initialize failed: 0x80070005 麦克风隐私权限 开"允许桌面应用访问麦克风"三开关
接通但听不到任何声音 默认播放设备被抢(常见于插拔 HDMI) mmsys.cpl 把扬声器设为默认和默认通信设备
没有来电铃声 铃声 wav 找不到 铃路径写的是安装时的绝对路径,deps 树整体搬家,或改 config 的 audio_path
LNK2038 RuntimeLibrary 不匹配 opus 没开静态 CRT 重编 opus 加 -DOPUS_STATIC_RUNTIME=ON
配置报 find_package(OpenSSL) 失败 re/baresip 无条件找 OpenSSL 两步都加 -DCMAKE_DISABLE_FIND_PACKAGE_OpenSSL=ON
--target baresip 找不到目标 目标名不对 用 --target baresip_exe
命令行 -f 等选项无效 MSVC 无 getopt,参数解析被编译掉了 死心,配置只认 %APPDATA%\.baresip
注册没反应 / 405 同机 SIP 端口冲突 `netstat -ano

8. 结语

从源码到可发布的单文件 exe,核心其实就是三段 cmake + 三个坑
(OpenSSL 门控、opus 的 CRT 覆盖、baresip_exe 目标名)。
真正花时间的反而是 Windows 的"环境玄学":配置文件为什么改了没用(位置不对)、
为什么别人机器上没有账号(配置没给)、为什么没声音(麦克风权限 + 默认播放设备
被 HDMI 插拔切走)。

源码里的行为都有据可查:
wasapi 模块选设备用的是 eCommunications(默认通信设备)角色、
CHECK_HR 宏决定了采集初始化失败就直接退线程不重试——
理解了这两点,上表里一半的"玄学"就都变成"必然而已"。

posted @ 2026-10-09 11:08  风间澈1998  阅读(5)  评论(0)    收藏  举报