处理连接请求的基本流程:
l Step 1 : 与客户端建立 RTSP 连接(调用 incomingConnectionHandler 方法 ),创建 ClientSession 并关联 fClientSocket 与 incomingRequestHandler (调用 incomingConnectionHandler1 ) 。
l Step 2 : 接收客户端请求(调用 incomingRequestHandler 方法 )。
l Step 3 : 从客户端 Socket 读取数据,并对请求数据(即 the request string )进行转换(调用 parseRTSPRequestString 方法,该方法在 RTSPCommon 类中)。
l Step 4 : 根据分离出来的指令进行分别处理:
n OPTIONS → handleCmd_OPTIONS
n DESCRIBE → handleCmd_DESCRIBE
handleCmd_DESCRIBE 这一个方法比较重要,首先根据 urlSuffix 查找 ServerMediaSession 是否存在(调用 lookupServerMediaSession 方法,该方法中通过 HashTable 来查找)。
在 testOnDemandRTSPServer 项目工程中,仅仅是通过 streamName 来确认 session 是否为 NULL 。而在完整的 live555MediaServer 项目工程中,则是通过 DynamicRTSPServer 类来处理,其首先是查找文件是否存在,若文件不存在,则判断 ServerMediaSession (即 smsExists )是否存在,如果存在则将其 remove (调用 removeServerMediaSession 方法 );若文件存在,则根据文件名创建一个 ServerMediaSession (调用 createNewSMS 方法,若在该方法中找不到对应的文件扩展名,则将返回 NULL )。
如果通过 lookupServerMediaSession 返回的是 NULL ,则向客户端发送响应消息并将 fSessionIsActive 置为 FALSE ;否则,为该 session 组装一个 SDP 描述信息(调用 generateSDPDescription 方法,该方法在 ServerMediaSession 类中),组装完成后,生成一个 RTSP URL (调用 rtspURL 方法,该 方法在 RTSPServer 类中)。
n SETUP → handleCmd_SETUP
handleCmd_SETUP 方法中,有两个关键的名词,一个是 urlPreSuffix ,代表了 session name (即 stream name );一个是 urlSuffix ,代表了 subsession name (即 track name ),后面经常用到的 streamName 和 trackId 分别与这两个名词有关。
接下来会创建 session's state ,包括 incrementReferenceCount 等。紧接着,会针对确定的 subsession ( track )查找相应的信息。接着,在 request string 查找一个 "Transport:" header ,目的是为了从中提取客户端请求的一些参数(调用 parseTransportHeader 方法,该方法在 RTSPServer 类中),如 clientsDestinationAddressStr 、 ClientRTPPortNum 等。
再接着是 getStreamParameters (该方法在 ServerMediaSession 类中被定义为纯虚函数并在 OnDemandServerMediaSubsession 类中被重定义),然后通过 fIsMulticast 和 streamingMode 来组装不同的响应消息。
n PLAY → handleCmd_PLAY : 处理播放请求,具体的实现流程请参见后面的步骤。
n PAUSE → handleCmd_PAUSE : 处理暂停请求,在执行了该请求后,最终会调用 StopPlaying 方法,并将 fAreCurrentlyPlaying 置为 FALSE 。
n TEARDOWN → handleCmd_TEARDOWN : 处理停止请求,将 fSessionIsActive 置为 FALSE 。
n GET_PARAMETER → handleCmd_GET_PARAMETER : 该方法主要是维持客户端与服务器通信的生存状态, just for keep alive 。
n SET_PARAMETER → handleCmd_SET_PARAMETER : 该方法未针对 SET_PARAMETER 作实现,使用该方法会调用 handleCmd_notSupported 方法,并将最终引发与客户端断开连接。
l Step 5 : 根据 Step 4 的不同指令进行消息响应(调用 send 方法),该消息响应是即时的。
l Step 6 : 处理客户端发送“ SETUP ”指令后即开始播放的特殊情况。
l Step 7 : 将 RequestBuffer 进行重置,以便于为之后到来的请求做好准备。
l Step 8 : 检查 fSessionIsActive 是否为 FALSE ,如果是则删除当前的 ClientSession 。
处理 PLAY 的基本流程:
l Step 1 : 对 rtspURL 及相关 header 的处理,涉及较多的细节。
l Step 2 : 根据不同的 header 对流进行缩放比例或定位的处理。
如果为 sawScaleHeader ,则进行缩放比例的处理(调用 setStreamScale 方法,该方法在 OnDemandServerMediaSubsession 类中实现)。
如果为 sawRangeHeader ,则进行寻找流的处理(即是对流进行定位,调用 seekStream 方法,该方法在 OnDemandServerMediaSubsession 类中实现;同时,该方法的调用是在初始播放前及播放过程中由于用户拖动播放进度条而产生的系列请求)。
在 OnDemandServerMediaSubsession 类中, seekStream 方法中调用了 seekStreamSource 方法,该方法在对应的媒体格式文件的 FileServerMediaSubsession 类中实现(如针对 WAV 格式,则在 WAVAudioFileServerMediaSubsession 类中实现;针对 MP3 格式,则在 MP3AudioFileServerMediaSubsession 类中实现)。
同理, OnDemandServerMediaSubsession 类中的 setStreamScale 方法中所调用的 setStreamSourceScale 方法亦是类似的实现机制。
l Step 3 : 开始进行流式播放(调用 startStream 方法,该方法在 OnDemandServerMediaSubsession 类中实现)。
n Step 3.1 : 根据 clientSessionId 从 fDestinationsHashTable 中查找到 destinations (包括了客户端的 IP 地址、 RTP 端口号、 RTCP 端口号等信息)。
n Step 3.2 : 调用 startPlaying 方法,在该方法中根据 RTPSink 或 UDPSink 分别调用 startPlaying 方法。
如果是调用 RTPSink 的 startPlaying 方法,则接着会调用 MediaSink 类中的 startPlaying 方法,并在该方法中调用 MultiFramedRTPSink 类中的 continuePlaying 方法,之后便是 buildAndSendPacket 了。这里已经来到重点了,即是关于不断读取 Frame 并 Send 的要点。在 MultiFramedRTPSink 类中,通过 buildAndSendPacket 、 packFrame 、 afterGettingFrame 、 afterGettingFrame1 、 sendPacketIfNecessary 和 sendNext 构成了一个循环圈,数据包的读取和发送在这里循环进行着。特别注意的是 sendPacketIfNecessary 方法中的后面代码( nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecondsToGo, (TaskFunc*)sendNext, this); ),通过 Delay amount of time 后,继续下一个 Task ,并回过来继续调用 buildAndSendPacket 方法。
在 packFrame 方法中,正常情况下,需要调用 getNextFrame 方法(该方法在 FramedSource 类中,并且对不同媒体格式的 Frame 的获取出现在 FramedSource 类的 getNextFrame 方法中,通过调用 doGetNextFrame 方法来实现)来获取新的 Frame 。
如果是调用 UDPSink 的 startPlaying 方法,则接着会调用 MediaSink 类中的 startPlaying 方法,并在该方法中调用 BasicUDPSink 类中的 continuePlaying 方法。在这之后由若干个方法构成了一个循环圈: continuePlaying1 、 afterGettingFrame 、 afterGettingFrame1 、 sendNext 。并在 afterGettingFrame1 方法中实现了 packet 的发送( fGS ->output (envir (), fGS ->ttl (), fOutputBuffer , frameSize ); )。
Step 3.3 : 针对 RTPSink 创建 RTCP instance ( RTP 与 RTCP 的配套使用决定了其必须这么做,否则可能就跟直接使用 UDP 发送数据包没什么两样了 ^_^ ),创建 RTCP instance 时,将 incomingReportHandler 句柄作为 BackgroundHandlerProc ,以便于处理 RTCP 的报告,并开始 startNetworkReading 。这里 RTP/RTCP 的使用方式有两种,一种建立在 TCP 之上,一种建立在 UDP 之上。