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 ()
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 214 of file RTSPServer.hh.


Constructor & Destructor Documentation

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

Definition at line 1096 of file RTSPServer.cpp.

References noteLiveness().

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

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

01102                                                 {
01103   // Turn off any liveness checking:
01104   envir().taskScheduler().unscheduleDelayedTask(fLivenessCheckTask);
01105 
01106   // Remove ourself from the server's 'client sessions' hash table before we go:
01107   char sessionIdStr[9];
01108   sprintf(sessionIdStr, "%08X", fOurSessionId);
01109   fOurServer.fClientSessions->Remove(sessionIdStr);
01110 
01111   reclaimStreamStates();
01112 
01113   if (fOurServerMediaSession != NULL) {
01114     fOurServerMediaSession->decrementReferenceCount();
01115     if (fOurServerMediaSession->referenceCount() == 0
01116         && fOurServerMediaSession->deleteWhenUnreferenced()) {
01117       fOurServer.removeServerMediaSession(fOurServerMediaSession);
01118       fOurServerMediaSession = NULL;
01119     }
01120   }
01121 }


Member Function Documentation

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

Definition at line 1215 of file RTSPServer.cpp.

References dateHeader(), 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, SOCKLEN_T, subsession, True, and AddressString::val().

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

01216                                                                                                {
01217   // Normally, "urlPreSuffix" should be the session (stream) name, and "urlSuffix" should be the subsession (track) name.
01218   // However (being "liberal in what we accept"), we also handle 'aggregate' SETUP requests (i.e., without a track name),
01219   // in the special case where we have only a single track.  I.e., in this case, we also handle:
01220   //    "urlPreSuffix" is empty and "urlSuffix" is the session (stream) name, or
01221   //    "urlPreSuffix" concatenated with "urlSuffix" (with "/" inbetween) is the session (stream) name.
01222   char const* streamName = urlPreSuffix; // in the normal case
01223   char const* trackId = urlSuffix; // in the normal case
01224   char* concatenatedStreamName = NULL; // in the normal case
01225 
01226   noteLiveness();
01227   do {
01228     // First, make sure the specified stream name exists:
01229     ServerMediaSession* sms = fOurServer.lookupServerMediaSession(streamName);
01230     if (sms == NULL) {
01231       // Check for the special case (noted above), before we give up:
01232       if (urlPreSuffix[0] == '\0') {
01233         streamName = urlSuffix;
01234       } else {
01235         concatenatedStreamName = new char[strlen(urlPreSuffix) + strlen(urlSuffix) + 2]; // allow for the "/" and the trailing '\0'
01236         sprintf(concatenatedStreamName, "%s/%s", urlPreSuffix, urlSuffix);
01237         streamName = concatenatedStreamName;
01238       }
01239       trackId = NULL;
01240 
01241       // Check again:
01242       sms = fOurServer.lookupServerMediaSession(streamName);
01243     }
01244     if (sms == NULL) {
01245       if (fOurServerMediaSession == NULL) {
01246         // The client asked for a stream that doesn't exist (and this session descriptor has not been used before):
01247         ourClientConnection->handleCmd_notFound();
01248       } else {
01249         // The client asked for a stream that doesn't exist, but using a stream id for a stream that does exist. Bad request:
01250         ourClientConnection->handleCmd_bad();
01251       }
01252       break;
01253     } else {
01254       if (fOurServerMediaSession == NULL) {
01255         // We're accessing the "ServerMediaSession" for the first time.
01256         fOurServerMediaSession = sms;
01257         fOurServerMediaSession->incrementReferenceCount();
01258       } else if (sms != fOurServerMediaSession) {
01259         // The client asked for a stream that's different from the one originally requested for this stream id.  Bad request:
01260         ourClientConnection->handleCmd_bad();
01261         break;
01262       }
01263     }
01264 
01265     if (fStreamStates == NULL) {
01266       // This is the first "SETUP" for this session.  Set up our array of states for all of this session's subsessions (tracks):
01267       ServerMediaSubsessionIterator iter(*fOurServerMediaSession);
01268       for (fNumStreamStates = 0; iter.next() != NULL; ++fNumStreamStates) {} // begin by counting the number of subsessions (tracks)
01269 
01270       fStreamStates = new struct streamState[fNumStreamStates];
01271 
01272       iter.reset();
01273       ServerMediaSubsession* subsession;
01274       for (unsigned i = 0; i < fNumStreamStates; ++i) {
01275         subsession = iter.next();
01276         fStreamStates[i].subsession = subsession;
01277         fStreamStates[i].streamToken = NULL; // for now; it may be changed by the "getStreamParameters()" call that comes later
01278       }
01279     }
01280 
01281     // Look up information for the specified subsession (track):
01282     ServerMediaSubsession* subsession = NULL;
01283     unsigned streamNum;
01284     if (trackId != NULL && trackId[0] != '\0') { // normal case
01285       for (streamNum = 0; streamNum < fNumStreamStates; ++streamNum) {
01286         subsession = fStreamStates[streamNum].subsession;
01287         if (subsession != NULL && strcmp(trackId, subsession->trackId()) == 0) break;
01288       }
01289       if (streamNum >= fNumStreamStates) {
01290         // The specified track id doesn't exist, so this request fails:
01291         ourClientConnection->handleCmd_notFound();
01292         break;
01293       }
01294     } else {
01295       // Weird case: there was no track id in the URL.
01296       // This works only if we have only one subsession:
01297       if (fNumStreamStates != 1 || fStreamStates[0].subsession == NULL) {
01298         ourClientConnection->handleCmd_bad();
01299         break;
01300       }
01301       streamNum = 0;
01302       subsession = fStreamStates[streamNum].subsession;
01303     }
01304     // ASSERT: subsession != NULL
01305 
01306     // Look for a "Transport:" header in the request string, to extract client parameters:
01307     StreamingMode streamingMode;
01308     char* streamingModeString = NULL; // set when RAW_UDP streaming is specified
01309     char* clientsDestinationAddressStr;
01310     u_int8_t clientsDestinationTTL;
01311     portNumBits clientRTPPortNum, clientRTCPPortNum;
01312     unsigned char rtpChannelId, rtcpChannelId;
01313     parseTransportHeader(fullRequestStr, streamingMode, streamingModeString,
01314                          clientsDestinationAddressStr, clientsDestinationTTL,
01315                          clientRTPPortNum, clientRTCPPortNum,
01316                          rtpChannelId, rtcpChannelId);
01317     if ((streamingMode == RTP_TCP && rtpChannelId == 0xFF) ||
01318         (streamingMode != RTP_TCP && ourClientConnection->fClientOutputSocket != ourClientConnection->fClientInputSocket)) {
01319       // An anomolous situation, caused by a buggy client.  Either:
01320       //     1/ TCP streaming was requested, but with no "interleaving=" fields.  (QuickTime Player sometimes does this.), or
01321       //     2/ TCP streaming was not requested, but we're doing RTSP-over-HTTP tunneling (which implies TCP streaming).
01322       // In either case, we assume TCP streaming, and set the RTP and RTCP channel ids to proper values:
01323       streamingMode = RTP_TCP;
01324       rtpChannelId = fTCPStreamIdCount; rtcpChannelId = fTCPStreamIdCount+1;
01325     }
01326     if (streamingMode == RTP_TCP) fTCPStreamIdCount += 2;
01327 
01328     Port clientRTPPort(clientRTPPortNum);
01329     Port clientRTCPPort(clientRTCPPortNum);
01330 
01331     // Next, check whether a "Range:" or "x-playNow:" header is present in the request.
01332     // This isn't legal, but some clients do this to combine "SETUP" and "PLAY":
01333     double rangeStart = 0.0, rangeEnd = 0.0;
01334     char* absStart = NULL; char* absEnd = NULL;
01335     if (parseRangeHeader(fullRequestStr, rangeStart, rangeEnd, absStart, absEnd)) {
01336       delete[] absStart; delete[] absEnd;
01337       fStreamAfterSETUP = True;
01338     } else if (parsePlayNowHeader(fullRequestStr)) {
01339       fStreamAfterSETUP = True;
01340     } else {
01341       fStreamAfterSETUP = False;
01342     }
01343 
01344     // Then, get server parameters from the 'subsession':
01345     int tcpSocketNum = streamingMode == RTP_TCP ? ourClientConnection->fClientOutputSocket : -1;
01346     netAddressBits destinationAddress = 0;
01347     u_int8_t destinationTTL = 255;
01348 #ifdef RTSP_ALLOW_CLIENT_DESTINATION_SETTING
01349     if (clientsDestinationAddressStr != NULL) {
01350       // Use the client-provided "destination" address.
01351       // Note: This potentially allows the server to be used in denial-of-service
01352       // attacks, so don't enable this code unless you're sure that clients are
01353       // trusted.
01354       destinationAddress = our_inet_addr(clientsDestinationAddressStr);
01355     }
01356     // Also use the client-provided TTL.
01357     destinationTTL = clientsDestinationTTL;
01358 #endif
01359     delete[] clientsDestinationAddressStr;
01360     Port serverRTPPort(0);
01361     Port serverRTCPPort(0);
01362 
01363     // Make sure that we transmit on the same interface that's used by the client (in case we're a multi-homed server):
01364     struct sockaddr_in sourceAddr; SOCKLEN_T namelen = sizeof sourceAddr;
01365     getsockname(ourClientConnection->fClientInputSocket, (struct sockaddr*)&sourceAddr, &namelen);
01366     netAddressBits origSendingInterfaceAddr = SendingInterfaceAddr;
01367     netAddressBits origReceivingInterfaceAddr = ReceivingInterfaceAddr;
01368     // NOTE: The following might not work properly, so we ifdef it out for now:
01369 #ifdef HACK_FOR_MULTIHOMED_SERVERS
01370     ReceivingInterfaceAddr = SendingInterfaceAddr = sourceAddr.sin_addr.s_addr;
01371 #endif
01372 
01373     subsession->getStreamParameters(fOurSessionId, ourClientConnection->fClientAddr.sin_addr.s_addr,
01374                                     clientRTPPort, clientRTCPPort,
01375                                     tcpSocketNum, rtpChannelId, rtcpChannelId,
01376                                     destinationAddress, destinationTTL, fIsMulticast,
01377                                     serverRTPPort, serverRTCPPort,
01378                                     fStreamStates[streamNum].streamToken);
01379     SendingInterfaceAddr = origSendingInterfaceAddr;
01380     ReceivingInterfaceAddr = origReceivingInterfaceAddr;
01381     
01382     AddressString destAddrStr(destinationAddress);
01383     AddressString sourceAddrStr(sourceAddr);
01384     if (fIsMulticast) {
01385       switch (streamingMode) {
01386         case RTP_UDP:
01387           snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
01388                    "RTSP/1.0 200 OK\r\n"
01389                    "CSeq: %s\r\n"
01390                    "%s"
01391                    "Transport: RTP/AVP;multicast;destination=%s;source=%s;port=%d-%d;ttl=%d\r\n"
01392                    "Session: %08X\r\n\r\n",
01393                    ourClientConnection->fCurrentCSeq,
01394                    dateHeader(),
01395                    destAddrStr.val(), sourceAddrStr.val(), ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()), destinationTTL,
01396                    fOurSessionId);
01397           break;
01398         case RTP_TCP:
01399           // multicast streams can't be sent via TCP
01400           ourClientConnection->handleCmd_unsupportedTransport();
01401           break;
01402         case RAW_UDP:
01403           snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
01404                    "RTSP/1.0 200 OK\r\n"
01405                    "CSeq: %s\r\n"
01406                    "%s"
01407                    "Transport: %s;multicast;destination=%s;source=%s;port=%d;ttl=%d\r\n"
01408                    "Session: %08X\r\n\r\n",
01409                    ourClientConnection->fCurrentCSeq,
01410                    dateHeader(),
01411                    streamingModeString, destAddrStr.val(), sourceAddrStr.val(), ntohs(serverRTPPort.num()), destinationTTL,
01412                    fOurSessionId);
01413           break;
01414       }
01415     } else {
01416       switch (streamingMode) {
01417         case RTP_UDP: {
01418           snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
01419                    "RTSP/1.0 200 OK\r\n"
01420                    "CSeq: %s\r\n"
01421                    "%s"
01422                    "Transport: RTP/AVP;unicast;destination=%s;source=%s;client_port=%d-%d;server_port=%d-%d\r\n"
01423                    "Session: %08X\r\n\r\n",
01424                    ourClientConnection->fCurrentCSeq,
01425                    dateHeader(),
01426                    destAddrStr.val(), sourceAddrStr.val(), ntohs(clientRTPPort.num()), ntohs(clientRTCPPort.num()), ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()),
01427                    fOurSessionId);
01428           break;
01429         }
01430         case RTP_TCP: {
01431           snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
01432                    "RTSP/1.0 200 OK\r\n"
01433                    "CSeq: %s\r\n"
01434                    "%s"
01435                    "Transport: RTP/AVP/TCP;unicast;destination=%s;source=%s;interleaved=%d-%d\r\n"
01436                    "Session: %08X\r\n\r\n",
01437                    ourClientConnection->fCurrentCSeq,
01438                    dateHeader(),
01439                    destAddrStr.val(), sourceAddrStr.val(), rtpChannelId, rtcpChannelId,
01440                    fOurSessionId);
01441           break;
01442         }
01443         case RAW_UDP: {
01444           snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
01445                    "RTSP/1.0 200 OK\r\n"
01446                    "CSeq: %s\r\n"
01447                    "%s"
01448                    "Transport: %s;unicast;destination=%s;source=%s;client_port=%d;server_port=%d\r\n"
01449                    "Session: %08X\r\n\r\n",
01450                    ourClientConnection->fCurrentCSeq,
01451                    dateHeader(),
01452                    streamingModeString, destAddrStr.val(), sourceAddrStr.val(), ntohs(clientRTPPort.num()), ntohs(serverRTPPort.num()),
01453                    fOurSessionId);
01454           break;
01455         }
01456       }
01457     }
01458     delete[] streamingModeString;
01459   } while (0);
01460 
01461   delete[] concatenatedStreamName;
01462 }

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

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

