freeswitch 权威指南 --- 基础篇

FreeSWITCH 官网:https://signalwire.com/freeswitch
FreeSWITCH 中文站:https://www.freeswitch.org.cn
FreeSwitch 官方文档:http://rts.cn/freeswitch/FreeSWITCH-Explained
FreeSwitch:https://github.com/signalwire
FreeSwitch 案例:https://developer.signalwire.com/guides

内容来自 《FreeSWITCH 权威指南》:目录:https://juejin.cn/post/7020580794829635591
代码下载:https://book.dujinfang.com/download.html

1、freeswitch 基础

FreeSWITCH 新手指南

https://www.freeswitch.org.cn/blog/2009/11/beginners-guide/

FreeSWITCH是一个开源的电话软交换平台,主要开发语言是C。它有很强的可伸缩性,从最简单的软电话到商业级的软交换平台几乎无所不能。它支持SIP、H323、WebRTC等通信协议。另外,它还支持很多高级的SIP特性,如Presence、BLF、SLA以及TCP、TLS和sRTP 等。它可以作为SBC使用和多协议网关使用,也可以作为B2BUA连接其它的VoIP系统,如OpenSIPS、Kamailio、Asterisk等。

FreeSWITCH支持各种带宽的语音编解码,支持 8K、16K、32K及48KHz的高清通话,并可以在桥接不同频率的语音时自动进行转换。它可以运行在32位及64位的Windows、macOS、Linux以及Solaris等平台上,伸缩性极强,不管是一个信用卡大小的Raspberry Pi,还是数十、数百核的大型Linux服务器,都能运行 FreeSWITCH。此外,它还可以运行于Docker容器及K8S云原生环境中。

它支持 TTS(Text To Speach)、ASR(Automatic-Speach Recogonition)以及VAD(Voice Activity Detection)等。允许你使用Lua、Javascript、Python等嵌入式脚本语言来控制呼叫流程,或者你也可以通过 Event Socket 与 C、Go、Ruby、Erlang、Python、Perl、Java 等任何你所熟悉的语言进行交互。

为了避免重复发明轮子,它使用了相当多的第三方软件库。同时,为了方便编译和安装,很多第三方库代码都集成到了FreeSWITCH源代码树中。

它使用一种模块化、可扩展性的结构,只有必需的功能和函数才会加入到内核中,从而保证了其稳定。作为一款开源软件,它最大的好处就是你可以拿过来自己编译进行,并根据你的需要来开发自己的模块。

如果从源代码编译安装 FreeSWITCH 有困难,也可以试一下 XSwitch,它是一个已经编译好的FreeSWITCH,并且有专业的图形管理界面。

最快的学习FreeSWITCH的方法是使用 这个Docker镜象。如果不使用Docker镜象,也可以通过以下方式安装和使用FreeSWITCH。FreeSWITCH最理想的运行平台是64位的Linux。FreeSWITCH核心开发者大部分都使用 Debian。

  • 在Debian上安装FreeSWITCH的方法见:Debian | FreeSWITCH Documentation 。如果系统提示你需要一个PAT,可以参考如果创建一个FreeSWITCH PAT
  • 如果你使用 Windows,可以到 files.freeswitch.org 下载已编译好的安装包。如果安装有 Visual Studio 的开发环境,也可以下载源代码自己编译。
  • 也可以下载源代码编译。如果你提交BUG时,官方也只对 Git master分支中的代码提供支持。而且,从源代码安装FreeSWITCH,不需要PAT。

安装 FreeSWITCH 前需要安装一些依赖。在不同的平台上,依赖不同的包,如:

  • Debian/Ubuntu:apt-get -y install build-essential subversion automake autoconf wget libtool libncurses5-dev
  • CentOS:yum install -y subversion autoconf automake libtool gcc-c++ ncurses-devel make

FreeSWITCH 最新的源代码将 Sofia-SIP 和 SpanDSP 移出了 FreeSWITCH 代码仓库,分离到了独立的仓库中,在安装 FreeSWITCH 之前需要单独安装:

最新的mod_verto模块也需要libks,源代码可以从以下地址获取:

最好参考一下 http://www.freeswitch.org.cn/Makefile 以确定你的平台上应该安装哪些包。当然,该文件不是永远能保证最新的。

编译安装:

./bootstrap.sh # 初始化源码环境和工具
./configure    # 配置,如果将FreeSWITCH装到特定位置,可以使用 --prefix 指定
make           # 编译
make install   # 安装

详见:Installation GuideFreeSWITCH Explained.

在这里可以找到FreeSWITCH中文语音包

通信中名词解释

  • 信令:是指在通信系统中用于控制和管理通信过程的消息和协议 ( 相当于IP网络中的各种数据包,传统的通信是各种脉冲信号),主要传输控制信号。它在通信网络中扮演着关键的角色,用于建立、维护和终止通信会话。在传统电话通信中,信令用于控制呼叫建立、终止和管理呼叫过程中的其他功能,如转接、保持、转移等。通常,信令由 呼叫控制协议(如SS7)传输,这些协议定义了消息的格式、传输方式和交互过程。信令在IP网络和移动网络中也起着重要作用。例如,在VoIP(Voice over Internet Protocol)通信中,SIP(Session Initiation Protocol)是一种常用的信令协议,用于建立、修改和终止语音和视频通话。
  • 媒体(Media):信令主要传输一些控制信号,而通信双方需要听到对方的语音数据,这些语音数据就称为 媒体(Media)。随着通信系统能力的提高及更加高级、智能的终端设备的出现,通信系统所能传输的媒体类型也越来越丰富,比较典型的有语音,还有视频、文字信息(短消息)、传真等。
  • 呼叫控制协议(Call Control Protocol):用于控制和管理通信呼叫的协议。它定义了在通信系统中建立、修改和终止呼叫的过程和规则。几种常见的呼叫控制协议:SS7(Signaling System 7)又叫 7号信令:SS7是一种在传统电话网络中使用的信令协议,用于控制电话呼叫的建立、终止和管理。它提供了各种功能,如呼叫转移、呼叫等待和呼叫保持等。

    SIP(Session Initiation Protocol)中文可以翻译为 "会话初始协议、会话发起协议,也可粗略的叫做 呼叫控制协议":SIP是一种基于IP的呼叫控制协议,广泛用于VoIP(Voice over IP)电话系统和多媒体通信。它用于建立、修改和终止语音和视频通话,并支持会话管理、身份验证和媒体协商等功能。
    H.323:H.323是一组标准,用于在IP网络上进行实时语音和视频通信。它包含了一系列协议,包括呼叫控制协议(H.225)、媒体协议(H.245)和音频/视频编解码协议(如G.711、H.264等)。
    MGCP(Media Gateway Control Protocol):MGCP是一种用于控制媒体网关的协议,它用于管理IP电话网络中的呼叫控制和信令传输。MGCP将信令控制从媒体网关中分离出来,使得呼叫控制可以集中管理。
  • ISDN(Integrated Services Digital Network)是一种数字化的综合服务数字网。通过使用数字信号传输数据,相比于传统的模拟电话线路,提供了更高的传输质量和更快的数据传输速度。它采用了基于交换机的数字技术,允许用户同时传输语音、图像、数据和视频等。随着更先进的通信技术的出现,如宽带互联网和VoIP等,ISDN 的使用逐渐减少。然而,在一些特定的应用场景中,仍然存在使用ISDN的需求,如某些企业、远程地区和特殊行业等。
  • PSTN:公共交换电话网(Public Switched Telephone Network)的缩写,也被称为传统电话网络。它是一种基于传统电路交换技术的全球公共通信网络,用于传输模拟语音信号和数字数据。PSTN是由电话运营商建立和维护的,包括电信公司、运营商和其他通信提供商。它利用铜线、光纤和其他传输介质,通过交换机和交换设备进行电话呼叫的路由和连接。PSTN提供了传统的电话服务,允许用户通过电话拨号进行语音通信。
  • VoIP(Voice over Internet Protocol)是一种利用互联网协议(IP)传输语音和多媒体通信的技术。它将传统的电话通信转换为基于IP网络的数字通信方式。通过VoIP,语音信号被数字化并分割成数据包,然后通过互联网进行传输。这些数据包在传输过程中,可以与其他数据包一起通过相同的网络基础设施进行传输,如电子邮件、文本消息等。在接收端,数据包再次被重新组装为原始的语音信号。H.323  SIP(Session Initiation Protocol)都属于 VoIP 领域的通信信令。SIP 在VoIP系统中起着核心的作用,它提供了一种标准化的方式来建立和管理多媒体会话。通过 SIP,用户可以进行语音通话、视频通话和即时消息等实时通信。它已成为现代通信领域的重要协议之一。
  • PBX:私有分支交换机(Private Branch Exchange)的缩写,它是一种电话系统,用于在企业中 "管理、路由、连接" 电话通信。PBX系统允许内部免费通话、呼叫保持、转接电话、转移呼叫、语音邮件等功能。比较高级的小交换机还可以提供自动总机、三方通话、语音信箱等。它可以与 公共电话网络(PSTN)互联网电话服务(VoIP)相连,使组织内部和外部通信更加高效和灵活。传统的PBX系统通常是基于硬件设备的,包括电话线路、交换机、控制单元和接口板等。随着技术的发展,现代的PBX系统也有基于软件的解决方案,称为IP-PBX。IP-PBX利用互联网协议(IP)传输语音数据,可以集成传统电话系统和互联网通信,提供更丰富的功能和更低的成本。PBX系统广泛应用于各种组织和企业,包括办公楼、学校、医院、政府机构等。它可以提供通信管理、成本控制、安全性和便利性等优势,使得电话通信更加高效、灵活和可靠。用户或企业的PBX要想打通外面的电话,或者外面的电话需要打进来,需要走运营商提供的中继线,以接入到 PSTN 网上去。理解中继线的概念对于理解PBX以及PSTN是非常重要的,中继的接入方式决定了我们如何拨号,
  • IVR:是交互式语音应答系统(Interactive Voice Response system)的简称。它是一种自动化电话系统,通过语音和按键输入与来电者进行互动和信息交流。IVR系统通常被用于提供自助服务、电话银行、预约管理、调查问卷等场景。来电者可以通过按键或语音指令选择所需的服务或获取信息。IVR系统能够提高客户服务效率,减少人工操作的负担,并可根据来电者的需求提供个性化的服务。
  • IMS 是 IP多媒体子系统(IP Multimedia Subsystem)的缩写,它是一种基于IP网络的通信架构,通过引入IP技术,将传统的电信网络与互联网相结合,实现了各种通信方式的融合和交互。它提供了丰富的业务接口,支持语音通话、视频通话、实时消息、多媒体会议、群组通信等功能。IMS的设计目标是将传统的语音、视频、消息和数据业务融合到一个统一的平台中,使用户能够以各种方式进行实时通信和互动。IMS还支持移动网络和固定网络之间的互操作,使得用户可以在不同类型的网络上使用相同的服务和应用。

freeswitch 产生原因

FreeSWITCH 的默认配置就是一个家用或小型企业级的 PBX,它是由纯软件实现的,基于IP网进行通信,因而又称为 IP-PBX

用户或企业的 PBX 要想打通外面的电话,或者外面的电话需要打进来,需要走运营商提供的 "中继线路",运营商的 中继线路 再接入到 PSTN 网上去。这样就间接实现了 PBX 接入到 PSTN。中继的接入方式决定了我们如何拨号。

假设一家新开的公司,需要7部电话,于是向运营商(在 PSTN交换机上)申请了7条模拟中继线。实际上就是7条普通的电话线,只是运营商在PSTN交换机端对这7条线(也可以解释为7个号码)做了特殊的设置,将其逻辑上分为一个组,并为该组设了一个总机号假设是88888888(可以是一个虚拟的号码,或者是其中某一条中继线的真实号码)。而其他的中继线号则可能是44440001~44440007。现在,把这7条线都接上话机。如果有人呼叫88888888,则PSTN交换机会从7条线中自动选择一条空闲的线路呼入,因此某个电话会就会振铃。如果有多个电话呼入,只要同时呼入的电话不超过7个则公司的7部电话就都有机会振铃,因而可以同时对外为7个人同时提供服务。一般来说,当有电话呼入时,交换机有两种选线策略"顺序选线、循环选线"。所谓顺序选线,就是每次都从44440001 开始,寻找一条空闲的线路进行呼入;而循环选线则是每次都从上一次呼叫的下一个开始选起,使用这种选线方式,每个话机接到的电话数会比较平均。公司安装的7部电话的结构示意如图所示。

为维护企业形象,当有人呼出时,不管是从哪个分机呼出,都显示总机号 88888888。当然,也可以设置显示单线的号码(如44440004),这个要在 PSTN 交换机 端设置,一旦设置后,用户端不能动态更改。一个月后,公司发展到 21个人,因此需要 21 部电话。但由于一般不会出现所有人同时都在打电话的情况,故安装21条线有些浪费。因此公司买了一个小交换机,把原来的7条中继线接到小交换机的外线接口上,而把每个人的话机接到小交换机的内线口上,这样,每个人就都有了一个分机号,从601到621,而PSTN端的配置不变,

当客户打总机号时,PSTN交换机仍然会选择一条线进入公司的小交换机,这时候,选线方式已经不像以前那样重要,因为现在是小交换机在接电话,对它来说,7条线哪条都一样。就这样小交换机接了电话,并播放“您好,欢迎致电某某公司,请直拨分机号,查号请拨 0......”如果客户按某一分机号,则对应分机振铃。电话接通。

有了小交换机,内部通话就免费了。但出现了另外一个问题,就是如果拨打外线,则需要先拨一个特殊的数字,一般是0或9。有的小交换机会送二次拨号音,即你拿起电话,听到小交换机的拨号音,拨了0之后,则听到外部 PSTN 交换机的拨号音,表明你可以拨打外线了。总之,小交换机会选择一条空闲的中继线对外呼叫。上述例子中,21:7称为集线比,即3:1。集线比是由话务量决定的,如果同时通话的人数比较多,那我们可能会把中继线增加到12条,集线比就降为21:12,约为2:1了。即使增加了线路,也经常会遇到这样的情况:由于打进来的电话太多,占用了太多的线路,经常一个电话都打不出,因此,我们联系运营商,将中继线分为三组,其中4条只进不出,4条只出不进,4条能出能进。在电信术语中,分别叫做单出,单入和双向,而北京联通则分别称为发专、受专和双向。当然,这种分配方式降低了总体线路的使用率,为此我们把每个组都增加1条线,现在中继线总共达到15条。

又过了几天,有客户反映这样的情况,正常上班期间打电话经常无人接听,需要打好几遍;而同时,内部也有人反映往外打电话时有时拨0没反应,再试一次就好了。我们没有处理这种问题的经验,只好请教 PBX专家,专家说可能是某条外线断了。因为,如果有一条线断了,当有电话呼入时,交换机仍会向主叫方送回铃音,跟被叫端没接话机是一样的。但到底是哪条线断了,却不好查。由于双方都是自动选线。我们只好将每条线都从小交换机上拔下来,接上话机试一试,以确定是哪条线断了。还算比较幸运,我们找到了断线的号码,联系运营商,很快修好了,把所有线路都插回小交换机,一切恢复正常。

几天后,老板又很幸运地搞到了一个新号码66666666,该号码并未加入中继线组,而是直接扯了根线拉到老板办公桌上。为了能拨打内线,他不得不在办公桌上放两部电话,另一部专门打内线。后来技术人员小张仔细阅读了 PBX 的说明书,发现该小交换机功能还比较强,就进行了以下设置:将66666666 这个号码接到小交换机上,仍给老板一个内线电话同时在小交换机上进行设置,只有老板打出时才走 66666666这个端口;而对于打入的电话也不播放“欢迎致电XX公司··.”,而是直接向老板电话振铃。这种拨入方式叫做 DID,即对内直接呼叫(Direct Inbound Dial)。

接下来,随着公司的发展,加入的中继线条数越来越多,维护起来更加复杂。比如,像我们刚才遇到的情况,其中有一条线断了,在很长的一段时间内根本不知道,即使知道了要找到是哪条线也非常麻烦。后来,当公司发展到 100 人的时候,购买了新设备,并将模拟中继线换成了两条E1数字中继线,可同时支持60路通话。
公司发展一帆风顺,电话量也越来越多,公司有了很多分支机构,也有了更多客户,需要更复杂的语音菜单及更智能的电话分配策略,而更换专门的电话系统不仅价格昂贵,而且跟现有业务系统进行集成难度也很大。在综合考虑了多种解决方案以后,技术人员开始学习 FreeSWITCH。。。

FreeSWITCH 的默认配置就是一个家用或小型企业级的PBX,它是由纯软件实现的,基于IP网进行通信,因而又称为 IP-PBX。IP-PBX 首先是一个 PBX(Private Branch eXchange),它具有传统PBX的绝大部分功能。另外,由于使用了 IP 通信,它能通过 IP 网提供语音、视频以及即时消息通信。这些通信不仅可以在企业内部网上进行,也可以通过 Internet 在外网甚至 PSTN(Public Switched Telephone Network) 电话间进行。由于大部分PBX功能都是用软件实现的,因而实现起来成本相对来讲都非常廉价,并且非常易于增加新功能,如多方会议、使用XML-RPC等控制正在进行的通话、IVR、TTS/ASR(Text To Speech/Automatic Speech Recognition)支持通过模拟或数字线路与PSTN网络对接,支持通过SIPIAX、H323 或Jingle(Google对XMPP 协议的扩展)以及其他协议与其他通信系统进行互联互通等。

IP-PBX 更易于部署,尤其是基于IP 的通信更加廉价。但是,IP-PBX并不是解决所有问题的良药,老技术向新技术过渡总要有些取舍。比如,在IP环境中,IP 电话终端 更加智能,如摘机检测、挂机检测、收号等,这些原来由 PBX或交换机实现的功能现在都在终端上实现了,因而在PBX上很难获得这些信息的细节。另外,当它与传统的PBX或PSTN 网络对接时,还需要相应的VP网关来实现。当然,现在国内某些运营商也开始试验性地提供基于IMS或SBC的SIP中继这样对接起来就方便多了。

呼叫中心

基于企业级的 PBX 和 IP-PBX 的通信还只是局限于基础的通信层。而随着企业规模的扩大及用户对服务要求的提高,企业更需要在业务逻辑和管理层方面为用户提供更好的服务。当这些服务可以通过远程电话支持的方式解决的情况下,一种称为 呼叫中心的业务(CallCenter)便产生了。

在呼叫中心中,有专门的话务员为客户提供服务。呼叫中心通常能同时处理大量的通话,并且为了给客户更好的服务体验,呼叫中心的通信系统通常通过技术手段与CRM(Customer Ralationship Management,客户关系管理)系统集成。

通俗地讲:呼叫中心是企业或机构建立的以电话为主要手段,为客户提供服务与沟通的部门组织及信息系统。就像 110、119、120 这些应急服务电话,及电信运营商的客服电话、电话银行等都是呼叫中心的具体应用。在呼叫中心中通常是由座席代表通过电话为客户提供相应的服务与沟通。根据呼叫中心业务量不同,可以同时处理的电话量和座席代表的人数也有所不同。较小规模的呼叫中心只有几个人,大规模的呼叫中心会达到几千人。

freeswitch 简介

