freeswitch软件架构分析

dreamour 贡献于2018-04-04

作者 Administrator  创建于2010-04-13 07:05:00   修改者Administrator  修改于2010-05-06 01:06:00字数27461

文档摘要:FreeSWITCH开发者Anthony Minessale II过去曾是Asterisk PBX系统的开发者之一,原来给asterisk贡献了不少代码,但根据他在其主页上的描述,他在asterisk上开发了有关呼叫队列的应用,但呼叫队列达到一定程度后会引起死锁和崩溃,作者感觉按照原有asterisk的设计思路无法彻底解决这个问题。而asterisk的许多开发者又不附和他的建议:即搞一个2.0的分支来重写代码。于是2005年夏天作者决定自己开发一个项目,就是freeswitch。
关键词:

Freeswitch软件架构分析 1. 背景 2 2. 项目需求 2 3. 部署 2 4. 软件框架 3 4.1模块描述 3 4.2软件架构 4 4.3模块加载机制 5 4.4应用(Applications) 6 4.4.1电话生命周期 6 4.4.2 mod_conference模块 8 4.4.3 mod_voicemail模块 14 4.4.4 mod_commands 14 4.4.5 mod_dptools 17 4.5 自动语音识别/语音合成 20 4.6 编码(Codecs) 20 4.7 拨号计划(Dialplan) 20 4.8 LDAP目录(Directories) 22 4.9 终端(Endpoints) 22 4.9.1 mod_sofia 23 4.9.2 mod_openzap 26 4.10事件处理(Event Handlers) 27 4.11文件格式(File Formats) 30 4.12语言(Languages) 30 4.13日志(Loggers) 30 4.14定时器(Timers) 30 4.15 XML(xml interface) 30 4.16核心库(core libary) 30 4.16.1事件 30 4.16.2任务调度 31 4.16.3日志 32 4.16.4 SQLDB 32 4.16.5 session状态监测线程 32 4.16.6 其它 32 本文基于freeswitch的源码和wiki文档分析freeswitch软件架构。Wiki文档的网址是:http://wiki.freeswitch.org/wiki/Main_Page。 1. 背景 FreeSWITCH开发者Anthony Minessale II过去曾是Asterisk PBX系统的开发者之一,原来给asterisk贡献了不少代码,但根据他在其主页上的描述,他在asterisk上开发了有关呼叫队列的应用,但呼叫队列达到一定程度后会引起死锁和崩溃,作者感觉按照原有asterisk的设计思路无法彻底解决这个问题。而asterisk的许多开发者又不附和他的建议:即搞一个2.0的分支来重写代码。于是2005年夏天作者决定自己开发一个项目,就是freeswitch。 FreeSWITCH是以C撰写而成的开放源码电话应用软件,可以连接SIP H.323、IAX2、LDAP、Zeroconf、XMPP / Jingle,Google Talk等现有技术,架构出开放源码PBX系统或开放源码的VoIP交换平台。同时Free-SWITCH也能跟现今各种开放源码PBX系统,如OpenPBX、Bayonne、YATE、Asterisk等,相互整合。扮演软交换角色的FreeSWITCH,可以接受来自各类VoIP协定或类比与数位设备的输入,将其彼此连接,让软体电话、IP电话与类比电话互通。特别的是,这套软件还可以用简单的脚本语言(script language)回应通话或执行TTS (Text to Speech,文字语音转换)传输等程序,达成让FreeSWITCH朗读网路上的新闻feeds等效果。按FreeSWITCH开发者的说法,Asterisk更适合于小型的PBX,而FreeSWITCH则是一个软交换系统 (Asterisk is an open source PBX and FreeSWITCH is an open source soft switch.)。 2. 项目需求 公司研发的IM系统中缺少电话互通、电话会议、语音信箱等功能,所以计划将在系统中集成freeswitch。企业使用freesitch主要目的是将其作为电话软交换系统使用,根据数码庄园的要求,公司内部组建电话交换系统,实现内部软电话或sip电话与外部电话或软电话互通。基于以上原因,分析freeswitch系统时主要分析电话互通、电话会议、语音信箱这三方面。 3. 部署 根据需求,画出freeswitch在企业内部的部署图,图中没有画出另一端通信端点,根据以后的要求进一步扩展。图中的虚线表示freeswitch服务器插上E1板块直接连接到PSTN网络。SIP网关连接internet,实现和其它的远程SIP客户端通信。 拓扑图只描述了freeswitch部署中的关键设备,在实际中,还会有防火墙等其它网络设备如防火墙,freeswitch具有NAT功能,可以很好的与防火墙兼容。 4. 软件框架 4.1模块描述 FreeSWITCH被分成几类模块,模块以.a或.dll动态加载(关于加载机制在4.3稍后的段落中描述),在需要时执行模块提供的APP或API接口。模块加载的配置文件是conf/autoload/modules.conf.xml,文件中可以设置需要加载的模块,fs启动后,会根据此文件内容加载模块。下面模块分类列表: · 应用(Applications)。 · 自动语音识别/语音合成(Speech Recognition/Text-to-Speech) · 编码(Codecs) · 拨号计划(Dialplan) · LDAP目录(Directories)提供访问LDAP服务器功能 · 终端(Endpoints) 各种协议终端 如SIP协议等 · 事件处理(Event Handlers) · 文件格式(File Formats)例如wav文件格式 · 语言(Languages) 如Python和JavaScript的嵌入式语言 · 日志(Loggers) · 定时器(Timers) · 数据库(SQLDB) · XML API接口(xml interface) · 核心库(core libary) FS核心库 · 依赖库 大量使用了第三方库 下图是摘自wiki的一张图片,通过核心库将各个模块连接在一起,很好的表明了他们之间的关系。 4.2软件架构 下图是各个模块的依赖关系图。上层是对外提供的接口,包括SOCKET、HTTP、APP、API、CONSOLE。为用户提供了多中控制接口。其中SOCKET接口是EVENT HANDLERS提供,用户可以远程连接FS并通过其发送API命令、MSG等控制FS的行为。XML INTERFACE对外提供了通过WEB方式控制FS,数据格式是HTTP协议。ENDPOINTS是提供了各种协议终端,如SIP、ASTERISK等。 关于这些模块的具体功能在稍后的章节中描述。 4.3模块加载机制 变量xxx_module_interface类型是: typedef struct switch_loadable_module_function_table {  int switch_api_version;  switch_module_load_t load;  switch_module_shutdown_t shutdown;  switch_module_runtime_t runtime;  switch_module_flag_t flags; } switch_loadable_module_function_table_t; 每一个模块都会在自己的实现文件中定义一个全局上面的结构变量,如mod_sofia_module_interface,结构变量中包含三个函数指针:load、shutdown和runtime。FS加载模块是根据modules.conf.xml文件内容加载模块。以mod_sofia模块为例,FS读取(如Linux系统下调用dlopen函数读取)mod_sofia_module_interface结构变量的地址,然后调用load函数,load函数会将APP/API接口加载到不同类别HASH表里。在以后调用模块接口时,根据APP或API的名称作为HASH的key定位,并调用APP或API函数。 APP和API接口加载完成后,检查模块是否有runtime函数,如果有为此函数创建switch_loadable_module_exec()线程,线程循环调用runtime函数。 当模块需要卸载时,会检查是否有shutdown函数,如果有调用它完成退出和释放资源。 下图是加载的流程图: 4.4应用(Applications) Freeswitch提供了大量的应用模块,包括mod_cidlookup、mod_cluechoo、|mod_commands mod_conference、mod_curl 、mod_directory 、mod_distributor 、mod_dptools 、mod_easyroute 、mod_enum 、mod_esf 、mod_expr 、mod_fax 、mod_fifo 、mod_fsv 、mod_lcr 、mod_limit 、mod_memcache 、mod_nibblebill 、mod_rss 、mod_skel 、mod_snapshot 、mod_snipe_hunt 、mod_snom 、mod_soundtouch 、mod_spy 、mod_stress 、mod_t38gateway 、mod_tone_detect 、mod_valet_parking 、mod_vmd 、mod_voicemail 、mod_xml_odbc。 根据项目需求重点分析以下四个APP模块mod_commands、mod_voicemail、mod_dialplantools和mod_conference。在分析这些模块之前我们先分析一下呼入和呼出是如何工作的。 4.4.1电话生命周期 下图是一个电话生命周期图(摘自wiki文档)。此图很好的描述了一个电话的运行流程。即session的状态监测线程,不断的根据状态解析拨号计划,再执行拨号的扩展应用。 一个新的连接建立后,创建session的状态监测线程,然后(呼入和呼出)mod_sofia模块会将这个状态设置成Routing状态,这种状态下将调用mod_dialplan_xml的dialplan_hunt接口解析拨号计划,然后设置为execute状态,这种状态下状态监测线程执行拨号计划设置的APP命令。这些命令将在dialplan中描述,其中bridge命令是最主要的,它实现桥接两个通道的作用(桥接呼入的电话和目标或桥接呼出电话和目标)。下面是bridge的流程图: 4.4.2 mod_conference模块 一、会议 mod_conference模块注册的APP接口conference_function(),实现电话会议创建功能。根据会议名称检查会议对象是否存在,如果不存在创建电话会议对象,如果已经存在则引用电话会议对象。 创建电话会议函数是conference_new(),这个函数设置conference的数字键控制功能(下一段落描述如何设置),初始化conference对象,加入globals(对于本模块是全局变量)结构变量的哈希表里。创建会议对象后会创建对象的监控线程conference_thread_run()。线程内部检查是否需要录音,如果需要启动录音线程conference_record_thread_run();检查是否需要启动视频线程,如果需要启动视频线程conference_video_thread_run()。会议对象监控线程循环检查会议运行是否在运行状态,如果不是,就退出。如果是运行状态,从每一个会员的输入缓冲区member->audio_buffer读取数据,将数据处理之后,写到每一个会员的omember->mux_buffer输出缓存里。 会议对象的数字键控制功能设置。创建会议对象时,如果配置文件没有设置会议室控制功能,调用caller_controls()设置默认数字键控制函数。如果设置了则调用conference_new_install_caller_controls_custom()完成设置。此函数读取conference.conf.xml配置文件中的节,设置自定义的数字键控制函数。 二、会员 conference_function()接口还有添加会员的功能,会员加入到会议对象的members链表里。调用conference_loop_output()函数,此函数创建conference_loop_input()线程,这个线程稍后描述。检查向外呼叫列表,并调用conference_outcall()函数呼叫会员,向外呼叫在第四节描述。函数循环检查运行状态。在循环内部从member->mux_buffer读取数据,并发送到session对应的通道上,完成数据发送功能。 conference_loop_input线程循环检查运行状态,循环内部读取session对应通道上的音频数据,处理数据并写到member->audio_buffer缓存里。 三、呼入连接 mod_sofia模块加载时,调用config_sofia()读取配置文件,并创建线程sofia_profile_thread_run (),线程会注册SIP的回调函数sofia_event_callback(),当有新连接到来会触发sofia_event_callback(),此时event = nua_i_invite调用sofia_handle_sip_i_invite()分配一个session。连接成功后,event = nua_i_state调用sofia_handle_sip_i_state(),此函数判断SIP状态值等于nua_callstate_received时,设置channel为CS_INIT。 session的状态监测线程,会调用sip的sofia_on_init(),(这个函数是在mod_sofia_load注册的sofia_endpoint_interface->state_handler = &sofia_event_handlers)这个函数,会设置channel为CS_ROUTING。状态监测线程,执行sofia_on_routing(),再执行switch_core_standard_on_routing(),这个函数会调用拨号计划的接口dialplan_interface->hunt_function()解析拨号计划。此函数返回扩展结构即要执行的应用链表,将链表绑定到session->channel上,最后设置channel为CS_EXECUTE状态。状态监测线程,调用switch_core_standard_on_execute()->switch_core_session_execute_ application()。 分析案例:用户连接到freeswitch后,如果拨号是1000~1019会进入电话会议。在我们分析代码实现过程之前看一下下面的配置文件内容: default.conf.xml文件。              ...                  ...                                  features.conf.xml文件 ...                               ... 当用户拨号1000~1019条件(红色字体部分),会执行扩展名称是“cf”(蓝色字体的部分)。“cf”包含的动作是answer和transfer。answer是应答连接,transfer是转移到满足条件的扩展。扩展条件是30xx,也就是绿色字体部分。绿色字体部分包括的动作是应用conference,此APP命令将连接加入到会议室。 拨号进入会议室的流程了解之后,看代码的执行流程。建立新的SIP连接后,session的状态是CS_INIT,状态监测线程调用sofia_oninit(),这个函数会设置session为CS_ROUTING,之后状态线程会调用拨号计划接口hunt_function(),解析完拨号计划后,设置session为CS_EXECUTE状态。状态线程执行拨号计划的动作(APP应有接口)。自此状态线程会不断执行调用拨号计划解析扩展,之后执行拨号计划。session的状态也在CS_ROUTING和CS_EXECUTE之间转换,当然session还有其他状态,暂时不关注其他状态。 四、呼出连接 调用APP接口conference_auto_function()可以在呼叫列表里添加呼叫成员,会员的输出函数conference_loop_output()会检查呼叫列表,并创建线程conference_outcall_run()呼叫会员。线程会调用conference_outcall()完成呼叫功能。 conference_outcall()函数调用switch_ivr_originate()创建呼叫连接,得到新的session。调用switch_caller_extension_add_application()添加“conference”扩展到一个扩展变量上,将这个变量绑定到session->channel上,设置channel状态为CS_EXECUTE状态。到此,session的状态线程会执行channel上的应用,即会调用conferenceAPP接口,进入会员添加和运行的流程。 4.4.3 mod_voicemail模块 mod_voicemail实现语音信箱功能,因王洪永已经分析过此模块,所以请参考他的分析文档。 4.4.4 mod_commands 大部分API在模块mod_commands中实现,这些API命令可以通过mod_event_socket模块远程调用。 (1)core commands · acl 在ACL列表里查找IP 如: acl 。 · alias 设置api别名。 · bgapi 在一个线程里执行API命令,用法:bgapi [ ] 源码分析:启动一个线程bgapi_exec()执行API命令 · break 中断一个连接,格式:break [all] · complete 分析源码:只是在数据库complete表添加或删除一个“字符串”记录 · cond 测试一个条件并返回一个结果用法: cond  ?  : · domain_exists 检查域名是否存在 · eval 在一个通道上执行指令,用法: eval [uuid: ] · expand 执行带扩展变量的API,用法: expand originate sofia/internal/1001%${domain} 9999  变量${domain}如果值是192.168.0.1那么命令执行时是: expand originate sofia/internal/1001%192.168.0.1 9999 · fsctl freeswitch控制消息,用法:fsctl [send_sighup|hupall|pause|resume|shutdown [cancel|elegant|asap|restart]|sps|sync_clock|reclaim_mem|max_sessions|min_dtmf_duration [num]|max_dtmf_duration [num]|default_dtmf_duration [num]|loglevel [level]] · global_getvar 获取全局变量,用法:global_getvar 如果变量名不存在将返回所有变量 · global_setvar 设置全局变量,用法:global_setvar = · host_lookup 将一个主机名解析成IP地址,用法:host_lookup · hupall 关闭一个指定的channel,用法:hupall [ ] 关闭已经设置了变量=通道。 = normal_clearing · in_group 判断用户是否在用户组里,用法:in_group [@] · is_lan_addr 判断是否是网络IP地址,用法:is_lan_addr · load 加载一个模块,用法:load · md5 生成一个MD5串,用法:md5 · module_exists 判断模块是否被加载,用法:module_exists · nat_map 添加/删除NAT映射,用法:nat_map [status|reinit|republish] | [add|del] [tcp|udp] [sticky] status 输出NAT类型,IP地址;reinit重新初始化NAT引擎;republish 重新发表映射;sticky永久性的映射 · reloadxml 重新加载conf/freeswitch.xml 配置文件 · show 输出各种报表信息,用法:show codec|endpoint|application|api|dialplan|file|timer|calls [count] |channels [count|like ]|distinct_channels|aliases|complete|chat|management|modules |nat_map|say|interfaces|interface_types|tasks ² codec - 列举编码 ² endpoint - 列举终端模块 ² application - 列举应用 ² api - 列举API ² dialplan - 列举拨号模块 ² file - 列举支持的文件格式 ² timer - 列举时间模块 ² calls - 列举通话数 ² channels - 列举通道数 ² distinct_channels - 列举通道 ² aliases - 列举别名 ² complete - 列举commplete数据库表记录 ² chat -  列举聊天模块 ² management - 列举管理模块 ² modules - 列举模块 ² nat_map - 列举NAT表 ² say - 列举文字发音模块和支持的语言 ² interfaces - 列举所有接口 ² interface_types - 列举所有接口类型 ² tasks - 列举任务 · shutdown 停止freeswitch,只能通过控制台输入此命令。 · status 输出当前状态 · strftime_tz 输出格式化时间 · unload 卸载一个模块 · version 输出版本 · xml_locate 显示当前freeswitch用的XML模块 · xml_wrap 用XML封装一个API命令 (2)Management Commands  · create_uuid  创建一个新的UUID并以字符串形式返回 · group_call 返回一个含有bridge命令的字符串,还包含有组成员。 · help 显示所有命令帮助 · originate 创建一个通话,用法:originate |&() [] [] [] [] []是SIP地址,扩展电话来源, &表示是应用程序,括号里是参数,可以被调用的应用有park, bridge, javascript/lua/perl, playback 等。如果有多个APP参数 要用单引号如:originate sofia/example/1000@somewhere.com '&javascript(test.js myArg1 myArg2)'举例:originate sofia/example/300@foo.com &conference(conf_uuid-TEST_CON),访问300@foo.com参加会议 · pause 启动或暂停媒体,用法: pasue   · regex 执行正则表达式匹配,返回true|false; · reload 重新加载一个模块 · reloadacl 重新加载ACL · uuid_audio 调整一个通道的音量或是缄默等。用法:uuid_audio [start [read|write] [mute|level ]|stop] ,level -4到4 默认是0 · uuid_bridge 桥接两个UUID,用法:uuid_bridge · uuid_broadcast 给一个UUID播放音频文件,用法:uuid_broadcast [aleg|bleg|both] · uuid_chat 发送聊天文字信息 · uuid_debug_audio 调试音频,用法:uuid_debug_audio · uuid_deflect 转发REFER方法给接收方。 · uuid_displace 给UUID播放音频文件,用法:uuid_displace [start|stop] [] [mux] uuid_displace 1a152be6-2359-11dc-8f1e-4d36f239dfb5 start /sounds/test.wav 60 播放60秒 · uuid_display 更新SIP终端的显示。 · uuid_dump dump一个session的所有变量。 · uuid_exists 检查UUID是否存在。 · uuid_flush_dtmf 清除DTMF队列 · uuid_getvar 从一个通道读取变量值 · uuid_hold 设置一个通道为hold状态 · uuid_kill 重设一个指定的通道 · uuid_media 重新邀请UUID桥接媒体 · uuid_park 泊车一个UUID · uuid_send_dtmf 发送DTMF 数据 · uuid_session_heartbeat  · uuid_setvar 在一个通道上设置变量 · uuid_setvar_multi 在一个通道上设置多个变量 · uuid_transfer 转移一个电话到指定的 · uuid_simplify 分析源码:实现类似应答REFER方法的功能。 (3)Record/Playback Commands  · uuid_record 录音,用法:uuid_record [start|stop] [] (4) Misc. Commands · bg_system 执行系统命令 · echo 在控制终端显示字符串 · find_user_xml 查找用户是否存在,用法:find_user_xml · sched_api 以任务调度的方式执行API命令 · sched_broadcast 以任务调度的方式给源或目标线路播放一个音频文件 · sched_del 以任务调度的方式删除一个组或一个任务。 · sched_hangup 以任务调度的方式挂断一个通话 · sched_transfer 以任务调度的方式转移一个电话到指定的 · stun 查找stun server 用法:stun [:port] stun stun.freeswitch.org · system 执行系统命令 · time_test 时间测试 · timer_test 定时器测试 · tone_detect 在一个通道上启动音频测试 · unsched_api 根据任务号删除一个任务 · url_decode 将URL解码成一个字符串 · url_encode 将URL编码成一个字符串 · user_data 读取中directory定义的用户信息 · user_exists 检查用法是否存在。 4.4.5 mod_dptools dialplan使用的APP命令由mod_dptools提供。下面APP命令列表。 · answer 应答一个通道。源码分析:调用switch_channel_perform_mark_answered()实现此功能 。 · att_xfer 线上转接。源码分析:调用switch_ivr_originate()创建一个向外的session,然后调用switch_ivr_multi_threaded_bridge()桥接两个session。 switch_ivr_originate()—>switch_core_session_outgoing_channel()调用终端(如sofia)的IO接口io_routines->outgoing_channel()。 switch_ivr_multi_threaded_bridge()—>audio_bridge_thread(),线程循环从一个通道读取数据,写到另一个通道。在这个函数中,如果宏定义启用视频线程,就会创建视频线程video_bridge_thread(),这个线程检查两个通道状态,并从一个通道读取视频数据,写到另一个通道上。 · bind_meta_app转接时设置绑定在一条线路上执行某个拨号计划的命令。此命令无效,因为终端直接通信而不发给FS。 · break 取消当前通道上的应用程序,dialplan执行下一个程序。代码实现:设置channel 为CF_BREAK状态。 · bridge 桥接两个终端。目标可以是SIP地址或终端。可以设置多个目标、超时等例如: 超时 终端直接相连 铃声 多个目标 或者 一个组 或者 路由。 代码实现过程与att_xfer命令实现类似。都是调用switch_ivr_originate()再调用switch_ivr_multi_threaded_bridge()。 · check_acl dialplan检查IP是否符合ACL规则 如:。 · clear_speech_cache 清空语音缓存。 · conference 建立多方会议。参考mod_conference模块。 · conferece_set_auto_call 建立自动呼叫列表,参考mod_conference模块。 · db 执行数据库的insert|delete操作,是模块mod_limit注册的APP接口。 · deflect 在建立连接时转发SIP协议的REFER方法,REFER指示接收方通过第三方途径建立SIP连接。如:参数只能是SIP地址。源码分析:设置msg->id=SWITCH_MESSAGE_INDICATE_DEFLECT,调用switch_core_session_receive_message()àmod_sofia的io_routines->receive_message()。最后挂断当前连接。 · delay_echo 指定回音延迟。延迟单位毫秒。 · detect_speech 在一个通道上语音识别。通过mod_event_socket 模块的sendmsg调用detect_speech。 · dispalce_session 在一个会话上替换音频文件。 · eavesdrop 窃听,在dilaplan的配置文件中设置侦听,也可以通过mod_event_socket 发送api...侦听指定的某个终端,也可以侦听一个组。 · echo 简单的将任何数据回发给源,主要用来检查通话延时。 · enable_heartbeat 启动音频心跳检查,源码分析:加入到任务队列里。任务队列会定时调用任务callback函数。 · endless_playback 循环播放文件。 · event 发送一个事件,将事件加载到系统的事件队列中。 · execute_extension 执行拨号扩展。。 · export 桥接时,从A线路赋值变量给B线路。 · fax_detect 在dialplan中,如果目标是传真机执行扩展(传真的设置)。 · flush_dtmf 清空channel的dtmf队列。 · gentones 创建一个电话的嘟嘟声如800HZ,500毫秒的嘟嘟声。 · group 添加或删除一个呼叫组。可以参考conf/dialplan/default.xml中有关于group的例子。 · hungup  挂断channel,并回复一个挂断原因,如:USER_BUSY。 · hold 发送一个hold消息。 · info  在控制台上显示所有通道信息。 · intercept 拦截通话,将自己的线路桥接到A或B线路上。通过mod_event_socket的sendmsg 执行拦截。 · ivr 设置ivr菜单。详细看ivr.conf配置。 · log 设置在控制台上显示日志。 · mkdir 创建目录。 · park 等待通电话。 · park_state 设置通道为为CS_PARK状态。 · phrase 朗读文字。配置文件中在lang/en/*.xml。设置不同的配置信息。在dialplan中可以设置 “spell” 是*.xml文件中定义的宏标签名称。 · play_ang_get_digits 播放并获得访问者输入的数字。源码分析:播放提示音文件,读取通道数据。 · play_back 给创始人播放音频文件。 · pre_answer 在创建媒体链路前作一个应答。由于建立媒体链路时间可能会很长。 · presence 发送一个用户出席消息。 · privacy 设置免打扰。 · queue_dtmf 当桥接被建立后,传送数字字符串。 · read 读取按键数据。 · record 录音。 · record_session 录音通话。 · redirect  重定向一个通道,只有通道没有被应答时才可以被重定向,如果已经应答,用deflect。 · regx 执行正则表达式。 · remove_bugs 在一个通道上移除所有媒体bug · respond 发送一个SIP回应码。 · ring_ready 发送响铃给通话创建者,发送的是SIP 的180 Ringing。 · say  播放事先录好的文件或读日期、时间、IP地址、文本数字等。 · sched_broadcast 以任务方式广播音频数据。 · sched_hangup 以任务方式挂断通话。 · sched_transfer 以任务方式转到一个新的拨号计划上。 · sched_display 以任务方式给终端发送SIP提示信息包。 · send_dtmf 在session上发送数字DTMF,起到延时作用。 · set 执行APP前,在channel上设置APP的变量和变量值。 · set_audio_level 调整输入|输出音量大小。 · set_global 设置全局变量。 · set_name设置通道的名称。 · set_profile_var给channel->caller_profile结构各个字段赋值,这个结构是访问者的信息。 · set_user设置通道上已经认证过的用户信息。 · sleep 休眠channel,时间单位是毫秒 · socket 向外建立一条连接。是mod_event_sokcet 提供的APP接口。 · sound_test 音频分析。 · speak 朗读文字。用flite TTS 发声引擎。 · soft_hold 设置一个桥接通道为挂起状态。 · start_dtmf 启动一个带内DTMF探测。 · stop_dtmf 停止带内DTMf探测。 · start_dtmf_generate 在通道上生成DTMF。 · stop_displace_session 在一个session上停止替换文件。 · stop_dtmf_generate 停止生成DTMF · stop_record_session 停止session录音 · stop_tone_detect停止音频探测 · strftime 按输入格式显示当前时间 · system 执行系统调用 · three_way 第三方强行加入一个通话中。mod_event_socket的sendmsg可以调用此命令。 · tone_detect 探测音频并执行设定的应用 · transfer立即转到设定的拨号计划 · unbind_meta_app 解除bind_meta_app设置的绑定 · unset 清除一个channel变量 · unhold 发送一个un_hold 消息 · verbose_events 设置是否发送所有所有事件给订阅者 · valet_park 完成电话转线功能。看一个完整例子        目标号码是8500         执行自动进入号码是8501到8599中的任一个房间等待     此时房间号码会语音方式通知转线员,转线员会通知收听人拨号85xx接听电话。                    · wait_for_silence 暂停dialplan执行,沉默等待一定时长。 · javascript 是mod_spidermokey注册的APP函数是js_dp_function(),APP命令是javascript,完成的功能是在一个通道上执行javascript脚本。此接口会被dialplan调用 4.5 自动语音识别/语音合成 此模块没有分析。 4.6 编码(Codecs) 此模块没有分析。 4.7 拨号计划(Dialplan) dialplan提供了四个子模块mod_dialplan_asterisk、mod_dialplan_directory、mod_dialplan_xml和mod_yaml。本文将描述FS默认采用的mod_dialplan_xml模块。 一、mod_dialplan_xml介绍 1、描述 freeswitch默认加载mod_dialplan_xml模块,即配置文件是采用XML文件格式。XML文件格式非常灵活,而且可以用第三方软件编辑XML文件,而且也可以手工编辑。因为XML格式非常简单,这也是freeswitch采用mod_dialplan_xml为默认模块的原因。在配置文件中,采用正则表达式匹配字段。本文的重点是分析配置文件的格式和API/APP接口。 2、XML文件格式分析     XML dialplan 本质上是被用于呼叫路由,当一个session需要路由时,会解析dialplan,将要执行的应用程序按顺序保存在switch_caller_extension结构链表里,然后再顺序的执行应用程序。配置文件被放在conf/dialplan/目录下,里面有default|public目录,default下可以存放多个以数字开头的xml文件,如:01_example.com.xml。这些文件按数字顺序被加载。如果你想扩展拨号计划,而且想第一个被加载,可以将文件命名为"00_xxx.xml"。     文件包含5个元素,context|extension|condition|action|anti-action。格式如下:                                                                                                                                                  (1)condition filed 包含以下关键字: · rdnis 重定向号码 · destination_number 被呼叫号码 · dialplan  拨号计划模块的名称 例如:"XML"就是代表mod_dialplan_xml · caller_id_name  访问者名称 · caller_id_number 访问者电话号码 · ani 自动号码识别 · aniii 呼叫设备类型 · uuid 当前呼叫唯一标识 · source 接收呼叫的模块名称如:sofia · chan_name 当前通道的名称 · network_addr VOIP 的IP地址 · year 公历年 · yday 一年包含的天数 · mon 月份(1-12) · mday 一个月包含的天数 · week 星期 · mweek 一个月包含星期数(1-6) · wday 一个星期包含的天数 · hour 小时(0-23) · minute 分钟 · minute-of-day  一天包含的分钟(1-1440)     filed 还可以设置成api${api(args)}函数或变量${variable}。     cond api:          variables:      (2)action application     可以是API/APP,其中以inline方式执行的只有下面这些接口。     check_acl|eval|event|export|log|presence|set|set_global|set_profile_var|set_user|sleep|     unset|verbose_events|cidlookup|curl|easyroute|easyroute|enum|lcr|nibblebill|odbc_query     例如:     这些命令会在mod_dptools分析。 (3)完整用例      上下文名称是“default”       扩展名称是“demo”           目标号码是任意7个数字(这里采用正则表达式)           SIP路由访问变量是$1的号码           不匹配的号码,播放提示音                    二、源码分析     模块被加载后,只注册了一个拨号接口,dialplan_hunt()。这个接口,会在路由状态下被调用,解析拨号计划内容,解析有action则根据是否是inline选择执行应用或是保存应用接口的信息。     执行应用接口:是调用switch_core_session_exec()直接执行。     保存应用信息:是将应用信息,保存在switch_caller_extension结构链表。最后dialplan_hunt()会返回这个链表。当switch_core_session_run()线程——每一个CALL连接对应一个线程。监测到CS_ROUTING状态时会调用switch_core_standard_on_routing(),这个函数会调用注册的函数dialplan_hunt()。 4.8 LDAP目录(Directories) 提供访问LDAP服务器功能。此模块没有分析。 4.9 终端(Endpoints) 终端提供了多种类型终端,包括mod_dingaling(googleTtal模块)、 mod_h323(H323协议)、mod_iax (asterisk模块)、mod_khomp、mod_loopback 、mod_opal、mod_openzap(T1/E1卡的电话驱动封装库)、mod_portaudio、mod_skinny、mod_skypopen(Skype模块)、mod_sofia(SIP模块)、mod_opal。 模块根据项目的需要,重点描述mod_sofia。 4.9.1 mod_sofia 一、mod_sofia功能 1、描述     mod_sofia是freeswitch 使用的SIP协议栈,处理SIP协议。在配置文件中很多地方都可以看到sofia/...,在分析案例时我们会介绍怎样在dialplan中调用它。此模块既可以作为UAC使用,又可以作为UAS使用。Freeswitch启动时会读取配置文件conf/autoload_configs/sofia.conf.xml。文件中会用 “X-PRE-PROCESS”嵌入conf/sip_profiles/*.xml 多个配置文件。每一个配置文件以文件名区分,完成不同的配置。在dialplan配置文件中设置可以实现一台服务器上运行多个UAS。设置多条语句加载不同的配置文件实现多个UAS,需要注意的是,不同的配置文件设置的端口要区分开,默认的端口是5060。     关于多个UAS,举例说明:在办公室内部网络中,通常有防火墙。如果外拨电话号码时需要连接STUN server穿透防火墙,连接外部的PSTN网络。如果呼叫内部的SIP地址,则连接UAS。此时就需要两个配置文件,一个是配置stun server,一个是uas。然后在dialplan中设置分别根据目标采有不同的拨号方案。格式:sofia/profile_name/destination,“profile_name”是配置文件名,destination是目标号码。 2、配置文件 配置项很多这里只简单地列出主要的节。 · Gateway设置网关 具有多个属性:name,username,realm,password,register等等 · Dialplan 设置读取dialplan数据的来源 如: 设置从mod_xml_curl读取数据。 · media-option 媒体参数 · STUN 服务器设置 · User Agent 设置SIP消息头信息 · NATing [apply-nat-acl, aggressive-nat-detection] NAT功能 启用ACL检查 开启NAT · VAD and CNG 发言监测 · NDLB · TLS设置 SSL · Other 其他类型(Other)的配置项很多,比如可以在配置文件里设置绑定ACL控制表、绑定静态IP等。在分析案例时,根据需要再详细介绍各种属性。 3、Sofia控制台命令     可以通过控制台命令设置mod_sofia的功能。如添加网关、删除网关。>sofia profile external rescan reloadxml此命令是在conf/sip_profile/external目录下配置文件。在此目录下可以添加网关的设置。 sofia profile flush_inbound_reg      清除终端                              [|]  重载配置文件                              killgw      删除网关                              siptrace on|off       启动/停止调试信息          status                     查看状态信息       tracelevel                 调试输出信息级别      loglevel                   日志等级                           4、事件 可以通过mod_event_socket订阅下面的事件: · sofia::register · sofia::unregister · sofia::expire · sofia::gateway_state · sofia::notify_refer  二、源码分析 1、mod_sofia_load 系统加载模块时会调用此函数,它主要完成以下工作: l 初始化mod_sofia_globals变量; l config_sofia()读取配置文件初始化sip协议栈参数; l 注册事件:     SWITCH_EVENT_CUSTOM——event_handler();     SWITCH_EVENT_PRESENCE_IN | SWITCH_EVENT_PRESENCE_OUT | SWITCH_EVENT_ROSTER | SWITCH_EVENT_PRESENCE_PROBE —— sofia_presence_event_handler();     SWITCH_EVENT_MESSAGE_WAITING —— sofia_presence_mwi_event_handler();     SWITCH_EVENT_TRAP | SWITCH_EVENT_NOTIFY | SWITCH_EVENT_SEND_MESSAGE | SWITCH_EVENT_SEND_INFO —— general_event_handler(); l 设置IO控制接口、状态控制接口;通过函数指针实现注册。IO接口包括:呼叫,读/写音频,读/写视频,消息接收等。呼叫接口outgoing_channel ()最终会被mod_conference向外呼叫时调用。状态控制接口包括:on_init()等。 l 注册API 接口 sofia_function() | sofia_gateway_data_function()| sofia_contact_function()|sip_dig_function() | sofia_presence_chat_send(); 2、 启动流程和功能     config_sofia()完成主要功能。读取配置文件,启动模块的主线程等,完成工作如下: · 订阅sip协议栈的日志到本地的logger()函数; · 读取配置文件; · 创建线程sofia_profile_thread_run(); 注册函数sofia_event_callback()SIP协议事件的接收接口; · 创建sofia_profile_worker_thread_run()循环读取sql_queue队列,然后执行数据库相关操作。 · sofia_event_callback() 接收sip协议栈的状态消息。并处理sip协议信令。如nua_i_invite是来访状态,sofia_handle_sip_i_invite()函数完成这种状态处理功能。 检查ACL控制表,创建session等,最后启动状态检测线程switch_core_session_run()检测channel状态,根据状态值调用注册的状态控制接口。对数据库的操作是调用sofia_glue_actually_execute_sql()完成。 · 事件处理函数 event_handler()执行SQL语句做注册等相关信息的,并调用sofia_presence_event_handler()或sofia_presence_mwi_event_handler(); general_event_handler()设置sip的属性并发送消息等; sofia_presence_event_handler()将事件加入mod_sofia_globals.presence_queue队列,并检查启动sofia_presence_event_thread_run 线程;线程从presence_queue和mwi_queue队列读取消息,并进行相关数据库操作; sofia_presence_mwi_event_handler()将事件加入mod_sofia_globals.mwi_queue队列,并创建线程sofia_presence_event_thread_run; 3、API接口 · sofia_function() 这个API接口实现了sofia 控制台命令。具体命令参考第一部分第3段 · sofia_gateway_data_function() 输出网关信息,格式: [ivar|ovar|var]  ivar是inbound,over 是outbound,var是全部。 · sofia_contact_function() 查询用户信息。格式:[profile/]@ · sip_dig_function()是SIP消息的URI解析器。 · sofia_presence_chat_send()聊天室接口,mod_conference模块的chat_send()会最终调用这个接口。  4.9.2 mod_openzap 此模块是一个板块驱动的封装库,对外提供统一的调用接口。支持支持多种板卡如: Sangoma、 DAHDI和、PIKA。首先必须安装实际硬件的驱动,设置相关的配置文件。模块在驱动和freeswitch的通道之间起到桥梁的作用。下图是摘自wiki,描述出mod_openzap模块和驱动之间的关系。在以后的测试工作中详细分析此模块。 4.10事件处理(Event Handlers) 事件处理也提供了多种方式,包括mod_cdr、mod_cdr_csv 、mod_erlang_event 、mod_event_multicast 、mod_event socket、mod_event_test、mod_radius_cdr、mod_xml_cdr、mod_xmpp_event、mod_zeroconf。 其中最mod_event_socket是我们分析的重点。 一、 mod_event_socket 功能 1、 描述 mod_event_socket以socket的形式,对外提供控制FS一种途径,缺省的IP是127.0.0.1,TCP端口是8021。可以在外部通过sokcet执行API/APP命令。配置文件是conf/autoload_configs/modules.conf.xml,连接分两种模式: inbound/outboundmod_event_socket 的默认加载模式是inbound,outbound模式需要在dialplan的配置文件中设置。mod_event_socktet的配置文件是conf/autoload_configs/event_socket.conf.xml。在配置文件中可以设置listen-ip;listen-port;password等。 2、 ACL控制表 设置连接的访问控制列表,,可以在acl_conf.xml设置,也可以在event_socket.conf.xml中设置IP范围。下面例子是event_socket.conf.xml中启动ACL: acl表的在listener_run()线程中被使用,检查连接IP等。 3、 命令解析 对socket中读取的数据进行解析(parse_command()函数完成解析功能)。 · unload · reload · api(阻塞模式) api 例如: api originate sofia/mydomain.com/ext@yourvsp.com 1000   # connect sip:ext@yourvsp.com to extension 1000 api sleep 5000 · bapi(非阻塞模式) bgapi   api和bapi 只是执行方式不同,都是执行API命令。API命令很多见第三部分“命令列表” · event 启动或禁止接收任意类型的事件。例如:  event plain ALL  event plain CUSTOM conference::maintenance  event plain CHANNEL_CREATE CHANNEL_DESTROY CUSTOM conference::maintenance sofia::register sofia::expire · myevents 接收uuid的所有消息 · divert_event 脚本注册测接收事件的函数分转到event socket上 · filter 设置event socket 接收事件类型 · sendevent 发送一个事件到系统队列中 · sendmsg 给一个uuid发送一个消息,可以执行其他模块的应用接口,也可以挂断电话等。例如: SendMsg call-command: execute                  (命令execute/hangup/unicast/nomedia) execute-app-name: playback             (应用模块名称) execute-app-arg: /tmp/test.wav         (参数) · exit 退出 · auth 认证 · log 启动日志 · nolog 禁止日志 · nixevent 启动日志 · noevents 禁止事件 二、 源码分析 1、 整体流程 mod_event_socket_load():注册SWITCH_EVENT_ALL事件接收端口函数event_handler(),创建APP(socket_function)/API(event_sink_function)接口。加载完成后,系统会自动为runtime函数创建线程mod_event_socket_runtime();线程内部接收连接,并初始listener结构,为listener创建线程listener_run();下图是事件处理的架构图: 2、 APP接口 socket_function 是为FS提供OutBound模式提供的一个APP接口。 3、 API接口 event_sink_function是为FS提供的以http协议方式控制FS的接口。 4、 解析数据流程 执行其他模块已经注册的API/APP接口。流程如下:listener_run()线程调用read_packet()读取数据包,再调用 parse_command()解析命令。 l api:调用api_exec()执行命令; l bgapi:创建api_exec()线程执行命令; l sendevent:将事件发送到事件队列上; l sendmsg:如果参数是execute则调用switch_ivr_parse_event()解析事件并执行APP命令。如果需要异步执行,则将命令和参数加入到session的私有队列里。等待以后调用。 5、 事件处理 mod_event_load()注册接收所有事件接口event_handler()。event_handler()接收到的信息分发到连接对象listener的队列event_queue上。read_packet()函数会读取队列event_queue的事件,发送到socket。 6、 runtime线程 主线程是mod_event_socket_runtime();每一个连接创建一个子线程listener_run(),他们之间的独立运行,当mod_event_socket_shutdown()被调用后,变量prefs.done=1;则主线程和子线程,检查到prefs.done==1退出。 4.11文件格式(File Formats) 此模块没有分析。 4.12语言(Languages) 提供高级语言的调用接口,现在支持java、lula、.net、 perl、phyon、javascript,不支持的语言有php和ruby。 4.13日志(Loggers) 日志部分主要是提供日志的存储,包括mod_logfile、mod_syslog、 mod_console。这些模块调用switch_log_bind_logger()注册日志接收函数。然后不同的模块那指定的格式写如文件或输出到控制台。要提醒的是mod_event_socket等模块也注册了日志的接收函数,并在日志接收函数内,将日志发送到网络上。 4.14定时器(Timers) 在核心库中实现了定时器的功能。此模块并不是硬件定时器,只是一个软定时器。所以精度不是很高。 4.15 XML(xml interface) 以HTTP协议形式,对外提供控制FS行为和查询FS配置状态信息接口。 4.16核心库(core libary) 核心库实现的功能很多,本文之分析了重要的几个一部分,包括:事件、任务调度、日志、SQLDB、通道状态监测。很多关键的功能都是异步方式实现的,所采用的技术主要是队列。因此会在后面经常提到,队列最常用的方法就是入队push和pop出队。 4.16.1事件 事件可以说是核心库里比较重要的部分,它提供了事件信息传递功能,对于了解FS的运行过程很有帮助。不同的模块可以订阅自己感兴趣的事件类型。一个事件的生命周期是事件创建、事件入队、事件分发和事件投递。本文将从这几方面描述事件。 事件初始化,主要完成事件队列的创建和事件线程的创建。创建三个事件队列(0-2优先级)和三个事件处理线程;创建20个分发队列和分发队列线程。 事件创建,调用switch_event_create()创建事件。 事件加入到队列,调用switch_event_fire()函数将事件加入到事件队列。事件线程会将事件加入到分发队列上。 事件的投递,分发线程会获取分发队列上的事件,并调用事件对应的callback函数。即注册的事件接收函数。事件的注册调用switch_event_bind_removable()。 4.16.2任务调度 任务调度采用异步方式实现。freeswitch在执行初始化时,调用switch_core_init()完成初始化。这个函数调用switch_scheduler_task_thread_start()启动任务队列线程switch_scheduler_task_thread。线程循环调用task_thread_loop()循环的条件是,globals.task_thread_running == 1(glaobals变量是局部变量)。 task_thread_loop()循环检查globals.task_list,并判断节点是否满足运行时间的要求,如果满足判断节点是否启用线程,如果是创建线程task_own_thread(),在线程内部调用switch_scheduler_execute()执行节点上的函数。否则,直接调用switch_scheduler_execute()。 添加任务switch_scheduler_add_task()此函数会在globals.task_list的尾部,并会分配一个ID号。 删除任务switch_scheduler_del_task_id()此函数按ID查找,并删除此节点。 停止任务switch_scheduler_task_thread_stop()调用此函数释放globals。 4.16.3日志 日志的实现也是异步方式实现的。freeswitch在执行初始化时,调用switch_core_init()这个函数调用switch_log_init ()。这个函数创建队列,启动log_thread线程,线程会读取队列,并调用日志注册的接收函数。 4.16.4 SQLDB SQLDB的实现也是异步方式实现的。freeswitch在执行初始化时,调用switch_core_init()这个函数调用switch_core_sqldb_startt ()。这个函数创建队列,启动switch_core_sql_thread线程,线程会读取队列,并执行SQL语句。 4.16.5 session状态监测线程 通道状态监测线程,是FS的核心功能。每一个呼入和呼出的通道FS都会为其创建一个状态监测线程switch_core_session_run()。这部分功能在电话会议模块中已经分析过。这里不再复述。 4.16.6 其它 FS还有其它功能,这些功能王洪永已经做过分析,这里不再复述。

下载文档到电脑,查找使用更方便

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享文档获得金币 ] 3 人已下载

下载文档