RTSPServer::RTSPClientSession Class Reference

#include <RTSPServer.hh>

Collaboration diagram for RTSPServer::RTSPClientSession:

Collaboration graph
[legend]

Public Member Functions

 RTSPClientSession (RTSPServer &ourServer, u_int32_t sessionId)
virtual ~RTSPClientSession ()

Protected Member Functions

virtual void handleCmd_SETUP (RTSPClientConnection *ourClientConnection, char const *urlPreSuffix, char const *urlSuffix, char const *fullRequestStr)
virtual void handleCmd_withinSession (RTSPClientConnection *ourClientConnection, char const *cmdName, char const *urlPreSuffix, char const *urlSuffix, char const *fullRequestStr)
virtual void handleCmd_TEARDOWN (RTSPClientConnection *ourClientConnection, ServerMediaSubsession *subsession)
virtual void handleCmd_PLAY (RTSPClientConnection *ourClientConnection, ServerMediaSubsession *subsession, char const *fullRequestStr)
virtual void handleCmd_PAUSE (RTSPClientConnection *ourClientConnection, ServerMediaSubsession *subsession)
virtual void handleCmd_GET_PARAMETER (RTSPClientConnection *ourClientConnection, ServerMediaSubsession *subsession, char const *fullRequestStr)
virtual void handleCmd_SET_PARAMETER (RTSPClientConnection *ourClientConnection, ServerMediaSubsession *subsession, char const *fullRequestStr)
UsageEnvironmentenvir ()
void reclaimStreamStates ()
Boolean isMulticast () const
void noteLiveness ()
void setRTSPResponse (RTSPClientConnection *ourClientConnection, char const *responseStr)
void setRTSPResponse (RTSPClientConnection *ourClientConnection, char const *responseStr, u_int32_t sessionId)
void setRTSPResponse (RTSPClientConnection *ourClientConnection, char const *responseStr, char const *contentStr)
void setRTSPResponse (RTSPClientConnection *ourClientConnection, char const *responseStr, u_int32_t sessionId, char const *contentStr)
Boolean usesTCPTransport () const

Static Protected Member Functions

static void noteClientLiveness (RTSPClientSession *clientSession)
static void livenessTimeoutTask (RTSPClientSession *clientSession)

Protected Attributes

RTSPServerfOurServer
u_int32_t fOurSessionId
ServerMediaSessionfOurServerMediaSession
Boolean fIsMulticast
Boolean fStreamAfterSETUP
unsigned char fTCPStreamIdCount
TaskToken fLivenessCheckTask
unsigned fNumStreamStates
RTSPServer::RTSPClientSession::streamStatefStreamStates

Friends

class RTSPServer
class RTSPClientConnection

Data Structures

struct  streamState

Detailed Description

Definition at line 263 of file RTSPServer.hh.


Constructor & Destructor Documentation

RTSPServer::RTSPClientSession::RTSPClientSession ( RTSPServer ourServer,
u_int32_t  sessionId 
)

Definition at line 1357 of file RTSPServer.cpp.

References noteLiveness().

RTSPServer::RTSPClientSession::~RTSPClientSession (  )  [virtual]

Definition at line 1363 of file RTSPServer.cpp.

References ServerMediaSession::decrementReferenceCount(), ServerMediaSession::deleteWhenUnreferenced(), envir(), RTSPServer::fClientSessions, fLivenessCheckTask, fOurServer, fOurServerMediaSession, fOurSessionId, NULL, reclaimStreamStates(), ServerMediaSession::referenceCount(), HashTable::Remove(), RTSPServer::removeServerMediaSession(), UsageEnvironment::taskScheduler(), and TaskScheduler::unscheduleDelayedTask().

01363                                                 {
01364   // Turn off any liveness checking:
01365   envir().taskScheduler().unscheduleDelayedTask(fLivenessCheckTask);
01366   
01367   // Remove ourself from the server's 'client sessions' hash table before we go:
01368   char sessionIdStr[9];
01369   sprintf(sessionIdStr, "%08X", fOurSessionId);
01370   fOurServer.fClientSessions->Remove(sessionIdStr);
01371   
01372   reclaimStreamStates();
01373   
01374   if (fOurServerMediaSession != NULL) {
01375     fOurServerMediaSession->decrementReferenceCount();
01376     if (fOurServerMediaSession->referenceCount() == 0
01377         && fOurServerMediaSession->deleteWhenUnreferenced()) {
01378       fOurServer.removeServerMediaSession(fOurServerMediaSession);
01379       fOurServerMediaSession = NULL;
01380     }
01381   }
01382 }


Member Function Documentation

void RTSPServer::RTSPClientSession::handleCmd_SETUP ( RTSPClientConnection ourClientConnection,
char const *  urlPreSuffix,
char const *  urlSuffix,
char const *  fullRequestStr 
) [protected, virtual]

Definition at line 1476 of file RTSPServer.cpp.

References dateHeader(), destinationAddress, False, RTSPServer::RTSPClientConnection::fClientAddr, RTSPServer::RTSPClientConnection::fClientInputSocket, RTSPServer::RTSPClientConnection::fClientOutputSocket, RTSPServer::RTSPClientConnection::fCurrentCSeq, RTSPServer::RTSPClientConnection::fResponseBuffer, RTSPServer::RTSPClientConnection::handleCmd_bad(), RTSPServer::RTSPClientConnection::handleCmd_notFound(), RTSPServer::RTSPClientConnection::handleCmd_unsupportedTransport(), handleCmd_withinSession(), ServerMediaSession::incrementReferenceCount(), iter, MediaSubsessionIterator::next(), NULL, Port::num(), our_inet_addr(), parsePlayNowHeader(), parseRangeHeader(), parseTransportHeader(), RAW_UDP, ReceivingInterfaceAddr, MediaSubsessionIterator::reset(), RTP_TCP, RTP_UDP, SendingInterfaceAddr, sms, SOCKLEN_T, subsession, True, and AddressString::val().

