FreeSWITCH空号识别模块(mod_da2)使用说明

介绍

mod_da2是顶顶通开发的一个FreeSWITCH回铃音检测模块(通过分析FreeSWITCH早期媒体的声音,得到被叫号码状态【可以识别到回铃音、忙音、彩铃、空号、通话中,关机、停机,语音信箱和留言等各种号码状态】),用来对接顶顶通回铃音检测服务,在呼叫前设置通道变量execute_on_media=start_da2,就可以在呼叫的时候启动空号识别。

下载

http://www.dingdingtong.cn/mod_da2_last.rar

安装

假设 fs安装目录是 /usr/local/freeswitch/

da2.conf.xml copy到 fs安装目录/conf/autoload_configs/da2.conf.xml
da2.json copy到 fs安装目录/conf/da2.json
顶顶通回铃音检测系统在线测试环境(提供1并发免费测试)da2.json的addr修改为 da2.ddrj.com key修改为 aaa
顶顶通回铃音检测系统私有化部署单机版本版本addr 修改为 127.0.0.1 key 设置为空
顶顶通回铃音检测系统私有化部署多机版本版本addr 修改为 daserver 部署服务器IP key 设置为 daserver目录 users.json 里面分配的key

{
"da" :
{
"connect" :
{
"addr" : "da2.ddrj.com",
"port" : 9977,
"reConnectInterval" : 15000,
"timer" : 5000
},
"key" : "aaa"
}
}

mod_da2.so copy到 fs安装目录/mod/mod_da2.so

fs安装目录/conf/autoload_configs/modules.conf.xml 末尾( 之前)加入

fs_cli 执行 da2 show ,如果输出ready:ok ,就是和daserver连接正确。

测试

