吴伟贤のBlog

Feed Rss

FreeSWITCH 1.0.6 第一章-FreeSWITCH架构(一)

07.20.2013, freeswitch, by .

转自:http://www.8000hz.com/archives/7.html

Architecture  of  FreeSWITCH

                                                 Freeswitch的架构
欢迎来到FreeSWITCH! 如果你正读到此,那么无可置疑你对VOIP和电信行业很感兴趣。
FreeSWITCH 是一个在电信通讯技术革命中起到革命性软件。在看这个强大的软件的架构前,我们先瞧一瞧有趣的电信技术。这对我们深入了解FreeSWITCH会有帮助。
在此章节,我们将会了解:
• 一场电信业的革命
• FreeSWITCH的特性
• 终端和拨号模块
• FreeSWITCH是怎么简化像语音留言这样复杂的应用的
一场革命已经开始,秘密已被揭开
对于大多数人来说,电话的工作原理是比较神秘的。它的工作原理被保守为秘密已经许多年了。我们仅仅是把我们的电话插到墙上的电话口里,然后它们正常使用了。大多数人仅仅这样做,然后期待它们工作正常。不过电信界的革命已经开始了,我们已经开始去解开这个电信业遗留的最关键秘密。现在,像你我这样的普通人都有能力去创建比传统电信系统更好,同时可以以相对较低的价格提供高级功能的电话系统。不少人已经将FreeSWITCH用于商业电话系统,获取利润。FreeSWITCH已经把这一切变得更加的容易了,所以我们要看一下它的架构,更好的理解它是如何工作的。
如果这些概念听起来很晦涩难懂和抽象,不要担心和丧气,学习电话技术,特别是VOIP,可不是一朝一夕的事情,其实,我们建议你多读几遍本章节。在你看第一遍的时候尽可能多的理解与吸收,然后当你读完了第五章后再重复温习一遍,理解了XML的拨号方案,你会惊讶的发现你对VOIP和FreeSWITCH的理解将会大大的提升。当你读完第9章节的内容后再来略读它第三遍,通过外部命令来控制FreeSWITCH, 在那时,你将会有一个深刻地领会到VOIP和FreeSWITCH的概念。给自己一个充足的时间去消化这些陌生的新概念。然后你就会发现你已经是一个非常牛的FreeSWITCH管理员。如果你不气馁,你会对这个陌生而美妙的电话世界有一个有意义的认知。电话和电话系统都是非常复杂难理解的,并且经过多年的发展,通讯变来越来越多样化。
在美国和英国最流行的电话当属传统模拟电话,我们也叫它 POTS lines 或 纯粹老的电话服务(Plain Old Telephone Service)。大多数我们今天使用的电话,从传统的Ma Bell电话到移动无线电话,同样一件事被掩盖--那就是他们背后的技术。在最近的10到15年间,电脑和电话的技术交汇点,催生出了一对儿与POTS line通讯的便宜方案—–移动电话和VOIP 电话(也叫网络电话)。
FreeSWITCH 将多种电话通讯技术桥接在一起来适应如此混杂的通讯方案。所以,我们可以无障碍的沟通,尽管这些通讯技术间不兼容,FreeSWITCH还可以桥接电话到你自己写的电脑程序上去,然后用从未发生过的方式来响应。FreeSWITCH 是一个可以运行在Windows和多种UNIX上,如Mac OSX,linux,solaris和BSD上的软件。这表示无论是你的家庭PC和高端的服务器,你都可以安装FreeSWITCH和使用FreeSWITCH处理电话呼叫。
关于如何安装FreeSWITCH我们将会在第二章节进行详细的讨论,编译和安装。我们了解完了基础的架构后,我们就会立即进行安装。
 
 
 
 
 
FreeSWITCH的设计:模块化,可扩展,稳定
FreeSWITCH的设计目标是提供一个围绕在switching核心的模块化,可扩展,并且为开发人员提供用来添加和控制系统的健全接口的通讯系统。FreeSWITCH中的每一个分子都是彼此独立,同时不需要了解其他部分是如何工作,此外,每个分子所提供的我们叫作“裸露功能”。
FreeSWITCH的功能同样可以使用加载模块进行扩展,一种可以绑定外部功能到核心的特殊技术。
FreeSWITCH 拥有很多不同的模块类型围绕在中央核心,比较像行星围绕着恒星公转一样。列表如下:
Module Type:
模块类型:
Purpose:
作用:
终端
电话协议像SIP.H.323和POTS lines。
拨号方案
分析通话详细信息和决定将通话路由到哪里
编码
音频格式之间的转换
应用
执行一个任务,像播放声音或设置数据
应用程序接口
(API)
导出一个接受text参数同时也返回text内容的函数,这种函数可以供跨模块或使用外部连接调用的函数。
文件
提供一个接口来提取和播放各种格式的音频文件的声音
(TTS) 文本到语音转换
文本到语音引擎的接口。
自动语音识别ASR
语音识别系统的接口。
目录
连接目录信息服务到通用核心查找API,如LDAP。
Chat
桥接和交换多种聊天协议
Say
使用多种语言将语音文件串联,提供反馈播放像电话号码,时间,拼写单词,等等。

 