Referenced by RTSPServer::RTSPClientConnection::handleRequestBytes().

01477                                                                                                {
01478   // Normally, "urlPreSuffix" should be the session (stream) name, and "urlSuffix" should be the subsession (track) name.
01479   // However (being "liberal in what we accept"), we also handle 'aggregate' SETUP requests (i.e., without a track name),
01480   // in the special case where we have only a single track.  I.e., in this case, we also handle:
01481   //    "urlPreSuffix" is empty and "urlSuffix" is the session (stream) name, or
01482   //    "urlPreSuffix" concatenated with "urlSuffix" (with "/" inbetween) is the session (stream) name.
01483   char const* streamName = urlPreSuffix; // in the normal case
01484   char const* trackId = urlSuffix; // in the normal case
01485   char* concatenatedStreamName = NULL; // in the normal case
01486   
01487   do {
01488     // First, make sure the specified stream name exists:
01489     ServerMediaSession* sms = fOurServer.lookupServerMediaSession(streamName);
01490     if (sms == NULL) {
01491       // Check for the special case (noted above), before we give up:
01492       if (urlPreSuffix[0] == '\0') {
01493         streamName = urlSuffix;
01494       } else {
01495         concatenatedStreamName = new char[strlen(urlPreSuffix) + strlen(urlSuffix) + 2]; // allow for the "/" and the trailing '\0'
01496         sprintf(concatenatedStreamName, "%s/%s", urlPreSuffix, urlSuffix);
01497         streamName = concatenatedStreamName;
01498       }
01499       trackId = NULL;
01500       
01501       // Check again:
01502       sms = fOurServer.lookupServerMediaSession(streamName);
01503     }
01504     if (sms == NULL) {
01505       if (fOurServerMediaSession == NULL) {
01506         // The client asked for a stream that doesn't exist (and this session descriptor has not been used before):
01507         ourClientConnection->handleCmd_notFound();
01508       } else {
01509         // The client asked for a stream that doesn't exist, but using a stream id for a stream that does exist. Bad request:
01510         ourClientConnection->handleCmd_bad();
01511       }
01512       break;
01513     } else {
01514       if (fOurServerMediaSession == NULL) {
01515         // We're accessing the "ServerMediaSession" for the first time.
01516         fOurServerMediaSession = sms;
01517         fOurServerMediaSession->incrementReferenceCount();
01518       } else if (sms != fOurServerMediaSession) {
01519         // The client asked for a stream that's different from the one originally requested for this stream id.  Bad request:
01520         ourClientConnection->handleCmd_bad();
01521         break;
01522       }
01523     }
01524     
01525     if (fStreamStates == NULL) {
01526       // This is the first "SETUP" for this session.  Set up our array of states for all of this session's subsessions (tracks):
01527       ServerMediaSubsessionIterator iter(*fOurServerMediaSession);
01528       for (fNumStreamStates = 0; iter.next() != NULL; ++fNumStreamStates) {} // begin by counting the number of subsessions (tracks)
01529       
01530       fStreamStates = new struct streamState[fNumStreamStates];
01531       
01532       iter.reset();
01533       ServerMediaSubsession* subsession;
01534       for (unsigned i = 0; i < fNumStreamStates; ++i) {
01535         subsession = iter.next();
01536         fStreamStates[i].subsession = subsession;
01537         fStreamStates[i].streamToken = NULL; // for now; it may be changed by the "getStreamParameters()" call that comes later
01538       }
01539     }
01540     
01541     // Look up information for the specified subsession (track):
01542     ServerMediaSubsession* subsession = NULL;
01543     unsigned streamNum;
01544     if (trackId != NULL && trackId[0] != '\0') { // normal case
01545       for (streamNum = 0; streamNum < fNumStreamStates; ++streamNum) {
01546         subsession = fStreamStates[streamNum].subsession;
01547         if (subsession != NULL && strcmp(trackId, subsession->trackId()) == 0) break;
01548       }
01549       if (streamNum >= fNumStreamStates) {
01550         // The specified track id doesn't exist, so this request fails:
01551         ourClientConnection->handleCmd_notFound();
01552         break;
01553       }
01554     } else {
01555       // Weird case: there was no track id in the URL.
01556       // This works only if we have only one subsession:
01557       if (fNumStreamStates != 1 || fStreamStates[0].subsession == NULL) {
01558         ourClientConnection->handleCmd_bad();
01559         break;
01560       }
01561       streamNum = 0;
01562       subsession = fStreamStates[streamNum].subsession;
01563     }
01564     // ASSERT: subsession != NULL
01565     
01566     // Look for a "Transport:" header in the request string, to extract client parameters:
01567     StreamingMode streamingMode;
01568     char* streamingModeString = NULL; // set when RAW_UDP streaming is specified
01569     char* clientsDestinationAddressStr;
01570     u_int8_t clientsDestinationTTL;
01571     portNumBits clientRTPPortNum, clientRTCPPortNum;
01572     unsigned char rtpChannelId, rtcpChannelId;
01573     parseTransportHeader(fullRequestStr, streamingMode, streamingModeString,
01574                          clientsDestinationAddressStr, clientsDestinationTTL,
01575                          clientRTPPortNum, clientRTCPPortNum,
01576                          rtpChannelId, rtcpChannelId);
01577     if ((streamingMode == RTP_TCP && rtpChannelId == 0xFF) ||
01578         (streamingMode != RTP_TCP && ourClientConnection->fClientOutputSocket != ourClientConnection->fClientInputSocket)) {
01579       // An anomolous situation, caused by a buggy client.  Either:
01580       //     1/ TCP streaming was requested, but with no "interleaving=" fields.  (QuickTime Player sometimes does this.), or
01581       //     2/ TCP streaming was not requested, but we're doing RTSP-over-HTTP tunneling (which implies TCP streaming).
01582       // In either case, we assume TCP streaming, and set the RTP and RTCP channel ids to proper values:
01583       streamingMode = RTP_TCP;
01584       rtpChannelId = fTCPStreamIdCount; rtcpChannelId = fTCPStreamIdCount+1;
01585     }
01586     if (streamingMode == RTP_TCP) fTCPStreamIdCount += 2;
01587     
01588     Port clientRTPPort(clientRTPPortNum);
01589     Port clientRTCPPort(clientRTCPPortNum);
01590     
01591     // Next, check whether a "Range:" or "x-playNow:" header is present in the request.
01592     // This isn't legal, but some clients do this to combine "SETUP" and "PLAY":
01593     double rangeStart = 0.0, rangeEnd = 0.0;
01594     char* absStart = NULL; char* absEnd = NULL;
01595     Boolean startTimeIsNow;
01596     if (parseRangeHeader(fullRequestStr, rangeStart, rangeEnd, absStart, absEnd, startTimeIsNow)) {
01597       delete[] absStart; delete[] absEnd;
01598       fStreamAfterSETUP = True;
01599     } else if (parsePlayNowHeader(fullRequestStr)) {
01600       fStreamAfterSETUP = True;
01601     } else {
01602       fStreamAfterSETUP = False;
01603     }
01604     
01605     // Then, get server parameters from the 'subsession':
01606     int tcpSocketNum = streamingMode == RTP_TCP ? ourClientConnection->fClientOutputSocket : -1;
01607     netAddressBits destinationAddress = 0;
01608     u_int8_t destinationTTL = 255;
01609 #ifdef RTSP_ALLOW_CLIENT_DESTINATION_SETTING
01610     if (clientsDestinationAddressStr != NULL) {
01611       // Use the client-provided "destination" address.
01612       // Note: This potentially allows the server to be used in denial-of-service
01613       // attacks, so don't enable this code unless you're sure that clients are
01614       // trusted.
01615       destinationAddress = our_inet_addr(clientsDestinationAddressStr);
01616     }
01617     // Also use the client-provided TTL.
01618     destinationTTL = clientsDestinationTTL;
01619 #endif
01620     delete[] clientsDestinationAddressStr;
01621     Port serverRTPPort(0);
01622     Port serverRTCPPort(0);
01623     
01624     // Make sure that we transmit on the same interface that's used by the client (in case we're a multi-homed server):
01625     struct sockaddr_in sourceAddr; SOCKLEN_T namelen = sizeof sourceAddr;
01626     getsockname(ourClientConnection->fClientInputSocket, (struct sockaddr*)&sourceAddr, &namelen);
01627     netAddressBits origSendingInterfaceAddr = SendingInterfaceAddr;
01628     netAddressBits origReceivingInterfaceAddr = ReceivingInterfaceAddr;
01629     // NOTE: The following might not work properly, so we ifdef it out for now:
01630 #ifdef HACK_FOR_MULTIHOMED_SERVERS
01631     ReceivingInterfaceAddr = SendingInterfaceAddr = sourceAddr.sin_addr.s_addr;
01632 #endif
01633     
01634     subsession->getStreamParameters(fOurSessionId, ourClientConnection->fClientAddr.sin_addr.s_addr,
01635                                     clientRTPPort, clientRTCPPort,
01636                                     tcpSocketNum, rtpChannelId, rtcpChannelId,
01637                                     destinationAddress, destinationTTL, fIsMulticast,
01638                                     serverRTPPort, serverRTCPPort,
01639                                     fStreamStates[streamNum].streamToken);
01640     SendingInterfaceAddr = origSendingInterfaceAddr;
01641     ReceivingInterfaceAddr = origReceivingInterfaceAddr;
01642     
01643     AddressString destAddrStr(destinationAddress);
01644     AddressString sourceAddrStr(sourceAddr);
01645     char timeoutParameterString[100];
01646     if (fOurServer.fReclamationTestSeconds > 0) {
01647       sprintf(timeoutParameterString, ";timeout=%u", fOurServer.fReclamationTestSeconds);
01648     } else {
01649       timeoutParameterString[0] = '\0';
01650     }
01651     if (fIsMulticast) {
01652       switch (streamingMode) {
01653           case RTP_UDP: {
01654             snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
01655                      "RTSP/1.0 200 OK\r\n"
01656                      "CSeq: %s\r\n"
01657                      "%s"
01658                      "Transport: RTP/AVP;multicast;destination=%s;source=%s;port=%d-%d;ttl=%d\r\n"
01659                      "Session: %08X%s\r\n\r\n",
01660                      ourClientConnection->fCurrentCSeq,
01661                      dateHeader(),
01662                      destAddrStr.val(), sourceAddrStr.val(), ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()), destinationTTL,
01663                      fOurSessionId, timeoutParameterString);
01664             break;
01665           }
01666           case RTP_TCP: {
01667             // multicast streams can't be sent via TCP
01668             ourClientConnection->handleCmd_unsupportedTransport();
01669             break;
01670           }
01671           case RAW_UDP: {
01672             snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
01673                      "RTSP/1.0 200 OK\r\n"
01674                      "CSeq: %s\r\n"
01675                      "%s"
01676                      "Transport: %s;multicast;destination=%s;source=%s;port=%d;ttl=%d\r\n"
01677                      "Session: %08X%s\r\n\r\n",
01678                      ourClientConnection->fCurrentCSeq,
01679                      dateHeader(),
01680                      streamingModeString, destAddrStr.val(), sourceAddrStr.val(), ntohs(serverRTPPort.num()), destinationTTL,
01681                      fOurSessionId, timeoutParameterString);
01682             break;
01683           }
01684       }
01685     } else {
01686       switch (streamingMode) {
01687           case RTP_UDP: {
01688             snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
01689                      "RTSP/1.0 200 OK\r\n"
01690                      "CSeq: %s\r\n"
01691                      "%s"
01692                      "Transport: RTP/AVP;unicast;destination=%s;source=%s;client_port=%d-%d;server_port=%d-%d\r\n"
01693                      "Session: %08X%s\r\n\r\n",
01694                      ourClientConnection->fCurrentCSeq,
01695                      dateHeader(),
01696                      destAddrStr.val(), sourceAddrStr.val(), ntohs(clientRTPPort.num()), ntohs(clientRTCPPort.num()), ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()),
01697                      fOurSessionId, timeoutParameterString);
01698             break;
01699           }
01700           case RTP_TCP: {
01701             snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
01702                      "RTSP/1.0 200 OK\r\n"
01703                      "CSeq: %s\r\n"
01704                      "%s"
01705                      "Transport: RTP/AVP/TCP;unicast;destination=%s;source=%s;interleaved=%d-%d\r\n"
01706                      "Session: %08X%s\r\n\r\n",
01707                      ourClientConnection->fCurrentCSeq,
01708                      dateHeader(),
01709                      destAddrStr.val(), sourceAddrStr.val(), rtpChannelId, rtcpChannelId,
01710                      fOurSessionId, timeoutParameterString);
01711             break;
01712           }
01713           case RAW_UDP: {
01714             snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
01715                      "RTSP/1.0 200 OK\r\n"
01716                      "CSeq: %s\r\n"
01717                      "%s"
01718                      "Transport: %s;unicast;destination=%s;source=%s;client_port=%d;server_port=%d\r\n"
01719                      "Session: %08X%s\r\n\r\n",
01720                      ourClientConnection->fCurrentCSeq,
01721                      dateHeader(),
01722                      streamingModeString, destAddrStr.val(), sourceAddrStr.val(), ntohs(clientRTPPort.num()), ntohs(serverRTPPort.num()),
01723                      fOurSessionId, timeoutParameterString);
01724             break;
01725           }
01726       }
01727     }
01728     delete[] streamingModeString;
01729   } while (0);
01730   
01731   delete[] concatenatedStreamName;
01732 }

