看了前面三小节的内容,有什么收获和感想呢?是不是在为自己的第一个iax客户端程序能运行并接入asterisk server并连接到指定的电话号码而cheer up呢?是不是还想完善一下这个简单的testcall呢?比如实现hold和unhold call,实现多条线路接入呢?开动你的大脑,想想怎么实现这些现实生活中常见的功能吧!!!!
1. HOLD & UNHOLD
其实在iaxclient.dll中已经实现了hold and unhold call,只需要调用两个函数。
int iaxc_quelch (int callNo, int MOH) /* Causes the audio channel for callNo to QUELCH (be squelched). */
int iaxc_unquelch (int callNo) /*Causes the audio channel for callNo to be UNQUELCH (unsquelched). */
iaxc_quelch() 的callNo可以是当前selected的callNo,MOH(Music On Hold)只需要>0就可以啦,通常为1,这样callNo就将hold住了,同时播放music on hold。
iaxc_unquelch()的callNo是之前传入 iaxc_quelch() 的callNo,这样讲unhold当前的callNo。
2. Multiline access in iax client
IDEFISK是一款较出名的iax/sip soft phone,它实现了五条线的切换。为了让自己的程序也可以实现这样的功能,开始googling吧,遗憾的是在google上搜索的资料并没有太多的information,有的人说是要配置sip.conf / phone.conf,要switch支持,各种说法都有。我也考虑过,发现在上一节的testcall中第二次但电话进入后都是busytone,于是改写了extensions.conf中的from_sip extension如下:
[from_sip]
exten => _XXXXXX,1,Set(DISTRICT=cra)
exten => _XXXXXX,n,MixMonitor(/dev/shm/${DISTRICT}/${STRFTIME(${EPOCH},,%y%m%d)}/${CALLERID(ani)}.00.${STRFTIME(${EPOCH},,%Y%m%d%H%M%S)}.${EXTEN}.${UNIQUEID}.wav49,bv(0)V(0)) ;call rec
exten => _XXXXXX,n,Answer()
exten => _XXXXXX,n,Dial(IAX2/${EXTEN},30,rmM(sendUID,${UNIQUEID}))
exten => _XXXXXX,n,Hangup()
主要是comments掉if busy then hangup 的设定。
再次运行testcall,发现第二个接入电话无法进入并马上断线,同时testcall输出:
IAXCLIENT: -1
Incoming Call, but no appearances
IAXCLIENT: Event (type 2) for a non-existant session. Dropping Incoming Call, but no appearances
asterisk server的CLI上输出为:
-- Called 111001
-- Call accepted by 192.168.121.181 (format gsm)
-- Format for call is gsm
-- IAX2/111001-3609 is busy
-- Hungup 'IAX2/111001-3609'
== Everyone is busy/congested at this time (1:1/0/0)
-- Executing [111001@from_sip:5] Hangup("SIP/87654321hk-08bcb290", "") in new stack
== Spawn extension (from_sip, 111001, 5) exited non-zero on 'SIP/35984523hk-08bcb290'
== End MixMonitor Recording SIP/87654321hk-08bcb290
-- Hungup 'IAX2/111001-2939'
== Spawn extension (from_sip, 111001, 4) exited non-zero on 'SIP/87654321hk-08b262d8'
== End MixMonitor Recording SIP/87654321hk-08b262d8
为了试验实现接入多条线是否需要conf文件的支持,我使用了知名的IDEFISK软件,正确配置了相关的iax设定后(参考http://www.asteriskguru.com/tutorials/idefisk_softphone.html),再次用多个电话呼叫87654321,发现IDEFISK在现有的asterisk conf不变的情况下可以使用多条线路,到这里,我们可以下结论啦:实现多条线路的接入不需要改变默认的asterisk cong 文件设置,而应该是在客户端编程实现的。 那么我们的程序到底又应该怎么样实现呢?????
带着上面5个大问号,我有开始googling啦!不断的键入iax 2 line phone , asterisk multiline phone等等,最终还是收获不大。正在郁闷之极是,查看lib iaxclient的在线帮助页面,不断的查看每一个函数的解释和使用,发现我视乎遗漏了什么,但我查看iaxc_select_call()的实现代码是,发现有一个重要的变量-- max_calls,max_calls有是什么呢?是否和我内心所想的一样呢?于是在iaxclient-2.1beta3/lib/下搜索max_calls,发现是在iaxc_initialize()中赋值的,同时其他很多的iaxc_ 函数都要对max_calls做检查。实现multiline接入的关键是否就在这个max_calls呢?为此需要查看iaxc_initialize()的解释:
int iaxc_initialize ( int num_calls )
Initializes the IAXClient library.
Parameters: num_calls The maximum number of simultaneous calls to handle.
This initializes the IAXClient
在查看源文件iaxclient_lib.c.的实现如下:
EXPORT int iaxc_initialize(int num_calls)
{
int i;
int port;
os_init();
setup_jb_output();
MUTEXINIT(&iaxc_lock);
MUTEXINIT(&event_queue_lock);
iaxc_set_audio_prefs(0);
if ( iaxc_recvfrom != (iaxc_recvfrom_t)recvfrom )
iax_set_networking(iaxc_sendto, iaxc_recvfrom);
/* Note that iax_init() only sets up the receive port when the
* sendto/recvfrom functions have not been replaced. We need
* to call iaxc_init in either case because there is other
* initialization beyond the socket setup that needs to be done.
*/
if ( (port = iax_init(source_udp_port)) < 0 )
{
iaxci_usermsg(IAXC_ERROR,
"Fatal error: failed to initialize iax with port %d",
port);
return -1;
}
if ( iaxc_recvfrom == (iaxc_recvfrom_t)recvfrom )
iaxci_bound_port = port;
else
iaxci_bound_port = -1;
/* tweak the jitterbuffer settings */
iax_set_jb_target_extra( jb_target_extra );
max_calls = num_calls;
/* initialize calls */
if ( max_calls <= 0 )
max_calls = 1; /* 0 == Default? */
/* calloc zeroes for us */
calls = (struct iaxc_call *)calloc(sizeof(struct iaxc_call), max_calls);
if ( !calls )
{
iaxci_usermsg(IAXC_ERROR, "Fatal error: can't allocate memory");
return -1;
}
selected_call = -1;
for ( i = 0; i < max_calls; i++ )
{
strncpy(calls[i].callerid_name, DEFAULT_CALLERID_NAME, IAXC_EVENT_BUFSIZ);
strncpy(calls[i].callerid_number, DEFAULT_CALLERID_NUMBER, IAXC_EVENT_BUFSIZ);
}
if ( !test_mode )
{
#ifndef AUDIO_ALSA
if ( pa_initialize(&audio_driver, 8000) )
{
iaxci_usermsg(IAXC_ERROR, "failed pa_initialize");
return -1;
}
#else
/* TODO: It is unknown whether this stuff for direct access to
* alsa should be left in iaxclient. We're leaving it in here for
* the time being, but unless it becomes clear that someone cares
* about having it, it will be removed. Also note that portaudio
* is capable of using alsa. This is another reason why this
* direct alsa access may be unneeded.
*/
if ( alsa_initialize(&audio_driver, 8000) )
return -1;
#endif
}
#ifdef USE_VIDEO
if ( video_initialize() )
iaxci_usermsg(IAXC_ERROR,
"iaxc_initialize: cannot initialize video!/n");
#endif
/* Default audio format capabilities */
audio_format_capability =
IAXC_FORMAT_ULAW |
IAXC_FORMAT_ALAW |
#ifdef CODEC_GSM
IAXC_FORMAT_GSM |
#endif
IAXC_FORMAT_SPEEX;
audio_format_preferred = IAXC_FORMAT_SPEEX;
return 0;
}
认真看完这些代码后更加肯定了实现多条线接入的话在初始化iaxc的时候就要传入线路数,为此改写testcall中的相关代码:
#define MAX_ALLOW_LINE 2
if ( iaxc_initialize(MAX_ALLOW_LINE) ) // init iax client lib, max 2 calls in
fatal_error("cannot initialize iaxclient!");
重新编译运行并多次呼叫87654321,此时第二条线有电话接通的嘟嘟声,同时testcall的输出有这一项:IAXCLIENT: Incoming call on line 1。
到这里,你是否对实现多条线接入iax client而有很好的想法呢,lets getting down to the sold work now.
为实现这些功能的代码,我增加了一些变量:
int call_no=-1; //record current ringing call no
int curr_no[MAX_ALLOW_LINE]={IAXC_CALL_STATE_FREE ,IAXC_CALL_STATE_FREE }; //store line state
另外为了更好的观察这些iaxclient call event,我屏蔽了很多其他event在console的输出。同时在接入line2的时候应该hold住line 1,在结束line2通话后应回复line1的通话,为此改写了state_event_callback()如下:
int state_event_callback(struct iaxc_ev_call_state call){
int i=0;
printf("receive call[%d] state event: 0X%p/n",call.callNo,call.state);
if((call.state & IAXC_CALL_STATE_RINGING))
{
curr_no[call.callNo]=call.state;
call_no = call.callNo;
display_line_state();
if(call.state & IAXC_CALL_STATE_OUTGOING)
{
printf("Make outgoing call call no.[%d].../n",call.callNo);
printf("Remote:[%s][%s] Local:[%s][%s]/n",
call.remote,call.remote_name,call.local,call.local_context);
}
else
{
printf("Receiving Incoming Call Request call no.[%d].../n",call.callNo);
printf("Remote:[%s][%s] Local:[%s][%s]/n",
call.remote,call.remote_name,call.local,call.local_context);
if ( intercom )
{
printf("Intercom mode, answer automatically/n");
return iaxc_select_call(call.callNo);
}
}
}
else if(call.state & IAXC_CALL_STATE_SELECTED )
{
curr_no[call.callNo]=call.state;
display_line_state();
if(call.state & IAXC_CALL_STATE_OUTGOING )
printf("IAX Out conn [%d] - Remote:[%s][%s] Local:[%s][%s]/n",
call.callNo,call.remote,call.remote_name,call.local,call.local_context);
else
printf("IAX In conn [%d] - Remote:[%s][%s] Local:[%s][%s]/n",
call.callNo,call.remote,call.remote_name,call.local,call.local_context);
}
else if (call.state == IAXC_CALL_STATE_FREE )
{
if(curr_no[call.callNo] == IAXC_CALL_STATE_FREE) //already free?
return 0;
curr_no[call.callNo]=call.state;
printf("call [%d] free now! (%d)/n",call.callNo,iaxc_selected_call());
display_line_state();
if(call.callNo != iaxc_selected_call())//not the current call hang up
return 0;
printf("we're going to select another waiting call/n");