01468                                                       {
01469   // This will either be:
01470   // - a non-aggregated operation, if "urlPreSuffix" is the session (stream)
01471   //   name and "urlSuffix" is the subsession (track) name, or
01472   // - an aggregated operation, if "urlSuffix" is the session (stream) name,
01473   //   or "urlPreSuffix" is the session (stream) name, and "urlSuffix" is empty,
01474   //   or "urlPreSuffix" and "urlSuffix" are both nonempty, but when concatenated, (with "/") form the session (stream) name.
01475   // Begin by figuring out which of these it is:
01476   ServerMediaSubsession* subsession;
01477 
01478   noteLiveness();
01479   if (fOurServerMediaSession == NULL) { // There wasn't a previous SETUP!
01480     ourClientConnection->handleCmd_notSupported();
01481     return;
01482   } else if (urlSuffix[0] != '\0' && strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0) {
01483     // Non-aggregated operation.
01484     // Look up the media subsession whose track id is "urlSuffix":
01485     ServerMediaSubsessionIterator iter(*fOurServerMediaSession);
01486     while ((subsession = iter.next()) != NULL) {
01487       if (strcmp(subsession->trackId(), urlSuffix) == 0) break; // success
01488     }
01489     if (subsession == NULL) { // no such track!
01490       ourClientConnection->handleCmd_notFound();
01491       return;
01492     }
01493   } else if (strcmp(fOurServerMediaSession->streamName(), urlSuffix) == 0 ||
01494              (urlSuffix[0] == '\0' && strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0)) {
01495     // Aggregated operation
01496     subsession = NULL;
01497   } else if (urlPreSuffix[0] != '\0' && urlSuffix[0] != '\0') {
01498     // Aggregated operation, if <urlPreSuffix>/<urlSuffix> is the session (stream) name:
01499     unsigned const urlPreSuffixLen = strlen(urlPreSuffix);
01500     if (strncmp(fOurServerMediaSession->streamName(), urlPreSuffix, urlPreSuffixLen) == 0 &&
01501         fOurServerMediaSession->streamName()[urlPreSuffixLen] == '/' &&
01502         strcmp(&(fOurServerMediaSession->streamName())[urlPreSuffixLen+1], urlSuffix) == 0) {
01503       subsession = NULL;
01504     } else {
01505       ourClientConnection->handleCmd_notFound();
01506       return;
01507     }
01508   } else { // the request doesn't match a known stream and/or track at all!
01509     ourClientConnection->handleCmd_notFound();
01510     return;
01511   }
01512 
01513   if (strcmp(cmdName, "TEARDOWN") == 0) {
01514     handleCmd_TEARDOWN(ourClientConnection, subsession);
01515   } else if (strcmp(cmdName, "PLAY") == 0) {
01516     handleCmd_PLAY(ourClientConnection, subsession, fullRequestStr);
01517   } else if (strcmp(cmdName, "PAUSE") == 0) {
01518     handleCmd_PAUSE(ourClientConnection, subsession);
01519   } else if (strcmp(cmdName, "GET_PARAMETER") == 0) {
01520     handleCmd_GET_PARAMETER(ourClientConnection, subsession, fullRequestStr);
01521   } else if (strcmp(cmdName, "SET_PARAMETER") == 0) {
01522     handleCmd_SET_PARAMETER(ourClientConnection, subsession, fullRequestStr);
01523   }
01524 }

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

