當我們使用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;
}