#include <RTSPServer.hh>
Collaboration diagram for RTSPServer::RTSPClientSession:

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) |
| UsageEnvironment & | envir () |
| 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 | |
| RTSPServer & | fOurServer |
| u_int32_t | fOurSessionId |
| ServerMediaSession * | fOurServerMediaSession |
| Boolean | fIsMulticast |
| Boolean | fStreamAfterSETUP |
| unsigned char | fTCPStreamIdCount |
| TaskToken | fLivenessCheckTask |
| unsigned | fNumStreamStates |
| RTSPServer::RTSPClientSession::streamState * | fStreamStates |
Friends | |
| class | RTSPServer |
| class | RTSPClientConnection |
Data Structures | |
| struct | streamState |
Definition at line 214 of file RTSPServer.hh.
| RTSPServer::RTSPClientSession::RTSPClientSession | ( | RTSPServer & | ourServer, | |
| u_int32_t | sessionId | |||
| ) |
Definition at line 1096 of file RTSPServer.cpp.
References noteLiveness().
01097 : fOurServer(ourServer), fOurSessionId(sessionId), fOurServerMediaSession(NULL), fIsMulticast(False), fStreamAfterSETUP(False), 01098 fTCPStreamIdCount(0), fLivenessCheckTask(NULL), fNumStreamStates(0), fStreamStates(NULL) { 01099 noteLiveness(); 01100 }
| 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 }
| 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; }
friend class RTSPServer [friend] |
Definition at line 219 of file RTSPServer.hh.
friend class RTSPClientConnection [friend] |
Definition at line 220 of file RTSPServer.hh.
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().
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 RTSPServer::RTSPClientConnection::handleRequestBytes().
unsigned char RTSPServer::RTSPClientSession::fTCPStreamIdCount [protected] |
Definition at line 252 of file RTSPServer.hh.
Referenced by noteLiveness(), and ~RTSPClientSession().
unsigned RTSPServer::RTSPClientSession::fNumStreamStates [protected] |
struct RTSPServer::RTSPClientSession::streamState * RTSPServer::RTSPClientSession::fStreamStates [protected] |
Referenced by reclaimStreamStates().
1.5.2