Definition at line 1527 of file RTSPServer.cpp.

References False, NULL, RTSPServer::RTSPClientConnection::setRTSPResponse(), subsession, and True.

01528                                                         {
01529   unsigned i;
01530   for (i = 0; i < fNumStreamStates; ++i) {
01531     if (subsession == NULL /* means: aggregated operation */
01532         || subsession == fStreamStates[i].subsession) {
01533       if (fStreamStates[i].subsession != NULL) {
01534         fStreamStates[i].subsession->deleteStream(fOurSessionId, fStreamStates[i].streamToken);
01535         fStreamStates[i].subsession = NULL;
01536       }
01537     }
01538   }
01539 
01540   ourClientConnection->setRTSPResponse("200 OK");
01541 
01542   // Optimization: If all subsessions have now been torn down, then we know that we can reclaim our object now.
01543   // (Without this optimization, however, this object would still get reclaimed later, as a result of a 'liveness' timeout.)
01544   Boolean noSubsessionsRemain = True;
01545   for (i = 0; i < fNumStreamStates; ++i) {
01546     if (fStreamStates[i].subsession != NULL) {
01547       noSubsessionsRemain = False;
01548       break;
01549     }
01550   }
01551   if (noSubsessionsRemain) delete this;
01552 }

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

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

01580                                                                                 {
01581   char* rtspURL = fOurServer.rtspURL(fOurServerMediaSession, ourClientConnection->fClientInputSocket);
01582   unsigned rtspURLSize = strlen(rtspURL);
01583 
01584   // Parse the client's "Scale:" header, if any:
01585   float scale;
01586   Boolean sawScaleHeader = parseScaleHeader(fullRequestStr, scale);
01587 
01588   // Try to set the stream's scale factor to this value:
01589   if (subsession == NULL /*aggregate op*/) {
01590     fOurServerMediaSession->testScaleFactor(scale);
01591   } else {
01592     subsession->testScaleFactor(scale);
01593   }
01594 
01595   char buf[100];
01596   char* scaleHeader;
01597   if (!sawScaleHeader) {
01598     buf[0] = '\0'; // Because we didn't see a Scale: header, don't send one back
01599   } else {
01600     sprintf(buf, "Scale: %f\r\n", scale);
01601   }
01602   scaleHeader = strDup(buf);
01603 
01604   // Parse the client's "Range:" header, if any:
01605   float duration = 0.0;
01606   double rangeStart = 0.0, rangeEnd = 0.0;
01607   char* absStart = NULL; char* absEnd = NULL;
01608   Boolean sawRangeHeader = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd, absStart, absEnd);
01609 
01610   if (sawRangeHeader && absStart == NULL/*not seeking by 'absolute' time*/) {
01611     // Use this information, plus the stream's duration (if known), to create our own "Range:" header, for the response:
01612     duration = subsession == NULL /*aggregate op*/
01613       ? fOurServerMediaSession->duration() : subsession->duration();
01614     if (duration < 0.0) {
01615       // We're an aggregate PLAY, but the subsessions have different durations.
01616       // Use the largest of these durations in our header
01617       duration = -duration;
01618     }
01619 
01620     // Make sure that "rangeStart" and "rangeEnd" (from the client's "Range:" header) have sane values
01621     // before we send back our own "Range:" header in our response:
01622     if (rangeStart < 0.0) rangeStart = 0.0;
01623     else if (rangeStart > duration) rangeStart = duration;
01624     if (rangeEnd < 0.0) rangeEnd = 0.0;
01625     else if (rangeEnd > duration) rangeEnd = duration;
01626     if ((scale > 0.0 && rangeStart > rangeEnd && rangeEnd > 0.0) ||
01627         (scale < 0.0 && rangeStart < rangeEnd)) {
01628       // "rangeStart" and "rangeEnd" were the wrong way around; swap them:
01629       double tmp = rangeStart;
01630       rangeStart = rangeEnd;
01631       rangeEnd = tmp;
01632     }
01633   }
01634 
01635   // Create a "RTP-Info:" line.  It will get filled in from each subsession's state:
01636   char const* rtpInfoFmt =
01637     "%s" // "RTP-Info:", plus any preceding rtpInfo items
01638     "%s" // comma separator, if needed
01639     "url=%s/%s"
01640     ";seq=%d"
01641     ";rtptime=%u"
01642     ;
01643   unsigned rtpInfoFmtSize = strlen(rtpInfoFmt);
01644   char* rtpInfo = strDup("RTP-Info: ");
01645   unsigned i, numRTPInfoItems = 0;
01646 
01647   // Do any required seeking/scaling on each subsession, before starting streaming.
01648   // (However, we don't do this if the "PLAY" request was for just a single subsession of a multiple-subsession stream;
01649   //  for such streams, seeking/scaling can be done only with an aggregate "PLAY".)
01650   for (i = 0; i < fNumStreamStates; ++i) {
01651     if (subsession == NULL /* means: aggregated operation */ || fNumStreamStates == 1) {
01652       if (sawScaleHeader) {
01653         if (fStreamStates[i].subsession != NULL) {
01654           fStreamStates[i].subsession->setStreamScale(fOurSessionId, fStreamStates[i].streamToken, scale);
01655         }
01656       }
01657       if (sawRangeHeader) {
01658         if (absStart != NULL) {
01659           // Special case handling for seeking by 'absolute' time:
01660 
01661           if (fStreamStates[i].subsession != NULL) {
01662             fStreamStates[i].subsession->seekStream(fOurSessionId, fStreamStates[i].streamToken, absStart, absEnd);
01663           }
01664         } else {
01665           // Seeking by relative (NPT) time:
01666 
01667           double streamDuration = 0.0; // by default; means: stream until the end of the media
01668           if (rangeEnd > 0.0 && (rangeEnd+0.001) < duration) { // the 0.001 is because we limited the values to 3 decimal places
01669             // We want the stream to end early.  Set the duration we want:
01670             streamDuration = rangeEnd - rangeStart;
01671             if (streamDuration < 0.0) streamDuration = -streamDuration; // should happen only if scale < 0.0
01672           }
01673           if (fStreamStates[i].subsession != NULL) {
01674             u_int64_t numBytes;
01675             fStreamStates[i].subsession->seekStream(fOurSessionId, fStreamStates[i].streamToken,
01676                                                     rangeStart, streamDuration, numBytes);
01677           }
01678         }
01679       } else {
01680         // No "Range:" header was specified in the "PLAY", so we do a 'null' seek (i.e., we don't seek at all):
01681         if (fStreamStates[i].subsession != NULL) {
01682           fStreamStates[i].subsession->nullSeekStream(fOurSessionId, fStreamStates[i].streamToken);
01683         }
01684       }
01685     }
01686   }
01687 
01688   // Create the "Range:" header that we'll send back in our response.
01689   // (Note that we do this after seeking, in case the seeking operation changed the range start time.)
01690   char* rangeHeader;
01691   if (!sawRangeHeader) {
01692     // There wasn't a "Range:" header in the request, so, in our response, begin the range with the current NPT (normal play time):
01693     float curNPT = 0.0;
01694     for (i = 0; i < fNumStreamStates; ++i) {
01695       if (subsession == NULL /* means: aggregated operation */
01696           || subsession == fStreamStates[i].subsession) {
01697         if (fStreamStates[i].subsession == NULL) continue;
01698         float npt = fStreamStates[i].subsession->getCurrentNPT(fStreamStates[i].streamToken);
01699         if (npt > curNPT) curNPT = npt;
01700         // Note: If this is an aggregate "PLAY" on a multi-subsession stream, then it's conceivable that the NPTs of each subsession
01701         // may differ (if there has been a previous seek on just one subsession).  In this (unusual) case, we just return the
01702         // largest NPT; I hope that turns out OK...
01703       }
01704     }
01705 
01706     sprintf(buf, "Range: npt=%.3f-\r\n", curNPT);
01707   } else if (absStart != NULL) {
01708     // We're seeking by 'absolute' time:
01709     if (absEnd == NULL) {
01710       sprintf(buf, "Range: clock=%s-\r\n", absStart);
01711     } else {
01712       sprintf(buf, "Range: clock=%s-%s\r\n", absStart, absEnd);
01713     }
01714     delete[] absStart; delete[] absEnd;
01715   } else {
01716     // We're seeking by relative (NPT) time:
01717     if (rangeEnd == 0.0 && scale >= 0.0) {
01718       sprintf(buf, "Range: npt=%.3f-\r\n", rangeStart);
01719     } else {
01720       sprintf(buf, "Range: npt=%.3f-%.3f\r\n", rangeStart, rangeEnd);
01721     }
01722   }
01723   rangeHeader = strDup(buf);
01724 
01725   // Now, start streaming:
01726   for (i = 0; i < fNumStreamStates; ++i) {
01727     if (subsession == NULL /* means: aggregated operation */
01728         || subsession == fStreamStates[i].subsession) {
01729       unsigned short rtpSeqNum = 0;
01730       unsigned rtpTimestamp = 0;
01731       if (fStreamStates[i].subsession == NULL) continue;
01732       fStreamStates[i].subsession->startStream(fOurSessionId,
01733                                                fStreamStates[i].streamToken,
01734                                                (TaskFunc*)noteClientLiveness, this,
01735                                                rtpSeqNum, rtpTimestamp,
01736                                                RTSPServer::RTSPClientConnection::handleAlternativeRequestByte, ourClientConnection);
01737       const char *urlSuffix = fStreamStates[i].subsession->trackId();
01738       char* prevRTPInfo = rtpInfo;
01739       unsigned rtpInfoSize = rtpInfoFmtSize
01740         + strlen(prevRTPInfo)
01741         + 1
01742         + rtspURLSize + strlen(urlSuffix)
01743         + 5 /*max unsigned short len*/
01744         + 10 /*max unsigned (32-bit) len*/
01745         + 2 /*allows for trailing \r\n at final end of string*/;
01746       rtpInfo = new char[rtpInfoSize];
01747       sprintf(rtpInfo, rtpInfoFmt,
01748               prevRTPInfo,
01749               numRTPInfoItems++ == 0 ? "" : ",",
01750               rtspURL, urlSuffix,
01751               rtpSeqNum,
01752               rtpTimestamp
01753               );
01754       delete[] prevRTPInfo;
01755     }
01756   }
01757   if (numRTPInfoItems == 0) {
01758     rtpInfo[0] = '\0';
01759   } else {
01760     unsigned rtpInfoLen = strlen(rtpInfo);
01761     rtpInfo[rtpInfoLen] = '\r';
01762     rtpInfo[rtpInfoLen+1] = '\n';
01763     rtpInfo[rtpInfoLen+2] = '\0';
01764   }
01765 
01766   // Fill in the response:
01767   snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
01768            "RTSP/1.0 200 OK\r\n"
01769            "CSeq: %s\r\n"
01770            "%s"
01771            "%s"
01772            "%s"
01773            "Session: %08X\r\n"
01774            "%s\r\n",
01775            ourClientConnection->fCurrentCSeq,
01776            dateHeader(),
01777            scaleHeader,
01778            rangeHeader,
01779            fOurSessionId,
01780            rtpInfo);
01781   delete[] rtpInfo; delete[] rangeHeader;
01782   delete[] scaleHeader; delete[] rtspURL;
01783 }

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