void RTSPServer::RTSPClientSession::handleCmd_withinSession ( RTSPClientConnection ourClientConnection,
char const *  cmdName,
char const *  urlPreSuffix,
char const *  urlSuffix,
char const *  fullRequestStr 
) [protected, virtual]

Definition at line 1735 of file RTSPServer.cpp.

References RTSPServer::RTSPClientConnection::handleCmd_notFound(), RTSPServer::RTSPClientConnection::handleCmd_notSupported(), iter, MediaSubsessionIterator::next(), NULL, and subsession.

Referenced by handleCmd_SETUP(), and RTSPServer::RTSPClientConnection::handleRequestBytes().

01738                                                       {
01739   // This will either be:
01740   // - a non-aggregated operation, if "urlPreSuffix" is the session (stream)
01741   //   name and "urlSuffix" is the subsession (track) name, or
01742   // - an aggregated operation, if "urlSuffix" is the session (stream) name,
01743   //   or "urlPreSuffix" is the session (stream) name, and "urlSuffix" is empty,
01744   //   or "urlPreSuffix" and "urlSuffix" are both nonempty, but when concatenated, (with "/") form the session (stream) name.
01745   // Begin by figuring out which of these it is:
01746   ServerMediaSubsession* subsession;
01747   
01748   if (fOurServerMediaSession == NULL) { // There wasn't a previous SETUP!
01749     ourClientConnection->handleCmd_notSupported();
01750     return;
01751   } else if (urlSuffix[0] != '\0' && strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0) {
01752     // Non-aggregated operation.
01753     // Look up the media subsession whose track id is "urlSuffix":
01754     ServerMediaSubsessionIterator iter(*fOurServerMediaSession);
01755     while ((subsession = iter.next()) != NULL) {
01756       if (strcmp(subsession->trackId(), urlSuffix) == 0) break; // success
01757     }
01758     if (subsession == NULL) { // no such track!
01759       ourClientConnection->handleCmd_notFound();
01760       return;
01761     }
01762   } else if (strcmp(fOurServerMediaSession->streamName(), urlSuffix) == 0 ||
01763              (urlSuffix[0] == '\0' && strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0)) {
01764     // Aggregated operation
01765     subsession = NULL;
01766   } else if (urlPreSuffix[0] != '\0' && urlSuffix[0] != '\0') {
01767     // Aggregated operation, if <urlPreSuffix>/<urlSuffix> is the session (stream) name:
01768     unsigned const urlPreSuffixLen = strlen(urlPreSuffix);
01769     if (strncmp(fOurServerMediaSession->streamName(), urlPreSuffix, urlPreSuffixLen) == 0 &&
01770         fOurServerMediaSession->streamName()[urlPreSuffixLen] == '/' &&
01771         strcmp(&(fOurServerMediaSession->streamName())[urlPreSuffixLen+1], urlSuffix) == 0) {
01772       subsession = NULL;
01773     } else {
01774       ourClientConnection->handleCmd_notFound();
01775       return;
01776     }
01777   } else { // the request doesn't match a known stream and/or track at all!
01778     ourClientConnection->handleCmd_notFound();
01779     return;
01780   }
01781   
01782   if (strcmp(cmdName, "TEARDOWN") == 0) {
01783     handleCmd_TEARDOWN(ourClientConnection, subsession);
01784   } else if (strcmp(cmdName, "PLAY") == 0) {
01785     handleCmd_PLAY(ourClientConnection, subsession, fullRequestStr);
01786   } else if (strcmp(cmdName, "PAUSE") == 0) {
01787     handleCmd_PAUSE(ourClientConnection, subsession);
01788   } else if (strcmp(cmdName, "GET_PARAMETER") == 0) {
01789     handleCmd_GET_PARAMETER(ourClientConnection, subsession, fullRequestStr);
01790   } else if (strcmp(cmdName, "SET_PARAMETER") == 0) {
01791     handleCmd_SET_PARAMETER(ourClientConnection, subsession, fullRequestStr);
01792   }
01793 }

