Hi. Noted this when using RTSP relay.
Use case (probably can be simplified): - RTSP relay based on RTSPServer with RTP over TCP. - Open connection (#1) and let it run. At this point relay connects to the source stream and starts forwarding packets. - Open another connection (#2) to the same stream and close it once start receiving data. - Open third connection (#3) to the same stream. It will likely be through the same socket descriptor as connection (#2). This is important for the scenario. - 60 seconds after connection #2 was closed, server will stop sending data to connection #3. It won’t close the socket, it will just stop sending data. Explanation. - When RTSPServer::RTSPClientSession is created livenessTimeoutTask() is scheduled and it gets rescheduled while connection is on. - 60 seconds after connection is closed livenessTimeoutTask() is executed. It will delete clientSession, which will call reclaimStreamStates(), which deletes stream. Eventually RTPInterface::removeStreamSocket() gets called with file descriptor from connection #2 - At that time this file descriptor belongs to connection #3 and tcpStreamRecord for connection #3 is removed from the list. I assume that it is not expected that RTSPClientSession outlives RTSPClientConnection and suggest destroying them at the same time. Something like this: --- a/liveMedia/RTSPServer.cpp +++ b/liveMedia/RTSPServer.cpp @@ -433,7 +433,8 @@ RTSPServer::RTSPClientConnection ::RTSPClientConnection(RTSPServer& ourServer, int clientSocket, struct sockaddr_in clientAddr) : fOurServer(ourServer), fIsActive(True), fClientInputSocket(clientSocket), fClientOutputSocket(clientSocket), fClientAddr(clientAddr), - fRecursionCount(0), fOurSessionCookie(NULL) { + fRecursionCount(0), fOurSessionCookie(NULL), + fConnectionSessions(HashTable::create(STRING_HASH_KEYS)) { // Add ourself to our 'client connections' table: fOurServer.fClientConnections->Add((char const*)this, this); @@ -452,8 +453,19 @@ RTSPServer::RTSPClientConnection::~RTSPClientConnection() { fOurServer.fClientConnectionsForHTTPTunneling->Remove(fOurSessionCookie); delete[] fOurSessionCookie; } + + HashTable::Iterator* iter = HashTable::Iterator::create(*fConnectionSessions); + RTSPServer::RTSPClientSession* clientSession; + char const* key; + while ((clientSession = (RTSPServer::RTSPClientSession*)(iter->next(key))) != NULL) { + clientSession = (RTSPServer::RTSPClientSession*)(fOurServer.fClientSessions->Lookup(key)); + if (clientSession == NULL) continue; + delete clientSession; + } + delete iter; closeSockets(); + delete fConnectionSessions; } // Special mechanism for handling our custom "REGISTER" command: @@ -1029,6 +1041,7 @@ void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead) { } while (sessionId == 0 || fOurServer.fClientSessions->Lookup(sessionIdStr) != NULL); clientSession = fOurServer.createNewClientSession(sessionId); fOurServer.fClientSessions->Add(sessionIdStr, clientSession); + fConnectionSessions->Add(sessionIdStr, clientSession); } else { areAuthenticated = False; } diff --git a/liveMedia/include/RTSPServer.hh b/liveMedia/include/RTSPServer.hh index c998e29..4d77b89 100644 --- a/liveMedia/include/RTSPServer.hh +++ b/liveMedia/include/RTSPServer.hh @@ -264,6 +264,7 @@ public: // should be protected, but some old compilers complain otherwise Authenticator fCurrentAuthenticator; // used if access control is needed char* fOurSessionCookie; // used for optional RTSP-over-HTTP tunneling unsigned fBase64RemainderCount; // used for optional RTSP-over-HTTP tunneling (possible values: 0,1,2,3) + HashTable* fConnectionSessions; // maps 'session id' strings to "RTSPClientSession" objects }; // The state of an individual client session (using one or more sequential TCP connections) handled by a RTSP server: _______________________________________________ live-devel mailing list live-devel@lists.live555.com http://lists.live555.com/mailman/listinfo/live-devel