freeswitch源码分析


2023年12月15日发(作者:太太的拼音)

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

FreeSWITCH可以仅包括FS Core独立运行,外围各种不同种类的组件(module)增强了FreeSWITCH的功能。

开发者可以使用public API,遵循FreeSWITCH的接口标准,开发各种不同种类的module来增强FreeSWITCH的功能。

分析(一)

什么是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

├──

├──

├──

├── :需要编译的module列表

├── patches/ :补丁包

│ ├──

│ ├── mod_portaudio_snow_

│ ├──

│ └── zrtp_bnlib_

├── src/

│ ├──

│ ├── g711.c

│ ├── include/

│ ├── inet_pton.c

│ ├──

│ ├──

│ ├── 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_

│ ├── 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类型;

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. 加载""配置文件;

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/加载所有配置的module;

4. 遍历conf/post_load_加载所有配置的module;

5. 如果除集成module之外配置的模块为0,则从SWITCH_GLOBAL__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 Bypass

Default

(proxy_media=true) (bypass_media=true)

FreeSWITCH是否有媒Y Y N

体经过

是否可以对媒体流进Y N N

行编解码

是否需要终端语音编N Y Y

解码协商一致

支持录音、DTMF解析、Y N N

会议、编解码转换

[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/文件定义了系统的全局变量。

单个用户 的配置文件模板

//id 代表用户名,认证时用户名。

//此用户的

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 :附加属性,

一个分机默认配置包含:

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

Replace all occurrences of "1000" with "1030"

修改default dialplan

重新加载配置文件使配置生效:

reloadxml

控制台查看哪些分机主上已经注册:

sofia status profile internal

freeswitch的dialplan单独一个目录,分机的conext 为dialplan目录下的

与外部链接:

freeswitch通过 sip网关联系外部世界,freeswitch此时在sip server 来看是一个user.

配置网关方式:

创建中继文件:

conf/sip_profiles/external/

//sip provider提供的用户名及密码

-->

使配置生效:

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

每次呼叫,系统都会先访问8080

.....

2. 配置文件

文件:

global_settings 节点:

子节点:

log-level

tracelevel

debug-presence

debug-sla

auto-restart

rewrite-multicasted-fs-path

to_host

original_server_host

original_hostname

name="gateway-url" value="localhost:8080"

bindings="Dialplan"/>

profiles 节点: conf/sip_profiles/*.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_outgoing_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_session_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)

 CS_NEW       - Channel is newly created. 

CS_INIT - Channel has been initilized. CS_ROUTING - 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 - Channel is ready to be destroyed and out of the state machine

*/


本文发布于:2024-09-24 14:21:49,感谢您对本站的认可!

本文链接:https://www.17tex.com/fanyi/3400.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:事件   系统   调用   呼叫   状态   启动   媒体
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议