完整SIP:SDP媒体协商概论-SDP协商模式详解
原文出处:完整SIP/SDP媒体协商概论-SDP协商模式详解-offer初始化流程
当我们讨论SIP或者SDP的一些技术话题时,SDP的协商是一个绕不过的话题。具体的协商机制涉及了多个方面的内容。在我们的讨论中,笔者将会针对两个比较重要的话题进行讨论,一个是SDP offer/answer 模式,另外一个是在NAT场景中的SDP offer/answer交互模式拓展-ICE。在本章节中,我们将首先讨论SDP offer/answer交互模式,具体内容包括:offer/answer的背景介绍,基本操作,如何实现初始化的offer,重点讨论offer中的单播媒体操作,offer中关于指示方向的处理策略,编码协商优先级设置。

此图例和以下讨论均来自于互联网资源
在后续的章节中,我们将讨论answer如何回应offer,会话修改的细节处理,向对方指示协商能力,offer/answer交互模式的拓展ICE。
1 背景介绍和基本操作
首先,我们介绍一下我们讨论的基本背景环境。到目前为止,我们讨论的核心话题涉及了SDP和准备要讨论的offer/anser交互模式。基本上我们都是围绕两个核心的规范来讨论,其中,RFC4566(SDP)为基本蓝图,然后配合RFC3264关于offer/answer交互模式。关于SDP的内容,我们在前面的章节已经有完整的介绍(SDP基础),那个章节涵盖了SDP的核心定义/专有名词,语法,特征属性,使用场景等相关细节,读者可以查阅上一个章节的内容,这里不再赘述。根据上一个章节的内容,我们在本章节进一步讨论SDP协商时使用的交互模式-offer/answer模式。在本章节,我们将重点以RFC3264为蓝本,介绍offer/anwer交互模式。笔者主要以offer和answer两个部分为核心主线,分别按照两个部分中的单播媒体和多播媒体的处理方式的不同来展开讨论。这里提醒读者,为了更好地支持IPv4,在RFC3264的基础上,RFC6157对媒体描述管理进行了更新。因此,如果读者涉及了在IPv6环境中关于SDP中的媒体描述,读者可以查阅RFC 6157-4.1章节的细节。如果读者对SDP协商模式有兴趣的话,可以结合具体的代码示例来做进一步的研究。以下示例是开源SIP协议栈-PJSIP中关于SDP协商状态机的处理流程示意图:

链接:https://www.pjsip.org/pjmedia/docs/html/group__PJMEDIA__SDP__NEG.htm#details
很多技术人员,一说到offer/answer交互模式,就不假思索说这个概念其实非常简单。事实上,很多人对此概念有不少误解或者还在半桶水的认知状态。因为很多技术人员经常排除的问题基本上都是一个简单的双方呼叫(单播会话),排查的交互会话描述也就双方的会话参数,并没有涉及到多播会话使用场景,例如IP广播,会议等处理。另外,关于单播和多播场景中的RTP媒体流的处理也非常不同(可参考RFC6284-7.1.2的端口映射),需要读者对这些概念有一个充分的理解。因此,很多具体的协商需要大家了解。

我们需要首先提醒读者这些问题,在offer/answer交互模式下,单播会话和多播会话的处理方式是有差别的。SDP(RFC4566)当时设计的构想是提供一种方式来描述多播骨干网中传输的多播会话。SAP(RFC 2947)的设计构想是作为一种多播机制传输SDP消息的。在很多业务场景中,虽然规范中支持也允许支持单播操作,但是规范中支持单播操作相对不太完整。多播会话操作需要覆盖全部会话参与者的操作,单播会话则仅涉及了两个参与者以及其各自的认同的工作参数。具体来说,多播会话比单播会话涉及了更多的关于处理机制和处理方式,在offer/answer交互模式下有不同的处理方式。因此,提醒读者,我们在下面的讨论中,针对各种环境都会分别介绍单播会话和多播会话的处理方式,希望读者千万不要迷惑。例如,如果是多播会话的话,它要求针对具体的媒体流对所有参与方地址发送一个单多播地址,如果是单播会话的话,它仅需要双方的两个地址即可。再比如,如果是多播会话的话,它仅要求标识出会话所使用的具体编码即可,通知所有会话参与方仅使用标识的编码,但是,如果是单播会话中,则需要列出一个编码支持列表,双方可能都支持某些重复的编码,通过二次协商才能决定使用何种编码。另外,读者可以想象一下,在单播会话环境中,随着技术和网络环境的不断发展,终端支持的各种编码或者协商标识越来越多,尽管双方的SDP提供了丰富的足够的会话描述信息,但是因为会话描述定义越来越多,它们所带来的问题也越来越多,例如定义的语法格式问题,操作细节的统一性问题。因此,会话双方究竟如何成功协商,在技术方面,这仍然存在很多争议,这也直接导致了很多环境下,软硬件终端包括服务器端的双方SDP协商不成功的问题。因此,为了实现协商的简化和规范化,RFC3264基于SDP规定了一种简化的offer/answer 交互模式。接下来,我们简单介绍一下其基本工作原理。
“如无必要,勿增实体”-剃刀原理
在offer/answer交互模式环境中,会话中的发起方(offerer)生成一个offer数据,offer中包括媒体流参数和传输编码,希望接收媒体的IP地址和端口。offer数据传递到对端接收方(answerer),接收方也生成一个answer数据回复给offerer发起方,在answer回复数据中包含了已匹配的媒体会话描述参数,表示其媒体是否可以接受。然后通过协商好的传输编码,通过offer消息中的地址和端口对offerer发起方发送媒体流。无论是单播会话还是多播会话都可以支持offer/answer交互模式。注意,读者如果对交互模式下的offer/offerer和answer/answerer有歧义,最好查阅SDP基础章节中核心定义全解的内容。
现在,我们讨论一些关于协议层操作的流程。offer/answer模式工作的前提基于一些高级协议,例如SIP协议存在,SIP协议有能力支持SDP的协商来实现基于代理之间的对会话创建支持。协议层的操作从一方代理对另外一方发起初始化的offer开始。对端的代理可以接受offer,并且返回一个answer或者拒绝这个offer。拒绝offer取决于高级协议层。这里强调一点,offer/answer交互模式是atomic级的,这表示它具有一定的制锁功能。如果这个answer被拒绝的话,会话将回复到offer之前的状态。任何时间其中一个代理都可以生成一个新的offer来更新此会话状态。但是,如果代理已经收到了一个offer,代理还没有接受或者拒绝,此代理一定不能生成新的offer。此外,如果代理已经生成了一个较早的offer,这个代理在还没有收到针对较早的offer返回的answer消息或者拒绝消息,此代理一定不能生成一个新的offer。简单来说,就是代理在没有收到较早offer的回应结果(应答或者拒绝)之前,它一定不能再生成一个新的offer。有时,我们可能会看到一种特殊状态,代理已经发送了一个offer后,但是在没有收到此offer的answer前,如果代理收到了一个offer的话,这种状态称之为 “glare condition”。

