<html>
<head>
<style><!--
.hmmessage P
{
margin:0px;
padding:0px
}
body.hmmessage
{
font-size: 10pt;
font-family:Tahoma
}
--></style>
</head>
<body class='hmmessage'>
Hello,<BR>
&nbsp;<BR>
I am using the RTSP server functionality of the LiveMedia library, and I might have resolved a client session management issue.&nbsp; 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.&nbsp; All of the objects under the ServerMediaSession will be runaways.&nbsp; The MediaSink keeps playing, and the RTPInstance writes with socket error.&nbsp; The stderr shows socket write attempts after all of the RTSP clients are closed.<BR>
&nbsp;<BR>
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.&nbsp; #1, The RTPInterface::setServerRequestAlternativeByteHandler() function is overwriting the handler and clientData of an already assigned SocketDescriptor object.&nbsp; It makes all existing socket descriptor of the server media subsession to map to the latest RTSPClientSession instance.&nbsp; #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.&nbsp; Therefore, only the last newly added SocketDescriptor has a valid fServerRequestAlternativeByteHandler to notify of RTSP TEARDOWN command.<BR>
&nbsp;<BR>
I have not updated myself to the latest, but I&nbsp;am only working on an older version of the LiveMedia library from March 16, 2010.&nbsp; I think my proposed fix should not have any conflict with the latest code base.&nbsp; The changes are as the following:<BR>
&nbsp;<BR>
1a)&nbsp; Test before overwriting an assigned handler.<BR>
void RTPInterface::setServerRequestAlternativeByteHandler(ServerRequestAlternativeByteHandler* handler, void* clientData) {<BR>&nbsp; for (tcpStreamRecord* streams = fTCPStreams; streams != NULL;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; streams = streams-&gt;fNext) {<BR>&nbsp;&nbsp;&nbsp; // Get (or create, if necessary) a socket descriptor for "streams-&gt;fStreamSocketNum":<BR>&nbsp;&nbsp;&nbsp; SocketDescriptor* socketDescriptor = lookupSocketDescriptor(envir(), streams-&gt;fStreamSocketNum);<BR>
&nbsp;&nbsp;&nbsp; if (! socketDescriptor-&gt;hasServerRequestAlternativeByteHandler()) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Assign a handler &amp; clientData only when there has not been one to avoid mixing up the TCP socket with the intended RTSPClientSession object from RTSPClientSession::handleCmd_PLAY().<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; socketDescriptor-&gt;setServerRequestAlternativeByteHandler(handler, clientData);<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp; }<BR>}<BR><BR>
1b)&nbsp; &nbsp; Add a new public method to determine if a handler has been already installed.<BR>
&nbsp; Boolean SocketDescriptor::hasServerRequestAlternativeByteHandler() const {<BR>&nbsp;&nbsp;&nbsp; return NULL != fServerRequestAlternativeByteHandler;<BR>&nbsp; }<BR><BR>
2a)&nbsp; Preserve the mapping of handler of existing TCP client.<BR>
void RTPInterface::stopNetworkReading() {<BR>&nbsp; // Normal case<BR>&nbsp; envir().taskScheduler().turnOffBackgroundReadHandling(fGS-&gt;socketNum());<BR>
&nbsp; // Also turn off read handling on each of our TCP connections:<BR>&nbsp; for (tcpStreamRecord* streams = fTCPStreams; streams != NULL;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; streams = streams-&gt;fNext) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Do not deregister because the RTP interface will lose the handler and clientData inside the SocketDescriptor.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Instead simply stop reading of the socket by turning off the read handler.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; envir().taskScheduler().turnOffBackgroundReadHandling(streams-&gt;fStreamSocketNum);<BR>&nbsp; }<BR>}<BR><BR>
2b)&nbsp; Register new TCP client or renew the socket handler of an existing TCP client.<BR>
void RTPInterface<BR>::startNetworkReading(TaskScheduler::BackgroundHandlerProc* handlerProc) {<BR>&nbsp; // Normal case: Arrange to read UDP packets:<BR>&nbsp; envir().taskScheduler().<BR>&nbsp;&nbsp;&nbsp; turnOnBackgroundReadHandling(fGS-&gt;socketNum(), handlerProc, fOwner);<BR>
&nbsp; // Also, receive RTP over TCP, on each of our TCP connections:<BR>&nbsp; fReadHandlerProc = handlerProc;<BR>&nbsp; for (tcpStreamRecord* streams = fTCPStreams; streams != NULL;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; streams = streams-&gt;fNext) {<BR>&nbsp;&nbsp;&nbsp; // Get a socket descriptor for "streams-&gt;fStreamSocketNum":<BR>&nbsp;&nbsp;&nbsp; SocketDescriptor* socketDescriptor = lookupSocketDescriptor(envir(), streams-&gt;fStreamSocketNum);<BR>
&nbsp;&nbsp;&nbsp; // Restart a previously stopped stock read handler or start a new one<BR>&nbsp;&nbsp;&nbsp; if (NULL != socketDescriptor-&gt;lookupRTPInterface(streams-&gt;fStreamChannelId)) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; socketDescriptor-&gt;reregisterRTPInterface();<BR>&nbsp;&nbsp;&nbsp; } else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Tell it about our subChannel:<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; socketDescriptor-&gt;registerRTPInterface(streams-&gt;fStreamChannelId, this);<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp; }<BR>}<BR><BR>
Cheers,<BR>
John<BR>
&nbsp;<BR>                                               </body>
</html>