和以前版本相比,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桥接两个通道。
相关推荐
《Asterisk中的SS7通道处理模块:chan_ss7详解》 在VoIP通信领域,Asterisk是一款广泛使用的开源PBX系统,它允许用户构建功能丰富的电话系统。Asterisk的强大之处在于其高度可扩展性和灵活性,可以支持多种协议和...
在本文中,我们将深入探讨Asterisk如何通过其"chan_ss7"模块来处理7号信令,并分析提供的文件列表中的各个组件。 7号信令是全球电信网络中广泛使用的通信控制协议,用于建立、管理和终止电话通话,以及传输数据服务...
这个项目或软件与SIP(Session Initiation Protocol)和Asterisk有关,SIP是一种用于控制多媒体通信会话(如语音和视频通话)的信令协议,而Asterisk是一款开源的IP电话系统软件,常被用作VoIP(Voice over Internet...
Chan_Dahdi是Asterisk中的一个模块,负责管理和控制这些物理线路,包括拨号计划、呼叫路由和信号处理等。 CID,又称为Calling ID,是一种电话服务功能,可以显示来电者的电话号码。在Asterisk中,CID信息通常包含...
COMPONENTE DE DELPHI PARA LLAMADAS EN ASTERISK POR EL PUERTO SIP ES NECESARIO HACER LA INTALACION CORRESPONDIENTE Y CARGAR LA RUTA DEL ARCHIVO FUENTE.
Asterisk是一款开源的IP...提供的"asterisk1.8参考录像"应该会提供更详细的步骤指导和实际操作演示,帮助你更直观地了解安装过程。观看录像,结合本文的理论知识,相信你能够顺利掌握Asterisk 1.8的安装和基本使用。
标题中的"asterisk-1.8.32.3.tar.gz"是指Asterisk的一个特定版本——1.8.32.3,这是一个源代码压缩包,适用于Linux操作系统。通过下载此资源并进行编译安装,用户可以在自己的服务器上搭建功能丰富的电话系统。 ...
在初学者的学习过程中,"asterisk1.8资料.txt" 文件将涵盖以下几个核心知识点: 1. **安装与配置**:首先,你需要了解如何在不同的操作系统上安装 Asterisk 1.8,包括 Linux 发行版如 CentOS 或 Debian。安装后,...
### Asterisk 1.8 相关知识点 #### 一、Asterisk 1.8 简介 Asterisk 1.8 是一款功能强大的开源PBX(Private Branch Exchange,私有分组交换)解决方案,它允许用户构建定制化的通信系统。Asterisk 1.8 版本在前几...
Asterisk1.8中文语音包是专为开源通信平台Asterisk设计的一款语言资源,旨在为使用中文的用户提供更加本土化的语音体验。这个语音包适用于Asterisk 1.8版本,配合elastix 2.3PBX系统,能够帮助用户在进行电话通话、...
2. **Asterisk架构**:理解Asterisk的核心组件,如DAHDI(数字接入高性能驱动接口)用于模拟电话线路,chan_sip或chan_pjsip用于SIP通信,以及如何配置这些组件。 3. **配置文件**:Asterisk的配置主要通过文本文件...
Asterisk Java是针对Asterisk AMI的Java库,允许开发者通过Java代码与Asterisk服务器进行交互,实现诸如创建呼叫、监控通道、更改拨号计划等功能。"asterisk-java-0.3.1-javadoc.rar" 是这个库的Java文档压缩包,...
**SIP协议与Asterisk:构建VoIP通信的核心技术** SIP(Session Initiation Protocol)协议是一种用于控制多媒体通信会话(如语音和视频通话)的信令协议。它在互联网电话(VoIP)领域扮演着核心角色,使得用户可以...
Asterisk的配置相当灵活,其核心配置文件一般位于/etc/asterisk下,包括extensions.conf(定义拨号计划)、sip.conf(配置SIP用户代理)和iax2.conf(配置IAx2用户代理)等。这些文件允许我们根据需求定制通话流程,...
Asterisk_Elastix_FreePBX 技术解答
5. **chan_sip.c**:这是处理SIP通道的核心模块,负责处理SIP消息的接收与发送,如INVITE请求的处理。 6. **ast_waitfor_n**:这个函数用于等待指定数量的事件发生,常用于处理并发事件。 7. **app_queue.c**:...
【描述】中提到的“基于Asterisk搭建的一个电话交换系统,sip/voip,命令行界面”意味着这个压缩包内容将涉及如何使用 Asterisk 实现 SIP(Session Initiation Protocol)协议来处理VoIP(Voice over Internet ...
这个压缩包"Asterisk-1.8.8.0.tar.gz"包含了Asterisk 1.8.8.0版本的源代码,供开发者和系统管理员进行安装、配置和定制。 Asterisk的核心功能包括: 1. **通话处理**:Asterisk可以处理语音通话、视频通话、即时...
1. `videosupport=yes`:这行代码是开启视频支持的关键,它告诉Asterisk应该处理视频流。 2. `maxcallbitrate=384`:这个参数设定了最大呼叫比特率,单位通常是kbps。这里设置为384kbps,意味着你的视频通话质量将...
asterisk 代码分析, sip_chan