Definition at line 1786 of file RTSPServer.cpp.

References NULL, RTSPServer::RTSPClientConnection::setRTSPResponse(), and subsession.

01787                                                      {
01788   for (unsigned i = 0; i < fNumStreamStates; ++i) {
01789     if (subsession == NULL /* means: aggregated operation */
01790         || subsession == fStreamStates[i].subsession) {
01791       if (fStreamStates[i].subsession != NULL) {
01792         fStreamStates[i].subsession->pauseStream(fOurSessionId, fStreamStates[i].streamToken);
01793       }
01794     }
01795   }
01796 
01797   ourClientConnection->setRTSPResponse("200 OK", fOurSessionId);
01798 }

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

Definition at line 1801 of file RTSPServer.cpp.

References LIVEMEDIA_LIBRARY_VERSION_STRING, and RTSPServer::RTSPClientConnection::setRTSPResponse().

01802                                                                                                  {
01803   // By default, we implement "GET_PARAMETER" just as a 'keep alive', and send back a dummy response.
01804   // (If you want to handle "GET_PARAMETER" properly, you can do so by defining a subclass of "RTSPServer"
01805   // and "RTSPServer::RTSPClientSession", and then reimplement this virtual function in your subclass.)
01806   ourClientConnection->setRTSPResponse("200 OK", fOurSessionId, LIVEMEDIA_LIBRARY_VERSION_STRING);
01807 }

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