void RTSPServer::RTSPClientSession::handleCmd_TEARDOWN ( RTSPClientConnection ourClientConnection,
ServerMediaSubsession subsession 
) [protected, virtual]

Definition at line 1796 of file RTSPServer.cpp.

References False, NULL, subsession, and True.

01797                                                         {
01798   unsigned i;
01799   for (i = 0; i < fNumStreamStates; ++i) {
01800     if (subsession == NULL /* means: aggregated operation */
01801         || subsession == fStreamStates[i].subsession) {
01802       if (fStreamStates[i].subsession != NULL) {
01803         fStreamStates[i].subsession->deleteStream(fOurSessionId, fStreamStates[i].streamToken);
01804         fStreamStates[i].subsession = NULL;
01805       }
01806     }
01807   }
01808   
01809   setRTSPResponse(ourClientConnection, "200 OK");
01810   
01811   // Optimization: If all subsessions have now been torn down, then we know that we can reclaim our object now.
01812   // (Without this optimization, however, this object would still get reclaimed later, as a result of a 'liveness' timeout.)
01813   Boolean noSubsessionsRemain = True;
01814   for (i = 0; i < fNumStreamStates; ++i) {
01815     if (fStreamStates[i].subsession != NULL) {
01816       noSubsessionsRemain = False;
01817       break;
01818     }
01819   }
01820   if (noSubsessionsRemain) delete this;
01821 }

