EasyRTSPServer视频流媒体服务平台r解决Server端多track而客户端仅请求一个track,客户端关闭没有及时关闭流是什么原因?

RTSP协议以客户服务器方式工作,它是一个多媒体播放控制协议,用来使用户在播放从因特网下载的实时数据时能够进行控制,如:暂停/继 续、后退、前进等。因此 RTSP 又称为“因特网录像机遥控协议”。EasyRTSPServer是一套稳定、高效、可靠、多平台支持的RTSP-Server组件。

EasyRTSPServer解决Server端多track而客户端仅请求一个track,客户端关闭没有及时关闭流

提出问题

某通道对应的所有客户端断开后, 不能正常回调关闭。
某一通道同时支持视频和音频输出, 即video和audio两个track。
VLC和自有播放库来中的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;
		}
posted on 2019-10-15 16:31  TSINGSEE  阅读(299)  评论(0编辑  收藏  举报