当前位置:首页 >> 信息与通信 >>

freeswitch源码分析


总体来说,FreeSWITCH 是一个基于组件的架构,如下图:

FreeSWITCH 可以仅包括 FS Core 独立运行,外围各种不同种类的组件(module) 增强了 FreeSWITCH 的功能。 开发者可以使用 public API,遵循 FreeSWITCH 的接口标准,开发各种不同种类 的 module 来增强 FreeSWITCH 的功能。

r />分析(一)

什么是 FreeSWITCH
FreeSWITCH 是一个可扩展的开源跨平台的电话平台,支持音频、视频、文本或 任何其他形式的媒体使用的协议的路由与交互。它于 2006 年成立。FreeSWITCH 也提供一个稳定的技术平台,可供许多电话应用开发利用的免费工具。

FreeSWITCH 最初由 Anthony Minessale 在 Brian West 和 Michael Jerris 的协 助下设计和开发。这三人原先都是 asterisk 的开发者。这个项目的设计目标包 括模块化、 跨平台的支持,可扩展性和稳定性。今天,许多更多的开发者和使用者 都为 FreeSWITCH 在贡献力量。 FreeSWITCH 支持各种通信技术,如 Skype,SIP、H.323、GoogleTalk,因此它容易 与其他的开源 PBX 进行对接,如:sipXecs、Call Weaver、Bayonne、YATE 和 Asterisk。 FreeSWITCH 支持许多高级的 SIP 特性,如 presence、BLF、SLA 以及 TCP TLS 和 sRTP。它也可以作为一个透明代理(有媒体或无媒体),扮演 SBC 和 T.38 代理 的角色。 FreeSWITCH 既支持宽带、窄带编码。Voice channel 和 conference bridge 模块 可以支持 8k、16k、24k、32k 和 48k 不同的码率,而且这些不同码率的通道可以 进行 bridge。如果 G.729 编解码经过授权,FreeSWITCH 也是支持的。 FreeSWITCH 支持 Windows,Mac OS X ,Linux,BSD 和 Solaris 的 32 与 64 位平台。 FreeSWITCH 支持传真,无论是音频,还是 T.38,而且可以微微音频和 T.38 的网 关。 FreeSWITCH 的很多开发者,都是非常有经验的开发人员。他们同时也参与其他 开源软交换产品的开发,如:openSER, sipXecs, Asterisk 和 Call Weaver.

目录结构
├── configure ├── configure.in ├── Makefile.am ├── Makefile.in ├── modules.conf :需要编译的 module 列表 ├── patches/ :补丁包 │ ├── MODAPP-293.diff │ ├── mod_portaudio_snow_leopard.diff │ ├── sofia.diff

│ └── zrtp_bnlib_pic.diff ├── src/ │ ├── CMakeLists.txt │ ├── g711.c │ ├── include/ │ ├── inet_pton.c │ ├── Makefile.am │ ├── Makefile.in │ ├── mod/ │ ├── switch_apr.c │ ├── switch_buffer.c │ ├── switch.c :main 入口 │ ├── switch_caller.c │ ├── switch_channel.c │ ├── switch_config.c │ ├── switch_console.c │ ├── switch_core_asr.c │ ├── switch_core.c │ ├── switch_core_codec.c │ ├── switch_core_db.c │ ├── switch_core_directory.c │ ├── switch_core_event_hook.c │ ├── switch_core_file.c │ ├── switch_core_hash.c

│ ├── switch_core_io.c │ ├── switch_core_media_bug.c │ ├── switch_core_memory.c │ ├── switch_core_port_allocator.c │ ├── switch_core_rwlock.c │ ├── switch_core_session.c │ ├── switch_core_speech.c │ ├── switch_core_sqldb.c │ ├── switch_core_state_machine.c │ ├── switch_core_timer.c │ ├── switch_cpp.cpp │ ├── switch_dso.c │ ├── switch_event.c │ ├── switch_ivr_async.c │ ├── switch_ivr_bridge.c │ ├── switch_ivr.c │ ├── switch_ivr_menu.c │ ├── switch_ivr_originate.c │ ├── switch_ivr_play_say.c │ ├── switch_ivr_say.c │ ├── switch_loadable_module.c │ ├── switch_log.c │ ├── switch_mprintf.c │ ├── switch_nat.c