void RTSPServer::RTSPClientSession::handleCmd_PLAY ( RTSPClientConnection ourClientConnection,
ServerMediaSubsession subsession,
char const *  fullRequestStr 
) [protected, virtual]

Definition at line 1824 of file RTSPServer.cpp.

References dateHeader(), duration, RTSPServer::RTSPClientConnection::fClientInputSocket, RTSPServer::RTSPClientConnection::fCurrentCSeq, RTSPServer::RTSPClientConnection::fResponseBuffer, RTSPServer::RTSPClientConnection::handleAlternativeRequestByte(), NULL, parseRangeHeader(), parseScaleHeader(), RTSPServer::rtspURL(), scale, strDup(), and subsession.

01825                                                                                 {
01826   char* rtspURL = fOurServer.rtspURL(fOurServerMediaSession, ourClientConnection->fClientInputSocket);
01827   unsigned rtspURLSize = strlen(rtspURL);
01828   
01829   // Parse the client's "Scale:" header, if any:
01830   float scale;
01831   Boolean sawScaleHeader = parseScaleHeader(fullRequestStr, scale);
01832   
01833   // Try to set the stream's scale factor to this value:
01834   if (subsession == NULL /*aggregate op*/) {
01835     fOurServerMediaSession->testScaleFactor(scale);
01836   } else {
01837     subsession->testScaleFactor(scale);
01838   }
01839   
01840   char buf[100];
01841   char* scaleHeader;
01842   if (!sawScaleHeader) {
01843     buf[0] = '\0'; // Because we didn't see a Scale: header, don't send one back
01844   } else {
01845     sprintf(buf, "Scale: %f\r\n", scale);
01846   }
01847   scaleHeader = strDup(buf);
01848   
01849   // Parse the client's "Range:" header, if any:
01850   float duration = 0.0;
01851   double rangeStart = 0.0, rangeEnd = 0.0;
01852   char* absStart = NULL; char* absEnd = NULL;
01853   Boolean startTimeIsNow;
01854   Boolean sawRangeHeader
01855     = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd, absStart, absEnd, startTimeIsNow);
01856   
01857   if (sawRangeHeader && absStart == NULL/*not seeking by 'absolute' time*/) {
01858     // Use this information, plus the stream's duration (if known), to create our own "Range:" header, for the response:
01859     duration = subsession == NULL /*aggregate op*/
01860       ? fOurServerMediaSession->duration() : subsession->duration();
01861     if (duration < 0.0) {
01862       // We're an aggregate PLAY, but the subsessions have different durations.
01863       // Use the largest of these durations in our header
01864       duration = -duration;
01865     }
01866     
01867     // Make sure that "rangeStart" and "rangeEnd" (from the client's "Range:" header)
01868     // have sane values, before we send back our own "Range:" header in our response:
01869     if (rangeStart < 0.0) rangeStart = 0.0;
01870     else if (rangeStart > duration) rangeStart = duration;
01871     if (rangeEnd < 0.0) rangeEnd = 0.0;
01872     else if (rangeEnd > duration) rangeEnd = duration;
01873     if ((scale > 0.0 && rangeStart > rangeEnd && rangeEnd > 0.0) ||
01874         (scale < 0.0 && rangeStart < rangeEnd)) {
01875       // "rangeStart" and "rangeEnd" were the wrong way around; swap them:
01876       double tmp = rangeStart;
01877       rangeStart = rangeEnd;
01878       rangeEnd = tmp;
01879     }
01880   }
01881   
01882   // Create a "RTP-Info:" line.  It will get filled in from each subsession's state:
01883   char const* rtpInfoFmt =
01884     "%s" // "RTP-Info:", plus any preceding rtpInfo items
01885     "%s" // comma separator, if needed
01886     "url=%s/%s"
01887     ";seq=%d"
01888     ";rtptime=%u"
01889     ;
01890   unsigned rtpInfoFmtSize = strlen(rtpInfoFmt);
01891   char* rtpInfo = strDup("RTP-Info: ");
01892   unsigned i, numRTPInfoItems = 0;
01893   
01894   // Do any required seeking/scaling on each subsession, before starting streaming.
01895   // (However, we don't do this if the "PLAY" request was for just a single subsession
01896   // of a multiple-subsession stream; for such streams, seeking/scaling can be done
01897   // only with an aggregate "PLAY".)
01898   for (i = 0; i < fNumStreamStates; ++i) {
01899     if (subsession == NULL /* means: aggregated operation */ || fNumStreamStates == 1) {
01900       if (fStreamStates[i].subsession != NULL) {
01901         if (sawScaleHeader) {
01902           fStreamStates[i].subsession->setStreamScale(fOurSessionId, fStreamStates[i].streamToken, scale);
01903         }
01904         if (absStart != NULL) {
01905           // Special case handling for seeking by 'absolute' time:
01906         
01907           fStreamStates[i].subsession->seekStream(fOurSessionId, fStreamStates[i].streamToken, absStart, absEnd);
01908         } else {
01909           // Seeking by relative (NPT) time:
01910           
01911           u_int64_t numBytes;
01912           if (!sawRangeHeader || startTimeIsNow) {
01913             // We're resuming streaming without seeking, so we just do a 'null' seek
01914             // (to get our NPT, and to specify when to end streaming):
01915             fStreamStates[i].subsession->nullSeekStream(fOurSessionId, fStreamStates[i].streamToken,
01916                                                         rangeEnd, numBytes);
01917           } else {
01918             // We do a real 'seek':
01919             double streamDuration = 0.0; // by default; means: stream until the end of the media
01920             if (rangeEnd > 0.0 && (rangeEnd+0.001) < duration) {
01921               // the 0.001 is because we limited the values to 3 decimal places
01922               // We want the stream to end early.  Set the duration we want:
01923               streamDuration = rangeEnd - rangeStart;
01924               if (streamDuration < 0.0) streamDuration = -streamDuration;
01925                   // should happen only if scale < 0.0
01926             }
01927             fStreamStates[i].subsession->seekStream(fOurSessionId, fStreamStates[i].streamToken,
01928                                                     rangeStart, streamDuration, numBytes);
01929           }
01930         }
01931       }
01932     }
01933   }
01934   
01935   // Create the "Range:" header that we'll send back in our response.
01936   // (Note that we do this after seeking, in case the seeking operation changed the range start time.)
01937   if (absStart != NULL) {
01938     // We're seeking by 'absolute' time:
01939     if (absEnd == NULL) {
01940       sprintf(buf, "Range: clock=%s-\r\n", absStart);
01941     } else {
01942       sprintf(buf, "Range: clock=%s-%s\r\n", absStart, absEnd);
01943     }
01944     delete[] absStart; delete[] absEnd;
01945   } else {
01946     // We're seeking by relative (NPT) time:
01947     if (!sawRangeHeader || startTimeIsNow) {
01948       // We didn't seek, so in our response, begin the range with the current NPT (normal play time):
01949       float curNPT = 0.0;
01950       for (i = 0; i < fNumStreamStates; ++i) {
01951         if (subsession == NULL /* means: aggregated operation */
01952             || subsession == fStreamStates[i].subsession) {
01953           if (fStreamStates[i].subsession == NULL) continue;
01954           float npt = fStreamStates[i].subsession->getCurrentNPT(fStreamStates[i].streamToken);
01955           if (npt > curNPT) curNPT = npt;
01956           // Note: If this is an aggregate "PLAY" on a multi-subsession stream,
01957           // then it's conceivable that the NPTs of each subsession may differ
01958           // (if there has been a previous seek on just one subsession).
01959           // In this (unusual) case, we just return the largest NPT; I hope that turns out OK...
01960         }
01961       }
01962       rangeStart = curNPT;
01963     }
01964 
01965     if (rangeEnd == 0.0 && scale >= 0.0) {
01966       sprintf(buf, "Range: npt=%.3f-\r\n", rangeStart);
01967     } else {
01968       sprintf(buf, "Range: npt=%.3f-%.3f\r\n", rangeStart, rangeEnd);
01969     }
01970   }
01971   char* rangeHeader = strDup(buf);
01972   
01973   // Now, start streaming:
01974   for (i = 0; i < fNumStreamStates; ++i) {
01975     if (subsession == NULL /* means: aggregated operation */
01976         || subsession == fStreamStates[i].subsession) {
01977       unsigned short rtpSeqNum = 0;
01978       unsigned rtpTimestamp = 0;
01979       if (fStreamStates[i].subsession == NULL) continue;
01980       fStreamStates[i].subsession->startStream(fOurSessionId,
01981                                                fStreamStates[i].streamToken,
01982                                                (TaskFunc*)noteClientLiveness, this,
01983                                                rtpSeqNum, rtpTimestamp,
01984                                                RTSPServer::RTSPClientConnection::handleAlternativeRequestByte, ourClientConnection);
01985       const char *urlSuffix = fStreamStates[i].subsession->trackId();
01986       char* prevRTPInfo = rtpInfo;
01987       unsigned rtpInfoSize = rtpInfoFmtSize
01988         + strlen(prevRTPInfo)
01989         + 1
01990         + rtspURLSize + strlen(urlSuffix)
01991         + 5 /*max unsigned short len*/
01992         + 10 /*max unsigned (32-bit) len*/
01993         + 2 /*allows for trailing \r\n at final end of string*/;
01994       rtpInfo = new char[rtpInfoSize];
01995       sprintf(rtpInfo, rtpInfoFmt,
01996               prevRTPInfo,
01997               numRTPInfoItems++ == 0 ? "" : ",",
01998               rtspURL, urlSuffix,
01999               rtpSeqNum,
02000               rtpTimestamp
02001               );
02002       delete[] prevRTPInfo;
02003     }
02004   }
02005   if (numRTPInfoItems == 0) {
02006     rtpInfo[0] = '\0';
02007   } else {
02008     unsigned rtpInfoLen = strlen(rtpInfo);
02009     rtpInfo[rtpInfoLen] = '\r';
02010     rtpInfo[rtpInfoLen+1] = '\n';
02011     rtpInfo[rtpInfoLen+2] = '\0';
02012   }
02013   
02014   // Fill in the response:
02015   snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
02016            "RTSP/1.0 200 OK\r\n"
02017            "CSeq: %s\r\n"
02018            "%s"
02019            "%s"
02020            "%s"
02021            "Session: %08X\r\n"
02022            "%s\r\n",
02023            ourClientConnection->fCurrentCSeq,
02024            dateHeader(),
02025            scaleHeader,
02026            rangeHeader,
02027            fOurSessionId,
02028            rtpInfo);
02029   delete[] rtpInfo; delete[] rangeHeader;
02030   delete[] scaleHeader; delete[] rtspURL;
02031 }