Definition at line 1810 of file RTSPServer.cpp.

References RTSPServer::RTSPClientConnection::setRTSPResponse().

01811                                                                                                  {
01812   // By default, we implement "SET_PARAMETER" just as a 'keep alive', and send back an empty response.
01813   // (If you want to handle "SET_PARAMETER" properly, you can do so by defining a subclass of "RTSPServer"
01814   // and "RTSPServer::RTSPClientSession", and then reimplement this virtual function in your subclass.)
01815   ourClientConnection->setRTSPResponse("200 OK", fOurSessionId);
01816 }

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

Definition at line 239 of file RTSPServer.hh.

References Medium::envir(), and fOurServer.

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

00239 { return fOurServer.envir(); }

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

Definition at line 1123 of file RTSPServer.cpp.

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

Referenced by ~RTSPClientSession().

01123                                                       {
01124   for (unsigned i = 0; i < fNumStreamStates; ++i) {
01125     if (fStreamStates[i].subsession != NULL) {
01126       fStreamStates[i].subsession->deleteStream(fOurSessionId, fStreamStates[i].streamToken);
01127     }
01128   }
01129   delete[] fStreamStates; fStreamStates = NULL;
01130   fNumStreamStates = 0;
01131 }

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

Definition at line 241 of file RTSPServer.hh.

