DTMF从2833到inband的方案
概述
freeswitch是一款简单好用的VOIP开源软交换平台。
之前的文章中介绍过通过dialplan拨号计划配置的方法,实现2833到inband的转换,但是实际生产环境中的场景会更复杂,无法预先在dialplan中设置好相关参数和函数。
环境
CentOS 7.9
freeswitch 1.10.7
问题描述
在与运营商的对接过程中,因为对传统PSTN线路的兼容,被叫侧并不是所有的号码都支持2833,B路响应的SDP中会没有rfc2833的媒体描述,也可能183SDP中支持2833,但是200SDP中不支持2833。
fs对于该场景并没有简单的配置方案,默认的处理逻辑会使用INFO消息来转发DTMF,但是运营商并不支持该方式。
所以,我们需要一种简单的配置即可实现的rfc2833到inband的DTMF转发模式。
默认流程
从SDP媒体协商的日志中看到,协商过程都是在“switch_core_media_negotiate_sdp”函数实现的。
fs默认的dtmf匹配流程如下,略过了正常的rfc2833过程。
1,检查B路响应SDP中的“telephone-event”并根据rate匹配一个最佳te。switch_core_media.c:5964。
2,如果没有匹配到最佳te,检查vars.xml中的“rtp_liberal_dtmf”配置为true,则直接使用本地te作为最佳te。switch_core_media.c:6003。
3,如果仍然没有最佳te,检查通道变量“rtp_info_when_no_2833”不是false,则使用INFO模式转发DTMF,否则设置B路的“dtmf_type”为none。
修改方案
修改后的dtmf匹配流程。
1,检查B路响应SDP中的“telephone-event”并根据rate匹配一个最佳te。switch_core_media.c:5964。
2,如果没有匹配到最佳te,检查vars.xml中的“rtp_liberal_dtmf”配置为true,则直接使用本地te作为最佳te。switch_core_media.c:6003。
3,如果有最佳te,走第4步,没有最佳te,走第5步。
4,在设置协商好的te payload之后,增加逻辑,如果在183的协商中已经设置了inband模式,后续的update协商中需要取消inband模式的函数设置。
5,检查通道变量“rtp_info_when_no_2833”不是false,则使用INFO模式转发DTMF。通道变量“rtp_info_when_no_2833”不是false,设置B路的“dtmf_type”为inband,设置B路通道变量“spandsp_dtmf_rx_filter_dialtone”为true,设置B路在answer后调用app函数“deduplicate_dtmf”和“spandsp_start_dtmf”,设置A路在answer后调用app函数“start_dtmf_generate”。
代码如下,switch_core_media.c:6009行开始,红色部分为修改代码。
if (best_te) {
smh->mparams->te_rate = best_te_rate;
if (smh->mparams->dtmf_type == DTMF_AUTO || smh->mparams->dtmf_type == DTMF_2833 ||
switch_channel_test_flag(session->channel, CF_LIBERAL_DTMF)) {
if (sdp_type == SDP_TYPE_REQUEST) {
smh->mparams->te = smh->mparams->recv_te = (switch_payload_t) best_te;
switch_channel_set_variable(session->channel, "dtmf_type", "rfc2833");
smh->mparams->dtmf_type = DTMF_2833;
} else {
smh->mparams->te = (switch_payload_t) best_te;
switch_channel_set_variable(session->channel, "dtmf_type", "rfc2833");
smh->mparams->dtmf_type = DTMF_2833;
}
}
if (a_engine->rtp_session) {
switch_rtp_set_telephony_event(a_engine->rtp_session, smh->mparams->te);
switch_channel_set_variable_printf(session->channel, "rtp_2833_send_payload", "%d", smh->mparams->te);
switch_rtp_set_telephony_recv_event(a_engine->rtp_session, smh->mparams->recv_te);
switch_channel_set_variable_printf(session->channel, "rtp_2833_recv_payload", "%d", smh->mparams->recv_te);
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s Set 2833 dtmf send payload to %u recv payload to %u\n",
switch_channel_get_name(session->channel), smh->mparams->te, smh->mparams->recv_te);
//add by zr 20241018, for 2833 to inband, update method
//如果在183的协商中已经设置了inband模式,后续的update协商中需要取消inband模式的函数设置
if (switch_true(switch_channel_get_variable(session->channel, "inband_flag")))
{
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "reset inband_flag and channel variables.\n");
//B路
switch_channel_set_variable(session->channel, "inband_flag", NULL);
switch_channel_set_variable(session->channel, "spandsp_dtmf_rx_filter_dialtone", NULL);
switch_channel_set_variable(session->channel, "execute_on_answer_101", NULL);
switch_channel_set_variable(session->channel, "execute_on_answer_102", NULL);
//A路,2833 to inband
if( switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS )
{
switch_channel_set_variable(other_session->channel, "execute_on_answer_101", NULL);
switch_core_session_rwunlock(other_session);
}
}
} else {
/* by default, use SIP INFO if 2833 is not in the SDP */
if (!switch_false(switch_channel_get_variable(channel, "rtp_info_when_no_2833"))) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "No 2833 in SDP. Disable 2833 dtmf and switch to INFO\n");
switch_channel_set_variable(session->channel, "dtmf_type", "info");
smh->mparams->dtmf_type = DTMF_INFO;
smh->mparams->recv_te = smh->mparams->te = 0;
} else {
// switch_channel_set_variable(session->channel, "dtmf_type", "none");
// smh->mparams->dtmf_type = DTMF_NONE;
// smh->mparams->recv_te = smh->mparams->te = 0;
//add by zr 20241018, for 2833 to inband, update method
switch_channel_set_variable(session->channel, "dtmf_type", "inband");
smh->mparams->dtmf_type = DTMF_AUTO;
smh->mparams->recv_te = smh->mparams->te = 0;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "set inband_flag, No 2833 in SDP. Disable 2833 dtmf and switch to INBAND.\n");
//TODO: add inband dtmf
//A路,2833 to inband
if( switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS )
{
switch_channel_set_variable(other_session->channel, "execute_on_answer_101", "start_dtmf_generate");
switch_core_session_rwunlock(other_session);
}
//B路,inband to 2833
switch_channel_set_variable(session->channel, "inband_flag", "true");
switch_channel_set_variable(session->channel, "spandsp_dtmf_rx_filter_dialtone", "true");
switch_channel_set_variable(session->channel, "execute_on_answer_101", "deduplicate_dtmf");
switch_channel_set_variable(session->channel, "execute_on_answer_102", "spandsp_start_dtmf");
}
}
代码编译安装后,修改fs配置,B路使用inband模式。
vars.xml
<X-PRE-PROCESS cmd="set" data="rtp_liberal_dtmf=false"/>
dialplan
<action application="export" data="rtp_info_when_no_2833=false" />
上面的配置可以适配各种情况,当A路支持rfc2833,B路不支持2833的时候。
B路如果仍然希望使用2833则设置“rtp_liberal_dtmf”为true。
B路如果希望使用INFO模式则设置“rtp_liberal_dtmf”为fase,“rtp_info_when_no_2833”为true。
B路如果希望使用inband模式则设置“rtp_liberal_dtmf”为fase,“rtp_info_when_no_2833”为false。
测试
sip媒体的协商过程比较复杂,场景较多,下面列出了各种协商过程和预期结果。
1,200sdp带2833。2833双向正常
2,200sdp不带2833。2833 to inband双向正常
3,183sdp带2833,200不带sdp。2833双向正常
4,183sdp不带2833,200不带sdp。2833 to inband双向正常
5,183sdp不带2833,200sdp不带2833。2833 to inband双向正常
6,183sdp带2833,200sdp不带2833。2833双向正常
7,183sdp不带2833,200sdp带2833。2833 to inband双向正常
8,183sdp带2833,200sdp带2833。2833双向正常
9,183sdp带2833,update sdp带2833,200sdp带2833。2833双向正常
10,183sdp不带2833,update sdp带2833,200sdp带2833。2833双向正常
11,183sdp带2833,update sdp不带2833,200sdp带2833。2833 to inband双向正常
12,183sdp带2833,update sdp带2833,200sdp不带2833。2833双向正常
13,183sdp不带2833,update sdp不带2833,200sdp带2833。2833 to inband双向正常
14,183sdp不带2833,update sdp带2833,200sdp不带2833。2833双向正常
15,183sdp带2833,update sdp不带2833,200sdp不带2833。2833 to inband双向正常
16,183sdp不带2833,update sdp不带2833,200sdp不带2833。2833 to inband双向正常
17,183sdp带2833,update sdp带2833,200不带sdp。2833双向正常
18,183sdp不带2833,update sdp带2833,200不带sdp。2833双向正常
19,183sdp带2833,update sdp不带2833,200不带sdp。2833 to inband双向正常
20,183sdp不带2833,update sdp不带2833,200不带sdp。2833 to inband双向正常
21,A路不带2833的以上20种场景。
经过测试,上诉各场景的预期结果基本满足。从媒体抓包中,可以分别对A路的rtpevent和B路的inband媒体流一一对应起来。