void RTSPServer::RTSPClientSession::handleCmd_PAUSE ( RTSPClientConnection ourClientConnection,
ServerMediaSubsession subsession 
) [protected, virtual]

Definition at line 2034 of file RTSPServer.cpp.

References NULL, and subsession.

02035                                                      {
02036   for (unsigned i = 0; i < fNumStreamStates; ++i) {
02037     if (subsession == NULL /* means: aggregated operation */
02038         || subsession == fStreamStates[i].subsession) {
02039       if (fStreamStates[i].subsession != NULL) {
02040         fStreamStates[i].subsession->pauseStream(fOurSessionId, fStreamStates[i].streamToken);
02041       }
02042     }
02043   }
02044   
02045   setRTSPResponse(ourClientConnection, "200 OK", fOurSessionId);
02046 }

void RTSPServer::RTSPClientSession::handleCmd_GET_PARAMETER ( RTSPClientConnection ourClientConnection,
ServerMediaSubsession subsession,
char const *  fullRequestStr 
) [protected, virtual]

Definition at line 2049 of file RTSPServer.cpp.

References LIVEMEDIA_LIBRARY_VERSION_STRING.

02050                                                                                                  {
02051   // By default, we implement "GET_PARAMETER" just as a 'keep alive', and send back a dummy response.
02052   // (If you want to handle "GET_PARAMETER" properly, you can do so by defining a subclass of "RTSPServer"
02053   // and "RTSPServer::RTSPClientSession", and then reimplement this virtual function in your subclass.)
02054   setRTSPResponse(ourClientConnection, "200 OK", fOurSessionId, LIVEMEDIA_LIBRARY_VERSION_STRING);
02055 }

