第十二章 Dialog Invite会话和Usage
介绍
Dialog invite会话是一个高层的invite会话管理,它可以被应用用来管理invite会话(包括SDP管理)。这个invite会话封装了抽象的基本Dialog,因此应该不需要使用基本Dialog的API,当它使用invite会话API时。
一个Dialog INVITE会话是可以基于每个对话被应用创建的。Dialog INVITE会话被Dialog INVITE usage管理。Dialog INVITE usage将事件从这个Dialog转发到相应的INVITE会话,并且也处理交叉Dialog。
DialogINVITE会话和usage在单独的静态库中实现,即pjsip-ua库。应用必须包括
术语
DialogINVITE会话是一个在一个Dialog中的INVITE会话。如果应用决定使用高层的INVITE会话管理,它需要为每一个Dialog创建唯一一个Dialog INVITE会话实例。
DialogINVITE usage是一个注册到endpoint的PJSIP模块。当一个Dialog有Dialog INVITE session时,这个模块需要作为Dialog usage注册到特定的Dialog。这将在INVITE会话创建的时候自动实现。
特性
DialogINVITE会话提供下面的特性:
INVITE会话状态
DialogINVITE usage提供回调函数来通知应用会话的进度。对于电话应用这是非常有用的,因为通常会话的状态和电话拨打的状态相关。
一个INVITE会话的进度如下状态图所示。
每个状态的描述如下:
PJSIP_INV_STATE_NULL |
会话第一次被创建时的状态。在状态时,没有消息已经被发送或接收。 |
PJSIP_INV_STATE_CALLING |
第一个INVITE消息发送后,在收到任何临时响应之前的会话状态。 |
PJSIP_INV_STATE_INCOMING |
接收到第一个INVITE消息后,在没有发送任何临时响应之前的会话状态。 |
PJSIP_INV_STATE_EARLY |
在Dialog已经发送或接收到INVITE请求的临时响应之后的会话状态,仅当To标签存在时。 |
PJSIP_INV_STATE_CONNECTING |
2xx响应被发送或者接收之后的会话状态 |
PJSIP_INV_STATE_CONFIRMED |
ACK请求被发送或者接收之后的会话状态 |
PJSIP_INV_STATE_DISCONNECTED |
当会话已经失去连接时,或者INVITE的非成功的响应或BYE请求时的会话状态 |
INVITE会话的创建
对于外出的Dialog(即拨打电话的),应用需要使用pjsip_dlg_create_uac()创建一个UAC Dialog。应用接着调用pjsip_inv_create_uac()来创建invite会话,并将这个UAC实例作为这个函数的一个参数传入。应用禁止在invite会话被创建之前发送INVITE请求,或者否则这个INVITE会话将宽松一些事件。
对于到来的Dialog,应用可以通过调用pjsip_inv_verify_request()来验证是否这个请求可以被接收。这个函数验证Supported,Require,和请求的消息体来确认这个请求是否可以被接收。如果这个请求不能被接收,应用将才拒绝的响应。如果这个请求可以被接收,应用创建通过调用pjsip_dlg_create_uas()来创建UAS Dialog。应用接着调用pjsip_inv_create_uas()来为这个Dialog创建invite会话,并将这个UAS Dialog实例,作为一个参数传入。应用禁止在invite会话创建之前发送任何响应,否则这个会话将宽松一些事件。
当一个外出的Dialog交叉时,并且如果在原始的Dialog中存在一个invite会话,这个invite usage将自动为这个新的交叉的Dialog创建invite会话。通过一个回调函数,应用将被调整新的会话的创建。
Invite会话创建函数(即pjsip_inv_create_uac()和pjsip_inv_create_uas()函数)将自动注册invite会话usage到这个dialog。应用不需要调用pjsip_dlg_add_usage()来这吃这个invite usage模块到这个Dialog。
消息处理
Invite会话处理所有能改变会话状态的SIP方法。对于这版本的PJSIP,这个invite会话处理INVITE,BYE,ACK,CANCEL,UPDATE和PRACK方法。
应用必须使用invite会话API来创建和发送请求和响应消息。这是确保请求和响应消息被正确处理并且它包含了适当的被这个会话正在使用的特性(如可靠临时响应)必须的。
应用可以继续使用基本的Dialog API来创建和发送除了上面列出的之外的方法的请求和响应消息。例如,应用可以使用基本的Dialog API来创建和发送一个Dialog中的MESSAGE请求。
扩展Dialog
如上所述,invite会话处理发生在一个Dialog内的INVITE,BYE,ACK,CANCEL,UPDATE和PRACK消息。当应用想要支持或处理其他类型的消息,它必须注册作为Dialog usage注册到Dialog。这将使应用可以处理之前存在的Dialog usages不能处理到来的请求。
应用正确地设置它的模块的优先级。应用的优先级应该被设置成PJSIP_MOD_PRIORITY_APPLICATION。Invite usage将模块优先级设置为(PJSIP_MOD_PRIORITY_APPLICATION-1)。这些将确保这个invite usage可以在应用之前首先检查到来的请求。
扩展Invite会话
未来,invite会话可以被扩展来支持更多的SIP扩展,像呼叫转移,Dialog targetting等等。现在,应用可以通过手动地构建消息来实现这些特性。
指南
Invite会话功能声明在
下面代码展示了可以应用到会话中的不同的选项。当创建一个会话时,这些选项的位遮掩组合需要被指定。在一个Dialog被建立之后(包括早期),这个pjsip_inv_session的options展示了Endpoint的共同的能力。
Invite usage模块
invite usage模块必须在任何invite会话被创建之前被初始化。
初始化invite usage模块并把它注册到Endpoint。Callback参数包含在invite会话中事件发生时将会调用的函数的指针。
得到invite usage模块的实例。
会话回调
pjsip_inv_callback结构包含函数指针,这些指针将被应用注册到invite usage模块用来接收invite会话事件的通知。
回调中的函数如下。
这个回调在invite会话状态改变时被调用。应用应该检查会话的状态(inv_sess->state)来获取当前的状态。
这个回调是强制的。
这个回调在当外出请求交叉时,invite usage模块创建一个新的Dialog和invite会话后被调用。
这个回调是强制的。
这个回调在会话内的transaction发生状态改变时被调用。应用可以实现这个回调,如去检测一个外出消息的进度。
这个回调是可选的。
这个函数在invite会话接收到一个新的offer时被调用。应用使用pjsip_inv_set_sdp_answer()来设置本地answer。这个函数将不会发送外出消息。它只是为SDP协商过程保持这个answer,并且被包含在后续的响应或消息里被发送。
这个回调是可选的。当它没有指定,默认的行为是使用会话的初始能力来协商远端offer。
这个函数在SDP offer/answer会话完成之后被调用。Status参数指定了offer/answer的状态,pjmedia_sdp_neg_negotiate()返回的。
这个回调函数是可选的(从框架的角度来看),但是所有有用的应用通常需要实现这个回调。
会话的创建和终止
为指定的dlg创建UAC invite会话。如果应用已经决定它的媒体能力,它可以指定local_sdp中的SDP。否则它将设置为NULL,让远端的UAS指定一个offer。Options参数是pjsip_inv_options的位遮掩组合。
返回成功时,这个invite会话将被放入inv_sess参数并且这个函数将返回PJ_SUCCESS。否则将返回适当的错误码。
应用应该在接收到最初的rdata的INVITE请求之后,在invite会话创建之前(或者Dialog)调用这个函数来验证这个invite会话是否能处理这个INVITE请求。这个函数验证本地Endpoint是否能处理请求中需要的SIP扩展(即Require头部域)和媒体(如果媒体描述在消息中存在)。
当调用这个函数时,options参数应该包含想要应用到会话中的扩展。当返回时,这个参数将包含在考虑了Supported,Require,和Allow头部域之后将被应用到会话中的SIP扩展。
如果本地媒体能力已经被决定,并且应用想要验证它是否能处理到来的INVITE请求中的媒体offer,它应该验证local_sdp中的本地媒体能力。如果它没有被指定,媒体验证将在这个函数中不会被执行。
如果所有事都协商成功后,这个函数将返回PJ_SUCCESS。否则它将返回失败的原因。
这个函数当验证失败时是可以创建合适的响应消息的。如果tdata被指定,当验证失败时那么一个非2xx最终响应将被创建并返回时会放入tdata参数中。如果在调用这个函数之前一个Dialog已经被创建,那么它必须被指定在dlg参数中。否则应用必须指定endpt参数(这是很有用的,如当应用想要无状态地发送这个响应时)。
创建指定dlg的UAS invite会话。应用必须指定接收到的rdata中的INVITE请求。这个invite会话需要检测这个接收到的请求来看这个请求是否包含它支持的特性。
应用应该在调用这个函数之前调用验证函数,来确保可以成功创建这个会话。
如果应用已经决定它的媒体能力,它能指定这个能力在local_sdp。如果在初始的INVITE中接收到的SDP,这个UAS能力指定在local_sdp不需要匹配这个接收到的offer;SDP协商者能够重新排列answer中的媒体行来匹配这个offer。
Options参数是pjsip_inv_options中的SIP特性的位遮掩。
返回成功时,这个invite会话将被放入inv_sess参数并且这个函数将返回PJ_SUCCESS。否则将返回适当的错误码。
早点终止INVITE会话和销毁下层的Dialog(如果这个Dialog没有其他usage)。这个函数应该只有当INVITE会话初始失败时被调用。通常情况下,应用必须通过调用pjsip_inv_end_session()来终止这个INVITE会话。
St_code参数指定了失去连接的原因的SIP状态码。如果notify为true,应用的回调将被调用。
会话操作
为会话创建初始的INVITE请求。这个函数只能被UAC会话调用。如果创建成功,初始的INVITE请求将被放入tdata参数中。
如果invite会话创建之前,本地媒体能力被指定,这个函数将在外出的INVITE请求中放入一个SDP offer。否则外出的请求将不包含SDP体。
为INVITE请求创建一个响应消息。St_code包含要发送的临时或最终响应的状态码。如果自定义了状态文本,应用可以指定st_text;否则如果这个参数为NULL,默认的状态文本将被使用。
如果应用在UAS invite会话创建期间已经指定了它的媒体能力,local_sdp参数必须为NULL。这是因为应用不能在一个INVITE transaction中执行多于一个SDP offer/answer。
如果应用在UAS invite会话创建期间没有指定它的媒体能力,它可能或必须在local_sdp中指定它的能力,并依据st_code来判断是否是一个2xx最终响应。
创建一个SIP消息来初始化INVITE会话的终止。根据会话的状态,这个函数可能返回CANCEL请求,一个非2xx响应,或者一个BYE请求。如果这个会话没有被到来的INVITE应答,这个函数创建这个非2xx最终响应使用指定的st_code和st_text。
创建一个re-INVITE请求。如果应用想要更新它的本地Contact和通知对端刷新关于这个新联系人,它能指定这个新的联系人在new_contact中,否则这个参数必须为NULL。
当没有悬挂的answer被发送或接收时,应用可以初始化请求中的一个新的SDP offer/answer会话。它能通过观察SDP协商者的状态来检测这个条件。如果新的offer应该被发送到远端,这个offer必须在new_offer中指定,否则这个参数必须为NULL。
发送tdata中的请求或响应消息。token是将被放入transaction的mod_data数组对应的模块索引下的任意应用数组。
辅助API
获取与dlg相关的invite session实例,或者NULL。
获取与tsx相关的invite session实例,或者NULL。