fs_cli 测试

  • 外呼测试方法

    originate {ignore_early_media=consume,execute_on_pre_answer_da2=start_da2,execute_on_pre_answer_sendrtp=playback::silence_stream://1000,jitterbuffer_msec=60}sofia/external/st@sip.ddrj.com:11451 &park
    • ignore_early_media=consume 注意如果你程序的拨号串是ignore_early_media=true要改成ignore_early_media=consume
    • execute_on_pre_answer_da2=start_da2 收到183的时候启动检测 ,如果是模拟线路没有183直接进入应答后检测的,改成 execute_on_media_da2=start_da2
    • execute_on_pre_answer_sendrtp=playback::silence_stream://1000 如果你的fs的rtp端口没映射进来,需要先发送媒体流才可以收到早期媒体。如果RTP映射进来了,这个可以去除。
    • jitterbuffer_msec=60 开启抖动缓冲区,如果网络很好,不存在丢包,这个可以去除。
  • 有的线路,比如IMS或者fs内网,需要fs先发包,才可以接收到早期媒体,可以这样:
    bgapi originate {execute_on_media=start_da2}sofia/external/st@sip.ddrj.com:11451 wait_for_answer_da,park inline

  • 网络不好有丢包的线路,需要开启抖动缓冲区
    bgapi originate {execute_on_media=start_da2,jitterbuffer_msec=60}sofia/external/st@sip.ddrj.com:11451 wait_for_answer_da,park inline
  • 通过API启动
    originate {origination_uuid=myuuid,api_on_media=’uuid_start_da2 myuuid’}sofia/external/da2@ddrj.com:16060 wait_for_answer_da,park inline
  • 自定义检测参数覆盖da2.conf.xml配置
    bgapi originate {execute_on_media=start_da2,da2_param_stoptone=’sample busy’,da2_param_autohangup=true,da2_param_ignoresamples=’abc’,da2_param_answerautosotp=true,da2_param_maxdetecttime=30}sofia/external/da1@47.97.5.205:16080 wait_for_answer_da,park inline
  • 先呼叫手机,然后呼叫分机
    originate {origination_caller_id_number=主叫号码,execute_on_media=start_da2}sofia/external/vad1@ddrj.com:16080 ‘m:^:wait_for_answer_da:60000^bridge:{origination_caller_id_number=主叫号码,origination_caller_id_name=主叫用户}user/1000’ inline

dialplan测试

 <extension name="da">
<condition field="destination_number" expression="^(\d*)$">
<action application="export" data="nolocal:execute_on_media=start_da2"/>
<action application="bridge" data="sofia/external/da1@47.97.5.205:16080"/>
</condition>
</extension>

自动外呼程序注意

很多自动外呼程序 会加 ignore_early_media=true 来等待应答后再转入dialplan。
加了这个变量就无法识别了。
如果fs版本较高也可以改成 ignore_early_media=true 改成 ignore_early_media=consume 就可以了。
如果fs版本比较低
需要用 wait_for_answer_da 代替这个变量
比如 originate {execute_on_pre_answer=start_da2}sofia/external/da1@47.97.5.205:16080 wait_for_answer_da,transfer:’dest XML public’ inline

wait_for_answer_da支持设置等待超时,比如60秒不应答就挂机
originate {execute_on_pre_answer=start_da2}sofia/external/da1@47.97.5.205:16080 wait_for_answer_da:60000,transfer:’dest XML public’ inline

网络不好有丢包的线路需要加上 jitterbuffer_msec=60 开启抖动缓冲区
fs没公网IP的,需要 用wait_for_answer_da或者execute_on_pre_answer_sendrtp=playback::silence_stream://1000,来让fs向落地先发送RTP才可以。

配置

da2.conf.xml,设置空号识别模块参数。

<configuration name="da2.conf" description="Dial tone analysis">
<settings>

<!-- libda2配置文件路径 支持绝对路径和相对路径,相对路径就是fs的conf目录-->
<!-- <param name="configurefilename" value="/etc/da2.json"/> -->
<!-- <param name="configurefilename" value="da2.json"/> -->

<!-- libda2日志文件路径 支持绝对路径和相对路径,相对路径就是fs的log目录-->
<!-- <param name="logfilename" value="/var/log/da2.log"/> -->
<!-- <param name="logfilename" value="da2.log"/> -->


<!--
检测到什么信号停止检测
all busy ringback colorringback prompt sample silence 450hz unmatch
可以通过通道变量 da2_param_stoptone 覆盖这个的配置
-->
<param name="stoptone" value="sample busy"/>

<!-- 识别到 stoptone 包含的信号 自动挂断通话,默认true
可以通过通道变量 da2_param_autohangup 覆盖这个的配置
-->
<param name="autohangup" value="true"/>

<!-- 指定检测原因时挂断通话。 检测停止原因列表 all stoptone detecttime_timeout bus_close answer bug_add_fail break func_exit da1
和 autohangup 的区别,autohangup为真的时候 只有检测到stoptone包含的信号才挂断。
可以通过通道变量 da2_param_hangupcondition 覆盖这个的配置
-->
<param name="hangupcondition" value=""/>


<!-- 忽略那些的样本 用别名(alias) 英文的。多个用逗号隔开
可以通过通道变量 da2_param_ignoresamples 覆盖这个的配置
-->
<param name="ignoresamples" value=""/>


<!-- stoptone 包含unmatch时,连续多少次 unmatch,就执行挂机 -->
<param name="unmatchthreshold" value="2"/>

<!-- 样本库匹配命中多少次就停止da2,停止原因match,0:不启用这个配置 -->
<param name="matchthreshold" value="0"/>



<!-- 不设置recordpath 就不会录音 -->
<!-- <param name="recordpath" value="da_record"/> -->

<!-- 录音文件名格式 -->
<!-- <param name="recordformat" value="${strftime(%Y-%m-%d)}/${uuid}"/> -->

<!-- 把识别结果加到录音文件名 -->
<!-- <param name="recordrename" value="true"/> -->

<!--
电话接通后停止识别
可以通过通道变量 da2_param_answerautostop 覆盖这个的配置
-->
<param name="answerautostop" value="true"/>

<!--
最大检测时间 单位(秒)
可以通过通道变量 da2_param_maxdetecttime 覆盖这个的配置
-->
<param name="maxdetecttime" value="60"/>

<!--
最大分析时间,有声音后开始计算 单位(秒)
可以通过通道变量 da2_param_maxanalysistime 覆盖这个的配置
-->
<param name="maxanalysistime" value="60"/>

<!--
最大静音时间 单位(秒)
可以通过通道变量 da2_param_maxsilencetime 覆盖这个的配置
-->
<param name="maxsilencetime" value="30"/>


<!--
应答之后最大分析时间 单位(秒)
可以通过通道变量 da2_param_answer_after_maxanalysistime 覆盖这个的配置
answerautosotp设置为false,这个变量才生效
-->
<param name="answer_after_maxanalysistime" value="10"/>



<!--
提示音和彩铃最大分析时间 单位(秒)
可以通过通道变量 da2_param_ptd_maxanalysistime 覆盖这个的配置
-->
<param name="ptd_maxanalysistime" value="10"/>


<!--
检测时电话挂断,等待检测结果的时间 单位(毫秒)
-->
<param name="wait_result_timeout" value="500"/>



<!--
把检测结果写入通道变量da2_result
-->
<param name="da2_result" value="da2_result"/>


<!--
可以通过通道变量 da2_param_tone_busy_rule 覆盖这个的配置
短忙音标准 响350毫秒,停350毫秒,允许一定误差 响300-400 停250-400
-->
<!-- <param name="tone_busy_rule" value="300-400|250-400"/> -->

<!--
可以通过通道变量 da2_param_tone_congestion_rule 覆盖这个的配置
长忙音标准 响700毫秒,停700毫秒
-->
<!-- <param name="tone_congestion_rule" value="600-750|500-750"/> -->

<!--
可以通过通道变量 da2_param_tone_ringback_rule 覆盖这个的配置
回铃音标准 响1000毫秒,停4000毫秒
-->
<!-- <param name="tone_ringback_rule" value="900-1100|3000-4500"/> -->

<!--
可以通过通道变量 da2_param_tone_450hz_rule 覆盖这个的配置
自定义450hz信号源规则 ,一般快速检测第一个回铃音
-->
<!-- <param name="tone_450hz_rule" value="500-1500|1000-1000"/> -->

<!--
如果修改了fs代码导致挂断后da2不会停止,才需要打开这个
<param name="hook_state" value="true"/>
-->




</settings>


<hangupcause>
<!--


ID 状态 别名 描述
2 关机 power off 关机
3 空号 does not exist 空号
4 停机 out of service 停机
5 正在通话中 hold on 正在通话中
6 用户拒接 not convenient 用户拒接
7 无法接通 is not reachable 无法接通
8 暂停服务 not in service 暂停服务
9 用户正忙 busy now 用户正忙
10 拨号方式不正确 not a local number 拨号方式不正确
11 呼入限制 barring of incoming 呼入限制
12 来电提醒 call reminder 各类秘书服务
13 呼叫转移失败 forwarded 呼叫转移失败
14 网络忙 line is busy 网络忙
15 无人接听 not answer 无人接听
16 欠费 defaulting 欠费
17 无法接听 cannot be connected 无法接听
18 改号 number change 改号
19 线路故障 line fault 线路不能呼出,比如SIM卡欠费
20 稍后再拨 redial later 各种稍后再拨提示

根据识别结果发送不同的SIP挂断代码

<param name="2" value="432"/>
<param name="3" value="433"/>
<param name="4" value="434"/>
<param name="5" value="435"/>
<param name="6" value="436"/>
<param name="7" value="437"/>
<param name="8" value="438"/>
<param name="9" value="439"/>
<param name="10" value="440"/>
<param name="11" value="441"/>
<param name="12" value="442"/>
<param name="13" value="443"/>
<param name="14" value="444"/>
<param name="15" value="445"/>
<param name="16" value="446"/>
<param name="17" value="447"/>
<param name="18" value="448"/>
<param name="19" value="449"/>
<param name="20" value="450"/>

-->
</hangupcause>

<!--
通过 info 发送识别结果
header 设置info 消息头
compatibility 根据FS版本设置 40 或者 41


<sendinfo>
<param name="header" value="X-Genesys-Reason"/>
<param name="compatibility" value="41"/>
</sendinfo>
-->

</configuration>

da2.json 设置云服务器连接参数。

{
    "da" : 
    {
        "connect" : 
        {
            "addr" : "da2.ddrj.com",
            "port" : 9977,
            "reConnectInterval" : 15000,
            "timer" : 5000
        },
        "key" : "aaa"
    }
}

ESL获取识别结果

订阅esl 事件

java
setEventSubscriptions(EventFormat.PLAIN, “CHANNEL_HANGUP_COMPLETE CUSTOM da2”);

fs_cli测试 可以 /event CHANNEL_HANGUP_COMPLETE CUSTOM da2 订阅 事件

电话挂断获取检测结果

订阅 CHANNEL_HANGUP_COMPLETE,CHANNEL_HANGUPCOMPLETE 事件的通道变量,前面需要加 variable

检测停止后会设置以下通道变量

  • da2_finish_cause 停止原因
  • da2_all_tone 所以检测到信号类型位与结合 (详细见da2_tone说明)
  • da2_last_tone 检测到信号类型 (详细见da2_tone说明)
  • da2_last_sample_category 检测到样本ID (详细见category说明)
  • da2_last_sample_correction 检测的修正结果详细见category说明)
  • da2_result 根据以上信息设置一个方便读取的检测结果 (如果 da2_finish_cause的结果是 stoptone,那么界面显示 da2_tone,如果da2_tone等于sample,那么显示da2_sample_alias。)
  • da2_finish_cause 停止原因说明

    • unknown 未知
    • stoptone 检测到配置文件指定的信号,而停止的
    • detecttime_timeout 检测超时
    • answer 通话应答了
    • bus_close [start_da2]通话给挂断了 (就是还没检测到结果通话就给挂掉了,一般可能是呼叫失败)
    • bug_add_fail [start_da2]获取媒体失败
    • break [da2 app]执行了break
    • func_exit [da2 app]函数位置原因退出了
    • limit 分配并发失败,或者并发超过了。
  • da2_tone
    如果停止原因是 stoptone,可以获取通道变量da2_tone,继续获取具体信号类型。

    • da2_tone 数字值说明
      • DA_TONE_BUSYTONE = 0x01 //忙音
      • DA_TONE_RINGBACKTONE = 0x02 //回铃
      • DA_TONE_COLORRINGBACKTONE = 0x04 //彩铃(音乐)
      • DA_TONE_PROMPTTONE = 0x08 //提示音(说话声)
      • DA_TONE_SAMPLE = 0x10 //匹配到样本
      • DA_TONE_SILENCE = 0x20 //静音
      • DA_TONE_450HZ = 0x40 //450hz的嘟音
      • DA_TONE_UNMATCH = 0x80 //未匹配
    • da2_tone 文本值说明:
      • sample 匹配到样本
      • busy 忙音
      • ringback 回铃音
      • colorringback彩铃(音乐)
      • prompt 提示音(说话声)
      • silence 静音
      • 450hz 450hz的嘟音(如果持续的嘟声符合回铃或者忙音,也会有busy或者ringback)(比如忙音(嘟0.35秒,静音0.35秒),回铃音(嘟1秒,静音4秒))
      • unmatch 未匹配到样本库的样本(和prompt类似)
      • unkown 未知的值
  • da2_all_tone 把所有检测到的信号类型(da2_tone)位与的方式结合 - da2_last_tone 最后检测到的信号类型看da2_tone说明。
  • da2_last_sample_category 最后匹配到的样本ID
  • da2_last_sample_correction 如果最后的检测结果是通话中和用户正忙,并且之前检测到回铃或者彩铃,出现检测结果的呼叫时间大于45秒,设置是为 not answer(无人接听),小于 45秒 设置为 not convenient(用户拒接)