总结
根据sip协议的规范,在媒体协商的过程中,如果已经有183SDP或update的SDP了,则200SDP会被忽略。
上述流程中,对于AB路的其中一路固定为rfc2833的媒体协商是满足的,但是对于AB两路的DTMF模式都不固定的场景仍然有遗留问题,因为场景限制关系,暂不讨论。
上述流程中,对于B路的inband使用了“spandsp_start_dtmf”转换为rfc2833,但是该函数有一定缺陷,转换后的A路媒体流中既有rfc2833,又有少量遗留的inband(擦除不完全,遗留40ms,仍然可以被检测出DTMF),该缺陷会在后续的文章中解决。
空空如常
求真得真
DTMF从2833到inband的方案的更多相关文章
- FreeSWITCH收到重复的DTMF信号
一.背景 用户是运营商手机,拨打的是运营商的固话号码进入的FreeSWITCH的IVR,进入IVR语音播报后,按指定的分机号呼相关人员. 二.现象 用户反映拨打124870找不到指定人员,以前是正常的 ...
- DTMF在VOIP中的解决方案
双音多频DTMF(Dual Tone Multi-Frequency)信令,因其提供更高的拨号速率,迅速取代了传统转盘式电话机使用的拨号脉冲信令.DTMF也应用在交互式控制中,诸如语言菜单.语言邮件. ...
- HTML5实现DTMF(电话拨号按键信号)解码、编码,代码简单易于移植
目录 一.前言 1.1 HTML5实现DTMF的一些动机 1.2 一些有效场景 (1) 10086 (2) 软电话 (3) 小玩具 二.DTMF频率按键对照表 三.DTMF信号解码 得到按键值 3.1 ...
- SDN组网相关解决方案
http://www.muzixing.com/pages/2016/02/14/sdnzu-wang-xiang-guan-jie-jue-fang-an.html 2016-02-14 by mu ...
- DTMF三种模式(SIPINFO,RFC2833,INBAND)
转自:http://www.tuicool.com/articles/n6Vb2iJ 1.DTMF(双音多频)定义:由高频音和低频音的两个正弦波合成表示数字按键(0~9 * # A B C D). 2 ...
- DTMF的原理分析
转自:http://blog.csdn.net/wangwenwen/article/details/8264925 1. DTMF原理 DTMF(Double Tone MulitiFrequenc ...
- ADT公司G729 方案指标
ADT公司G729 方案指标 G.729 Voice Compression Algorithm and its many annexes G.729 is used in wireless voic ...
- 基于HTML5的Web跨设备超声波通信方案
前言:Chirp在iPhone上掀起了有声传输文件的序幕,我们再也不需要彩信.蓝牙配对.IM来传送数据.它通过“叽叽喳喳”的小鸟叫声来分享数据,简单有趣,而且可以快速的实现一对多的分享. 此外支付宝曾 ...
- FreeSWITCH检测DTMF数据的方法
一.RFC2833 1. 介绍: RFC2833为带内检测方式,通过RTP传输,由特殊的rtpPayloadType即TeleponeEvent来标示RFC2833数据包.同一个DTMF按键通常会对应 ...
- 1、Codevs 必做:2833、1002、1003、2627、2599
2833 奇怪的梦境 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description Aiden陷入了一个奇怪的梦境:他被困 ...
随机推荐
- 树莓派cm4更新bootloader(eeprom)
cm4不能在系统里通过 rpi-eeprom-update 指令进行升级,也不能通过 update 进行更新,只能通过recovery模式进行更新. 以下为Windows的升级方式. Setp 1:下 ...
- 函数 | Go语言
函数 不支持命名参数与默认值 可变参数 求多个int的和 func IntSum(x ...int) { sum := 0 for _, v := range x { sum += v } retur ...
- 在已有的项目中使用vuiew ui库
官方提供了三种方式,但是我觉得在已有的项目中使用是比较常见的 我在刚开始使用的时候不知道如何使用,我希望这个对大家有点帮助,特此来记录下! 我用的是创建了一个uview插件的项目,然后把里面uview ...
- linux-杂项
1.常用基础 防火墙systemctl status firewalldsystemctl stop firewalldsystemctl start firewalld find / -size + ...
- jwt-shiro-springsecurity-oauth2对比
1 实现token的方式概述 在cookie\session\token辨析一文已经知道了token这个概念,里面简单说明了token的组成就是数据+签名,给出了token实现身份验证的流程,并且详细 ...
- Golang-接口7
http://c.biancheng.net/golang/interface/ Go语言接口声明(定义) Go语言不是一种 "传统" 的面向对象编程语言:它里面没有类和继承的概念 ...
- 链路追踪 Sleuth 和 Zipkin
微服务的链路追踪概述: 分布式链路追踪(Distributed Tracing),就是将一次分布式请求还原成调用链路,进行日志记录,性能监控并将一次分布式请求的调用情况集中展示.比如各个服务节点上的耗 ...
- Redis的高可用?(主从、哨兵、集群)
高可用有两个含义:一是数据尽量不丢失,二是保证服务尽可能可用. AOF 和 RDB 数据持久化保证了数据尽量不丢失,那么多节点来保证服务尽可能提供服务. 一般在实际生产中,服务不会部署成单节点,主要是 ...
- nginx里面的路径定位关键词root、alias
nginx里面的路径定位关键词root.alias是有区别的: 设置请求资源的目录root / alias root:设置请求的根目录 语法 root path; 默认值 root html; 位置 ...
- ctfshow--web9 md5二进制格加密的绕过
dirsearch 扫到robots文件 查看一下 发现有个index.phps文件 访问这个index.phps,可以下载下来 我们来审计一下这里的代码 <?php $flag="& ...