首页 / 资讯中心 / 文章详情

jssip+FreeSWITCH WebRTC音视频互通实战指南

jssip+FreeSWITCH WebRTC音视频互通实战指南 ★ FEATURED ARTICLE
简介本资源是一个基于JSSIP库的Web端音视频通信与短信功能实战Demo面向VoIP开发初学者、Web实时通信学习者及FreeSWITCH集成开发者解决在浏览器中快速实现SIP注册、音视频呼叫、媒体协商、WebSocket信令交互及SIP MESSAGE短信收发等核心问题。压缩包共385个文件以115个HTML页面含交互入口与UI模板、71个LESS样式文件支持主题定制、63个JS脚本涵盖JSSIP初始化、会话管理、设备控制与错误处理逻辑及34个CSS文件为主干辅以JPG/PNG/SVG等2466个媒体资源和YML/JSON/MD等配置与说明文件整体4.42MB结构清晰、模块解耦便于逐层理解SIP信令流程与媒体流控制。已有1438人学习下载读者可直接运行调试完整通信链路获取可复用的FreeSWITCH对接范例、WebSocket连接封装、音视频设备选择与分辨率适配代码以及SIP INVITE/ACK/BYE全生命周期事件监听实践。1. jssip音视频demo一个能跑通WebRTC双向音视频的真实前端沙盒不是“Hello World”式摆设你花两小时配好FreeSWITCH、装完certbot、改了三遍sip.conf最后在浏览器里点“呼叫”却只看到黑屏和控制台里一串Failed to set remote offer sdp: Called with SDP without media sections——这不是你的错。jssip音视频demo的本质是一个被严重低估的WebRTC信令桥接验证器它不负责编解码、不托管媒体流、不替代SIP服务器但能用最轻量的JS代码把浏览器端的getUserMedia、RTCPeerConnection、SDP协商、ICE候选者交换、DTLS指纹校验这一整条链路在真实网络条件下跑通、卡点、暴露问题。它适合三类人正在调试FreeSWITCH WebRTC互通的后端工程师、需要快速验证前端音视频能力边界的产品技术负责人、以及刚学完WebRTC API但总卡在“offer/answer对不上”的前端开发者。这个demo的价值不在功能多炫而在它把WebRTC中最容易出玄学问题的5个关键断点媒体轨道绑定、SDP m-line顺序、ICE candidate触发时机、DTLS角色协商、track ID映射全部显性化、可打断、可日志追踪。别急着改配置先让这个demo在你本地Chrome里打出第一声“喂听得见吗”——这才是真正落地的第一步。2. jssip核心机制与FreeSWITCH信令交互为什么必须用jssip而不是原生WebRTC API2.1 jssip不是封装库而是SIP协议栈的JavaScript实现jssip本质是将RFC 3261定义的SIP协议状态机、事务模型、消息头解析、认证流程、对话管理全部用TypeScript重写。它不依赖任何后端代理所有SIP信令INVITE/200 OK/ACK/BYE都通过WebSocket或长轮询直接发往SIP服务器。这意味着它必须自己处理SIP Digest认证WWW-Authenticate头解析、HA1计算、nonce重用判断它要维护完整的Dialog状态call-id、local-tag、remote-tag、CSeq它需在收到180 Ringing时启动本地振铃逻辑在200 OK中提取SDP并触发setRemoteDescription。提示jssip v3.9默认启用force_rport和enable_rfc5626若FreeSWITCH未开启rfc5626模块会导致REGISTER失败。这是新手最常翻车的第一关。2.2 FreeSWITCH作为SIP终端的特殊性媒体协商必须走asendrecvFreeSWITCH默认将WebRTC终端视为“对称NAT穿透困难户”其sofia.conf中rtp-ip和sip-ip若未显式指向公网IP会导致SDP中的c行地址为内网地址如cIN IP4 192.168.1.100浏览器无法建立ICE连接。更关键的是FreeSWITCH的sofia profile必须设置param namertp-ip valueYOUR_PUBLIC_IP/ param namesip-ip valueYOUR_PUBLIC_IP/ param nameext-rtp-ip valueauto-nat/ param nameext-sip-ip valueauto-nat/ param namertp-timeout-sec value300/ param namertp-ip valueYOUR_PUBLIC_IP/否则jssip发出的INVITE中asendrecv会被FreeSWITCH降级为asendonly导致单向音频。2.3 jssip与WebRTC API的职责切分谁管媒体谁管线jssip只负责构造SIP消息体含SDP offer/answer解析SIP响应头如Contact:、Record-Route:触发onAccepted、onProgress等事件将远端SDP传给RTCPeerConnection.setRemoteDescription()。而媒体流控制完全交给WebRTC原生APInavigator.mediaDevices.getUserMedia({video:true, audio:true})获取轨道pc.addTrack(localVideoTrack, localStream)绑定本地轨道pc.ontrack (e) { remoteVideo.srcObject e.streams[0] }接收远端流pc.onicecandidate (e) { session.sendDTMF(e.candidate?.candidate || ) }—— 错正确做法是通过SIP INFO或自定义WebSocket通道发送candidate。jssip本身不处理ICE candidate传输它只提供session.on(icecandidate, callback)事件钩子你需要自己实现candidate中继逻辑常见做法是通过同一个WebSocket连接转发candidate字符串。3. 快速启动从零部署jssip音视频demo到FreeSWITCH环境3.1 环境准备最小可行依赖清单你不需要Docker、不需要Kubernetes、不需要Node.js后端服务。只需一台Linux服务器Ubuntu 22.04 LTS实测最稳FreeSWITCH 1.10.7必须带mod_verto和mod_sofia一个域名用于HTTPSLets Encrypt免费证书浏览器Chrome 115 或 Firefox 110禁用所有WebRTC相关扩展。注意jssip要求HTTPS才能调用getUserMediaHTTP localhost除外。生产环境必须用HTTPS否则媒体流请求直接被浏览器拦截。3.2 FreeSWITCH关键配置修改编辑/usr/local/freeswitch/conf/sip_profiles/external.xmlprofile nameexternal settings !-- 必须开启WSS支持 -- param namews-binding value:5066/ !-- 允许跨域jssip需要 -- param namews-origin valuehttps://your-domain.com/ !-- 强制使用UDP/TCP/WSS混合传输 -- param namertp-ip valueYOUR_PUBLIC_IP/ param namesip-ip valueYOUR_PUBLIC_IP/ !-- 关键允许WebRTC终端使用asendrecv -- param nameinbound-codec-negotiation valuegenerous/ /settings /profile然后重启FreeSWITCHsudo systemctl restart freeswitch。3.3 jssip前端工程初始化创建index.html引入jssip v3.9.0CDN最简!DOCTYPE html html head titlejssip音视频demo/title script srchttps://unpkg.com/jssip3.9.0/lib/jssip.min.js/script /head body video idremoteVideo autoplay muted/video video idlocalVideo autoplay muted/video button idcallBtn呼叫/button button idhangupBtn挂断/button script // 1. 创建UA实例 const socket new JsSIP.WebSocketInterface(wss://your-domain.com:5066); const configuration { sockets: [socket], uri: sip:1001your-domain.com, password: 123456, register: true, register_expires: 300, use_preloaded_route: false, hack_via_tcp: true, hack_ip_in_contact: true, connection_recovery_max_interval: 30, connection_recovery_min_interval: 2, log: { builtin: false, level: 3, filter: [INFO, WARN, ERROR] } }; const ua new JsSIP.UA(configuration); // 2. 注册成功回调 ua.on(registered, () { console.log(✅ SIP注册成功); document.getElementById(callBtn).disabled false; }); // 3. 呼叫按钮绑定 document.getElementById(callBtn).onclick () { const options { mediaConstraints: { audio: true, video: true }, pcConfig: { iceServers: [ { urls: stun:stun.l.google.com:19302 }, { urls: turn:your-domain.com:3478, username: turnuser, credential: turnpass } ], sdpSemantics: unified-plan } }; const session ua.call(sip:1002your-domain.com, options); // 4. 处理远端流 session.on(accepted, () { console.log(✅ 呼叫已接受); const remoteStream session.connection.getReceivers().map(r r.track).filter(t t); if (remoteStream.length 0) { document.getElementById(remoteVideo).srcObject new MediaStream(remoteStream); } }); }; /script /body /html3.4 关键参数说明与可调项参数作用修改建议ws-bindingFreeSWITCH监听WSS端口必须与jssip中WebSocketInterface地址一致且防火墙放行ws-origin白名单域名必须精确匹配前端页面域名不支持通配符mediaConstraints浏览器媒体请求约束video: { width: 640, height: 480, frameRate: 15 }可降低带宽压力iceServersICE候选者服务器STUN必填TURN选填若无TURNNAT类型为Symmetric时必然失败sdpSemanticsWebRTC SDP语义unified-plan是Chrome/Firefox现代标准plan-b已废弃4. 避坑指南五个让开发者凌晨三点还在查日志的典型问题4.1 现象注册成功但呼叫无响应控制台无错误原因FreeSWITCH未启用mod_sofia的WSS监听或ws-origin域名不匹配导致WebSocket连接被拒绝但jssip未抛出异常。解决在FreeSWITCH CLI中执行sofia status profile external确认WS Binding显示wss:YOUR_IP:5066打开浏览器开发者工具Network标签页筛选WS看WebSocket连接状态码是否为101若状态码为403检查ws-origin是否与页面location.origin完全一致注意https/http、www前缀、端口号。4.2 现象本地视频显示正常远端视频黑屏但音频正常原因SDP中mvideo行缺少asendrecv或FreeSWITCH将视频轨道静音v0。解决在jssipsession.on(accepted)中打印session.remoteDescription.sdp搜索mvideo行确认其后紧跟asendrecv若为ainactive或asendonly在FreeSWITCHsofia.conf中添加param nameinbound-codec-negotiation valuegenerous/检查FreeSWITCH日志grep VIDEO /usr/local/freeswitch/log/freeswitch.log看是否出现video disabled by remote。4.3 现象呼叫建立后几秒自动断开日志显示ICE failed原因STUN/TURN服务器不可达或ICE候选者未正确中继。jssip默认不自动发送candidate需手动实现。解决在ua.call()后立即监听session.on(icecandidate, (e) { ... })将e.candidate.candidate字符串通过WebSocket或SIP INFO发送至FreeSWITCHFreeSWITCH需配置param nameaggressive-nat-detection valuetrue/并启用mod_verto处理candidate。4.4 现象Chrome报错DOMException: Permission denied但摄像头权限已授权原因页面未通过HTTPS加载或getUserMedia被其他脚本阻塞如广告拦截插件。解决确认地址栏显示锁图标且证书有效可用https://your-domain.com/.well-known/pki-validation/验证在Chrome地址栏输入chrome://flags/#unsafely-treat-insecure-origin-as-secure将你的HTTP地址加入白名单仅开发用禁用所有浏览器扩展尤其是uBlock Origin、Privacy Badger。4.5 现象FreeSWITCH日志显示Invalid SDP: no media sectionjssip控制台无提示原因jssip生成的SDP offer中m行缺失通常因mediaConstraints: { video: false }但未同步更新pcConfig。解决检查ua.call()的options对象确保mediaConstraints与实际调用getUserMedia的参数严格一致在session.on(sending, (e) console.log(e.request.body))中打印原始SDP确认maudio和mvideo均存在若只想要音频显式设置mediaConstraints: { audio: true, video: false }并移除pcConfig.sdpSemantics中对video的依赖。5. 进阶验证用三组测试用例定位真实网络瓶颈5.1 测试用例设计覆盖NAT类型与网络策略我们不靠猜靠可复现的测试组合。准备三台设备A/B/C分别模拟不同网络环境设备ACaller家用宽带UPnP开启NAT类型Full Cone设备BCallee企业防火墙后NAT类型Symmetric仅开放TCP 443/5066设备C旁路监控同一局域网内运行Wireshark抓包。每组测试执行以下操作A呼叫B记录呼叫建立时间从点击到onAcceptedA停止本地视频观察B端是否立即黑屏验证asendonly动态协商B主动挂断A端是否收到BYE并触发onTerminated抓包分析STUN Binding Request响应延迟500ms即需优化STUN服务器。5.2 关键指标采集表测试项正常阈值采集方式异常含义SIP注册耗时 2sua.on(registered, () console.timeEnd(register))FreeSWITCH DNS解析慢或网络丢包INVITE到180 Ringing 1.5ssession.on(progress, () console.timeEnd(ringing))FreeSWITCH路由策略复杂或数据库查询阻塞ICE连接建立 3ssession.connection.onconnectionstatechange () { if(pc.connectionState connected) ... }STUN不可达或TURN凭据错误音频首包延迟 200msChrome DevTools → Network → Media → 查看audio流首帧时间戳编码器缓冲区过大或Jitter Buffer配置过高视频分辨率稳定性持续640x480remoteVideo.videoWidth每秒采样FreeSWITCHvideo-bitrate限制过低或带宽波动5.3 实战技巧用FreeSWITCH CLI实时诊断当Web界面卡住时别急着重启服务。登录FreeSWITCH命令行fs_cli执行# 查看所有SIP注册用户 sofia status registrations # 查看当前活跃会话含SDP摘要 sofia status sessions # 抓取指定用户的完整SIP信令流替换1001为分机号 sofia trace 1001 # 强制踢掉异常注册防僵尸UA占坑 sofia unregister 1001your-domain.com特别注意sessions输出中的Media State字段若为inactive说明FreeSWITCH未收到有效candidate若为active但无流量用tcpdump -i any port 3478 -w stun.pcap抓STUN包验证连通性。5.4 血泪经验我每次部署jssip demo都强制做的三件事在ua实例创建前先执行JsSIP.debug.enable(JsSIP:*)jssip默认日志级别过滤掉DEBUG而关键的SDP生成、transaction状态变更全在DEBUG里。不打开等于闭眼开车。用chrome://webrtc-internals替代控制台log这里能看到真实的ICE候选者列表、DTLS握手状态、SRTP加密套件、Jitter Buffer大小比读jssip日志直观十倍。在FreeSWITCHvars.xml中硬编码variable namertp-ip valueYOUR_PUBLIC_IP/别信auto-nat它在多网卡服务器上大概率选错IP导致SDP里的c行地址错误这是黑屏问题的终极元凶。从那以后我每次上线新环境都先用curl -s https://api.ipify.org确认公网IP再手敲进FreeSWITCH配置最后用sofia status profile external二次核对。少一次核对多两小时排查。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站