双方代理可能同时对对方发送一个更新offer。这种特殊情况下的处理机制需要更高层协议来对这种特殊状态进行数据包发送到排序处理。如果读者对glare case或者异常处理有兴趣做进一步研究的话,可以参考RFC6337-4章节。接下来,我们开始讨论生成offer的流程处理,以及在生成初始化offer中关于单播媒体和多播媒体的处理方式。
2 关于生成初始化offer处理流程
我们讨论生成offer消息之前,首先我们一定要确定,无论是offer消息还是answer消息,它们一定是一个有效的SDP消息。
在SDP中针对offer/answer的构成语法可以忽略“e”或者“p=”行。笔者不清楚为什么在offer/answer的消息中可以忽略“e”或者“p=”行,难道因为这两个会话描述不是十分重要的协商参数? “o“行中的会话ID的数值和版本号必须是一个64位的有符号整形数,并且版本的初始值必须小于(262)− 1,这样可以防止翻转。在SDP规范中,可以允许多个SDP会话描述拼接在一起构成一个大的SDP消息,但是SDP消息使用在offer/answer交互模式下时,一个SDP消息必须包含一个完整的会话描述。**
读者知道,SDP中的"s="传递会话主题,这种定义方式对多播会话方式是非常合理的,但是对单播会话方式存在一定问题。因此,一般推荐是”s“行有一个单空格字符或一个破折号构成。SDP "t="行负责传递会话时间,通常情况下,对单播会话的媒体来说,它的创建或者结束一般来自于外部的信令的控制,例如,我们现在讨论的SIP协议。那种情况下,"t="行应该设定为”0 0.“。
offer消息中包含零个或者多个媒体流,每个媒体流通过”m=“行的媒体会话和其关联特征属性进行说明。读者可能不明白,为什么offer消息中可能会包含零媒体信息?其实,包含零媒体仍然有它的原因。如果offer消息中包含零个媒体,这表示发起方-offerer希望和对端通信,但是发起方会在将来某一时间时间在此会话中添加那个媒体,添加媒体的方式是通过发送一个修改的offer来处理。 此媒体可以是一个单播和多播的混合。如果是多播的媒体,那offer消息中需要在"c="行添加多播地址。它们的构成方式取决于它们的媒体是单播还是多播媒体。简单理解,这里的零媒体不是表示offer没有什么可做,这表示发起方可能将来在某一时间发起媒体流,但是不是现在。如果将来发起媒体流,offerer会更新offer再次提醒对端answerer。下面,我们继续讨论offer中的单播媒体场景的处理流程。
3 关于offer的单播媒体处理讨论
这里,我们花费一点时间需要详细说明offer中的单播媒体处理流程。在offer中的单播媒体处理中,offer对媒体的发送和接收标注涉及了多个方向指示特征属性(a=sendonly,a=recvonly,a=sendrecv,a=inactive-参考RFC3108和RFC2327),多个特征属性又具有不同的含义,所以请读者在阅读本部分内容时一定要特别注意。读者也可以查阅笔者的历史文档来学习关于这三种属性应用场景示例。
如果offer仅希望对对端发送媒体时,offer必须对此媒体标注为send-only的表示,通过特征属性"a=sendonly"行来标识。如果媒体发送的方向属性作为媒体媒体流属性或者会话属性出现的话,我们就会认为此媒体标注了媒体发送方向。同理,如果此offer仅希望从对端接收媒体的话,offer必须对此媒体标识为recvonly,表示仅为接收状态,通过特征属性“a=recvonly”表示。如果,offer希望和对端通信,但是,此时,offer既不想发送媒体也不行接收媒体时,它通过对媒体标识为"a=inactive"行来表示其当时状态。
这里,笔者需要提醒一下,因为很多最新的规范已经对RTP协议的实时应用程序传输协议-RFC3550进行了更新(包括5761,6051,6222,7022,7160,7164,8083和8108),因此一些特别的处理流程需要提醒读者。如果是涉及了RFC3550中描述的实时传输协议和实时传输控制需要的话,为了支持以上三种方向指示的状态,RTCP同样需要被发送和接收。这种情况下,媒体流的方向指示不会影响RTCP的使用。如果offer希望对对端发送和接收媒体的话,它可以通过"a=sendrecv"行来标识也可以忽略其属性设置(因为此属性为默认设置属性)。
读者需要注意,offer消息中三种媒体流向的方向有一些不同。对于标记了“a=recvonly”和“a=sendrecv”的媒体流来说,在offer消息中,端口号和地址表示发起方offerer希望接收媒体流的端口和地址。对于标识为“a=sendonly” RTP 媒体流,端口号和地址间接指示为offerer希望接收RTCP数据的地址。除非有明确表示说明,否则RTCP报表数据将会发送到比标识的端口高一位的端口(这是默认的RTCP端口发送方式)。很多时候,读者可能对offer中的IP地址和端口使用有一些误解,这里笔者做进一步的解释。在offer中出现的ip地址和端口不能指示将要由发起方offerer发送出去的RTP/RTCP的源地址,源端口。在offer消息中,如果端口数为零,这表示媒体已经被经过offer消息处理,但是此媒体一定不能被使用,因为此初始的offer中没有任何有效的语法参数。有时,如果设置端口零可以结束现存的媒体流。一般来说,端口数量为零表示此媒体流不是offer/answer双方需要的媒体流。但是,一些特殊的环境下,编码类型一样,但是可能因为payload不同的话,也可能需要做一些协商处理。针对标识为”a=sendonly“ 或“a=sendrecv”的媒体,在answer的消息中同样的媒体可能标识了不同的payload。offerer发起方收到answerer回复后,重新发送一个offer。这次发送中,offerer发起方必须包含一个和answer消息中一样的媒体payload。这样才能保证双方的媒体的payload一致性。有时,为了保证和H323的兼容性,每个方向可以支持不同的payload类型号。
在所有的RTP流使用场景中,fmtp媒体格式类型可能出现来支持更多的媒体格式类型描述。所有的媒体描述应该包含"a=rtpmap"行,它用来映射相应的payload 类型号码来对编码进行编码处理。如果没有"a=rtpmap"行的话,应该使用默认的当前的属性设置(RTP/AVP)。具体默认属性设置,读者可查阅RFC3551-6。
offer的协商过程中,读者需要另外注意几个经常使用的媒体描述参数。在协商过程中,offer消息中必须提供一个"m="行的格式列表,通过偏好优先级的顺序来通知对端offer中推荐使用的优先级编码格式。优先级最高的是最优先使用的编码格式。有时,我们在配置软交换或者媒体服务器时,双方呼叫失败,有可能是编码不匹配导致,比如说没有匹配的prefered的编码。所以,编码列表的顺序是非常重要的,技术人员需要对照服务器端的编码列表实现对照本地终端的编码列表顺序检查。比如FreeSWITCH中的编码优先级设置:
<X-PRE-PROCESS cmd="set" data="global_codec_prefs=G722,PCMA,PCMU,GSM"/>
或者思科设备设置:
Cisco-router(config-class)#codec preference 1 g723r63
Cisco-router(config-class)#codec preference 2 g729br8
Cisco-router(config-class)#codec preference 3 g711ulaw
Cisco-router(config-class)#codec preference 4 g726r32 bytes 240
如果ptime特征属性出现offer中,它表示offer所期望的收到的打包时长,当然,ptime必须大于零。如果offer中出现了bandwidth的话,它表示offer所期望带宽来传输媒体流。当然,带宽值也允许设置为零(不建议),这表示没有媒体发送,同时也关闭了RTCP数据发送。
如果是offerer中的多媒体的话,根据多媒体类型的不同处理方式也有一定的差别。如果在offer中出现了不同类型的多媒体流(语音,视频,文本等),这表示发起方offerer希望同时使用这些多媒体流。比较典型的例子就是视频会议,视频会议中,语音和视频媒体流被同时使用。如果在offer中出现来同样的媒体类型的话,这表示offerer发起方期望同时接收或发送那种同一类型的媒体流。关于同一类型的媒体流收发,双方可能有一定的规则策略需要遵守,这取决于本地资源(例如,麦克风/摄像头和录像终端)和业务需求的设置。另外一些限制也可能影响多媒体的本地处理策略,例如不同语音或者视频媒体流的混音/混屏处理,按需录像录音处理等业务需求。
Offerer发送方需要根据不同的指示标识调整为一个相应的状态。一旦发起方Offerer发送了offer消息以后,发起方必须准备接收offer描述的任何标识为recvonly的媒体。另外,发起方必须准备发送和接收在offer中标识为sendrecv的媒体,并且准备发送在offer中标识为sendonly的媒体。当然,何时发送还要取决于对端answer的地址和端口。在RTP的环境中,虽然可能offerer在answer消息到达之前,提前收到了媒体流,但是发起方仍然需要收到answer消息后,它才能发送RTCP接收方报告数据。
4 关于offer的多播媒体处理讨论
在offer中,如果一个会话描述中包含一个多播媒体流,此多播媒体流列为仅接收或者发送状态,这表示参与者(包括了发起方offerer和接收方answerer)仅能接收或发送媒体流。和多播媒体流处理方式相比,单播媒体处理仅是发起方和接收方媒体流的直接收发。除了以上这个区别以外,offer中的多播媒体的语法定义可以查阅RFC4566的规范说明。
参考资料
- https://www.rfc-editor.org/rfc/rfc3264
- https://tools.ietf.org/html/rfc6284
- https://tools.ietf.org/html/rfc5939
- https://www.pjsip.org/pjmedia/docs/html/group__PJMEDIA__SDP__NEG.htm
原文出处:完整SIP/SDP媒体协商概论-SDP协商模式详解-answe处理
前面的章节笔者重点讨论了offer初始化流程的处理,包括基本的SDP协商模式的背景知识,针对发起方对offer消息中单播和多播环境处理。从本章节开始,我们继续介绍SDP协商中的接收方或者应答方的单播和多播媒体的应答处理(answer消息)。除了针对应答方生成answer的讨论以外,本章节的后续还将继续介绍关于SDP的媒体管理(修改必要会话描述参数),指示能力的讨论和一个交互模式的示例。
5 构建offer消息的answer消息
发起方提供了一个offer消息给应答方,同样,应答方answerer也会根据offer的消息回复一个answer信息。在answer的讨论中,笔者首先需要提醒读者需要注意几个媒体描述的设置("o=","m="行数和“t=”行),并且"m="中包含接受的媒体格式和不接受的媒体格式指示。通常来说,已提供的offer消息和它的answer是相互对应的,否则双方的交互没有办法完成。它们之间的绑定关系是通过"o="行来关联在一起。具体来讲,如果answer消息中的"o="行版本号和offer消息中的"o="行版本号不一致的话,那么,说明这answer消息是由不同的实体生成。另外,在offer中的每个"m="行必须对应相应的answer中的"m="行,answer中的"m="行必须包含和offer消息中完全一样的"m="行数。简单来说,offer中包含两行"m="行,answer必须也同样包含两行"m="行。当然,如果在offer消息中包含零行"m="行的话,answer中也是包含零行"m="行。一些情况下,这样规定的目的是为了保证媒体流格式按照一定的顺序来匹配。最后,会话时间是不能协商的,因此,在answer中的"t="行设置必须等于offer中的"t="设置。offer消息中提供的流可以因为各种原因被answer消息拒绝。进一步说明,如果offer的流媒体被拒绝的话,发起方和应答方双方一定不能生成针对此流媒体生成媒体流数据(或者RTCP报表数据)。对已拒绝的offer的媒体流,answer消息中相应的"m=0"行表示。任何的媒体格式列表要被忽略,至少保留一个媒体格式以支持指定的SDP。针对单播和多播流媒体来说,offer的流媒体的answer消息生成也有各自的流程。另外提醒读者,很多网关设备支持的默认媒体媒体格式的列表有自己的设置限制,有的网关可能至少支持一个媒体格式,最大支持4个媒体格式列表。有的可能更多,如果列表支持的最大数不能满足协商的列表的话,可能出现编码匹配或者协商机制的不兼容性。下面,我们针对answer消息的单播媒体流和多播媒体流的构建进行讨论。