void RTSPServer::RTSPClientSession::handleCmd_SET_PARAMETER ( RTSPClientConnection ourClientConnection,
ServerMediaSubsession subsession,
char const *  fullRequestStr 
) [protected, virtual]

Definition at line 2058 of file RTSPServer.cpp.

02059                                                                                                  {
02060   // By default, we implement "SET_PARAMETER" just as a 'keep alive', and send back an empty response.
02061   // (If you want to handle "SET_PARAMETER" properly, you can do so by defining a subclass of "RTSPServer"
02062   // and "RTSPServer::RTSPClientSession", and then reimplement this virtual function in your subclass.)
02063   setRTSPResponse(ourClientConnection, "200 OK", fOurSessionId);
02064 }

UsageEnvironment& RTSPServer::RTSPClientSession::envir (  )  [inline, protected]

Definition at line 288 of file RTSPServer.hh.

References Medium::envir(), and fOurServer.

Referenced by noteLiveness(), and ~RTSPClientSession().

00288 { return fOurServer.envir(); }

void RTSPServer::RTSPClientSession::reclaimStreamStates (  )  [protected]

Definition at line 1384 of file RTSPServer.cpp.

References fNumStreamStates, fOurSessionId, fStreamStates, NULL, RTSPServer::RTSPClientSession::streamState::streamToken, and subsession.

Referenced by ~RTSPClientSession().

01384                                                       {
01385   for (unsigned i = 0; i < fNumStreamStates; ++i) {
01386     if (fStreamStates[i].subsession != NULL) {
01387       fStreamStates[i].subsession->deleteStream(fOurSessionId, fStreamStates[i].streamToken);
01388     }
01389   }
01390   delete[] fStreamStates; fStreamStates = NULL;
01391   fNumStreamStates = 0;
01392 }

Boolean RTSPServer::RTSPClientSession::isMulticast (  )  const [inline, protected]

Definition at line 290 of file RTSPServer.hh.

References fIsMulticast.

00290 { return fIsMulticast; }

void RTSPServer::RTSPClientSession::noteLiveness (  )  [protected]

Definition at line 2076 of file RTSPServer.cpp.

References envir(), fLivenessCheckTask, fOurServer, RTSPServer::fReclamationTestSeconds, livenessTimeoutTask(), TaskScheduler::rescheduleDelayedTask(), and UsageEnvironment::taskScheduler().

Referenced by RTSPServer::RTSPClientConnection::handleRequestBytes(), noteClientLiveness(), and RTSPClientSession().

02076                                                {
02077   if (fOurServer.fReclamationTestSeconds > 0) {
02078     envir().taskScheduler()
02079       .rescheduleDelayedTask(fLivenessCheckTask,
02080                              fOurServer.fReclamationTestSeconds*1000000,
02081                              (TaskFunc*)livenessTimeoutTask, this);
02082   }
02083 }

