最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 正文

Asterisk learn

来源:动视网 责编:小OO 时间:2025-09-26 04:13:43
文档

Asterisk learn

从内核的角度去分析问题时,弄清楚呼叫流程是非常关键的,只有理清了呼叫流程,才能从流程的各个环节细节中分析出问题所在。Asterisk所有功能都是基于函数调用的模式,呼叫流程也不例外。因此如何从一团乱麻似的内核函数调用中理出函数调用执行路线,是解读呼叫流程的关键。所有呼叫都跟astersisk的channel有关。这路通话都包含一个incoming连接和一个outbound连接。每个电话都是通过对应的channel程序建立起来的,比如Chan_sip,Chan_zap,Chan_iax2等等。每
推荐度:
导读从内核的角度去分析问题时,弄清楚呼叫流程是非常关键的,只有理清了呼叫流程,才能从流程的各个环节细节中分析出问题所在。Asterisk所有功能都是基于函数调用的模式,呼叫流程也不例外。因此如何从一团乱麻似的内核函数调用中理出函数调用执行路线,是解读呼叫流程的关键。所有呼叫都跟astersisk的channel有关。这路通话都包含一个incoming连接和一个outbound连接。每个电话都是通过对应的channel程序建立起来的,比如Chan_sip,Chan_zap,Chan_iax2等等。每
从内核的角度去分析问题时,弄清楚呼叫流程是非常关键的,只有理清了呼叫流程,才能从流程的各个环节细节中分析出问题所在。

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:     Find_sdp()

   {

           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

保存以前做过的东西

文档

Asterisk learn

从内核的角度去分析问题时,弄清楚呼叫流程是非常关键的,只有理清了呼叫流程,才能从流程的各个环节细节中分析出问题所在。Asterisk所有功能都是基于函数调用的模式,呼叫流程也不例外。因此如何从一团乱麻似的内核函数调用中理出函数调用执行路线,是解读呼叫流程的关键。所有呼叫都跟astersisk的channel有关。这路通话都包含一个incoming连接和一个outbound连接。每个电话都是通过对应的channel程序建立起来的,比如Chan_sip,Chan_zap,Chan_iax2等等。每
推荐度:
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top