Asterisk 1.8 chan_sip模块代码分析

和以前版本相比,Asterisk在架构上有了不小的变动,本文基于asterisk 1.8.10.1分析整理。

        chan_sip模块属于通道驱动模块。它实现了协议的相关内容,使Asterisk能够和支持SIP协议的其它设备通信。在1.8版本下,还是没有实现S/MIME的内容,有部分代码实现了TCP和TLS,但我没用过。对SIP事务的支持,还是不好。

        在chan_sip.c文件的顶部,简要地描述了这个模块实现的功能和一些缺陷,并描述了这个模块的发展计划,这里不重复这些内容。

        说明,下面代码引用处的行号来源于asterisk社区doxygen生成的文档,因为代码更新同步原因,可能和您看到的实际代码略有差异

模块初始化

       Asterisk是模块化设计的,内核会负责管理外围的模块。内核管理模块信息的回调函数,封装在ast_module_info这个数据结构中。这个数据结构的实例化过程,定义了一个宏,叫做AST_MODULE_INFO,所有的外围模块都会调用这个宏。在chan_sip.c中,实例化的代码是

31889 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Session Initiation Protocol (SIP)",
31890       .load = load_module,
31891       .unload = unload_module,
31892       .reload = reload,
31893       .load_pri = AST_MODPRI_CHANNEL_DRIVER,
31894       .nonoptreq = "res_crypto,chan_local",
31895           );

 

        上面说明了模块的加载函数入口load_module。当内核加载SIP模块时,就会跳转到这里执行。这个函数的原型是int load_module(void)。

        load_module首先初始化一系列的容器,1.8版本里,容器采用哈希表,不过这个表不能自动调整大小。容器实例主要有peers、dialogs等,这些在模块范围内是全局的。

        接下来,创建调度管事器和POLL模型的IO管理器。接下来,调用reload_config(sip_reloadreason)读取模块的配置文件。

31600    can_parse_xml = sip_is_xml_parsable();
31601    if (reload_config(sip_reloadreason)) { /* Load the configuration from sip.conf */
31602       return AST_MODULE_LOAD_DECLINE;
31603    }

        UDP是缺省支持的,如果配置文件里配置了TCP或TLS支持,那么在reload_config里会初始化相应的IO。

 

        接下来,注册通道类型和一系列的回调函数入口,比如说调用ast_rtp_glue_register设置RTP引擎相关的回调函数。最后,调用restart_monitor()来创建SIP监听线程。restart_monitor还在另外两个地方被调用,一个是sip_request_call,一个是sip_reload

 

        在restart_monitor函数中,调用ast_pthread_create_background创建一个新的线程,线程ID记录在monitor_thread,线程执行体是do_monitor。

26939       /* Start a new monitor */
26940       if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
26941          ast_mutex_unlock(&monlock);
26942          ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
26943          return -1;
26944       }

 

        do_monitor首先调用ast_io_add添加一个IO实体,把IO的FD记录在sipsock_read_id,并注册IO回调函数sipsock_read,这个回调是SIP信令的入口。然后进入线程循环体。循环中,首先检查是否需要重新加载模块,然后检查容器中需要挂断的通话(比如说RTP监测超时)。

 

        现在,回头来看reload_config这个函数,这里关注一下TCP和TLS初始化,如果对这部分不关心,可以跳过:

TCP

30000    /* Start TCP server */
30001    if (sip_cfg.tcp_enabled) {
30002       if (ast_sockaddr_isnull(&sip_tcp_desc.local_address)) {
30003          ast_sockaddr_copy(&sip_tcp_desc.local_address, &bindaddr);
30004       }
30005       if (!ast_sockaddr_port(&sip_tcp_desc.local_address)) {
30006          ast_sockaddr_set_port(&sip_tcp_desc.local_address, STANDARD_SIP_PORT);
30007       }
30008    } else {
30009       ast_sockaddr_setnull(&sip_tcp_desc.local_address);
30010    }
30011    ast_tcptls_server_start(&sip_tcp_desc);
30012    if (sip_cfg.tcp_enabled && sip_tcp_desc.accept_fd == -1) {
30013       /* TCP server start failed. Tell the admin */
30014       ast_log(LOG_ERROR, "SIP TCP Server start failed. Not listening on TCP socket.\n");
30015    } else {
30016       ast_debug(2, "SIP TCP server started\n");
30017    }