void RTSPServer::RTSPClientSession::noteClientLiveness ( RTSPClientSession clientSession  )  [static, protected]

Definition at line 2086 of file RTSPServer.cpp.

References fOurServerMediaSession, fOurSessionId, noteLiveness(), NULL, and ServerMediaSession::streamName().

02086                                                      {
02087 #ifdef DEBUG
02088   char const* streamName
02089     = (clientSession->fOurServerMediaSession == NULL) ? "???" : clientSession->fOurServerMediaSession->streamName();
02090   fprintf(stderr, "RTSP client session (id \"%08X\", stream name \"%s\"): Liveness indication\n",
02091           clientSession->fOurSessionId, streamName);
02092 #endif
02093   clientSession->noteLiveness();
02094 }

void RTSPServer::RTSPClientSession::livenessTimeoutTask ( RTSPClientSession clientSession  )  [static, protected]

Definition at line 2097 of file RTSPServer.cpp.

References fOurServerMediaSession, fOurSessionId, NULL, and ServerMediaSession::streamName().

Referenced by noteLiveness().

02097                                                       {
02098   // If this gets called, the client session is assumed to have timed out,
02099   // so delete it:
02100 #ifdef DEBUG
02101   char const* streamName
02102     = (clientSession->fOurServerMediaSession == NULL) ? "???" : clientSession->fOurServerMediaSession->streamName();
02103   fprintf(stderr, "RTSP client session (id \"%08X\", stream name \"%s\") has timed out (due to inactivity)\n",
02104           clientSession->fOurSessionId, streamName);
02105 #endif
02106   delete clientSession;
02107 }

void RTSPServer::RTSPClientSession::setRTSPResponse ( RTSPClientConnection ourClientConnection,
char const *  responseStr 
) [inline, protected]

Definition at line 296 of file RTSPServer.hh.

References RTSPServer::RTSPClientConnection::setRTSPResponse().

00296 { ourClientConnection->setRTSPResponse(responseStr); }

void RTSPServer::RTSPClientSession::setRTSPResponse ( RTSPClientConnection ourClientConnection,
char const *  responseStr,
u_int32_t  sessionId 
) [inline, protected]

Definition at line 297 of file RTSPServer.hh.

References RTSPServer::RTSPClientConnection::setRTSPResponse().

00297 { ourClientConnection->setRTSPResponse(responseStr, sessionId); }

void RTSPServer::RTSPClientSession::setRTSPResponse ( RTSPClientConnection ourClientConnection,
char const *  responseStr,
char const *  contentStr 
) [inline, protected]

Definition at line 298 of file RTSPServer.hh.

References RTSPServer::RTSPClientConnection::setRTSPResponse().

00298 { ourClientConnection->setRTSPResponse(responseStr, contentStr); }

void RTSPServer::RTSPClientSession::setRTSPResponse ( RTSPClientConnection ourClientConnection,
char const *  responseStr,
u_int32_t  sessionId,
char const *  contentStr 
) [inline, protected]

Definition at line 299 of file RTSPServer.hh.

References RTSPServer::RTSPClientConnection::setRTSPResponse().

00299 { ourClientConnection->setRTSPResponse(responseStr, sessionId, contentStr); }

Boolean RTSPServer::RTSPClientSession::usesTCPTransport (  )  const [inline, protected]

Definition at line 307 of file RTSPServer.hh.

References fTCPStreamIdCount.

00307 { return fTCPStreamIdCount > 0; }


Friends And Related Function Documentation

friend class RTSPServer [friend]

Definition at line 268 of file RTSPServer.hh.

friend class RTSPClientConnection [friend]

Definition at line 269 of file RTSPServer.hh.


Field Documentation

RTSPServer& RTSPServer::RTSPClientSession::fOurServer [protected]

Definition at line 302 of file RTSPServer.hh.

Referenced by envir(), noteLiveness(), and ~RTSPClientSession().

u_int32_t RTSPServer::RTSPClientSession::fOurSessionId [protected]

Definition at line 303 of file RTSPServer.hh.

Referenced by livenessTimeoutTask(), noteClientLiveness(), reclaimStreamStates(), and ~RTSPClientSession().

ServerMediaSession* RTSPServer::RTSPClientSession::fOurServerMediaSession [protected]

Definition at line 304 of file RTSPServer.hh.

Referenced by RTSPServer::closeAllClientSessionsForServerMediaSession(), livenessTimeoutTask(), noteClientLiveness(), and ~RTSPClientSession().

Boolean RTSPServer::RTSPClientSession::fIsMulticast [protected]

Definition at line 305 of file RTSPServer.hh.

Referenced by isMulticast().

Boolean RTSPServer::RTSPClientSession::fStreamAfterSETUP [protected]

Definition at line 305 of file RTSPServer.hh.

Referenced by RTSPServer::RTSPClientConnection::handleRequestBytes().

unsigned char RTSPServer::RTSPClientSession::fTCPStreamIdCount [protected]

Definition at line 306 of file RTSPServer.hh.

Referenced by usesTCPTransport().

TaskToken RTSPServer::RTSPClientSession::fLivenessCheckTask [protected]

Definition at line 308 of file RTSPServer.hh.

Referenced by noteLiveness(), and ~RTSPClientSession().

unsigned RTSPServer::RTSPClientSession::fNumStreamStates [protected]

Definition at line 309 of file RTSPServer.hh.

Referenced by reclaimStreamStates().

struct RTSPServer::RTSPClientSession::streamState * RTSPServer::RTSPClientSession::fStreamStates [protected]

Referenced by reclaimStreamStates().


The documentation for this class was generated from the following files:
Generated on Tue Mar 25 14:40:49 2014 for live by  doxygen 1.5.2