libRTSPServer 客户端断开后没有回调关闭的问题解决方法

##libRTSPServer 客户端断开后没有回调关闭的问题解决方法

###背景
去年上半年已成功将live555改造成支持多线程的流媒体服务器, 在实际项目中也早已使用, 一切正常;
后来有客户反馈一个问题: 使用一款人脸分析的服务器向流媒体取流, 在客户端断开连接后, 流媒体服务器没有相应的回调关闭流;

一番远程,在使用那款人脸分析服务器拉流时确实没有回调关闭, 使用其它的设备拉流正常;
在本地复现调试, 找到N年前写的一个rtspclient, 一运行, 问题必现;

###现象分析:
服务端的sdp中存在video和audio两个track;
vlc或其它rtspclient: 在收到sdp后,都会请求sdp中存在的track;
这个rtspclient, 仅请求了一个video, 没有请求audio;

###得出结论:
在VLC或其它客户端断开连接时,都会正常回调关闭;
在仅请求了video的客户端断开连接时, 一定不会回调关闭;

###解决方法
既然找到了问题根源,就来动手解决:

	RTSPServer.cpp:
	在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;
	}

因本代码中增加了多线程及其它功能,所以参数上会与live555官方代码有些许不同;

#####以上代码解决了rtp over tcp模式下, rtsp 客户端请求的track小于sdp中的track时不能回调关闭流的问题;
#####下一篇为解决rtp over udp模式下, rtsp客户端没有发送teardown而直接断开连接时, 需要等待默认65秒后才回调关闭流的问题;

####下载地址:
https://pan.baidu.com/s/129_dxIrui3YB69L3YAKqVw

交流QQ: 760983740

你可能感兴趣的:(live555)