References fIsMulticast.

00241 { return fIsMulticast; }

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

Definition at line 1828 of file RTSPServer.cpp.

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

Referenced by noteClientLiveness(), and RTSPClientSession().

01828                                                {
01829   if (fOurServer.fReclamationTestSeconds > 0) {
01830     envir().taskScheduler()
01831       .rescheduleDelayedTask(fLivenessCheckTask,
01832                              fOurServer.fReclamationTestSeconds*1000000,
01833                              (TaskFunc*)livenessTimeoutTask, this);
01834   }
01835 }

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

Definition at line 1838 of file RTSPServer.cpp.

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

01838                                                      {
01839 #ifdef DEBUG
01840   char const* streamName
01841     = (clientSession->fOurServerMediaSession == NULL) ? "???" : clientSession->fOurServerMediaSession->streamName();
01842   fprintf(stderr, "RTSP client session (id \"%08X\", stream name \"%s\"): Liveness indication\n",
01843           clientSession->fOurSessionId, streamName);
01844 #endif
01845   clientSession->noteLiveness();
01846 }

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

Definition at line 1849 of file RTSPServer.cpp.

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

Referenced by noteLiveness().

01849                                                       {
01850   // If this gets called, the client session is assumed to have timed out,
01851   // so delete it:
01852 #ifdef DEBUG
01853   char const* streamName
01854     = (clientSession->fOurServerMediaSession == NULL) ? "???" : clientSession->fOurServerMediaSession->streamName();
01855   fprintf(stderr, "RTSP client session (id \"%08X\", stream name \"%s\") has timed out (due to inactivity)\n",
01856           clientSession->fOurSessionId, streamName);
01857 #endif
01858   delete clientSession;
01859 }

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

