🎏:你只管努力,剩下的交给时间
🏠 :小破站
docker搭建freeswitch实现点对点视频,多人视频
本来我信心满满,打算在 CentOS 7 上一步一步搭建 FreeSWITCH,实现点对点视频通话和多人房间视频。毕竟网上"教程"那么多嘛,看起来也不复杂。可是真动手才发现,不是缺依赖就是编译不过,不是模块装不上就是版本冲突,踩坑踩到心态爆炸。搜索出来的博客,十有八九都是"人云亦云"的复制粘贴,根本跑不通。最终我还是选择了 Docker:简单粗暴,至少能保证环境统一,不至于自己跟自己打架。内存多花点就多花点,反正 FreeSWITCH 本身也不算重。
我实现了什么?
-
使用Docker部署FreeSWITCH服务
-
配置SIP配置文件支持WebRTC
-
配置SSL证书以支持WSS安全连接
-
配置STUN/TURN服务器以支持NAT穿透
-
实现SIP用户注册功能
-
实现基本的SIP呼叫流程
-
实现挂断通话功能
-
实现加入会议功能
-
支持多种会议URI格式(3000@default、3000-domain@profile等)
-
实现会议保活机制(定期发送DTMF信号)
-
实现视频网格布局
来个题外话
当以后大家出现本机搭建有问题的时候,并且很复杂的时候,可以无脑使用docker。(纯自己的看法,可能有问题,自己甄别),比如这次的freeswitch,各种博客+AI,但是在自己操作过程中真的各种报错,包括yum安装时候的版本问题,镜像源等等。遇到这种,就是万能公式,进入dockerhub查一下freeswitch
。能看到下面的内容:

臣附议:当你什么都不懂的时候就随大流吧,铁子!找下载最多的。
再附议 :其实它里面已经封装好了你要的所有东西,你只需要一行命令,大不了10行就OK了,直接不需要管繁琐的版本等等。
**再再附议:**我一般不喜欢用最新版本,所以我选择了最新版的前一个版本(没什么问题)。
它是什么?
首先我想啰嗦几句,和大家说说这个玩意它是干home的?
FreeSWITCH 是一个开源的通信软件平台,设计用于创建语音、视频和文本通信应用。它是一个强大的软交换(softswitch)解决方案,可以处理各种通信协议,包括 SIP、WebRTC、H.323 等。FreeSWITCH 既可以作为一个独立的电话系统,也可以作为更大规模通信系统的组件。
docker部署
sh
docker run --net=host --name freeswitch \
-e SOUND_RATES=8000:16000 \
-e SOUND_TYPES=music:en-us-callie \
-v /home/acowbo/freeswitch-sounds:/usr/share/freeswitch/sounds \
-v /etc/freeswitch/:/etc/freeswitch \
safarov/freeswitch:1.10.12
注意📢:这里可能会卡到,如果卡到的内容是这样的,你可以直接ctrl+c,然后重启容器即可,因为这个模块我们用不到,当然你也可以去除这个模块:

我来说明一下这个模块:
mod_signalwire 是 FreeSWITCH 中的一个用于接入 SignalWire 云通信平台的模块。
我们应该是用不到的,禁用方法有两种,一种直接配置文件查找后删除即可,另外一种容器已经启动,直接禁用。
第一种方式:
-
执行
vim /etc/freeswitch/autoload_configs/modules.conf.xml
可以看到如下图所示:
**第二种方式:**执行docker exec -it freeswitch fs_cli
后输入unload mod_signalwire
wss加密配置实现
这一步如果你使用的是certbot
就可以直接按照我的来,反正原理就是把证书挂载即可!https://mp.weixin.qq.com/s/e3vd0ritP8Dkp7SgsNqt8Q
就下面的两个文件,至于/etc/freeswitch/certs
,这个目录是不存在的需要先创建mkdir -p /etc/freeswitch/certs
sh
cp /etc/letsencrypt/live/status.acowbo.fun/fullchain.pem /etc/freeswitch/certs/wss.pem
cp /etc/letsencrypt/live/status.acowbo.fun/privkey.pem /etc/freeswitch/certs/wss.key
chmod 644 /etc/freeswitch/certs/wss.pem
chmod 640 /etc/freeswitch/certs/wss.key
chown -R root:root /etc/freeswitch/certs
上面的都弄好之后就是修改配置文件了:
执行vim /etc/freeswitch/sip_profiles/internal.xml
然后找到wss模块,如下图。建议端口不要用它之前的,安全一点

