前言
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规则编码。
DTMF载荷作为audio(音频)载荷的一种,在会话的offer/answer流程中协商。协商方式有很多种:比如采用H.323/SIP信令或者webrtc中的https等等。
本文档采用SIP信令作为协商的信令,在SDP中承载。
offer/answer是SIP类能力交换的术语:
offer是发起端主动提供自身的能力:比如payload类型、采样率等信息。
answer是应答端根据发起端能力和自身能力的交集以及其他规则(比如:优选8000Hz的DTMF)得到的最终能力结果。此结果是两端都接受的共同语言,用于后续的DTMF传输。
通过查询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。
由于对接或者变换的原因,可能在某些情况下,上述的信息并不完整,这种情况并不能够作为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的解析对不上,也是一种错误:
在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的超宽带采样率。
采样率表示每秒产生多少编码bits,这会影响RTP包中的时间戳(timestamp)和RFC 4733 DTMF包的持续周期(duration)。
时间戳和时间戳+持续周期表示流的持续时间点。由产生者生成,接受者根据这些时间点还原流。
以语音流为例,大部分的RTP包间隔为20ms(通过抓包的时间戳可以看出),RTP时间戳的差值为20 * (采样率/ 1000)。
所以8000Hz采样率,时间戳差值为160;而16000Hz采样率为320。
由于DTMF的持续时间由duration来表示,也可以看到同样的情况。
这些信息可以用于帮助分析RTP包和RFC 4733包是否存在异常。
RFC 2833/RFC 4733 DTMF包在RTP包中传输,因此遵循RTP的一般规则。
虽然RTP包结构定义有非常大的灵活性,比如:Externsion(扩展)、CSRC(参与源标识符)等。但是在具体的实践中缺尽量采用最小功能,保证对接的兼容性。像Externsion/CSRC等基本上不使用。
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定义的媒体编码数据。
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单位相同。
从语音流切换到DTMF和从DTMF切换回语音,包都具有一些特点。
当发起端从语音流中检测到DTMF按键,将把RTP流切换到DTMF模式,并送出DTMF RTP包。
在送出第一个DTMF包和后续的DTMF包的时候,会冻结RTP时间戳,不再变化。DTMF的持续时间由duration表示。
同时,第一个包会将RTP Mark位设置为1,表示有事件发生。
时间戳和上一个包相同,如下图:
时间戳不完全都是这样,某些实现会采用DTMF检测到的时间作为时间戳,第一个DTMF的duration会反映出关联关系。
当从DTMF切换到语音流时,最后一个事件一般会重复发送,避免DTMF包丢失带来的问题。如下图:
相同的包重发了两次。
由于毅航互联的研发会深入到编解码的实现,在对接和实现的过程中有一些记录。在本章展示一些纪录,供参考。
在RFC 2833中对duration并没有做太多的说明,但是在RFC 4733中,duration为0是有明确的说明:用来表示状态。因此,一般实现不将duration设置为0。如下图:
并不是兼容性较好的实现。
比如在信令offer/answer中协商为30ms的iLBC,语音包周期变成了30ms,RTP的时间戳也就是按照240的间隔往前推动。
但是下图并不是按30ms来构造包:
ITU G.722采用16000Hz的采样率,而根据RFC 3550的定义,为了兼容性,采用8000Hz来构造数据。
信令反映了这种实事:
RTP的时间戳和DTMF的duration也同样反映这种事实。