在生成应答的消息环境中,单播媒体流需要注意一些设置,特别是指向特征属性。注意,规范中的定义相对比较严格,为了保证能够完整准确的解释其官方的内容,笔者也只能遵从其用法,尽量不使用口语化的用词。这里的流媒体是一个总称,媒体流是指具体的音视频,数据等具体媒体格式。现在,我们说明在answer消息中关于指向属性的设置。读者一定要注意,这里有很多“如果”。如果发送的或者提供的流媒体使用的是一个单播地址,此媒体其应答的地址或answered的地址必须包含一个单播地址。如果提供的流媒体标记为sendonly状态,在answer消息中其相应的流媒体必须标记为recvonly 或者inactive状态。如果在offer消息中,媒体流列为recvonly状态,在answer消息中其媒体流必须标记为sendonly或者 inactive状态。如果提供的媒体流标记为sendrecv(或如果在媒体级或会话级没有指向属性,这种情况下,媒体流默认的标记为sendrecv状态),在answer消息中相应的媒体流可以标记为sendonly,recvonly,sendrecv,或inactive的状态。如果提供的媒体流标记为inactive状态,此媒体流在answer消息中必须标记为inactive状态。指向属性讨论完以后,笔者再结合指向属性介绍一些关于"m="行以及其他相关属性设置。对于在answer消息中标记为recvonly状态的流媒体来说,其"m="行必须包含至少一个媒体格式,此媒体格式是应答方期望使用的媒体格式(在offer消息中包含的媒体格式列表中挑选),使用此媒体格式接收媒体。另外,此流媒体也可以指示另外其他的媒体格式,指示的媒体格式也不在offer消息中包含的媒体格式,应答方期望使用此媒体格式来接收媒体流。对于在anwer消息中标记为sendonly状态的流媒体,其"m="行必须包含至少一种媒体格式(此媒体格式在offer消息列表中的),应答方(answerer)使用此媒体格式发送媒体流。对于在answer中标记为sendrecv的流媒体,在"m="行必须至少包含一个媒体格式,其媒体格式表示应答方期望使用其媒体格式来发送和接收媒体流(在offer消息中包含的媒体格式列表中挑选)。流媒体也可以指示一个其他的媒体格式(此格式不在offer消息中的媒体列表中),应答方期望使用此媒体格式发送或接收媒体流。当然,此时,因为此媒体格式不在offer消息的媒体列表格式中,应答方还不能使用此媒体格式发送媒体流。对于answer中的媒体流标识为inactive的状态,实际使用场景比较复杂。对于在answer消息中标识为inactive的媒体流,其媒体格式的构建是基于offer消息中的媒体格式。具体来说:
- 如果offer的消息是sendonly状态,answer中构建的媒体格式列表好像answer中的recvonly状态。
- 如果offer的消息中标记的媒体流是recvonly的状态,answer中构建的媒体格式列表好像answer中的sendonly状态。
- 如果offer的中的消息是sendrecv状态,answer中构建的媒体格式列表好像answer中的sendrecv状态。
- 如果offer的中的消息是inactive状态,answer中构建的媒体格式列表好像这样一种状态,它们分别是,offer实际上是sendrecv状态,answer实际上是sendrecv状态。
在answer消息中的连接地址和端口表示一个应答方期望接收媒体的地址(RTP和RTCP的地址,RTCP端口是默认比RTP端口高一位数的端口,否则必须明确说明)。现在,笔者讨论一下关于RTP的媒体格式使用以及其他参数设置。如果在offer消息中,一个特定的编码格式支持了一种指定的payload类型码的话,此指定的payload类型码也应该使用在answer的payload类型中。虽然在answer中使用了同样类型的payload类型号,answer也必须包含rtpmap属性来定义payload类型的映射,使用此映射支持动态的payload类型,并且在answer消息中应该包含payload类型映射支持静态的payload类型。在answer消息中,"m="行中的媒体格式列表应该按照偏好顺序来排列,列表中的第一个媒体格式是实际推荐的媒体格式。在这种情况下,推荐的媒体格式表示对端offerer应该使用answer消息中推荐列表的最高排序的媒体格式。简单来说,answerer通知offerer使用answer中的最高优先级编码格式。虽然answerer应答方可以根据自己的推荐媒体格式在answer消息中列出一个媒体格式推荐顺序列表,但是,除非有特别的原因,一般的推荐方式是,应答方应根据出现在offer中的列表的格式顺序列出answer中的推荐列表。换句话说,如果出现在offer中的媒体流的语音编码格式顺序是8,22和48,answerer支持的语音编码是8和48的话,根据一般的推荐方式,如果answerer没有任何理由修改这个顺序的话,在answer中的语音编码的顺序应该是8和48,而不是48和8的顺序。这样可以保持双向编码一致,降低了编码协商的多余处理步骤。answerer可以包括一个非零的ptime属性来支持任何媒体格式,这表示应答方期望使用此打包时长来接收媒体。对于一个媒体流来说,针对每个方向的媒体流打包时长可以不同,不一定是一样的。应答方也可以包括一个带宽属性支持任何媒体流,此带宽表示answerer应答方希望发起方发送媒体时使用的带宽。带宽属性可以为零,这表示无媒体发送,使用RTP的场景中,answerer同时也关闭了RTCP。针对一个某个由发起方的媒体流,如果answerer没有对此媒体流提供任何媒体格式的话,answerer必须设置端口为零来拒绝此媒体。但是,如果answerer对所有的媒体没有提供媒体格式的话,发起方提供的整个会话将会被answerer拒绝。
现在,我们继续讨论answerer发送answer后的处理流程。一旦answerer已经发送了answer消息,answerer必须准备好接收在answer消息中标识为recvonly的媒体。answerer必须准备好发送和接收在answer消息中标识为sendrecv的媒体流,并且它也可能马上发送媒体流。answerer必须准备好接收媒体流,此媒体流是在answer消息中发送的,并且标记为recvonly或者sendrecv的任何媒体流格式,并且answerer也可能马上发送媒体流。当应答方answerer发送媒体流的时候,如果offer消息中出现了ptime打包时长的话,应答方应该使用和offer消息中设定的ptime相等的打包时长来处理发送的媒体流。当应答方answerer发送媒体流的时候,如果收到的offer消息中出现了带宽设置的话,应答方应该使用不高于offer消息中设定的带宽来处理发送媒体流。answerer必须使用offer消息中列出的媒体格式,同时也是在answer消息中列出的媒体格式发送媒体流,并且应该使用offer消息中推荐优先级最高的编码格式,同时也是在answer列表中的编码格式。这里提醒读者,通常,在生产环境中,我们可能使用所谓的使用本地推荐编码或者远端推荐编码的方式来进行编码协商处理设置,每个厂家所支持的设置方式不同,读者可以查阅一些厂家语音产品的设置。笔者在后续章节的延伸讨论中会做进一步的介绍。在RTP使用场景中,虽然有时answer消息中的payload类型号和offer的payload类型号有所区别,answerer必须使用offer消息中的payload类型号。以上是关于单播媒体流在answerer中的answer消息构建的核心讨论。接下来,笔者继续讨论多播媒体流在answerer中关于answer消息构建的规范。
多播媒体流在answerer中关于answer和单播媒体流的处理有一定的差别,一些基础的设置是相同的。我们这里简要介绍多播媒体流在answerer方的构建处理规范。和单播媒体流的offer/answer交互模式不同,从单播媒体流的角度可以看到双向的媒体流流程,但是多播媒体流的交互模式下,我们仅能看到一个媒体流。如果严格地讲,对一个多个offer流媒体生成一个answer消息通常会涉及到修改流媒体的一些限定选项参数。如果接受了一个多媒体流的话,在answer消息中的地址和端口消息必须匹配offer消息中的地址和端口信息。同样,在answer中的指向消息(sendonly,recvonly,或者sendrecv)必须和offer消息中的指向消息相同。因为,RFC 2327针对多参与方的一种前提假设,在一个多会话中,所有的参与方需要会话参数的等同理解。在answer消息中,一个媒体格式集必须等于offer中的媒体集或媒体集的子集。answerer应答方通过移除一个媒体格式来对offerer发起方表示应答方不支持此媒体格式。如果在answer中出现了ptime的话,在answer中的ptime和bandwidth带宽必须等于offer消息中的ptime和bandwidth带宽。如果在answer中没有出现ptime,在answer中可以增加一个非零的ptime值。
6 发起方Offerer对answer信息处理
前面,我们讨论了answerer应答方如何生成answer消息。现在,笔者讨论发起方是如何接收answer消息的。当发起方收到answer的消息以后,offerer发起方可以通过流数据来发送媒体内容(假设在answer消息中标识的指向是sendrecv 或者recvonly)。这时,作为offerer发起方,它发送媒体流时必须使用answer中的媒体格式列表中的媒体格式,并且应该使用answer媒体格式列表中的第一个媒体格式。读者注意,这里笔者强调的是应该使用,而不是必须使用,因为在answerer端规定的也是“应该使用”,不是必须使用,通常情况下,双方的编码常常需要及时修改保证协商的及时性。例如,在双方通话的无声时段,一个终端可能想切换到一个舒适噪音的编码(参考RFC3389),另外,有时终端可能通过终端面板摁DTMF按键发送DTMF按键音。除了一些必要的应用场景需要推荐“应该使用”,很多时候,拥塞控制也根据对端的响应强迫用户不得不使用相对低速率的编码。在拥塞控制的草案中涉及了一个控制模型,读者有兴趣的话可以进一步学习:

资源来源:https://tools.ietf.org/html/draft-ietf-rmcat-cc-codec-interactions-02
offerer应该基于answer消息中的ptime和bandwidth带宽发送媒体。有一种媒体格式发起方可能会马上终止监听。具体来说,offerer可以马上终止侦听媒体格式,这种媒体格式是在初始化offer中列出的,但是没有出现在收到的answer消息中的媒体格式。
后续还将继续介绍关于SDP的媒体管理(修改会话描述参数),指示能力的讨论和一个交互模式的示例。
参考资料
- https://tools.ietf.org/id/draft-ietf-mmusic-ice-sip-sdp-14.html
- https://tools.ietf.org/html/draft-ietf-rmcat-cc-codec-interactions-00
原文出处:完整SIP/SDP媒体协商概论-SDP协商模式详解-会话管理全解
笔者在前面的章节重点讨论了answerer中answer消息的构建,针对发起方对answer消息中单播和多播环境处理进行了讨论说明。除了前面章节中针对应答方生成answer的讨论以外,本章节还将继续介绍关于SDP的会话管理(修改会话描述参数),指示能力的讨论和一个交互模式的示例,关于earlyoffer和later offer的详解以及later offer可能出现的问题。
7 关于会话管理讨论
前面的章节包括上一次笔者发布的文章中,笔者分别讨论了offer和answer双方的消息生成。这里,笔者将讨论会话的修改。修改会话是常见的业务场景,通过修改会话实现另外一个新的流程。在会话的某个点上,任何会话参与方都可以发出一个新的offer来修改会话的属性参数。会话修改是offer/answer交互模式的基本操作,它的处理流程其实和我们前面讨论的流程完全一致,通过某些流程修改现存会话的某些参数。offer可能和以前的相同,也可能和以前的不同。当讨论修改会话时,我们需要涉及上一个或者前面的SDP,上一个SDP提供了其他的会话参数基础,这些参数可能已经在offer或者answer消息中存在。现在,笔者先说明一下修改会话的一些基本框架和前提条件。如果offer相同,answer可能和从answerer获得的上一个SDP中的相同,也可能answer不同。如果已提供的SDP和前面的SDP不同,一些特定的限制需要注意。关于这些限定,笔者在后续部分讨论。理论上说,几乎所有的会话属性都可以修改。例如,可以增加一个媒体流,可以删除现存媒体流,可以修改现存媒体流参数。当发起一个offer,此offer修改需要修改会话时,除了origin域值其版本号必须在前面的SDP版本号基础上增加一位数以外,新SDP中的"o="行必须等同于前面的SDP中的"o="行。如果在新SDP中,origin域值不能增加的话,此SDP必须等同于同一origin值域的SDP。answerer应答方必须准备接收offer,此offer包含一个无改变origin值域版本的SDP。根据笔者在前面应答方生成answer的流程,虽然answerer接收了这样的一个offer,然而,answerer仍然必须生成一个有效的answer消息(此answer消息可能和以前SDP相同不同,也可能不同)。如果已提供的SDP和前面的SDP不同的话,这个新的SDP必须有一个媒体流和前面的SDP媒体流中的匹配。换句话说,如果前面的SDP有四个"m="行,那么在新的SDP中必须至少有四个"m="行。前面SDP中的媒体流排序(从最顶端计数)关系必须匹配新的SDP中的媒体流排序(从最顶端计数)关系。为什么需要这样的匹配关系呢?其实,这样的匹配关系是非常必要的,answerer应答方决定新的SDP中某个媒体流可以对应前面SDP中的那个媒体流,然后可以绑定SDP媒体流的具体的匹配关系,否则,修改会话就会出现数据混乱。因为为了满足以上的这些要求,我们看到,媒体流中的"m="行从来不会减少,“m=”行数保持不变或者增加。另外,从前面SDP中删除的媒体流一定不能从新SDP中移除,不过,这些媒体的属性不能出现在新的SDP中。

