live555作爲RTSP流媒體服務器時Server端多track而客戶端僅請求一個track,當客戶端關閉時沒有關閉流的問題解決方案

當我們使用live555作爲流媒體服務器時,某個通道對應的所有客戶端斷開後, 不能正常回調關閉。
某一通道同時支持視頻和音頻輸出, 即video和audio兩個track
VLC和EasyPlayer播放庫來中的RTSPClient則都會請求(所以不存在問題);
而某些客戶端則只請求了一個track, 比如video;

此時再關閉,會有兩種情況:
在VLC或自有播放庫來斷開連接時,都會正常回調關閉;
在僅請求了video的客戶端斷開連接時, 一定不會回調關閉;

分析問題:

在RTSPClient斷開時,一定會調用RTSPServer::stopTCPStreamingOnSocket, 當流爲RTP Over TCP時, 則會調用deleteStreamByTrack;
在deleteStreamByTrack函數中, 是一個for循環,檢測fStreamStates的subsession, 因爲sdp中存在video和audio, fNumStreamStates的值爲2, 而客戶端只請求了video,所以在以下的代碼中,判斷不成立:

  Boolean noSubsessionsRemain = True;
  for (unsigned i = 0; i < fNumStreamStates; ++i) {
    if (fStreamStates[i].subsession != NULL) {
      noSubsessionsRemain = False;		//因爲存在video和audio, 所以此處條件會成立
      break;
    }
  }

因爲noSubsessionsRemain爲False, 所以下面的代碼不會被執行;

if (noSubsessionsRemain) delete this;

順便說一下, delete this是刪除RTSPClientSession, 在ClientSession的析構函數中,會減少引用計數,如果計數爲0, 則刪除fOurServerMediaSession;

解決問題:

在RTSPClientConnection中增加一個計數變量, 用於記錄客戶端請求的track個數, 然後在deleteStreamByTrack中遞減,如果爲0, 則使上面的noSubsessionsRemain爲True;
修改如下:

	//在RTSPClientConnection的聲明中,增加以下兩個變量
	int	clientRequestTrackNum;		//for rtp over tcp
	char fClientSessionIdStr[16];	//for rtp over udp
	
	void RTSPServer::RTSPClientSession
	::handleCmd_SETUP(UsageEnvironment *pEnv, RTSPServer::RTSPClientConnection* ourClientConnection,
			  char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr) {
			  
			  ......
			  
			  
	    if (streamingMode == RTP_TCP) {
	      // Note that we'll be streaming over the RTSP TCP connection:
	      fStreamStates[trackNum].tcpSocketNum = ourClientConnection->fClientOutputSocket;
	      fOurRTSPServer.noteTCPStreamingOnSocket(fStreamStates[trackNum].tcpSocketNum, this, trackNum);

				//此處增加計數
		  	ourClientConnection->clientRequestTrackNum ++;
	    }		
	    
	    ......
	}		

修改deleteStreamByTrack的聲明和實現:

	void deleteStreamByTrack(UsageEnvironment *pEnv, unsigned trackNum, Boolean lockFlag, int *clientTrackNum);

	void RTSPServer::RTSPClientSession::deleteStreamByTrack(UsageEnvironment *pEnv, unsigned trackNum, Boolean lockFlag, int *clientTrackNum) {

		lockClientFlag = lockFlag;

	  if (trackNum >= fNumStreamStates) return; // sanity check; shouldn't happen
	  if (fStreamStates[trackNum].subsession != NULL) {
	    fStreamStates[trackNum].subsession->deleteStream(fOurSessionId, fStreamStates[trackNum].streamToken);
	    fStreamStates[trackNum].subsession = NULL;
	  }
	  
	  // Optimization: If all subsessions have now been deleted, then we can delete ourself now:
	  Boolean noSubsessionsRemain = True;
	  for (unsigned i = 0; i < fNumStreamStates; ++i) {
	    if (fStreamStates[i].subsession != NULL) {
	      noSubsessionsRemain = False;
	      break;
	    }
	  }

	  if (NULL!=clientTrackNum)
	  {
		  if (*clientTrackNum > 0)		*clientTrackNum -= 1;
		  if (*clientTrackNum == 0)		noSubsessionsRemain = True;
	  }

	  if (noSubsessionsRemain) delete this;
	}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章