最近做项目有用到在PJSIP客户端接收手机通过过程中发送过来的DTMF按键信息,刚开始查了一点资料发现pjsip有相应的方法,在pjsua_config cfg; 这个结构体的通过设置此回调函数cfg.cb.on_call_state貌似就可以实现 ,之后就开始实践了。用pjsip客户端+freeswitch+x-lite进行试验,试验相当顺利,心情那个激动啊。但是没激动多久,当我放到正式的运营环境用我的pjsip客户端+IMS运营商+真实手机进行测试时彻底傻眼了,根本检测不出来手机的按键信息,查了资料才发现手机发送的DTMF信息应该是属于in-band DTMF no rfc2833 ,悲催的是PJSIP官方并没有支持这中DTMF格式。
还好没放弃经过一个多月的瞎折腾总算弄出点结果出来了。
PJSIP官网的FAQ有对in-band DTMF no rfc2833问题解释,我就不扯淡了,直接给链接 http://trac.pjsip.org/repos/wiki/FAQ#tone-detect 。解释到是挺简单的,看起来很容易,但是对于我这中新手,我就只能呵呵了。查了快一个月的资料到是发现三四种方法,一一去试,没一个成功的,心情郁闷啊。最后还是厚着脸皮,凭着自己一手烂英语邮件去问外国友人。还好,外国友人都是相当热情的,等了一天就有人回复了,在这里要感谢 Eize Slange 这位大哥,不仅很快就给出了回复而且也给了相应的实现代码,非常感谢啊。
参照着给出的代码来进行实现果然有效,高兴啊!!!
我就简单介绍一下他给出的示例代码吧。
要取得in-band DTMF 按键信息就要检测sip客户端接收到的RTP流数据,并对RTP数据流使用Spandsp的库分析其中的DTMF按键信息,然后就大功告成了。
代码流程如下:
1.pjsip客户端层
在on_call_media_state 的回调函数中修改代码如下:
Note: pCallData / callData is some own class holding some info per call... callback: on_call_media_state: ... /* Handle media status */ switch (call_info.media_status) { case PJSUA_CALL_MEDIA_ACTIVE: ... pj_status_t result = PJ_SUCCESS; pjmedia_port* media_port = NULL; unsigned slot_port = 0; pjsua_dtmfdet_config dtmf_cfg; // Only attach once... if ( NULL == callData->dtmfData.media_port ) { dtmf_cfg.call_id = call_id; dtmf_cfg.filter_dialtone = PJ_FALSE; dtmf_cfg.twist = 8; // Twist is set to 8 dB by default. dtmf_cfg.reverse_twist = 4; // Reverse twist is set to 4 dB by default. This value can be safely increased up to 6 or 7 without a significant increase in talk-off to allow DTMFs that exceed this threshold to be detected. dtmf_cfg.threshold =-42; // Threshold is set to -42 dBm0 by default. dtmf_cfg.cb.on_dtmf_digit = &on_ib_dtmf_callback; log_writer(SLL_EVENT, "StackDll:on_call_media_state: Attach INBAND DTMF detection"); result = pjsua_attach_inband_dtmf_detector( call_id, app_config.media_cfg.clock_rate, app_config.media_cfg.channel_count, 160, // samples per frame 16, // bits per sample &dtmf_cfg, PJ_FALSE, // because this called from pjsua-callback &media_port, &slot_port ); if ( PJ_SUCCESS == result ) { callData->dtmfData.media_port = media_port; callData->dtmfData.slot_port = slot_port; } ... callback: on_stream_destroyed:: ... // Release/reset Inband DTMF pCallData = CallGetCallData(call_id); if ( NULL != pCallData ) { if ( NULL != pCallData->dtmfData.media_port ) { log_writer(SLL_LEVEL_2, "StackDll:on_stream_destroyed> Detach INBAND detector..."); pjsua_detach_inband_dtmf_detector( pCallData->dtmfData.media_port, pCallData->dtmfData.slot_port, PJ_FALSE); // because called from callback } pCallData->dtmfData.detectedOutbound = PJ_FALSE; pCallData->dtmfData.lastDigit = -1; pCallData->dtmfData.media_port = NULL; pCallData->dtmfData.slot_port = 0; } ... static void on_ib_dtmf_callback(pjsua_call_id call_id, int dtmf) { // Your callback code when inband DTMF digit has been detected. // Note: this is the same callback prototype as the normal DMTF callback of PJSIP, // but to distinguish between RFC2833 DTMF and this inband a dedicated // callback can be created. }
2.在pjsua.h新增如下: