live555 rtsp server簡要流程解析

通過分析live源碼中的 testOnDemandRTSPServer.cpp 可以得知rtsp server的主體流程如下:

RTSPServer::createNew(*env, 8554, authDB)

  1. setUpOurSocket(env, ourPort):根據傳遞進來的端口號,創建相應的socket,同時會綁定IP,進行相應的監聽,還會設置一些socket參數;
  2. RTSPServer(env, ourSocket, ourPort, authDatabase, reclamationSeconds)
    在該函數中,將會創建GenericMediaServer,最後會在 GenericMediaServer::incomingConnectionHandlerOnSocket() 函數中通過 accept() 函數等待client的連接,將會設置client的發送緩衝大小等參數。

ServerMediaSession::createNew(*env, streamName, streamName, descriptionString)

創建 ServerMediaSession 。

RTSPServer->addServerMediaSession(ServerMediaSession);

根據 streamName ,將 serverMediaSession 添加到哈希表 fServerMediaSessions,等待連接使用。

env->taskScheduler().doEventLoop()

等待 client 連接,然後將會進行相應的調度。

連接

在 GenericMediaServer::incomingConnectionHandlerOnSocket() 函數獲取到client的IP以及相應的socket之後,將會創建相應的連接,將調用到 RTSPServer::createNewClientConnection()。將會將client添加到 fClientConnections。同時將在 GenericMediaServer::ClientConnection::ClientConnection 設置,當有數據可讀時,將會執行 incomingRequestHandler(),在該函數中,讀取到數據之後,將會執行 handleRequestBytes() 函數處理數據。

發送數據

以H264編碼格式爲例,當接收到client的PLAY命令時,最終將會調用到 H264or5VideoRTPSink::continuePlaying() 函數。

H264or5VideoRTPSink::continuePlaying()
	|
	|
   \ /
MultiFramedRTPSink::continuePlaying()
	|
	|
   \ /
buildAndSendPacket(True)

在 buildAndSendPacket() 函數中,完成RTSP包的封裝以及數據的讀取等,完成之後,將會通過 afterGettingFunc 調用 MultiFramedRTPSink::afterGettingFrame(),最後在 MultiFramedRTPSink::sendPacketIfNecessary() 函數中,通過envir().taskScheduler().scheduleDelayedTask(uSecondsToGo, (TaskFunc*)sendNext, this) 完成下一次發送的延時調用。

什麼時候真正的發送數據?

真正通過TCP發送數據,是在 MultiFramedRTPSink::sendPacketIfNecessary() 通過調用 fRTPInterface.sendPacket() 函數發送完成的。

上面說的延時發送,又是怎麼確定延時多久的呢?

延時是在調用 afterGettingFunc() 時傳遞下來的 durationInMicroseconds 變量決定的(該變量賦值爲 FramedSource->fDurationInMicroseconds),該變量實際上就是當前幀播放的持續時間。

MultiFramedRTPSink::afterGettingFrame() 都做了什麼?

  1. 檢查是否數據過長,將會進行溢出處理放到下一次發送;
  2. 進行相應的數據發送,同時等待下一次的數據讀取;

相應的source doGetNextFrame() 函數都做什麼?

  1. 從代碼來看,應該是一次讀取一幀,同時,通過 fPresentationTime 確認當前讀取幀的時間戳;
  2. fDurationInMicroseconds 爲該幀播放的持續時間,live將會根據該值延時調度下一次的數據讀取;
  3. fFrameSize 變量爲該幀數據的大小;

通過 doGetNextFrame() 讀取到的數據,什麼時候使用呢,時間戳又是什麼時候設置呢?

  1. 在通過 StreamParser::ensureValidBytes1() 函數調用到輸入源的 getNextFrame() 函數,在 getNextFrame() 函數中將會賦值時間戳,而後通過 fAfterGettingFunc() 調用 StreamParser::afterGettingBytes() 函數,將時間戳保存在 fLastSeenPresentationTime 變量。
  2. 上面最終將會調用到 MPEGVideoStreamFramer::continueReadProcessing()。在這裏,將會通過parse函數調用到 H264or5VideoStreamParser::parse() 。將會在這個函數設置時間戳以及下一幀的時間戳。
unsigned H264or5VideoStreamParser::parse() {
    ...
    /* 這裏將會設置 fPresentationTime = fNextPresentationTime ,
	 * 之後在調用 fAfterGettingFunc() 函數時就將是該時間戳而不是上
	 * 述輸入源的時間戳了
	 */
    usingSource()->setPresentationTime();

	...
	/* fNextPresentationTime 是在當前幀解析完畢之後,通過幀率計算得
	 * 到的,從而影響下一幀時間戳
	 */
    if (thisNALUnitEndsAccessUnit) {
#ifdef DEBUG
      fprintf(stderr, "*****This NAL unit ends the current access unit*****\n");
#endif
      usingSource()->fPictureEndMarker = True;
      ++usingSource()->fPictureCount;

      // Note that the presentation time for the next NAL unit will be different:
      /* 這裏通過引用的方式修改了 fNextPresentationTime */
      struct timeval& nextPT = usingSource()->fNextPresentationTime; // alias
      nextPT = usingSource()->fPresentationTime;
      double nextFraction = nextPT.tv_usec/1000000.0 + 1/usingSource()->fFrameRate;
      unsigned nextSecsIncrement = (long)nextFraction;
      nextPT.tv_sec += (long)nextSecsIncrement;
      nextPT.tv_usec = (long)((nextFraction - nextSecsIncrement)*1000000);
    }
	...
}
  1. 完成上述之後,又將通過 afterGetting() 函數調用到 H264or5Fragmenter::afterGettingFrame() 函數。最後又將通過 FramedSource::afterGetting(this) 調用到 MultiFramedRTPSink::afterGettingFrame()。在 MultiFramedRTPSink::afterGettingFrame() 函數中,最終將會通過 doSpecialFrameHandling() 函數將時間戳設置到 RTP package 中。
  2. 在 doSpecialFrameHandling() 中設置時間戳時,最終將調用到 MultiFramedRTPSink::setTimestamp()。在該函數中,通過 RTPSink::convertToRTPTimestamp() 將時間戳轉換爲 RTP 使用的時間戳,其中,在轉換過程中使用到的 fTimestampFrequency 變量在 H264or5VideoRTPSink::H264or5VideoRTPSink() 構造函數中,通過 VideoRTPSink 類的構造函數最終賦值爲 90000。在 RTPSink::convertToRTPTimestamp() 轉爲爲時間戳之後,在將數據插入到 fTimestampPosition 地址中,該變量是在 MultiFramedRTPSink::buildAndSendPacket() 中設置的,從package中預留了設置時間戳的空間。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章