------------------------------------------------------------------------------------------------------------------------------------------------------------------
asterisk
------------------------------------------------------------------------------------------------------------------------------------------------------------------
Asterisk是一个开源代码的软件VOIP PBX系统,它是一个运行在Linux环境下的实施方案。
Asterisk是一种功能非常齐全的应用程序,提供了许多电信功能,能够把你的X86机器变成你自己的交换机,还能够作一台企业级的商用交换机。
Asterisk能够支持传统的线路包括:
TDM (Time Division Multiplexing)
T1/E1 PRI / PRA & RBS (Robbed Bit Signal) modes
Analog phone lines/phones (POTS)
IDSN (Integrated Services Digital Network)
Both BRI (Basic Rate) and PRI (Primary Rate)
Asterisk支持的协议包括:
Session Initiation Protocol (SIP)
H.323 (ITU standard, contributed support)
Inter-Asterisk eXchange (IAX)
Media Gateway Control Protocol (MGCP)
与硬件VOIP比较
Asterisk具有硬件VOIP的常用功能
Asterisk能够支持多媒体,具有可编程功能
Asterisk有友好的管理界面
Asterisk需要的宽带,一般为:32KB/线路。也就是说每支持一条线路,只需要增32KB的带宽,但是需要网络质量良好
Asterisk可支持成千的客户端。(需要板卡与带宽支持)
brian@ubuntu:/usr/src/asterisk-1.8.12.0$ sudo asterisk -r
Connected to Asterisk 1.8.12.0 currently running on ubuntu (pid = 18851)
ubuntu*CLI>
When you have saved the file, you will need to reload the logger by issuing the following
command from the shell:
$ asterisk -rx 'logger reload'
or from the Asterisk CLI:
*CLI> logger reload
*CLI> logger show channels
ding-desktop*CLI> core set debug 9
Core debug was 0 and is now 9
1. chan_sip.c 中的load_module是怎么被调用的?
每个模块文件中有个宏: AST_MODULE_INFO 用来设定 struct ast_module_info // 回调,module.c
main-->load_modules-->load_resourc_list-->load_resource-->start_resource: res = mod->info->load()
app_dial.c 中的load_module通过宏注册了,但 load_modules 在哪里知道要调用app ?
__attribute__((constructor)) // 其调用在main函数前(在全局类构造函数后)
__attribute__((unused)) // 可能不使用,避免编译器产生告警
app_dial.c 中 的宏 AST_MODULE_INFO_STANDARD,不但注册了load_modules回调函数,还通过__attribute__注册了构造函数!
构造函数--〉ast_module_register --〉将注册的信息保存在 embedded_module_list
main-->load_modules--〉将embedded_module_list 保存为 module_list 再处理---〉把需要加载的放到load_order--〉load_resource_list
2. static struct ao2_container *dialogs; 中的 struct astobj2 *astobj; 是如何确定的? // astobj2.c 中定义了ao2相关的结构
load_module: dialogs = ao2_t_container_alloc(HASH_DIALOG_SIZE, dialog_hash_cb, dialog_cmp_cb, "allocate dialogs");
sip_alloc : ao2_t_link(dialogs, p, "link pvt into dialogs table");
internal_ao2_link 中通过 dialog_hash_cb,得到 p->astobj = obj;
3. do_monitor 线程的启动时机?
chan_sip.c : load_module-->reload_config(加载配置sip.conf)/restart_monitor()-->do_monitor()
7. 主叫为SIP的流程
//call in
do_monitor-->ast_io_add/ast_io_wait // ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL)
sipsock_read-->ast_recvfrom/handle_request_do
handle_request_do-->parse_request/find_sip_method/find_call/handle_incoming
find_sip_method-->sip_methods[]
find_call--> pedanticsipchecking / sip_alloc
handle_incoming-->handle_request_invite(when SIP_INVITE)
handle_request_invite-->get_destination / sip_new / build_route / ast_pbx_start
sip_new--// from sip_pvt to ast_channel, and get &sip_tech
ast_pbx_start-->pbx_thread-->__ast_pbx_run-->ast_spawn_extension-->pbx_extension_helper-->pbx_find_extension /pbx_findapp/ pbx_exec
pbx_exec-->dial_exec // exten => _2.,1,Dial(SIP/${EXTEN},20,r)
// call out
dial_exec-->dial_exec_full---->while(ast_request/ast_call) / wait_for_answer / ast_bridge_call
while(可能有多个被叫)
ast_request--> sip_request_call(chan->tech->requester)--> sip_new / restart_monitor // 建立被叫通道并监听
ast_call--> sip_call (chan->tech->call)-->transmit_invite // 给被叫(发送invite消息)
wait_for_answer-->wait_for_answer [1193] -- SIP/90002-00000003 answered SIP/90001-00000002
ast_bridge_call ----> ast_raw_answer(sip_answer)/ ast_channel_set_linkgroup /
for(ast_channel_bridge--->for(bridge_play_sounds / sip_bridge / ast_generic_bridge-->for(ast_waitfor_n / ast_read / ast_write)))
// other msg
BYE等消息--》 handle_incoming
// 把桢写入具体通道的流程:
ast_write sip_write ast_rtp_instance_write ast_rtp_write(reload_config 中SIP默认的engine : res_rtp_asterisk.c)
ast_rtp_raw_write rtp_sendto ast_sendto sendto(socket.h)
//得到远端地址后保存在sip的rtp字段中
process_sdp ast_rtp_instance_set_remote_address
// sip呼叫中RTP的建立流程:
handle_request_invite check_user_fulldialog_initialize_rtp ast_rtp_instance_new—》instance->engine->new—》ast_rtp_new—》create_new_socket
桥接通道(Bridging channels)
下面为当asterisk往外拨号时的情景:
2)当被叫应答时,Asterisk开始桥接媒体流,这样使得呼入通道和呼出通道分别对应的电话之间可以互通媒体流,使得双方都可以听到通话语音。
3)某些情况下,被桥接的两个通道来自相同的类型的通道,例如被桥接的通道都是SIP通道,或都是DAHDI通道,且两个语音通道支持相同的编解码格式,那么此种情况下
被桥接的语音通道被称之为原生通道。原生通道中的通道驱动直接处理转发进出的媒体流,而不需要将媒体流音频帧发送到PBX,因此种情况下,不需要进行编解码转换。
其他情况下被桥接的媒体流都得经过pbx,因为通过pbx才能够进行编解码转换。
4)若呼入和呼入通道驱动皆为SIP通道时,该原生语音通道由称之为“外部原生语音通达”,意思是媒体流直接在呼入者终端对应的设备和呼出终端对应的设备之间传输
而不经过Asterisk,但是媒体的控制信号任然保留在Asterisk上。
植入通道(Masquerading channels)
有些情形下,一个通道可能植入到另一个通道中。这种情景在呼叫转移中比较常见,新的通道植入并且接管桥接的工作,旧的通道就成了僵通道而被挂起。
当通过queue, dial, fallowme 等app 桥接主被叫时,
双方接听后即转向ast_bridge_call, 此函数内部是一个无限循环,不断监听双侧通道上的事件,
循环内部首先调用 ast_channel_bridge,
对于第一次调用则产生link事件,然后进入另一个循环,
调用协议栈提供的brigdge 回调,如,sip_bridge ,检测挂机事件并发射unlink事件,
然后调用ast_generic_bridge 检测双方通道上的事件,
ast_generic_bridge 具体又调用ast_waitfor_n 轮询 fd, 实际过程为读取rtp 流(ast_read)
通过判断帧数据类型做不同动作,这里帧数据是处理过的rtp包,如 帧数据类型为 onhold则说明为保持动作,还有dtmf等,这里还处理了jitibuf 问题(抖动)。
4. 被叫时 :pbx_exec 怎么调用 dial_exec ?
app_dial.c 中 注册了 Dial dail_exec
pbx_exec 中 app->execute
10. 拨号规则的的load
static const char config[] = "extensions.conf";
pbx_config.c 中, load_module-->pbx_load_module-->pbx_load_config/pbx_load_users
9. 拨号规则
sip.conf : [2133] context=demo
extension : [general]... [demo] exten=>name, priority, application() // n(lable) 跳转
Goto(context, name, priority)
i,1,notExist()
t,1,timeout()
Dial(dest, timeout, flags, URI)
exten=>_NZX123, 1, Set(ABC=2133) // 变量
same=>n, Dial($(ABC))
same=>n, SsyDigist($(EXTEN:2)) // 拨打的号码
Exten => name, priority, application()
exten => _2.,1,Dial(SIP/${EXTEN},20,r)
sip show users 察看SIP用户端,友端对象信息。
ast_log(LOG_WARNING, "Asked to transmit frame type %s, while native formats is %s read/write = %s/%s\n",
ast_getformatname(&frame->subclass.format),
ast_getformatname_multiple(s1, sizeof(s1), ast->nativeformats),
ast_getformatname(&ast->readformat),
ast_getformatname(&ast->writeformat));
ast_log(LOG_NOTICE, "Call from '%s' (%s) to extension"
" '%s' rejected because extension not found in context '%s'.\n",
S_OR(p->username, p->peername), ast_sockaddr_stringify(&p->recv), decoded_exten, p->context);
[Jul 3 11:22:31] WARNING[2760]: chan_sip.c:6537 sip_write: Asked to transmit frame type gsm, while native formats is (g729) read/write = g729/g729
[Jul 3 11:24:28] NOTICE[2739]: chan_sip.c:25601 handle_request_subscribe: Received SIP subscribe for peer without mailbox: 2133
有人会问为什么不能用asterisk直接做?因为图象混合是十分消耗系统性能的。所以,应该分开两个软件来实现。市面上可以购买到这个图象算法。
CC:http://lihanwuhan.blog.163.com/blog/static/514325562008597425617/
7.Asterisk 10
通信平台Asterisk的最新版本Asterisk 10中,采用了被称为confbridge的新会议应用,支持视频会议。
从开源平台到能进行电话视频会议,是一个不小的进步,它将成为本年度IT业界最大的进步之一。
你在你的 分机属性里添加这个字段 媒体流的传输
canreivite=no 是不转发
canreivite=yes 是转发