EasyDarwin

面向企业级的流媒体平台框架

EasyDarwin开始于2013年,遵循 Apple开源License APSL,为了敏捷流媒体服务器开发和简化企业流媒体应用开发而诞生的。

详细 >>

EasyDarwin RTSP请求的处理过程


分析RTSPSession对每一个RTSP请求的处理过程...

EasyDarwin流媒体服务器解析1中,我们分析了建立一路RTSP Session的过程,本文将具体深入到RTSPSession内部,分析RTSPSession对每一个RTSP请求的处理过程:

继续在Darwin流媒体服务器解析1中最后说到的,进入到RTSPSession::Run()函数中进行请求报文的处理。处理RTSP请求的流程中,DSS主要采用了状态机(state machine)的处理方式。

这里流程不太一样的就是第一次对请求报文进行处理,后续的所有请求流程大体一致(非第一次请求则直接从kReadingRequest状态转到@3状态):

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

if (err == QTSS_RequestArrived)//此时收到完整请求的报文
fState = kHTTPFilteringRequest;

@2:kHTTPFilteringRequest:对报文内容进行过滤,检查是否有HTTP报文中所出现的一些关键字(POST/GET等),是则直接进入RTSP-over-HTTP状态处理的下一步:kSocketHasBeenBoundIntoHTTPTunnel(此处因为并没有这种需求,故不进行深入),否的话进入@3kHaveNonTunnelMessage状态。

//检测是否为RTSP-over-HTTP tunneling
QTSS_Error preFilterErr = this->PreFilterForHTTPProxyTunnel();

@3:kHaveNonTunnelMessage:进入此状态,说明请求报文格式是正确的,请求已进入受理状态,故在此状态中对fReadMutex,fSessionMutex进行加锁,禁止在处理报文的过程中接收以RTP Interleaved接收RTP数据或者发出RTSP响应报文

// 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();

重置响应缓冲区并进入下一个状态(中间有一些对错误报文[E2BIG/QTSS_BadArgument/etc..]的响应此处就忽略)@4kFilteringRequest

@4:kFilteringRequest:在处理RTSP请求的时候,服务器调用RTSP Filter(过滤器)角色。它会调用所有注册了RTSP Filter角色的模块,并将RTSP请求对象作为参数传递给它。每个RTSP Filter角色都可以改变qtssRTSPReqFullRequest属性的值。比如说,一个RTSP Filter角色可以把/foo/foo.mov改为/bar/bar.mov,从而改变用于满足请求的文件夹。

重要提示

任何处理RTSP Filter角色的模块对客户端进行响应,都会导致服务器跳过其它已经注册了RTSP Filter角色的模块,以及跳过已经注册其它RTSP角色的模块,然后立即调用该响应模块的RTSP Postprocessor(后处理)角色。这里所说的对客户进行响应是指模块向客户端发送任何形式的数据。

// Invoke filter modules
numModules = QTSServerInterface::GetNumModulesInRole(QTSSModule::kRTSPFilterRole);
for (; (fCurrentModule < numModules) && ((!fRequest->HasResponseBeenSent()) ||
fModuleState.eventRequested); fCurrentModule++){}

在调用了所有RTSP Filter(过滤器)角色之后,服务器就会对请求进行解析。解析请求包括填充RTSP对象剩余的属性值,以及创建下面两个会话:

一个RTSP会话(RTSPSession),和当前这个特定的请求相关联,在客户端关闭与服务器之间的RTSP连接时,这个会话也会被关闭。

一个客户会话(RTPSession),和产生当前请求的客户端连接相关联,这个会话会一直保持,直到客户端的流播放结束。

// Otherwise, this is a normal request, so parse it and get the RTPSession.
this->SetupRequest();

@5:kRoutingRequest:RTSP请求解析完成之后,服务器会以RTSP Route(路由)角色调用所有注册了该角色的模块,并传入一个RTSP对象。每个RTSP Route角色都可以使用RTSP对象中的属性值来确定是否要改变qtssRTSPReqRootDir属性的值,进而改变用于处理当前请求的目录。举例来说,如果语言的类型是French(法语),则模块可以把qtssRTSPReqRootDir属性修改到包含法语版本的被请求文件的目录下。