FreeSwitch 是开源的软交换平台,多媒体通讯平台!官方的定义是世界上第一个跨平台的、伸缩性极好的、免费的、多协议的电话软交换平台。FreeSwitch 从一个简单的软电话客户端到运营商级别的软交换设备几乎无所不能。

FreeSWITCH 的默认配置就是一个家用或小型企业级的 PBX,它是由纯软件实现的,基于IP网进行通信,因而又称为 IP-PBX

FreeSWITCH 最典型的应用是作为一个服务器 (它实际上是一个背靠背的用户代理,B2BUA),并用 电话客户端软件(一般叫软电话)连接到它。

虽然 FreeSWITCH 支持 IAX、H323、Skype、Gtalk 等众多通信协议,但其最主要的协议还是 SIP。支持 SIP 的软电话有很多,最常用的是 X-Lite 和 Zoiper。这两款软电话都支持 Linux、MacOSX 和 Windows平台,免费使用但是不开源。在 Linux 上你还可以使用 ekiga 软电话。

FreeSWITCH 是一个 B2BUA(背靠背的用户代理),所以它能做的工作非常多。

一些典型应用场景:在国外,很多ISP和运营商把它作为关键的软交换设备,处理成千上万路的并发通话,也有的把它用于呼叫中心,与各种企业级的应用系统(如CRM、ERP 等)集成,在国内,也已经有很多应用案例,其被广泛用于金融、保险、电力、石油、煤炭等领域的呼叫中心、企业通信以及应急指挥调度平台等。从这一方面讲,它是传统的电话交换系统及商业的电话交换系统良好的替代品。除了简单的替代以外,它往往还提供更多的新功能、更灵活的数据集成能力和更快速的应用开发能力,在业务需求千变万化的今天显得格外有生命力。另外,在当今的移动互联、物联网与大数据、云计算盛行的时代,好多厂商和互联网的创业者也把 FreeSWITCH 用于通信领域的“云”平台。FreeSWITCH 诞生的年代和背景良好的设计架构以及活跃的技术支持社区都是它能在“云”平台上成功的坚实基础。

FreeSWITCH 典型功能:

  • 在线计费、预付费功能
  • 电话路由服务器
  • 语音转码服务器
  • 支持资源优先权和OoS的服务器
  • 多点会议服务器
  • IVR、语音通知服务器
  • VoiceMail 服务器
  • PBX应用和软交换
  • 应用层网关
  • 防火墙/NAT穿越应用
  • 私有服务器
  • 第三方呼叫控制应用
  • 业务生成环境运行时引擎
  • 会话边界控制器
  • IMS中的S-CSCF/P-CSCF/I-CSCF
  • SIP网间互联网关
  • SBC及安全网关
  • 传真服务器、T30到T38 网关

OpenSER ( OpenSIPS、Kamailio ) 和 FreeSWITCH

OpenSIPS、Kamailio、FreeSWITCH 区别:https://www.toutiao.com/article/6804706056887861771

SIP 服务器有很多种类型,典型是以下几类:

  • 注册服务器:即只管 Register 消息,这里相当于location也在这里了
  • 重定向服务器:给ua回一条302后,转给其它的服务器,这样保证全系统统一接入
  • 代理服务器:只做proxy,即对SIP消息转发
  • 媒体服务器:只做 rtp 包相关处理,即 media server
  • B2BUA:这个里包实际一般是可以含以上几种服务器类型

OpenSER 后来分家了,一家叫 Kamailio,另一家是 OpenSIPS。Kamailio、OpenSIPS 和 FreeSWITCH 都是流行的开源通信软件,用于构建和管理实时通信系统。OpenSER ( OpenSIPS、Kamailio ) 和 FreeSWITCH 三个SIP服务器 应用场景和区别

  1. Kamailio:Kamailio(以前称为OpenSER)是一个高性能、可扩展的SIP(会话初始化协议)服务器。它专注于提供稳定和高效的SIP路由、注册和身份验证功能。Kamailio主要用于大规模VoIP网络和运营商级别的部署,可以处理大量的并发呼叫和用户注册。

  2. OpenSIPS:OpenSIPS是另一个强大的SIP服务器,也是一个多功能的实时通信引擎。它提供了比Kamailio更多的功能和灵活性,包括SIP路由、负载平衡、故障转移、身份验证、会话保持等。OpenSIPS适用于中小规模的VoIP部署,也可以作为SIP代理服务器或SIP调度器使用。

  3. FreeSWITCH:FreeSWITCH是一个全功能的开源软交换平台,支持音频、视频、消息和数据通信。与Kamailio和OpenSIPS不同,FreeSWITCH不仅仅局限于SIP协议,还支持其他协议如WebRTC、XMPP等。它提供了更广泛的媒体处理功能,包括音频/视频会议、语音信箱、IVR等。FreeSWITCH适用于构建复杂的实时通信系统,特别是需要多媒体处理和高级应用功能的场景。
    FreeSWITCH 自带一个 fs_cli 用于一些日常的监控和维护。但是因为FreeSWITCH的用户属性-业务优先,所以它有个 event_socket 这个模块,简称为 esl (event socket library) ,让用户以自己的业务需求,通过 socket 编程实现相应的处理,当然其还支持一堆的 lua/python/perl 等各语言的原生调用模块。

总的来说,Kamailio 和 OpenSIPS 更专注于提供高性能的 SIP 路由和核心通信功能,适用于大规模和中小规模的 VoIP 网络。而 FreeSWITCH 则提供了更广泛的媒体处理功能,适用于更复杂的实时通信系统。选择适合自己需求的软件取决于具体的应用场景和功能需求。

freeswitch 架构

FreeSWITCH 由一个稳定的核心(Core)及一些外围模块组成。这些外围的模块根据其功能和用途的不同又分为 Endpoint、Codec Dialplan、Application 等不同的类别,如下图:

FreeSWITCH 内部使用线程模型来处理并发请求,每个连接都在单独的线程中进行处理,不同的线程间通过 Mutex 互斥访问共享资源,并通过消息和异步事件等方式进行通信。更重要的是,即使某路电话发生问题,也只影响到它所在的线程,而不会影响到其它电话。这种架构能处理很高的并发,并且在多核环境中运算能均匀地分布到多颗CPU或单CPU的多个核心上。

FreeSWITCH的核心非常短小精悍,这也是其保持稳定的关键。绝大部分应用层的功能都在外围的模块中实现。外围模块是可以动态加载(以及卸载)的,在实际应用中可以只加载用到的模块。外围模块通过核心提供的 Public API与核心进行通信,而核心则通过回调(或称钩子)机制执行外围模块中的代码。

  • 公共应用程序接口 (Public API)。FreeSWITCH 在核心层实现了一些 Public API。这些 Public API 可以被外围的模块调用。例如,当FreeSWITCH外围的 Endpoint 模块收到一个呼人请求时,该模块就可以调用核心的 switch_core_session_request 函数为该呼叫生成一个新的 Session,此后,该呼叫的生命周期就由该 Session 管理。如果呼叫挂断,就调用 switch_core_session_destroy 函数将该 Session 释放。一般来说,与 Session 相关的函数都是与呼叫相关的。进一步细分,可以认为它主要与信令层相关,因为一个呼叫的生命周期是由信令来控制的。
  • 接口(interface)。FreeSWITCH在核心中除实现了大量的 Public API 供外围模块调用外,还提供了很多抽象的接口,这些接口对同类型的逻辑或功能实体进行了抽象,但没有具体实现。具体的实现一般由外围的模块负责,核心层通过回调(钩子)方式调用具体的实现代码或函数。
  • 事件 (event)。除了使用 Public API 及接口回调方式执行内部逻辑和通信外,FreeSWITCH在内部也使用消息和事件机制进行进程间和模块间通信。消息机制完全是内部的。在FreeSWITCH 外部,也可以通过 Event Socket等接口订阅相关的事件,通过这种方式可了解FreeSWITCH 内部发生了什么,如当前呼叫的状态等。fs cli 就是一个典型的外部程序,它通过 Event Socket 与 FreeSWITCH 通信,可以对 FreeSWITCH 进行控制和管理,也可以订阅相关的事件对 FreeSWITCH 的运行情况进行监控。订阅事件最简单的方法是:
    fs cli> /event plain ALL 在 fs_cli 中执行上述命令可以订阅所有的事件。FreeSWITCH的事件主要有两大类:一类是主事件可根据事件的名字(Event-Name)来区分,如CHANNEL ANSWER(应答)CHANNELHANGUP(挂机)等;另一类是自定义事件,它们的 Event-Name 永远是 CUSTOM,不同的事件类型可根据子类型(Event-Subclass)来区分,如 sofia:register (SIP注册)、sofia::unregister(SIP 注销)等。在 fs_cli 中可以单独订阅某类事件,如:
    fs_cli> /event plain CHANNEL ANSWER
    fs_cli> /event plain CUSTOM sofia::register
    后面会细地讨论事件机制、函数及相关用法。

freeswitch 模块

FreeSWITCH 在核心(Core)中提供了很多抽象的接口,这些接口对同类型的逻辑或功能实体进行了抽象,但没有具体实现,具体的实现一般由外围的模块负责,核心层通过回调(钩子)方式调用具体的实现代码。

  • Core (核心):FS Core 是 FreeSWITCH 的核心,它包含了关键的数据结构和复杂的代码,但这些代码只出现在核心中,并保持了最大限度的重用。外围模块只能通过 API 调用核心的功能,因而核心运行在一个受保护的环境中,核心代码都经过精心的编码和严格的测试,最大限度地保持了系统整体的稳定。核心代码保持了最高度的抽象,因而它可以调用不同功能,不同协议的模块。同时,良好的 API 也使得编写不同的外围模块非常容易。
  • 终点 (End Points ):是终结 FreeSWITCH 的地方,也就是说再往外走就超出 FreeSWITCH 的控制了。它主要包含了不同呼叫控制协议的接口,如 SIP, TDM 硬件,H323 以及 Google Talk 等。这使得 FreeSWITCH 可以与众多不同的电话系统进行通信。如,可以使用 mod_skypopen 与 Skype 网络进行通信。另外,前面也讲过,它还可以通过 portaudio 驱动本地声卡,用作一个软电话。
  • 应用程序(Application,APP):mod_dptools模块提供了系统中大部分的APP。FreeSWITCH 提供了许多 App 使复杂的任务变得异常简单,如 mod_voicemail 模块可以很简单地实现语音留言,而 mod_conference 模块则可以实现高质量的多方会议。FreeSWITCH 本身是一个B2BUA,而实际上所有与 FreeSWITCH的通话(或通过FreeSWITCH的通话) 都是在与一个或多个 App 在交互。mod_dptools 模块提供了系统中大部分的 App。
  • 聊天计划 (Chatplan):类似于 Dialplan,但是 Chatplan 主要对文本消息进行路由,如:SIP SIMPLE、Skype Message、XMPP Message 等。它是在 mod_sms 中实现的。
  • 编解码器(Codec):FreeSWITCH 支持最广泛的 Codec,除了大多数 VoIP 系统支持的 G711、G722、G729、GSM 外,它还支持 iLBC,BV16/32、SILK、CELT等。它可以同时桥接不同采样频率的电话,以及电话会议等。
  • 数据库(DB):在核心中实现。FreeSWITCH 的核心除了使用内部的队列、哈希表存储数据外,也使用外部的关系型数据库存储数据。使用外部数据库的好处是查询数据不用锁定内存数据结构,这不仅能提高性能,还能降低死锁的风险保证了系统稳定。命令 show calls、show channels 等都可直接从
    数据库中读取内容并显示。FreeSWITCH 支持多种流行的关系型数据库。为了尽量减少对其他系统的依赖,FreeSWITCH 默认使用的数据库类型是 SOLite。SOLite 是一种嵌入式数据库,FreeSWITCH 可以直接调用它提供的库函数来访问数据。由于 SQLite 会进行读锁定,因此在使用 SOLite 时不建议通过外部应用直接读取核心数据库。FreeSWITCH使用一个核心的数据库(默认的存放位置是/usr/local/freeswitch/db/core.db)来记录系统的接口 (interfaces)、任务(tasks)以及当前的通道(channels)通话(calls)等实时数据。某些模块如mod sofia mod fifo等都有自己的数据库(表)一般来说这些模块也提供相关的 API用于从这些表里查询数据。必要时,用户也可以直接查询这些数据库(表)来获取数据(如mod sofia中的分机注册数据等)。系统对数据库操作做了优化,在高并发状态下,核心会尽量将几百条 SOL语一起执行,这可以大大提高系统性能。windows 下FreeSwitch 连接mysqlhttps://blog.csdn.net/qq_22626483/article/details/124248905
  • 拨号计划 (Dialplan)Dialplan主要提供查找电话路由的功能,默认的 Dialplan 由mod_dialplan_xml 提供。也支持 Asterisk 格式的配置文件。另外它也持 ENUM 查询。
  • 嵌入式语言(Embeded Language):通过 swig 可支持多种嵌入式语言进而控制呼叫流程。如 Lua、Javascript、Perl 等。
  • 事件消费者(Event Consumer):通过Event Socket可以使用任何其他语音(只要支持Socket),通过TCP Socket可控制呼叫流程、扩展FreeSWITCH的功能。
  • 格式、文件接口(Format,FileInterface)。支持不同格式的声音文件回放、录音。如 WAV、MP3 等。mod_sndfile 模块通过 libsndfile 库提供了对大部分音频文件格式的支持。MP3 格式是在 mod_shout 中实现的。
  • 语音识别(ASR)、语音合成(TTS):支持语音自动识别(ASR),文本-语音转换(TTS)。
  • 命令接口(FSAPI):即FreeSWITCH API,简称API,它是一种对外的命令接口,它的原理非常简单——输入一个简单的字符串(以空格分隔),该字符串由模块的内部函数处理,然后得到一个输出。系统中大部分的API都是由mod_commands模块提供。
  • 日志(Logger)。日志可以写到控制台、日志文件、系统日志(syslog)以及远的日志服务器。实现日志功能的模块有 mod_console、mod_logfile、mod_syslog 等。
  • 分词短语(Say)
  • 定时器(Timer):实时的话音通话需要非常准确的定时器,在FreeSWITCH中,可以使用软时钟(soft timer)或内核提供的时钟来定时(如Linux中的timerfd或posixtimer)。FreeSWITCH 最理想的工作时钟频率是 1000Hz,而某些 Linux 发行版或虚拟机的内核的默认时钟可能是 100Hz或 250Hz,在这种情况下,可以使用内核提供的时钟接口,或者可以重新编译内核调整时钟频率。
  • XML接口(XML Interface):用来支持多种获取 XML 配置的方式,它可以是本地的配置文件,或从数据库中读取,甚至是一个能动态返回 XML 的远程 HTTP 服务器。对XML的解析和访问是在核心中实现的,但对XML的应用和扩展都是在外部模块中完成的。
  • 事件套接字 (Event Socket):可以使用任何其它语言通过 Socket 方式控制呼叫流程、扩展 FreeSWITCH 功能。

安装 freeswitch 

FreeSWITCH 默认的配置是一个 SOHO PBX ( 家用电话小交换机 ),可以实现分机互拨电话,测试各种功能,并通过添加一个 SIP-PSTN 网关拨打 PSTN 电话。

商业使用时都是使用 Linux 进行安装部署,但是今天是体验,所以使用 Windows。

最好从 github 直接下载源码编译安装。

  • Linux 默认安装位置在 /usr/local/freeswitch,源码安装成功后 freeswitch 源码目录最好保留,以便以后升级及安装配置其它组件。
  • windows 系统根据自己的系统选择不同的目录,然后就一直下一步下一步就可以了,安装完成后使用管理员执行 C:\Program Files\FreeSWITCH>FreeSwitchConsole.exe 便可启动,配置文件都在 C:\Program Files\FreeSWITCH\conf\

目录结构。windows 和 linux 的目录稍微有所不同。

  • bin:可执行程序目录。
    • freeswitch:服务器。
    • fs_cli:客户端。
    • fs_encode:将声音文件从一种编码转换到另一种编码。
    • fsxs:编译模块的辅助工具。不用Makefile也可编译,意味着不用源代码环境也可编译模块。
    • tone2wav:从一个TGML标记语言描述文件生成声音文件。
  • conf:配置文件目录。
    • autoload_configs:自动加载的模块的配置文件的目录。
      • *.conf.xml:一般每个模块一个配置文件,文件名格式为“不包含mod_的模块名.conf.xml”。
    • chatplan:聊天计划的配置文件的目录。
    • dialplan:拨号计划的配置文件的目录。
      • default.xml:默认Context,一般用于内部注册用户路由。
      • public.xml:一般用于外部来话路由。
    • directory:用户目录。支持多个域(Domain),每个域可以写到一个XML文件中。
      • default:默认域的SIP用户的配置目录。
        • *.xml:默认域的SIP用户,每个用户一个文件,文件名为用户名,依次为1000~1019。
      • default.xml:默认域。
    • freeswitch.xml:主配置文件,载入其他配置文件。
    • ivr_menus:IVR菜单配置目录。
    • jingle_profiles:连接Google Talk的相关配置的目录。
    • lang:多语言支持的配置目录。
      • en:英语。
      • fr:法语。
    • mrcp_profiles:MRCP的相关配置的目录,用于跟第三方语音合成和语音识别系统对接。
      • vestec-mrcp-v1.xml:Vestec支持MRCP的V1版本协议(RTSP)的配置。
    • sip_profiles:SIP配置文件目录。一般每个文件描述一个Profile。
      • external.xml:一般用于外部网关。
      • internal.xml:一般用于本地用户。
    • skinny_profiles:思科SCCP协议话机的配置文件目录。
    • vars.xml:定义一些全局变量。
  • db:数据库(sqlite)目录。将呼叫信息存放到数据中这样在查询时就无须对核心数据结构加锁
    • core.db:核心数据库。记录系统的接口(interfaces)、任务(tasks)以及当前的通道(channels)、通话(calls)等实时数据。
    • sofia_reg_*.db:SIP数据库。每个Profile一个文件。
  • grammar:语法文件目录,用于 ASR。
  • htdocs:HTTP Server根目录。
  • include:头文件目录。
  • lib:库文件目录。
    • libfreeswitch.so
  • log:日志文件及CDR话单目录。
    • cdr-csv:CSV话单目录。
      • Master.csv:话单汇总文件。轮替的文件加上.2006-01-02-15-04-05格式的后缀。
      • *.csv:每个独立分机一个话单文件。轮替的文件加上.2006-01-02-15-04-05格式的后缀。
    • cdr-pg-csv:话单写入ProstgreSQL失败时保存的本地文件话单目录。
    • freeswitch.log:日志文件。
    • freeswitch.xml.fsxml:完整的XML文档的镜像。
    • xml_cdr:XML话单目录。当话单POST请求失败时也会将话单保存至此。
  • mod:可加载的模块目录。
  • recordings:录音文件目录。record应用程序默认的存放路径。
  • run:运行目录,存放 FreeSWITCH 运行时的 PID
    • freeswitch.pid:存放FreeSWITCH运行时的PID。
  • scripts:嵌入式语言写的脚本的目录。lua、luarun、jsrun等应用程序默认的查找路径。
  • sounds:声音文件目录。playback 应用程序默认的查找路径。
    • en:英语提示音文件目录。
      • us:美式英语提示音文件目录。
        • callie:Callie(人名)录制的嗓音文件目录。每个子目录为某一类声音文件的目录。
          • ascii:ASCII字符提示音目录。每个子目录为某一采样率(Hz)的声音文件目录。
            • 8000:8000Hz语音的目录。
          • base256
          • conference
          • currency
          • digits:数字语音的目录。
          • directory
          • ivr:IVR提示音目录。
          • misc
          • phonetic-ascii
          • time
          • voicemail
          • zrtp
    • music:MOH保持音乐目录。
  • storage:语音信箱的录音文件及从其他HTTP服务器下载的语音文件缓存目录。

