标签:asterisk1
Asterisk[1]是一款GPLv2协议下的开源电话应用平台。简单来说,Asterisk是一个服务器应用,能够完成发起电话呼叫、接受电话呼叫、对电话呼叫进行定制处理。
1.2.1 通道驱动
asterisk的通道驱动接口是最复杂也是最重要的可用接口。asteisk的通道API提供了对各种通信协议的抽象,使得asterisk的各种功能特性不必关心具体的通信协议。该组件主要是负责在asterisk通道抽象和具体的通信协议实现中的通信。
asterisk通道驱动接口的定义是ast_channel_tech接口。这个接口中定义了一些通道驱动必须要实现的方法。通道驱动首先要实现的方法是ast_channel工厂方法,即ast_channel_tech中的requester。当一个asterisk通道创建后,无论该通道是incoming方向的还是outgoing方向的,与该通道相关联的ast_channel_tech实现负责实例化和初始化该路通话对应的ast_channel。
ast_channel创建完成后,该结构中有一个创建该通道的ast_channel_tech指针。当然有很多其他的操作需要按照具体技术相关的方式来处理。图1.2中展示了asterisk中的两个通道,图1.4进行了扩展,展示了两个桥接的通道,以及通道技术如何实现的。
在ast_channel_tech中最重要的方法包括:
· requester:用于向通道驱动请求并实例化一个ast_channel对象,根据通道类型进行适当的初始化工作。
· call: 用户向ast_channel表示的终端发起一个出局呼叫。
· answer: 当asterisk决定应该对ast_channel关联的入局呼叫进行应答时调用。
· hangup: 当系统决定当前的呼叫应该挂断时调用。通道驱动需要与终端按照一定的协议进行通信。
· indicate: 通话开始后,还会产生一些其他的事件,需要将这些事件通知给终端。例如,如果设备被保持住了,这个函数就会被调用。
· send_digit_begin: 当终端设备开始向asterisk发送按键DTMF的时候,调用该函数。
· send_digit_end: 当终端设备向asterisk发送按键DTMF结束的时候,调用该函数。
· read: 当asterisk核心需要从终端读入一个ast_frame数据帧的时候调用read函数。ast_frame帧是asterisk中用来封装媒体(诸如音频或者视频)和信号的抽象结构。
· write: 使用该函数向终端设备发送一个ast_frame帧。一般是由通道驱动来完成数据的处理(采集等)和打包使得数据包能够适合所采用的通信协议,然后将打包后的数据发送到终端。
· bridge:该通道类型中的本地桥接函数。前面提到了,进行本地桥接是通道驱动为相同类型的两个通道提供了一种更高效的桥接方法,而不是将所有的信令流和媒体流都通过额外的抽象层来完成。这对于提供性能极其重要。
通话结束后,asterisk核心中的抽象通道处理代码会调用ast_channel_tech中的hangup函数,然后销毁ast_channel对象。
1.1.2 通道桥接
一个更为熟悉一点的呼叫场景是两个电话间的连接。在这个场景里,有两个电话终端与Asterisk系统连接,所以这个通话里存在两个通道。
图1.2 两个呼叫leg,代表了两个通道
展示了asterisk中的两个通道,图1.4进行了扩展,展示了两个桥接的通道,以及通道技术如何实现的。
图1.4 通道技术和通道抽象层
在我们进入例子之前,让我们一起看看asterisk拨号方案中处理呼叫1234这个号码的语法。
注意,1234这个号码是随便选的。
呼叫该号码后,调用了3个拨号方案应用,
首先接听该通话,然后播放一个声音文件,最后挂断该通话。
; Define the rules forwhat happens when someone dials 1234. ;
exten => 1234,1,Answer()
same=> n,Playback(demo-congrats)
same => n,Hangup() .csharpcode, .csharpcode
pre { font-size: small; color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin:0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff;} .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color:#ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color:#ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin:0em; } .csharpcode .lnum { color: #606060; }
exten关键字是用来定义extension的。
在exten行的右侧,1234是为呼叫1234这个号码定义的呼叫规则。
下面的1是拨打1234这个号码时执行的第一步操作,Answer是告诉系统接听这个呼叫。下面的两行,是用same这个关键字开头的,是定义的上面的extension接下来的规则,这里就是1234接下来的规则。
n是下一步要执行的操作,后面的项指明了拨号方案要执行的具体动作。
1.1.1 通道
1.1.2 通道桥接
一个更为熟悉一点的呼叫场景是两个电话间的连接。在这个场景里,有两个电话终端与Asterisk系统连接,所以这个通话里存在两个通道。
图1.2 两个呼叫leg,代表了两个通道
当asterisk的通道像上面这样连接在一起的,就称之为一个通道桥接。执行通道桥接后将两个通道桥接在一起,其目的是在这两个通道间可以传递媒体信息
所有的媒体流都是通过asterisk来协商的。asterisk可以在不同的技术之间进行录音、音频操作、和转码。
两个通道桥接在一起,可以通过如下两个方法来完成:通用桥接和本地桥接。通用桥接是不管使用什么样的通道技术均能正常工作,这种桥接是通过asterisk的抽象通道接口来传递所有的音频和信令数据。这种桥接方法是最复杂的,也是最有效的。
本地桥接是和通话所使用的技术相关的一种桥接。如果两个通道使用相同的媒体传输技术,可以使用一种更高效的方式而不用通过像不同技术那种方式要通过asterisk的抽象层来完成
决定使用通用桥接还是本地桥接是在桥接的时候通过两个通道的比较完成的。如果两个通道均支持本地桥接,则采用本地桥接,反之,则使用通用桥接。为了判断两个通道是否支持相同的本地桥接方法,可以简单的通过c函数指针的比较。这种比较方法,并不是最优雅的方法,但是我们还没有遇到不能满足我们需要的情况。关于通道的本地桥接将会在1.2节讨论。图1.3说明的是一个本地桥接的例子。
图3 本地桥接
1.1.3 帧
在asterisk代码里一个通话的通信是通过使用帧来完成的。帧是数据结构ast_frame的一个实例。
Asterisk中支持的帧类型列表是静态定义的,每种类型的帧是通过数字编码的类型(type)和子类型(subtype)标识的。完整的帧类型列表中include/asterisk/frame.h文件中,一些例子如下:
· VOICE: 这些帧携带部分语音流
· VIDEO: 这些帧携带部分视频流
· MODEM: 这种帧里面的数据的编码,诸如T.38是通过IP网络来发送传真的。这种帧类型主要是用来处理传真。一定要注意的一点是这种数据帧,一定要连续不能中断,以保证对端能对数据进行正确的解码。这与AUDIO帧不同,音频帧可以通过不同的音频编码进行转码虽然牺牲了音频质量但节省了网络带宽。
· CONTROL: 这种帧中包括的是通话的信令消息。这些帧通常用来说明通话信令时间,包括电话接通,挂断,保持等。
· DTMF_BEGIN: 数字开始处。这种帧一般是通话者在电话机上开始按一个DTMF按键。(双音多频:dual-tonemultifrequency双音多频 DTMF)
· DTMF_END: 数字结束处。这种帧是通话者在结束电话机上的DTMF按键。
1.2 Asterisk组件抽象
Asterisk是一个高度模块化的应用程序。包括一个核心的应用,可以通过Asterisk代码树的main目录编译构建。但是,光这个核心通常没有什么用。核心应用主要处理模块注册,也有代码包括如何连接抽象接口来完成电话通话。具体的实现接口是通过可以在运行时刻加载到系统中的模块完成的。
默认情况下,所有的模块均放在asterisk预定义好的模块文件目录中,该目录下的所有模块会由主应用启动后进行加载。之所以这样设计,就是为了保持更加简单。在asterisk中还有一个配置文件,在这个配置文件中可以定义加载的模块和加载模块的顺序。这样会显得配置起来有点麻烦,不过可以让用户指定哪些模块中不需要时,可以不加载。这样最大的好处就是减少应用的内存占用,当然有时候也会有助于提高系统安全。最好的做法是如果不是非常需要,不要加载那些能够接受网络连接的模块。
当模块加载完成后,会向asterisk主应用注册本模块所实现的组件抽象接口。模块可以实现并向asterisk核心注册的接口有多种类型。一般而言,相关联的功能会放在一个模块中。
1.2.1 通道驱动
asterisk的通道驱动接口是最复杂也是最重要的可用接口。asteisk的通道API提供了对各种通信协议的抽象,使得asterisk的各种功能特性不必关心具体的通信协议。该组件主要是负责在asterisk通道抽象和具体的通信协议实现中的通信。
asterisk通道驱动接口的定义是ast_channel_tech接口。这个接口中定义了一些通道驱动必须要实现的方法。通道驱动首先要实现的方法是ast_channel工厂方法,即ast_channel_tech中的requester。当一个asterisk通道创建后,无论该通道是incoming方向的还是outgoing方向的,与该通道相关联的ast_channel_tech实现负责实例化和初始化该路通话对应的ast_channel。
ast_channel创建完成后,该结构中有一个创建该通道的ast_channel_tech指针。当然有很多其他的操作需要按照具体技术相关的方式来处理。图1.2中展示了asterisk中的两个通道,图1.4进行了扩展,展示了两个桥接的通道,以及通道技术如何实现的。
图1.4 通道技术和通道抽象层
在ast_channel_tech中最重要的方法包括:
· requester:用于向通道驱动请求并实例化一个ast_channel对象,根据通道类型进行适当的初始化工作。
· call: 用户向ast_channel表示的终端发起一个出局呼叫。
· answer: 当asterisk决定应该对ast_channel关联的入局呼叫进行应答时调用。
· hangup: 当系统决定当前的呼叫应该挂断时调用。通道驱动需要与终端按照一定的协议进行通信。
· indicate: 通话开始后,还会产生一些其他的事件,需要将这些事件通知给终端。例如,如果设备被保持住了,这个函数就会被调用。
· send_digit_begin: 当终端设备开始向asterisk发送按键DTMF的时候,调用该函数。
· send_digit_end: 当终端设备向asterisk发送按键DTMF结束的时候,调用该函数。
· read: 当asterisk核心需要从终端读入一个ast_frame数据帧的时候调用read函数。ast_frame帧是asterisk中用来封装媒体(诸如音频或者视频)和信号的抽象结构。
· write: 使用该函数向终端设备发送一个ast_frame帧。一般是由通道驱动来完成数据的处理(采集等)和打包使得数据包能够适合所采用的通信协议,然后将打包后的数据发送到终端。
· bridge:该通道类型中的本地桥接函数。前面提到了,进行本地桥接是通道驱动为相同类型的两个通道提供了一种更高效的桥接方法,而不是将所有的信令流和媒体流都通过额外的抽象层来完成。这对于提供性能极其重要。
通话结束后,asterisk核心中的抽象通道处理代码会调用ast_channel_tech中的hangup函数,然后销毁ast_channel对象。
1.2.2 拨号应用
asterisk管理员通过/etc/asterisk/extensions.conf中的拨号规划来设置呼叫路由。拨号方案是一系列的呼叫路由规则(称为extension)构成。电话呼叫进到系统中后,系统使用被叫号码在该呼叫应该使用的拨号方案中查找对应的extension。extension包括一系列可以在该通道上执行的拨号方案应用。拨号方案中使用的应用是由asterisk中的应用注册机制来维护的。应用的注册是在对应的模块加载的时候就完成。
通过asterisk的拨号方案,多个应用可以一起使用来定制呼叫的处理过程。对于使用提供的拨号方案无法完成的复杂定制,\
1.2.4 编码转换
在VOIP世界中,我们使用多种不同的编码来对媒体进行编码,并将编码后的数据发送到网络上。目前存在有很多的编码供选择,但在媒体质量、CPU消耗、带宽需求上会有所牺牲。Asterisk支持多种不同的压缩编码,并且在必要的时候可以进行编码格式间的转换。
在呼叫建立时,Asterisk会尝试让两个终端使用相同的媒体编码格式,这样就可以不用进行转码。但是,这只是理想情况。即便是有共同的编码,转码也仍然需要。例如,如果通过配置让asterisk对经过系统的音频数据进行信号处理诸如增加或者降低音量。Asterisk也可以通过配置进行通话录音,如果配置的录音文件的格式与通话的编码格式不一致,仍然需要编码。
说明:编码协商
协商媒体流使用何种编码的方法对于连接到asterisk所使用的各种技术是确定的。在有些时候,例如通过传统电话网络的呼叫,就不需要进行任何协商。但是,在另外一些情况下,特别是使用IP协议,需要使用一种协商机制,通过描述的终端的能力和优先选择的编码,协商出共同的编码。
以SIP协议为例,这里说明一下呼叫到asterisk后如何进行编码协商的。
说明:编码协商
协商媒体流使用何种编码的方法对于连接到asterisk所使用的各种技术是确定的。在有些时候,例如通过传统电话网络的呼叫,就不需要进行任何协商。但是,在另外一些情况下,特别是使用IP协议,需要使用一种协商机制,通过描述的终端的能力和优先选择的编码,协商出共同的编码。
以SIP协议为例,这里说明一下呼叫到asterisk后如何进行编码协商的。
1.终端向asterisk发起呼叫请求,在该请求中包含了终端希望采用的编码格式。
2.Asterisk通过查询管理员配置好的语音编码优先顺序,选择一种最优先的编码,该编码既是asterisk优先顺序表的编码,同时终端也可以支持。
Asterisk编码处理不太好的地方是对于较为复杂的编码,特别是视频编码。编码协商的需求在过去的10年内已经非常复杂。为了更好的处理新的音频编码和能更好的支持视频编码,我们还有很多工作需要做。这是asterisk下一发行版本中优先考虑的新需求。
编码转换模块提供了一个或者多个ast_translator接口的实现。编码转换器具有源和目的格式属性,提供了一个回调函数可以完成一段媒体信息从源格式到目的格式的转换。编码转换器本身对通话本身并不知道,所需知道的仅仅是如何完成媒体从一个格式到另一个格式的转换。
关于转换器API的更多信息,可以参考include/asterisk/translate.h和main/translate.c。转换器抽象的实现可以在codecs 目录中找到。
标签:asterisk1
原文地址:http://blog.csdn.net/pingdouble/article/details/46476675