如图ssss.png

 

通过各个模块接口的功能相结合,FreeSWITCH可以配置连接IP电话,POSTS lines和基于IP的电话服务。它也可以转换音频格式和你可以自己创建带自定义菜单系统的接口。你甚至可以从其他的机器上来控制一台正在运行中的Freeswitch 服务器。让我们从一个使用很广泛的模块终端模块来开始更进一步的观察。
Important modules: Endpoint and Dialplan
重要模块:终端和拨号方案
终端模块是极为重要的,添加了一些使FreeSWITCH变成现在强大平台的关键性的功能。
终端模块扮演的主要角色是添加通用的通信技术,然后将他们初始化成一个 我们叫做session的抽象实体。一个Session代表一个FreeSWITCH的和特定的协议之间的连接。目前FreeSWITCH拥有好些个终端模块,已经实现了像SIP,H323,GTALK以及其他的通信协议。我们将会花一些时间去了解freeswitch中一个比较常用叫mod_sofia的模块。Sofia-SIP(http://sofia-sip.sourceforge.net)是一个由诺基亚(NOKIA)赞助的开源项目。这个项目的设计之初是为会话发始协议也叫SIP做一个程序接口。我们在FreeSWITCH中的mod_sofia模块中使用了这个库。mod_sofia模块注册了所有在FreeSWITCH中创建模块的必要 Hook. 同时互相转换FreeSWITCH结构和SIP结构。模块的配置信息取自FreeSWITCH的中心配置文件,允许mod_sofia加载用户定义的参数和详细连接信息。这样FreeSWITCH 就可以接受SIP电话设备的注册,注册到其他的SIP服务,发送通知,以及提供像语言留言这样的服务。当一个SIP呼叫已经建立在FreeSWITCH与另一个SIP设备之间,它会在FreeSWITCH中显示为一个活动中的会话。如果有电话打进来,可以将呼叫转移或格拉到IVR菜单,保持音乐,一个或多个分机,有许多其它的选择可用。让我们来看一个典型的场景,当一个已经注册的SIP电话分机2000呼叫分机2001,希望建立一个呼叫。首先,SIP电话通过网络发送一个呼叫建立消息(INVITE)到mod_sofia(mod_sofia 正在侦听等待这类的消息).收到消息后,mod_sofia 模块分析有关细节,同时将呼叫转入到FreeSWITCH的核心状态机。下一步是寻找拨号方案模块,依据配置内容进行呼叫终端。默认及最常用的拨号文案模块是XML 拨号方案模块。XML拨号方案模块设计用来从freeswitch的中央XML注册表中读取指令列表。XML拨号方案模块使用 正则表达式 匹配来 解析一系列的XML扩展对象。
当我们呼叫2001时,我们希望找到一个XML extension 测试其destination_number字段是否匹配“2001”,然后再使用相应的规则进行路由(route);拨号方案并不是限制仅匹配一个extension.
其实,在第五章,理解XML拨号方案,你将会得到extension的更详细的定义。XML拨号方案为每一个通话创建了一种“task list”, 每一个可以匹配的extension中的action都将会添加到通话的“task list”。假设FreeSWITCH至少找到一个extension符合通话,XML Diaplan 将会插入一些尝试把通话连接到2001的一些说明到会话对象。一旦这些instructions被插入到会话中,通话的状态由ROUTING转到EXECUTE状态,EXECUTE从列表中找下一个handler drill同时执行由ROUTING状态获得的instructions。This is where the application interface comes into the picture.
每一个添加到session的instruction都使用包含 应用名称和一个将要传到应用的数据参数.
在例子中我们要用的应用是bridge.这个应用的目的是创建一个session和另一个出站连接(outbound connection), 然后连接这两个session,开启语音交流。
我们提供给bridge的参数是user/2001, 一个很简单的方式生成一个呼叫分机2001的通话。
一个2001拨号规则的条目可以看起来像这样:
<extension name=”example”>
<condition field=”destination_number” expression=”^2001$”>
<action application=”bridge” data=”user/2001″/>
</condition>
</extension>
Extension的名字是”example”,它只有一个条件可以匹配。
如果条件被匹配,它只有一个应用去执行。
用白话来说,刚才提到的extension,可以这样表达:如果 主叫 拨打了2001,然后在主叫和对端(一个电话)2001确立一个连接。思考一下这是怎样发生的。
一旦我们插入instructions到一个session, 它的状态就会从切换到EXECUTE, FreeSWITCH的核心将会使用收集到的数据执行想要的行为.首先, 默认执行的将会是解析命令以便于执行bridge 到 user/2001, 然后它会寻找bridge应用, 然后传送参数 user/2001 到bridge 应用中.FreeSWTICH的核心将会建立一个所需类型的outbound session.用户2001也是一个SIP电话, 所以user/2001将会解析到一个SIP的拨号字符串, 它将用来传送到mod_sofia 去请求mod_sofia去创建一个新的outbound session.
如果新的session建立成功, 现在FreeSWTICH核心中会有两个session.
Bridge应用将会取得新的session和原来的session(主叫的电话), 然后在它们之间执行bridge function.
这样只要分机2001的人实际上接听了电话, 双向的语音流就可以传输了.
如果用户无法接听或繁忙, 一个超时(这表示一个失败)将会产生,同时发送对应的MESSAGE到主叫的电话.
如果电话未接通或分机繁忙, 很多的 routing 选项是可选择的, 像呼叫转移或语音邮箱.
所有的一切都由一个简单的动作产生—拿起电话听筒然后拨打2001.
FreeSWTICH拥有复杂的SIP协议的全部, 同时把它的复杂性减少到了一个common denominator.
From there, 它通过允许我们来配置一个简单的指令到拨号规则实现连接电话2000到电话2001来进一步降低复杂度.
如果我们想让2001电话能拨打电话2000, 我们可以添加另一个相反的条目到拨号方案.
<extension name=”example 2″>
 <condition field=”destination_number” expression=”^2000$”>
    <action application=”bridge” data=”user/2000/”>
 </condition>
</extension>
在这个情景中, 终端模块把SIP转换为FreeSWTICH的session, 拨号方案模块转换XML为extension.
Bridge应用把创建一个outbound call 和连接音频的复杂代码转换为一个简单的 应用/参数 对。
无论是拨号方案模块和应用模块接口的设计都是围绕在通用的FreeSWITCH sessions.
因此, 抽象不仅仅使用户级别更加简单,同时它也简化了应用和拨号方案的设计,因为they can be made agnostic of the actual endpoint technology involved in the call.
就是因为这种抽象, 当我们明天添加一个新的模块为一些其它协议像Skype(顺便说一下,也确实有一个这样的存在),我们可以再使用相同的应用和拨号方案模块。相同的理论适用与Say, ASR, TTS等其他模块。
如果你想让一些由终端的原生协议提供的一些特有的数据变的可用,这也是可能的。
举个例子,在SIP中有几个特有的协议头以及SIP包中一些有趣数据。
我们通过向channel中添加变量去解决这个问题。使用channel变量,mod_sofia可以设置它独有的值,,在你的拨号方案和应用中你可以通过名字从channel重新取得这些值。这样, 我们分享我们对于这些特殊变量的理解通过SIP终端。然而, FreeSWITCH的核心仅仅把它们当作是专用的channel变量,这些核心并不关心。也有一些保留的channel变量,它们可以影响FreeSWITCH的行为通过很多有的趣的方式。如果你使用过一种脚本语言,或使用变量的配置文件引擎, 那么有助于你的理解,因为channel变量和它们的概念非常的相似。一个简单的变量名和一个值传到了channel中,数值就被设定了。甚至有一个应用接口专门为此: SET应用允许你通过diaplan来设置你自己的变量。
<extension name=”example 3”>
 <condition field=”destination_number” expression=”^2000$”>
    <action application=”set” data=”foo=bar”/>
    <action application=”bridge” data=”user/2000”/>
 </condition>
</extension>
上面的示例和上一个例子几乎相同, 但是并没有直接进行了呼叫,我们先设置了一个名为foo值为bar的变量。这个变量将会自始至终存在于这个通话中,在通话结束时的详细记录中也会被提及。
我们把事情分的越细,越多的相同的底层资源就可以被复用, 使系统可以简单地使用。
例如, 编码接口除了关心它的编码解码世界,对核心一无无知。一旦一个可用的编码模块已经被写成, 它就可以被任何终端使用,在它的音频流中使用这个编码。这表明如果我们把一个 文字-到-语音 模块工作正常,我们可以生成合成的语音在任何,所有FreeSWITCH支持的模块。 这不需要关心哪一个模块先被添加,互相也不需要更改些什么。TTS模块因它可以使用更多的编码更变得更有用;编码变得更有用,因为我们利用它们添加了一个新的功能。同样的想法用于用 应用模块。如果我们写了一个新的应用模块,已经存在终端可以立即执行和使用那个应用。
Complex applications made simple
复杂的应用简单化
FreeSWITCH降低了许多高级应用的复杂性。让我们来看一下更复杂的应用中的两个示例。
Voicemail语音邮件
第一个我们要讨论的模块就是语音邮件应用。这个模块的作用是很容易猜到。它提供语音留言服务。在通话不能正常接通的情况下,这个应用作为第二个选择是很有用的。我们可以使用一个精心的组合去实现这个应用选择,这两个中的某一个新奇的变量,我们已经了解过了。
让我们来看一下最后的一个extension. 它允许我们留下一个口讯
<extension name=”example 4”>
 <condition field=”destination_number” expression=”^2000$”>
    <action application=”set” data=”hangup_after_bridge=true”/>
    <action application=”bridge” data=”user/2000”/>
    <action application=”voicemail” data=”default ${domain} 2000”/>
 </condition>
</extension>
这里我们看到了channel 变量的两种用法。首先,我们设置了 hangup_after_bridge=true 告诉系统,一旦我们至少成功bridge一个通话到另一个电话则挂断,忽视其余的指令。
你已经看到了,我们也通过以美元符号做前缀括号使用了domain变量,${domain}. 这个特殊的变量默认是所有的电话配置文件中的自动配置的域名。在例子中,我们检查如果有人正在拨打2000,我们尝试去bridge注册在分机2000的电话。如果呼叫失败或对方正忙,我们会执行下一个指令,也就是进入语言留言应用。我们提供了一些应用需要的一些信息,哪一个分机要进入语音邮件,所以语音邮件应用知道在这种情形下怎么去处理。下一步, 语音邮件应用播放预先录制的问候语或使用我们刚才简短的讨论过的SAY模块接口去生成一个问候语。SAY模块串连一些语音文件去生成一个语音说:“分机2000的人现在没空,请留下您的口信”。然后,mod_voicemail 提醒你去录下你的留言,现在是你的选择是否通过留下一个口信来留下你的标记在那个人的收件箱里。还有一个附加的功能,如果你对你的录音不太满意,你可以重复录音多次,直到你满意。一旦你最终提交了口信,一个FreeSWITCH的 MESSAGE_WAITING 事件被触发到核心的事件系统,它被mod_sofia作为一个实现事件接收器的方式,就是把事件信息转换为SIP。如果一切都能如计划一样进行,注册在分机2000的电话将会点亮它的消息提醒灯。
再回到这个例子中来,除了表面我们能看到的怎么去播放一个问候语,录下一条留言,然后发送它到用户那里,我们也揭开了另一个没有被歌颂的英雄--事件系统。
FreeSWITCH的事件系统不是一个像其它例子中那样的模块化接口,它是一个核心引擎,你可以用它来侦听特定命令的消息,然后当事件被收到时根据你的需要来进行响应。换句话说,整个FreeSWITCH的核心,都是事件的发来和接收。
模块可以侦听很多消息,当然也可以”fire”消息到 core engine(事件引擎)中,其它的模块可以侦听这些消息。如我们讨论的那样,sofia SIP模块侦听和订阅 指定的事件 为获取MESSAGE_WAITING 信息。这样就允许我们的mod_voicemail 模块和mod_sofia模块进行交互,互相不需要拥有任何了解关于对方的存在。事件由mod_voicemail触发,由mod_sofia接收然后转换事件到合适的SIP消息—一切看起来是神奇的,人性化的事件系统。当考虑到所有的需要被支持的语言以及如何自动化的播放文件和把它们串连起来,还有好几个难点在这样一个复杂的交互式的系统中。Say模块提供了一个很好的方式能把语音文件串连起来,但在有些情况,如说一个单词,计算或说一个特定的日期,它就显得不足了。我们克服这个困难通过在Say模块的上层定义了一个更复杂的层—宏片(Phrase Macros).宏片是一个XML语法的集合,它通过正则表达式匹配来组建一个参数列表和执行一串命令。这和XML 的diaplan工作原理非常相似,只有量身定制的交互式语音应答方案系统(IVR)。例如,当mod_voicemail请求你录下留言, 不是编码文件的字符串去例say去说的,而是仅仅调用 宏片(Phrase Macro) voicemail_record_message.这个特定的字符串在mod_voicemail和 宏片 间共享,允许我们和用户去编辑文件而不用任何奇特的编程。
<macro name=”voicemail_record_message”>
 <input pattern=”^(.*)$”>
    <match>
      <action function=”play-file” data=”voicemail/vm_record_message.wav”/>
    </match>
 </input>
</macro>
当mod_voicemail 执行宏voicemail_record_message的时候,它首先对pattern进行匹配,在这个例子中是匹配所有,因为这个特别的宏没有输入。
如果宏有输入的话,pattern匹配根据不同的输入可以用来执行不行的动作。 一旦寻找到一个匹配,被匹配的参数就被解析成XML标签就像我们的dialplan例子。
这个宏仅仅是播放文件vm_record_message.wav, 但是更复杂的宏,像那个验证你的录音或告诉你的语音收件箱里有几封语音留言的宏,他们可能需要使用许多次say模块和播放声音文件。宏短语会详细的讨论在第6章-使用内置的XML IVR引擎,然后更广范的使用在第7章节-使用LUA建立IVR应用。
再说一次,我们让phrase系统,声音文件和SAY模块,由系统加载在一起使他们协作可以提供强大的功能。Say模块是专门为一种特定语言或某中语言的声音所开发的。我们可以通过编程来请求说一个特定的时间和根据输入的变量来翻译这个时间到合适的say模块。Phrase Macro 系统是一个很伟大的方式去在你的代码中加入更大的变量概念,这样可以让普通用户非常容易的调整.
例如,如果我们想要建立一个轻量级的IVR,请求用户输入一个4位的号码然后把它读出来后挂断通话,我们可以建立一个宏叫“myapp_ask_for_digits”和另一个宏叫“myapp_read_digits”。
在我们的代码中,我们可以用过名字来执行这些宏--当是时候请求数字的时候执行第一个,另一个去读回我们输入后传入的数字。一旦这些就位,一个无经验的个人就有能力实现通过XML文件去播放适当的声音。他/她可以使用say模块去读出输入的数字,并且支持多种语言而不需要再进行编码。Voicemail仅仅是FreeSWITCH作为一个应用服务器使用的例子。当我们使用FreeSWITCH连接电话和电脑时,一切皆有可能!
Multi-party conferencing
多方会议
FreeSWITCH的另一个非常受欢迎的功能是由mod_conference模块提供的会议功能。mod_conderence模块提供动态的会议室,会议室可以将好几个不同的语音通道桥接在一起。
这样可以用来支持会议,当有好几个呼叫者想互相沟通在同一个通话中。每一个新的session连接到相同的会议室时将会互相连接,并且可以立即在相同的时间向与会的所有成员聊天。通用一个Diaplan的示例,和brigde到其他的电话一样,我们可以使一个分机加入一个会议室:
<extension name=”example 4”>
 <condition field=”destination_number” expression=”^3000$”>
    <action application=”conference” data=”3000@default”/>
 </condition>
</extension>
这和连接一通电话很像,但是在这个extension中特别地是很多的主叫者都可以呼叫3000去加入相同的会议室。如果三个人加入了这个会议,其中的一个决定离开,其它的两个人依然可以继续他们的会议。这个会议模块还有一个特别的功能,像可以播放声音文件或TTS,甚至只是一个单个成员会议。
你可能已经猜到了,我们可以通过TTS和声音文件接口他们各自的模块去提供这些功能。再说一遍,
the smaller pieces组合在一起可以不需要清楚的了解系统中其它组件去扩展功能。
会议模块通过一个叫做自定义事件(custom events)的方式去使用事件系统。像mod_conference这样的模块在它被加载的时候可以保留一个特殊的事件命名空间的子集。当我们关心的事件发生,像一个成员加入或离开了会议,它触发这类CUSTOM的事件到核心中。当我们对接收这类的事件很感兴趣时,所有我们需要做的是仅仅是订阅这个CUSTOM事件,提供一个额外的子集字符标识,这个标识用来指定哪一种CUSTOM的事件我们希望接收。在这个例子中,这是conference::maintenance.这样就使获取到重要的事件,像有人加入或离开会议,开始和结束讲话,变成了可能。会议将会在在第10章,高级应用和加深阅读,进行详细的讨论。

评论已关闭。