Asterisk所有功能都是基于函数调用的模式,呼叫流程也不例外。因此如何从一团乱麻似的内核函数调用中理出函数调用执行路线,是解读呼叫流程的关键。
所有呼叫都跟astersisk的channel有关。这路通话都包含一个incoming连接和一个outbound连接。每个电话都是通过对应 的channel程序建立起来的,比如Chan_sip,Chan_zap,Chan_iax2等等。每一类的channel,都拥有自己私有的 channel数据结构,例如chan_sip的struct sip_pvt结构,这些私有的结构从属于一个通用的Asterisk通道数据结构中,具体定义在channel.h的struct ast_channe中。
下图是asterisk 的呼叫流程图:
我们以sip的呼叫过程为例来描述,其他channel的呼叫过程基本类似。
Astersik下注册的sip用户主动发起一个呼叫的函数调用过程(incoming)如下:
do_monitor->sipsock_read->handle_request->handle_request_invite->sip_new/ast_pbx_start->pbx_thread->__ast_pbx_run
-> ast_spawn_extension ->pbx_extension_helper->pbx_exec->执行dialplan
当Chan_sip模块被加载时,会启动一个的监听线程do_monitor,不断侦听sip端口上的外部消息;
当sip用户拨叫被叫号码后,chan_sip的do_monitor调用sipsock_read函数,在sip端口收到invite消息,然后就调用handle_request和handle_request_invite进行处理。
在handle_request_invite中,首先解析invite消息,对该sip用户的业务属性分析,确认被叫可达,然后就调用sip_new申请channel资源,并调用ast_pbx_start函数启动一个pbx_thread线程来专门处理该呼叫。
pbx_thread线程调用__ast_pbx_run。
__ast_pbx_run是一个衔接dialplan和内核的关键函数,它首先调用ast_exists_extension函数,根据分机号码 的context属性,匹配到对应的dialplan;然后进入一个for死循环,逐条执行dialplan对应的context中的语句。
pbx_extension_helper函数调用pbx_extension_helper,在pbx_extension_helper中调用 pbx_find_extension找到对应的context后,通过verbose打印dialplan执行语句“Executing ……”,同时调用pbx_exec执行该dialplan。执行到dial语句呼叫被叫。
在等待被叫接通的过程中,完成媒体协商过程,向主叫发送180、200OK消息接通呼叫。
当其他用户呼叫asterisk的sip用户时,函数调用过程(outbound)如下: Dial->dial_exec->dial_exec_full->ast_request/ast_call/wait_for_answer/ ast_bridge_call
呼叫执行到dial时,pbx_exec调用application dial的接口函数dial_exec,dial_exec调用dial_exec_full。
在dial_exec_full中,首先调用ast_request,在ast_request调用chan_sip对应的回调函数 sip_request_call为该被叫sip用户申请channel资源。然后调用ast_call,在ast_call中调用chan_sip对应 的回调函数sip_call向被叫发送INVITE消息,呼叫被叫SIP用户。
然后该呼叫线程会调用wait_for_answer等待被叫接通。
在呼叫接通后,也即wait_for_answer函数返回,在dial_exec_full中调用ast_bridge_call桥接媒体,这样呼叫就正式接通了。
当chan_sip的侦听线程接收到BYE消息,则调用handle_request_bye找到相应的channel,执行hangup释放呼叫。
sipsock_read() 从socket 读取数据保存到req.data然后调用
handle_request_do(struct sip_request *req, struct ast_sockaddr *addr)
{
req->debug = 1;
req->len = lws2sws(req->data->str, req->len);
parse_request 解析sip 报文内容并且赋值给req。
p=find_call(req, addr, req->method) 根据call-id 如果能在dialogs找到相应的 pvt 则返回,如果找不到需要创建一个新的pvt link pvt into dialogs table。
copy_socket_data(&p->socket, &req->socket); socket 赋值
ast_sockaddr_copy(&p->recv, addr); 拷贝IP地址
queue_request(struct sip_pvt *p, const struct sip_request *req) 把req添加到p
handle_incoming()几乎所有的message都是在这一部分来处理的
}
handle_incoming()
{
case SIP_INVITE:
res = handle_request_invite(p, req, debug, seqno, addr, recount, e, nounlock);
}
handle_request_invite(p, req, debug, seqno, addr, recount, e, nounlock);
{
p->pendinginvite = seqno;
check_via(p, req);//检查via并对 p->sa赋值
{
if (maddr && ast_sockaddr_resolve_first(&p->sa, maddr, 0)) {
p->sa = p->recv;
}
ast_sockaddr_resolve_first(&tmp, c, 0);
port = ast_sockaddr_port(&tmp);
ast_sockaddr_set_port(&p->sa,
port != 0 ? port : STANDARD_SIP_PORT);
}
copy_request(&p->initreq, req); 保存invite
parse_ok_contact(p, req); 保存Contact: { if (!strncasecmp(content_type, "application/sdp", 15)) { req->sdp_start = 0; req->sdp_count = req->lines; return req->lines ? 1 : 0; } Process_sdp() { process_sdp_o() process sdp version o=- 30824820 30824860 IN IP4 135.252.33.1 process_sdp_c()读取 IP类型 和IP地址 } } } sipsock_read() 从socket 读取数据保存到req.data然后调用handle_request_do() res = ast_recvfrom(fd, readbuf, sizeof(readbuf) - 1, 0, &addr); 在handle_request_do() Handle incoming SIP message - request or response parse_request(req) req->method = find_sip_method(REQ_OFFSET_TO_STR(req, rlPart1)); 随后找到对应的sip_pvt结构,或者创建新的sip_pvt结构,结构指针返回到变量p中。 在全局链表dialogs 根据call-id 找相应的dialog,找到返回p,找不到创建新的dialog然后insert到dialogs中。 24192 p = find_call(req, addr, req->method); /* returns p locked */ 对p进行一系列的赋值 copy_socket_data(&p->socket, &req->socket); ast_sockaddr_copy(&p->recv, addr); 然后把p 和req 传给 handle_incoming(p, req, addr, &recount, &nounlock) p->method = req->method; static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct ast_sockaddr *addr, int *recount, const char *e, int *nounlock) p->pendinginvite = seqno; check_via(p, req); 拿到Via: SIP/2.0/UDP 135.252.33.1:7710 This is a new invite set_pvt_allowed_methods(p, req); res = check_user_full(p, req, SIP_INVITE, e, XMIT_RELIABLE, addr, &authpeer); 找到peer 2000 peer = find_peer(NULL, &p->recv, TRUE, FINDPEERS, FALSE, p->socket.type); dialog_initialize_rtp()创建一个新的rtp instance 并对其初始化 p->rtp = ast_rtp_instance_new(p->engine, sched, &bindaddr_tmp, NULL)) 对新创建的instance 进行初始化 ast_rtp *rtp 新建,并初始化然后 link 到 instance中 static int ast_rtp_new(struct ast_rtp_instance *instance, struct sched_context *sched, struct ast_sockaddr *addr, void *data) rtp->ssrc = ast_random(); rtp->seqno = ast_random() & 0xffff; rtp->strict_rtp_state = (strictrtp ? STRICT_RTP_LEARN : STRICT_RTP_OPEN); rtp->s =create_new_socket() ast_bind(rtp->s, addr) //addr 000 instance->data=rtp 在10000 到20000之间找到一个port x = (rtpend == rtpstart) ? rtpstart : (ast_random() % (rtpend - rtpstart)) + rtpstart; x = x & ~1; p->rtp->codecs= &peer->prefs RTCP config ast_rtp_prop_set() rtp->rtcp = ast_calloc(1, sizeof(*rtp->rtcp)) ast_rtp_instance_get_local_address(instance, &rtp->rtcp->us); ast_sockaddr_set_port(&rtp->rtcp->us, ast_sockaddr_port(&rtp->rtcp->us) + 1); rtp->rtcp->s = create_new_socket("RTCP",) ast_bind(rtp->rtcp->s, &rtp->rtcp->us) [Apr 11 17:00:51] DEBUG[12007] chan_sip.c: Body 0 [ 3]: v=0 [Apr 11 17:00:51] DEBUG[12007] chan_sip.c: Body 1 [ 43]: o=- 30824820 30824860 IN IP4 135.252.33.1 [Apr 11 17:00:51] DEBUG[12007] chan_sip.c: Body 2 [ 9]: s=eyeBeam [Apr 11 17:00:51] DEBUG[12007] chan_sip.c: Body 3 [ 23]: c=IN IP4 135.252.33.1 [Apr 11 17:00:51] DEBUG[12007] chan_sip.c: Body 4 [ 5]: t=0 0 [Apr 11 17:00:51] DEBUG[12007] chan_sip.c: Body 5 [ 26]: m=audio 7712 RTP/AVP 8 101 [Apr 11 17:0:51] DEBUG[12007] chan_sip.c: Body 6 [ 49]: a=alt:1 1 : 8F23B033 0000002B 135.252.33.1 7712 [Apr 11 17:00:51] DEBUG[12007] chan_sip.c: Body 7 [ 15]: a=fmtp:101 0-15 [Apr 11 0:51] DEBUG[12007]chan_sip.c: Body 8 [ 33]: a=rtpmap:101 telephone-event/8000 [Apr 11 17:00:51] DEBUG[12007] chan_sip.c: Body 9 [ 10]: a=sendrec Process SDP() 先解析m=之前的部分 nextm = get_sdp_iterate(&next, req, "m"); 然后解析m=之后的部分 nextm = get_sdp_iterate(&next, req, "m"); 在m=audio 7712 RTP/AVP 8 101中读取RTP audio format 8 101 然后 根据 8 和 101 设置 newaudiortp ast_rtp_codecs_payloads_set_m_type() 对a=rtpmap:101 telephone-event/8000... 进行process ast_rtp_codecs_payloads_set_rtpmap_type_rate Found audio description format telephone-event for ID 101 /* Setup audio address and port */ ast_sockaddr_set_port(sa, portno); ast_rtp_instance_set_remote_address(p->rtp, sa); chan_sip.c: Peer audio RTP is at port 135.252.33.1:7712 把codec 信息 从newaudiortp 拷贝到p->rtp ast_rtp_codecs_payloads_copy(&newaudiortp, ast_rtp_instance_get_codecs(p->rtp), p->rtp); instance->codecs ->payloads[i].asterisk_format = newaudiortp ->payloads[i].asterisk_format; instance->codecs ->payloads[i].code = newaudiortp ->payloads[i].code; gotdest = get_destination(p, NULL, &cc_recall_core_id); /* Get destination right away */ extract_uri(p, req); /* Get the Contact URI */ build_contact(p); /* Build our contact header */ p->owner=sip_new() 创建channel tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, linkedid, i->amaflags, "SIP/%s-%08x", my_name, ast_atomic_fetchadd_int((int *)&chan_idx, +1)); 初始化 ast_channel_cc_params_init(tmp, i->cc_params); 通道设置fd ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0)); ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1)); /* Save Record-Route for any later requests we make on this dialogue */ build_route(p, req, 0); transmit_provisional_response(p, "100 Trying", req, 0); p->jointcapability = ast_codec_choose(&p->prefs, p->jointcapability, 1); payload_type = {asterisk_format = 0, code = 1}, type = 0x577e7f "audio subtype = 0x58a378 "telephone-event", sample_rate = 8000}, found = 1; codecs->payloads[101] = t->payload_type; (gdb) p static_RTP_PT[8] = {asterisk_format = 1, code = 8} (gdb) p static_RTP_PT[101] = {asterisk_format = 0, code = 1} codecs->payloads[8].asterisk_format = static_RTP_PT[8].asterisk_format; codecs->payloads[8].code = static_RTP_PT[8].code; codecs->payloads[101].asterisk_format = static_RTP_PT[101].asterisk_format; codecs->payloads[101].code = static_RTP_PT[101].code; p->rtp->codecs->payloads[101].asterisk_format= codecs->payloads[101].asterisk_format p->rtp->codecs->payloads[101].code= codecs->payloads[101].code p->rtp->codecs->payloads[8].asterisk_format= codecs->payloads[8].asterisk_format p->rtp->codecs->payloads[8].code= codecs->payloads[8].code 100 tring 后启动pbx线程result = ast_pbx_start(c); sip_indicate condition 3 会调用 transmit_provisional_response 把180ring sent out asterisk 收到 1000的ring后 会 ast_channel_early_bridge(in, c); ast_indicate(in, AST_CONTROL_RINGING); 然后struct ast_frame *f = ast_read(in); 读数据 Asterisk 收到200OK 后给1000回复ACK 几个要搞明白的问题 1.每个channel 里面用来接收数据的socket是哪个socket 2.这些socket的建立是否只需要SDP的参与 3.rtp->f 是从哪里赋值的?如何赋值的?这个是code 转换的关键 rtp->f 的值来自ast_rtp_read。 下面只分析channel in sip_new() 在handle_request_invite里面调用完sip_new之后core掉。看创建时需要的东西以及创建后的初始化 c = sip_new(p, AST_STATE_DOWN, S_OR(p->peername, NULL), NULL); (gdb) p p->peername $2 = 0x2aaaac5f3c53 "2000" (gdb) p p->cid_num $4 = 0x2aaaac5f3c4c "2000" (gdb) p p->cid_name $5 = 0x2aaaac5f3bf7 "2000" (gdb) p p->accountcode $8 = 0x82c926 "" (gdb) p p->exten $7 = 0x2aaaac5f3bfe "1000" (gdb) p p->context $9 = 0x2aaaac5f3c61 "from-sip" (gdb) p p->amaflags $10 = 0 创建channel tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, linkedid, i->amaflags, "SIP/%s-%08x", my_name, ast_atomic_fetchadd_int((int *)&chan_idx, +1)); 初始化channel ast_channel_cc_params_init(tmp, i->cc_params); (gdb) p p->cid_tag $13 = 0x82c926 "" tmp->caller.id.tag = ast_strdup(i->cid_tag); (gdb) p p->flags $17 = {{flags = 34078720}, {flags = 134222080}, {flags = 0}} tmp->tech = ( ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_INFO || ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_SHORTINFO) ? &sip_tech_info : &sip_tech; (gdb) p p->jointcapability $18 = 8 (gdb) p p->capability $19 = 12 enable_dsp_detect(i); ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0)); chan->fds[0] =i->rtp->data; RTP socket set ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1)); 创建 f = create_dtmf_frame(instance, AST_FRAME_DTMF_END, 0); ast_channel_bridge ast_generic_bridge f = ast_read(who); ast_write(other, f); 使用这个来让transcode 来进行transcode __ast_read{ if (chan->readtrans && (f = ast_translate(chan->readtrans, f, 1)) == NULL) { f = &ast_null_frame; } } 当前遇到的问题 1.没有经过codec 转换 2.smoother 先把readtrans弄出来再说吧 readtrans是在产生的ast_set_read_format() 产生readtrans所需要的各种fmt是在sip_new 中赋值的 translate.c: Out of buffer space pvt->samples 0,f->samples 8180,pvt->t->buffer_samples 8000 错误 translate.c: Out of buffer space pvt->samples 0,f->samples 80, pvt->t->buffer_samples 8000 正确 到底神马地方设置成这样滴呢 ? f->samples 80 先强制转一下 alpha_test sip_read after get frame->samples 160 frame->samples =160 是在sip_read中设置的 static struct translator_path tr_matrix[MAX_FORMAT][MAX_FORMAT]; tr_matrix 是如何来的?里面的值有什么作用 在codec的module reload 进去的时候 会对在__ast_register_translator 会调用rebuild_matrix 对tr_matrix[x][z]的值进行初始化 tr_matrix[x][z].step = t; tr_matrix[x][z].cost = t->cost; tr_matrix[x][z].rate_change = new_rate_change; 其中t 就是 alawtoulaw static struct ast_translator alawtoulaw = { .name = "alawtoulaw .srcfmt = AST_FORMAT_ALAW, .dstfmt = AST_FORMAT_ULAW, .framein = alawtoulaw_framein, .sample = alaw_sample, .buffer_samples = BUFFER_SAMPLES, .buf_size = BUFFER_SAMPLES, }; static struct ast_translator ulawtoalaw = { .name = "ulawtoalaw .srcfmt = AST_FORMAT_ULAW, .dstfmt = AST_FORMAT_ALAW, .framein = ulawtoalaw_framein, .sample = ulaw_sample, .buffer_samples = BUFFER_SAMPLES, .buf_size = BUFFER_SAMPLES, }; static struct ast_sockaddr externaddr; /*!< External IP address if we are behind NAT */ static struct ast_sockaddr media_address; /*!< External RTP IP address if we are behind NAT */ /*! \\brief NAT fix - decide which IP address to use for Asterisk server? * * Using the localaddr structure built up with localnet statements in sip.conf * apply it to their address to see if we need to substitute our * externaddr or can get away with our internal bindaddr * 'us' is always overwritten. */ static void ast_sip_ouraddrfor(const struct ast_sockaddr *them, struct ast_sockaddr *us, struct sip_pvt *p) /*! \\brief Set nat mode on the various data sockets */ static void do_setnat(struct sip_pvt *p) ast_dsp_process这个函数加debug确定 shortdata的作用 确定AST_ALAW AST_MULAW的作用 ast_rtp_read 读取到电话发过来的只 储存到rtp->rawdata + AST_FRIENDLY_OFFSET rtp->f.src = "RTP"; rtp->f.mallocd = 0; rtp->f.datalen = res - hdrlen; rtp->f.data.ptr = rtp->rawdata + hdrlen + AST_FRIENDLY_OFFSET; rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET; rtp->f.seqno = seqno; 需要弄明白的问题 刚收到数据包的时候 res是多少, 为什么要-12 samples 是什么时候赋值的 frame->samples =160 是在sip_read中设置的 弄明白 ast_read ast_write 之间是 DSP ? 使用这个来让transcode 来进行transcode __ast_read{ if (chan->readtrans && (f = ast_translate(chan->readtrans, f, 1)) == NULL) { f = &ast_null_frame; } } 是否是在这个时候调用了rtp_read f = chan->tech->read(chan); 求出f->frametype的值 F->samples 在哪里计算的至关重要 在这个里面打印 pvt->samples ulawtoalaw_framein 保存以前做过的东西