您的位置:首页 > 其它

从零开始学习EasyDarwin之RTSP请求处理过程

2016-03-22 13:05 399 查看
今天详细的看了下RTSPSession的会话处理代码,该模块主要是Run方法内对会话的状态机进行管理,实现对RTSP会话的处理,我以点播Movie文件夹下的视频文件为例,并按照状态机的处理顺序对他们进行一一解析,如下:

进入到RTSPSession::Run()函数中进行请求报文的处理。处理RTSP请求的流程中,DSS主要采用了状态机(state machine)的处理方式

1. 状态机 kReadingFirstRequest

进入初始化状态,实际在此状态中读取请求报文的方式与后续在kReadingRequest状态的读取及处理的方式是一致的,只不过在第一次进行Request处理时,需要检测该请求的方式是以普通RTSP的方式,还是以RTSP-over-HTTP tunneling方式进行报文交互

通过fInputStream.ReadRequest()调用Session绑定的socket,获取数据,该函数有两个返回码:

a) QTSS_NoErr,意味着已经收到该socket的完整数据,但是没有收集到本次请求的全部数据,这时需要接着请求监听读事件,获取更多数据组成完整的RTSP请求消息。

b) QTSS_RequestArrived,意味着此时已经收到完整请求的报文,可以正常进入下一状态机了。

c) E2BIG,表示缓存区已经溢出,默认缓存区的大小为( kRequestBufferSizeInBytes = 4096),进入kHaveNonTunnelMessage状态机,然后在该状态机下响应错误

2. 状态机 kHTTPFilteringRequest

对报文内容进行过滤,检查是否有HTTP报文中一些关键字(POST/GET等),

是则直接进入RTSP-over-HTTP状态处理的下一步:kSocketHasBeenBoundIntoHTTPTunnel状态

否的话进入kHaveNonTunnelMessage状态。

//检测是否为RTSP-over-HTTP tunneling的函数:

QTSS_Error preFilterErr = this->PreFilterForHTTPProxyTunnel();

切入PreFilterForHTTPProxyTunnel(),调用RTSPSession::ParseProxyTunnelHTTP()函数,

在ParseProxyTunnelHTTP函数中,判断HTTP报文中一些关键字

if (theParsedData.EqualIgnoreCase("post", 4 ))
{
fHTTPMethod = kHTTPMethodPost;//Post
}
else if (theParsedData.EqualIgnoreCase("get", 3 ))
{

fHTTPMethod = kHTTPMethodGet;//Get
}


3. 状态机 kHaveNonTunnelMessage

进入此状态,说明请求报文格式是正确的,请求已进入受理状态,具体操作步骤如下:

a) 创建RTSPRequest对象,用于解析RTSP消息;

b) 进入此状态,说明请求报文格式是正确的,请求已进入受理状态,此状态中对fReadMutex,fSessionMutex进行加锁,禁止在处理报文的过程中发送interleaved RTP报文,或者在处理报文过程中插入一个POST Session

c) 对错误码E2BIG、QTSS_BadArgument进行处理,响应qtssClientBadRequest;

d) 将状态机跳转到kFilteringRequest下;

4. 状态机kFilteringRequest

a) 刷新超时任务fTimeoutTask,运转RTSP会话的超时机制。

b) 通过IsDataPacket属性进行判断当前数据是否是一个数据包,而不是一个信令消息。该属性判断方法在RTSPRequestStream::ReadRequest()中。RTP包格式以$字符开头,后面紧跟着一个字节是信道标示符,后面两个字节是数字长度,Darwin就用这个字符区分是否为数据包。

c) 这时第一次开始调用module了,角色为kRTSPFilterRole。注册了该角色模块只有一个QTSSRefMovieModule

d) 在SetupRequest()函数中,解析RTSP消息,同时创建一个客户会话(RTPSession),和产生当前请求的客户端连接相关联,这个会话会一直保持,直到客户端的流播放结束。

// We have an RTSP request and are about to begin processing. We need to
// make sure that anyone sending interleaved data on this session won't
// be allowed to do so until we are done sending our response
// We also make sure that a POST session can't snarf in while we're
// processing the request.
fReadMutex.Lock();
fSessionMutex.Lock();


注意:服务器根据被调用的模块是否对请求做了应答来决定后面的调用(方法HasResponseBeenSent()),如果注册了RTSP Filter Role的某一个模块在被调用的时候对请求作出了应答,服务器将立即调用注册了RTSPPostprocessorRole的模块,不再调用其他尚未调用的注册了RTSP Filter Role的模块,否则服务器调用其它注册了RTSP Filter Role的模块。

5. 状态机kRoutingRequest

这里只走到一个模块QTSSReflectorModule中,调用其RedirectBroadcast()方法,主要是增加两个字段到inParams->inRTSPRequest中。key值分别为qtssRTSPReqRootDir、qtssRTSPReqFilePath。

6. 状态机kPreprocessingRequest

遍历调用所有注册了QTSS_RTSPPreProcessor_Role角色的模块。在这个角色模式下,分别处理了每种RTSP消息,比如本次的点播请求的Describe、Setup、Play指令,模块中针对各种消息都有对应的单独函数处理。 处理完每次RTSP请求后即进入下一状态kPostProcessingRequest,待下轮循环进入本状态机再处理下一个RTSP消息。

kPreprocessingRequest:如果请求的类型互相匹配,则RTSP Preprocessor角色就调用QTSS_Write或者QTSS_WriteV函数来向客户发送数据,对客户端进行响应。如果只需要发送标准响应,则模块可以调用QTSS_SendStandardRTSPResponse,或者QTSS_AppendRTSPHeader和QTSS_SendRTSPHeaders函数。

重要提示

任何处理RTSP Preprocessor角色的模块对客户端进行响应,都会导致服务器跳过注册了RTSP Preprocessor角色的所有其它模块,以及跳过注册了其它RTSP角色的所有模块,并且立即调用响应模块的RTSP Postprocessor角色。

如果没有RTSP Preprocessor角色对RTSP的请求进行响应,则服务器就以RTSP Request(请求)角色调用成功注册了该角色的模块(第一个注册RTSP Request角色的模块,是唯一一个可以注册RTSP Request角色的模块)。RTSP Request角色负责响应所有没有被RTSP Preprocessor角色(的模块)处理过的RTSP请求。

7.状态机kProcessingRequest

RTSP Request角色对请求进行处理之后,服务器就调用注册了RTSP Postprocessor角色的模块。RTSP Postprocessor角色通常执行一些统计任务,比如记录各种统计信息。

处理RTSP Preprocessor或者RTSP Request角色的模块可能需要为特定的客户会话生成一些媒体数据。如果这样的话,模块可以通过调用QTSS_Play函数来实现,这个函数会使模块的RTP Send Packets(RTP发送数据包)角色被调用。

8.RTP数据包发送

RTP Send Packets角色调用QTSS_Write或者QTSS_WriteV函数,在RTP会话的基础上向客户发送数据。当RTP Send Packets角色发送完成一些数据包之后,就会把控制权返回给服务器,并指定服务器下次调用模块的RTP Send Packets角色的间隔时间。这个周期会一直重复,直到所有的媒体数据包被发送完成,或者由于客户请求的原因需要暂停或中止客户会话为止。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息