新的媒体流可以通过添加一个新的媒体描述来实现,它可以添加到当前媒体描述的下面,也可以重新使用旧的媒体流的空间来重新激活新的媒体流。注意,这里,旧的媒体流通过端口设置为零以后已经被关闭。如果新的媒体流使用旧媒体流空间的话,新媒体描述需要替换掉旧的媒体描述,但是新媒体描述应该在SDP中和其他媒体描述位置不变。新媒体描述必须出现在已存在媒体描述的下面(这是一个格式规则)。当answerer应答方收到的SDP中所包含的媒体描述比offerer发起方发送的媒体描述多时,或者收到的SDP中包含媒体流,此媒体流占用的以前旧媒体流空间的话(端口为零),应答方就知道这里增加了一个新的媒体流。当然,新增加的媒体流也可以通过answer消息,在answer重构媒体描述接受或者拒绝这个新的媒体流。关于应答发生成answer的流程,读者需要参考前面的章节。
除了在会话中增加媒体流以外,媒体流当然也可以被从会话中移除。如果要移除媒体流的话,需要创建一个新的SDP,在新的SDP中对需要移除的媒体流端口设置为零,这样就移除了媒体流。媒体描述可以忽略以前出现的所有描述,仅保留单个媒体格式。所有在offer消息中出现被标识为关闭的媒体流(端口设置为零)也必须在anwer标识这些媒体流,并且关闭端口。反之,answer也必须针对offer做同样的处理流程。在这种情况下,RTP和RTPC的传输流程也会停止退出,释放任何和这些媒体关联的资源,例如占用的电脑麦克风,摄像头资源也会关闭。
除了增加媒体流,删除媒体流以外,本章节开始时,笔者说过,几乎所有的会话中的媒体流属性参数都可以修改。现在,我们主要介绍一下修改地址,端口,传输方式,媒体格式,媒体类型,属性,以及单播媒体等待状态处理。
媒体的端口号是可以被修改的。笔者将讨论offerer和answerer两个方向的端口修改。如果要修改媒体的端口号,发起方offerer需要创建一个新的会话描述,在新会话描述中的“m=”行包含一个端口号,此端口号必须和前面相应媒体SDP中的会话描述中的端口号不同。如果仅修改端口号,那么其他的媒体描述应该保持不变状态。因为offerer修改了端口以后,还没有收到任何answer消息之前,所以,只要offerer发送了offer以后,offerer发起方必须准备接收旧端口发送过来的媒体和新端口发送过来的媒体。直到收到了answer消息,媒体流从新端口抵达,offerer发起方才能退出旧端口的侦听。简单来说,在使用新端口的媒体抵达之前,发起方不应该退出旧端口的侦听,否则,可能出现传输的媒体丢失的问题。需要特别注意到是,这里存在一个新旧端口切换的过渡阶段,可能一些应用场景中的已接收到媒体流。这里的已接收到媒体可能已经被存放在了一个系统的存储节点上,如果有一个缓冲区的处理列表的话,接收方用户终端会继续侦听旧端口来的这个媒体流,直到从新端口接收到的媒体流存放在优先级最高的空间,这样,接收方终端才会退出旧端口的媒体侦听,真正开始侦听新端口的媒体。
前面,笔者讨论了offerer发起方的端口修改,现在开始讨论answerer的端口修改。在answer中相应的媒体流可以和前面answerer发送的SDP中的媒体流相同也可能不同。如果应答发接受了一个已更新的媒体流,应答方应该使用新的端口马上开始发送此更新的媒体流数据。如果应答方从前面的SDP的端口改变了发送端口,只要answer消息发送后,answerer应答方必须准备同时从新端口和旧端口接收媒体流数据。关于新端口和旧端口的侦听退出和offerer的思路完全相同,笔者这里不再重复。以上我们讨论的是answerer接受了offer的更新媒体。如果answerer应答方拒绝了offer的媒体流,offerer发起方收到拒绝消息后,offerer会停止前面准备接收媒体的端口。
改变媒体发送地址的处理方式和改变端口的处理方式相同,区别在于其连接属性“c=”需要更新,端口没有修改。媒体传输方式也可以修改,处理方式和端口的处理方式是相同 的,传输方式修改,其端口不能修改。
除了会话中修改地址,端口和传输方式以外,会话管理的另外一个功能就是修改媒体格式。在会话中支持的媒体列表中的任何一个媒体格式都可以被修改。如果需要修改媒格式的话,offer需要创建一个媒体描述,在这个新的媒体描述中,"m="行的媒体格式列表和此媒体流前面SDP中的不同。在新的会话描述中,此媒体列表可以增加新的媒体格式,也可以删除出现在前面SDP中的媒体格式。具体到RTP的使用中,在此媒体流中,会话期间的这个匹配关联关系一定不能被修改,具体来说,这个绑定关联关系是从一个指定的动态payload类型匹配到一个指定的编码,它们两者之间绑定关系在此会话生命周期内是不能修改的。例如,如果Bob端生成了一个offer消息,此offer消息中标识了编码格式G.711绑定到了一个动态的payload 类型号46,在此会话期间,在此媒体流中,无论是offer或者answer,这个46 payload 类型号码一定是表示G.711编码格式。但是,多个payload 类型号映射到同一编码格式的这种情况也是可以接受的。因此,一个已更新的offer消息中可以使用其他payload 类型号码来映射G.711编码,例如,也可以在更新的offer中使用payload type是72来映射G.711编码。

