[Live-devel] Server runaway streams for shared session for RTP-over-TCP client
John Tam
tamj01 at hotmail.com
Fri Aug 6 13:47:09 PDT 2010
Hello,
I am using the RTSP server functionality of the LiveMedia library, and I might have resolved a client session management issue. The senario is when a ServerMediaSubsession is shared [reuseFirstSource=True] and clients choose RTP-over-TCP for streaming mode, only the last requested RTP-over-TCP RTSPClientSession can be torn down. All of the objects under the ServerMediaSession will be runaways. The MediaSink keeps playing, and the RTPInstance writes with socket error. The stderr shows socket write attempts after all of the RTSP clients are closed.
I am trying to limit the scope of the impacted code, and there seems to be two area in the RTPInstance.cpp that are contributing to the problem. #1, The RTPInterface::setServerRequestAlternativeByteHandler() function is overwriting the handler and clientData of an already assigned SocketDescriptor object. It makes all existing socket descriptor of the server media subsession to map to the latest RTSPClientSession instance. #2, When RTCPInstance::addStreamSocket() function adds a new stream channel, the RTPInterface::stopNetworkReading() function calls deregisterSocket() and wipes out the existing SocketDescriptor to RTSPClientSession mapping. Therefore, only the last newly added SocketDescriptor has a valid fServerRequestAlternativeByteHandler to notify of RTSP TEARDOWN command.
I have not updated myself to the latest, but I am only working on an older version of the LiveMedia library from March 16, 2010. I think my proposed fix should not have any conflict with the latest code base. The changes are as the following:
1a) Test before overwriting an assigned handler.
void RTPInterface::setServerRequestAlternativeByteHandler(ServerRequestAlternativeByteHandler* handler, void* clientData) {
for (tcpStreamRecord* streams = fTCPStreams; streams != NULL;
streams = streams->fNext) {
// Get (or create, if necessary) a socket descriptor for "streams->fStreamSocketNum":
SocketDescriptor* socketDescriptor = lookupSocketDescriptor(envir(), streams->fStreamSocketNum);
if (! socketDescriptor->hasServerRequestAlternativeByteHandler()) {
// Assign a handler & clientData only when there has not been one to avoid mixing up the TCP socket with the intended RTSPClientSession object from RTSPClientSession::handleCmd_PLAY().
socketDescriptor->setServerRequestAlternativeByteHandler(handler, clientData);
}
}
}
1b) Add a new public method to determine if a handler has been already installed.
Boolean SocketDescriptor::hasServerRequestAlternativeByteHandler() const {
return NULL != fServerRequestAlternativeByteHandler;
}
2a) Preserve the mapping of handler of existing TCP client.
void RTPInterface::stopNetworkReading() {
// Normal case
envir().taskScheduler().turnOffBackgroundReadHandling(fGS->socketNum());
// Also turn off read handling on each of our TCP connections:
for (tcpStreamRecord* streams = fTCPStreams; streams != NULL;
streams = streams->fNext) {
// Do not deregister because the RTP interface will lose the handler and clientData inside the SocketDescriptor.
// Instead simply stop reading of the socket by turning off the read handler.
envir().taskScheduler().turnOffBackgroundReadHandling(streams->fStreamSocketNum);
}
}
2b) Register new TCP client or renew the socket handler of an existing TCP client.
void RTPInterface
::startNetworkReading(TaskScheduler::BackgroundHandlerProc* handlerProc) {
// Normal case: Arrange to read UDP packets:
envir().taskScheduler().
turnOnBackgroundReadHandling(fGS->socketNum(), handlerProc, fOwner);
// Also, receive RTP over TCP, on each of our TCP connections:
fReadHandlerProc = handlerProc;
for (tcpStreamRecord* streams = fTCPStreams; streams != NULL;
streams = streams->fNext) {
// Get a socket descriptor for "streams->fStreamSocketNum":
SocketDescriptor* socketDescriptor = lookupSocketDescriptor(envir(), streams->fStreamSocketNum);
// Restart a previously stopped stock read handler or start a new one
if (NULL != socketDescriptor->lookupRTPInterface(streams->fStreamChannelId)) {
socketDescriptor->reregisterRTPInterface();
} else {
// Tell it about our subChannel:
socketDescriptor->registerRTPInterface(streams->fStreamChannelId, this);
}
}
}
Cheers,
John
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.live555.com/pipermail/live-devel/attachments/20100806/24c4809c/attachment.html>
More information about the live-devel
mailing list