当前位置: > Linux服务器 >

Darwin Streaming Server中RTSP请求的处理过程(Darwin流媒体服务

时间:2014-09-17 12:17来源:未知 作者:admin

在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。

 

[cpp] view plaincopy
  1. if (err == QTSS_RequestArrived)//此时收到完整请求的报文  
  2.                     fState = kHTTPFilteringRequest;  

 

 

 

 

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

[cpp] view plaincopy
  1. //检测是否为RTSP-over-HTTP tunneling  
  2. QTSS_Error  preFilterErr = this->PreFilterForHTTPProxyTunnel();  
[cpp] view plaincopy
  1.    

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

[cpp] view plaincopy
  1. // We have an RTSP request and are about to begin processing. We need to  
  2. // make sure that anyone sending interleaved data on this session won't  
  3. // be allowed to do so until we are done sending our response  
  4. // We also make sure that a POST session can't snarf in while we're  
  5. // processing the request.  
  6. fReadMutex.Lock();  
  7. 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(后处理)角色。这里所说的对客户进行响应是指模块向客户端发送任何形式的数据。

[cpp] view plaincopy
  1. // Invoke filter modules  
  2. numModules = QTSServerInterface::GetNumModulesInRole(QTSSModule::kRTSPFilterRole);  
  3. for (; (fCurrentModule < numModules) && <span style="color:#ff0000;">((!fRequest->HasResponseBeenSent()) || fModuleState.eventRequested)</span>; fCurrentModule++){}  

 

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

  • 一个RTSP会话(RTSPSession),和当前这个特定的请求相关联,在客户端关闭与服务器之间的RTSP连接时,这个会话也会被关闭。
  • 一个客户会话(RTPSession),和产生当前请求的客户端连接相关联,这个会话会一直保持,直到客户端的流播放结束。
[cpp] view plaincopy
  1. // Otherwise, this is a normal request, so parse it and get the RTPSession.  
  2.    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_AppendRTSPHeaderQTSS_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角色的间隔时间。这个周期会一直重复,直到所有的媒体数据包被发送完成,或者由于客户请求的原因需要暂停或中止客户会话为止。

[cpp] view plaincopy
  1. QTSS_Error  QTSSFileModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParamBlock)  
  2. {  
  3.     switch (inRole)  
  4.     {  
  5.         case QTSS_Register_Role:  
  6.             return Register(&inParamBlock->regParams);  
  7.         case QTSS_Initialize_Role:  
  8.             return Initialize(&inParamBlock->initParams);  
  9.         case QTSS_RereadPrefs_Role:  
  10.             return RereadPrefs();  
  11.         case QTSS_RTSPRequest_Role:  
  12.             return ProcessRTSPRequest(&inParamBlock->rtspRequestParams);  
  13. <span style="color:#ff0000;">        case QTSS_RTPSendPackets_Role:  
  14.             return SendPackets(&inParamBlock->rtpSendPacketsParams);  
  15. </span>        case QTSS_ClientSessionClosing_Role:  
  16.             return DestroySession(&inParamBlock->clientSessionClosingParams);  
  17.     }  
  18.     return QTSS_NoErr;  
  19. }  
[cpp] view plaincopy
  1. theErr = QTSS_Write(theStream, &(*theFile)->fPacketStruct, (*theFile)->fNextPacketLen, NULL, theFlags);  
  2. //每一次发送返回在发送后是否继续的消息  
  3.  isBeginningOfWriteBurst = false;  
  4.    
  5.  if ( theErr == QTSS_WouldBlock )//阻塞,等待下一次服务器唤醒  
  6.  {   
  7.   
  8.      if (currentTimeStamp != pauseTimeStamp) // reset the packet time stamp so we adjust it again when we really do send it  
  9.         SetPacketTimeStamp(currentTimeStamp, packetDataPtr);  
  10.       
  11.      // 如果在返回参数中设置了下一次唤醒的时间间隔则  
  12.      // In the case of a QTSS_WouldBlock error, the packetTransmitTime field of the packet struct will be set to  
  13.      // the time to wakeup, or -1 if not known.  
  14.      // If the time to wakeup is not given by the server, just give a fixed guess interval  
  15.      if ((*theFile)->fPacketStruct.suggestedWakeupTime == -1)  
  16.          <span style="color:#ff0000;">inParams->outNextPacketTime = sFlowControlProbeInterval;    // for buffering, try me again in # MSec不确定下次唤醒时间  
  17.             else  
  18.      {  
  19.          Assert((*theFile)->fPacketStruct.suggestedWakeupTime > inParams->inCurrentTime);  
  20.          <span style="color:#ff0000;">inParams->outNextPacketTime = (*theFile)->fPacketStruct.suggestedWakeupTime - inParams->inCurrentTime;//确定下次唤醒时间  
  21.             }  
  22.        
  23.      //qtss_printf("Call again: %qd\n", inParams->outNextPacketTime);  
  24.            
  25.      return QTSS_NoErr;  
  26.  }  
  27.  else//继续发送  
  28.  {  
  29.   
  30.    (void) QTSS_SetValue(theStream, sRTPStreamLastSentPacketSeqNumAttrID, 0, &curSeqNum, sizeof(curSeqNum));  
  31.    (*theFile)->fPacketStruct.packetData = NULL;  
  32.  }  


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

(责任编辑:IT)
------分隔线----------------------------
栏目列表
推荐内容