│ ├── switch_odbc.c │ ├── switch_pcm.c │ ├── switch_profile.c │ ├── switch_regex.c │ ├── switch_resample.c │ ├── switch_rtp.c │ ├── switch_scheduler.c │ ├── switch_stun.c │ ├── switch_swig.c │ ├── switch_swig.i │ ├── switch_time.c │ ├── switch_utils.c │ ├── switch_xml.c │ └── switch_xml_config.c ├── libs/

启动逻辑
FreeSwitch 主程序的入库函数在 src/switch.c 文件中。Main 函数启动: 1. 分析启动参数; 2. 为全局变量 SWITCH_GLOBAL_dirs 分配内存和赋值; 3. 创建 pid 文件; 4. 调用 switch_core.c 中的 switch_core_init_and_modload()函数,初始化 并加载所有的 module,内部实现逻辑如下: a) switch_core_init_and_modload

i. 首先调用 switch_core_init 1. 初始化全局变量 runtime 的相关参数; 2. 对 SWITCH_GLOBAL_dirs 指定的目录进行创建; 3. load_mime_types 加载 conf/mime.types 加载所有的 mime 类型; 4. 设置运行参数的默认值(注意:switch_find_local_ip 获取本地 IP 的时候 会访问外网); 5. 依次调用:switch_core_session_init、switch_console_init、 switch_event_init、switch_xml_init、switch_log_init 进行相关的初始化, 初始化的内容主要:hash、mutex。 6. 加载"switch.conf"配置文件; 7. switch_core_state_machine_init 对 state_machine 进行初始化; 8. switch_core_sqldb_start,启动数据库相关线程; 9. switch_scheduler_task_thread_start,启动任务调度模块 10. switch_rtp_init:初始化 rtp/zrtp 相关的 11. switch_scheduler_add_task( heartbeat_callback),将心跳任务加入任 务调度模块; ii. switch_loadable_module_init()[switch_loadable_module.c]加载各 module,freeswitch 为了后续高效的运行 module 中的各种接口,将所有的接口 都存放到了 hash 表中,每种接口建立了一个 hash 桶: 1. 初始化全局变量:loadable_modules 的各类 hash。 2. 加载集成 module:CORE_SOFTTIMER_MODULE、CORE_PCM_MODULE; a) switch_loadable_module_load_file 进行 module 加载 i. 找到 name##_module_interface 的全局变量进行加载(module 中宏 SWITCH_MODULE_DEFINITION 定义的); ii. 执行 module->load(会初始化参数 module_interface,是 SWITCH_MODULE_LOAD_FUNCTION 定义的函数的一个参数),load 中会增加各类接 口:API、CHAT、APP、DIALPLAN、CODEC b) switch_loadable_module_process

i. 遍历每个节点的 endpoint_interface、codec_interface、 dialplan_interface、timer_interface、application_interface、 api_interface、file_interface、speech_interface、asr_interface、 directory_interface、 chat_interface、 say_interface、 management_interface, 加载并存储到 hash。 c) 创建线程,执行 module->runtime() 3. 遍历 conf/modules.conf 加载所有配置的 module; 4. 遍历 conf/post_load_modules.conf 加载所有配置的 module; 5. 如果除集成 module 之外配置的模块为 0, 则从 SWITCH_GLOBAL_dirs.mod_dir 加载所有的 mod; 6. switch_loadable_module_runtime,为每个 module 的 runtime 入口创建一个 线程并运行 runtime;
分析(二)

事件处理模型
FreeSWITCH 内部处理事件的基本数据结构和逻辑如下图:

