下载本文档
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、ubuntu安装编译运行可以使用 telnet 5038登录到yate输入可以显示支持的命令连按tab键可以补全命令先debug on然后再debug level 10,可以打开log编译步骤要先安装 apt-get install libqt4-dev在 yate project 的根 目录下./autogen./configuremake install然后在clients目录下运行./run-qt4脚本,会调用同样目录下的yate-qt4,这个是编译生成的。wimdows安装编译运行 yate client安装 安装了这2个软件后就可以使用vs2010 vs2005了,下面的不走可以不用
2、 做,但要设置环境变量。在环境变量中的系统环境变量增加:用vs2005打开 的文件有错误,需要改成class WinClientPlugin : public Pluginpublic:WinClientPlugin():Plugin("winyateclient" ),m_thread(0) virtual void initialize( void);virtual bool isBusy() const return true ; private:WinClientThread* m_thread;Qt4 相关的几个工程和_customtable _customtab
3、le _customtext _clientarchive 需要加上 qt 库和头文件的路径。然后挨个编译每个工程,然后把中的release改成debug,然后双击之,就会运行 yate client 了。Yate 代码学习笔记Yate 开发向导框架设计Yate 的设计是为了提供一个可扩展性的电话引擎, 试图以最简简洁的代码, 在扩展所需功能 与性能、稳定性之间达到最佳平衡。Server EngineConfigurationMessage running thread poolPlugin loaderMessage QueueMessage Dispatchersocket Tnt trf
4、a.eexrnbo ddadMesCard channelPluginHardwareInlerfaceDriverBaseclassesMe航,包g邑i Handler (VoIP channelPlugin .二 1r 一 ILibraryI Int e jrf" jExternalVoIPLibraryGatewayPlugin Socket Intarfacn ;Control TExternalApplicationMesaa9e; 1I Hdndl&r ;ScrlplingPluginJavascript,Pen, PHPYate设计分为三大部分:引擎(Engin
5、e )将把所有模块组件连接在一起,上图描述了各组件之间的交互该引擎已Yate C+类为基础, 过程。(2)模块(Modules )大部分功能由运行时加载的模块来实现。这些模块以动态链接库为载体,作为插件被引擎或外部程序的特定模块加载。被外部程序特定模块加载时,需能够与引擎或其他模块之间相互 交互(通信)。消息(Messages )Yate模块(包括插件和外部模块)之间的交互是依靠消息实现的。消息提供了一种可扩展,可定制,并且与具体技术无关的交互机制。每个模块在需要得到信息或者需要通知其他模块 时只需要创建并向引擎提交消息,引擎负责会将消息传递给合适的目标。-订阅)的设计Yate以Class E
6、ngine为核心,构建了插件式的管理体系,按照观察者(发布 模式来处理数据流。Engine类根据配置文件加载插件,缺省参数的情况下, 会加载指定目录下所有的插件。然后运行插件的初始化函数initialize。完成插件的初始化。Class Yate提供了一些API (静态函数)用于加载分析配置参数,加载特定模块,和指定 目录下的所有模块。Message *m = new Message("");2. m->addParam("driver","iax");I |3. if (e->I 4.m->addParam(&qu
7、ot;callername",e- >;I 5. elseI)6. m->addParam("callername",e->session->callerid);7. if (e->8. m->addParam("called",e->9. else10. m->addParam("called",e->session->dnid);然后我们将消息发送给引擎,检查是否有模块( module )接收并处理了,最后必须将消息 销毁。1. if (Engine:dispa
8、tch(m)2. Output("Routing returned: %s",m->retValue().c_str();3. else4. Output("Nobody routed the call!");5. m->destruct();上面的处理方式是阻塞式的,模块发送消息之后需要等待该消息被发送之后才进行后续的处理。Yate中还有一种“发射后忘记"( fire-and-forget )的消息机制,非阻塞式消息机制,这种消息被存储在引擎中的一个队列中,当消息被分发后,由引擎负责释放。这种消息一般都是事关系统全局的重要消息,例
9、如错误报警,如下代码所示:1. Message *m = new Message("alert");2. m->addParam("reason","Hardware malfunction");3. Engine:enqueue(m);如果我们编写的模块需要处理一个路由请求(极有可能是其他模块产生的),我们首先需要声明一个名为 RouteHandler的类,并从MessageHandler基类继承。1. class RouteHandler : public MessageHandler2. 3. public:4. Rout
10、eHandler(int prio)5. : MessageHandler("",prio) 6. virtual bool received(Message &msg);7. ;然后,由于在received方法中实现,他是类 MessageHandler中是纯虚函数,我们必须重载。1. bool RouteHandler:received(Message &msg)2. 3. const char *driver = ("driver")4. Debug(DebugInfo,"Route for driver %s"
11、,driver);5. return false;7. l最后,在插件的initialized方法中,安装此消息处理器1. void RegfilePlugin:initialize()2. 3. m_route = new RouteHandler(priority);4. Engine:install(m_route);5. 这样,该插件就能处理"”消息了,这个例子中实际上只是接收了消息但没有做任何动作,如果需要什么操作,在 received方法里实现即可。Yate中的几乎所有消息操作者都是按照这样的框架实现的。消息流示例以呼叫进入为例:路由当一个通道模块检测到有呼叫进入(1),
12、它便发送(2)消息来决定将此呼叫路由到哪个位置。消息将被叫号码映射到一个呼叫目标。连接当呼叫对象已知以后,呼入通道将其呼叫端点( CallEndPoint )附在 消息上(4)。接收方应 该将它的呼叫端点连接到 中携带的呼叫端点上。在等待对端接受呼叫期间应该发送消息(6),当呼叫被接受时, 被发送。会话期间在会话期间,消息(8,9)能在两个方向上发送。挂机当呼入通道检测到挂机 (10),它将断开其呼叫端点。断开呼叫端点将引发两个通道消息和 消息的(图中未包括)发送。从消息流的示例我们可以看到, Yate 对呼叫的抽象很清晰,在逻辑上符合人们的思维习惯,比较容易理解。消息类型1. 引擎消息( E
13、ngine messages)由引擎发送给普通模块,通知他们 Yate 准备就绪,并已进入主循环2. 通道消息( Channel messages )双音多频信号( Dual-tone multi-frequency signaling )3. 呼叫消息( Call messages)4. 用户消息( User messages )5. 资源描述 / 通知消息 (Resource subscribe/notify messages)6. SIP 消息 (SIP messages) sip.<methodname>7. 编解码特定协议消息(Encode or decode proto
14、col specific messages)8. 网络操作消息( Socket operation messages )9. 集群相关消息( Clustering related messages )10. 即时信息相关消息( Instant messaging related messages )11. Jabber / XMPP messages12. 其他消息( Miscellaneous messages )database模块创建方法在实际应用中, 可能需要编写自己的模块插入到系统中或者替换掉自己的模块, 例如, 使用 商用的SIP协议栈替代Yate中开源的SIP协议栈,所以,编写在
15、Yate框架下编写自己的模块是使用 Yate 平台必备的技能。Yate 可分为两个部分* Yate 内核* Yate 模块* Yate 内核提供基础,辅助 API 以及消息系统* Yate 模块使用 Yate 内核实现特定的功能Yate 模块的类型Yate 模块可分为一下几种1 . 通道2 .路由器3 . 电话历史记录(Call Detail Recorder)4 .计费程序5 .其他模块如何指定模块的类型模块类型在这里涉及许多概念性的东西。 Yate 的设计中并不区分模块的种类,而是根据模块处理的消息类型来区分模块类型。 例如一个通道模块接受消息, 并创建一个通道来接受处理它。 有此特征的模
16、块我们称之通道模块。 另一方面即使如果模块可能接受消息并处理一个事情,但并不创建一个通道/终端,则它不是一个通道模块。CDRBuilder就是这样的模块。如果你还不清楚,稍等,接下来的例子会说明清楚。程序员眼里的消息消息应正确派发到注册了并在监听该消息的模块中。模块可以指定接受消息的优先级。如果一模块监听的消息优先级为 50 ,其他模块也在监听消息,但优先级值大于50,则该模块应该先于其他模块获取消息。 一旦接收到该消息, 模块可向派发器返回 true 或 fale , 并附带一些额外信息。如果返回 true ,则派发器停止向后续的模块发送消息。返回false ,则允许消息按照优先级继续派发到
17、其他模块中。Yate 消息不会同 Windows 消息相混淆,因为他只在Yate 系统范围内发送而没有使用操作系统机制发送消息。此外,Yate消息构造是以字符串定义的,而OS消息使用的是数值。TelEngine 命名空间所有的Yate API都什么在TelEngine命名空间中,命名空间是C+的一个概念,用于减少程序中变量名和函数名重名冲突。因此,要使用命名空间TelEngine,如不想写 TelEngine:blahblah,你可以使用:using namespace TelEngine;我们的第一个模块我们现在开始写我们的第一个模块,可以接受消息,并将其呼叫和被叫的号码输出到控制台中。我们
18、设模块名为。在这个模块中我们需要讨论以下几个类。*Module*Message*MessageReceiver*String同样还得介绍CPP文件准备我们需在模块目录下创建一个名为的文件包含文件则可使用YATE的API第一步-模块代码和前面解释的一样, Yate 模块要申明他是一个Yate 模块都需从Module 类派生。 Module 是从多个类派生的。在Yate API 继承关系如下:class YATE_API Module :public Plugin, public Mutex, public MessageReceiver, public DebugEnabler注意Plugin类
19、向Yate提供了加载模块的功能在我们的模块中声明类如下:class MyModule : public Module public:MyModule(): Module("MyModule1" ,"misc" ), m_init(false) virtual void initialize();virtual bool received(Message &msg,int id);private :enumCallRoute = Private + 1 PrivateRelayID; bool m_init;第二步:创建Module 类的静态变量当
20、Yate模块加载一个模块是,他首先会在模块中( Plugin)查找名为_plugin的静态变量。 在我们的例子中,我们需要定义类MyModule (派生于Plugin)的一个静态变量。Yate提供了帮助做这个,仅需要这样使用:INIT_PLUGIN(MyModule);第三步:模块initialize() 的重要性你需特别注意到我们在MyModule 声明定义的 initialize 方法。 initialize 函数在 Plugin 类是虚函数, 在 Yate 加载模块时会被调用。 MyModule 需要重载它, 做一些模块初始化工作。 通常在这里注册模块想监听的消息。我们可这样写代码:vo
21、id MyModule:initialize()Output("Initializing module mymodule1" );if(!m_init) m_init = true; Yate 提供的 Output 函数输出信息到控制台。它只在必要的时候使用。 Yate 同样提供了一些 其他 API 函数将调试信息输出到控制台。第四步:添加代码接受消息模块通常需要接受一个或多个消息。模块接受到一个消息并执行程序制度的工作。 Module 类派生与MessageReceiver, MessageReceiver通过虚函数received。提供了接受消息能力。我们可按自己爱好添
22、加代码接受消息,像这样:void MyModule:initialize() Output("Initializing Module mymodule1" );if(!m_init) Engine:install(new MessageRelay("" ,this ,CallRoute,55);m_init = true;Yate中的类Engine。Yate创建了一个Engine的静态对象作为他(Yate)的启动(不是模块的启动)。这个类启动真个Yate 的服务器 / 服务。这个类全是静态成员函数。 Install() 同样是静态函数,这也就是我们为什么
23、 Engine:install 这样调用的原因。 install 向 yate 提供了我们的MessageRelay类对象,指定了我们想要监听消息的消息id、监听优先级等。第三个参数CallRoute是枚举值,它仅指定我们想关联消息的消息ID。枚举值CallRoute为Private+1,在类 Module 中定义, 用于指定 (说明) 该消息在模块中没有任何RelayID。 可看看Yate 中 Module类的枚举值。当你监听到多个消息, 你需要识别监听到的消息,当然这有多种方式实现。注意在 MessageRelay 的最好一个参数值为 55 , 这说明我们想监听的消息优先级别值为55。 因
24、此如果其他模块也在监听同样的消息且优先级更高, 将先接收到这个消息并且如果他返回非ture,我们的这个模块才能接受到这个消息。MessageRelay被Yate用例发送消息,它的构造函数拥有一个MessageReceiver类的对象用户消息的通知。我们 MyModule继承于Module , 而 Module 继承于MessageReceiver ,因此,我们在MyModule 可提供这个对象( MessageReceiver )。第五步:重载received类 MessageReceiver 的 received(Message &msg,int id) 方法由 Yate 调用, 用
25、于派发注册过监听的消息。 received 在 MessageReceiver 中声明为虚函数。注意,返回值为 bool ,如果处理完这个消息, 并不想后面的模块接受处理该消息, 返回 true 。 希望后续的模块继续处理该消息返回 false 即可。这里( received )我们可以添加自己的逻辑代码。在 received 中我们可写一 些代码,在消息来临时,输出呼叫者和被呼叫者名。bool MyModule:received(Message &msg, int id) String called,caller;called = ("called" );call
26、er = ("caller" );Output("mymodule1: called caller %s, and called %s",(),(); return false;上述代码使用了 Yate API 提供的 String 类。从名可知这个类提供了字符串的相关操作。Message继承与NamedList,这是YATE提供的另一个类,用于更好管理字符串与字符串直接 的映射链表。类 NamedList的函数getValue(),我们先获取被叫号码(熟知的DNID),然后获 取呼叫者的号码,作为电信运营商的ANI/CLI (被叫者ID/呼叫者ID)。
27、c_str为String的成员函数,返回字符串存储的数据,类型为 const char*. #include <> #include <>#include <>#include <>using namespace TelEngine;class MyModule : public Modulepublic:MyModule(): Module("MyModule1" ,"misc" ), m_init(false) virtual void initialize();virtual bool receive
28、d(Message &msg,int id);private :enumCallRoute = Private + 1 PrivateRelayID;bool m_init;bool MyModule:received(Message &msg, int id)String called,caller;called = ("called" );caller = ("caller" );Output("mymodule1: calledcaller %s, and called %s",(),();return fals
29、e;void MyModule:initialize()Output("Initializing Module mymodule1" );if(!m_init) Engine:install(new MessageRelay("" ,this,CallRoute,55); m_init = true; INIT_PLUGIN(MyModule);/* vi: set ts=8 sw=4 sts=4 noet: */YAT日肖息机制消息是YATE的一个主要组成部分。所有的模块间通信都使用消息而不使用函数调用,这主 要是因为我们考虑当一个模块发生变化的时候不
30、影响或不依赖于其他模块,也因为我们知道模块直接传递的参数,使得我们的调试模块可以更容易些。一个消息由以下几个部分组成:* 名字(name)消息类型的标识,允许消息处理器通过名字进行匹配* 返回值(return value) 一个用字符串表示的处理消息之后的返回值* 时间(time)消息被创建的时间;这对于排队的消息检查等待时长非常重要* 参数(parameters)可能有多个或0个参数组成,每个参数由名称、值对构成。每个处理器都能根据参数进行不同的动作,或者修改参数本身。不知道的参数必须忽略。所有的消息在YATE内部是二进制形式的。然而我们可以通过 rmanager模块提供一个对人可 读的形式
31、。YATE内部消息传递通过内存共享( memory sharing )。在这种方式下,可以提高系统的 性能。其他可以考虑的形式如管道或Sockets,没有内存共享灵活和高效。当被传递到外部模块(external modules )时,消息被转换成字符串编码的形式,这样所有 能处理文本的外部模块都可以处理消息了。可参考文档external module ,获取更多详细信息。消息由消息处理器处理,每个消息处理器仅处理与它名字相同的消息。他们可以任意修改 消息元素(参数,返回值,甚至消息名称),一个消息处理器处理完之后,可 由下一个消息处理器处理。消息处理器处理消息的顺序,再插入派发器中就已经决定。
32、处理顺序按照消息处理器的优 先级决定,优先级值越低的预处理器优先级越高,先与优先级值高的接受到消息。对于相同优先级的消息处理器,调用顺序是不确定的。调用顺序按以下的规则:* 同名的消息调用顺序是不会改变的* 为了避免不确定性,如果消息处理器被移除,并插入一个同等优先级的消息处理器,则 他们的顺序由她的的内存地址决定。以下是“在消息系统重的工作过程例子当某个电话打来时,消息是这样产生的:Message *m = new Message("");m->addParam("driver" ,"iax");if (e->m->
33、;addParam("callername" ,e->elsem->addParam("callername" ,e->session->callerid);if (e->m->addParam("called" ,e->elsem->addParam("called" ,e->session->dnid);然后我们将消息发送给引擎,检查是否有模块( module )接收并处理了,最后必须将消息销毁。if (Engine:dispatch(m)Output
34、( "Routing returned: %s" ,m->retValue().c_str();elseOutput( "Nobody routed the call!" );m->destruct();引擎在得到上述消息,将其发送到所有注册名为 “ route 消息处理器模块中。”对于 fire-and-forget 类型消息,他们先存储在引起的消息队列中并由引擎内部的线程池进行延后派发。一旦消息派发完成后则由引擎将其销毁。Message *m = new Message("alert" );m->addParam
35、("reason" ,"Hardware malfunction" );Engine:enqueue(m);为了处理 route 消息请求(很有可能在另外一个模块中),我们首先声明一个从MessageHandler 派生的类 RouteHandler 。class RouteHandler : public MessageHandlerpublic:RouteHandler(int prio): MessageHandler("",prio) virtual bool received(Message &msg);然后,由于方
36、法received 在类 MessageHandler 中是纯虚函数,我们必须重载。bool RouteHandler:received(Message &msg)const char *driver = ( "driver" )Debug(DebugInfo,"Route for driver %s" ,driver);class TranslatorFactory2. class DataTranslator3. class DataBlock4. class FormatRepository5. struct FormatInfo6. st
37、ruct TranslatorCaps假定你已经看过我的第一个教程。这里我们iLBC编解码库为例子。我们使用iLBC 20ms的编解码器。本教程的编解码库和项目中的代码并不是100%的相同。所以如果你想使用iLBC,必须从CVS或Release版本获取最新代码,而不是使用本例代码运用于实际项目之中。第一步:创建一个插件( Plugin)我们需从 Plugin 派生出一个类。虽然普通模块一般都是从类 Module 派生,但是由于我们无需处理消息,因此我们只需要类Plugin 派生, Plugin 的核心功能已经能满足咱们的需求。我们同样需要从TranslatorFactory 派生出一个类,使得
38、我们的模块具有编解码的能力。你这样声明你的类:class iLBCPlugin :public Plugin, public TranslatorFactory public:iLBCPlugin();iLBCPlugin();virtual void initialize() ;注意,从Plugin类继承的initialize方法。我们不需要为ILBC 20ms编解码库做任何事情。第二步:申明一个TranslatorCaps 结构体变量模块需通过某种途径来指定说明他所支持的编码和解码的数据格式。 编解码器将一种格式数据转化成另外一种格式数据使用的编码/解码。编解码器通过使用 slin(Sig
39、ned Linear Audio)格式,编码成指定的数据格式(我们的例子是 iLBC 20m9,解码过程亦是将iLBC 20ms转换成 slin 数据。但是如果你的编解码器能够编码或解码除了 slin 之外的格式,你可以在TranslatorCaps指定编解码的数据格式,并通知 Yate。在我们的例子中我们只能编码slin和解码iLBC 20mso总而言之,TranslatorCaps是你的编解码器数据类型转换的表格。TranslatorCaps 变量声明为全局变量。static TranslatorCaps caps = 0, 0 , 0, 0 , 0, 0 ;关于这点后面详述第三步:定义编
40、解码器功能在构造函数中,我们将用到类FormatRepository 定义编解码器的功能, FormatRepository 仅有两个静态的 public 成员变量。 FormatInfo 是一个含有编解码信息 (例名称、 比特率、 帧数、通道数等等)的结构体。我们需要使用 FormatRepository 来构建 FormatInfo 信息。我们在Plugin 插件的构造函数中做这事。iLBCPlugin:iLBCPlugin() Output("Loaded module iLBC - based on libiLBC");const FormatInfo* f = F
41、ormatRepository:addFormat( "ilbc" ,1900,38);caps0.src = caps1.dest = f;caps0.dest = caps1.src = FormatRepository:getFormat( "slin" );FormatRepository:addFormat 函数有六个参数,后三个有缺省值, Kdoc 文档的注释如下 标准无空白小写的格式名称2 drate 每秒数据样本大小Data rate in octets/xsecond, 0 for variable3 fsize 每帧大小(字节) Fr
42、ame size in octets/frame, 0 for non-framed formatsYate 版本参数*2 fsize 每帧大小Data frame size in octets/frame, 0 for non-framed formats*3 ftime 每帧时间间隔单位为 ms (实际代码确实微妙) Data frame duration in microseconds, 0 for variable4 type 格式类型 audio video text5 srate采样率 音频 样本数/秒(audio) 1e-6帧数/秒(video) 0位置6 nchan 通道数量
43、缺省为 1最后三个参数的缺省值1. "audio"2. 80003. 1你可以根据你的需要和编解码器使用而不同参数。在addFormat中参数name值为ilbc,第二个参数字节/秒(octets/second )可由帧大小乘以每 秒帧数而得。在编解码器中我们是帧 /ms,因此你须将转换成帧/秒。例ilbc 20ms我们每秒 帧数为 1000/20=50 ,所以我们得到的值应该为1900 = 38* ( 1000/20 )帧大小仅仅是编解码帧大小,iLBC 20ms每帧大小为38字节,也就是38 octects , GSM每帧大小 33 字节。所以,我们的格式转换表的应该是这样的:INOUTslinilbc (编码)ilbcslin (解码)第四步 提供所需功能我们需要向YATE提供我们的编解码器功能,因此我们需实现这个方法const TranslatorCaps* iLBCPlugin:getCapabilities()
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年微型金融服务平台搭建可行性研究报告及总结分析
- 2025年门禁系统访问控制协议
- 电信大学(大数据、5G、云计算)考试题库(含答案)
- 2025年多媒体展示系统建设项目可行性研究报告及总结分析
- 2025年区域创新创业孵化器建设项目可行性研究报告及总结分析
- 2025年矿井维修电工(中级)职业技能(理论知识)考试真题试题 含答案
- 2025年新能源交通工具发展可行性研究报告及总结分析
- 2025年货运无人驾驶技术项目可行性研究报告及总结分析
- 2025年增强现实技术在购物体验中的应用可行性研究报告及总结分析
- 2025年快递上门服务合同协议
- 2025年法院检察院书记员面试题和参考答案
- 2025年中国射频模组行业市场集中度、企业竞争格局分析报告-智研咨询发布
- 2025年广西度三类人员(持b证人员)继续教育网络学习考试题目及答案
- 2025年广东省公务员考试(行政执法专业和申论)综合练习题及答案
- 电玩城充值活动方案
- 《我们神圣的国土》课件
- 重晶石矿开采项目可行性研究报告
- 矿山开采沉降管理办法
- GJB939A-2022外购器材的质量管理
- 纪委监督检查知识培训课件
- 2025 年 九年级英语上册Unit 7 单元测试卷含答案
评论
0/150
提交评论