安装声音文件

在 Windows系统上,这些声音文件是默认安装的。声音文件有两种:

  • 提示音,用于通话期间的语音提示,如 VoiceMail 提示音、TTS功能的提示音等,
  • 音乐,用于在Hold 状态时播放,即所谓的 Music on Hold(MOH)。

在 Linux 或 Mac 上安装这些声音文件也异常简单。你只需在源代码目录中执行:

make sounds-install
make moh-install

安装过程中将自动从 files.freeswitch.org 下载相关的语音包,并解压缩到相关的安装路径中(默认安装在/usr/local/freeswitch/sounds下)。另外,FreeSWITCH 支持 8kHz、16kHz、32kHz及48kHz 的语音。与上面的声音文件相对应的高清声音文件可以选择安装。如以下命令安装 16kHz 的声音文件:

make cd-sounds-install
make cd-moh-install

freeswitch 对接 chatGPT

freeswitch-sip 电话和 chatGPT 连通:

freeswitch-chatGPT 是一个开源项目,旨在将 FreeSWITCH 与 OpenAI 的 Stream API 集成,并使用 FreeSWITCH 实现基于 MRCP 的 ASR(自动语音识别)和 TTS(文本转语音)。

freeswitch_chatGPT:https://github.com/laoyin/freeswitch_chatGPT

启动 freeswitch

在终端中执行 freeswitch 命令,可以将其启动到前台,启动过程中会有许多log输出,第一次启动时会有一些错误和警告,不用理会。启动完成后会进入到系统控制台(以下称称FS-Con)。并显示类似的提示符 "freeswitch@internal>" (以下简作 "FS> ")。通过在 FS-Con 中输入shutdown 命令可以关闭 FreeSWITCH。输入 help 可以查看帮助。

执行:C:\Program Files\FreeSWITCH>FreeSwitchConsole.exe 。执行成功后如图

如果想将 FreeSWITCH 启动到后台 (daemon,服务模式),执行 freeswitch -nc (No console)。后台模式没有控制台。

无论 FreeSWITCH 是在前台运行还是后台运行,都可以使用 fs_cli 连接 freeswitch 并进行控制。注意,在 fs_cli 中需要使用 fsctl shutdown 命令关闭 FreeSWITCH。当然也可以直接使用命令 freeswitch -stop 关闭。如果不想退出 FreeSWITCH 服务,只退出 fs_cli 客户端,则需要输入 /exit,或 Ctrl + D,或者直接关掉终端窗口。

freeswitch 配置文件

FreeSWITCH 配置文件默认放在 conf/

conf 是由一系列 XML 配置文件组成,手工编辑这些 XML 比较困难,有图形化的配置工具。系统还允许在某些 XML 节点上安装回调程序(函数),当这些节点的数据变化时,系统便自动调用这些回调程序。

XML配置文件:https://www.cnblogs.com/garvenc/p/freeswitch_learning_xml_configuration_file.html

FreeSWITCH默认的配置是一个SOHO PBX(家用电话小交换机)。

X-PRE-PROCESS标签是FreeSWITCH特有的,称为预处理指令,用于根据data参数设置(set)一些全局变量,以及将data参数指定的文件内容包含(include)到当前文件中。FreeSwitch在加载阶段只对其进行简单替换,并不进行语法分析,因此对它进行注释是没有效果的,解决办法是破坏X-PRE-PROCESS的定义(如将其替换为XPRE-PROCESS)。

可以$${VAR[:OFFSET[:LENGTH]]}形式引用全局变量,以${VAR[:OFFSET[:LENGTH]]}形式引用通道变量(局部变量)。OFFSET从0开始,如为负数表示从尾部开始。

配置文件的加载顺序如下:


   
  1. freeswitch.xml
  2. |- vars.xml
  3. |- autoload_configs / *.xml
  4. | [autoload_configs /acl.conf.xml]
  5. | [autoload_configs /callcenter.conf.xml]
  6. | [autoload_configs /cdr_csv_conf.xml]
  7. | [autoload_configs /cdr_pg_csv.conf.xml]
  8. | [autoload_configs /conference.conf.xml]
  9. | [autoload_configs /distributor.conf.xml]
  10. | [autoload_configs /event_socket.conf.xml]
  11. | [autoload_configs /fifo.conf.xml]
  12. | [autoload_configs /ivr.conf.xml]
  13. | `- ivr_menus / *.xml
  14. | [ivr_menus /demo_ivr.xml]
  15. | [autoload_configs /local_stream.conf.xml]
  16. | [autoload_configs /lua.conf.xml]
  17. | [autoload_configs /modules.conf.xml]
  18. | [autoload_configs /nibblebill.conf.xml]
  19. | [autoload_configs /post_load_modules.conf.xml]
  20. | [autoload_configs /sofia.conf.xml]
  21. | `- sip_profiles / *.xml
  22. | [sip_profiles / external.xml]
  23. | `- sip_profiles / external / *.xml
  24. | [sip_profiles / external /example.xml]
  25. | [sip_profiles /internal.xml]
  26. | [autoload_configs /switch.conf.xml]
  27. | [autoload_configs /tts_commandline.conf.xml]
  28. | [autoload_configs /xml_cdr.conf.xml]
  29. | [autoload_configs /xml_curl.conf.xml]
  30. | [autoload_configs /xml_rpc.conf.xml]
  31. |- dialplan / *.xml
  32. | [dialplan / default.xml]
  33. | `- dialplan / default / *.xml
  34. | [dialplan /public.xml]
  35. | `- dialplan /public / *.xml
  36. |- directory / *.xml
  37. | [directory / default.xml]
  38. | `- directory / default / *.xml
  39. | [directory / default / 1000.xml]
  40. `- lang /en / *.xml
  41. [lang /en /en.xml]
  42. `- lang /en /demo / *.xml
  43. [lang /en /demo /demo-ivr.xml]
AI生成项目

重要的配置文件:

  • autoload_configs/modules.conf.xml
  • autoload_configs/sofia.conf.xml
  • autoload_configs/switch.conf.xml
  • dialplan/default.xml
  • dialplan/public.xml
  • directory/default.xml
  • directory/default/1000.xml
  • freeswitch.xml
  • sip_profiles/external.xml
  • sip_profiles/external/example.xml
  • sip_profiles/internal.xml
  • vars.xml

freeswitch.xml 

最顶层、最重要的配置文件是 freeswitch.xml 。系统启动时 freeswitch.xml 会依次装入其它 XML 文件并最终组成一个大的 XML 文件 (将所有配置文件组合到一起)。组装后的 XML 非常大,而且已经对 X-PRE-PROCESS 标签进行了处理并隐藏了很多配置的细节。完整的 XML 文档分为以下几个重要的部分,每一部分又分别装入不同的 XML。

  • configuration (配置)
  • dialplan  (拨号计划)
  • chatplan (聊天计划)
  • directory (用户目录)
  • phrase (分词)

在加载 XML 时,FreeSWITCH 的XML解析器会先将预处理命令展开,在FreeSWITCH内部生成一个大的XML文档。log/freeswitch.xml.fsxml 是 FreeSWITCH 内部 XML 的一个内存镜像 ( 在log目录中,而不是在 conf 目录中,由于它是动态生成的,所以用户不应该手工编辑它)。它对调试非常有用。假设你不慎弄错了某个标签,又不知道它错在哪了,可以尝试让 FreeSWITCH 重新加载XML(执行命令:reloadxml ),这时在 FreeSWITCH 的日志中就可以看到 XML某一行出错的提示,在 freeswitch.xml.fsxml 就能很容易地定位到这一行。

为了便于了解,下面是精减的 freeswitch.xml 配置:


   
  1. <?xml version="1.0"?>
  2. <document type="freeswitch/xml">
  3. <X-PRE-PROCESS cmd="include" data="vars.xml"/>
  4. <section name="configuration" description="Various Configuration">
  5. <X-PRE-PROCESS cmd="include" data="autoload_configs/*.xml"/>
  6. </section>
  7. <section name="dialplan" description="Regex/XML Dialplan">
  8. <X-PRE-PROCESS cmd="include" data="dialplan/*.xml"/>
  9. </section>
  10. <section name="chatplan" description="Regex/XML Chatplan">
  11. <X-PRE-PROCESS cmd="include" data="chatplan/*.xml"/>
  12. </section>
  13. <section name="directory" description="User Directory">
  14. <X-PRE-PROCESS cmd="include" data="directory/*.xml"/>
  15. </section>
  16. <section name="languages" description="Language Management">
  17. <X-PRE-PROCESS cmd="include" data="lang/de/*.xml"/>
  18. <X-PRE-PROCESS cmd="include" data="lang/en/*.xml"/>
  19. <X-PRE-PROCESS cmd="include" data="lang/fr/*.xml"/>
  20. <X-PRE-PROCESS cmd="include" data="lang/ru/*.xml"/>
  21. <X-PRE-PROCESS cmd="include" data="lang/he/*.xml"/>
  22. <X-PRE-PROCESS cmd="include" data="lang/es/es_ES.xml"/>
  23. <X-NO-PRE-PROCESS cmd="include" data="lang/es/es_MX.xml"/>
  24. <X-PRE-PROCESS cmd="include" data="lang/pt/pt_BR.xml"/>
  25. <X-NO-PRE-PROCESS cmd="include" data="lang/pt/pt_PT.xml"/>
  26. <X-NO-PRE-PROCESS cmd="include" data="lang/sv/*.xml"/>
  27. </section>
  28. </document>
AI生成项目 XML

可以看到它的根是 document,在 document 中,有许多 section,每个 section 都对应一部分功能。其中 X-PRE-PROCESS 是预处理指令,它们的作用是将 data 参数指定的文件包含(include)到本文件中来。由于它是一个预处理指令,FreeSWITCH 在加载阶段只对其进行简单替换,并不进行语法分析,因此,对它进行注释是没有效果的,这是一个新手常犯的错误因为 当 FreeSWITCH 预处理时,还没有到达 XML 解析阶段,也就是说它还不认识 XML 注释语法,而仅会机械地将预处理指令替换,由于 XML 的注释不能嵌套,因此便产生错误的XML。解决办法是破坏掉 X-PRE-PROCESS 的定义。常用下面两种方法:

<xX-PRE-PROCESS cmd="include" data="vars.xml"/>   <!-- 前面加一个x -->
<XPRE-PROCESS cmd="include" data="vars.xml"/>      <!-- 去掉一个 中划线 -->

由于 FreeSWITCH 不认识 xX-PRE-PROCESSXPRE-PROCESS,因此它会忽略掉该行,相当于注释掉了。

vars.xml

vars.xml 主要通过 X-PRE-PROCESS 指令定义了一些全局变量,它们在 FreeSWITCH 运行期间永远都是有效的。而后面可能还会遇到局部变量,它们通常在拨号计划中,在一个呼叫的生命周期中才有效。如果需要引用这些变量,则全局变量以 $${var} 表示,临时变量以 ${var} 表示。

在加载 vars.xml 之前,FreeSWITCH 就已经“算”出并设置了一些全局变量,也就是说有些变量是系统在运行时自动设置的,其有默认的值。

在实际使用中,可以使用 global_getvar 或这个API命令来查看这些变量的值,如

由于这些变量是在 vars.xml 加载前设置的,因而可以在 vars.xml 中覆盖它们,如
<X-PRE-PROCESS cmd="set" data="local_ip_v4=192.168.1.123"/>
上面的设置经常会用到,因为有时候 FreeSWITCH 自动“算”出的值可能不是你想要的,如上面的 local_ip_v4 的值,在服务器有多个网卡的情况下,可能希望它能得到另外一个网卡的IP地址,这时候就可以通过手动的方式设置该变量的 IP 来实现。

autoload_configs 目录

autoload_configs 目录下面的各种配置文件会在系统启动时装入。一般来说都是模块级的配置文件,每个模块对应一个。文件名一般以 模块名.conf.xxml 方式命名。其中 modules.conf.xml 决定了 FreeSWITCH 启动时自动加载哪些模块。

dialplan 目录 ( 拨号计划 )

定义 XML 拨号计划,用户对电话进行 路由(选路)。