显而易见,为了保证SDP信令交互和媒体流之间的同步,在一个会话期间,动态payload号类型和编码的映射关系必须维持一个固定状态。我们在前面的章节介绍过在answer中相应的媒体流构建,构建的过程也可能导致修改媒体格式。同样的,只要应答方发送answer应答消息,answerer应答方必须开始使用某种媒体格式发送媒体流,这种媒体格式出现在answer消息中,也出现在了其offer消息中,answerer应该使用offer中优先级最高的推荐媒体格式(此媒体格式也出现answer中的媒体格式)。即使某种媒体格式出现在前面的SDP中,如果此媒体格式没有出现在offer中的话,answerer一定不能使用这种媒体格式。反之亦然,offerer发起方收到answer后,它发送媒体时必须使用answer中优先级最高的媒体格式。即使某种媒体格式在前面的SDP中出现过,但是在本answer中没有出现的话,offerer发起方一定不能使用此媒体格式。当终端agent停止使用某种媒体格式时(这种媒体格式不在offer或answer消息中),它仍然需要在将来一定时间内准备使用此媒体格式接收媒体。这里,agent仍然需要做一个状态处理。它自己本身如何知道何时停止使用某种媒体格式接收媒体流呢?它需要一些必要的技巧来处理,使得agent变得“聪明”一点,它可以判断对端是否停止使用旧媒体格式。第一种处理技巧,agent可以修改端口来修改媒体格式。当媒体抵达新端口后,agent知道对端peer已经停止使用旧媒体格式发送媒体流,agent也能停止使用此媒体格式接收媒体流。这种处理方式的好处是不依赖于媒体格式,但是,如果涉及了加密处理的话,修改端口要求原来预留的资源也要做出改变,原来安全加密的密钥需要重新签发。接下来,我们讨论第二种处理技巧,当一个媒体格式被弃用时,agent使用一组全新的payload动态类型来映射所有的编码。agent收到媒体时,它使用了新payload类型中的其中一种格式的话,agent知道对端已经停止使用就媒体格式发送媒体流。和第一种方式相比,这种方式不会影响预留资源和加密处理,但是需要增加额外的空间重新创建新payload 类型号。最后一种方式是使用定时器。当agent收到一个从对端peer发送过来的SDP,agent设置一个定时器。当定时器被触发后,agent就停止使用旧的媒体格式来接收媒体流。定时器设置为一分钟的超时设置是完全没有问题的。在一些使用环境中,agent也可以不用考虑是否继续使用旧媒体格式接收媒体流。因此,agent也无需过多干预。如果agent拒绝了offer的媒体流,对端也收到拒绝消息后也会停止准备使用任何新媒体格式发送媒体流。第三种方式相对比较适用于简单的环境中,通过定时器超时来处理媒体格式的修改响应。
广告