TLS

30019    /* Start TLS server if needed */
30020    memcpy(sip_tls_desc.tls_cfg, &default_tls_cfg, sizeof(default_tls_cfg));
30021 
30022    if (ast_ssl_setup(sip_tls_desc.tls_cfg)) {
30023       if (ast_sockaddr_isnull(&sip_tls_desc.local_address)) {
30024          ast_sockaddr_copy(&sip_tls_desc.local_address, &bindaddr);
30025          ast_sockaddr_set_port(&sip_tls_desc.local_address,
30026                      STANDARD_TLS_PORT);
30027       }
30028       if (!ast_sockaddr_port(&sip_tls_desc.local_address)) {
30029          ast_sockaddr_set_port(&sip_tls_desc.local_address,
30030                      STANDARD_TLS_PORT);
30031       }
30032       ast_tcptls_server_start(&sip_tls_desc);
30033       if (default_tls_cfg.enabled && sip_tls_desc.accept_fd == -1) {
30034          ast_log(LOG_ERROR, "TLS Server start failed. Not listening on TLS socket.\n");
30035          sip_tls_desc.tls_cfg = NULL;
30036       }
30037    } else if (sip_tls_desc.tls_cfg->enabled) {
30038       sip_tls_desc.tls_cfg = NULL;
30039       ast_log(LOG_WARNING, "SIP TLS server did not load because of errors.\n");
30040    }
30041 

 

        这两个处理,都用到了一个关键的结构体ast_tcptls_session_args,实例名字分别是sip_tcp_desc和sip_tls_desc,看一下它们是怎样实例化的。

02213 static struct ast_tcptls_session_args sip_tcp_desc = {
02214    .accept_fd = -1,
02215    .master = AST_PTHREADT_NULL,
02216    .tls_cfg = NULL,
02217    .poll_timeout = -1,
02218    .name = "SIP TCP server",
02219    .accept_fn = ast_tcptls_server_root,
02220    .worker_fn = sip_tcp_worker_fn,
02221 };
02222 
02223 /*! \brief The TCP/TLS server definition */
02224 static struct ast_tcptls_session_args sip_tls_desc = {
02225    .accept_fd = -1,
02226    .master = AST_PTHREADT_NULL,
02227    .tls_cfg = &sip_tls_cfg,
02228    .poll_timeout = -1,
02229    .name = "SIP TLS server",
02230    .accept_fn = ast_tcptls_server_root,
02231    .worker_fn = sip_tcp_worker_fn,
02232 };

 

        跟踪一下工作回调函数sip_tcp_worker_fn,会发现,最终调用了handle_request_do函数处理读取的数据,无论传输层用什么协议,数据都是调用这个函数处理的。

 

内核接口

01612 /*! \brief Definition of this channel for PBX channel registration */
01613 struct ast_channel_tech sip_tech = {
01614    .type = "SIP",
01615    .description = "Session Initiation Protocol (SIP)",
01616    .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,
01617    .requester = sip_request_call,         /* called with chan unlocked */
01618    .devicestate = sip_devicestate,        /* called with chan unlocked (not chan-specific) */
01619    .call = sip_call,       /* called with chan locked */
01620    .send_html = sip_sendhtml,
01621    .hangup = sip_hangup,         /* called with chan locked */
01622    .answer = sip_answer,         /* called with chan locked */
01623    .read = sip_read,       /* called with chan locked */
01624    .write = sip_write,        /* called with chan locked */
01625    .write_video = sip_write,     /* called with chan locked */
01626    .write_text = sip_write,
01627    .indicate = sip_indicate,     /* called with chan locked */
01628    .transfer = sip_transfer,     /* called with chan locked */
01629    .fixup = sip_fixup,        /* called with chan locked */
01630    .send_digit_begin = sip_senddigit_begin/* called with chan unlocked */
01631    .send_digit_end = sip_senddigit_end,
01632    .bridge = ast_rtp_instance_bridge,        /* XXX chan unlocked ? */
01633    .early_bridge = ast_rtp_instance_early_bridge,
01634    .send_text = sip_sendtext,    /* called with chan locked */
01635    .func_channel_read = sip_acf_channel_read,
01636    .setoption = sip_setoption,
01637    .queryoption = sip_queryoption,
01638    .get_pvt_uniqueid = sip_get_callid,
01639 };

          这些函数,实现了内核的回调接口,在模块初始化时,会把这些函数的入口注册给内核的相关管理器。

