浅谈RFC 2833/RFC 4733中DTMF的细节
更新时间:2023-02-14

前言

IETF RFC 2833/4733就像它们的标题(RTP Payload for DTMF Digits, Telephony Tones, and Telephony Signals),是定义采用RTP协议传输DTMF、电话tone信号音和其他电话相关的信令的关键规范。RFC 4733是RFC 2833的更新版本,在原来的基础上定义更加准确(比如:DTMF在4733中定义为0-15,而在2833中定义为0-15,再加上16表示flash),概念更加清晰。

在毅航互联从事的客服或者呼叫中心领域,本文档定义的DTMF传输非常重要。因此,本文针对DTMF相关部分做一些较为详细的说明,为在对接中处理DTMF问题提供一些帮助。

RFC 4733载荷既然是一种RTP Payload,就存在发起者和接收者之间的协商,协商过程一般随着SIP信令的呼叫过程在SDP中传递和协商。

DTMF信号是固定的,并不因为编解码的不同而传递不同的编码数据(这点不像语音编码),但是,具体细节也会受到编解码参数的影响:采样率会影响Duration、ptime会影响包间隔等等。

最奇葩的是ITU G.722编码,也就是号称高清语音的那个编码:它的采样率是16000Hz,编码后的数据量和G.711相同。但是为了兼容性,Payload却采用8000Hz规则编码。

1 载荷格式协商

DTMF载荷作为audio(音频)载荷的一种,在会话的offer/answer流程中协商。协商方式有很多种:比如采用H.323/SIP信令或者webrtc中的https等等。

本文档采用SIP信令作为协商的信令,在SDP中承载。

1.1 offer/answer

offer/answer是SIP类能力交换的术语:

offer是发起端主动提供自身的能力:比如payload类型、采样率等信息。

answer是应答端根据发起端能力和自身能力的交集以及其他规则(比如:优选8000Hz的DTMF)得到的最终能力结果。此结果是两端都接受的共同语言,用于后续的DTMF传输。

1.1.1 支持或不支持的标志

通过查询SDP中audio payload是否有telephone-event就可以很容易判断是否支持RFC 4733的DTMF。

如下图,在audio的payload中并没有查询到telephone-event,因此,是不支持RFC 4733的DTMF的,只能够接受带内的DTMF:

audio有8、0和18三种载荷,分别表示PCMA、PCMU和G729,并没有telephone-event有关的信息。

而下图,可以根据audio的载荷查询到telephone-event,表示支持RFC 4733的DTMF:


从上图可以看到96和97分别表示16000Hz和8000Hz的DTMF。

注:由于存在宽带编码EVS和AMR_WB,因此可以提供宽带相关的DTMF。


1.1.2 误解或者无效

由于对接或者变换的原因,可能在某些情况下,上述的信息并不完整,这种情况并不能够作为RFC 4733 DTMF的标志。

比如下图,可以看到有telephone-event字符串,但是,在audio行并没有相应的payload类型,也是不具有RFC 4733 DTMF:


audio行只有8和9两个payload值。

比如下图,可以在audio行看到101的payload值,但是,后续并没有rtpmap来解释101为telephone-event,也是表示不存在RFC 4733 DTMF:

或者是payload只和rtpmap的解析对不上,也是一种错误:


1.2参数

1.2.1 telephone-event

在SDP中,用rtpmap来解释RFC 4733编码的DTMF   。

如:rtpmap:101 telephone-event/8000

101表示RTP的payload值,为101。通过此值可以将DTMF和语音RTP分离出来。

8000表示采样率,像alaw/G.729等窄带编码为8000Hz的采样率。而像AMR WB/EVS等宽带编码就是16000Hz的采样率。

如果像下图的OPUS编码,也许是48000Hz的超宽带采样率。


1.2.2采样率的含义

采样率表示每秒产生多少编码bits,这会影响RTP包中的时间戳(timestamp)和RFC 4733 DTMF包的持续周期(duration)。

时间戳和时间戳+持续周期表示流的持续时间点。由产生者生成,接受者根据这些时间点还原流。

以语音流为例,大部分的RTP包间隔为20ms(通过抓包的时间戳可以看出),RTP时间戳的差值为20 * (采样率/ 1000)。

所以8000Hz采样率,时间戳差值为160;而16000Hz采样率为320。

由于DTMF的持续时间由duration来表示,也可以看到同样的情况。

这些信息可以用于帮助分析RTP包和RFC 4733包是否存在异常。


2 载荷格式说明

RFC 2833/RFC 4733 DTMF包在RTP包中传输,因此遵循RTP的一般规则。

虽然RTP包结构定义有非常大的灵活性,比如:Externsion(扩展)、CSRC(参与源标识符)等。但是在具体的实践中缺尽量采用最小功能,保证对接的兼容性。像Externsion/CSRC等基本上不使用。


2.1 RTP载荷格式

RTP载荷的详细说明见RFC 3550,这里只做简单的说明。还是用一个抓包的截图会非常明确,如下图:



