拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于sip.js与FreeSWITCH的WebRTC网页电话实现指南

简介基于sipjs与FreeSWITCH的WebRTC电话通信示例面向需要快速验证网页端电话呼入、呼出、转移、保持等功能的开发者。包内共4个文件包含网页入口、SIP逻辑脚本、样式表及使用说明文档整个压缩包仅79KB结构轻量修改分机、密码和服务器地址后可在Chrome浏览器中直接运行测试适合学习SIP.js基础集成与FreeSWITCH环境联调。目前已有2314人学习/下载对初次接触WebRTC电话应用的读者较有参考价值。示例围绕SIP.js官方API展开代码精简能帮助快速理解网页端发起呼叫、接听、转接及保持通话的核心流程为自建电话客服或音视频通信页面提供可复用的起点。 有段时间没写通信相关的总结了正好最近在做一个浏览器端电话功能需求不复杂网页上能呼入、呼出通话中能保持还能转给同事最好不装插件、不跑客户端。我选的技术组合是 sip.js FreeSWITCH WebRTC前端用 sip.js 走 SIP over WebSocket服务端用 FreeSWITCH 做注册和媒体桥接。这套方案做完之后效果比较理想把整个实现过程、关键配置和踩过的坑整理了出来给后面要做类似网页端语音呼叫的同学做个参考。先交代一下我能提供的核心能力网页端注册分机、点击呼叫呼出、网页端来显弹窗呼入、通话保持/恢复、盲转和咨询转。整个过程音频走 WebRTC信令走 WSS 加密通道不需要用户安装任何软件打开浏览器登录页面就能当话机用。适合前端开发、通信开发、做呼叫中心或企业内部沟通工具的技术同学参考尤其适合第一次把 FreeSWITCH 和浏览器端 WebRTC 打通的人。1. 为什么是这套组合WebRTC 网页电话的架构思考1.1 网页端通话的产品需求与常见实现路线先聊需求。网页端发起的语音通话通常出现在三类场景里客服系统要一键外呼、CRM 里要点击拨号、办公平台要做软电话。早些年实现这些功能很别扭网页里没法直接拿到麦克风只能跳转到本地装的软电话客户端去呼叫或者用 Flash、ActiveX 这类插件体验差、兼容性也差。后来 WebRTC 成熟了浏览器原生支持采集音频、编码、传流网页端才真正具备了“当电话机用”的基础。但 WebRTC 本身只是媒体传输和点对点连接的能力它不管分机注册不管电话号码怎么路由也不管怎么连接到传统电话网。如果一个网页端只做两个浏览器之间的裸 WebRTC 通话那距离“能打电话”还差得远——你没法呼到手机号没法呼到 PSTN 固话也没法跟已有的 SIP 语音网络互通。所以现实方案通常要在 WebRTC 和业务系统之间加一层网关承担信令接入、媒体转发、路由控制这些事。选型的时候我主要看三条路线。一条是直接用云厂商的 WebRTC 通信 PaaS 服务快是快但费用按量计企业内部长期用成本不可控另一条是投入大成本自研一套信令媒体服务器周期太长第三条就是我这次采用的 SIP over WebSocket 开源软交换浏览器端用 sip.js 发标准 SIP 信令服务端用 FreeSWITCH 做注册和媒体桥接信令走 WSS 加密通道媒体走 WebRTC 的 SRTP 通道。这套组合的好处是模型标准、对接传统电话网容易、社区资料多能同时满足网页端内互通和与外部电话系统互通的需求。1.2 三个组件各自的职责把这套架构拆开看三个角色的分工很清楚sip.js 负责 SIP 信令。它运行在浏览器里负责注册分机发起 INVITE 邀请处理来电 INVITE发送 BYE 挂断发送 REFER 实现转移以及通过 re-INVITE 实现保持/恢复。它不是浏览器原生能力而是把 SIP 协议栈用 JavaScript 重新实现了一遍。FreeSWITCH 是信令与媒体网关。它接收 sip.js 的注册请求维护分机的在线状态按照拨号计划把呼叫路由到分机、SIP 中继或 PSTN 网关。媒体层面它也可以做转发或桥接比如网页端和传统电话网关之间编码不一致时由它负责转码。WebRTC浏览器负责音频的采集、编码、传输和播放。具体到代码里就是 getUserMedia 采集麦克风RTCPeerConnection 管理和远端建立媒体连接这部分工作 sip.js 内部会帮我们封装好。我用一个更生活化的类比WebRTC 就是电话机里的听筒和话筒负责声音sip.js 是打电话的人负责拨号、接听、挂断这些动作FreeSWITCH 是总机交换机负责找到人、接通线、路由到外部网络。这样各司其职扩展性也好比如想在网页端加视频只需要在 SDP 协商里加上 video其余流程基本不用动。2. 环境部署FreeSWITCH 侧的 WebSocket 接入准备2.1 安装与模块确认FreeSWITCH 的安装方式有两种发行版软件源安装和源码编译安装。如果只是快速把功能跑起来用官方提供的 apt 源或 yum 源装稳定版就行。源码编译主要的好处是方便自定义模块和比较新的特性当初我编译时加的./configure --disable-dependency-tracking这类参数能减少部分重复依赖检查对全量编译时间优化有些帮助但注意不同版本的依赖项差异比较大需要看 INSTALL 文档里的说明。装完以后确认几件事一是核心模块mod_sofia必须在模块列表里这是 SIP 协议栈模块WebSocket 接入就是靠它提供的二是如果有音频编码转码需求比如与只支持 G.711 的网关互通确认mod_opus和mod_g711在编译选项里三是确认mod_event_socket是启用的后续排查问题、用 ESL 做点击拨号事件通知非常有用。用fs_cli进控制台执行module_exists mod_sofia就能查。2.2 启用 WSS 与 TLS 证书配置sip.js 和 FreeSWITCH 之间的信令走的是 WebSocket生产环境必须用 WSS即 TLS 加密的 WebSocket因为浏览器只有在 HTTPS 或 localhost 环境下才允许调用麦克风权限同时 SIP 注册中携带的账号密码也需要加密传输。FreeSWITCH 的 SIP over WebSocket 支持是在 SIP profile 里开启的。以默认的 external profile 为例配置文件在/etc/freeswitch/sip_profiles/external.xml找到并确认下面两行没有被注释param namews-binding value:5066/ param namewss-binding value:7443/5066 是普通 WS 端口7443 是 WSS 端口。浏览器端 sip.js 连接的就是wss://你的服务器IP:7443。如果某些版本安装后默认没有这两个参数手动加上然后执行reloadxml或者重启 FreeSWITCH 即可。TLS 证书方面FreeSWITCH 安装后自带一套自签名证书位于/etc/freeswitch/tls/下。开发环境可以用但浏览器会提示证书不受信任连接会被拦。处理办法可以先在浏览器里以 https 方式访问一次https://你的服务器IP:7443手动点击信任异常证书后面 WSS 连接就能建立。生产环境建议用正规 CA 签发的证书把证书和私钥放到/etc/freeswitch/tls/下并替换原有文件重启后就是一条完全可信的 WSS 通道。证书路径没有硬编码默认读取的是wss.pem、dtls-srtp.pem这几个文件配置前留意一下文件是否存在。2.3 分机与拨号计划配置sip.js 在网页端注册的本质上是一个 SIP 分机所以 FreeSWITCH 目录里必须要有这个用户。在/etc/freeswitch/directory/default.xml里添加分机一个最小配置长这样include user id1001 params param namepassword value123456/ /params variables variable nameuser_context valuedefault/ /variables /user /include添加多个分机就重复加多个user块密码会在后续注册认证时用到。然后配置拨号计划让分机之间可以互拨。修改/etc/freeswitch/dialplan/default.xml加一条让分机互拨的规则extension nameLocalExtension condition fielddestination_number expression^(10\d{2})$ action applicationbridge datauser/${destination_number}/ /condition /extension这里^(10\d{2})$匹配的是 1001~1099 这样的分机号bridge 到user/就是直接呼到内部注册的分机。如果后续要呼到外部 PSTN只需要再写一条规则把匹配到的号码桥接到对应的 SIP 网关或中继上。分机互拨的流程走到这里网页端注册、互拨的基础环境就齐了。3. sip.js 前端实现注册、呼入呼出、保持与转移3.1 初始化与注册前端部分我用的是 sip.js 0.21.x 版本新版本 API 可能有调整但核心概念一致。安装方式npm install sip.js模块引入和初始化import { UserAgent } from sip.js/lib/platform/web; const userAgent new UserAgent({ uri: UserAgent.makeURI(sip:1001your-server.com), transportOptions: { server: wss://your-server.com:7443 }, authorizationUsername: 1001, authorizationPassword: 123456 }); await userAgent.start();uri是分机的 SIP 地址authorizationUsername和authorizationPassword对应 FreeSWITCH 里的分机号和密码。启动连接后sip.js 会发送 REGISTER 请求注册成功后 FreeSWITCH 日志里能看到该分机的注册状态。有个细节要提醒浏览器里跑 WebRTC 录音必须有安全上下文也就是 HTTPS 或 localhost。如果生产环境没有配 HTTPS麦克风采集会直接失败表现为呼叫能建立但听不到声音或者 getUserMedia 直接抛异常。开发期间用 localhost 没问题但一旦部署到远端不要省掉 HTTPS。3.2 呼出实现点击拨号呼出核心就是调用userAgent.invite()发起一个新的 SIP 会话const target UserAgent.makeURI(sip:1002your-server.com); const session userAgent.invite(target, { sessionDescriptionHandlerOptions: { constraints: { audio: true, video: false } } }); session.delegate { onSessionDescriptionHandler: () { // 远端应答或 SDP 协商更新 }, onBye: () { // 对方挂断 }, onReject: () { // 呼叫被拒绝 } };呼出到分机和呼出到外线的区别主要在目标 URI呼外线就写sip:手机号你的中继地址FreeSWITCH 侧拨号计划负责把呼叫路由到中继网关。网页端完全不用关心底层走的是哪种通道。还要处理一下振铃状态。session.invite()返回的是一个会话但真正进入通话状态需要等对端接受。可以用session.stateChange事件来监听会话状态比如Establishing对应振铃中Established对应已接通Terminated对应已结束。在这个事件里驱动 UI 的挂断按钮、通话时长计时器最合适。3.3 呼入处理呼入的入口是 UserAgent 的delegate收到来电时会触发onInvite。示例代码如下userAgent.delegate { onInvite: (session) { // 有来电弹窗提示 session.accept(); } };来电时sip.js 已经完成 SDP offer/answer 的一部分工作这时浏览器不要立刻抢麦克风而是先把来电信息展示给用户等用户点击接听按钮后调用session.accept()。如果用户点了拒接调用session.reject()。开发时我踩过一个小坑onInvite回调里如果直接session.accept()在某些版本里会因为浏览器没有完成用户授权而抛异常。稳妥做法是弹层提醒用户“是否接听”点击后先navigator.mediaDevices.getUserMedia({ audio: true })拿到授权再调用session.accept()成功率最高。3.4 保持、转移与通话状态管理保持/恢复在协议层面是发送 re-INVITE把媒体方向设置为sendonly保持或sendrecv恢复。sip.js 封装了两个方法// 进入保持 session.hold(); // 解除保持 session.unhold();调用hold()后对方听到的是静音媒体方向已经改变。原理理解起来其实不复杂保持不是挂断只是告诉对端“我暂时不发送音频了”底层的 RTP 通道还保持。转移分两种盲转和咨询转。盲转就是直接把当前通话转到目标分机不需要和目标先通话。sip.js 里用Referrer实现import { Referrer } from sip.js/lib/api/session/Referrer; const referTarget UserAgent.makeURI(sip:1003your-server.com); const referrer new Referrer(session, referTarget); referrer.refer();咨询转则要复杂一些需要先发起一个通往目标分机的新呼叫通上话后再把原有的来电转过去。实现上可以先把当前通话保持住同时新建一个invite呼叫目标分机在第二通电话接通后用当前会话执行 refer 把原始会话引到第三方。这部分逻辑增加了不少状态管理建议在代码里单独封装一个CallTransferManager不要把所有状态散落在组件里。还有个容易忽略的点通话中的 DTMF 按键。比如拨分机后需要输入密码或者按键导航这个在网页端也常有sip.js 提供session.dtmf()方法可以发送 DTMF 信号。实际测试下来FreeSWITCH 侧默认能正常识别不需要额外配置。4. 常见问题与排查实录4.1 常见问题速查表做完全部流程后我整理了一张问题速查表遇到同类问题可以先对照排查问题现象可能原因解决方法注册不上403/401 循环分机密码错误或账号不存在检查 directory 里分机密码确认注册域名匹配WSS 连接报证书错误使用了自签名证书且未被信任浏览器先访问一次 WSS 地址并信任证书或换成正规证书有信令但没声音NAT 环境 RTP 地址不通检查 FreeSWITCH 的 ext-rtp-ip/ext-sip-ip 是否配置为公网或可达 IP呼出无响应拨号计划没有匹配号码fs_cli 里执行dialplan或直接看日志路由结果保持后恢复不了声音对端网关不支持 re-INVITE检查对端是否是标准 SIP 网关必要时在网关侧开启 re-INVITE 支持转接后原通话被挂断REFER 处理异常检查 FreeSWITCH 是否允许 transfer查看日志中 REFER 的响应码4.2 日志工具与排查思路排查这套链路问题我建议分三步走。第一步看 FreeSWITCH 日志。在 fs_cli 里执行console loglevel debug然后复现问题重点看呼叫经过拨号计划时命中哪条规则以及 bridge 的目标分机是否在线。日志文件的默认位置在/var/log/freeswitch/freeswitch.log排查结束后记得把 loglevel 调回 warning否则日志量太大。第二步看浏览器端的 WebSocket 消息流。打开 Chrome 开发者工具的 Network 面板过滤器选 WS刷新页面能看到 sip.js 和 FreeSWITCH 之间每一条 SIP 消息。比如注册失败时能找到 401/403 的响应呼出时能看到 INVITE 和重推的 re-INVITE保持时能确认媒体方向字段。第三步看 WebRTC 内部状态。Chrome 地址栏输入chrome://webrtc-internals打开后能看到 RTCPeerConnection 的实时统计包括音轨状态、丢包率、码率、候选地址。如果明明通了但没声音先在这里看audioLevel有没有波动能立刻判断是采集端问题还是传输端问题。我遇到印象最深的一次开发环境一切正常部署到服务器后所有呼叫都变成单通。排查发现 FreeSWITCH 的 external profile 没有配置外层 IPRTP 包携带的是内网地址浏览器没法回传音频。在 external.xml 里把ext-rtp-ip和ext-sip-ip指到公网 IP 后立即恢复正常。这个问题不复杂但没有实际部署过很容易忽略。4.3 弱网环境下的体验优化思路热搜词里有一条是“webrtc 弱网卡顿怎么优化”这里专门说一下。网页端通话遇到网络抖动时优先调整音频编码参数而不是盲目升级带宽。使用 OPUS 编码时可以限制最大码率让 WebRTC 在弱网下自动降码率同时开启前向纠错来抵抗丢包。sip.js 支持自定义sessionDescriptionHandler来重写 SDP 参数但对大多数业务场景更实用的做法是在 FreeSWITCH 侧配置合理的编码优先级和带宽限制把压力放在服务端而不是每个浏览器客户端。5. 实操心得与扩展建议5.1 生产环境容易被忽略的几个细节这套方案从“能跑通”到“能上线”中间还隔着几个细节我在实际落地时印象很深。音频编码优先级要提前规划。网页端最好用 OPUS音质好、带宽友好但如果业务要呼到传统 PSTN 或老式语音网关对方往往只支持 G.711。FreeSWITCH 会负责转码但转码是要消耗 CPU 的并发量上来以后这个开销不能忽视。建议在拨号计划的 bridge 环节显式指定编码避免不必要的转码。再就是浏览器的兼容性。sip.js 在 Chrome、Edge 上表现最稳定Firefox 大版本迭代时偶尔有 WebRTC 兼容问题。Safari 对某些 SDP 格式处理差异明显实际测试发现部分场景下保持和恢复操作在 Safari 上媒体会中断。如果业务用户群集中在 Safari需要留足测试时间。呼叫状态机要收敛好。网页端通话不是只有“空闲”和“通话中”两种状态还有“呼出中”“来电振铃中”“保持中”“转移中”这些中间态。我用一个简单的状态机枚举来管理避免了按钮重复点击和状态错乱。特别是转移和保持并发操作时要对操作加锁防止用户在保持状态下又发起一次转移导致会话状态异常。5.2 后续可扩展的功能方向基础通话做完之后这套架构的扩展空间很大。录音功能可以通过 FreeSWITCH 的录音 API 实现呼入呼出的时候启动录音文件落到服务器上对接质检系统多方会议可以用conference模块把多个网页端分机拉进同一个会议室排队和话务分配可以基于 ESL 事件配合业务系统做来电时由后台逻辑决定转给哪个座席。我个人做下来的体会是WebRTC 本身不难难的是把信令、媒体、路由、状态管理这些环节串起来而这套“sip.js FreeSWITCH WebRTC”的组合正好把所有环节都用标准协议串在了一起出了问题能分层排查不像黑盒方案那样一头雾水。如果只是想快速验证网页端能否打电话照着上面的配置和代码走一遍基本一两天就能跑通。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门