会议配置(房间)
需要修改的文件为/etc/freeswitch/autoload_configs/conference.conf.xml
,使用AI问问即可,当然我改的内容会贴出来:是在默认的profile下哈
xml
<param name="domain" value="你的公网ip"/>
<param name="conference-flags" value="audio-always|video-bridge|rfc-4579|livearray-sync|minimize-video-encoding"/>
<param name="conference-flags" value="livearray-sync"/>
<!-- 添加基本视频支持 -->
<param name="video-mode" value="mux"/>
<param name="video-layout-name" value="group:grid"/>
<param name="video-canvas-size" value="1280x720"/>
<param name="video-canvas-bgcolor" value="#333333"/>
<param name="video-layout-bgcolor" value="#000000"/>
<param name="video-codec-bandwidth" value="2mb"/>
<param name="video-fps" value="30"/>
<param name="video-auto-floor-msec" value="1000"/>
<!-- 添加视频布局控制 -->
<param name="video-layout-width" value="1280"/>
<param name="video-layout-height" value="720"/>
<param name="video-mute-exit-canvas" value="false"/>
<param name="video-required-for-canvas" value="false"/>
<param name="max-members" value="16"/>
<!-- 配置DTMF布局控制 -->
<param name="video-layout-group" value="grid"/>
<param name="video-layout-name" value="3x3"/>
<param name="video-layout-conf-flags" value="auto-3d-position|video-floor-only"/>
<param name="enable-video-layout-overlay" value="true"/>
<param name="video-layout-callout-timeout" value="10"/>
<param name="video-canvas-count" value="1"/>
效果展示


常见问题解决
注册出错解决
这里有很多种情况,目前我遇到的是两种,具体如下:
domain识别到的是容器ip
直接说问题:/etc/freeswitch/vars.xml
文件种的配置你没有进行修改,导致它默认读取的是容器的ip,可以使用vim,输入/来定位找到此内容:<X-PRE-PROCESS cmd="set" data="domain=$${local_ip_v4}"/>
然后将$${local_ip_v4}
改为你的公网ip即可。
验证方法:执行docker exec -it freeswitch fs_cli -x 'global_getvar domain_name'
,freeswitch
是我的容器名称,换为你的即可!最后输出的结果需要是你的公网ip才行。
用户密码不正确
这个目录下的这些个文件,我统称为用户,他这里有一个默认密码,如果你没有修改,就直接去/etc/freeswitch/vars.xml
里面找。