如果有a-leg,会在a-leg通道设置以下通道变量

  • bridge_da2_finish_cause
  • bridge_da2_all_tone
  • bridge_da2_last_tone
  • bridge_da2_last_sample_category
  • bridge_da2_last_sample_correction

实时获取检测结果 订阅

Event-Subclass: da2 Event-Name: CUSTOM

  • 事件和通道变量内容
    • Event-Subclass da2
    • da2_point_begin
    • da2_point_end
    • da2_tone
    • da2_sample_uniqueid
    • da2_sample_name
    • da2_sample_alias
    • da2_sample_brief
    • da2_sample_detail
    • da2_sample_language
    • da2_sample_category
  • 具体含义

    • da2_point_begin: 识别到结果的开始位置
    • da2_point_end: 识别到结果的结束位置
    • tone: 识别到的信号音类型,具体看 da2_tone 说明
    • 如果 tone是sample 才会设置以下变量
    • uniqueid: 样本ID
    • name: 样本名字,具体看 category。
    • alias: 样本别名
    • brief: 样本文本简介
    • detail: 样本详细内容
    • language: 样本语言类型 可能是 普通话|方言|英语 | zh | en 等
    • source: 样本来源
    • number: 样本来源的号码
    • recognize: 算法引擎
    • accuracy: 识别结果的准确性, LOOSE(不准),INACCURACY(一般),ACCURACY(准确),只有ACCURACY(准确)的才会通知ESL和执行挂机

    • category: 样本分类

      2 	关机 		power off 		关机
      3 空号 does not exis 空号
      4 停机 out of service 停机 (欠费很久给停机了)
      5 正在通话中 hold on 正在通话中,大部分交换机用户拒接,无应答,也是返回正在通话中。
      6 用户拒接 not convenient 用户拒接,可能用户直接挂断吧,很少见,大部分交换机都是直接返回通话中。
      7 无法接通 is not reachable 无法接通,可能手机没信号
      8 暂停服务 not in service 暂停服务,刚欠费,给限制呼入了。
      9 用户正忙 busy now 用户正忙 ,没开通来电等待时,用户已经在通话中。
      10 拨号方式不正确 not a local number 拨号方式不正确,不符合线路拨号要求,一般是外地手机要加0.
      11 呼入限制 barring of incoming 呼入限制 可能是没开通语音功能,或者欠费,暂停服务等。
      12 来电提醒 call reminder 各类秘书服务,和各类来电提醒,语音信箱,语音留言。
      13 呼叫转移失败 forwarded 呼叫转移失败 ,被叫开通了呼叫转移,转移的目标手机呼叫失败。
      14 网络忙 line is busy 网络忙 很少见,一般是线路局端出故障了。
      15 无人接听 not answer 无人接听,很少见。
      16 欠费 defaulting 欠费 ,被叫欠费了,也可能是主叫欠费了,样本库没区分那么细致。
      17 无法接听 cannot be connected 无法接听,很少见。和无人接听,不清楚2个提示音的具体区别。
      18 改号 number change 改号,有的地方有改号提示,就是用户换号了。
      19 线路故障 line fault 线路不能呼出,比如SIM卡欠费
      20 稍后再拨 redial later 各种稍后再拨提示,一般是用户忙,通话中,关机等之后的提示音(这个会导致没法细分,可以在da2.conf.xml,配置忽略这个分类)完整的提示一般是,你拨打的用户正忙,请稍后在拨,或者你拨打的正在通话中,请稍后在拨