1. freeSWITCH 启动的时候,会创建 3 个 EVENT_QUEUE,用来保存相关 api、 dialplan 或呼叫触发的事件,队列长度为 100000,每个队列的优先级不一样, 平台根据事件的优先级将事件入队。注意:如果符合优先级要求的队列已经满了 或其他原因造成入队失败,可能将事件放入到低优先级的队列[c1] ; 2. 每个 EVENT_QUEUE 会伴随一个 event_thread 线程,该线程的作用就是将 EVENT_QUEUE 的事件转移到 EVENT_DISPATCH_QUEUE,队列长度为 5000。 FreeSWITCH 启动的时候会创建 1 个 EVENT_DISPATCH_QUEUE 队列,正常情况下, event_thread 会将事件放入到 EVENT_DISPATCH_QUEUE[0], 如果该队列已满或其 他原因造成入队失败,再系统会动态创建 EVENT_DISPATCH_QUEUE[1],[c2] 将 事件放入该队列,依次类推; 3. 每个 EVENT_DISPATCH_QUEUE 队列伴随一个 event_dispatch_thread,该线程 的作用就是将 EVENT_DISPATCH_QUEUE 的事件转移到对应的事件订约方队列 NODE_EVENT_QUEUE,每个订阅会自带一个 NODE_EVENT_QUEUE,长度为 100000.。

媒体处理方式

FreeSWITCH 有三种媒体处理方式,分别是:default、proxy、bypass,具体的 应用和对比参考下表: Proxy Default FreeSWITCH 是否有媒 Y 体经过 是否可以对媒体流进 Y 行编解码 是否需要终端语音编 N 解码协商一致 支持录音、DTMF 解析、Y 会议、编解码转换 (proxy_media=true) (bypass_media=true) Y N N Y N N Y N Bypass

[c1]带来了可能的事件时序错误的问题 [c2]带来了可能的事件时序错误的问题
分析(三)

esl
api/bgapi 调用逻辑
应用程序以普通方式对 api 的调用,是顺序、阻塞的,直到 api 执行完成才 返回。 应用程序以 bgapi 的方式调用,是异步的,mod_event_sockt 收到请求后,启动 执行线程,直接返回。 如下所示,是 api 普通方式调用的时序图:

Application 调用逻辑
应用程序调用 dialplan 相关的应用,都是异步模式,mod_event_socket 收 到请求之后,将请求放入到 session 的私有队列,接口直接返回。

获取事件
1. mod_event_socket 启动的时候会订阅 FreeSWITCH 的所有事件,产生一个 NODE_EVENT_QUEUE,mod_event_socket 会根据不同的客户端订阅,给不同的客 户端返回需要的事件; 2. 平台产生事件之后,会将事件放到 mod_event_socket 的 NODE_EVENT_QUEUE 队列,mod_event_socket 会遍历每个连接,将每个事件推送给订阅该事件的客 户端; 3. 客户端调用 esl 接口获取事件,esl 库首先根据 check_q 变量的设置,判断 是否优先从自己的 race_event 队列(调用 esl 相关接口 esl_send_recv 执行程 序的时候,为了获取相关 api 的返回值,esl 需要从服务端等待消息,此过程中 可能获取到了事件,则 esl 会将事件放入到 race_event 中)中获取,如果 race_event 没有,则首先从自己的缓存 packet_buf 中取数据处理,如果取到合 适的数据直接返回,否则调用 socket 函数 recv,按固定超时时间从 mod_event_socket 上获取事件,将获取到的内容放入到 packet_buf。

媒体应用处理
同步
典型 application/api:playback、play_and_get_digist、read、record、park 以上这些应用,内部会循环调用 switch_ivr_parse_all_events 处理所有的 dptool 的请求,直到放音、录音、unpark 等操作完成,因此不要在这些应用没 有执行完的时候,继续执行其他应用。 如:在上次 playback 未完成的时候,又进行一次 playback,这样会优先进行第 二次 playback,然后再进行第一次 playback,形成了递归;playback 和 record 未完成操作直接调用,操作也类似,下图描述了一个递归的调用(在放音的过程 中,调用 record,如果 record 不被打断,则放音永远无法放出声音):

