| 
       
	在Darwin流媒体服务器解析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状态。 
	
	
		- 
			  
 
		- 
			QTSS_Error  preFilterErr = this->PreFilterForHTTPProxyTunnel();  
 
	 
 
	      @3:kHaveNonTunnelMessage:进入此状态,说明请求报文格式是正确的,请求已进入受理状态,故在此状态中对fReadMutex,fSessionMutex进行加锁,禁止在处理报文的过程中接收以RTP Interleaved接收RTP数据或者发出RTSP响应报文 
	
	
		- 
			  
 
		- 
			  
 
		- 
			  
 
		- 
			  
 
		- 
			  
 
		- 
			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(后处理)角色。这里所说的对客户进行响应是指模块向客户端发送任何形式的数据。 
	
		
		
			- 
				  
 
			- 
				numModules = QTSServerInterface::GetNumModulesInRole(QTSSModule::kRTSPFilterRole);  
 
			- 
				for (; (fCurrentModule < numModules) && <span style="color:#ff0000;">((!fRequest->HasResponseBeenSent()) || fModuleState.eventRequested)</span>; fCurrentModule++){}  
 
		 
	 
	
		  
 
	在调用了所有RTSP Filter(过滤器)角色之后,服务器就会对请求进行解析。解析请求包括填充RTSP对象剩余的属性值,以及创建下面两个会话: 
	
		- 
			一个RTSP会话(RTSPSession),和当前这个特定的请求相关联,在客户端关闭与服务器之间的RTSP连接时,这个会话也会被关闭。
 
		- 
			一个客户会话(RTPSession),和产生当前请求的客户端连接相关联,这个会话会一直保持,直到客户端的流播放结束。
 
	 
 
	      @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);  
 
		- 
			<span style="color:#ff0000;">        case QTSS_RTPSendPackets_Role:  
 
		- 
			            return SendPackets(&inParamBlock->rtpSendPacketsParams);  
 
		- 
			</span>        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)   
 
		- 
			        SetPacketTimeStamp(currentTimeStamp, packetDataPtr);  
 
		- 
			      
 
		- 
			       
 
		- 
			       
 
		- 
			       
 
		- 
			       
 
		- 
			     if ((*theFile)->fPacketStruct.suggestedWakeupTime == -1)  
 
		- 
			         <span style="color:#ff0000;">inParams->outNextPacketTime = sFlowControlProbeInterval;      
 
		- 
			            else  
 
		- 
			     {  
 
		- 
			         Assert((*theFile)->fPacketStruct.suggestedWakeupTime > inParams->inCurrentTime);  
 
		- 
			         <span style="color:#ff0000;">inParams->outNextPacketTime = (*theFile)->fPacketStruct.suggestedWakeupTime - inParams->inCurrentTime;  
 
		- 
			            }  
 
		- 
			       
 
		- 
			       
 
		- 
			           
 
		- 
			     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请求进行解析和处理的! 
      (责任编辑:IT) |