常见问题

空号识别,本质是语音识别,所有识别结果,都是通过分析声音获取的,和信令无关。

比如识别结果是空号,那么这个号码是不是真的是空号呢,这个不能保证的,只能保证,系统呼叫这个号码听到的声音和空号样本匹配上了。

比如系统检测到是空号,用自己手机呼叫这个号码是正常的,因为外呼的线路呼叫接续出了问题也可能提示空号的。

有以下几个可能

1 线路问题,就是 系统使用的线路有问题,把正常的号码播放了空号的提示音。 (大部分是这样的问题)
2 运营商问题, 就是运营商有时候会把正常的号码播放空号的声音,重新呼叫一次又正常了,这样的也属于线路问题(外呼发起的运营商和被叫号码归属地运营商接续出问题)。 (小概率可能出现)
3 样本库问题,就是系统把一个其他样本标志成空号。 (非常小概率,默认样本库基本上没这样的问题)

怎么排查问题,录音, 1 fs系统去录音未接通之前的声音,2 da2.conf.xml 配置去打开录音 3 通过命令动态开录音 da2 record /tmp (第三个参数录音目录,可以自己设置),

听一下录音,就可以知道是 线路问题,还是样本库问题。

fs_cli 里面执行

显示状态,可以判断和daserver连接是否正常。看输出的 ready是否1,limit是否正确。
da2 show