异步/订阅
典型 application/api:uuid_record、record_session、start_dtmf。 以上这些应用, 会给媒体流的 read, 增加 hook (media_bug) 不会阻塞媒体流。 ,

典型场景
客户呼入
1) 客户呼入,mod_sofia 会受到 invite 消息; 2) Mod_sofia 获取一个 session,启动 session 处理线程(session 状态机); 3) Mod_sofia 收到状态改变消息,设置 session 未 INIT 状态,后转为 ROUTING 状态; 4) Session 状态机,根据规则找到对应的 dialplan 定义,session 状态变迁为 EXECUTE; 5) 状态机根据 dialplan 的定义,执行对应的 application。

平台外呼
1) 程序调用 esl 的接口发送外呼命令; 2) Mod_event_socket 收到命令之后,调用 mod_command 的 originate; 3) Originate 调用 switch_ivr 定义的函数,创建 session,启动 session 处理 线程(状态机),同时状态变迁未 INIT; 4) Mod_sofia 发起 invite 请求,同时设置 session 状态为 ROUTING;

5) 外部应答,session 状态机变迁未 CONSUME_MEDIA; 6) 程序通过 esl 接口,发送执行 application 命令,同时 session 的状态变迁 未 EXECUTE。

freeswitch 内核研究
2011-11-23 21:34:14| 分类: 默认分类 | 标签: |字号大中小 订阅

比较零散,先放这留个脚印,慢慢添加整理。 1. freeswitch 分机号都保存在 conf/directory 目录下 系统启动时加载分机信息到内存,当收到注册包时在 directory 目录下的 usr 被搜寻,搜 寻依据是注册请求的 to ,from 头域的域名为系统所在的域名, 分机配置文件的分级结构:

domain groups group usr

usr group groups domain

directory 目录下包含若干 xml 文件,可以每个用户一个 xml profile,系统启动时动态加 载, 除了通过文件方式配置用户外,可以通过 mod_xml_curl 模块 访问 web server, web server 在 访问数据库,实现大批量分机的添加。

可以在用户的配置文件中设置一些附加给此用户的变量。

directory 目录的内容加载后可以被系统的所有模块获取,这样减少数据冗余。

dialplan

全 局 变 量



$${default_areacode}"

访 问 , 通 道 变 量 用

${default_areacode}"访问

conf/var.xml 文件定义了系统的全局变量。

单个用户 的配置文件模板

<include> <user id="1000"> //id 代表用户名,认证时用户名。 <params> <param name="password" value="$${default_password}"/> </params> <variables> <variable name="toll_allow value="domestic,international,local"/> //此用户的

channel 会设置此变量,用户的权限 其她可设置的变量: accountcode :用户账户,会出现在 CDR 中。 user_context: 用户打电话时走 dialplan 中 context 名为 user_context 的值 effective_caller_id_name: 呼叫其他用户时显示给对方的用户名, (只有被叫在系统上 注册才有效) effective_caller_id_number :给对方显示的用户号, (只有被叫在系统上注册才有效) outbound_caller_id_name:通过 sip 中继外乎时给对方送的用户名 outbound_caller_id_number :通过 sip 中继外乎时给对方送的用户号码 callgroup :附加属性,

</variables> </user> </include>

一个分机默认配置包含: A username for SIP and for authorization A voicemail password A means of allowing/restricting dialling A means of handling caller ID being sent out Several arbitrary variables that can be used or ignored as needed

配置一个分机过程:

#>cd /usr/local/freeswitch/conf/directory/default #>cp 1000.xml 1030.xml Replace all occurrences of "1000" with "1030"

修改 default dialplan

重新加载配置文件使配置生效: reloadxml 控制台查看哪些分机主上已经注册: sofia status profile internal freeswitch 的 dialplan 单独一个目录, 分机的 conext 为 dialplan 目录下的 conext.xml 与外部链接: freeswitch 通过 sip 网关联系外部世界, freeswitch 此时在 sip server 来看是一个 user.

