SIP的主要功能是允许用户或设备通过消息传递来产生互动,这些消息可以满足以下四种目的:
- 用户向系统注册;
- 邀请用户参加互动会议;
- 协商会议媒体内容的格式;
- 建立两端点或更多人之间的媒体流;
- 结束会议
SIP提供了注册的机制将用户和系统做连接。当SIP电话或终端设备为在线状态时,通常第一件要做的事就是发送REGISTER消息给Registration服务器,告知目前所在的联络位址和相关信息,而Registration服务器则会将用户ID和IP地址结合起来记录在Location Server的数据库里,以供日后使用。注册的机制可以使用户具可移动性的优点,用户只需随时向服务器更新自己的IP地址,便可使用相同ID,在不同地区或设备上让其他联络人找到自己,而不需固定在某一IP地址上。
注册的机制并不是必要的,SIP电话在知道双方IP地址的条件下,也可以不用透过任何服务器达成建立连线的目的。注册的机制通常都有时效的限制,而使用者则必须每隔一段时间和服务器联络,以随时更新目前的位址。
在SIP的系统里,建立会议的定义就是通过发送INVITE消息给一到多个设备建立双方连接,等建立连线后双方便可传送语音、影像或其他数据资料。在SIP里定义的位址可能是传统的电话号码、直接的IP地址或SIP URIs等,当使用者想邀请另一方加入会议,使用者只需在设备里键入对方的位址,并等待对方的回应。
在等待对方回应的过程中,SIP设备会建立INVITE消息并将它传送到网络上,透过Proxy或Redirection Server到Location Server寻找对方真实的IP地址,有了Location Server提供的资讯,系统便可以判断如何将邀请传达给对方。
建立SIP会议的主要目的是用来传送双方的媒体内容,所以在SIP传送的讯息内容中会附加可提供的媒体格式供对方参考。和email服务相同,SIP协议只关心所传送的讯息内容,并不管所附加的内文是什么,而在SIP里最常使用到的附加内文则采用Session Description Protocol (SDP, RFC 2327)协议格式。
当接收方收到INVITE讯息的时候,接收方可以选择接受或拒绝这次的邀请。有时候接收方拒绝邀请的原因是因为它没有足够的能力接受对方所提供的媒体需求,例如接收方可能不提供视讯流服务,这时候只能拒绝对方的邀请。
当对方接受了邀请,代表着此次会议已成功建立,接下来进行传送的便是媒体位元流(media stream)。在SIP里,媒体位元流和SIP协议的讯息是分开建立,并且使用Real-time Transport Protocol (RTP, RFC 1889)进行传送。
和SIP讯息传递不同的是,媒体位元流直接使用得到的IP地址进行点对点的传输,而不需要再次经过SIP讯息用来建立会议的servers。
当使用者结束会议挂断电话的时候,此时设备便会传送SIP的BYE讯息给对方用来结束媒体位元流,连线便正式终止。
在SIP的标准里定义了几个SIP的元件,用来建立和传送SIP讯息,其中主要包含两大类,SIP User Agents和SIP servers。
SIP User Agents是用来建立和接收呼叫的终端装置,它们可能是实体IP电话,或是在PC上执行的软件。SIP Servers则包含了许多会议控制系统,用来提供location、proxy、redirection和registration服务。以下则是这些元件的介绍。
在SIP协议里,电话装置被定义为User Agents (UAs),UAs可视为user agent clients (UACs)和user agent servers (UASs)的通称。当开始建立会议时,UAC是用来建立和传送原始request的一方,而UAS则为所有server型态的泛称,可用来接收requests讯息,并回传response讯息。SIP UAs可用硬件实现,例如IP电话,也可以是在个人电脑里的软件电话。
尽管 UA(用户代理) 包含 server 元素,但当我们提到 SIP servers 时,通常指的是在分散式系统中扮演主要控制角色的 server。在这里,我们讨论四种在 RFC 3261 中定义的 SIP server 类型:
- Location server(位置服务器):Redirect 或 Proxy server (重定向或代理服务器)可以使用位置服务器获取被呼叫端可能的地址信息。
- Proxy server(代理服务器):代理服务器扮演中介者的角色,可以同时是 client(客户)端和server(服务器)端。代理服务器的目的是接收并转发收到的消息到其他服务器。
- Redirect server(重定向服务器):重定向服务器负责将收到的 SIP 请求,找出对应于旧地址的新地址,并返回给客户端。与代理服务器不同的是,重定向服务器不能处理对话,但可以产生 SIP 响应消息,指示用户连接其他 SIP 地址。
- Registrar server(注册服务器):注册服务器负责接收 REGISTER 消息,并将用户发送的地址信息记录在位置服务器中,以供代理或重定向服务器查询用户地址信息。
在我们进一步探讨 SIP 消息的功能之前,我们必须先定义 SIP 组成呼叫的单元,其中包括最基本的消息(Message),以及由消息所组成的事务(Transaction)和会话(Dialog)。
- 消息(Message)是指在用户代理之间传递的数据,它是 SIP 协议的一部分,可以用来建立、修改、结束会议,并分成由客户端发送到服务器端的请求消息(request),以及服务器端发送到客户端的响应消息(response)。
- 事务(Transaction)发生在客户端和服务器之间,它包含单一的请求(request)和针对该请求消息所回复的所有响应消息。不同的请求消息形成不同的事务。图中的建立对话过程包含三个事务,其中步骤1-3是第一个事务,包含一开始 UAC 发送的 INVITE 请求,以及接下来 UAS 发送的 180 Ringing 和 200 OK 响应,步骤4 的 ACK 请求则自己成为一个事务,步骤6-7 是第三个事务,其中包含 UAC 发送的 BYE 请求和 UAS 回复的 OK 响应。
- 对话(Dialog)是 SIP 定义的两个相同用户从会议开始到结束的这一段连续时间里所传递的所有消息。我们可以使用 Call-ID、From tag 和 To tag 这三个不同的标头来表示对话。如果两个消息中的 Call-ID、From tag 和 To tag 都相同,则表示它们属于同一个对话。通过图2.1 可以清楚地了解消息、事务和对话三者之间的关系。
上一节提到了 Message 的定义,其中 Message 又可分为 request 和 response。SIP 定义了许多用来传递信息的 request 和 response 消息,以下介绍几个常见的消息,以及如何使用这些消息达成会议的建立。
1.2.4.1 sip request 信息
Request消息指的是从客户端发送到服务器端的消息。SIP协议将Request消息定义为各种不同的类型,每种类型都可以视为一种动作,用于请求用户代理或服务器提供特定的服务。以下介绍几个常见的Request消息。
- INVITE:初始会议
- ACK:确认会议建立
- BYE:结束会议
- CANCEL:取消尚未建立的会议
- REGISTER:向SIP服务器登记用户地址。
1.2.4.2 sip response 信息
Response指的是从服务器端发送到客户端的消息,用于响应客户端发出的Request消息,Response消息可能包含额外提供的信息,但也可能只是简单的回应,用于防止客户端再次发送Request消息。在SIP协议中,Response消息被分成六大类,其中前五大类借用了HTTP协议,第六大类则由SIP协议建立。以下介绍这六大类Response消息。
a. 1xx Responses: Information Response
代表请求已收到,并且正在执行Request消息所要求的事件。Example: 180 Ringing
b. 2xx Responses: Successful Responses
代表 Request 要求的动作已被成功接收、了解并接受。Example: 200 OK
c. 3xx Responses: Redirection Responses
表示为了完成Request消息需要进行额外的动作。Example: 302 Moved Temporarily。
d. 4xx Responses: Request Failure Responses
代表使用者所发出的Request消息包含了语法上的错误,所以server端无法满足此Request消息的要求。Example: 400 Bad Request。
e. 5xx Responses: Server Failure Responses
代表Server端发生错误而无法满足Request要求。Example: 500 Server Internal Error。
f. 6xx Responses: Global Failure Responses
代表使用者的要求在所有的Server端皆无法被满足。Example: 600 Busy Everywhere。
1.2.4.3 基本呼叫建立
图表示SIP基本呼叫建立和结束的流程,UAC通过拨打对方的IP地址建立新的连接,SIP设备则会向UAS发送INVITE消息。
以下为图所有消息的描述:
a. INVITE:当用户拿起电话开始拨打对方的IP地址时,此时UAC会向UAS发送INVITE消息。INVITE消息通常包含SDP协议的内容,用来告诉UAS可接受的媒体格式。
b. 180 Ringing:当UAS端接收到INVITE消息后,此时UAS的电话铃声就会响起,并回传180 Ringing给UAC端。此回传的Response消息包含了SDP格式的可接受编解码媒体格式。
c. 200 OK:当UAS端的用户接起电话时,此时UAS就会回传200 OK的Response消息给UAC端。
d. ACK:在UAC接收到180 Ringing和200 OK的Response消息之后,为了回应200 OK消息,必须回传ACK request消息给对方。此时就完成了消息传递的三方握手机制,而连接也正式建立。
e. 媒体会话:一旦SIP的连接建立完成,双方用户的媒体通道也会立刻建立,在这里使用Real-time Transport Protocol(RTP,RFC 1889)实现即时媒体传送的目的。
f. BYE:当双方通话结束时,UAC端会向UAS端发送BYE request消息,代表UAC要挂断电话。
g. 200 OK:当UAS端接收到BYE request消息则会回传200 OK消息,此时会议结束,并且媒体通道也会跟着中断。
每个有意义的SIP消息都包含许多头部信息。以下是由UAC生成的INVITE消息的头部信息:
代表消息预期接收的地址,在这个例子里是:
其中,代表接收端的显示名称,则是接收端的SIP URI。To头部的值不会随着经过的服务器改变。
所有的请求和响应消息都需要From的头部位,它代表了消息生成者的地址,在这里的例子是:
其中,The Master Blaster代表发送端的显示名称,sip:则是发送端的SIP URI。From头部由用户事先提供,并有代表发送端的tag位。
提供了一个全世界独一无二的识别码,用来辨别每一次的会议。当UAC开启会谈时就会建立一个Call-ID以便与其他会谈区隔,而所有在同一个会谈中传递的请求和响应消息皆拥有相同的Call-ID。在这里的Call-ID的例子是:
其中,是用随机密码所组成的,而则是邀请端的IP地址。
包含sequence number和请求消息类型。在这里的例子是:
其中1为 ,INVITE则为请求消息的类型。所有的请求和响应消息都需要Cseq头部。的初始值不一定要是1,但必须是整数。而在同一个Call-ID的情况下,每传送一个新的请求消息,Cseq值就会增加。接收端则可藉由不同的Cseq值分辨这是一个新的请求,而不是重新传送的请求。重新传送的Cseq值是相同的。Cseq可以用来区隔和排序每一次的请求消息,而响应消息则可藉由Cseq头部得知所回应的请求消息是什么。
Via头部位记录使用的transport以及所有请求消息经过的地址,以供响应消息找回请求消息发出的地方。在这里的例子是:
从以上的例子可以知道,此消息协议为SIP,版本号码是2.0,并透过UDP协议传输。发送端的地址和port number则为127.0.0.1和5060。同一个消息的Via头部可能不只一个。如果有两个以上的Via头部,则所有Via顺序是非常重要的,因为响应必须沿着这些地址才能回到请求生成的地方。假设请求消息经过SIP Proxy,则此Proxy会将地址记录在Via头部的最上端。
头部里的branch位是由Request-URI、To、From、Call-ID和CSeq number,利用hash function所产生的。branch参数可用来当作transaction id。每产生一个新的transaction,branch值就会不同。
Max-Forwards头部是用来限制请求消息到目的地之前可以经过的hop数目。每经过一个hop,max-forwards的值就会依次遞减1。当proxy收到一个max-forwards的值为0的请求消息,则会将此请求消息丢掉,并回传483 Too Many Hops响应。Max-forwards的建议预设值为70。
JAIN SIP是Java API for SIP的缩写,是Java平台上的一个SIP协议栈,用于开发基于SIP的应用程序,如VoIP电话、会议、即时消息等。JAIN SIP提供了一组API,使开发人员可以轻松地创建、发送和接收SIP消息。它基于标准的SIP协议,支持RFC 3261规范,并提供了一些扩展功能,如负载均衡、可靠传输和安全传输等。JAIN SIP可以与其他Java平台技术(如Java Servlet、JavaBeans和Java Media Framework)无缝集成,使得开发SIP应用程序变得更加容易和灵活。
JAIN SIP包含了四个主要的packages,它们分别是:
:这个package包含了JAIN SIP的核心API,提供了创建、发送和接收SIP消息的基本功能,同时也提供了一些与SIP相关的辅助类和接口,如地址解析、会话管理等。
:这个package含了代表 SIP 协议中 Addressing 部分的内容,其中定义了 URI 接口,而通常被称为 URI 接口又可以分为 SipURI 和 TelURI。
:该package包含了SIP头相关的API,SIP头用于在SIP消息中传递额外的信息,如发件人、收件人、会话参数等。
:该package包含了SIP消息相关的API,提供了SIP消息的创建、解析和操作等功能,同时也提供了一些与SIP消息相关的辅助类和接口。
在JAIN SIP的主要架构中,大量使用了面向对象的工厂模式,使得应用程序的开发与JAIN的实现类架构无关,提高了应用程序的可移植性。在JAIN的架构中,应用程序以SipFactory为中心生成其他相关类或接口,包括AddressFactory、HeaderFactory、MessageFactory和重要的SipStack,如图所示。我们可以通过使用getInstance()方法获取唯一的SipFactory。
当使用 SipFactory 生成其他接口或类时,其中最重要的是 SipStack 接口。我们可以将 SipStack 视为与外界通信的网络接口卡,该接口可用于接收网络上传来的消息或对象,并将其传递给内部应用程序进行处理,或将内部的消息或对象发送到网络上。每个网络接口卡都有一组自己的IP地址,因此。
在生成SipStack对象时,必须先指定该对象的属性(Property),属性的内容包括IP地址、堆栈名称、出站代理等参数,并通过SipFactory的createSipStack(Properties)方法进行建立。
在获得SipStack接口之后,接下来我们需要为应用程序建立SipProvider接口。如图三所示,SipProvider是JAIN SIP事件模型的中心。通过它,我们可以处理从SipStack发送过来的事件,并调用相应的函数进行处理。
每个SipStack可能会有一个以上的SipProvider,每产生一个SipProvider则需要给定一个port端口号,因此不同port端口号的SipProvider则负责处理接收属于自己端口号传来的消息。我们可以通过SipStack的createSipProvider(ListeningPoint)方法来建立新的SipProvider。在JAIN SIP中,端口号是用ListeningPoint接口代表的。
当拥有了SipStack和SipProvider之后,最后一个JAIN SIP中的重要接口为SipListener。SipListener必须向SipProvider注册,以接收从SipProvider传送过来的事件。每个SipListener可以向多个SipProvider注册。我们可以将SipListener接口视为开发者所实现的应用程序。
当实现SipListener接口时,必须包含processRequest()、processResponse()以及processTimeout()三种方法,分别处理接收到的request、response和timeout事件。
当SipProvider接收到来自SipStack的Message时(Request或Response),SipProvider会将该Request或Response加入Event对象内,并透过对应的Event Handler通知SipListener。例如,当接收到Request时,SipProvider会呼叫已登记SipListener的processRequest()方法,此时应用程序便可作适当的处理。
当我们介绍SipFactory接口时,提到SipFactory可以帮助我们创建MessageFactory,使用MessageFactory可以创建SIP协议中的Request和Response消息对象。在JAIN SIP中,Message对象可视为Request和Response的泛型,目的是将Request和Response共用的方法集合并在同一接口中。Request和Response分别对应SIP协议中的各种Request和Response类型,使用MessageFactory创建时,应用程序需要指定所需的类型信息(例如INVITE、ACK或180、200等)。
AddressFactory是由SipFactory生成的,生成后应用程序利用此对象生成Sip协议的各种URI相关对象,其中URI为SipURI和TelURL的泛型(Generic Type),目的是将SipURI和TelURL两者共用的方法集中在同一界面上,并预留日后扩展之用。另外值得注意的是,Address对象除了包含URI对象之外,还包含用来显示用户名的display name,display name通常会显示在用户界面上,以方便用户判断来电者为何。
HeaderFactory是由SipFactory生成的,生成后应用程序利用此对象生成Sip Protocol的所有Header相关对象,例如ToHeader、FromHeader、CseqHeader等。JAIN SIP规格将每一个不同类型的头定义为对象,并且所有在RFC3261中定义的Header在API中都有相应的类别。
具体版本可以参考 https://mvnrepository.com/artifact/javax.sip
因为jainsip 用到了log4j,所以pom里也要加入,不然会报错
当使用 SipFactory 生成其他接口或类时,其中最重要的是 SipStack 接口。我们可以将 SipStack 视为与外界通信的网络接口卡,该接口可用于接收网络上传来的消息或对象,并将其传递给内部应用程序进行处理,或将内部的消息或对象发送到网络上。
每个网络接口卡都有一组自己的IP地址,因此在同一个应用程序中,一个IP地址只能对应一个SipStack。在生成SipStack对象时,必须先指定该对象的属性(Property),属性的内容包括IP地址、堆栈名称、出站代理等参数,并通过SipFactory的createSipStack(Properties)方法进行建立。
每一个SipStack对象都有属于自己的名称、IP地址等属性,如图所示。
因此,在建立SipStack之前,我们必须建立Properties对象以存储这些属性,并使用这些属性生成SipStack。
在建立SipStack对象之前,必须先创建用于存储SipStack属性的Properties对象。
首先创建新的properties对象。
有了SipFactory对象之后,我们就可以使用它的createSipStack()方法创建新的SipStack。在创建时,必须提供预先设置好的Property对象。
如图所示,我们可以通过SIPStack对象创建新的ListeningPoint,ListeningPoint代表网络接口的端口号,因此在创建时必须指定端口号和使用的传输协议。
同样地,我们也可以通过SIPStack对象创建SIPProvider对象,用于接收和处理SIPStack发送的事件。在创建时,必须提供之前创建的ListeningPoint,表示此SipProvider在接收事件时侦听的端口号。
以下是创建ListeningPoint的示例代码。
有了SipStack对象之后,我们便可以使用它来建立ListeningPoint。以下是建立一个使用UDP传输的ListeningPoint对象的示例代码:
同样地,我们也可以建立一个使用TCP传输的ListeningPoint对象:
在这里,myPort参数是我们用来发送消息的端口号,通常为5060。建立完ListeningPoint对象后,我们可以将其绑定到SIPProvider对象上,以接收和处理SIPStack发送的事件。
通过SipStack的createSipProvider()方法,我们可以创建属于此SipStack的SipProvider对象。在创建时,必须提供之前创建的ListeningPoint对象作为参数。以下是示例代码:
这样,我们就可以使用该SipProvider对象接收和处理SIPStack发送的事件。
我们在实现应用程序时需要实现SipListener接口,该接口的方法将在下一节中介绍。在这里假设我们实现的SipListener名称为JainSipPhone,因此我们将JainSipPhone指定为this(其实就是),表示当前编写的应用程序
每个SipListener都可以向SipProvider注册,以接收从SipProvider传送过来的消息。在JAIN API中,我们可以通过以下方式将SipListener注册到SipProvider上
这样,我们就可以使用该SipListener对象接收和处理从SipProvider发送的SIP消息
在成功建立JAIN SIP的基础对象之后,我们可以开始编写程序的第一步:UAC向UAS发送INVITE消息。
在构建消息之前,我们必须先构建消息中包含的所有头部。以INVITE消息为例,成功构建一个INVITE消息需要包含六个头部对象:CSeqHeader、FromHeader、ToHeader、ViaHeaders、MaxForwards、CallIDHeader。
以下是构建和发送INVITE消息的原始代码:
在介绍 UAS 端如何处理收到的 INVITE 消息并回传 Response 消息时,我们先介绍 SipProvider 和 SipListener 之间的事件处理关系,如图 3.12 所示。
SipListener 是我们实现应用程序的一部分,其目的是为了接收从外界传来的事件。这些事件包括 Request Event、Response Event 和 Timeout Event,这些事件通过 SipProvider 发送给 SipListener 实现的方法。例如,SipProvider 通过 processRequest(RequestEvent) 方法发送 Request 事件。当 SipListener 接收到事件后,将进行相应的处理。在这里,我们需要编写处理这些事件的代码。
假设JainSipPhone 是我们实现的应用程序。
现在介绍UAC如何处理UAS所发送过来的Response消息,并发送ACK Request来完成三方握手协议。处理Response的方法是实现processResponse()方法,在这里我们以收到200/OK response为例。以下是processResponse()的原始代码。
当UAS收到ACK消息之后,代表SIP会议已经成功建立,接下来则需要开启媒体比特流,进行媒体会议。
在本节中,我们将讨论UAS如何通过SipListener实现的processorRequest()方法处理ACK请求,以下是处理ACK请求的原始代码。
在网络多媒体通话场景下,会议的参与者往往是用 SDP 协议来传递、协商媒体详细信息、网络地址和其他元数据。SDP 协议的全称是 session description protocol,含义是会话描述协议,它不是一种传输协议,而是由 ITEF 组织下的 MMusic 工作组设计的一种会话描述格式。
SDP 最常用于 RTC 实时通话的协商过程,在 WebRTC 中,通信双方在连接阶段使用 SDP 来协商后续传输过程中使用的音视频编解码器(codec)、主机候选地址、网络传输协议等。
在实际的应用过程中,通信双方可以使用 HTTP、WebSocket、DataChannel 等传输协议来相互传送 SDP 内容,这个过程称作 offer/answer 交换,也就是发起方发送 offer,接收方收到 offer 后回复一个 answer。例如在下图的服务端架构中,客户端将 offer 发送给信令服务器,信令服务器转发给媒体服务器,媒体服务器将 offer 和自身的能力进行比较后得到 answer,信令服务器再将 answer转发给客户端,随后客户端和媒体服务器就可以进行 RTP 通信。
SDP 协议的设计可以参考 rfc4566 文档。它是一种具有特殊约定格式的纯文本描述文档,也就是它的内容都是由 UTF-8 编码的文本,有点类似于 JSON/XML。一个 SDP 会话描述包括若干行 type=value 形式的文本,其中 type 是一个区分大小写的字母,例如 v、m 等,value 是一个结构化的文本,格式不固定。通常 value 由若干分割符隔开的字段组成或者是一个字符串, 整个协议文本区分大小写。"=" 两侧不允许有空格存在。
SDP 由一个会话级描述(session level description)和多个媒体级描述(media level description)组成。会话级描述的作用域是整个会话,在 SDP 中,从 "v=" 行开始到第一个 "m=" 行之前都是属于会话级描述的内容。媒体级描述对某个媒体流的内容进行描述,例如某个音频流或者某个视频流,从某个 "m=" 行开始到下个 "m=" 行之前是属于一个媒体级描述的内容。
总之,除非媒体部分重载,会话级的值是各个媒体的缺省默认值(就是说媒体级描述其实也是一个会话级描述,只不过没写出来的会话级描述参数都用的缺省值)。
SDP 中有的字段是必须的,有的字段是可选的,可选的字段在如下的示例中都使用 进行标记。 SDP 中 type 出现的顺序是固定的,按照如下顺序进行排列,这样可以增强解析器错误检测的能力,另外也可以简化解析器的实现。
Sessiondescription 会话级描述
Time description 时间描述
Media description 媒体描述
上面的有些行是有的,有些行是的。。(这是对于会话级描述和媒体及描述总体而言的,对于媒体级描述而言只有m=是必须的)。
可以看到 type 都是单个小写字母,它们是不可扩展的,如果 SDP 解析器不能解析某个 type 则必须忽略, 是 SDP 的主要扩展手段,来针对某中特定类型的媒体或应用做扩展。
有一个很好的网站:webrtchacks.com/sdp-anatomy… 可用于学习 SDP,这个网站里面鼠标移动到 SDP 某一行时,就会显示这一行 SDP 的具体含义。在这里简单介绍一下 SDP 协议中常用的 type 以及对应的含义。
v 的含义是 SDP 协议的版本号,目前 v 都是 0。
会话所有者有关的参数,包括用户名、session 信息,地址信息等。 (Owner/creator and session identifier)。
- username: 会话发起者的名称。如果不提供则用"-"表示,用户名不能包含空格;
- session-id: 主叫方的会话标识符;
- session-version: 会话版本号,一般为 0;
- nettype: 网络类型,目前仅使用 IN 来表示 Internet 网络类型;
- addrtype: 地址类型,可以是 IPV4 和 IPV6 两种地址类型;
- unicast-address:会话发起者的 IP 地址。
本次会话的标题或会话的名称(Session name)。
会话的起始时间和结束时间(Time session starts and stops),如果没有规定这两个时间的话,都写为 0 即可。
媒体行,描述了发送方所支持的媒体类型等信息(Media information)。
- media:媒体类型,可以为 "audio"、"video"、"text"、"application"、"message",表示音频类型、视频类型、文本类型、应用类型、消息类型等,以后也可能扩展其他类型;
- port/number of ports: 流传输端口号。表示在对应的本地端口上发送流;
- proto:流传输协议。举例说明:
- RTP/SAVPF 表示用 UDP 传输 RTP 包;
- TCP/RTP/SAVPF 表示用 TCP 传输 RTP 包;
- UDP/TLS/RTP/SAVPF 表示用 UDP 来传输 RTP 包,并且使用 TLS 加密;
- 最后的 SAVPF 还有其他几种值:AVP, SAVP, AVPF, SAVPF。
- AVP 意为 AV profile
- S 意为 secure
- F 意为 feedback
fmt 表示媒体格式描述,它可能是一串数字,代表多个媒体,这个字段的含义与 proto 字段的类型相关。在后面,可以使用"a=rtpmap:"、"a=fmtp:"、"a=rtcp-fb" 等扩展字段来对 fmt 进行说明。
一个会话描述必须在每个媒体层都包含“c=”字段或者在会话层包含一个“c=”字段。如果这两个层都出现的话,则媒体层出现的“c=”会覆盖会话层出现的“c=”字段的值。
- nettype: 是一个文本字符串,目前只定义了“IN”,表示“Internet”,未来会定义其他值。
- addrtype: 目前只定义了 IP4 和 IP6。
- connection-address: 标志连接的地址。取决于 addrtype 字段的不同,在 connection-address 之后可能也会跟随其他的字段。
这个字段的意思是本会话或者媒体所需占用的带宽。 bwtype 可以为 "CT" 或者 "AS",给出了 bandwidth(单位 kbps)数字所代表的含义:
CT:表示会话所占的所有的带宽的大小。当用于 RTP 会话时,表示所有的 RTP 会话所占用的带宽。
AS:这个带宽类型是针对特定应用的。通常,这表示某应用所占用的最大带宽。当用于 RTP 会话时,表示单一 RTP 会话所占用的带宽,可以理解为 CT 代表的是整个通话过程的带宽,AS 代表的是某个流的带宽。
如果在一个安全的信道上传输 SDP 消息,那么 SDP 之中也可以携带密钥,携带的方式就是采用字段 "k="。当然这种方式目前已经不推荐了。
字段 "k=" 可以是全局的,也可以是放在某个 "m=" 中的,分别代表应用于所有的媒体流,或者单独应用于某条媒体流。定义格式有如下几种:
- k=clear:
在这种方法中,密钥是没有经过任何转换的。除非传输通道是绝对安全的,否则不应当使用这种方法。
- k=base64:
在这种方法中,密钥经过 base64 的编码。除非保证传输通道绝对安全,否则不应当使用这种方法。
- k=uri:
在这种方法中,给出一个 URI。通过这个 URI,可以获得密钥,访问 URI 的过程中可能还需要认证。
- k=prompt
在这种方法中,没有给出密钥。但是加上这个字段后,当用户加入会话时会提示其输入密钥。这种方式目前也不推荐。
a 表示的是属性。a 字段是扩展 SDP 的主要方式,有会话层属性和媒体层属性。会话层的属性应用于所有的媒体流,媒体层的属性只应用于当前的媒体流。
属性有两种方式:
- 特性属性,a= 表示,例如:"a=recvonly"
- 值属性,以a=:表示,例如"a=ice-ufrag:khLS"
常用的属性列表如下:
a=rtpmap:111 opus/48000/2 a=fmtp: Format transport a=fmtp:111 minptime=10 a=rtcp: Explicit RTCP port (and address) a=rtcp:54278 IN IP4 180.6.6.6 a=mid: Media identification grouping a=mid:audio a=ssrc: "ssrc" indicates a property (known as a"source-level attribute") of a media source (RTP stream) within an RTP session a=ssrc: msid:GUKF430Khp9jEQiPrdYe0LbTAALiNAKAIfl2 ea-e126-4573-bea3-bfba519b4d59 a=ssrc-group: Ssrc identification grouping a=ssrc-group:SIMULCAST 32040 32142
a=ssrc:32040 imageattr:96 [x=1280,y=720]
a=ssrc:32142 imageattr:96 [x=640,y=480] a=ice-ufrag: a=ice-pwd: The "ice-ufrag" and "ice-pwd" attributes convey the username fragment and password used by ICE for message integrity a=ice-ufrag:kwlYyWNjhC9JBe/V
a=ice-pwd:AU/SQPupllyS0SDG/eRWDCfA a=ice-pwd: a=fingerprint: A certificate fingerprint is a secure one-way hash of the DER (distinguished encoding rules) form of the certificate. a=fingerprint:sha-256 D1:2C:BE:AD:C4:F6:64:5C:25:16:11:9C:AF:E7:0F:73:79:36:4E:9C:1E:15:54:39:0C:06:8B:ED:96:86:00:39 a=candidate: It contains a transport address for a candidate that can be used for connectivity checks. a=candidate: 1 udp 192.168.0.197 36768 typ host generation 0 a=ptime: Length of time in milliseconds for each packet a=ptime:20 a=recvonly Receive only mode a=recvonly a=sendrecv Send and receive mode a=sendrecv a=sendonly Send only mode a=sendonly a=sdplang: Language for the session description a=framerate: Maximum video frame rate in frames per second a=framerate:15 a=inactive Inactive mode a=inactive
SDP 协商过程对应的 RFC 文档是 rfc3264。
在 WebRTC 通信过程中,连接的两端通过 交换过程来互相进行 SDP 协商,发起方发送 offer 给接收方,其中包含了发起方的媒体能力和其他信息(包括 ICE、DTLS fingerprint,SSRC 等),接收方收到 offer 后和自己支持的能力进行比较取出交集,回复 answer 给发起方,其中包含了协商之后的媒体能力和其他信息(ICE、DTLS fingerprint,SSRC 等),要注意这个过程中只有媒体能力是协商的,也就是发起方和接收方共同支持的媒体能力,例如支持的编解码格式,以及是否支持 RTX、FEC、TCC 等 QoS 功能,而 ICE、DTLS、SSRC 的信息是直接给出通知对方,是连接双方各自的信息。
在 offer/answer 交换完成之后,双方就可以使用这些共同支持的媒体能力进行通信。
协商的过程如下图所示:
A 向服务器发送 offer 携带自己的 SDP 信息,包括:
- 地址信息是 UDP 10.124.17.11:32132,A 通过这个地址来和服务端进行媒体通信;
- ICE 信息,服务器通过 ICE 信息来验证客户端 A 的身份;
- 在接下来的媒体通信过程,客户端 A 只发不收 RTP;
- A 音频支持 48kHz 的双声道 Opus 编解码,使用 SSRC=1111 来发送;
- A 视频支持 VP8 或者 VP9 或者 H264 编解码,使用 SSRC=2222 来发送; 服务器在收到 A 的 offer 后,回复给 A 一个 answer,这是双方协商出来的媒体能力:
- 地址信息是 UDP 10.108.27.64:8888,服务器通过这个地址来和 A 进行媒体通信;
- ICE 信息,A 通过 ICE 信息来验证服务器的身份;
- 在接下来的媒体通信过程,服务器对于 A 来说只收不发 RTP;
- 协商出来的音频能力是 48kHz 的双声道 Opus 编解码;
- 协商出来的视频能力是 H264 编解码。 B 向服务器发送 offer 携带自己的 SDP 信息,包括:
- 地址信息是 UDP 192.168.1.1:32222,B 通过这个地址来和服务端进行媒体通信;
- ICE 信息,服务器通过 ICE 信息来验证客户端 B 的身份;
- 在接下来的媒体通信过程,客户端 A 需要收发 RTP;
- B 音频支持 48kHz 的双声道 Opus 编解码或者 G711 编解码,使用 SSRC=1234 来进行发送;
- B 视频支持 H264,使用 SSRC=4567 来进行发送; 服务器在收到 B 的 offer 后,回复给 B 一个 answer,这是双方协商出来的媒体能力:
- 地址信息是 UDP 10.108.27.64:8888,服务器通过这个地址来和 B 进行媒体通信;
- ICE 信息,B 通过 ICE 信息来验证服务器的身份;
- 在接下来的媒体通信过程,服务器对于 B 来说可以收发 RTP;
- 协商出来的音频能力是 Opus 编解码、48KHz,双声道,使用 SSRC=1122 进行发送;
- 协商出来的视频能力是 H264 编解码,使用 SSRC=3344 可以发送。 从以上两个 offer/answer 交换过程中可以看出,上图中的媒体服务器音频支持 Opus 编解码,但不支持 G711 编解码;视频支持 H264 编解码,但不支持 VP8、VP9 编解码。
在介绍完 SDP 的文本结构和 SDP 的协商过程中后,这里我们举一个实际传输的 SDP 内容来帮助理解。实际工程中,客户端和服务端都会支持好几种音视频媒体编解码,并且可能支持好几种能力,例如 FEC、NACK、TCC 等等,所以实际工程中的 SDP 都会非常长。为了方便阅读,下文将直接在 SDP 中每一行上方的注释中解释其含义。这个 SDP 中包括了 audio 和 video 两种流,video 的内容有部分也在 audio 中出现过,因此不再重复解释。
RTP定义:Real-time Transport Protocol,是由IETF的多媒体传输工作小组于1996年在RFC 1889中公布的。RTP为IP上的语音、图像等需要实时传输的多媒体数据提供端对端的传输服务,但本身无法保证服务质量(QoS),因此,需要配合实时传输控制协议(RTCP)一起使用。
RTCP定义:Real-time Transport Control Protocol,监控服务质量并传送会话参与者信息,服务器可利用RTCP数据包信息改变传输速率、负载数据类型。
流媒体:使用流式传输技术的连续时基媒体。使用流式传输可以边下载边播放,无需等待音频或视频数据信息全部下载完成后再播放。
混频器(Mixer):一种中间系统,将一个或多个源的RTP数据包合成一个新的RTP数据包,然后转发出去。混频器可能会改变数据包的数据格式,并对各个流组合的新数据包生成一个新SSRC。
转换器(Translator):一种中间系统,转发RTP数据包但不改变数据包的同步源标识符,可用于通过IP多播无法直接到达的用户区,如在防火墙两端使用转换器,外侧转换器通过安全连接将数据传输到内侧转换器。
RTP利用混频器和转换器完成实时数据传输,混频器接收来自一个或多个发送方的RTP数据包,并把它们组合成一个新的RTP数据包继续转发。这个组合数据包使用新的SSRC标识,组合数据包将作为新的发送方加入到RTP传输中。混频器将不同的媒体流组合在一起,需要通过转换器来对单个媒体流进行操作,可进行编码转换或协议翻译。典型的RTP数据包传输流程如下图所示,其中S1、S2、S3、S4是数据源的发送端,R4是RTP数据包的接收端。
RTP和RTCP位于传输层,但运行在UDP协议之上。UDP协议实时性更好,可减少数据传输延时,另外,应用程序在UDP上运行RTP还可利用UDP的多路复用,校验和服务。
RTCP向RTP会话中的所有成员周期性的发送控制包,RTCP使用和RTP数据包相同的传输机制。RTP会话使用合法的偶数端口(2n),对应的RTCP包使用下一个奇数端口(2n+1)。
每帧RTP报文都是由头部(Header)和负载数据(Payload)两部分组成,头部前12个字节固定,存在于每一个RTP数据包中,最后的CSRC列表只在Mixer中使用。负载数据可以是音频数据或视频数据。
版本号(V):2比特,用来标志使用的RTP版本。
填充位(P):1比特,如果该位置位,则该RTP包的尾部就包含附加的填充字节。
扩展位(X): 1比特,如果该位置位的话,RTP固定头部后面就跟有一个扩展头部。
CSRC计数器(CC):4比特,含有固定头部后面跟着的CSRC的数目。
标记位(M): 1比特,该位的解释由配置文档(Profile)来承担.
载荷类型(PayloadType): 7比特,标识了RTP载荷的类型。
序列号(SN):16比特,每发送一个 RTP 数据包,序列号增加1。接收端可以据此检测丢包和重建包序列。
时间戳(Timestamp): 2比特,记录了该包中数据的第一个字节的采样时刻。在一次会话开始时,时间戳初始化成一个初始值。即使在没有信号发送时,时间戳的数值也要随时间而不断地增加(时间在流逝嘛)。时钟频率依赖于负载数据格式,并在描述文件(profile)中进行描述。
同步源标识符(SSRC):32比特,同步源就是指RTP包流的来源。在同一个RTP会话中不能有两个相同的SSRC值。该标识符是随机选取的 RFC1889推荐了MD5随机算法。
贡献源列表(CSRC List):0~15项,每项32比特,用来标志对一个RTP混合器产生的新包有贡献的所有RTP包的源。由混合器将这些有贡献的SSRC标识符插入表中。SSRC标识符都被列出来,以便接收端能正确指出交谈双方的身份。
RTP只负责传输数据包,需要与RTCP配合使用,由RTCP来保证RTP数据包的服务质量。RTCP的主要功能:服务质量的监控和反馈、媒体设备间的同步以及多播组中的成员标识。在RTP会话期间,各参与者周期性传送RTCP数据包,RTCP数据包中包含已发送的数据包数量、丢失的数据包数量等信息,各参与者通过这些信息动态改变传输速率或传输的数据类型。
RTCP可划分成五种类型,如下表所示:
RTP和RTCP协作传输多媒体数据的流程图如下图所示,在一个RTP会话中有发送端和接收端,发送端将数据封装到RTP中发送,同时以一定的时间间隔周期性发送RTCP中的发送报告(SR),也收到接收端发过来的接收报告(RR)或从其他发送端发送过来的发送报告(SR)。接收端获取到RTP数据包后解析数据,取得应用数据,获取其他参与者发送的发送报告(SR),接收数据的同时通过发送接收报告(RR)将RTCP反馈信息发送出去。
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.bianchenghao6.com/java-jiao-cheng/10395.html