RTSPServer::RTSPClientSession Class Reference

#include <RTSPServer.hh>

Collaboration diagram for RTSPServer::RTSPClientSession:

Collaboration graph
[legend]

Protected Member Functions

 RTSPClientSession (RTSPServer &ourServer, u_int32_t sessionId)
virtual ~RTSPClientSession ()
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 269 of file RTSPServer.hh.


Constructor & Destructor Documentation

RTSPServer::RTSPClientSession::RTSPClientSession ( RTSPServer ourServer,
u_int32_t  sessionId 
) [protected]

Definition at line 1356 of file RTSPServer.cpp.

References noteLiveness().

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

Definition at line 1362 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().

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


Member Function Documentation

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

Definition at line 1475 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().

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

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

Definition at line 1738 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().

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

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

Definition at line 1799 of file RTSPServer.cpp.

References False, NULL, subsession, and True.

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

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

Definition at line 1827 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.

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

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

Definition at line 2037 of file RTSPServer.cpp.

References NULL, and subsession.

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

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

Definition at line 2052 of file RTSPServer.cpp.

References LIVEMEDIA_LIBRARY_VERSION_STRING.

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

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

Definition at line 2061 of file RTSPServer.cpp.

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

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

Definition at line 294 of file RTSPServer.hh.

References Medium::envir(), and fOurServer.

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

00294 { return fOurServer.envir(); }

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

Definition at line 1383 of file RTSPServer.cpp.

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

Referenced by ~RTSPClientSession().

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

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

Definition at line 296 of file RTSPServer.hh.

References fIsMulticast.

00296 { return fIsMulticast; }

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

Definition at line 2079 of file RTSPServer.cpp.

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

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

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

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

Definition at line 2089 of file RTSPServer.cpp.

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

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

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

Definition at line 2100 of file RTSPServer.cpp.

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

Referenced by noteLiveness().

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

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

Definition at line 302 of file RTSPServer.hh.

References RTSPServer::RTSPClientConnection::setRTSPResponse().

00302 { ourClientConnection->setRTSPResponse(responseStr); }

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

Definition at line 303 of file RTSPServer.hh.

References RTSPServer::RTSPClientConnection::setRTSPResponse().

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

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

Definition at line 304 of file RTSPServer.hh.

References RTSPServer::RTSPClientConnection::setRTSPResponse().

00304 { 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 305 of file RTSPServer.hh.

References RTSPServer::RTSPClientConnection::setRTSPResponse().

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

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

Definition at line 313 of file RTSPServer.hh.

References fTCPStreamIdCount.

00313 { return fTCPStreamIdCount > 0; }


Friends And Related Function Documentation

friend class RTSPServer [friend]

Definition at line 274 of file RTSPServer.hh.

friend class RTSPClientConnection [friend]

Definition at line 275 of file RTSPServer.hh.


Field Documentation

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

Definition at line 308 of file RTSPServer.hh.

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

u_int32_t RTSPServer::RTSPClientSession::fOurSessionId [protected]

Definition at line 309 of file RTSPServer.hh.

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

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

Definition at line 310 of file RTSPServer.hh.

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

Boolean RTSPServer::RTSPClientSession::fIsMulticast [protected]

Definition at line 311 of file RTSPServer.hh.

Referenced by isMulticast().

Boolean RTSPServer::RTSPClientSession::fStreamAfterSETUP [protected]

Definition at line 311 of file RTSPServer.hh.

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

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

Definition at line 312 of file RTSPServer.hh.

Referenced by usesTCPTransport().

TaskToken RTSPServer::RTSPClientSession::fLivenessCheckTask [protected]

Definition at line 314 of file RTSPServer.hh.

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

unsigned RTSPServer::RTSPClientSession::fNumStreamStates [protected]

Definition at line 315 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 Wed Apr 23 16:17:34 2014 for live by  doxygen 1.5.2