配置网关方式: 创建中继文件: conf/sip_profiles/external/test.xml <include> <gateway name="custom"> <param name="username" value="MY_USER_NAME"/> //sip provider 提供的用 户名及密码 <param name="password" value="MY_PASSWORD"/> <param name="realm" value="iptel.org"/> <!-- iptel.org requires a 'proxy' parameter --> <param name="proxy" value="sip.iptel.org"/> </gateway>--> </include> 使配置生效: cli 执行: sofia profile external restart reloadxml (此命令会把正在通话的分机挂掉,更安全的

方式是用 sofia profile external rescan reloadxml) cli 执行 sofia status 返回系统 sip 配置信息 主要分两类:1.网关(gateway) 2.本地注册用户(profile) mod_xml_curl: 此模块为与 Asterisk realtime 机制差不多,可以通过此模让 freeswitch 需要时动态访 问外部数据库或 Web Server.这样可以实现动态控制 freeswitch 核心。 比如 分机的添加可以通过在数据库配置,freeswitch 通过此模块来加载分机。 通过此模块可以绑定: 1 .dialplan <param name="gateway-url" value="http://localhost:8080"

bindings="Dialplan"/>

每次呼叫,系统都会先访问 8080 ..... 2. 配置文件

sofia.conf 文件:

global_settings 节点: 子节点: log-level tracelevel debug-presence debug-sla auto-restart rewrite-multicasted-fs-path to_host original_server_host original_hostname

profiles 节点: conf/sip_profiles/*.xml 默认有两个 internalx.ml external.xml

freeswitch 希,状态机。

高 性 能 技 术 特 性 : memory

pool , task

queue,

event

driven,multithread,hash,state Machine,内存池,多线程,任务队列,事件驱动,哈

内核启动流程: 两个函数 switch_core_init 负责核心的初始化 apr_initialize(), switch_core_session_init, switch_core_hash_init, switch_console_init, switch_event_init, switch_xml_init, switch_log_init, switch_core_state_machine_init switch_scheduler_task_thread_start switch_nat_late_init, switch_rtp_init, switch_loadable_module_init 各个模块的初始化。 呼叫流程:呼入 sip 协 议 栈 从 传 输 层 收 到 sip 消 息 , 最 终 转 到 SIPUA 层 , 进 入 sofia_event_callback->sofia_queue_message

sofia_msg_thread_start->sofia_msg_thread_run-> sofia_process_dispatch_event-----------------

->our_sofia_event_callback->sofia_handle_sip_i_invite->switch_core_session _request switch_core_session_thread_launch switch_core_session_thread_launch--->switch_ivr_originate() ->

外乎: fifo, conferrance,switch_ivr_originate enterprise_originate_thread-> switch_ivr_originate----switch_core_session_outgoing_channel---->sofia_out going_channel->switch_core_session_request->

freeswitch 内核几个概念:session,channle, tec_private,event, 核心通过一个有限状态机来管理呼叫过程中每个状态对应的回调。 一个呼叫进入系统后建立一条 channel,此 channle 属于一个 server 维护的 session, 核 心通过状态机来调度 session 以实现事件的流转。 一个典型的呼叫流程 uac1-uas, uas-uac2, uac1-bridge-uac2,呼叫进入系统,系统建 立 session 后启动 switch_core_session_thread_launch 线程不断监听 session 状态, 根据状态调用相应的回调,当然,这里的状态机是一个状态有限的,freeswitch 状态机分 以下状态: on_init, on_routing, on_execute, on_hangup, on_exchange_media on_soft_execute, on_consume_media, on_hibernate, on_reset, on_park, on_reporting, on_destroy。 实际呼叫过程为这些状态的流转。 uac1-uas: 呼入系统,系统建立 session:

sofia_handle_sip_i_invite->switch_core_session_request->switch_core_sessio n_thread_launch, 解 析 请 求 后 从 CS_NEW -> CS_INIT , 初 始 化 后 进 入 dialplan : CS_INIT -> CS_ROUTING, 状态机回调 switch_core_standard_on_routing 查找 dialplan 系统根据主叫,被叫的 sip 设置查找其对应的 dialplan,然后加载到内存, 呼 叫 状 态 转 换 : CS_ROUTING -> CS_EXECUT , 状 态 机 调 用 switch_core_standard_on_execute,然后按照规则一条一条执行刚加载的 dialplan, 这里即是可编程软交换的体现, 根据需求灵活控制提供给一个呼叫的服务。 当执行到 bridge action 时, 是让系统呼叫 uac2, 调用 switch_ivr_originate 外乎 uac2,创建 uac2 在服 务器端的 session,channel, 最后启动状态机线程 switch_core_session_thread_launch 进入状态机模式, CS_NEW -> CS_INIT-> CS_ROUTING-> CS_CONSUME_MEDIA, usc2 最终应答 200 ok,服务器 根据 200 ok sdp 消息体与 uac1 提供的 sdp 协商,成 功后发 200 ok 给 uac1,呼叫桥接成功。uac2 状态机转换 CS_CONSUME_MEDIA -> CS_EXCHANGE_MEDIA,媒体流桥接开始。

状态机流转节点:
/*! \enum switch_channel_state_t \brief Channel States (these are the

defaults, CS_SOFT_EXECUTE, CS_EXCHANGE_MEDIA, and CS_CONSUME_MEDIA are often overridden by specific apps) <pre> CS_NEW CS_INIT - Channel has been initilized. CS_ROUTING - Channel is newly created. - Channel is looking for

an extension to execute. CS_SOFT_EXECUTE - Channel is ready to execute from 3rd party control. CS_EXECUTE - Channel is executing it's dialplan.

CS_EXCHANGE_MEDIA - Channel is exchanging media with another channel. CS_PARK - Channel is accepting media awaiting commands. CS_CONSUME_MEDIA - Channel

is consuming all media and dropping it. CS_HIBERNATE - Channel is in a sleep state. CS_RESET - Channel is in a reset state. CS_HANGUP - Channel is flagged for

hangup and ready to end. CS_REPORTING - Channel is ready to collect call detail. CS_DESTROY </pre> */ - Channel is ready to be destroyed and out of the state machine