WSS访问问题
这里存在两个问题,一个是访问不到,一个是视频连接出错,也就是不能打视频
第一个问题排查如下:
- 你要确定你的wss的端口是可达的,这里你可以使用telnet来尝试
- 你要确定端口对应,别服务中的端口是10014,实际去绑定的时候是10012
- 千万别用nginx去做中转,如果做也是对应的wss,而非直接/处理
- 如果你排查下来发现还是不可访问,我说的不可访问是这个端口是通的,执行`lsof -i tcp:你的端口,也是可以看到有输出的,并且是执行freeswitch的。这个时候我建议你用他本来的端口,wss的是7443,ws的是5066。我的有一台服务器就是这样的,我用了10011,10014。服务也存在,也能连接上,就是不通,很奇怪。关键是用的freeswitch版本都一样。
第二个问题其实没什么排查的必要:因为是需要穿透的,也就是需要穿透nat。这个穿透的第三者作为一个桥梁,告诉他们两个对方的真实信息。具体搭建和使用可以看我的另一个文章:https://mp.weixin.qq.com/s/XxnaUMwP0dy8aVA6kHbGmA
SIP 设备(Tcp/Udp)不能视频
这里大致的情况就是打通了,管道中也存在,但是30s后就自动断开了。使用了抓流工具也看不出什么门道(在下比较菜),总结就是不通。哈哈哈~~,言归正传,大致是两种情况。如果想要自己抓包看一下,可以使用tcpdump -i any -nn -s 0 -w /tmp/fs_video_full.pcap "udp port 5060 or udp portrange 16384-32768 or tcp port 5060 or port 10014 or port 10011"
。
-
视频流和音频流不通,比如服务端发出响应,客户端没有回,或者说不知道怎么回。反之一样。
-
udp端口没开,默认是16384-32768,记住了是udp,udp,udp。(大部分就是这个原因了)
测试udp端口是否开用这个命令即可
*nc -vzu 你的服务器ip 16384*
常用命令
sh
# 查看所有SIP profile的状态
docker exec -it freeswitch fs_cli -x "sofia status"
# 查看某个profile的详细信息(如internal或external)
docker exec -it freeswitch fs_cli -x "sofia status profile internal"
# 查看 internal profile 下的所有注册用户信息(已注册的终端列表)
docker exec -it freeswitch fs_cli -x "sofia status profile internal reg"
# 重新加载XML配置(包括用户、拨号计划等)
docker exec -it freeswitch fs_cli -x "reloadxml"
# 列出当前所有正在运行的会议室及相关信息
docker exec -it freeswitch fs_cli -x "conference list"
# 挂断指定UUID的通话
docker exec -it freeswitch fs_cli -x "uuid_kill <uuid>"
# 显示当前所有活动的通道(channels),包括呼叫详情
docker exec -it freeswitch fs_cli -x "show channels"
# 查看指定 UUID 的通道详细信息,UUID 是通话的唯一标识符
docker exec -it freeswitch fs_cli -x "uuid_dump 44e97f0b-bc3c-41d8-9929-936a890c3cb4"
# 使用 Lua 脚本 dissolve_conference.lua 解散会议室,参数是会议室ID和发起者IP(这里示例是会议室12345,IP 154.11.80.119)
docker exec -it freeswitch fs_cli -x "luarun dissolve_conference.lua 12345-154.11.80.119"
加餐
lua脚本实现解散会议
有时候会议中的人可能退不出来,比如sip设备。这时候就需要别人辅助去解散会议了。目前有两种方式,第一种是通过后端api来实现调用(未使用),第二种就是使用会议发起人来实现(看似未使用,实则使用了)。
下面我针对第二种来说明实现思路:
解散会议和加会议其实是一样的,仅仅就是在解散会议的时候走一个lua脚本,然后脚本里调用了API,这一步和使用后端API神似。
需要确定几件事:
-
是否使用lua,这里提供了很多,比如py。
-
如果使用lua,确定模块是否已经加载?执行一下命令
docker exec -it freeswitch fs_cli -x "module_exists mod_lua"
,如果返回true,就证明加载了。当然还有一种方法就是看配置文件 -
在
/etc/freeswitch/autoload_configs/lua.conf.xml
修改如下参数<param name="script-directory" value="/etc/freeswitch/scripts/?.lua"/>
,这个好像没什么卵用,最后执行我还是带了全路径。
上面的都搞好了,就直接搞吧!!在/etc/freeswitch/dialplan/default.xml
加入如下内容
xml
<!-- acowbo 解散会议-->
<extension name="dynamic_end_conference_lua">
<condition field="destination_number" expression="^\*66(\d+)$">
<action application="set" data="conf_num=${regex(${destination_number}|^\\*66(\\d+)$|$1)}"/>
<action application="log" data="DEBUG: destination_number123=${destination_number}, conf_num123=${conf_num}"/>
<action application="set" data="conf_full=${conf_num}-${domain_name}"/>
<action application="log" data="INFO: domain_name=${domain_name}, conf_full=${conf_full}"/>
<action application="lua" data="/etc/freeswitch/scripts/dissolve_conference.lua ${conf_full}"/>
<action application="hangup"/>
</condition>
</extension>
此时,如果你的会议是3000,比如此时你加入会议了,你再执行一次加入会议,前面加*66,也就是*663000
。大哥们这里别怼,这里的按钮你可以是解散会议,但是执行的方法还是加入会议的方法。这样就实现了!
注意📢:这里必须是lua,而非luarun
,而在控制台必须是luarun
,比如docker exec -it freeswitch fs_cli -x "luarun dissolve_conference.lua 12345-154.11.80.119"