##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