入呼处理流程

        前面说过,sipsock_read是UDP SIP消息的入口。调用ast_recvfrom(recvfrom函数的封装),从socket上读取数据包。把数据存储在一个sip_request结构体对象的data字段中,然后调用handle_request_do函数处理读取的数据,这里面,很多地方的request泛指了SIP消息,包括response,光看字面,很容易误解。

 

        首先,解析SIP消息:

26367    if (parse_request(req) == -1) { /* Bad packet, can't parse */
26368       ast_str_reset(req->data); /* nulling this out is NOT a good idea here. */
26369       return 1;
26370    }

 

        SIP消息解析完毕之后,匹配SIP的方法,在asterisk里,把response消息也当成一种方法来处理。接着调用find_call检索消息对应的sip_pvt结构,如果原先没有,find_call函数中会创建一个新的,最终把指针返回。紧接着,通过pvt结构中的owner字段,判断消息是否经过权鉴,最后调用handle_incoming函数,对消息进一步的处理:

26371    req->method = find_sip_method(REQ_OFFSET_TO_STR(req, rlPart1));
26372 
26373    if (req->debug)
26374       ast_verbose("--- (%d headers %d lines)%s ---\n", req->headers, req->lines, (req->headers + req->lines == 0) ? " Nat keepalive" : "");
26375 
26376    if (req->headers < 2) { /* Must have at least two headers */
26377       ast_str_reset(req->data); /* nulling this out is NOT a good idea here. */
26378       return 1;
26379    }
26380    ast_mutex_lock(&netlock);
26381 
26382    /* Find the active SIP dialog or create a new one */
26383    p = find_call(req, addr, req->method); /* returns p with a reference only. _NOT_ locked*/
26384    if (p == NULL) {
26385       ast_debug(1, "Invalid SIP message - rejected , no callid, len %zu\n", ast_str_strlen(req->data));
26386       ast_mutex_unlock(&netlock);
26387       return 1;
26388    }
26389 
26390    /* Lock both the pvt and the owner if owner is present.  This will
26391     * not fail. */
26392    owner_chan_ref = sip_pvt_lock_full(p);
26393 
26394    copy_socket_data(&p->socket, &req->socket);
26395    ast_sockaddr_copy(&p->recv, addr);
26396 
26397    /* if we have an owner, then this request has been authenticated */
26398    if (p->owner) {
26399       req->authenticated = 1;
26400    }
26401 
26402    if (p->do_history) /* This is a request or response, note what it was for */
26403       append_history(p, "Rx", "%s / %s / %s", req->data->str, sip_get_header(req, "CSeq"), REQ_OFFSET_TO_STR(req, rlPart2));
26404 
26405    if (handle_incoming(p, req, addr, &recount, &nounlock) == -1) {
26406       /* Request failed */
26407       ast_debug(1, "SIP message could not be handled, bad request: %-70.70s\n", p->callid[0] ? p->callid : "<no callid>");
26408    }

 

        下面看一下handle_incoming函数,它首先对消息的合法性做一些检查,如果是应答消息则走入应答消息处理分枝,否则,继续向下处理:

26064    if (req->method == SIP_RESPONSE)
......
26099          handle_response(p, respid, e + len, req, seqno);

 

       

        这其中,还检查消息是否为重发消息,如果是,填充重发标识:

else if (p->icseq &&
26143          p->icseq == seqno &&
26144          req->method != SIP_ACK &&
26145          (p->method != SIP_CANCEL || p->alreadygone)) {
26146       /* ignore means "don't do anything with it" but still have to
26147          respond appropriately.  We do this if we receive a repeat of
26148          the last sequence number  */
26149       req->ignore = 1;
26150       ast_debug(3, "Ignoring SIP message because of retransmit (%s Seqno %u, ours %u)\n", sip_methods[p->method].text, p->icseq, seqno);
26151    }

 

 

        根据3261规范定义,检查消息的一些通用合法性之后,根据请求的method,调用各自的处理函数:

26199    /* Handle various incoming SIP methods in requests */
26200    switch (p->method) {
26201    case SIP_OPTIONS:
26202       res = handle_request_options(p, req, addr, e);
26203       break;
26204    case SIP_INVITE:
26205       res = handle_request_invite(p, req, debug, seqno, addr, recount, e, nounlock);
               ......
           }

 

 

         下面,把注意力集中在handle_request_invite函数。

        首先,查看消息里的Supported和Require头域,看asterisk能否支持对方所要求的扩展。

23101    if (ast_test_flag(&p->flags[0], SIP_OUTGOING) && p->owner && (p->invitestate != INV_TERMINATED && p->invitestate != INV_CONFIRMED) && ast_channel_state(p->owner) != AST_STATE_UP)

        上面这个判断的处理就是检查SIP扩展的。

 

23150    if (!req->ignore && p->pendinginvite)

         这个判断请求的处理情况,是否正在处理。

 

       接下来,检查是否有Replaces头域,如果有,做呼叫转移处理,里面调用到一个叫handle_invite_replaces的函数:

23192    p_replaces = sip_get_header(req, "Replaces");
23193    if (!ast_strlen_zero(p_replaces)) {
23194       /* We have a replaces header */
             ........
          }

 

         接下来的处理,有两种情况,一个是re-invite,一个是初始的invite。这两种情况的处理,有相同的地方,又有差异。代码中先处理不同的部分,再处理相同的部分;先处理re-invite,再处理原始INVITE。

 

