1. 视频通话难调,根源往往不在网络而在协商
先把结论放这:绝大多数FreeSWITCH视频通话翻车,都不是带宽不够或者路由器不行,而是SIP会话协商环节出了问题。我做过的项目里,至少七成视频故障查到最后都落在配置文件和编解码协商这两个点上。
语音SIP跑得好好的,一上视频就出现各种花屏、卡顿、黑屏、无图有声、有图无声,甚至呼叫直接被拒。这时候很多人第一反应是抓包看网络、查丢包,折腾半天发现核心问题根本不在传输链路。原因在于:语音通话只需要协商一个音频编码,而视频通话引入了几组完全不同量级的变量——视频编码格式、RTP负载类型、带宽上限、分辨率、帧率,还需要处理音频和视频两条媒体链路的时序关系。任何一个环节协商失败,表现都不是“报错”,而是以黑屏、单通这类看似物理故障的形式呈现。
这篇文章主要面向三类人:一是准备在FreeSWITCH上开通视频业务的运维人员,二是做呼叫中心或软交换集成的开发工程师,三是被各家SBC和网关文档绕晕、想回到源头把原理吃透的技术人。我会按实际排查的顺序把这些内容过一遍,包括关键配置文件逐行拆解、编解码协商的底层机制、优化参数的设置逻辑,以及上线后最常见的几个隐蔽坑。
1.1 视频通话与语音通话在FreeSWITCH中的本质差异
先说带宽需求。G.711音频大约占80kbps,OPUS高质量模式也不过100kbps上下。而视频这边,H.264在720p 30fps下,正常码率就要1到2Mbps,1080p基本奔着3到5Mbps去。一个视频通话占用的带宽顶得上二十路语音。但这还不是最关键的差异——带宽多花钱就能解决,真正的问题在协商。
视频呼叫的SDP报文里多了媒体行,一个m=video行会带一堆编解码参数,每个视频编码还带fmtp属性,比如H.264的profile-level-id、packetization-mode,VP8的max-fr、max-fs。这意味着协商时不仅要匹配“双方都支持H.264”,还要匹配profile级别、打包模式这类细粒度参数。FreeSWITCH在这块的行为与纯软电话终端差异很大,它作为一个B2BUA,会代表两端分别协商,中间稍有不匹配就会降级或失败。
另一个本质差异是媒体处理开销。语音转码在高性能服务器上几乎可以忽略不计,但视频转码是实打实的CPU杀手。一个720p的H.264软件转码,在没有硬件加速的普通Xeon上,单路就能吃掉一个核心的20%到40%。所以视频业务里,“能不能避免转码”直接决定了服务器能扛多少并发。
1.2 FreeSWITCH的模块化架构对视频意味着什么
FreeSWITCH这套系统设计上是模块化的,信令走mod_sofia,媒体走各种codec模块和endpoint模块。这带来一个视频场景下很关键的认知:你加载了mod_h264,不代表所有呼叫都会用H.264,模块只是把能力注册到系统里,具体用不用、在什么条件下用,由协商策略和配置文件决定。
同时,正因为模块化,很多问题其实是“模块没加载”或“加载了但配置层没配上”造成的。比如曾经排查过一个案例,对方终端只有VP8和VP9,FreeSWITCH侧加载了mod_vp8但vars.xml里的全局编解码列表没把VP8放进去,导致协商直接落到PCMU,视频被静默丢弃——配置层的问题,表现为“视频不通”。
2. 核心配置文件逐个拆解:vars.xml、sip_profiles与隐藏开关
FreeSWITCH的配置文件很多,但视频通话真正绕不开的就那么几个。按排查优先级排列:vars.xml定基调、sip_profiles定入口行为、dialplan定路由变量、acl.conf.xml管媒体白名单。每个文件里都有几个跟视频强相关的参数,下面逐个拆开讲。
2.1 vars.xml:全局编解码变量是整个系统的“宪法”
vars.xml位于/etc/freeswitch/vars.xml,是FreeSWITCH启动时加载的全局变量文件。两个视频场景里最重要的变量是global_codec_prefs和outbound_codec_prefs。
<X-PRE-PROCESS cmd="set" data="global_codec_prefs=OPUS,G722,PCMU,PCMA,H264,VP8"/> <X-PRE-PROCESS cmd="set" data="outbound_codec_prefs=PCMU,PCMA,OPUS,G722,H264,VP8"/>这两个变量很多人搞混。global_codec_prefs决定FreeSWITCH作为被叫方(接收呼入)时,向对端宣称自己支持哪些编码,也用于SIP中继协商;outbound_codec_prefs决定FreeSWITCH作为主叫发起呼叫时,在SDP里主动提供哪些编码,以及从Dialplan发起出局呼叫时的编码偏好。实际使用中,两者的顺序都代表优先级——排在前面的编码会在协商时优先被选中。
我把视频相关的编码加在变量里用,默认模板只包含音频编码,不加的话视频编码根本不会出现在SDP offer里,这就是很多人“明明加载了mod_h264却没有视频”的第一个原因。需要注意,加编码顺序有讲究:H.264和VP8这类视频编码应该放在音频编码后面,不然某些终端解析SDP时可能因编码类型混排出现异常。
另一个跟视频有关的全局变量是default_video_bitrate。默认值是2000,单位kbps,也就是2Mbps。还有default_video_fps,默认25。如果要求不高可以不改,但在内网高码率场景下,建议把default_video_bitrate提到3000或4000,公网场景反而要降。
2.2 sip_profiles:呼叫入口处的编解码与媒体行为开关
sip_profiles是FreeSWITCH作为SIP服务器时每个监听端口对应的配置,最常见的两个是internal.xml(默认监听5060)和external.xml(默认监听5080)。视频通话相关的关键参数都藏在这里。
<param name="inbound-codec-negotiation" value="generous"/> <param name="inbound-late-negotiation" value="true"/>inbound-codec-negotiation有两个值,generous和scrooge。从字面就能感觉到区别:generous是“慷慨”模式,FreeSWITCH倾向于选择对方SDP里的第一个编解码;scrooge是“吝啬”模式,FreeSWITCH强制选择自己偏好列表里最靠前的编解码。视频场景默认用generous更稳妥,因为视频SDP里编码列表的顺序往往反映了终端对分辨率和性能的偏好,强行改成scrooge可能导致终端被指派一个自己不太擅长的编码。
inbound-late-negotiation在视频场景里非常关键。开启后,FreeSWITCH会等收到200 OK或18x响应里的最终SDP后才确定媒体参数,而不是在一开始就基于初始offer做死决定。为什么这对视频重要?因为很多视频终端的早期媒体和最终媒体用的编码不一样,早期用低分辨率小码率做预览,接通后切到全分辨率。如果关闭late-negotiation,FreeSWITCH可能拿着早期媒体的参数去配置媒体处理器,导致接通瞬间黑屏或者前几秒花屏。
还有两个媒体透传参数也在这类配置里,inbound-bypass-media和inbound-proxy-media。前者是媒体彻底绕过FreeSWITCH,点对点直连;后者是媒体流经FreeSWITCH但不解码重编码,只做转发。两个参数都默认false,视频场景按需开启,后面专门展开讲。
2.3 dialplan与directory:路由和用户配置里的视频开关
Dialplan里也藏着视频通话的重要控制点。最常见的是在拨号计划里为特定呼叫设置视频相关变量,用export或set语法:
<action application="export" data="video_media=true"/> <action application="export" data="video_bitrate=2500"/> <action application="export" data="video_fps=30"/>video_media=true是强制启用视频媒体处理的开关,不设置这个变量时,FreeSWITCH在某些B2BUA场景下会只处理音频,把视频媒体流直接忽略掉。这条在日常排障中价值很大:任何“打电话能通但视频起不来”的案例,我都会先查Dialplan里有没有显式设置video_media=true。
directory目录配置里也有一组参数。在<params>标签下可以配置video-codec、video-bitrate、video-fps这几个针对单个用户或分机的视频参数。分机粒度配置通常用于给重点用户单独扩码率上限。
2.4 acl.conf.xml:一个被忽视但影响媒体流走向的参数
acl.conf.xml的主要作用是定义网络白名单,比如<list name="lan" default="allow">。视频通话场景里,这个文件本身不用频繁改,但它引出一个容易被忽略的关联点:很多视频终端为了传输高质量媒体流,会在非标准RTP端口发送数据。如果ACL配置过严,把媒体流来源IP或端口受限,就会出现“SIP能通但视频RTP收不到”或“收到几秒就断了”的奇怪现象。
遇到这类问题,可以在FreeSWITCH日志里看到Security级别的RTP拒绝记录。排障时先用sofia global siptrace on开启SIP跟踪,同时打开freeswitch -repl进入控制台看日志,确认RTP流是否被ACL拦截。从网关上把媒体端口范围限制在<param name="rtp-port-range" value="30000:31000"/>这个区间内也是一种常见做法,这样ACL和防火墙策略都更好收敛。
3. 编解码协商机制:FreeSWITCH选择编码的潜规则
折腾配置这么久,绕不开的核心问题是:FreeSWITCH到底按什么规则从双方支持的一堆编码里选出最终使用的那个?理解了协商机制,很多配置就不再是死记硬背,而是可以推演出来的。
3.1 SDP Offer/Answer协商流程简述
标准的SIP呼叫里,主叫方在INVITE里带一个SDP offer,列出自己支持的所有媒体流和编码列表。被叫方收到后,对照自己的能力,选一个双方都支持的编码,放在200 OK的SDP answer里返回。这是基本规则。
FreeSWITCH作为B2BUA时这套流程要跑两次:一次是它与主叫方,另一次是它与被叫方。中间FreeSWITCH还可能根据自己的配置改写字面意思上的offer和answer,同时动态决定媒体的路径。视频通话在这个流程里最常翻车的点在“payload type冲突”:同一个RTP payload type号在双方的SDP里代表不同编码。这类问题在纯语音时代也有,但视频时代更常见,因为视频编码的payload type动态范围更宽。
比如终端A在offer里把96号定义为H.264,终端B在answer里把96号定义为VP8,如果中间没有信令代理去重写,两边就会各说各话。FreeSWITCH处理得比较好,它内部会维护一个标准的payload映射,按需重写SDP。所以遇到多厂商终端互通的视频问题,先看FreeSWITCH日志里[INFO]级别的SDP收发记录,确认payload type是否已被正确归一化。
3.2 passthrough模式:让媒体绕过FreeSWITCH的核心开关
FreeSWITCH最消耗资源的操作是视频转码。有一种模式可以彻底避免转码:媒体透传,也就是让两端的RTP媒体流不经过FreeSWITCH解码,直接在两个终端之间传送。配置方式是SIP profile里设置inbound-bypass-media=true,或者在Dialplan里动态设置bypass_media=true。
passthrough模式的协商好处是:FreeSWITCH不把自己当成媒体终端,不会强制插入自己的编解码能力判断,而是尽力让两端的SDP保持一致。视频通话在这种模式下,画质和延迟表现最好,服务器负载几乎为零,因为FreeSWITCH只处理信令,RTP直接从终端A流向终端B。
但passthrough不是万能药,有几个硬限制:
- 需要录音时不能用,因为没解码就没法混音和落盘
- 需要做DTMF转换时不能用,比如RFC 2833转SIP INFO
- 需要做码率整形或转码时不能用
- 终端在NAT后面且无法打洞时不能用,因为FreeSWITCH无法主动帮媒体流穿墙
还有一种折中模式叫代理媒体,配置是inbound-proxy-media=true。RTP流经过FreeSWITCH服务器,但不解码不重编码,只按IP包级别转发。这种模式适合需要监控媒体流但又不想承担转码开销的场景。代理媒体模式下FreeSWITCH依然能统计流量、做带宽限制,但CPU占用远低于完全转码。
我实测过三者的服务器负载差异:在同样100路720p视频通话的服务器上,完全转码模式CPU占用达到70%以上,代理媒体模式直接降到15%,而passthrough模式下FreeSWITCH进程的CPU占用几乎可以忽略。所以原则是:能透传就透传,不能透传就代理,不得已才转码。
3.3 转码的代价:CPU、延迟与画质的三重损失
为什么视频转码是被当成最后手段?因为转码不只是CPU成本问题。一次完整的视频转码链路是:接收RTP包→解码成原始YUV帧→缩放或改帧率→重新编码→打包RTP发送。每一步都有时间开销,整体增加几十到上百毫秒延迟。实时通话里,这个延迟会直接反映为“对讲机效应”——我说完话对方要等一会儿才听到,而视频的声画同步也会被打破。
画质方面,每次有损编码都会引入量化误差。假设终端A用H.264以4Mbps编码发给FreeSWITCH,FreeSWITCH转成VP8以2Mbps发给终端B,这个过程中分辨率可能从1080p降到720p,色度抽样也可能变化。用户感知到的就是画面发虚、运动场景有马赛克。
还有一个隐性成本:并发上限。FreeSWITCH的视频转码线程模型对CPU很敏感,超负荷后不表现为掉线,而是表现为延迟猛增、丢包率上升,同时系统日志里出现大量[WARNING]级别的“Engine thread is lagging”告警。所以做容量规划时,先算清楚有多少路呼叫需要转码,再决定要不要上硬件转码卡或者换用支持GPU加速的架构。
4. 编解码优化实操:从参数调整到抓包验证
前面把机制讲清楚了,现在进入实战环节。这节我会给出一套能直接落地的编解码优化方案,包括不同场景下的编码选型、码率控制参数、分辨率帧率限制,以及调整后的验证方法。
4.1 先定编解码策略:不同场景该选什么编码
编解码选型没有银弹,取决于终端类型、网络环境和业务要求。下面是我在多个项目中验证过的对照表,可以按场景套用:
| 场景 | 推荐视频编码 | 推荐音频编码 | 理由 |
|---|---|---|---|
| 内网视频会议(同一局域网) | H.264 High Profile | OPUS | 带宽充裕,H.264在高码率下画质优势明显 |
| 公网点对点视频通话 | VP8 | OPUS | VP8在丢包环境下的容错性更好,无专利许可问题 |
| WebRTC网关互通 | VP8/VP9 | OPUS | 浏览器端的标准编码,兼容性最好 |
| 硬件视频会议终端互通 | H.264 Baseline/High | G.722或OPUS | 硬件终端的H.264实现最成熟,Baseline兼容老设备 |
| 多终端混网场景(移动端+PC+硬件终端) | VP8或H.264均保留 | OPUS优先 | 保留两个视频编码兜底,靠协商决定 |
一个关键经验是:不要只留一个视频编码。曾经有一个项目只配了H.264,结果一批安卓终端只支持VP8,视频直接协商失败,只剩音频通了。保留H.264+VP8两个视频编码是最稳的做法,让协商机制去选。
H.264在FreeSWITCH里还有一个特殊注意事项:profile-level-id。这是SDP fmtp属性里的一个十六进制字符串,比如42C01F代表Baseline Level 3.1,64001F代表High Level 3.1。如果FreeSWITCH侧的H.264 profile设置过高(比如High),而终端只支持Baseline,协商会失败。通过mod_h264配置或SDP重写可以强制指定profile级别,很多兼容性问题的根子就在这里。
4.2 码率、分辨率与帧率:三个参数怎么匹配才不浪费带宽
编解码优化最核心的参数有三个:码率、分辨率、帧率。三者关系是:码率决定了画质上限,分辨率决定了画面细节量,帧率决定了运动流畅度。调参时应该先定分辨率和帧率,再反推需要的码率。
在vars.xml里,这几个参数分别对应:
<X-PRE-PROCESS cmd="set" data="default_video_bitrate=2000"/> <X-PRE-PROCESS cmd="set" data="default_video_fps=25"/>在sip_profiles或Dialplan里还可以限制分辨率上限:
<action application="export" data="max_video_width=1280"/> <action application="export" data="max_video_height=720"/> <action application="export" data="max_video_fps=30"/>码率参考值:720p 30fps的视频,用H.264编码,建议设置在1500-2500kbps;1080p 30fps建议3000-5000kbps;如果网络条件差,降到480p 25fps时800-1000kbps基本够用。VP8在同码率下画质比H.264略差,所以同等画质要求下VP8的码率建议上浮20%左右,例如720p下给VP8分配2000-3000kbps。
这些参数之间有一个容易被忽略的联动逻辑:如果码率设得很高但分辨率上限没放开,实际画面不会变清晰,因为源分辨率被压住了;反过来,分辨率放开了但码率不够,画面会糊成一团,全是大颗粒马赛克。所以调参的合理顺序是:先定目标分辨率和帧率,再按表格反查码率,最后把两组参数写进配置。
FreeSWITCH还支持设置视频质量因子,变量名是video_quality,取值1到10。这个参数会作为编码器输出质量的参考,数值越高画质优先级越高、码率消耗越大。它不是一个精确控制参数,更像编码器内部的倾向调节。实际使用中,video_quality=6配合显式码率限制是比较均衡的组合。
4.3 实操:改完配置后怎么验证协商结果确实生效
配置改完了,不能拍拍脑袋说“好了”。我用了一套固定的验证流程,推荐你也照着走一遍。
第一步,重启或reload配置后,检查模块加载状态:
freeswitch> module_exists mod_h264 freeswitch> module_exists mod_vp8 freeswitch> sofia status profile internal确认想要的视频编码模块已经加载,SIP profile处于RUNNING状态。
第二步,发起一个带视频的测试呼叫,观察协商结果。在fs_cli里:
freeswitch> originate user/1000 &echo更好的方式是用软电话或支持视频的SIP客户端分别注册两个分机,然后互拨视频通话。通话建立后,在fs_cli里查看通道信息:
freeswitch> show channels as json重点看video_codec字段和video_bit_rate字段,确认视频编码协商到了H.264还是VP8,码率是否落在预设区间。
第三步,抓包验证真实媒体流。在服务器上跑tcpdump抓RTP流量:
tcpdump -i eth0 -s 0 -w video_call.pcap udp portrange 30000-31000然后用Wireshark打开,用rtp过滤器筛选RTP包,查看RTP payload type和SSRC。通过对比协商的payload type,可以确认实际发送的视频编码与SDP协商结果一致。这也是排查“协商说H.264但实际发的是VP8”这类问题的唯一可靠方法。
第四步,观察服务器负载。视频呼叫运行五分钟后,用top -u freeswitch查看CPU占用,用freeswitch> global_getvar cpu或者直接看系统指标。如果CPU占用和预估一致,说明转码路径符合预期;如果占用异常高,多半是发生了意料之外的转码,需要回头检查bypass_media设置。
4.4 一个真实容量数据参考
拿一个实际项目举例:客户是呼叫中心,需要在FreeSWITCH上做视频客服,终端是浏览器WebRTC,编码固定VP8+OPUS。服务器配置是双路Xeon Gold 6248(40核80线程)、128GB内存。前期没做任何调优就压测,结果20路视频通话CPU就冲到70%,视频质量一塌糊涂。
后来抓包发现,因为WebRTC网关在FreeSWITCH侧是以SIP中继接入的,DP忘了配bypass_media,导致所有视频流在FreeSWITCH上做了VP8到VP8的转码——同编码转码是最浪费资源的操作。改Dialplan强制bypass_media=true后,同一台服务器跑到80路视频通话,FreeSWITCH进程CPU才35%。这个案例说明,同编码转码的浪费是隐性杀手,也是优化空间最大的地方。
5. 上线后最容易踩的坑:早期媒体、NAT与DTMF
配置优化完不等于万事大吉。视频通话在真实网络上线的第一天,往往才是问题爆发的开始。这一节我把实际运维中遇到的几个高频坑逐个列出来,附带排查链路。
5.1 早期媒体导致的视频黑屏与呼叫超时
早期媒体问题在FreeSWITCH社区里是个常青话题,热词列表里的p-early-media-support就是干这个的。早期媒体指的是被叫方在呼叫真正应答之前(比如振铃阶段)发来的媒体流,典型场景是运营商侧回铃音、彩铃、IVR提示音。
对语音通话来说,早期媒体顶多就是回铃音异常,用户还能忍。但视频通话里,早期媒体可能会携带视频流,而且麻烦的是,很多终端在早期媒体阶段发送的是低分辨率或者特殊的预览画面。如果FreeSWITCH在早期媒体阶段就锁定了媒体参数,等到正式应答时终端切换成高分辨率视频流,FreeSWITCH可能还按旧参数处理,于是出现接通后黑屏、花屏、画面冻结。
sip_profile里的p-early-media-support参数接入各个中继时有不同设置方式。在FreeSWITCH侧,针对特定中继可以在dialplan里做更精细的控制:对需要透传早期媒体的运营商或平台,用export设置early_media=true;对不需要的,设置early_media=false。这个参数的核心作用是控制FreeSWITCH是否在收到183 Session Progress时就开始处理媒体流。
我的建议是:视频业务刚上线时先全部关闭早期媒体,等基础通话验证通过后,再对特定中继逐步放开。因为早期媒体功能对视频的影响比语音大得多,属于“开了容易出妖、关了最多没彩铃”的参数。
完整排查链路:如果用户反馈“呼叫接通后黑屏3秒才有画面”,先看FreeSWITCH日志里是否在180/183阶段就打印了视频channel的媒体信息。再用show channels as json查看video_media是否为true、early_media状态。最后对照SIP抓包,确认INVITE的早期SDP和200 OK的最终SDP中视频编码是否一致。如果二者不同,问题基本可以锁定在早期媒体协商上。
5.2 NAT穿透:公网视频通话的隐性问题
公网环境下的视频通话,NAT穿透问题比语音场景难处理得多。语音通话码率低,即使媒体走代理路径也不觉得慢;视频流量大,媒体路径一旦绕远或者受限,画质立刻崩。
FreeSWITCH在公网部署时,先确认external.xml里的这几个参数:
<param name="ext-rtp-ip" value="公网IP"/> <param name="ext-sip-ip" value="公网IP"/> <param name="rtp-port-range" value="30000:31000"/>ext-rtp-ip和ext-sip-ip决定FreeSWITCH在SDP里对外宣告的IP地址,必须配置成公网可达地址,否则终端会尝试往内网IP发RTP流。rtp-port-range限定了媒体端口范围,方便防火墙放行。这里有个实践经验:端口范围不要太小,至少1000个端口起,否则高并发视频呼叫时端口会被快速占满,新呼叫拿不到RTP端口直接失败。
NAT场景中最难排查的是“信令通、媒体不通”。现象是呼叫能建立、但视频画面完全出不来,FreeSWITCH日志里能看到RTP超时或丢包告警。排查链路是:
- 在FreeSWITCH服务器上确认收到的RTP包源地址是真实终端IP还是NAT网关IP
- 用
tcpdump抓UDP 30000-31000端口的流量,确认RTP包是否真的到达了服务器 - 检查SDP里FreeSWITCH宣告的IP是否为公网地址
- 检查终端NAT类型——如果是对称型NAT,即使用
inbound-bypass-media=true也大概率打洞失败,这个时候老老实实启用inbound-proxy-media=true。
5.3 视频通话中的DTMF问题
DTMF在视频通话里是个容易被忽略的坑。用户按了数字键,但IVR没有反应,排查时发现音频编码协商正常、视频画面也正常,就是DTMF丢了。
原因通常在RTP事件与视频流的负载类型冲突上。RFC 2833/4733规定DTMF用动态payload type(通常96到101),而视频编码也常用动态payload type。如果双方的SDP协商把同一个payload type号分配给了DTMF和视频编码,就会造成事件包和视频包在接收端被互相误解。
FreeSWITCH正常会统一重写paylaod type,但如果走的是inbound-bypass-media=true透传模式,FreeSWITCH不做SDP深层改写,此时两端的payload type冲突就直接暴露出来了。
解决思路分两层:如果必须用透传模式,提前检查终端SDP里DTMF事件使用的payload type,并确保对端能接受;如果不强求透传,就关掉inbound-bypass-media,让FreeSWITCH统一处理。同时确认全局DTMF类型设置:
<param name="dtmf-type" value="rfc2833"/>另外有一个原则:视频通话尽量统一用RFC 2833作为DTMF传输方式。SIP INFO在语音场景还能用,视频场景下因为信令和媒体的时序关系,SIP INFO的丢消息概率会上升,用户体验更差。
5.4 park hold与视频通话的保持问题
热词列表里的park hold其实是一个很实际的运维痛点。呼叫保持(Hold)在视频通话里不像语音那样平滑——语音保持时只要停发RTP包就够了,但视频保持时,如果只是简单地停发RTP,对端画面会冻结在最后一帧,用户会以为视频卡死了。
FreeSWITCH对视频呼叫保持的处理,是通过mod_callcenter里的park应用和媒体参数的重新协商来实现的。常用的做法是:在保持时让FreeSWITCH向对端发送一个修改后的SDP,把视频流方向改成sendonly或inactive,同时保持音频通道可用。这样对端视频显示会进入标准的保持画面(有的是黑屏,有的是“呼叫保持中”的提示),而不是冻结在最后一帧。
实操参数在dialplan的park应用前设置:
<action application="export" data="hold_video_media=sendonly"/> <action application="answer"/> <action application="park"/>这个细节很多项目都会踩:不加hold_video_media变量,分机按保持后对端画面直接冻结。加上后,大多数主流终端都能正确解析sdp的sendonly属性,自动进入保持状态。
5.5 其他容易忽视的坑:日志级别与分机注册参数
最后补几个零散但实用的点。
排查视频问题时,把日志级别调到debug是基本操作,但生产环境长期开debug不可取。我的习惯是:先开sofia global siptrace on抓SIP消息,这个级别下能清楚看到SDP offer/answer的完整内容,足以排查九成问题。只有在需要确认RTP媒体流向时才会临时开debug并抓包,问题解决后立刻关回notice级别。
分机注册方面,FreeSWITCH的directory配置里有一个参数跟视频有关:<param name="video-support" value="true"/>。虽然现在版本的FreeSWITCH默认不限制视频能力,但如果是老配置迁移过来的系统,最好检查一下这个参数。之前遇到过一个客户,从旧版本升级后视频加密通话全部失败,查了半天发现就是旧配置文件里残留了video-support=false。
另外,建议在分机或中继配置里考虑use-rtp-timer参数。视频流对抖动比语音更敏感,开启RTP定时器可以更早检测到媒体中断从而快速恢复或切换。默认情况下这个参数是false,对视频业务建议在sip profile里设为true。
6. 一些实践总结和后续扩展方向
文章写到这里,核心内容都过完了。最后分享几个亲自踩出来的体会,也许比上面大段的原理更实用。
第一个体会是,排视频问题一定要养成“看SDP全文”的习惯。很多工程师排查视频问题只看摘要或者只看端口号,但视频协商的核心信息全在SDP的m=video行、rtpmap和fmtp属性里。每次出问题,先把INVITE和200 OK的完整SDP打出来对比,很多问题一眼就能定位,这个习惯能省掉大把抓包分析时间。
第二个体会是,配置改动要形成“一次只动一个变量”的纪律。视频通话涉及变量很多,如果一次同时改码率、分辨率、协商模式、保持策略,出了问题根本没法定位。FreeSWITCH的变量体系是全局和逐通道堆叠的,改错一个就可能覆盖掉另一个,而日志并不会明确告诉你哪个变量覆盖了哪个。所以改配置前先记下原值,改完只验证一个指标。
第三个体会,预留好升级路径。FreeSWITCH的视频能力在持续变化,硬件的视频转码能力也在快速发展。如果业务刚起步,可以先用软件转码顶上,但要在架构上预留硬件加速或MCU的接入点。当并发视频路数突破某个量级时,软件转码的性价比会急剧下降,到那时候再临时考虑硬件方案,周期会拉得很长。