Definition at line 251 of file RTSPServer.hh.

References fTCPStreamIdCount.

00251 { return fTCPStreamIdCount > 0; }


Friends And Related Function Documentation

friend class RTSPServer [friend]

Definition at line 219 of file RTSPServer.hh.

friend class RTSPClientConnection [friend]

Definition at line 220 of file RTSPServer.hh.


Field Documentation

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

Definition at line 246 of file RTSPServer.hh.

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

u_int32_t RTSPServer::RTSPClientSession::fOurSessionId [protected]

Definition at line 247 of file RTSPServer.hh.

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

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

Definition at line 248 of file RTSPServer.hh.

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

Boolean RTSPServer::RTSPClientSession::fIsMulticast [protected]

Definition at line 249 of file RTSPServer.hh.

Referenced by isMulticast().

Boolean RTSPServer::RTSPClientSession::fStreamAfterSETUP [protected]

Definition at line 249 of file RTSPServer.hh.

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

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

Definition at line 250 of file RTSPServer.hh.

Referenced by usesTCPTransport().

TaskToken RTSPServer::RTSPClientSession::fLivenessCheckTask [protected]

Definition at line 252 of file RTSPServer.hh.

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

unsigned RTSPServer::RTSPClientSession::fNumStreamStates [protected]

Definition at line 253 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 Mon Apr 29 13:32:34 2013 for live by  doxygen 1.5.2