具体的字节流如下图:


Version(V):RTP包的当前版本号,为2;

Padding(P):填充位。如果为1,表示在载荷的尾部添加一个或多个填充字节,这些填充字节不属于RTP载荷的部分,只是为了某些设备的32bits对齐,某些加密算法的要求或下层传输机制的要求。填充字节的最后一个字节表示填充的字总节数(包括此字节)。

Extension(X):扩展位,表示RTP头是否带有扩展头。如果此位为1,会有RTP扩展头将追加在CSRC的后面。

CSRC Count(CC):贡献源标示符(Contributing Source Identifier)的个数(0~15),一般为0,相应的CSRC部分也不出现在RTP头中。

Marker(M):标志位。用于标示RTP包流中的事件边界,由不同的Profile来定义此位的用法,如RFC 2833使用此位定义DTMF事件的开始和结束。

Payload Type(PT):载荷类型,此域指定RTP载荷的格式。ietf用大量的文档来规定PT的值和相应的载荷格式,常见的是RFC 3551(RTP Profile for Audio and Video Conferences with Minimal Control)。RFC 3551将载荷类型分成静态载荷类型和动态载荷类型。静态载荷类型是预分配的载荷类型(如PCMA是8,G.729是18等),动态载荷(96~127)是非预分配的载荷类型,使用中必须通过其他协议方式(如SIP中的SDP方式)确定载荷的内容。一般来说,单个RTP包只包含一种PT类型的载荷,但RED(冗余载荷)载荷除外。冗余载荷RTP也占用一个PT值(动态载荷类型),但载荷内容可以是多个载荷类型的组合,具体见RFC 2198。

Sequence Number:顺序号。会话启动时,顺序号赋予随机数,然后每发送一个RTP包加1。接收端利用顺序号检测丢包或者包的乱序。

timestamp:时间戳。时间戳反映RTP载荷第一个字节采样的时刻。如果RTP周期性产生,采样时刻由采样时钟决定而不是系统时钟。如固定率的音频,每个采样周期都导致采样时钟加一。因此,如果一个采样块包含160个采样周期,时间戳将增加160。时间戳的初始值也像顺序号一样采样随机数。

Synchronization Source(SSRC) Identifier:同步源标识符。同步源是设置RTP包的顺序号和时间戳的实体,基本上是RTP包的发送者。

Contributing Source(CSRC) Identifiers:贡献源标识符,用于标示会话的贡献者。CSRC由Mixer(混音器)插入,标示媒体的源。如果CC为0,此域不存在。

Payload:载荷。由Payload Type定义的媒体编码数据。


2.2 RFC 4733 DTMF包格式

DTMF事件包封装在RTP中如下图:

在RFC 2833/RFC 4733中的字节流如下图:


Event:事件类型,指定电话信令。

Event值和相应的含义如下表:

事件

含义

DTMF事件

0 -- 9

0 -- 9


*

10


#

11


A -- D

12 – 15


End(E):事件结束指示位;

Reserved(R):保留位,现在不使用;

volume:音量。对于像DTMF等用音调重现的事件才有效,单位dBm0,取值0~-63;

duration:事件的持续周期,与RTP timestamp单位相同。

2.3语音流变化

从语音流切换到DTMF和从DTMF切换回语音,包都具有一些特点。

2.3.1 语音流切换到DTMF

当发起端从语音流中检测到DTMF按键,将把RTP流切换到DTMF模式,并送出DTMF RTP包。

在送出第一个DTMF包和后续的DTMF包的时候,会冻结RTP时间戳,不再变化。DTMF的持续时间由duration表示。

同时,第一个包会将RTP Mark位设置为1,表示有事件发生。


时间戳和上一个包相同,如下图:

时间戳不完全都是这样,某些实现会采用DTMF检测到的时间作为时间戳,第一个DTMF的duration会反映出关联关系。

2.3.2 DTMF切换到语音流

当从DTMF切换到语音流时,最后一个事件一般会重复发送,避免DTMF包丢失带来的问题。如下图:



相同的包重发了两次。

3 一些编码的例子

由于毅航互联的研发会深入到编解码的实现,在对接和实现的过程中有一些记录。在本章展示一些纪录,供参考。

3.1 Duration为0

在RFC 2833中对duration并没有做太多的说明,但是在RFC 4733中,duration为0是有明确的说明:用来表示状态。因此,一般实现不将duration设置为0。如下图:

并不是兼容性较好的实现。


3.2包周期的影响

比如在信令offer/answer中协商为30ms的iLBC,语音包周期变成了30ms,RTP的时间戳也就是按照240的间隔往前推动。


但是下图并不是按30ms来构造包:


3.3 G.722编码

ITU G.722采用16000Hz的采样率,而根据RFC 3550的定义,为了兼容性,采用8000Hz来构造数据。

信令反映了这种实事:

RTP的时间戳和DTMF的duration也同样反映这种事实。



返回