directory 目录 ( XML用户目录 )
  • conf/directory/ 是用户xml目录,目录中的配置文件决定了当 FreeSWITCH 作为注册服务器时,哪些用户可以注册,即用于配置本地用户,其中的配置信息称为用户目录。FreeSWITCH 的用户目录支持多个域(Domain),每个域可以写到一个XML文件中。默认的配置(系统自带的配置) 文件为 default.xml,里面定义了 1000 ~ 1019 共 20 个用户 ( 实际上是 include(包括) 子目录中的20个配置文件)。
  • SIP 并不要求一定要注册才可以打电话,但是通话前的用户认证参数仍需要在用户目录中进行配置。

   
  1. <include>
  2. <!--the domain or ip (the right hand side of the @ in the addr-->
  3. <domain name="$${domain}">
  4. <params>
  5. <param name="dial-string" value="{^^:sip_invite_domain=${dialed_domain}:presence_id=${dialed_user}@${dialed_domain}}${sofia_contact(*/${dialed_user}@${dialed_domain})},${verto_contact(${dialed_user}@${dialed_domain})}"/>
  6. <!-- These are required for Verto to function properly -->
  7. <param name="jsonrpc-allowed-methods" value="verto"/>
  8. <!-- <param name="jsonrpc-allowed-event-channels" value="demo,conference,presence"/> -->
  9. </params>
  10. <variables>
  11. <variable name="record_stereo" value="true"/>
  12. <variable name="default_gateway" value="$${default_provider}"/>
  13. <variable name="default_areacode" value="$${default_areacode}"/>
  14. <variable name="transfer_fallback_extension" value="operator"/>
  15. </variables>
  16. <groups>
  17. <group name="default">
  18. <users>
  19. <X-PRE-PROCESS cmd="include" data="default/*.xml"/>
  20. </users>
  21. </group>
  22. <group name="sales">
  23. <users>
  24. <!--
  25. type="pointer" is a pointer so you can have the
  26. same user in multiple groups. It basically means
  27. to keep searching for the user in the directory.
  28. -->
  29. <user id="1000" type="pointer"/>
  30. <user id="1001" type="pointer"/>
  31. <user id="1002" type="pointer"/>
  32. <user id="1003" type="pointer"/>
  33. <user id="1004" type="pointer"/>
  34. </users>
  35. </group>
  36. <group name="billing">
  37. <users>
  38. <user id="1005" type="pointer"/>
  39. <user id="1006" type="pointer"/>
  40. <user id="1007" type="pointer"/>
  41. <user id="1008" type="pointer"/>
  42. <user id="1009" type="pointer"/>
  43. </users>
  44. </group>
  45. <group name="support">
  46. <users>
  47. <user id="1010" type="pointer"/>
  48. <user id="1011" type="pointer"/>
  49. <user id="1012" type="pointer"/>
  50. <user id="1013" type="pointer"/>
  51. <user id="1014" type="pointer"/>
  52. </users>
  53. </group>
  54. </groups>
  55. </domain>
  56. </include>
AI生成项目 XML

一般来说,所有用户都应该属于同一个 domain(除非你想使用多 domain)。这里的 $${domain} 全局变量是在 vars.xml 中设置的,它默认是主机的 IP 地址,但也可以修改使用一个域名。

  • params 中定义了该 domain 中所有用户的公共参数。在这里只定义了一个 dial-string,这是一个至关重要的参数。当你在使用 user/user_name 或 sofia/internal/user_name 这样的呼叫字符串时,它会扩展成实际的 SIP 地址。其中 sofia_contact 是一个 API,它会根据用户的注册地址扩展成相应的呼叫字符串。
  • variables 则定义了一些公共变量,在用户做主叫或被叫时,这些变量会绑定到相应的 Channel 上形成 Channel Variable。
  • domain 中还定义了许多组(group),组里面包含很多用户(user)。在这里,组名 default 并没有什么特殊的意义,它只是随便起的,你可以修改成任何值。在用户标签里,又使用预处理指令装入了 default/ 目录中的所有 XML 文件。可以看到,在 default/ 目录中,每个用户都对应一个文件。

你也可以定义其它的用户组,组中的用户并不需要是完整的 XML 节点,也可以是一个指向一个已存在用户的 “指针”,如下图,使用 type="pointer" 可以定义指针。


   
  1.   <group name="sales">
  2.     <users>
  3.       <user id="1000" type="pointer"/>
  4.       <user id="1001" type="pointer"/>
  5.       <user id="1002" type="pointer"/>
  6.     </users>
  7.   </group>
AI生成项目 XML

虽然我们这里设置了组,但使用组并不是必需的。如果你不打算使用组,可以将用户节点(users)直接放到 domain 的下一级。但使用组可以支持像群呼、代接等业务。使用 group_call 可以同时或顺序的呼叫某个组的用户。
实际用户相关的设置也很直观,下面显示了 alice 这个用户的设置:


   
  1. <user id="alice">
  2.   <params>
  3.     <param name="password" value="$${default_password}"/>
  4.     <param name="vm-password" value="alice"/>
  5.   </params>
  6.   <variables>
  7.     <variable name="toll_allow" value="domestic,international,local"/>
  8.     <variable name="accountcode" value="alice"/>
  9.     <variable name="user_context" value="default"/>
  10.     <variable name="effective_caller_id_name" value="Extension 1000"/>
  11.     <variable name="effective_caller_id_number" value="1000"/>
  12.     <variable name="outbound_caller_id_name" value="$${outbound_caller_name}"/>
  13.     <variable name="outbound_caller_id_number" value="$${outbound_caller_id}"/>
  14.     <variable name="callgroup" value="techsupport"/>
  15.   </variables>
  16. </user>
AI生成项目 XML

由上面可以看到,实际上 params 和 variables 可以出现在 user 节点中,也可以出现在 group 或 domain 中。 当它们有重复时,优先级由高到低顺序为:user > group > domain。

sip_profiles

定义了 SIP 配置文件,实际上它是由 mod_sofia 模块在 autoload_configs/sofia.conf.xml 中加载的。本身比较复杂又是核心的功能

初体验:软电话 (sip软件)

示例:软电话(sip软件)

在同一局域网上的其它机器上安装 软电话(sip软件)。也可以在同一台机器上安装。只是需要注意:软电话不要占用 UDP 5060 端口,因为 FreeSWITCH 默认要使用该端口。执行命令 netstat -an | grep 5060 可以知道 FreeSWITCH 监听在哪个IP地址上。FreeSWITCH 默认配置了 1000 ~ 1019 共 20 个用户,可以随便选择一个用户进行配置,默认密码都是 1234

接下来需要下载 sip 终端用于通话。

可用的 sip 软件有 X-Lite (已经变成 Bria)、Yate client、eyeBeam、Linphone。

  • 对于 mac 或 windows 可以使用 X-Lite (已经变成 Bria)、eyeBeam、YateClient
  • 对于 android 建议使用 Linphone。
  • 对于 iOS 可以使用 wave lite 。

eyebeam V1.5 下载:https://download.csdn.net/download/freeking101/88751188

eyebeam网络电话 中文版v1.5(附序列号)、使用教程:http://www.3h3.com/soft/147163.html

eyebeam V1.5 下载 :https://www.xtxz.com/soft/3893.html

也可以选择其他 sip 终端,只要是在同一网络下的多台设备,就可以相互通信。

网络电话软件SIP常见客户端:https://www.cnblogs.com/wuchangsoft/p/16744391.html

  • Zoiper 几乎全平台支持。免费版只有基本功能,专业版收费有更多功能。官网:https://www.zoiper.com/en/voip-softphone/download/current
  • X-Lite、eyeBeam、Bria 官网地址:http://www.counterpath.com/。X-Lite (现在也称为 Bria Solo Free) 是一款软电话软件,有一个免费版本和三个付费版本,适用于个人、团队和组织。可以帮助用户从传统的电话使用过渡到VoIP。免费的功能非常有限,因此,如果想体验更多,则必须购买。
  • Blink 号称是最好的开源SIP客户端。地址:http://icanblink.com/
  • Doubango网络电话软件 ( https://www.doubango.org/)
  • Telephone 是一款开源免费的电话,界面非常简洁,仅支持 Mac
  • PC-Telephone 利用互联网和ISDN/PSTN拨打电话,把你的电脑变成一部网络电话,一个SDN电话、一部传真机,提供语音邮件、传真、数据传输等。
  • 开源的Android版的SIP客户端,支持多种注册方式
  • LinPhone 是开源跨平台,支持Windows、Linux、Mac、Android、iOS,支持视频。
  • Jitsi (https://jitsi.org/Main/Download)用Java开发的SIP客户端,支持音视频,支持录音,它是跨平台的,是开源的。功能简单,中文显示易懂。
  • baresip 是一个协议栈,不过,它带了一个完整的命令行SIP客户端,也很好用,它是开源的,跨平台的,支持语音和视频。http://creytiv.com/baresip.html;http://creytiv.com/pub/(linux)。
  • Ekiga(http://www.ekiga.org)Ekiga,原名GnomeMeeting,支持Windows和Linux,是一个兼容SIP和H.323的视频会议程序,兼容VoIP,IP电话,通过Ekiga可以与使用任何SIP和H.323软硬件的远程用户进行视频和音频对话。
  • GSWave Grandstream出品的免费的Android和iOS上的客户端。支持会议(6人会议)、短信、sip信息跟踪等,视频支持H.264。
  • Yate Yate其实包含服务端和客户端,它是开源的,支持Window、Linux、Mac OS X。功能较为简单,不支持视频
  • LifeSize(https://www.lifesize.com/)支持Windows、Mac、Android、iOS,不开源的,支持视频,支持会议,支持SIP和H323 。
  • PJSIP(http://www.pjsip.org/) PJSIP是个协议栈,也有个客户端,它是开源的,支持语音和视频。支持STUN、ICE、WebRTC AEC等,很多客户端都是用它作协议栈。它的实现是为了能在嵌入式设备上高效实现SIP/VoIP。
  • TIPcon1TIPcon1是一个Mac版的客户端工具

软件电话 是安装在计算机上的程序,可以通过互联网拨打电话。传统上这是需使用固定电话才能完成,但随着技术世界的不断变化,用户可以通过更多方式与他人联系。虽然固定电话有很多好处,但软电话也有很多好处。

这是使用的 sip 终端是 eyeBeam,打开 eyeBeam 软件将 sip 终端注册到 freeswitch 上去,按 Windows + r ,输入cmd进入命令行。输入netstat -an | grep 5060

红框框里的内容就是我们要记住的地址。保证 windows 系统防火墙已经关闭。在 sip 终端上设置一个 sip 账号,用户名可以为 1000 - 1019,这是 freeswitch 默认的 20 个用户,密码是1234,也是默认的,domain中输入我们刚才从命令行中得到的地址。手机端也是类似过程,只不过需要另换一个账户,所有账户的默认密码都是1234

电脑端

这是注册成功的界面。

手机端也是类似过程。安卓手机安装 Linphone apk (一个sip客户端软件, 下载地址:https://f-droid.org/zh_Hans/packages/org.linphone/),安装成功后,设置sip账号,这里使用 1010 ,输入秘密 1234。所有账户的默认密码都是1234。

电脑端 sip 账号 (电话号码):1000
手机端 sip 账号 (电话号码):1010
然后就可以相互呼叫对方了。

从手机上呼叫电脑端

到这里,初探 freeswitch 完成。。。

freeswitch 常用默认号码

9664    保持音乐
9191    注册CluCon
9192    调用info在log中显示Channel信息
9195    echo, 回音测试,延迟5秒
9196    echo,回音测试
9197    milliwatte extention, 铃音生成
9198    TGML铃音生成示例
9180    铃音测试,使用远端生成的回铃音
9181    铃音测试,产生英式铃音
9182    铃音测试,使用音乐当铃音,彩铃
9183    先应答,然后发送英式铃音
9184    先应答,然后发送音乐铃音
9178    收传真
9179    发传真
5000    IVR实例
4000    听取主意信箱
33xx    电话会议,48Hz(其中xx为00~99,下同)
32xx    电话会议,32Hz(其中xx为00~99,下同)
31xx    电话会议,16Hz(其中xx为00~99,下同)
30xx    电话会议,8Hz(其中xx为00~99,下同)
2000-2002    呼叫组
1000-1019    默认分机号码(默认密码1234)

FreeSwitch 默认号码大部分是拨号计划的名称,具体定义在 conf/diaplan/default.xml中, 如9182定义如下:


   
  1. <extension name="ringback_183_music_ring">
  2. <condition field="destination_number" expression="^9182$">
  3. <action application="set" data="ringback=$${hold_music}"/>
  4. <action application="bridge" data="{ignore_early_media=true}loopback/wait"/>
  5. </condition>
  6. </extension>
AI生成项目 XML

freeswitch 添加 sip 用户

添加一个新的SIP用户,熟悉 FreeSWITCH 的配置文件。FreeSWITCH 默认设置了20个用户(1000-1019),如果需要更多的用户,或者想通过添加一个用户来学习 FreeSWITCH 配置,只需要简单执行以下三步:

  • 在 conf/directory/default/ 增加一个用户配置文件
  • 修改 拨号计划(Dialplan) 使其它用户可以呼叫到它
  • 重新加载配置使其生效

示例:添加用户 king ,分机号是 1234。只需要到 conf/directory/default 目录下,将 1000.xml 拷贝到 1234.xml。打开1234.xml,将所有1000都改为1234。并把 effective_caller_id_name 的值改为 king,然后保存退出。如:<variable name="effective_caller_id_name" value="king"/>

xml 格式化工具:https://c.runoob.com/front-end/710/

接下来,打开 conf/dialplan/default.xml,找到 <condition field="destination_number"
expression="^(10[01][0-9])$"> 一行,改为 <condition field="destination_number" expression="^(10[01][0-9]|1234)$">,然后保存。

熟悉正则表达式的人应该知道,"^(10[01][0-9])$" 匹配被叫号码 1000-1019。因此修改之后的表达式就多匹配了一个1234。FreeSWITCH 使用 Perl 兼容的正则表达式(PCRE)。现在,回到 FS-Con,或启动 fs_cli,执行 reloadxml 命令或按快捷键 F6,使新的配置生效。

在软电话中添加一个sip为 1234 ,然后重新注册,注册成功后就可以与 1010 相互拨打。

在同一台机器上运行多个软电话可能有冲突,可以直接进入 FreeSWITCH 控制台使用命令进行测试:originate (翻译:起源,产生,创始,开创)

FS> sofia status profile internal      (显示多少用户已注册)
FS> originate user/1000 &echo      (拨打1000并执行echo程序)


FS> originate sofia/internal/1000 9999        (相当于在软电话1000上拨打9999)
FS> originate sofia/internal/1000 9999 XML default       (同上)

其中,echo() 程序一个很简单的程序,它只是将你说话的内容原样再放给你听,在测试时很有用。

freeswitch 用作 软电话

FreeSWITCH 也可以简单的用作一个软电话,如 eyeBeam,配置相比 eyeBeam 略微麻烦一些,但是可以了解到 freeswitch 的配置流程。freeswitch 也是目前唯一支持 CELT 高清通话的软电话。FreeSWITCH 使用 mod_portaudio 支持你本地的声音设备。

执行 FS> load mod_portaudio  载入模块

如果得到“Cannot find an input device”之类的错误可能是你的声卡驱动有问题。如果是提示“+OK”就是成功了。模块载入成功后,才能使用 pa 命令

接着执行:FS> pa devlist

FS> pa devlist

API CALL [pa(devlist)] output:
0;Built-in Microphone;2;0;
1;Built-in Speaker;0;2;r
2;Built-in Headphone;0;2;
3;Logitech USB Headset;0;2;o
4;Logitech USB Headset;1;0;i

以上是在我笔记本上的输出,它列出了所有的声音设备。其中,0 和 3 最后的 “i” 和 “o” 分别代表声音输入(in) 和 输出(out)设备。每个电脑上可能不一样,如果想选择其它设备,可以使用命令:

FS> pa indev #0
FS> pa outdev #2

以上命令会选择我电脑上内置的麦克风和耳机。接下来你就可以有一个可以用命令行控制的软电话了。

FS> pa looptest    (回路测试,echo)
FS> pa call 9999
FS> pa call 1000
FS> pa hangup

现在你可以呼叫刚才试过的所有号码。现在假设想从SIP分机1000呼叫到你,那需要修改拨号计划(Dialplan)。用你喜欢的编辑器编辑以下文件放到conf/dialplan/default/portaudio.xml

<include>
  <extension name="call me">
    <condition field="destination_number" expression="^(me|12345678)$">
      <action application="bridge" data="portaudio"/>
    </condition>
  </extension>
</include>

然后在 FS-Con 中按 F6 或输入 reloadxml 使配置生效,在分机1000上呼叫“me”或“12345678”(你肯定想为自己选择一个更酷的号码),然后在FS-Con上应该能看到类似“[DEBUG] mod_portaudio.c:268 BRRRRING! BRRRRING! call 1”的输出(如果看不到的话按“F8”能得到详细的Log),这说明你的软电话在振铃。多打几个回车,然后输入“pa answer”就可以接听电话了。“pa hangup”可以挂断电话。

当然,你肯定希望在振铃时能听到真正的振铃音而不是看什么BRRRRRING。好办,选择一个好听一声音文件(.wav格式),编辑conf/autoload_configs/portaudio.conf.xml,修改下面一行:<param name="ring-file" value="/home/your_name/your_ring_file.wav"/>  然后重新加载模块:

FS> reloadxml
FS> reload mod_portaudio

再打打试试,看是否能听到振铃音

配置 SIP网关 拨打 外部电话

如果你拥有某个运营商提供的 SIP 账号,那么你就可以通过配置SIP 来拨打外部电话了。该SIP账号(或提供该账号的设备)在 FreeSWITCH 中称为SIP 网关(Gateway)。添加一个网关只需要在conf/sip_profiles/external/中创建一个XML文件,名字可以随便起,如:gw1.xml


   
  1. <gateway name="gw1">
  2. <param name="realm" value="SIP服务器地址,可以是IP或IP:端口号"/>
  3. <param name="username" value="SIP用户名"/>
  4. <param name="password" value="密码"/>
  5. <param name="register" value="true"/>
  6. </gateway>
AI生成项目 XML

如果你的 SIP 网关还需要其它参数,可以参阅同目录下的 example.xml,但一般来说上述参数就够了。然后重启 FreeSWITCH,或者不重启直接执行下面命令使配置生效。

FS> sofia profile external rescan reloadxml

FS> sofia status    查看状态。如果 gateway gw1 显示 REGED 表示成功注册到了网关上。

FS> originate sofia/gateway/gw1/xxxxxx &echo()    命令测试网关是否工作正常

通过网关 gw1 呼叫号码 xxxxxx(可能是你的手机号),被叫号码接听电话后,FreeSWITCH 会执行 echo() 程序,你应该能听到自己的回音。

从某一分机上呼出

如果网关测试正常,你就可以配置从你的 SIP 软电话或 portaudio 呼出了。由于我们是把 FreeSWITCH 当作 PBX 用,我们需要选一个出局字冠。常见的 PBX 一般是内部拨小号,打外部电话就需要加拨 0 或先拨 9 。当然这是你自己的交换机,你可以用任何你喜欢的数字(甚至是字母)。继续修改拨号计划,创建新 XML文件: conf/dialplan/default/call_out.xml


   
  1. <include>
  2. <extension name="call out">
  3. <condition field="destination_number" expression="^0(\d+)$">
  4. <action application="bridge" data="sofia/gateway/gw1/$1"/>
  5. </condition>
  6. </extension>
  7. </include>
AI生成项目 XML

其中,(\d+)为正则表达式,匹配 0 后面的所有数字并存到变量 $1 中。然后通过 bridge 程序通过网关 gw1 打出该号码。当然,建立该XML后需要在 Fs-Con 中执行 reloadxml 使用之生效。当添加网关对外注册时,FreeSWITCH 就相当于一个SIP 客户端

呼入电话处理

如果你的 SIP 网关支持呼入,那么你需要知道呼入的 DID (Direct Inbound Dial,对内直接呼入) 。 一般来说,呼入的 DID 就是你的 SIP 号码。

编辑以下XML文件放到 conf/dialplan/public/my_did.xml


   
  1. <include>
  2. <extension name="public_did">
  3. <condition field="destination_number" expression="^(你的DID)$">
  4. <action application="transfer" data="1000 XML default"/>
  5. </condition>
  6. </extension>
  7. </include>
AI生成项目 XML

在 FS-Con 执行 reloadxml 使之生效。上述配置会将来话直接转接到分机 1000 上。在后面会说明如何更灵活的处理呼入电话,如转接到语音菜单或语音信箱等。

FreeSwitch 帮助

bin/freeswitch 和 bin/fs_cli 是两个常用命令,可以创建符号链接放到  /usr/local/bin/ 下,如果 /usr/local/bin 不在你的搜索路径中,可以把 /usr/local/bin 换成 /usr/bin/。也可以不创建符号连接,直接通过修改 PATH 环境变量以包含该路径。

ln -sf /usr/local/freeswitch/bin/freeswitch /usr/local/bin/
ln -sf /usr/local/freeswitch/bin/fs_cli /usr/local/bin/

freeswitch -h

FreeSWITCH 是 Client-Server结构,不管 FreeSWITCH 运行在前台还是后台,都可以使用客户端软件 fs_cli 连接 FreeSWITCH。FreeSwitch 服务端用来提供服务。

使用 help 命令可以列出所有命令的帮助信息:freeswitch -h 或者 freeswitch -help  。某些命令也有自己的帮助信息,如 sofia。FreeSWITCH 的命令参数没有统一的解析函数,而都是由命令本身的函数负责解析的,因而不是很规范,不同的命令可能有不同的风格。所以使用时,除使用帮助信息外,最好还是查阅一下 Wiki 上的帮助(mod_commands | FreeSWITCH Documentation),那里大部分命令都有相关的例子。关于 APP,则可以参考 mod_dptools | FreeSWITCH Documentation。本书的附录中也有相应的中文参考。

常用命令:
        freeswitch -nc
        freeswitch -nonat
        freeswitch -nc -nonat
通过查看 log/freeswitch.log 跟踪系统运行情况

查看 freeswitch 是否运行

        ps -aux | grep freeswitch
        netstat -an | grep 5060
        netstat -anp | grep 5060

FreeSwitch 默认端口:Firewall | FreeSWITCH Documentation (signalwire.com)

TCP 5060:SIP internal 默认端口。
TCP 5080:SIP external 默认端口。
TCP 8021:Event Socket 默认端口。
UDP 5060:SIP internal 默认端口。
UDP 5080:SIP external 默认端口。
TCP 8080:FreeSWITCH Portal 默认端口。

用法:freeswitch [OPTIONS]
可以传递给 freswitch 的可选参数:
        -nf                    -- 不允许fork新进程
        -reincarnate           -- 异常退出时重新启动
        -reincarnate-reexec    -- 重新启动时运行execv(有助于升级)
        -u [user]              -- 启动后以非root用户user身份运行
        -g [group]             -- 启动后以非root组group身份运行
        -core                  -- dump cores
        -help            --帮助
        -version         --打印版本并退出
        -rp              --启用高(实时)优先级设置
        -lp              --启用低优先级设置
        -np              --启用正常优先级设置
        -vg              --在valgrind下运行
        -nosql           --禁用内部SQL记分板
        -heavy-timer     --更精确的时钟,但要付出代价
        -nonat           --禁用自动NAT检测
        -nonatmap        --禁用自动NAT端口映射
        -nocal           -- 禁用时钟 calibration
        -nort            -- 禁用时钟 clock_realtime
        -stop            -- 停止 freeswitch
        -nc              -- 后台方式运行,没有控制台
        -ncwait          -- 后台模式。等待系统初始化完成后再退出父进程。隐含 -nc 选项
        -c               -- 启动到控制台。默认
控制文件位置的选项:
        -base [basedir]          --指定基准目录,在配置文件中使用 $${base}
        -cfgname [filename]      --FreeSWITCH主配置文件的备用文件名
        -conf [confdir]          --FreeSWITCH配置文件的备用目录
        -log [logdir]            --日志文件的备用目录
        -run [rundir]            --运行时文件的备用目录
        -db [dbdir]              --内部数据库的备用目录
        -mod [moddir]            --模块的备用目录
        -htdocs [htdocsdir]      --htdocs的备用目录
        -scripts [scriptsdir]    --脚本的备用目录
        -temp [directory]        --临时文件的备用目录
        -grammar [directory]     --语法文件的备用目录
        -certs [directory]       --证书的备用目录
        -recordings [directory]  --记录的备用目录
        -storage [directory]     --语音邮件存储的备用目录
        -cache [directory]       --缓存文件的备用目录
        -sounds [directory]      --声音文件的备用目录

freeswitch 控制台、常用命令

不带参数会启动到控制台,在控制台上可以输入各种命令以控制或查询 FreeSWITCH 的状态。

常用命令

reloadxml      修改 xml 后,用来重新加载配置文件

shutdown      退出
help               显示帮助
version           显示当前版本
status             显示当前状态

sofia status     显示 sofia 状态
show codecs    显示编解码器

sofia status profile internal    查看连接状态
sofia status profile internal reg    查看当前已经注册的用户
sofia profile internal siptrace on    打开sip日志打开sip日志
console loglevel 7    开启控制台日志级别, 0-7, 数字越大日志越多 
originate user/1000 &echo    呼叫 默认账号,可以用来测试
originate user/1000 9195      呼叫 默认账号,可以用来测试
originate user/1008 &playback(file_string://D:/Temp/1.wav)    自动拨打音频文件测试
hupall    挂断所有通话

bgapi        放到后台执行(命令执行都是阻塞的,有些命令需要执行相对较长的时间才能返回,因而有时我们会把某些命令放到后台执行)。

官方文档:https://freeswitch.org/confluence/display/FREESWITCH/mod_commands

在 freeswitch 或 fs_cli 可执行以下内部命令:只输入 tab,然后 enter 可以查看所有命令

输入 tab 可补全命令,输入空格再输入 tab 可补全子命令只输入命令不输入参数,或输入错误的参数,都会提示用法。

  • bgapi API [ARG [ ...]]:将API命令放到后台执行。
  • callcenter_config agent add AGENT callback|uuid_standby:向呼叫中心添加坐席。
  • callcenter_config agent list:列出呼叫中心所有坐席。
  • callcenter_config agent set ATTRIBUTE AGENT VALUE:设置呼叫中心坐席的属性。
  • callcenter_config queue list:列出所有呼叫中心所有队列。
  • callcenter_config tire list:列出呼叫中心所有梯队。
  • cdr_csv rotate:令CSV话单文件轮替。
  • conference:查看会议命令的帮助。
  • conference CONFERENCE agc MEMBERID|all|last|non_moderator VALUE:启用自动增益控制(Auto Gain Control)。
  • conference CONFERENCE bgdial CALLURL [CALLERIDNUMBER] [CALLERIDNAME]:从会议呼出,令对方加入会议。其是非阻塞的。
  • conference CONFERENCE chkrecord [FILEPATH]:检查会议录音情况。
  • conference CONFERENCE clear-vid-floor:取消视频会议的floor。
  • conference CONFERENCE deaf MEMBERID|all|last|non_moderator:将会议成员禁听。可指定所有成员、最后加入的成员、非主席成员。
  • conference CONFERENCE dial CALLURL [CALLERIDNUMBER] [CALLERIDNAME]:从会议呼出,令对方加入会议。其是阻塞的。
  • conference CONFERENCE dtmf MEMBERID|all|last|non_moderator DIGITS:向会议成员发送DTMF。可指定所有成员、最后加入的成员、非主席成员。
  • conference CONFERENCE energy MEMBERID|all|last|non_moderator VALUE:设置会议成员的能量值(音量超过此值才能混音到会议桥中)。可指定所有成员、最后加入的成员、非主席成员。
  • conference CONFERENCE enter_sound file FILEPATH:设置会议成员进入时向其它成员播放声音的文件。
  • conference CONFERENCE enter_sound none:设置会议成员进入时向其它成员播放声音为静音。
  • conference CONFERENCE enter_sound off:设置会议成员进入时向其它成员播放声音为禁用。
  • conference CONFERENCE enter_sound on:设置会议成员进入时向其它成员播放声音为启用。
  • conference CONFERENCE exit_sound file FILEPATH:设置会议成员退出时向其它成员播放声音的文件。
  • conference CONFERENCE exit_sound none:设置会议成员退出时向其它成员播放声音为静音。
  • conference CONFERENCE exit_sound off:设置会议成员退出时向其它成员播放声音为禁用。
  • conference CONFERENCE exit_sound on:设置会议成员退出时向其它成员播放声音为启用。
  • conference CONFERENCE file_seek [+|-]VALUE MEMBERID:会议中播放声音文件的快进、快退。
  • conference CONFERENCE floor MEMBERID|last:将会议成员设置为floor。没什么用。
  • conference CONFERENCE get PARAM:获取会议参数的值。参数有max_members、sound_prefix、caller_id_name、caller_id_number、endconference_grace_time。
  • conference CONFERENCE hup MEMBERID|all|last|non_moderator:挂断会议成员。可指定所有成员、最后加入的成员、非主席成员。
  • conference CONFERENCE kick MEMBERID|all|last|non_moderator:从会议中踢出成员。可指定所有成员、最后加入的成员、非主席成员。
  • conference [CONFERENCE] list:列出会议的成员。
  • conference CONFERENCE lock:锁定会议,别人无法加入。
  • conference CONFERENCE mute MEMBERID|all|last|non_moderator:将会议成员禁言。可指定所有成员、最后加入的成员、非主席成员。
  • conference CONFERENCE nopin:取消会议的密码。
  • conference CONFERENCE norecord [FILEPATH|all]:停止会议录音。
  • conference CONFERENCE pause [FILEPATH]:暂停会议中声音文件的播放,或暂停会议录音。
  • conference CONFERENCE pin PIN:设置会议的密码。
  • conference CONFERENCE play FILEPATH [MEMBERID]:向会议中播放声音文件,可指定成员。
  • conference CONFERENCE record FILEPATH:对会议录音。
  • conference CONFERENCE recording check|pause|resume|start|stop FILEPATH|all:如在会议的Profile中设置了录音摸板,则可如此检查/暂停/恢复/开始/停止录音。
  • conference CONFERENCE relate MEMBERID1[,...] MEMBERID2[,...] clear:清除会议成员前者和后者之间可否听到的关系。
  • conference CONFERENCE relate MEMBERID1[,...] MEMBERID2[,...] nohear:令会议成员后者听不到前者。
  • conference CONFERENCE relate MEMBERID1[,...] MEMBERID2[,...] nospeak:令会议成员后者说话前者听不到。
  • conference CONFERENCE resume [FILEPATH]:恢复会议录音。
  • conference CONFERENCE say TEXT:在会议中使用TTS播放文本。
  • conference CONFERENCE saymember MEMBERID TEXT:在会议中仅向某个成员使用TTS播放文本。
  • conference CONFERENCE set PARAM VALUE:设置会议参数的值。参数有max_members、sound_prefix、caller_id_name、caller_id_number、endconference_grace_time。
  • conference CONFERENCE stop all|current|last [MEMBERID]:停止会议中声音文件的播放。可指定所有正在播放、当前正在播放、最后播放。
  • conference CONFERENCE tmute MEMBERID|all|last|non_moderator:切换会议成员的禁言/不禁言状态。可指定所有成员、最后加入的成员、非主席成员。
  • conference CONFERENCE transfer OTHERCONFERENCE MEMBERID[ ...]:将会议成员转到另一个会议。
  • conference CONFERENCE undeaf MEMBERID|all|last|non_moderator:取消对会议成员的禁听。可指定所有成员、最后加入的成员、非主席成员。
  • conference CONFERENCE unlock:解锁会议,允许加入。
  • conference CONFERENCE unmute MEMBERID|all|last|non_moderator:取消对会议成员的禁言。可指定所有成员、最后加入的成员、非主席成员。
  • conference CONFERENCE vid-floor MEMBERID|last [force]:将视频会议成员设置为持有floor,在视频会议中所有成员都看到持有floor成员的画面。如指定force则固定成员,不再根据声音大小自动切换画面。
  • conference CONFERENCE volume_in MEMBERID VALUE:设置会议成员的输入音量。
  • conference CONFERENCE volume_out MEMBERID VALUE:设置会议成员的输出音量。
  • conference [CONFERENCE] xml_list:列出会议的成员,XML格式。
  • console loglevel console|alert|crit|err|warning|notice|info|debug:设置控制台日志等级。
  • curl URL:与HTTP服务器交互。
  • db delete|insert|select:持久化数据库存储。
  • distributor LIST:从号码连选的列表中选择一个节点名称。
  • echo STR[ ...]:原样输出字符串。
  • esf_page_group [MULTICASTIP MULTICASTPORT CONTROLPORT] :发送组播RTP包。三个参数的默认值依次为:224.168.168.168、34567、6061。
  • expand API [ARG [ ...]]:将${}$${}引起的变量替换为实际值后再执行API命令。
  • expr EXPR[;...]:计算表达式结果。EXPR可为:
    • 数学表达式:如1+1。
    • 函数:
      • ceil(NUMBER):向上取整。
      • random(BEGIN,END,&SEED):获取区间之间的随机数。
      • randomize(&SEED):设置随机数种子。
  • fifo list [LIST]:查看呼叫队列的状态。
  • fifo reparse:重新解析呼叫队列的配置。
  • fifo_member add LIST CALLURL:动态增加呼叫队列坐席。
  • fifo_member del LIST CALLURL:动态删除呼叫队列坐席。
  • fsctl crash:令FreeSWITCH崩溃。
  • fsctl max_sessions [VALUE]:查看或设置呼叫最大并发数。
  • fsctl sps [VALUE]:查看或设置每秒最大呼叫数。
  • global_getvar VAR:查看全局变量的值。
  • hash delete/HASH/KEY:删除内存中哈希表数据结构的键值对。
  • hash insert/HASH/KEY/VALUE:插入内存中哈希表数据结构的键值对。
  • hash select/HASH/KEY:获取内存中哈希表数据结构的键值对的值。
  • help:查看帮助。
  • hupall:挂断所有通话。
  • jsrun JSFILEPATH:执行JavaScript脚本。
  • load MOD:加载模块。
  • lua LUAFILEPATH [ARG [ ...]]:执行Lua脚本。在当前线程执行,会阻塞当前线程。
  • luarun LUAFILEPATH [ARG [ ...]]:执行Lua脚本。在新线程执行,不会阻塞当前线程。
  • nat_map status:查看NAT端口映射状态。
  • originate CALLURL EXTEN|&APP(ARG[ ...]) [DIALPLAN] [CONTEXT] [CALLERIDNAME] [CALLERIDNUMBER] [TIMEOUTSEC]:发起对CALLURL的呼叫,接听后将另一端转入Dialplan路由至EXTEN或执行APP。当使用XML Dialplan时EXTEN为另一个号码;当使用inline Dialplan时EXTEN为[m:C:]APP[:ARG][,...,APPN[:ARGN]],如果ARG有空格则需使用单引号括起,如ARG有逗号则将分隔符替换为字符C。DIALPLAN默认为XML。inline Dialplan会忽略CONTEXT,Lua Dialplan的CONTEXT为Lua文件路径。CALLERIDNAME为来电显示的名字,CALLERIDNUMBERBER为来电显示的号码。TIMEOUTSEC为对方收到INVITE消息后不回复100 Trying的超时秒数。会阻塞并在收到媒体指示后返回(如183或200消息)。
  • pa answer:接听电话。
  • pa call USERNAME:呼叫用户。
  • pa devlist:列出portaudio模块发现的设备。
  • pa hangup:挂机。
  • pa indev #N:使用第N个设备作为输入设备。
  • pa looptest:echo回路测试。
  • pa outdev #N:使用第N个设备作为输出设备。
  • python PYFILEPATH:执行Python脚本。文件路径为相对于Python的查找目录(如/usr/lib/python2.7/dist-packages/)。
  • regex STR | REGEX [| REPLACEMENT]:测试字符串STR是否匹配正则表达式REGEX,如有REPLACEMENT则使用其替换匹配的内容。REPLACEMENT可以使用$N%N的形式引用匹配的分组。
  • reload MOD:重新加载模块。
  • reloadxml:重新加载XML配置文件。
  • rtmp status:查看RTMP状态。
  • rtmp_contact [PROFILE/]USERNAME[@DOMAIN]:返回使用RTMP的已注册用户的联系地址。
  • show channels:查看正在通话的通道。
  • show codec:查看可用的编解码类型及其支持模块。
  • show dialplan:查看可用的Dialplan及其支持模块。
  • show file:查看可用的文件类型及其支持模块。
  • show nat_map:查看NAT端口映射关系。
  • shutdown:停止FreeSWITCH。
  • sofia global capture on|off:开启/关闭所有Profile的Homer方式抓包。
  • sofia global siptrace on|off:开启/关闭所有Profile的SIP消息打印。
  • sofia help:查看帮助。
  • sofia loglevel all|default|tport|iptsec|nea|nta|nth_client|nth_server|nua|soa|sresolv|stun 0|1|2|3|4|5|6|7|8|9:设置Sofia底层协议栈中指定模块的日志级别。越大信息越详细。
  • sofia profile PROFILE capture on|off:开启/关闭指定的Profile的Homer方式抓包。
  • sofia profile PROFILE flush_inbound_reg USERNAME@DOMAIN|CALLID:将指定的Profile中指定的已注册用户清除。
  • sofia profile PROFILE killgw GATEWAY:删除指定的Profile中指定的网关。
  • sofia profile PROFILE rescan:刷新指定的Profile。隐含reloadxml,并不是所有配置参数都能生效。
  • sofia profile PROFILE register GATEWAY:令指定的Profile中指定的网关立即向外注册。
  • sofia profile PROFILE restart:重启指定的Profile。隐含reloadxml。
  • sofia profile PROFILE siptrace on|off:开启/关闭指定的Profile的SIP消息打印。
  • sofia profile PROFILE start:启动指定的Profile。隐含reloadxml。
  • sofia profile PROFILE stop:停止指定的Profile。隐含reloadxml。
  • sofia profile PROFILE unregister GATEWAY:令指定的Profile中指定的网关立即向外注销。
  • sofia recover:从数据库中读出FreeSWITCH崩溃前的通话信息并恢复。
  • sofia status|xmlstatus:查看Sofia状态。
  • sofia status|xmlstatus gateway GATEWAY:查看指定网关的状态。
  • sofia status|xmlstatus profile PROFILE:查看指定的Profile的状态。
  • sofia status|xmlstatus profile PROFILE reg:查看指定的Profile所有已注册用户。
  • sofia status|xmlstatus profile PROFILE reg USERNAME:查看指定的Profile中指定的已注册用户。通过SIP的Contact头过滤。
  • sofia status|xmlstatus profile PROFILE user USERNAME@DOMAIN:查看指定的Profile中指定的已注册用户。
  • sofia tracelevel console|alert|crit|err|warning|notice|info|debug:console只会打印到控制台,不会写入日志文件。
  • sofia_contact [PROFILE/]USERNAME[@DOMAIN]:返回已注册用户的联系地址。
  • sofia_count_reg USERNAME@DOMAIN:查看该用户使用多少个客户端注册。在允许多点注册的情况下会有多个。
  • sofia_dig IP:返回其他服务器的地址和端口。类似DNS的dig。
  • sofia_presence_data list USERNAME@DOMAIN:列出指定用户的Presence信息。
  • sofia_presence_data status USERNAME@DOMAIN:列出指定用户的Presence状态。
  • sofia_presence_data user_agent USERNAME@DOMAIN:列出指定用户的Presence的user agent信息。
  • sofia_username_of USERNAME@DOMAIN:返回已注册用户的用户名。
  • status:查看服务器状态。
  • strepoch:显示当前的Unix时间戳。
  • strftime [FORMAT]:将当前时间格式化显示。
  • stun STUNSERVER:使用STUN服务器检测公网IP和端口。
  • system CLI [ARG [...]]:调用系统命令。
  • uptime:查看服务启动的秒数。
  • uuid_bridge CHANNELUUID1 CHANNELUUID2:使用UUID桥接两个通话Channel。
  • uuid_debug_media CHANNELUUID read|write|both|vread|vwrite|vboth on|off:打开/关闭指定通话Channel的媒体流调试信息。read为收,write为发,both为收发,v开头为可打印视频媒体流。每行输出包括以下信息:R或W表示收或发,呼叫字符串,b=表示RTP包大小(含包头),本地IP和端口,远端IP和端口,pt=表示载荷类型,ts=表示时间戳,m=表示RTP的Marker。
  • uuid_kill CHANNELUUID:释放通话Channel。
  • uuid_record CHANNELUUID start FILENAME:对指定的通话Channel开始录音。如不指定扩展名,则以原生格式录音。
  • uuid_record CHANNELUUID stop FILENAME|all:对指定的通话Channel停止录音。如使用all则停止通话Channel的所有录音。如不指定扩展名,则以原生格式录音。
  • uuid_setvar CHANNELUUID VAR VALUE:对指定的通话Channel设置通道变量。
  • uuid_transfer(CHANNELUUID CALLURL|lua:LUAFILENAME [DIALPLAN [CONTEXT]]):将通话重新转移到ROUTING阶段,重新去Dialplan中进行路由。
  • version:查看服务器版本。
  • xml_curl debug_on:打开mod_xml_curl模块的调试。会将每次请求得到的XML文件存放到系统临时目录中。

有些命令可以使用COMMAND help查看帮助。示例:

originate user/1000 &echo
originate user/1000 1001
originate user/1000 1001 XML
originate user/1000 echo
originate user/1000 echo inline
originate {origination_caller_id_name=test}{effective_caller_id_name=haha}user/1000 &bridge(user/1001)

快捷键

为了调试方便,FreeSWITCH 还在 conf/autoload_configs/switch.conf.xml 中定义了一些控制台快捷键。可以通过 F1-F12 来使用它们(不过在某些操作系统上,有些快捷键可能与操作系统的相冲突,那你就只直接输入这些命令或重新定义他们了)。

fs_cli -h

FreeSWITCH 是 Client-Server结构,不管 FreeSWITCH 运行在前台还是后台,都可以使用客户端软件 fs_cli 连接 FreeSWITCH。fs_cli 是一个类似 Telnet 的客户端,它使用 FreeSWITCH 的 ESL(Event Socket Library)库与 FreeSWITCH 通信。当然,需要加载模块 mod_event_socket。该模块是默认加载的。如果 fs_cli 启动失败,检车FreeSWITCH 有没有启动或 mod_event_socket 没有正确加载,检查TCP端口8021端口是否处于监听状态或被其它进程占用。

输入 /help <enter> 查看命令列表
用法: ./fs_cli [-H <host>] [-P <port>] [-p <secret>] [-d <level>] [-x command] [-t <timeout_ms>] [profile]
    -?,-h --help                    帮助
    -H, --host=hostname             freeswitch服务端的IP 
    -P, --port=port                 freeswitch服务端的端口 (1 - 65535)
    -u, --user=user@domain          user@domain
    -p, --password=password         密码
    -i, --interrupt                 允许 Control-c 中断
    -x, --execute=command           执行命令后,在退出
    -l, --loglevel=command          Log 级别
    -U, --log-uuid                  log 输出中包括 UUID
    -S, --log-uuid-short            在日志输出中包含缩短的UUID
    -q, --quiet                     禁用日志
    -r, --retry                     连接失败时重试
    -R, --reconnect                 断连是重新连接
    -d, --debug=level               Debug 级别 (0 - 7)
    -b, --batchmode                 Batch mode
    -t, --timeout                       API命令超时时间 (in milliseconds)
    -T, --connect-timeout           socket连接超时时间 (in milliseconds)
    -n, --no-color                  禁用彩色显示
    -s, --set-log-uuid              设置 UUID 来过滤 log events

-x 参数,它允许执行一条命令后退出。示例

bin/fs_cli -x "version"
bin/fs_cli -x "status"

进入控制台后,可执行以下命令:

  • 在 fs_cli 中,有几个特殊的命令是以 / 开头,这些命令并不直接发送到 FreeSWITCH,而是先由 fs_cli 处理。其它一些 / 开头的指令与 Event Socket 中相关的命令相同,如:
            /help        帮助
            /quit、/bye、/exit、Ctrl + D  都可以退出 fs_cli
            /event plain|json|xml EVENTTYPE [SUBCLASS]:订阅事件。
            /noevents   关闭事件接收
            /nixevent    除了特定一种外,开启所有事件
            /log       设置 log 级别,如 /log info 或 /log debug 等
            /nolog     关闭 log
            /filter    过滤事件
  • 另外,一些 "重要" 命令不能直接在 fs_cli 中执行,如 shutdown 命令,在控制台上可以直接执行,但在 fs_cli 中,需要执行 fsctl shutdown。除此之外,其它命令都与直接在 FreeSWITCH 控制台上执行是一样的。它也支持快捷键,最常用的快捷键是 F6(reloadxml)、F7(关闭 log输出)、F8(开启 debug 级别的 log 输出)。

日志级别可使用数值或字符串表示。数值越大显示越详细,字符串不区分大小写:

  • 0 - EMERG/CONSOLE
  • 1 - ALERT
  • 2 - CRIT
  • 3 - ERR
  • 4 - WARNING
  • 5 - NOTICE
  • 6 - INFO
  • 7 - DEBUG

uuid_kill <uuid>        # 挂断(结束)一个当前活动的通话
fsctl hupall               # 挂断所有当前活动的通话
fsctl hupall normal_clearing        # 挂断并发送正常挂断原因
fsctl hupall USER_BUSY              # 挂断并发送用户忙原因
fsctl hupall ORIGINATOR_CANCEL      # 挂断并发送发起者取消原因

fs_cli 连接 远程 fs

使用 fs_cli,不仅可以连接到本机的 FreeSWITCH,也可以连接到其它机器的 FreeSWITCH,通过在用户主目录下编辑配置文件 .fs_cli_conf(注意前面的点 "." ),可以定义要连接的多个机器:

[server1]
host     => 192.168.1.10
port     => 8021
password => secret_password
debug    => 7

[server2]
host     => 192.168.1.11
port     => 8021
password => someother_password
debug    => 0

一旦配置好,就可以这样使用它:

bin/fs_cli server1
bin/fs_cli server2

如果要连接到其他机器,要确保目标机器的 FreeSWITCH 的 Event Socket 是监听在真实网卡的IP地址上,而不是 127.0.0.1,可以修改 conf/autoload configs/event_socket.conf.xml 中的IP地址改成为服务器IP 或"0.0.0.0"实现,当然这可能带来潜在的安全性问题。如果你的服务器运行在公网上,则需要考虑你是否确实需要这样做,或者至少考虑设置一下ACL或防火墙规则只允许特定的IP 地址访问。当然,记得改完后在控制台上要执行 "reload mod_event_socket"

core.db

show 命令的大部分内容基于这些表。包含的表有:

  • aliases:别名表,用于存储命令行别名。
  • basic_calls:一个视图,基于channels和calls表,提供基本的呼叫信息。
  • calls:呼叫表,在bridge的呼叫中,用于关联Channel表中的两条腿。
  • channels:存储所有当前的Channel。
  • complete:存储所有Tab Complete数据。
  • detailed_calls:一个视图,基于channels和calls,提供详细的呼叫信息。
  • interfaces:存储所有的Interface。
  • nat:存储当前的NAT映射关系。
  • recovery:在使用系统恢复功能时,该表存储所有呼叫的详细信息。
  • registrations:存储注册用户的信息。与sofia_reg_PROFILE.db的sip_registrations表中有些数据是重复的。由于不止SIP用户需要注册,其它模块的用户也可能需要注册,此处存储的是统一的注册信息。
  • tasks:当前的任务表,如heartbeat(心跳)、检测IP地址变化等。

sofia_reg_PROFILE.db。每个Profile都使用一个单独的数据库(PROFILE替换为实际的Profile名字)。包含的表有:

  • sip_authentication:SIP认证相关。
  • sip_dialogs:SIP对话相关。
  • sip_presence:SIP呈现相关。
  • sip_registrations:SIP用户的注册信息。
  • sip_shared_appearance_dialogs:SIP SLA相关,记录当前的Dialog。
  • sip_shared_appearance_subscriptions:SIP SLA相关,记录订阅信息。
  • sip_subscriptions:SIP订阅相关。

sofia 命令

FreeSWITCH 的 sofia 命令用于管理和配置其 SIP 组件,如注册用户、呼叫路由、呼叫转移等。

下面是一些常用的 sofia 命令及其功能:

  • sofia status:显示 SIP 组件的状态信息,包括已注册用户、呼叫路由等。
  • sofia profile:管理 SIP 配置文件的配置选项。
    sofia profile internal start:启动内部配置文件。
    sofia profile external stop:停止外部配置文件。
  • sofia global:管理全局 SIP 配置选项。
    sofia global siptrace on:开启 SIP 跟踪。
    sofia global siptrace off:关闭 SIP 跟踪。
  • sofia register:管理 SIP 注册用户。
    sofia register <profile> <username> <password> <server>:注册一个 SIP 用户。
    sofia register <profile> unregister <username>:取消注册一个 SIP 用户。
  • sofia contact:管理 SIP 联系方式。
    sofia contact list:列出所有已注册的 SIP 用户。
    sofia contact kill <contact-id>:断开指定的 SIP 联系。
  • sofia gateway:管理 SIP 网关。
    sofia gateway list:列出所有已配置的 SIP 网关。
    sofia gateway kill <gateway-name>:停止指定的 SIP 网关。
  • sofia profile inbound_reg:管理入站 SIP 注册。
    sofia profile inbound_reg start:启动入站 SIP 注册。
    sofia profile inbound_reg stop:停止入站 SIP 注册。
  • sofia profile outbound_reg:管理出站 SIP 注册。
    sofia profile outbound_reg start:启动出站 SIP 注册。
    sofia profile outbound_reg stop:停止出站 SIP 注册。

这只是一些常用的 sofia 命令示例,FreeSWITCH 的 sofia 命令非常强大且灵活,还有很多其他选项和功能可以探索和使用。

发起呼叫

在 FreeSWITCH 中使用 originate 命令发起一次呼叫。示例:用户 1000 已经注册,那么:originate user/1000 &echo  命令在呼叫 1000 这个用户后,便执行 echo 这个程序。echo 是一个回音程序,即它会把任何它 "听到" 的声音(或视频)再返回(说)给对方。因此,如果这时候用户 1000 接了电话,无论说什么都能听到自己的声音。

呼叫 字符串 (Dial String)

user/1000 称为 "Dial String 呼叫字符串" 或 "呼叫 URL"。user 是一种特殊的呼叫字符串。

示例,假设:

  • FreeSWITCH  UA 的地址为 192.168.4.4:5050,
  • alice  UA 的地址为 192.168.4.4:5090,
  • bob  UA 的地址为 192.168.4.4:26000。

若 alice 已向 FreeSWITCH 注册,在 FreeSWITCH 中就可以看到她的注册信息:freeswitch@xxxx> sofia status profile internal reg

Registrations:
============================================================
Call-ID:        ZTRkYjdjYzY0OWFhNDRhOGFkNDUxMTdhMWJhNjRmNmE.
User:           alice@192.168.4.4
Contact:        "Alice" <sip:alice@192.168.4.4:5090;rinstance=a86a656037ccfaba;transport=UDP>
Agent:          Zoiper rev.5415
Status:         Registered(UDP)(unknown) EXP(2010-05-02 18:10:53)
Host:           du-sevens-mac-pro.local
IP:             192.168.4.4
Port:           5090
Auth-User:      alice
Auth-Realm:     192.168.4.4
MWI-Account:    alice@192.168.4.4

============================================================

FreeSWITCH 根据 Contact 字段知道 alice 的 SIP 地址 sip:alice@192.168.4.4:5090。当使用 originate 呼叫 user/alice 这个地址时,FreeSWITCH 便查找本地数据库,向 alice 的地址 sip:alice@192.168.4.4:5090 发送 INVITE 请求(实际的呼叫字符串是由用户目录中的 dial-string 参数决定的)。

呼叫字符串格式为(此处使用<>引起表示内容可选,/表示使用左侧或右侧内容,...表示重复之前内容。):<{GLOBALVAR=VALUE<,...>}...><[LOVALVAR=VALUE<,...>]>DIALSTR<,/|...>

{}括起的通道变量是全局的,作用于呼叫字符串中每一条腿;[]括起的通道变量为局部通道变量,只作用于呼叫字符串中靠近它的某一条腿。通道变量VALUE中的逗号需要使用\转义,或在VALUE的开头使用^^C来将分隔符替换为指定的字符C。DIALSTR使用,隔开表示同振,使用|隔开表示顺振。

DIALSTR可为(一般来说,每种Endpoint都会提供相应的呼叫字符串,每一种呼叫字符串的类型都属于一个Endpoint Interface):

  • freetdm/SPAN/CHANNEL/USERNAME:mod_freetdm呼叫字符串。CHANNEL为a表示从小到大选择一个空闲时隙,为A表示从大到小选择一个空闲时隙。
  • h323/USERNAME@IP:mod_h323的h323呼叫字符串。
  • loopback/USERNAME:loopback呼叫字符串。
  • opal/h323:USERNAME@IP:mod_opal的h323呼叫字符串。
  • rtmp/UUID/USERNAME@IP[:PORT]:rtmp呼叫字符串。
  • sofia/PROFILE/[sip:]USERNAME[@IP[:PORT]]:指定PROFILE的用户。
  • sofia/gateway/GATEWAY/USERNAME:网关用户。
  • user/USERNAME[@DOMAIN]:本地用户。

示例:

{origination_caller_id_name=test}{effective_caller_id_name=haha}user/1000
{origination_caller_id_name=test}{effective_caller_id_name=haha}user/1000|sofia/internal/1001
{origination_caller_id_name=test}[effective_caller_id_name=haha]user/1000,[effective_caller_id_name=hehe]sofia/internal/1001

API 与 APP

originate 命令(Command)用于控制 FreeSWITCH 发起一个呼叫。FreeSWITCH 的命令不仅可以在控制台上使用,也可以在各种嵌入式脚本、Event Socket (fs_cli 就是使用了 ESL库)或 HTTP RPC 上使用,所有命令都遵循一个抽像的接口,因而这些命令又称 API Commands。

echo() 则是一个程序(Application,简称 APP),它的作用是控制一个 Channel 的一端。我们知道,一个 Channel 有两端,在上面的例子中,alice 是一端,别一端就是 echo()。电话接通后相当于 alice 在跟 echo() 这个家伙在通话。另一个常用的 APP 是 park()

示例:originate user/alice &park()

初始化了一个呼叫,在 alice 接电话后对端必须有一个人在跟也讲话,否则的话,一个 Channel 只有一端,那是不可思议的。而如果这时 FreeSWITCH 找不到一个合适的人跟 alice 通话,那么它可以将该电话“挂起”,park() 便是执行这个功能,它相当于一个 Channel 特殊的一端。

park() 的用户体验不好,alice 不知道要等多长时间才有人接电话,由于她听不到任何声音,实际上她在奇怪电话到底有没有接通。相对而言,另一个程序 hold()则比较友好,它能在等待的同时播放保持音乐(MOH, Music on Hold)。

示例:originate user/alice &hold()

也可以直接播放一个声音文件:originate user/alice &playback(/root/welcome.wav)

或者 直接录音: originate user/alice &record(/root/voice_of_alice.wav)

以上的例子实际上都只是建立一个 Channel,相当于 FreeSWITCH 作为一个 UA 跟 alice 通话。它是个一条腿(one leg,只有a-leg)的通话。

在大多数情况下,FreeSWITCH 都是做为一个 B2BUA 来桥接两个UA 进行通话的。在 alice 接听电话以后,bridge()程序可以再启动一个 UA 呼叫 bob:originate user/alice &bridge(user/bob) 。终于,alice 和 bob 可以通话了。我们也可以用另一个方式建立他们之音的通话:

originate user/alice &park()
originate user/bob &park()
show channels
uuid_bridge <alice_uuid> <bob_uuid>

在这里,我们分别呼叫 alice 和 bob,并把他们暂时 park 到一个地方。通过命令 show channels 我们可以知道每个 Channel 的 UUID,然后使用 uuid_bridge 命令将两个 Channel 桥接起来。与上一种方式不同,上一种方式实际上是先桥接,再呼叫 bob。

上面,我们一共学习了两条命令(API),originate 和 uuid_bridge。以及几个程序(APP) - echo、park、bridge 等。可以发现 uuid_bridge API 和 bridge APP 有些类似,他们一个是先呼叫后桥接,另一个是先桥接后呼叫,那么,它们到底有什么本质的区别呢?

api 和 app 区别

  • 一个 APP 是一个程序(Application),它作为一个 Channel 一端,与另一端的 UA 进行通信,相当于它工作在 Channel 内部;在 dialplan 中执行的程序都是 APP( dialplan 中也能执行一些特殊的 API)。APP 通过 mod_dptools 模块加载,因而 APP 又称为拨号计划工具(Dialplan Tools)。
  • 一个 API 则是独立于一个 Channel 之外的,它只能通过 UUID 来控制一个 Channel。通常在控制台上输入的命令都是 API;大部分公用的 API 都是在 mod_commands 模块中加载的;
  • 某些模块(如 mod_sofia)有自己的的 API 和 APP。某些 APP 有与其对应的 API,如上述的 bridge/uuid_bridge,还有 transfer/uuid_transfer、playback/uuid_playback等。UUID 版本的 API 都是在一个 Channel 之外对 Channel 进行控制的,它们不参与到通话中,但是却可以对正在通话的 Channel 进行操作。场景:例如 alice 和 bob 正在畅聊,有个坏蛋使用 uuid_kill 将电话切断,或使用 uuid_broadcast 给他们广播恶作剧音频,或者使用 uuid_record 把他们谈话的内容录音等。

呼叫流程、相关概念

在 FreeSWITCH 中,每一次呼叫都由一条或多条 "腿" (Call Leg) 组成,每一条腿又称为一个 Channel(信道),每一个 Channel 都有好多属性,用于标识 Channel 的状态,性能等。

总结:只要发起呼叫,就会产生一条腿,或者一个 Session (也可以叫做Channel通道)

来话 (Inbound call)、去话 (Outbound call)

典型的呼叫流程有以下两种:

  • 单腿呼叫:这也是比较常见的呼叫,通常用于语音通知,IVR 等业务场景。单腿呼叫只有一个call leg,这里FreeSWITCH自身作为一方与UA进行“通话”。
  • 两方呼叫:这是最常见的呼叫,它用于完成两个用户 UAa 和 UAb之间的通话。
    在通过 FreeSWITCH 建立的两方呼叫中,包含两个 call leg;
    FreeSWITCH(作为B2BUA)分别与UAa建立一个call leg(a-leg) 和 UAb 建立一个call leg(b-leg)。建立后,FreeSWITCH将两个 call leg 桥接起来,UAa 和 UAb 两方就可以进行通话了。
    这一种又有一种变种:如市场上有人利用上、下行通话的不对称性,卖电话回拨卡获取不正当利润:UAa 呼叫 FreeSWITCH,FreeSWITCH 不应答,而是在获取 UAb 的主叫号码后直接挂机;然后 FreeSWITCH 回拨 UAb;UAb 接听后 FreeSWITCH 启动一个 IVR 程序指示 UAb 输入 UAa 的号码;然后 FreeSWITCH 呼叫 UAa

呼叫腿 (Call legs) 是呼叫的基本构成,一个呼叫中可能包含一个呼叫腿(单腿呼叫),也可能包含两个呼叫腿(两方呼叫),甚至多个呼叫腿(多方呼叫)。对 FreeSwitch 而言,在一个呼叫中,作为B2BUA,它和每个对端的 SIP UA 之间的连接都构成了一个 call leg。

根据 SIP呼叫发起的方向 ( 都是以 FreeSWITCH 作为参照物),call leg 可以分为来话和去话两种

  • 来话(Inbound call):对端为主叫,FreeSwitch是被叫
    示例:"用户UAa ---> FreeSWITCH" 的通话称为 来话
  • 去话(Outbound call):对端是被叫,FreeSwitch是主叫。
    示例:"FreeSWITCH ---> 用户UAb" 的通话称为 去话。

在两方通话中,根据call leg建立的先后关系,call leg常被称为A leg 和 B leg

  • a-leg:通常是先建立的 call leg,对应主动发起呼叫的一方。
  • b-leg:通常是后建立的 call leg, 对应被动接受呼叫一方。
  • 通俗理解:相对于 freeswitch 来说,进来的是 a-leg,出去的是 b-leg

注:无论来话还是去话,对每一次呼叫 FreeSWITCH 都会启动一个Session (会话) 用于控制整个呼叫,它会一直持续到通话结束。在FreeSWITCH中,一个channel 代表一个call leg。

每个 Session 都控制着一个 Channel (通道,又称信道),Channel 是一对UA间通信的实体,相当于FreeSWITCH的一条腿(leg),每个 Channel 都用一个唯一的 UUID 来标识,称为Channel UUID。另外,Channel 上可以绑定一些呼叫参数,称为 Channel Variable (通道变量)。Channel 中可能包含媒体(音频或视频流)也可能不包含。通话时,FreeSWITCH 的作用是将两个 Channel (a-leg 和 b-leg,通常先创建的或占主动的叫a-leg)桥接(bridge)到一起,使双方可以通话。这两路桥接的通话(两条腿)在逻辑上组成一个通话,称为一个 Call。

呼叫 "基本概念、发起模式"

有两种典型的呼叫发起模式

  • 1PCC(First-party call-control)
  • 3PCC(Third party call control)

如上图所示,1PCC 与 3PCC 关键的区别在于第一段呼叫的发起

  • 1PCC模式中,第一段呼叫(a-leg)是由Caller SIP UA通过SIP INVITE发起的,在FreeSwitch中呼叫是由SIP协栈(Sofia)触发的。
  • 3PCC模式中,第一段呼叫(a-leg)在FreeSwich中是应用(即所谓third pary)触发的(通过originate命令),SIP呼叫是FreeSwitch主动发起的。

在1PCC模式中,第一段呼叫(a-leg)中,FreeSwitch是被叫,因此这段呼叫是来话(inbound call),第二段呼叫(b-leg)中,FreeSwitch是主叫,因此这段呼叫是去话(outbound call)。

在3PCC模式中,两段呼叫(a-leg,b-leg)中,FreeSwitch都是主叫,因此这两段呼叫都是去话(outbound call)。

FreeSWICH 呼叫控制

来话处理(呼入)

来话处理,即对呼入呼叫的处理,分为以下几个阶段

呼叫发起&初始化

  1. 在一个 SIP Profile 的 SIP 端口(例如5060)上收到INVITE,
  2. FreeSwitch 首先创建新的 channel(A-leg)。
    进行ACL鉴权,通过以后根据当前SIP Profile的conext参数确定呼叫路由上下文,否则
    根据主叫号码查询user directory得到用户信息,完成用户名密码鉴权,并根据用户信息中的context 配置确定呼叫路由上下文

呼叫路由

  1. 根据呼叫初始化阶段确定的呼叫路由上下文,解析响应的dialplan配置脚本
  2. 使用当前呼叫信息匹配dialplan extention,确定要执行的actions
  3. 根据actions中的app的类型进行呼叫处理;或者进一步路由到脚本或外部应用中进行处理,如:
    <action application="lua" data="test.lua"/> 将来话路由给Lua脚本(test.lua)进行处理。
    <action application="socket" data="127.0.0.1:8040"/> 将来话路由到event socket接口上,交给外部应用(TCP Server)来处理。

呼叫处理

1. 根据dianplan actions中的app(或者从脚本或外部应用发送的api命令)进行呼叫处理

2. 这些app/api分为两类

去话处理(外呼)

FreeSWITCH发起外呼有两种方式:

  • brigde :在既有呼叫(a-leg)中发起新的呼叫腿(b-leg);可能在来话处理逻辑中触发。
  • originate:创建新的呼叫(a-leg);只能通过命令行或ESL触发。

无论那种方式发起呼叫,首先要指定呼叫目的方。呼叫目的方是通过Dialstrings 拨号字符串来标识的。拨号字符串以要使用的模块标识开头,后跟(可选)特定于模块的其他信息,最后是目标号码。拨号字符串的组件由“/”分隔。

FreeSWITCH中针对不同类型的呼叫目的方的 Dial-Strings。示例如下

通过指定SIP UA呼叫对端:sofia/external/17896061576@202.112.121.2001
通过gateway发起呼叫:sofia/gateway/gw/18005551212
呼叫注册用户:user/1000 

回铃音与 Early Media

假定A与B不在同一台服务器上(如在PSTN通话中可能不在同一座城市),中间需要经过多级服务器的中转:

在 PSTN 网络中,A 呼叫 B,B 话机开始振铃,A 端听回铃音(Ring Back Tone)。在早期,B 端所在的交换机只给 A 端交换机送地址全(ACM)信号,证明呼叫是可以到达 B 的,A 端听到的回铃音铃流是由 A 端所在的交换机生成并发送的。但后来,为了在 A 端能听到 B 端特殊的回铃音(如“您拨打的电话正在通话中…” 或 “对方暂时不方便接听您的电话” 尤其是现代交换机支持各种个性化的彩铃 - Ring Back Color Tone 等),回铃音就只能由 B 端交换机发送。在 B 接听电话前,回铃音和彩铃是不收费的(不收取本次通话费。彩铃费用一般是在 B 端以月租或套餐形式收取的)。这些回铃音就称为 Early Media(早期媒体)。它是由 SIP 的183(带有SDP)消息描述的。

理论上讲,B 接听电话后交换机 b 可以一直不向 a 交换机发送应答消息,而将真正的话音数据伪装成 Early Media,以实现“免费通话”。

Dialplan (拨号计划)

freeswitch 的拨号规则配置:https://www.cnblogs.com/einyboy/archive/2012/11/21/2780539.html

Dialplan (拨号计划) 主要作用就是对电话进行 路由 (选路、寻路)。就是当一个用户拨号时,对用户所拨的号码进行分析,进而决定下一步该做什么。当然,实际上它所能做的比你想象的要强大的多。

Dialplan 支持多种不同的格式,系统支持的拨号计划(Dialplan)及对应的模块:

  • asterisk:mod_dialplan_asterisk。
  • enum:mod_enum。
  • inline:mod_dptools。
  • LUA:mod_lua。
  • XML:mod_dialplan_xml。用得最多的还是 XML格式的 Dialplan (拨号计划)

xml Dialplan 解析

XML Dialplan 由一系列的 XML 配置文件组成,这些 XML可以是静态配置的,也可以使用动态配置方式从其他服务器或脚本中动态获取。Dialplan 有特定的结构,FreeSWITCH 通过解析相关的结构对 Dialplan 进行路由的呼叫,决定执行何种动作或流程。拨号计划的配置文件在 conf/dialplan 中,是通过 freeswitch.xml 装入:<X-PRE-PROCESS cmd="include" data="dialplan/*.xml"/>

拨号计划

  • 由多个 Context (上下文/环境)组成。context 可以类比为一个公司。
  • 每个 Context 中有多个 Extension (分支,在简单的 PBX 中也可以认为是分机号,但很显然,Extension 涵盖的内容远比分机号多)。所以,Context 就是多个 Extension 的逻辑集合,它相当于一个分组,一个 Context 中的 Extension 与其它 Context 中的 Extension 在逻辑上是隔离的。Extension 可以类比为公司的一个分机号。

注意:在处理 Dialplan 时是顺序的对每一项Extension 进行正则表达式匹配。是非常影响效率的。所以,在生产环境中,往往要删除这些默认的 Dialplan,而只配置有用的部分。现在可以暂时不删,因为里面有好多例子可以学习。

下面是 Dialplan 的完整结构:

Extension 相当于路由表中的表项。

dialplan xml 解析


   
  1. <extension name="incoming_call">
  2. <condition field="destination_number" expression="^1234$">
  3. <action application="answer" data=""/>
  4. <action application="sleep" data="1000"/>
  5. <action application="ivr" data="welcome"/>
  6. </condition>
  7. </extension>
AI生成项目 XML

呼叫目标号码 "1234" 时,自动接听电话,并在1秒后播放欢迎语音(通过 "ivr" 应用程序实现)。就可以听到配置的IVR菜单了。注意,在实际应用中,为了能接受外部来的呼叫,可能要把这里的 1234 改成实际的 DID(Direct Inbound Dial)号码。

  • <extension>:定义一个扩展(extension),即一个电话呼入的处理逻辑。
  • name="incoming_call":扩展的名称,可能是用于标识并区分不同的扩展。
  • <condition>:定义一个条件,用于判断是否应该执行下面的一系列操作。
  • field="destination_number" expression="^1234$":设置条件的字段为 destination_number,并且要求其值为正则表达式 ^1234$ 匹配的内容。即如果用户拨打1234时,就会在 Dialplan 中路由到这里。
  • <action>:定义一个操作,即在满足条件时执行的动作。三个action,分别指定接下来要执行的动作。
  • application="answer" data="":执行 answer 应用程序,用于接听呼入电话。就是 执行answer对来话进行应答(必须先应答才能向对方播放声音);
  • application="sleep" data="1000":执行 sleep 应用程序,使当前线程暂停1000毫秒(1秒)。使用sleep暂停一秒,以防止由于声音通路没建立好(有时候,特别是当主叫与被叫之间的链路比较复杂时,声音链路的建立需要一个过程)而丢失声音;
  • application="ivr" data="welcome":执行 ivr 应用程序,并传递 welcome 作为数据。

每一个 Extension 都有一个 name 属性。它可以是任何合法的字符串,本身对呼叫流程没有任何影响,但取一个好听的名字,有助于在查看 Log 时发现它。在 Extension 中可以对一些 condition (条件)进行判断,如果满足测试条件所指定的表达式,则执行相对应的 action (动作)。

FreeSWITCH 安装时,提供了很多例子,为了避免与提供的例子冲突,强列建议在学习时把自己写的 Extension 放到第一个 Extension 的位置。示例:编辑 conf/dialplan/default.xml 并将下面 Extension 作为第一个 Extension


   
  1. <extension name="My_Echo_Test">
  2. <condition field="destination_number" expression="^echo|1234$">
  3. <action application="echo" data=""/>
  4. </condition>
  5. </extension>
AI生成项目 XML

保存后,重新载入配置文件,设置日志级别、最后呼叫 1234

  • 在 FreeSWITCH 命令行上(Console 或 fs_cli)执行 reloadxml 或按 F6键,使 FreeSWITCH 重新读入你修改过的配置文件。
  • 按 F8 键将 log 级别设置为 DEBUG 从而显示详细日志。
  • 然后注册软电话,并拨叫 1234 或 echo (大部分软电话都能呼叫字母,如 Zoiper,X-lite 可以使用空格键切换数字和字母)。

debug 查看 log

重点提示遇到 Dial plan 的问题,按 F8 打开 DEBUG 级别的日志

一个合法的 XML 文件,所有的标签都是成对出现的。如果有的标签比较简单,可以使用简写的形式来关闭标签,即大于号前面的 /,如果不小漏掉,在 reloadxml 时将会出现类似 "+OK [[error near line 3371]: unexpected closing tag ]" 之类的错误,而实际的错误位置又通常不是出错的那一行。这是在编辑 XML 文件时经常遇到的问题,又比较难于查找。因此在修改时要多加小心,推荐使用具有语法高亮的功能的编辑器来编辑 xml,例如:notepad++、vscode、sublime text、等。

在XML加载阶段,FreeSWITCH的XML解析器会先将预处理命令展开,在FreeSWITCH内部生成一个大的XML文档。log/freeswitch.xml.fsxml 是 FreeSWITCH 内部 XML 的一个内存镜像(注意,它在log目录中,而不是在 conf 目录中,由于它是动态生成的,所以用户不应该手工编辑它)。它对调试非常有用:假设你不慎弄错了某个标签,又不知道它错在哪了,可以尝试让 FreeSWITCH 重新加载XML(reloadxml),这时在 FreeSWITCH 的日志中就可以看到 XML 某一行出错的提示,在 freeswitch.xml.fsxml 就能很容易地定位到这一行。

查看上面示例 Log,注意如下的行:

省去了 Log 中的日期以及其它不关键的一些信息。

  • 第2行 Processing ,说明是在处理 Dialplan,my_sip_name 是我的的 SIP 名字,1000 是我的分机号, 1234 是我所拨叫的号码,这里,我直接拨叫了 1234。它完整意思是说,呼叫已经达到路由阶段,要从 XML Dialplan 中查找路由,该呼叫来自 my_sip_name ,分机号是1000,它所呼叫的被叫号码是 1234 (或 echo,如果你拨叫 echo 的话)。
  • 第3行 呼叫进入 parsing (解析XML) 阶段,它首先找到 XML 中的一个 Context,这里是 default(它是在 user directory 中定义的。如果 1000 这个用户发起呼叫,则它的 context 就是 default,所以要从 XML Dialplan 中的 default 这个 Context 查起)。它首先找到的第一个 Extension 的 name 是 My_Echo_Test。continue=false 的意思后面再讲。
  • 第4行,由于该 Extension 中有一个 Condition,它的测试条件是 destination_number,也就是被叫号码,所以, FreeSWITCH 测试被叫号码(这里是 1234)是否与配置文件中的正则表达式相匹配。 ^echo|1234$ 是正则表达式,它匹配 echo 或 1234。所以这里匹配成功,Log 中显示 Regex (PASS) 表示匹配成功,它就开始执行动作 echo(它是一个 APP),所以你就听到了自己的声音。

在这个例子中,我们呼叫 1234 时创建了一个单腿的呼叫,与其说我们跟 FreeSWITCH 在通话,还不如说我们在跟 FreeSWITCH 中的一个 App 在通话。而 XMI Dialplan 只是帮助我们找到这个(些) App。上面所说的是最简单的路由查找,实际上,系统自带的一些 Dialplan 的例子很有代表性。

用户目录配置文件中有一项 <variable name="user_context" value="default">,说明,如果 1000 这个用户发起呼叫,则它的 context 就是 default,所以,待呼叫进行到路由阶段后,要从XML Dialplan 中的 default 这个 Context 查起。

默认的配置文件结构

系统默认提供的配置文件包含三个 Context:default、features 和 public,它们分别在三个 XML 文件中。

  • default 是默认的 dialplan,一般来说注册用户都可以使用它来打电话,如拨打其它分机或外部电话等。
  • public 则是接收外部呼叫,因为从外部进来的呼叫是不可信的,所以要进行更严格的控制。如,你肯定不想从外部进来的电话再通过你的网关进行国内或国际长途呼叫。当然,这么说不是绝对的,等你熟悉了 Dialplan 的概念之后,可以发挥你的想象力进行任何有创意的配置。
  • 在 default 和 public 中,又通过 include 预处理指令分别加入了 default/ 和 include/ 目录中的所有 XML 文件。 这些目录中的文件仅包含一些额外的 Extension。由于 Dialplan 在处理是时候是顺序处理的,所以,一定要注意这些文件的装入顺序。通常,这些文件都按文件名排序,如 00_,01_等等。如果你新加入 Extension,可以在这些目录里创建文件。但要注意,这些文件的优先级比直接写在如 default.xml 中低。可以将新加的 Extension 加在 Dialplan 中的最前面,以便于说明问题。

Channel (通道、信道) Variables、、及相关操作

在 FreeSWITCH 中,每一次呼叫都由一条或多条 "腿" (Call Leg) 组成,每一条腿又称为一个 Channel(信道),每一个 Channel 都有好多属性,用于标识 Channel 的状态,性能等,这些属性称为 Channel Variable(信道变量),简写为 Channel Var 或 Chan Var 或 Var。

通过使用 info 这个 APP,可以查看所有的 Channel Var。我们先修改一下 Dialplan


   
  1. <extension name="Show Channel Variable">
  2. <condition field="destination_number" expression="^1235$">
  3. <action application="info" data=""/>
  4. </condition>
  5. </extension>
AI生成项目 XML

加入 default.xml 中,这次可以放在 My_Echo_Test 这个 Extension 后面然后保存,

执行 reloadxml 重新载入配置。从软电话上呼叫 1235,可以看到有很多Log输出,还是从绿色的行开始看:

在处理 Dialplan 时是顺序的对每一项Extension 进行正则表达式匹配。可以看到,由于呼叫的是 1235,它在第三行测试 My_Echo_Test 的 1234 的时候失败了,接在接下来测试 1235 的时候成功了,便执行相对应的 Action - info 这个APP。它的作用就是把所有 Channel Variables 都打印到 Log 中。所有的 Channel Variable 都是可以在 Dialplan 中访问的,使用格式是 ${变量名},如 ${destination_number}。将下列配置加入 Dialplan 中保存配置,


   
  1. <extension name="Accessing_Channel_Variable">
  2. <condition field="destination_number" expression="^1236(\d+)$">
  3. <action application="log" data="INFO Hahaha, I know you called ${destination_number}"/>
  4. <action application="log" data="INFO The Last few digists is $1"/>
  5. <action application="log" data="ERR This is not actually an error, just jocking"/>
  6. <action application="hangup"/>
  7. </condition>
  8. </extension>
AI生成项目 XML

执行 reloadxml 重新载入配置。这次呼叫 1236789,看看结果,跟前面一样,我们还是从绿色的行开始看。

1236789 匹配了正则表达式 ^1236(\d+),并将 789 存储在变量 $1 中。然后看到它解析出的四个 Action(3个 log,1个 hangup),到这里为止,Channel 的状态一直没有变,还处在路由查找的阶段。在所有 Dialplan 解析完成后,Channel 状态才进行 Standard Execute 阶段。理解这一点是非常重要的,后面再做详细说明,在这里你要记住:路由查找(解析)和执行分属于不同的阶段。当 Channel 状态进入执行阶段后,它才开始依次执行所有的 Action。log() 的作用就是将信息写到 Log 中,它的第一个参数是 leglevel,就是 Log 的级别,有 INFO、Err、DEBUG 等,不同的级别能以不同的颜色显示。(详细的级别请参考mod_logfile | FreeSWITCH Documentation)。

这次实验特意增加了几个 Action。一个 Action 通常有两个参数,一个是 application,代表要执行的 APP,另一个是 data,就是 APP 的参数。当 APP 没有参数时 data 可以省略。

读到这里,你或许还有疑问,既然我们在 info APP 的输出里没看到 destination_number 这一变量,它到底是从哪里来的呢?是这样的,它在 info 中的输出是 Caller-Destination-Number,但你在引用的时候就需要使用 destination_number。还有一些变量,在 info 中的输出是 variable_xxxx,如 variable_domain_name,而实际引用时要去掉 variable_ 前缀。不要紧张,这里有一份对照表: Channel Variables | FreeSWITCH Documentation

在Dialplan中,可以对通道变量(Channel Variable)进行各种操作。

  • set App 可以给变量赋值:<action application="set" data="my_var=my_value"/>
  • export 可以对 a-leg 和 b-leg 同时赋值(即使此时 b-leg 还不存在):<action application="export" data="my_var=my_value"/>

以上两条命令都可以将 my_var 变量的值设置为 my_value。不同的是 set 程序仅会作用于当前的Channel(aleg),而 export 程序则会将变量设置到两个Channel(a-leg和b-leg)上,如果当时 b-leg 还没有创建,则会在创建时设置。另外,export 也可以通过 nolocal 参数将变量值限制仅设置到 b-leg上:<action appliction="export" data="nolocal:my_var=my_value"/>

在实际应用中,如果a-leg上已经有一些变量的值(如var1、var2、var3),但想同时把这些变量都复制到bleg上,可以使用以下几种办法:

<action application="export" data="var1=$var1"/>
<action application="export" data="var2=$var2"/>
<action application="export" data="var3=$var3"/>

或者使用如下等价的方式:
<action application="set" data="export_vars=var1,var2,var3">

所以,set 也具有能往b-leg上赋值的能力。其实,它和 export 一样,都是操作 export_vars 这个特殊的变量。只不过 export 的语法更直观一些。

另外,我们也可以随时取消某些Variable的定义。取消Variable定义只需对它赋一个特殊的值 "_undef_" 或使用 unset App,如,下面两行代码是等价的:

<action application="set" data="var1=_undef_"/>
<action application="unset" data="var1"/>

在实际使用时,还常常会用到截取Variable值的部分操作。在FreeSWITCH中,可以使用特殊的语法取一个通道变量值的子字符串,使用格式是“${var:位置:长度}”。其中“位置”从0开始计数,若为负数则从字符串尾部开始计数;如果“长度”为0或小于0,则会从当前“位置”一直取到字符串结尾(或开头,若“位置”为负的话)。例如var的值为1234567890,那么各种取值方法见下面的例子:

官方文档:https://freeswitch.org/confluence/display/FREESWITCH/Channel+Variables+Catalog

通道变量与info应用程序输出变量的对应关系见官方文档:https://freeswitch.org/confluence/display/FREESWITCH/Channel+Variables

  • ani:主叫的ANI,一般与caller_id_number相同。
  • aniii:主叫的ANI II,如果有的话。
  • answered_time:应答时间。
  • billsec:计费时长。从应答开始到呼叫结束。
  • bridge_hangup_cause:记录b腿bridge失败的原因。
  • bypass_media:来话是否启用媒体绕过模式。
  • caller_id_name:主叫名称。
  • caller_id_number:主叫号码。
  • channel_name:Channel名称。
  • context:Dialplan当前的Context。
  • continue_on_fail:当bridge失败时是否继续执行后面的action。还可以为挂断原因的逗号分隔的列表,挂断原因见originate_disposition。
  • created_time:创建时间。
  • destination_number:被叫号码。
  • detect_speech_result:语音识别结果。
  • dialplan:使用的Dialplan模块的名字,如XML、YAML、lua、enum、asterisk、lcr等。
  • direction:呼叫方向,Inbound或Outbound。
  • domain:租户域。
  • domain_name:租户域的名字。
  • dtmf-type:DTMF类型。inband为使用带内DTMF,info为使用SIP的INFO消息。
  • duration:呼叫时长。从呼叫开始到呼叫结束。
  • effective_caller_id_number:设置在a-leg上,但影响b-leg的主叫号码显示。
  • enable_file_write_buffering:录音时是否先写到内存缓冲区中,以减少IO访问。默认为true。默认缓冲区大小是65536字节。
  • end_stamp:结束时间。
  • execute_on_answer:应答时的钩子。格式为:APP [ARG[ ...]]。
  • fifo_bridge_uuid:呼叫取回时指定来话Channel。
  • fifo_caller_exit_key:退出呼叫队列等待的按键。
  • fifo_caller_exit_to_orbit:退出呼叫队列等待时是否转至其它号码。如否则直接退出。
  • fifo_chime_freq:呼叫队列等待时给来话播放提示音的间隔秒数。
  • fifo_chime_list:呼叫队列等待时每隔一段时间给来话播放的提示音。
  • fifo_music:呼叫停泊时播放的音乐。
  • fifo_orbit_announce:坐席接听时先给来话播放的提示音。
  • fifo_orbit_context:等待超时转至的Context。
  • fifo_orbit_dialplan:等待超时转至的Dialplan。
  • fifo_orbit_exten:等待超时转至的号码。格式为USERNAME:TIMEOUTSEC。
  • fifo_override_announce:坐席接听后在来话通话前给坐席播放的提示音。
  • fifo_position:来话在当前队列中的位置。执行过程自动产生。
  • fifo_priority:将来话放至呼叫队列的优先级。1至10,默认为5,越大优先级越高。
  • fifo_serviced_by:为来话服务的坐席Channel。执行过程自动产生。
  • hangup_after_bridge:在bridge正常完成后是否挂机,不继续执行后面的action。
  • hangup_time:挂机时间。
  • ignore_early_media:忽略早期媒体,避免被认为已接听。
  • instant_ringback:见《FreeSWITCH权威指南》P219。
  • is_loopback:通道是否是由loopback呼叫字符串创建的。
  • leg_timeout:等待返回媒体(如183或200)的超时时间。
  • loopback_bowout:是否令loopback呼叫字符串创建的那条额外的腿在完成使命后尽快释放。
  • network_addr:主叫的IP。
  • nibble_account:计费模块的计费账号。
  • nibble_rate:计费模块的每分钟费率。
  • origination_caller_id_name:可以设置在a-leg上,也可以设置在b-leg上,影响本leg的主叫名称显示。
  • origination_caller_id_number:可以设置在a-leg上,也可以设置在b-leg上,影响本leg的主叫号码显示。
  • origination_cancel_key:取消协商转的按键。
  • originate_disposition:记录b腿bridge失败的原因,只读。其值为挂机原因的枚举值。
  • outbound_caller_id_name:设置在a-leg上,但影响b-leg的主叫名称显示。
  • playback_delimiter:file_string中多个文件的分隔符。
  • playback_sleep_val:file_string中多个文件的播放时间间隔。
  • profile_index:Profile Index。
  • read_codec:读Codec。
  • read_rate:读采样率。
  • record_sample_rate:录音时的采样率,进行实时的转码。可为:8000、12000、16000、24000、32000、11025、22050、44100、48000。
  • RECORD_ANSWER_REQ:是否等通话被应答后才开始录音,可以防止录制不必要的早期媒体。默认为false。
  • RECORD_APPEND:如录音文件已存在,是否在文件后追加录音而不是覆盖文件,可用于电话中断重新建立后重新录音。默认为false。
  • RECORD_ARTIST:文件元信息之一。
  • RECORD_BRIDGE_REQ:是否等当前的Channel与其它Channel桥接后才开始录音,可以防止录制不必要的早期媒体。
  • RECORD_COMMENT:文件元信息之一。
  • RECORD_COPYRIGHT:文件元信息之一。
  • RECORD_DATE:文件元信息之一。
  • RECORD_FINAL_TIMEOUT_MS:录音过程中检测不到声音的超时毫秒数,超时则停止录音。
  • RECORD_HANGUP_ON_ERROR:录音失败时是否挂断电话。默认为false。
  • RECORD_INITIAL_TIMEOUT_MS:从录音开始检测不到声音的超时毫秒数,超时则停止录音。
  • RECORD_MIN_SEC:最小录音秒数。如小于该值,则删除录音文件。默认为3。
  • RECORD_READ_ONLY:是否只录读方向的录音。
  • RECORD_SILENCE_THRESHOLD:静音能量阈值,小于该值认为是静音。默认为200。
  • RECORD_SOFTWARE:文件元信息之一。
  • RECORD_STEREO:是否录制立体声。
  • RECORD_STEREO_SWAP:是否录制立体声,同时将左右声道互换。
  • RECORD_TITLE:文件元信息之一。
  • RECORD_WRITE_ONLY:是否只录写方向的录音。
  • rdnis:原被叫号码,一般用于呼叫转移时。
  • rtp_auto_adjust:是否启用RTP自动调整。
  • source:呼叫来源,来自哪一个FreeSWITCH模块,如mod_sofia、mod_portaudio、mod_freetdm等。
  • start_stamp:开始时间。
  • state:当前Channel的状态。
  • state_number:当前Channel的状态整数值。
  • transfer_after_bridge:通话结束后再转移到呼叫此号码,重新等待。
  • transfer_ringback:见《FreeSWITCH权威指南》P218。
  • transfer_time:转移时间。
  • tts_engine:TTS引擎。
  • tts_voice:TTS嗓音。
  • username:用户名。
  • uuid:Channel的UUID。
  • write_codec:写Codec。
  • write_rate:写采样率。

$${var} 与 ${var}

在每一个 Channel 上都可以设置好多 Variable,称为信道变量。FreeSWITCH 呼叫过程中,会根据这些变量控制 Channel 的行为。


   
  1. ${var} 是在dialplan、application 或 directory中设置的变量
  2. 它会影响呼叫流程并且可以动态的改变。
  3. $${var} 是全局的变量,它仅在预处理阶段(系统启动时,或重新装载 - reloadxml时)被求值。
  4. 一般用于设置一些系统一旦启动就不会轻易改变的量。
  5. 如 $${domain} 或 $${local_ip_v4} 等。
  6. 所以,两者最大的区别是:
  7. $${var} 只求值一次,
  8. ${var} 则在每次执行时求值(如一个新电话进来时)。
AI生成项目 XML
  • $variable_xxxx:会发现,有些变量在显示时(可以使用dp_tools 中的 info() 显示,后面会讲到)是以 "variable_" 开头的,但在实际引用时要去掉这开头的 "variable_"。如 "variable_user_name",引用时要使用 "${user_name}"。Channel Variables | FreeSWITCH Documentation 列举了一些常见的变量显示与引用时的对应关系。

  • 给 Variable 赋值:在 dialplan 中,有两个程序可以给 Variable 赋值:
    <action application="set" data="my_var=my_value"/>
    <action application="export" data="my_var=my_value"/>
    以上两条命令都可以设置 my_var 变量的值为 my_value。不同的是 -- set 程序仅会作用于“当前”的 Channel (a-leg),而 export 程序则会将变量设置到两个 Channel (a-leg 和 b-leg)上,如果当时 b-leg 还没有创建,则会在创建时设置。另外,export 还可以只将变量设置到 b-leg 上:
    <action appliction="export" data="nolocal:my_var=my_value"/>
    在实际应用中,如果 a-leg 上已经有一些变量的值(如 var1、var2、var3),而想同时把这些变量都复制到 b-leg 上,可以使用以下几种办法:
    <action application="export" data="var1=$var1"/>
    <action application="export" data="var2=$var2"/>
    <action application="export" data="var3=$var3"/>
    或者使用如下等价的方式:
    <action application="set" data="export_vars=var1,var2,var3">
    所以,其实 set 也具有能往 b-leg 上赋值的能力,其实,它和 export 一样,都是操作 export_vars 这个特殊的变量。

  • 取消 Variable 定义:只需设置一个特殊的值 _undef_:<action application="set" data="var1=_undef_">

  • 截取 Variable 的一部分:可以使用特殊的语法取一个 Variable 的子串,格式是“${var:位置:长度}”。其中 “位置” 从 0 开始计烽,若为负数则从字符串尾部开始计数;如果“长度”为 0 或小于 0,则会从当前“位置”一直取到字符串结尾(或开头,若“位置”为负的话)。例如 var 的值为 1234567890,那么:
    ${var}      = 1234567890
    ${var:0:1}  = 1
    ${var:1}    = 234567890
    ${var:-4}   = 7890
    ${var:-4:2} = 78
    ${var:4:2}  = 56

关于 set 和 export : set 是将变量设置到当前的 Channel 上,即 a-leg。而 export 则也将变量设置到 b-leg 上。

Conditions (测试条件)

一个简单的测试条件:<condition field="destination_number" expression="^1234S">,它使用正则表达式匹配测试一个变量是否满足预设的正则表达式。大部分的测试都是针对被叫号码(destination_number) 的,也可以对其他变量进行测试,如 IP 地址 ( 注意由于在正则表达式中 . 具有特殊意义,所有需要用 \ 进行转义) :<condition field="network_addr" expression="^192\.168\.7\.7$">

除此之外,测试还接受用户在用户目录中设置的变量,但要注意必须使用 ${} 对变量进行引用。

示例:toll_allow 就是在用户目录中设置的

<condition field="${toll_allow}" expression="international">

如果在你的用户目录中设置了以下变量


   
  1. <user id="1000">
  2. <variables>
  3. <variable name="my_test_var" value="my_test_value"/>
  4. </variables>
  5. </user>
AI生成项目 XML

则可以在 Dialplan 中通过下面的 condition 进行相关测试。

<condition field="${my_test_var}" expression="^my_test_value$">
   
AI生成项目 XML

一般来说,测试条件不可以嵌套,但可以迭加,如下面的例子并不能达到你预期的效果:


   
  1. <extension name="Testing Stacked Conditions">
  2. <condition field="network_addr" expression="^192\.168\.7\.7$">
  3. <condition field="destination_number" expression="^1234$">
  4. <action application="log" data="INFO Hahaha, I know you called ${destination_number}"/>
  5. </condition>
  6. </condition>
  7. </extension>
AI生成项目 XML

但以下配置是正确的:


   
  1. <extension name="Testing Stacked Conditions">
  2. <condition field="network_addr" expression="^192\.168\.7\.7$"/>
  3. <condition field="destination_number" expression="^1234$">
  4. <action application="log" data="INFO Hahaha, I know you called ${destination_ number}"/>
  5. </condition>
  6. </extension>
AI生成项目 XML

两个 condition 是迭加的关系。因此就构成了一个简单的 "逻辑与" 的关系,即只有在IP地址和被叫号码两个条件都匹配的情况下才执行此处的 Action,否则就跳过本 extension,继续解析下一项。因此说这两个 "平行" 的 condition 是 "逻辑与" 的关系。

除此之外,两个迭加在一起的 condition 还可以构成其他关系,使你可以只用 XML 就能完成比较复杂的路由配置,而无须编程。break 参数就是用于实现这个功能的,它有以下几个值(为方便讨论,假设两个条件分别为A和B):

  • on-false:在第一次匹配失败时停止(但继续处理其他的extension),这是默认的配置。结果相当于A and B。
  • on-true:在第一次匹配成功时停止(但会先完成对应的Action,然后继续处理其他的extension),不成功则继续,所以结果相当于((not A)and B)
  • always:不管是否匹配,都停止。
  • never:不管是否匹配,都继续。

通过使用 break 参数,你可以写出类似 if-then-else 的结构。如上面的例子(因为没有break参数,所以默认是 on-false):


   
  1. if(network_addr 正则匹配 /^192\.168\.7\.7$/) then
  2. if(destination_number 正则匹配 /^1234$/) then
  3. //wirte log
  4. end
  5. end
AI生成项目 XML

再来看一个例子,假设你呼叫1234,如果来自 192.168.7.7 这个IP,就播放早上好,如果来自192.168.7.8 就说晚上好,这时你可以这样设置:


   
  1. <extension name="Testing Stacked Conditions">
  2. <condition field="destination_number" expression="^1234$">
  3. <condition field="network_addr" expression="^192\.168\.7\.7$"/>
  4. <action application="playback" data="good-morning.wav"/>
  5. </condition>
  6. </extension>
  7. <extension name="Testing Stacked Conditions">
  8. <condition field="destination_number" expression="^1234$">
  9. <condition field="network_addr" expression="^192\.168\.7\.8$"/>
  10. <action application="playback" data="good-night.wav"/>
  11. </condition>
  12. </extension>
AI生成项目 XML

通过使用break参数,你就可以将两种情况写到一个extension里:


   
  1. <extension name="Testing Stacked Conditions">
  2. <condition field="destination_number" expression="^1234$"/>
  3. <condition field="network_addr" expression="^192\.168\.7\.7$" break="on-true">
  4. <action application="playback" data="good-morning.wav"/>
  5. </condition>
  6. <condition field="network_addr" expression="^192\.168\.7\.8$">
  7. <action application="playback" data="good-night.wav"/>
  8. </condition>
  9. </extension>
AI生成项目 XML

这种情况跟上面两个extension的情况是等价的。虽然看起来不如第一种情况容易理解,但它把相似的功能逻辑上放到一个extension里,却比较直观。如果你从192.168.7.7上呼叫1234,它会首先匹配1234,进而匹配网络地址,匹配成功,便不再往下进行。但是,它会先把已经匹配到的条件中的Action执行完毕,所以播放goodmorning.wav。若你从192.168.7.8上呼叫,则它不能匹配192.168.7.7,但由于break参数的值是on-true,所以在这里,它不会中止,而是继续尝试匹配下面的condition,进而播放good-night.wav。所以,它相当于:


   
  1. if(destination_number 正则匹配 /^1234$/) then
  2. if(network_addr 正则匹配 /^192\.168\.7\.7$/) then
  3. // play good morning
  4. else if(network_addr 正则匹配 /^192.168.7.8$/) then
  5. // play good night
  6. end
  7. end
AI生成项目 XML

always 和 never 不常用,发挥想象力可以造出类似 if a then b end 或 if c then d end之类的条件。

Action (动作)、Anti-Action (反动作)

除使用 condition 的 break 机制来完成复杂的条件以外,还可以使用 "反动作"(Anti-Action)来达到类似的目的,如:


   
  1. <extension name="Anction and Anti-Action">
  2. <condition field="destination_number" expression="^1234$"/>
  3. <condition field="network_addr" expression="^192\.168\.7\.7$">
  4. <action application="playback" data="good-morning.wav"/>
  5. <anti-action application="playback" data="good-night.wav"/>
  6. </condition>
  7. </extension>
AI生成项目 XML

上述代码它说明,如果呼叫来自192.168.7.7,则播放 good-morning.wav,否则播放 good-night.wav(不管是不是来自192.169.7.8)。因此,读者可以看到,它没有condition条件那么强大,但在简单的条件下也经常使用,它相当于:


   
  1. if(destination_number 正则匹配 /^1234$/) then
  2. if(network_addr 正则匹配 /^192\.168\.7\.7$/) then
  3. // play good morning
  4. else
  5. // play good night
  6. end
  7. end
AI生成项目 XML

dialplan 工作流程

在 Hunting 时,只解析 Dialplan,并不执行任何 Action 而是将所有满足条件的 Action 都放到一个列表中,待呼叫流程进行到 EXECUTE 阶段时,再依次执行列表中的 Action。了解 Dialplan 的工作机制之前,先来看一下 Channel 的状态机。

新建(NEW) 一个 Channel 时,它首先会进行 初始化(INIT),然后进入路由(ROUTING)阶段,也就是查找解析 Dialplan 的阶段。这里注意一个专门的术语 Hunting(译为 "选线、选路")。找到合适的路由入口后,Hunting 会执行(EXECUTE)一系列动作,最后无论哪一方挂机,都会进入挂机(HANGUP)阶段。后面的报告(REPORTING)阶段一般用于进行统计、计费等,最后将 Channel 销毁(DESTROY),释放系统资源。

在 EXECUTE 状态,可能会发生转移(Transfer,该转移跟通常说的呼叫转移不太一样),它可以转移到同一 context 下其他的 extension,或者转移到其他 context 下的 extension,但无论发生哪种转移,都会重新进行路由,也就是重新进入ROUTING (图中虚线部分),重新 Hunt  Dialplan。

一定要记住 ROUTING 和 EXECUTE 是属于两个不同阶段的,只有 ROUTING 完毕后才会进行EXECUTE 阶段的操作。当一个 Channel 进入 ROUTING 阶段时,它首先会到达 Dialplan(英文叫 Hit the Dialplan),然后对预设的 Dialplan 进行解析(是的,每个电话都会重新解析Dialplan),解析 Dialplan 的这一过程称为 Parsing 或 Hunting。解析完毕(成功)后,会得到一些 Action,然后 Channel 进入 EXECUTE 阶段, 依次执行所有的 Action。

示例:用另一种方式实现类似上面的 good-morning/good-night 中的例子,但这一次稍有不同:用户呼叫1234,如果来自192.168.7.7,则播放good-morning.wav;如果来自192.168.7.8,则播放goodnight.wav;否则,什么都不做。( 注意: 这个例子是错误的,不会出现你预期的结果, 但这是新手常常犯的错误,而遇到这种情况,大多数新手都会以为FreeSWITCH出问题):


   
  1. <extension name="Testing Hunting and Executing" continue="true">
  2. <condition>
  3. <action application="set" data="greeting=no-greeting.wav"/>
  4. </condition>
  5. </extension>
  6. <extension name="Testing Hunting and Executing" continue="true">
  7. <condition field="network_addr" expression="^192\.168\.7\.7$">
  8. <action application="set" data="greeting=good-morning.wav"/>
  9. </condition>
  10. </extension>
  11. <extension name="Testing Hunting and Executing" continue="true">
  12. <condition field="network_addr" expression="^192\.168\.7\.8$">
  13. <action application="set" data="greeting=good-night.wav"/>
  14. </condition>
  15. </extension>
  16. <extension name="Testing Hunting and Executing">
  17. <condition field="destination_number" expression="^1234$"/>
  18. <condition field="${greeting}" expression="^good">
  19. <action application="playback" data="${greeting}"/>
  20. </condition>
  21. </extension>
AI生成项目 XML

在前三个 extension 中都增加了一个参数 continue="true"。如果没有该参数,则默认为 false。在默认的情况下,在 Dialplan 的 Hunting 阶段,一旦根据前面介绍的 condition 匹配规则找到对应的extension,就执行相应的 Action,而不会再继续查找其他的 extension了, 不管后面的 extension是否有可能匹配。

但有些情况下,Dialplan 中会有多个 extension 满足匹配规则,而我们希望所有对应的 Action 都能得到执行,这时我们就要使用 continue="true" 参数了。

此外,我们这次还在第一个 extension 中用了一个空的 condition,这个空的condition没有匹配规则,因此它被认为匹配所有规则,故称其为绝对条件(Absolute Condition)。所以从表面上看,它等价于:


   
  1. $greeting = no-greeting.wav
  2. if (network_addr 正则匹配 /^192\.168\.7\.7$/) then
  3. $greeting = good-morning.wav
  4. else if (network_addr 正则匹配 /192\.168\.7\.8$/) then
  5. $greeting = good-night.wav
  6. end
  7. if (destination_number 正则匹配 /^1234$/ and $greeting 正则匹配 /^good/) then
  8. play $greeting
  9. end
AI生成项目 XML

但实际上两者不是等价的。原因在于,对 Dialplan 的 Hunting 和 Executing 分属于不同的阶段。在Hunting 阶段,只解析 Dialplan,并不执行任何动作,而是将所有满足条件的 Action 都放到一个动作列表(队列)中,待呼叫流程进行到 Executing 阶段时,再依次执行动作列表中的动作。所以上述的 XML 实际上等价于:


   
  1. $action_list[0] = "$greeting = no-greeting.wav"
  2. if (network_addr 正则匹配 /^192\.168\.7\.7$/) then
  3. $action_list[1] = "$greeting = good-morning.wav"
  4. else if (network_addr 正则匹配 /^192\.168\.7\.8$/) then
  5. $action_list[1] = "$greeting = good-night.wav"
  6. end
  7. if (destination_number 正则匹配 /^1234$/ and $greeting 正则匹配 /^good/) then
  8. $action_list[2] = "play $greeting"
  9. end
  10. // 开始执行命令列表中的命令
  11. foreach $action in $action_list do
  12. execute $action
  13. end
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值