除了前面我们讨论的一些媒体描述参数可以修改以外,媒体类型(语音,视频,数据等)也可以进行修改。对流媒体来说,媒体类型也可以通过一定的处理方式来修改。一般的推荐方式是,如果传输的逻辑数据是一样的,如果修改媒体类型的话,可以通过不同的媒体格式来实现。以前比较常见的示例是传真部署中的场景,基于VBD(Voiceband Data)的传真和模拟传真信号的媒体类型修改。在当媒体流修改为不同的媒体类型。Voiceband Data和传统传真是分离的两种类型,它们可以在路由器,网关和IPPBX之间进行类型修改支持传真功能。如果需要修改媒体类型的话,offerer方使用一个新的媒体类型创建一个新的媒体描述,替代掉前面SDP中需要修改的媒体类型。在answer中相应的媒体流需要遵从笔者前面讨论的answer消息中构建流程。假设媒体流被接受的话,只要answerer应答方收到了offer以后,它应该开始通过新的媒体类型和格式发送媒体流。收到answer消息后,发起方offerer必须准备使用旧媒体类型和新媒体类型接收媒体流,新媒体类型的媒体流抵达后,新类型媒体上升到播放缓冲区顶部,开始正式使用最新媒体类型。
修改会话中的特征属性也需要通过offer/answer来修改。在媒体描述中其他的属性也可以通过一个offer消息或answer消息来更新。一般情况下,如果agent收到了一个已修改参数的SDP,agent必须使用新的参数属性发送媒体。
在以上所有的会话描述修改中,我们没有涉及到一个非常普遍使用的应用场景。这个使用场景就是呼叫等待功能。笔者再花费一点时间来进一步和大家说明如何在呼叫等待状态中处理单播媒体流。如果在呼叫中,一方(例如,A)把另外一方(例如,B)的呼叫执行了电话驻留,让另外一方处于呼叫等待状态(需要暂时停止发送一个或者多个单广播媒体流)。这时,呼叫方A会对另一方B发送一个offer消息,offer消息中包含了一个更新的SDP。如果前面被停靠的媒体是一个被标记为sendrecv的媒体流,现在,这个流媒体将会被替换成标记为sendonly的媒体流。
如果前面被停靠的媒体是一个被标记为recvonly的媒体流,现在,这个流媒体将会被替换成标记为inactive的媒体流。这样的处理方式表示在每个方向停靠的呼叫等待媒体可以独立分别标识。等待状态的媒体流都是各自独立的。在等待状态的媒体的offer消息的接收方不应该自动返回一个针对此等待状态的answer消息。一个对所有等待状态的媒体的SDP看作是已接受的SDP(held SDP)。当answerer对已接受的SDP响应SDP时,某些第三方的呼叫控制方案中,已接受的SDP不能正常工作。比较典型的应用场景是,当终端agent摁"Hold" 按键时,agent端将会在SDP中对所有的流媒体生成一个offer消息,此offer消息表示一个sendonly指向,并且agent自己也执行本地静音,agent也没有对远端发送媒体和播放媒体。还有很多的应用遵从RFC 2543(不再使用),从规范规定通过连接地址设置为0.0.0.0来驻留呼叫。这种规范方式其实已不再推荐为一种停靠方式,并且也不能支持IPv6,它不能支持RTCP对媒体流的报表监控。但是这种处理方式在初始offer时会非常有用。在发起时间,当发起方offerer知道它想使用的具体的一组媒体流和它们的格式,但是此时还不知道接地址和端口,这种处理方式就满足了初始offer的需求。Agent必须有一个支持能力来接收SDP,此SDP中的连接地址为0.0.0.0,关闭对RTP语音包数据或者RTCP报表数据的发送。关于呼叫等待的完整的流程介绍,读者可以参考笔者历史文档:
8 指示能力讨论
在agent发送offer之前,如果知道offer中的媒体格式可以被远端answerer 应答方接受的话,那将对agent有很大的帮助。SIP协议具有查询能力,可以对对端进行基本的能力查询。在SIP响应中的SDP可以用来指示对端的媒体支持能力。读者可以查阅RFC 3261-11获得关于查询能力的介绍。这里,我们讨论如何实现类似SDP的构建。可能一些读者知道,其实,SDP没有办法指示此消息来说明为了实现能力指示的目的,SDP只能进行一些简单的能力查询。它真正的的能力指示是由更高级的协议层内容完成,SDP的基本能力指示的媒体承载能力是非常有限的。SDP本身不能表达允许参数值范围,也不能通过offer/answer本身实现并行处理。SDP的这个局限性可能在未来的规范中得以支持。
使用一个结构化的SDP表示媒体能力支持,其创建过程是这样的。当然,首先,此SDP必须是一个有效的SDP结构,另外,它可以忽略"e=" 和 "p="行(前面讨论已提及)。“t=”必须等于“0 0”。agent所支持的每个媒体类型必须有相应的媒体类型的描述。在origin域中的会话ID是唯一的,唯一的会话ID用来支持每个已构建的SDP指示其媒体能力。端口必须设置为零,但是连接地址可以是随意的。如果一个SDP解析为offer或answer消息的话,使用端口为零的设置手段可以保证一个已经构建好的SDP不会引起媒体流创建,因为此已格式化的SDP仅针对其支持能力,并且端口设置为零。"m="行表示传输的媒体类型。对于agent支持的媒体类型中的每个媒体格式来说,"m="行应该有一个媒体格式列表。在RTP使用方面,如果使用了动态payload类型的话,rtmp属性必须出现在SDP中,它用来绑定媒体类型和具体的媒体格式。注意,在SDP中没有办法指示媒体限定,例如一种编码可以支持的并发媒体流数量等。
9 交互示例和其他延伸讨论
在前面的几个章节包括现在我们正在讨论的章节中,笔者完整介绍了基本的语法概要,offer/answer各自生成消息的流程,还介绍了如何对会话中的媒体描述进行管理。为了保证offer/answer交互模式规范的标准化,笔者在前面的所有章节中没有涉及太多规范以外的内容。此部分的内容中,笔者结合几个实例来具体说明offer/answer的交互示例,终端/网关产品中关于媒体协商的示例,还有关于早期offer和后期offer的区别等延伸讨论。说明,因为篇幅有限,在示例中,我们仅列出一般正常的交互方式,不涉及非常特别的或具体每个特殊场景的环境和参数讨论。
首先读者可以了解一个最简单的offer/answer交互模式的处理流程,示例中仍然是经典的Alice/Bob举例。这里,假设Alice在offer中已经包含了以下的媒体描述,描述中一个双向的语音媒体,两个双向的视频媒体,其中视频媒体使用了H.261(payload类型是31)和MPEG(payload类型是32)。这里,offer的SDP是这样的:
v=0
o=alice 2890844526 2890844526 IN IP4 host.anywhere.com
s=
c=IN IP4 host.anywhere.com
t=0 0
m=audio 49170 RTP/AVP 0
a=rtpmap:0 PCMU/8000
m=video 51372 RTP/AVP 31
a=rtpmap:31 H261/90000
m=video 53000 RTP/AVP 32
a=rtpmap:32 MPV/90000
Alice呼叫了Bob,但是,Bob不想接收或发送第一个视频媒体流,因此Bob返回了一个answer消息:
v=0
o=bob 2890844730 2890844730 IN IP4 host.example.com
s=
c=IN IP4 host.example.com
t=0 0
m=audio 49920 RTP/AVP 0
a=rtpmap:0 PCMU/8000
m=video 0 RTP/AVP 31
m=video 53000 RTP/AVP 32
a=rtpmap:32 MPV/90000
在每个时间点,Bob想决定修改他的语音媒体流接收端口(从49920修改到65422),同时,他还要增加一个额外的媒体流,此媒体流标识为receive指向状态,使用的payload类型支持event(例如DTMF,传真等)。关于event的介绍,读者可以参考RFC4734-2。Bob会生成这样的offer消息返回到Alice:
v=0
o=bob 2890844730 2890844731 IN IP4 host.example.com
s=
c=IN IP4 host.example.com
t=0 0
m=audio 65422 RTP/AVP 0 // 修改了端口
a=rtpmap:0 PCMU/8000
m=video 0 RTP/AVP 31
m=video 53000 RTP/AVP 32
a=rtpmap:32 MPV/90000
m=audio 51434 RTP/AVP 110
a=rtpmap:110 telephone-events/8000
a=recvonly // 增加的指向
假设Alice接受了额外的媒体流支持请求,Alice生成以下answer:
v=0
o=alice 2890844526 2890844527 IN IP4 host.anywhere.com
s=
c=IN IP4 host.anywhere.com
t=0 0
m=audio 49170 RTP/AVP 0
a=rtpmap:0 PCMU/8000
m=video 0 RTP/AVP 31
a=rtpmap:31 H261/90000
m=video 53000 RTP/AVP 32
a=rtpmap:32 MPV/90000
m=audio 53122 RTP/AVP 110
a=rtpmap:110 telephone-events/8000
a=sendonly
通过几次交互以后,完成双方会话管理。以上交互流程实际上是Alice对Bob呼叫时的一个会话修改的流程,具体的协商响应和回复需要参考笔者前面介绍的内容。
多选一”One of N Codec“是编码协商中比较常见的场景。现在,很多物理SIP话机已经非常普及,SIP话机的DSP支持了多种编码的压缩。