23339    if (!req->ignore) {
23340       int newcall = (p->initreq.headers ? TRUE : FALSE);
23341 
23342       if (sip_cancel_destroy(p))
23343          ast_log(LOG_WARNING, "Unable to cancel SIP destruction.  Expect bad things.\n");
23344       /* This also counts as a pending invite */
23345       p->pendinginvite = seqno;
23346       check_via(p, req);
23347 
23348       copy_request(&p->initreq, req);     /* Save this INVITE as the transaction basis */
23349       if (sipdebug)
23350          ast_debug(1, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
23351       if (!p->owner) {  /* Not a re-invite */
23352          if (debug)
23353             ast_verbose("Using INVITE request as basis request - %s\n", p->callid);
23354          if (newcall)
23355             append_history(p, "Invite", "New call: %s", p->callid);
23356          parse_ok_contact(p, req);
23357       } else { /* Re-invite on existing call */
              ......
             }}

         这一段处理re-invite的情况,主要是关于SDP变化处理什么的。

 

23417    if (!p->lastinvite && !req->ignore && !p->owner) {
23418       /* This is a new invite */
23419       /* Handle authentication if this is our first invite */
23420       int cc_recall_core_id = -1;
23421       set_pvt_allowed_methods(p, req);
23422       res = check_user_full(p, req, SIP_INVITE, e, XMIT_RELIABLE, addr, &authpeer);
23423       if (res == AUTH_CHALLENGE_SENT) {
23424          p->invitestate = INV_COMPLETED;     /* Needs to restart in another INVITE transaction */
23425          goto request_invite_cleanup;
23426       }
               ......

          这一段处理原始INVITE的情况。check_user_full这个函数调用,检查被叫是否是合法的用户,还完成了RPT引擎的初始化。调用栈是:check_user_full-->check_peer_ok-->dialog_initialize_rtp-->ast_rtp_instance_new(还可能是check_user_full直接调用dialog_initialize_rtp)。RTP引擎的处理是Asterisk构架中比较重大的变化,缺省使用asterisk的RTP栈(称之为引擎),但允许用户嵌入自己的RTP栈。

 

        check_user_full返回之后,权鉴通过,设置标识位,然后处理SDP,如果请求中不带SDP,则可能是3PCC的流程:

23450       /* We have a successful authentication, process the SDP portion if there is one */
23451       if (find_sdp(req)) {
23452          if (process_sdp(p, req, SDP_T38_INITIATE)) {
23453             /* Asterisk does not yet support any Content-Encoding methods.  Always
23454              * attempt to process the sdp, but return a 415 if a Content-Encoding header
23455              * was present after processing fails. */
23456             if (!ast_strlen_zero(sip_get_header(req, "Content-Encoding"))) {
23457                transmit_response_reliable(p, "415 Unsupported Media type", req);
23458             } else {
23459                /* Unacceptable codecs */
23460                transmit_response_reliable(p, "488 Not acceptable here", req);
23461             }
23462             p->invitestate = INV_COMPLETED;
23463             sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
23464             ast_debug(1, "No compatible codecs for this SIP call.\n");
23465             res = INV_REQ_ERROR;
23466             goto request_invite_cleanup;
23467          }
23468       } else { /* No SDP in invite, call control session */
23469          ast_format_cap_copy(p->jointcaps, p->caps);
23470          ast_debug(2, "No SDP in Invite, third party call control\n");
23471       }

 

         对于原始INVITE来说,很关键的跳转点就是创建新的channel处理:

23554          /* First invitation - create the channel.  Allocation
23555           * failures are handled below. */
23556          c = sip_new(p, AST_STATE_DOWN, S_OR(p->peername, NULL), NULL);

        sip_new这个函数用于创建一个SIP CHANNEL,这里是入呼的调用,外呼时则是在sip_request_call函数中调用,sip_request_call是内核接口的SIP实现。sip_new返回之后,调用build_route记录SIP消息的路由信息。

 

        回到handle_request_invite函数,re-invite和原始invite的差异处理完之后,处理SST(SIPSession Timer)扩展的支持:

23594    /* Session-Timers */
23595    if ((p->sipoptions & SIP_OPT_TIMER) && !ast_strlen_zero(sip_get_header(req, "Session-Expires")))
......

 

         处理完SST之后,判断自身是还是Attendedtransfer 或call pickup的目标,最后,处理CHANNEL状态,根据状态,选择合适的应答码,交发出SIP应答。

23784       switch(c_state) {
23785       case AST_STATE_DOWN:
23786          ast_debug(2, "%s: New call is still down.... Trying... \n", ast_channel_name(c));
23787          transmit_provisional_response(p, "100 Trying", req, 0);
23788          p->invitestate = INV_PROCEEDING;
23789          ast_setstate(c, AST_STATE_RING);
23790          if (strcmp(p->exten, ast_pickup_ext())) { /* Call to extension -start pbx on this call */
23791             enum ast_pbx_result result;
23792 
23793             result = ast_pbx_start(c);
23794 
23795             switch(result) {
23796             case AST_PBX_FAILED:
23797                ast_log(LOG_WARNING, "Failed to start PBX :(\n");
23798                p->invitestate = INV_COMPLETED;
23799                transmit_response_reliable(p, "503 Unavailable", req);
23800                break;
23801             case AST_PBX_CALL_LIMIT:
23802                ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
23803                p->invitestate = INV_COMPLETED;
23804                transmit_response_reliable(p, "480 Temporarily Unavailable", req);
23805                res = AUTH_SESSION_LIMIT;
23806                break;
23807             case AST_PBX_SUCCESS:
23808                /* nothing to do */
23809                break;
23810             }
                ......

 

         如果是新的呼叫,则调用ast_pbx_start,在这里面启动一个独立的线程,接管这个CHANNEL,新创建的线程入口是pbx_thread函数,在这个函数里调用__ast_pbx_run函数,跳转执行拨号计划。

 

外呼流程

       

        从SIP角度理解,Asterisk就是一个B2BUA,一路通话,在Asterisk内部需要两个UA,Asterisk负责把两个UA桥接在一块。一个UA对应了一个通道,上面分析了入呼(incoming)通道,接下来我们分析一下外呼(outgoing)通道的情况。

 

        还是从ast_pbx_start说起:

05518    if (increase_call_count(c))
05519       return AST_PBX_CALL_LIMIT;
05520 
05521    /* Start a new thread, and get something handling this channel. */
05522    if (ast_pthread_create_detached(&t, NULL, pbx_thread, c)) {
05523       ast_log(LOG_WARNING, "Failed to create new channel thread\n");
05524       decrease_call_count();
05525       return AST_PBX_FAILED;
05526    }

        这几行代码检查是否还有空闲处理能力(取决于配置),如果有调用ast_pthread_create_detached启动一个线程,线程入口函数是pbx_thread。当然,这个线程是处理入呼通道的。

 

        跳转到pbx_thread看一下都做了些什么?

05479 static void *pbx_thread(void *data)
05480 {
05481    /* Oh joyeous kernel, we're a new thread, with nothing to do but
05482       answer this channel and get it going.
05483    */
05484    /* NOTE:
05485       The launcher of this function _MUST_ increment 'countcalls'
05486       before invoking the function; it will be decremented when the
05487       PBX has finished running on the channel
05488     */
05489    struct ast_channel *c = data;
05490 
05491    /* Associate new PBX thread with a call-id */
05492    struct ast_callid *callid = ast_create_callid();
05493    ast_callid_threadassoc_add(callid);
05494    callid = ast_callid_unref(callid);
05495 
05496    __ast_pbx_run(c, NULL);
05497    decrease_call_count();
05498 
05499    pthread_exit(NULL);
05500 
05501    return NULL;
05502 }

       

        这里调用了一个很关键的函数:__ast_pbx_run

       /* Start by trying whatever the channel is set to */
       if (!ast_exists_extension(c, c->context, c->exten, c->priority,
               S_COR(c->caller.id.number.valid, c->caller.id.number.str, NULL))) {
               /* If not successful fall back to 's' */
               ast_verb(2, "Starting %s at %s,%s,%d failed so falling back to exten 's'\n", c->name, c->context, c->exten, c->priority);
               /* XXX the original code used the existing priority in the call to
                * ast_exists_extension(), and reset it to 1 afterwards.
                * I believe the correct thing is to set it to 1 immediately.
                */
               set_ext_pri(c, "s", 1);
               if (!ast_exists_extension(c, c->context, c->exten, c->priority,
                       S_COR(c->caller.id.number.valid, c->caller.id.number.str, NULL))) {
                       /* JK02: And finally back to default if everything else failed */
                       ast_verb(2, "Starting %s at %s,%s,%d still failed so falling back to context 'default'\n", c->name, c->context, c->exten, c->priority);
                       ast_copy_string(c->context, "default", sizeof(c->context));
               }
       }

 

        在这里,首先调用ast_exists_extension查找拨号计划的入口,如果找到了,进入一个for循环,逐条执行拨号计划:

05141    for (;;) {
05142       char dst_exten[256]; /* buffer to accumulate digits */
05143       int pos = 0;      /* XXX should check bounds */
05144       int digit = 0;
05145       int invalid = 0;
05146       int timeout = 0;
05147 
05148       /* loop on priorities in this context/exten */
05149       while (!(res = ast_spawn_extension(c, ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c),
05150          S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL),
05151          &found, 1))) {
05152          if (!ast_check_hangup(c)) {
05153             ast_channel_priority_set(c, ast_channel_priority(c) + 1);
05154             continue;
5155              }
         ........

 

       ast_spawn_extension这个函数调用pbx_extension_helper完成具体的动作:

05051 int ast_spawn_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid, int *found, int combined_find_spawn)
05052 {
05053    return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_SPAWN, found, combined_find_spawn);
05054 }

 

        pbx_extension_helper调用pbx_find_extension查找系统中的extension:

04342    e = pbx_find_extension(c, con, &q, context, exten, priority, label, callerid, action);

找到之后,调用pbx_exec执行

04390          return pbx_exec(c, app, passdata);  /* 0 on success, -1 on failure */

 

        到目前为止,处理的都还是入呼的流程,如果需要外呼,就是在拨号计划中执行到dial()应用,从这个应用跳转到外呼处理。

 

        在app_dial模块加载时,注册了一个应用的回调入口:

03160    res = ast_register_application_xml(app, dial_exec);

 

        而dial_exec则调用了dial_exec_full

03029 static int dial_exec(struct ast_channel *chan, const char *data)
03030 {
03031    struct ast_flags64 peerflags;
03032 
03033    memset(&peerflags, 0, sizeof(peerflags));
03034 
03035    return dial_exec_full(chan, data, &peerflags, NULL);
03036 }

 

 

        在dial_exec_full函数里:首先调用ast_request分配通道资源,最终调用的就是通道模块注册的回调函数,SIP模块就是sip_request_call函数。然后调用ast_call发起呼叫,对应最终调用SIP通道注册的回调函数sip_call。最后,调用wait_for_answer等待被叫方应答。被叫应答后调用ast_bridge_call桥接两个通道。

猜你喜欢

转载自coolwhy1.iteye.com/blog/1772210
今日推荐