相关文章:
freeswitch的mod_dailplan_xml模块分析报告
freeswitch 默认是加载 mod_dialplan_xml,即配置文件是采用 XML 文件格式。XML ...(这里采用的正 二、源码分析模块被加载后,只注册了一个拨号接口,dialplan_hunt...
Centos7.2 安装freeswitch1.6(最新最完整最成功的教程)
获得 freeswitch 源码 cd /usr/local/src git clone -b v1.6 https://freeswitch.org/stash/scm/fs/freeswitch.git 14、源码下载完成 15、开始编译和安装,因...
WebRtc-Freeswitch 搭建视频通话_图文
Freeswitch 源码 git clone https://freeswitch.org/stash/scm/fs/freeswitch....同时可以发送文字 chat 流程分析 WebRTC 在 WebRTC 的实现过程中对于音视频数据...
SIP服务器调查报告
源码下载地址:http://files.freeswitch.org/ 参考资料:http://en.wikipedia....n=Main.Download 参考资料:http://en.wikipedia.org/wiki/Yate 四、性能分析 ...
全等三角形复习
喜欢此文档的还喜欢 freeswitch源码分析 20页 3下载券 【地理】2011年新版3年高... 19页 免费全​等​三​角​形​复​习...
基于Linux平台的SIP网络电话系统构建
speex speex-devel libedit-devel bison[1] (2)下载 freeswitch 通过 git 下载 freeswitch源代码到/usr/local/src/目录下, 如图所示,我下载的是 1.4 发行版...
bluebox使用
freeswitch 如 果你不熟悉的 FreeBSD 但你可以选择安装源代码,这使在/ usr ...证券从业资格考试投资分析考点识记 157份文档 2015国家公务员考试备战攻略 2015国...
更多相关标签:
freeswitch源码下载 | freeswitch权威指南 | 瘆人 | freeswitch下载 | freeswitch 源码 | freeswitch源代码下载 | freeswitch代码分析 | freeswitch 视频 分析 |