打开录音
da2 record /tmp

关闭录音
da2 record

界面展示问题

如果信令结果是呼叫应答的,分类到应答
如果呼叫结果是没应答的
先根据 da2_finish_cause 来判断,如果 da2_finish_cause的结果是 stoptone,那么界面显示 da2_tone,如果da2_tone等于sample,那么显示da2_sample_name。
2.13版本开始添加了变量da2_result,就是这样规则来设置的。

da2_finish_cause的其他检测原因为了更友好的显示,建议如下显示
bus_close 如果呼叫持续时间小于30秒,分类到呼叫失败,如果大于30秒,分类到未识别
detecttime_timeout 如果da2_last_tone有数据,就根据 da2_last_tone 来显示,如果没有,就分类到未识别。
answer 分类到应答
其他值,不应该出现,直接归类到其他。

建议在CHANNEL_HANGUP_COMPLETE 事件直接根据da2_result来分类。 如果变量 Caller-Channel-Answered-Time有值 ,就是电话给接通了,号码状态为接通

da2_result的结果如下

  • 未启动strart_da,比如没收到到183,或者daserver没开启。不会设置da2_result这个变量,如果 variable_duration小于20, 建议号码状态为呼叫失败, 大于20 时 Caller-Channel-Progress-Media-Time为0建议号码状态为 无早期媒体,Caller-Channel-Progress-Media-Time不是0,建议号码状态为 检测未开启

  • 和样本库匹配上了,建议号码状态直接显示为da2_result的值。

    • power off 关机
    • does not exis 空号
    • out of service 停机 (欠费很久给停机了)
    • hold on 正在通话中,大部分交换机用户拒接,无应答,也是返回正在通话中。
    • not convenient 用户拒接,可能用户直接挂断吧,很少见,大部分交换机都是直接返回通话中。
    • is not reachable 无法接通,可能手机没信号
    • not in service 暂停服务,刚欠费,给限制呼入了。
    • busy now 用户正忙 ,没开通来电等待时,用户已经在通话中。
    • not a local number 拨号方式不正确,不符合线路拨号要求,一般是外地手机要加0.
    • barring of incoming 呼入限制 可能是没开通语音功能,或者欠费,暂停服务等。
    • call reminder 各类秘书服务,和各类来电提醒,语音信箱,语音留言。
    • forwarded 呼叫转移失败 ,被叫开通了呼叫转移,转移的目标手机呼叫失败。
    • line is busy 网络忙 很少见,一般是线路局端出故障了。
    • not answer 无人接听,很少见。
    • defaulting 欠费 ,被叫欠费了,也可能是主叫欠费了,样本库没区分那么细致。
    • cannot be connected 无法接听,很少见。和无人接听,不清楚2个提示音的具体区别。
    • number change 改号,有的地方有改号提示,就是用户换号了。
    • line fault 线路不能呼出,比如SIM卡欠费
    • redial later 各种稍后再拨提示,一般是用户忙,通话中,关机等之后的提示音(这个会导致没法细分,可以在da2.conf.xml,配置忽略这个分类)完整的提示一般是,你拨打的用户正忙,请稍后在拨,或者你拨打的正在通话中,请稍后在拨
  • 检测到信号音了,如果是busy,ringback,colorringback,silence 建议号码状态直接显示为这几个值。其他值号码状态显示为 未接通或者未识别

    • busy 忙音
    • ringback 回铃音
    • colorringback彩铃(音乐)
    • prompt 提示音(说话声)
    • silence 静音
    • 450hz 450hz的嘟音(如果持续的嘟声符合回铃或者忙音,也会有busy或者ringback)(比如忙音(嘟0.35秒,静音0.35秒),回铃音(嘟1秒,静音4秒))
    • unmatch 未匹配到样本库的样本(和prompt类似)
  • 其他原因导致检测停止了 ,detecttime_timeout和 bus_close如果 variable_duration 小于20, 建议号码状态为呼叫失败, 大于20 建议号码状态为 未接通或者未识别。answer就是应答了,limit并发不够。其他统一为 未识别。

    • unknown 未知
    • detecttime_timeout 检测超时
    • answer 通话应答了
    • bus_close [start_da2]通话给挂断了 (就是还没检测到结果通话就给挂掉了,一般可能是呼叫失败)
    • limit 分配并发失败,或者并发超过了。
    • 这几个基本上不会出现
      • bug_add_fail [start_da2]获取媒体失败
      • break [da2 app]执行了break
      • func_exit [da2 app]函数位置原因退出了