重要提示

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

在调用了所有RTSP Route角色之后,服务器就会以RTSP Preprocessor角色调用每个注册了该角色的模块。通常情况下,RTSP Preprocessor角色会通过qtssRTSPReqAbsoluteURL属性值来确定当前请求是否和模块处理的请求的类型相匹配。

@6: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角色的间隔时间。这个周期会一直重复,直到所有的媒体数据包被发送完成,或者由于客户请求的原因需要暂停或中止客户会话为止。

QTSS_Error QTSSFileModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParamBlock)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParamBlock->regParams);
case QTSS_Initialize_Role:
return Initialize(&inParamBlock->initParams);
case QTSS_RereadPrefs_Role:
return RereadPrefs();
case QTSS_RTSPRequest_Role:
return ProcessRTSPRequest(&inParamBlock->rtspRequestParams);
case QTSS_RTPSendPackets_Role:
return SendPackets(&inParamBlock->rtpSendPacketsParams);
case QTSS_ClientSessionClosing_Role:
return DestroySession(&inParamBlock->clientSessionClosingParams);
}
return QTSS_NoErr;
}

theErr = QTSS_Write(theStream, &(*theFile)->fPacketStruct, (*theFile)->fNextPacketLen, NULL, theFlags);
//每一次发送返回在发送后是否继续的消息
isBeginningOfWriteBurst = false;
if ( theErr == QTSS_WouldBlock )//阻塞,等待下一次服务器唤醒
{
if (currentTimeStamp != pauseTimeStamp) // reset the packet time stamp so we adjust it again when we really do send it
SetPacketTimeStamp(currentTimeStamp, packetDataPtr);
// 如果在返回参数中设置了下一次唤醒的时间间隔则
// In the case of a QTSS_WouldBlock error, the packetTransmitTime field of the packet struct will be set to
// the time to wakeup, or -1 if not known.
// If the time to wakeup is not given by the server, just give a fixed guess interval
if ((*theFile)->fPacketStruct.suggestedWakeupTime == -1)
inParams->outNextPacketTime = sFlowControlProbeInterval; // for buffering, try me again in # MSec不确定下次唤醒时间
else
{
Assert((*theFile)->fPacketStruct.suggestedWakeupTime > inParams->inCurrentTime);
inParams->outNextPacketTime = (*theFile)->fPacketStruct.suggestedWakeupTime - inParams->inCurrentTime;//确定下次唤醒时间
}
//qtss_printf("Call again: %qd\n", inParams->outNextPacketTime);
return QTSS_NoErr;
}
else//继续发送
{
(void) QTSS_SetValue(theStream, sRTPStreamLastSentPacketSeqNumAttrID, 0, &curSeqNum, sizeof(curSeqNum));
(*theFile)->fPacketStruct.packetData = NULL;
}

至此,一路RTSP Request的框架处理过程已经打通,其中能够包括:OPTIONS/DESCRIBE/SETUP/PLAY/TEARDOWN/PAUSE/SET_PARAMETER等RTSP请求,下一节我们将深入进行到RTSPSession所请求的Role Module中,看他们是如何RTSP请求进行解析和处理的!



------------------------------ 分割线 ------------------------------
本站文章除注明转载外,均为本站原创或编辑,欢迎大家转载,但请务必注明出处,尊重他人成果,谢谢。
转载请注明:EasyDarwin;非本站文章均来自互联网,转载目的在于传递更多信息,并不代表本站赞同其观点和对其真实性负责。
文章中如果有错误可联系:johnson@easydarwin.org
推荐文章
安防和移动互联网行业比较火热的移动端手机......>
有人问到像美拍、秒拍这些短视频拍摄是怎么......>
最近更新
Easydarwin中大量使用gettimeofday来获取系......>
最近很多EasyDarwin爱好者提出了手机移动端......>
第一个到达的音频RTP包就将作为音频的关键帧......>
打开手机直播立即就能显示出主播视频,其实就......>
h264的功能分为两层,视频编码层(VCL)和网......>
友情链接 | 申请链接
EasyDarwin官方微信!