有时,编码一旦被选定以后,编码不能轻易被修改。以下这个示例演示了通过初始化的offer/answer交互创建会话的流程,然后马上通过第二个offer选择编码组锁定需要的编码。从Alice发送到Bob的初始offer中指示单媒体流可使用三种编码,这三种编码都是DSP中支持的编码。因为Alice还没有收到媒体流,编码锁定以后才能发送媒体流,因此,这里的媒体流标识为inactive状态:
v=0
o=alice 2890844526 2890844526 IN IP4 host.anywhere.com
s=
c=IN IP4 host.anywhere.com
t=0 0
m=audio 62986 RTP/AVP 0 4 18
a=rtpmap:0 PCMU/8000
a=rtpmap:4 G723/8000
a=rtpmap:18 G729/8000
a=inactive
Bob端可以支持动态切换编码格式,可以从PCMU和G.723编码。因此,Bob返回一个answer消息,包含的编码如下:
v=0
o=bob 2890844730 2890844731 IN IP4 host.example.com
s=
c=IN IP4 host.example.com
t=0 0
m=audio 54344 RTP/AVP 0 4
a=rtpmap:0 PCMU/8000
a=rtpmap:4 G723/8000
a=inactive
Alice收到Bob的answer以后,从收到的两种编码中选择了其中一种编码G.723,通过一个更新的offer消息,并且标识其媒体流的媒体指向是sendrecv状态。
v=0
o=alice 2890844526 2890844527 IN IP4 host.anywhere.com
s=
c=IN IP4 host.anywhere.com
t=0 0
m=audio 62986 RTP/AVP 4
a=rtpmap:4 G723/8000
a=sendrecv
Bob收到更新的offer以后,接受其编码G.723, 发送answer给Alice。
v=0
o=bob 2890844730 2890844732 IN IP4 host.example.com
s=
c=IN IP4 host.example.com
t=0 0
m=audio 54344 RTP/AVP 4
a=rtpmap:4 G723/8000
a=sendrecv
如果Bob这里只能支持N选一(one-of-Ncodecs)的编码方式的话,Bob将会从offer中提供的编码中选择其中一个可用编码,然后把选择的编码通过answer发送到对端。这种情况下,Alice将会重新发起一个re-INVITE请求启用此最后协商的编码来传输媒体流。以上笔者介绍的是在第一次初始化offer中使用了"a=inactive"。对于在第一次交互中使用"a=inactive"指向的方式来说,另外一种处理方式是Alice可以列出所有的编码,只要她从Bob收到媒体流以后,然后马上生成一个更新的offer消息,在更新的offer中锁定一种刚才从Bob收到的媒体格式,使用其锁定的编码格式来传输媒体流。当然,如果Bob这里只能支持N选一(one-of-N codecs)的编码方式的话,他的answer中将会包含一种编码格式,这种情况下,Alice也不需要发起一个re-INVITE请求锁定编码。当然,如果双方的编码协商不能成功的话,需要在B2BUA上进行编码转换的处理。关于编码转换的问题,笔者在历史文档中有深入的讨论,这里不再重复。读者可以参考:编码转换处理
以上是一个关于带物理DSP的SIP话机简单编码协商流程。除了SIP终端之间的协商以外,我们在生产环境中还会看到网关产品的编码协商处理的使用场景。现在,我们通过一个语音网关的编码协商的示例来说明编码协商是如何进行的。再次说明,这里的示例仅是某个厂家的网关的简单示例(多个“m=”行支持),可能和其他厂家的处理机制在细节上有所差别,但是原理基本上都是一致的(遵从RFC 3264规范)。示例中,网关通过offer/answer交互模式,通过第一个“m=”行和端口设置(m=0)接受了所需要的媒体格式,拒绝了不能支持的媒体。注意,这里的编码优先级不会影响offer中编码的选择,仅影响answer中编码选择方式。

语音呼叫环境中,SIP INVITE进入到网关以后的offer和网关返回的answer消息对比:
| 网关配置文件 | 发送到网关的offer | 网关发送的answer |
Default Codec = G.711 ulaw Codec Priority = Local // 可以支持远端 | m=audio 38400 RTP/AVP 0 a=rtpmap:0 PCMU/8000 m=audio 49500 RTP/AVP 8 a=rtpmap:8 PCMA/8000 | m=audio 6000 RTP/AVP 0 // 接受 a=..(忽略) m=audio 0 RTP/AVP 8 // 拒绝 |
offer中提供了两种编码的支持,但是网关发送到answer接受了payload type为0的媒体格式(在answer中是第一个编码),拒绝了payload是8的媒体,设置其端口为0。
黄金法则:
Early offer= SDP in INVITE而 Late offer= SDP in ACK
除了以上协商的策略以外,可能还有一个需要问题需要读者要注意,那就是呼叫流程中“early offer”和“late offer”的问题,因为在B2BUA中涉及了编码协商的机制和编码转换的问题,它们会影响到编码协商的最终结果。最早的SIP应用场景中很多是使用early offer模式,但是随着网络架构和网络环境越来越复杂,更多的性能优化需要调整编码能力或者带宽/QoS等业务要求,所以,在SIP网络中使用later offer的场景也逐渐多了。一些读者可能没有注意这两个概念的不同或比较迷惑这两个概念。首先,讨论简单说明一下什么是“early offer”和“late/delayed offer”。其实,这两个概念也没有太难理解的地方,顾名思义,early offer就是在INVITE请求中包含SDP消息,later offer就是在后续的ACK中包含SDP消息。例如,如果一个UAC发起一个INVITE请求,并且在INVITE(offer消息)做包括了SDP消息,SDP消息中指示了UAC所支持的媒体格式,例如通常使用的G.711和G.723等编码。请求的接收方收到SDP消息后解析编码,然后根据所支持的编码决定接收方UAS使用何种编码来进行媒体传输。注意,UAC(被呼叫方)首先看到的是UAS(呼叫方)在SDP中的编码。这种情况下,相当于UAC已经把可以选择的编码提供给了被呼叫方,被呼叫方没有什么选择的余地,它只能根据UAS提供的编码进行选择。所以,其实在early offer中,UAS具有更多的自主权,而UAC只能从编码中被动选择需要的编码。当然,其优点是UAS降低了编码编码处理的的复杂度。

early offer 示例(INVITE包含SDP)
前面笔者讨论了early offer,呼叫方首先提供了一种选择权让对方来选择。和early offer相反的是later offer。从字面意思也可以看到,early或later仅是一个offer SDP的时间问题。Later offer更多的offer消息的选择权则是由被呼叫方晚一点提供。呼叫方在INVITE呼叫请求发送时,没有包含SDP消息,同时双方也进行正常的INVITE流程处理,被呼叫方返回一个临时响应,例如,发送 180振铃(但是,被呼叫方此时还没有准备好或意识到使用何种编码)或者183(带SDP,下面讨论此问题),紧接着发送 200 ok(带SDP消息,这时开始发送offer消息/later offer)。呼叫方收到 200 ok 以后,结合其offer中的SDP消息,然后返回ACK(包含SDP消息体)。实际上,呼叫方最终决定使用何种编码。

later offer(ACK中包含)
在later offer中的场景中,有两个比较重要的问题可能会影响呼叫。有时,发送了200 ok最终响应的设备会马上发送媒体流,此时对端可能还没有收到200 ok(包括SDP),它们之间的会话也根本没有创建,因此可能出现语音丢失的问题,语音丢失的长度可能完全取决于双方最后协商的时间。所以,有时我们经常遇到,通话开始的前几秒可能双方都听不到对方的声音,或者播放IVR时,IVR语音的前几秒语音丢失。另外一个问题就是在被呼叫方发送临时响应时,它没有发送180 振铃,但是有时发送的是183(带SDP),这样的临时响应携带了媒体。因为此时此刻,没有协商流程还没有完成也没有协商成功,呼叫方的媒体端口或者地址还没有开启,因此,SDP中携带的媒体流可能就会在中途丢失,例如,听不到回铃音,彩铃或者其他的语音业务播放的媒体。这些问题可能需要经过一些B2BUA的处理(在Asterisk或者FreeSWITCH都针对两种offer有相关的具体配置。),有时需要PRACK的支持,在媒体服务器端针对性地进行设置(增加progress处理,等待,或者answer处理等)或者在其呼叫路径中增加一个SBC来处理later offer带来的这些问题。
在接下来的章节中,笔者将继续讨论SDP offer/answer交互模式中NAT处理以及ICE。
参考资料
- https://tools.ietf.org/id/draft-ietf-mmusic-ice-sip-sdp-14.html
- https://tools.ietf.org/html/draft-ietf-rmcat-cc-codec-interactions-00
- https://www.swisscom.ch/content/dam/swisscom/en/about/company/network/all-ip/documents
- https://freeswitch.org/confluence/display/FREESWITCH/Codec+Negotiation
- https://lists.cs.columbia.edu/pipermail/sip-implementors/2018-December/031167.html