liveMedia/RTSPServer.cpp

Go to the documentation of this file.
00001 /**********
00002 This library is free software; you can redistribute it and/or modify it under
00003 the terms of the GNU Lesser General Public License as published by the
00004 Free Software Foundation; either version 2.1 of the License, or (at your
00005 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
00006 
00007 This library is distributed in the hope that it will be useful, but WITHOUT
00008 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00009 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
00010 more details.
00011 
00012 You should have received a copy of the GNU Lesser General Public License
00013 along with this library; if not, write to the Free Software Foundation, Inc.,
00014 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00015 **********/
00016 // "liveMedia"
00017 // Copyright (c) 1996-2008 Live Networks, Inc.  All rights reserved.
00018 // A RTSP server
00019 // Implementation
00020 
00021 #include "RTSPServer.hh"
00022 #include "RTSPCommon.hh"
00023 #include <GroupsockHelper.hh>
00024 
00025 #if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
00026 #else
00027 #include <signal.h>
00028 #define USE_SIGNALS 1
00029 #endif
00030 #include <time.h> // for "strftime()" and "gmtime()"
00031 
00032 #define RTPINFO_INCLUDE_RTPTIME 1
00033 
00035 
00036 RTSPServer*
00037 RTSPServer::createNew(UsageEnvironment& env, Port ourPort,
00038                       UserAuthenticationDatabase* authDatabase,
00039                       unsigned reclamationTestSeconds) {
00040   int ourSocket = -1;
00041 
00042   do {
00043     int ourSocket = setUpOurSocket(env, ourPort);
00044     if (ourSocket == -1) break;
00045 
00046     return new RTSPServer(env, ourSocket, ourPort, authDatabase,
00047                           reclamationTestSeconds);
00048   } while (0);
00049 
00050   if (ourSocket != -1) ::closeSocket(ourSocket);
00051   return NULL;
00052 }
00053 
00054 Boolean RTSPServer::lookupByName(UsageEnvironment& env,
00055                                  char const* name,
00056                                  RTSPServer*& resultServer) {
00057   resultServer = NULL; // unless we succeed
00058 
00059   Medium* medium;
00060   if (!Medium::lookupByName(env, name, medium)) return False;
00061 
00062   if (!medium->isRTSPServer()) {
00063     env.setResultMsg(name, " is not a RTSP server");
00064     return False;
00065   }
00066 
00067   resultServer = (RTSPServer*)medium;
00068   return True;
00069 }
00070 
00071 void RTSPServer::addServerMediaSession(ServerMediaSession* serverMediaSession) {
00072   if (serverMediaSession == NULL) return;
00073 
00074   char const* sessionName = serverMediaSession->streamName();
00075   if (sessionName == NULL) sessionName = "";
00076   ServerMediaSession* existingSession
00077     = (ServerMediaSession*)
00078     (fServerMediaSessions->Add(sessionName,
00079                                (void*)serverMediaSession));
00080   removeServerMediaSession(existingSession); // if any
00081 }
00082 
00083 ServerMediaSession* RTSPServer::lookupServerMediaSession(char const* streamName) {
00084   return (ServerMediaSession*)(fServerMediaSessions->Lookup(streamName));
00085 }
00086 
00087 void RTSPServer::removeServerMediaSession(ServerMediaSession* serverMediaSession) {
00088   if (serverMediaSession == NULL) return;
00089 
00090   fServerMediaSessions->Remove(serverMediaSession->streamName());
00091   if (serverMediaSession->referenceCount() == 0) {
00092     Medium::close(serverMediaSession);
00093   } else {
00094     serverMediaSession->deleteWhenUnreferenced() = True;
00095   }
00096 }
00097 
00098 void RTSPServer::removeServerMediaSession(char const* streamName) {
00099   removeServerMediaSession(lookupServerMediaSession(streamName));
00100 }
00101 
00102 char* RTSPServer::rtspURLPrefix(int clientSocket) const {
00103   struct sockaddr_in ourAddress;
00104   if (clientSocket < 0) {
00105     // Use our default IP address in the URL:
00106     ourAddress.sin_addr.s_addr = ReceivingInterfaceAddr != 0
00107       ? ReceivingInterfaceAddr
00108       : ourIPAddress(envir()); // hack
00109   } else {
00110     SOCKLEN_T namelen = sizeof ourAddress;
00111     getsockname(clientSocket, (struct sockaddr*)&ourAddress, &namelen);
00112   }
00113 
00114   char urlBuffer[100]; // more than big enough for "rtsp://<ip-address>:<port>/"
00115 
00116   portNumBits portNumHostOrder = ntohs(fServerPort.num());
00117   if (portNumHostOrder == 554 /* the default port number */) {
00118     sprintf(urlBuffer, "rtsp://%s/", our_inet_ntoa(ourAddress.sin_addr));
00119   } else {
00120     sprintf(urlBuffer, "rtsp://%s:%hu/",
00121             our_inet_ntoa(ourAddress.sin_addr), portNumHostOrder);
00122   }
00123 
00124   return strDup(urlBuffer);
00125 }
00126 
00127 char* RTSPServer
00128 ::rtspURL(ServerMediaSession const* serverMediaSession, int clientSocket) const {
00129   char* urlPrefix = rtspURLPrefix(clientSocket);
00130   char const* sessionName = serverMediaSession->streamName();
00131 
00132   char* resultURL = new char[strlen(urlPrefix) + strlen(sessionName) + 1];
00133   sprintf(resultURL, "%s%s", urlPrefix, sessionName);
00134 
00135   delete[] urlPrefix;
00136   return resultURL;
00137 }
00138 
00139 #define LISTEN_BACKLOG_SIZE 20
00140 
00141 int RTSPServer::setUpOurSocket(UsageEnvironment& env, Port& ourPort) {
00142   int ourSocket = -1;
00143 
00144   do {
00145     ourSocket = setupStreamSocket(env, ourPort);
00146     if (ourSocket < 0) break;
00147 
00148     // Make sure we have a big send buffer:
00149     if (!increaseSendBufferTo(env, ourSocket, 50*1024)) break;
00150 
00151     // Allow multiple simultaneous connections:
00152     if (listen(ourSocket, LISTEN_BACKLOG_SIZE) < 0) {
00153       env.setResultErrMsg("listen() failed: ");
00154       break;
00155     }
00156 
00157     if (ourPort.num() == 0) {
00158       // bind() will have chosen a port for us; return it also:
00159       if (!getSourcePort(env, ourSocket, ourPort)) break;
00160     }
00161 
00162     return ourSocket;
00163   } while (0);  
00164 
00165   if (ourSocket != -1) ::closeSocket(ourSocket);
00166   return -1;
00167 }
00168 
00169 Boolean RTSPServer
00170 ::specialClientAccessCheck(int /*clientSocket*/, struct sockaddr_in& /*clientAddr*/, char const* /*urlSuffix*/) {
00171   // default implementation
00172   return True;
00173 }
00174 
00175 RTSPServer::RTSPServer(UsageEnvironment& env,
00176                        int ourSocket, Port ourPort,
00177                        UserAuthenticationDatabase* authDatabase,
00178                        unsigned reclamationTestSeconds)
00179   : Medium(env),
00180     fServerSocket(ourSocket), fServerPort(ourPort),
00181     fAuthDB(authDatabase), fReclamationTestSeconds(reclamationTestSeconds),
00182     fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)), 
00183     fSessionIdCounter(0) {
00184 #ifdef USE_SIGNALS
00185   // Ignore the SIGPIPE signal, so that clients on the same host that are killed
00186   // don't also kill us:
00187   signal(SIGPIPE, SIG_IGN);
00188 #endif
00189 
00190   // Arrange to handle connections from others:
00191   env.taskScheduler().turnOnBackgroundReadHandling(fServerSocket,
00192         (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandler,
00193                                                    this);
00194 }
00195 
00196 RTSPServer::~RTSPServer() {
00197   // Turn off background read handling:
00198   envir().taskScheduler().turnOffBackgroundReadHandling(fServerSocket);
00199 
00200   ::closeSocket(fServerSocket);
00201 
00202   // Remove all server media sessions (they'll get deleted when they're finished):
00203   while (1) {
00204     ServerMediaSession* serverMediaSession
00205       = (ServerMediaSession*)fServerMediaSessions->RemoveNext();
00206     if (serverMediaSession == NULL) break;
00207     removeServerMediaSession(serverMediaSession);
00208   }
00209 
00210   // Finally, delete the session table itself:
00211   delete fServerMediaSessions;
00212 }
00213 
00214 Boolean RTSPServer::isRTSPServer() const {
00215   return True;
00216 }
00217 
00218 void RTSPServer::incomingConnectionHandler(void* instance, int /*mask*/) {
00219   RTSPServer* server = (RTSPServer*)instance;
00220   server->incomingConnectionHandler1();
00221 }
00222 
00223 void RTSPServer::incomingConnectionHandler1() {
00224   struct sockaddr_in clientAddr;
00225   SOCKLEN_T clientAddrLen = sizeof clientAddr;
00226   int clientSocket = accept(fServerSocket, (struct sockaddr*)&clientAddr,
00227                             &clientAddrLen);
00228   if (clientSocket < 0) {
00229     int err = envir().getErrno();
00230     if (err != EWOULDBLOCK) {
00231         envir().setResultErrMsg("accept() failed: ");
00232     }
00233     return;
00234   }
00235   makeSocketNonBlocking(clientSocket);
00236   increaseSendBufferTo(envir(), clientSocket, 50*1024);
00237 
00238 #if defined(DEBUG) || defined(DEBUG_CONNECTIONS)
00239   fprintf(stderr, "accept()ed connection from %s\n", our_inet_ntoa(clientAddr.sin_addr));
00240 #endif
00241 
00242   // Create a new object for this RTSP session:
00243   new RTSPClientSession(*this, ++fSessionIdCounter,
00244                         clientSocket, clientAddr);
00245 }
00246 
00247 
00249 
00250 RTSPServer::RTSPClientSession
00251 ::RTSPClientSession(RTSPServer& ourServer, unsigned sessionId,
00252               int clientSocket, struct sockaddr_in clientAddr)
00253   : fOurServer(ourServer), fOurSessionId(sessionId),
00254     fOurServerMediaSession(NULL),
00255     fClientSocket(clientSocket), fClientAddr(clientAddr),
00256     fLivenessCheckTask(NULL),
00257     fIsMulticast(False), fSessionIsActive(True), fStreamAfterSETUP(False),
00258     fTCPStreamIdCount(0), fNumStreamStates(0), fStreamStates(NULL) {
00259   // Arrange to handle incoming requests:
00260   resetRequestBuffer();
00261   envir().taskScheduler().turnOnBackgroundReadHandling(fClientSocket,
00262      (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this);
00263   noteLiveness();
00264 }
00265 
00266 RTSPServer::RTSPClientSession::~RTSPClientSession() {
00267   // Turn off any liveness checking:
00268   envir().taskScheduler().unscheduleDelayedTask(fLivenessCheckTask);
00269 
00270   // Turn off background read handling:
00271   envir().taskScheduler().turnOffBackgroundReadHandling(fClientSocket);
00272 
00273   ::closeSocket(fClientSocket);
00274 
00275   reclaimStreamStates();
00276 
00277   if (fOurServerMediaSession != NULL) {
00278     fOurServerMediaSession->decrementReferenceCount();
00279     if (fOurServerMediaSession->referenceCount() == 0
00280         && fOurServerMediaSession->deleteWhenUnreferenced()) {
00281       fOurServer.removeServerMediaSession(fOurServerMediaSession);
00282     }
00283   }
00284 }
00285 
00286 void RTSPServer::RTSPClientSession::reclaimStreamStates() {
00287   for (unsigned i = 0; i < fNumStreamStates; ++i) {
00288     if (fStreamStates[i].subsession != NULL) {
00289       fStreamStates[i].subsession->deleteStream(fOurSessionId,
00290                                                 fStreamStates[i].streamToken);
00291     }
00292   }
00293   delete[] fStreamStates; fStreamStates = NULL;
00294   fNumStreamStates = 0;
00295 }
00296 
00297 void RTSPServer::RTSPClientSession::resetRequestBuffer() {
00298   fRequestBytesAlreadySeen = 0;
00299   fRequestBufferBytesLeft = sizeof fRequestBuffer;
00300   fLastCRLF = &fRequestBuffer[-3]; // hack
00301 }
00302 
00303 void RTSPServer::RTSPClientSession
00304 ::incomingRequestHandler(void* instance, int /*mask*/) {
00305   RTSPClientSession* session = (RTSPClientSession*)instance;
00306   session->incomingRequestHandler1();
00307 }
00308 
00309 void RTSPServer::RTSPClientSession::incomingRequestHandler1() {
00310   noteLiveness();
00311 
00312   struct sockaddr_in dummy; // 'from' address, meaningless in this case
00313   Boolean endOfMsg = False;
00314   unsigned char* ptr = &fRequestBuffer[fRequestBytesAlreadySeen];
00315   
00316   int bytesRead = readSocket(envir(), fClientSocket,
00317                              ptr, fRequestBufferBytesLeft, dummy);
00318   if (bytesRead <= 0 || (unsigned)bytesRead >= fRequestBufferBytesLeft) {
00319     // Either the client socket has died, or the request was too big for us.
00320     // Terminate this connection:
00321 #ifdef DEBUG
00322     fprintf(stderr, "RTSPClientSession[%p]::incomingRequestHandler1() read %d bytes (of %d); terminating connection!\n", this, bytesRead, fRequestBufferBytesLeft);
00323 #endif
00324     delete this;
00325     return;
00326   }
00327 #ifdef DEBUG
00328   ptr[bytesRead] = '\0';
00329   fprintf(stderr, "RTSPClientSession[%p]::incomingRequestHandler1() read %d bytes:%s\n", this, bytesRead, ptr);
00330 #endif
00331 
00332   // Look for the end of the message: <CR><LF><CR><LF>
00333   unsigned char *tmpPtr = ptr;
00334   if (fRequestBytesAlreadySeen > 0) --tmpPtr;
00335       // in case the last read ended with a <CR>
00336   while (tmpPtr < &ptr[bytesRead-1]) {
00337     if (*tmpPtr == '\r' && *(tmpPtr+1) == '\n') {
00338       if (tmpPtr - fLastCRLF == 2) { // This is it:
00339         endOfMsg = 1;
00340         break;
00341       }
00342       fLastCRLF = tmpPtr;
00343     }
00344     ++tmpPtr;
00345   }
00346   
00347   fRequestBufferBytesLeft -= bytesRead;
00348   fRequestBytesAlreadySeen += bytesRead;
00349 
00350   if (!endOfMsg) return; // subsequent reads will be needed to complete the request
00351 
00352   // Parse the request string into command name and 'CSeq',
00353   // then handle the command:
00354   fRequestBuffer[fRequestBytesAlreadySeen] = '\0';
00355   char cmdName[RTSP_PARAM_STRING_MAX];
00356   char urlPreSuffix[RTSP_PARAM_STRING_MAX];
00357   char urlSuffix[RTSP_PARAM_STRING_MAX];
00358   char cseq[RTSP_PARAM_STRING_MAX];
00359   if (!parseRTSPRequestString((char*)fRequestBuffer, fRequestBytesAlreadySeen,
00360                               cmdName, sizeof cmdName,
00361                               urlPreSuffix, sizeof urlPreSuffix,
00362                               urlSuffix, sizeof urlSuffix,
00363                               cseq, sizeof cseq)) {
00364 #ifdef DEBUG
00365     fprintf(stderr, "parseRTSPRequestString() failed!\n");
00366 #endif
00367     handleCmd_bad(cseq);
00368   } else {
00369 #ifdef DEBUG
00370     fprintf(stderr, "parseRTSPRequestString() returned cmdName \"%s\", urlPreSuffix \"%s\", urlSuffix \"%s\"\n", cmdName, urlPreSuffix, urlSuffix);
00371 #endif
00372     if (strcmp(cmdName, "OPTIONS") == 0) {
00373       handleCmd_OPTIONS(cseq);
00374     } else if (strcmp(cmdName, "DESCRIBE") == 0) {
00375       handleCmd_DESCRIBE(cseq, urlSuffix, (char const*)fRequestBuffer);
00376     } else if (strcmp(cmdName, "SETUP") == 0) {
00377       handleCmd_SETUP(cseq, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
00378     } else if (strcmp(cmdName, "TEARDOWN") == 0
00379                || strcmp(cmdName, "PLAY") == 0
00380                || strcmp(cmdName, "PAUSE") == 0
00381                || strcmp(cmdName, "GET_PARAMETER") == 0) {
00382       handleCmd_withinSession(cmdName, urlPreSuffix, urlSuffix, cseq,
00383                               (char const*)fRequestBuffer);
00384     } else {
00385       handleCmd_notSupported(cseq);
00386     }
00387   }
00388     
00389 #ifdef DEBUG
00390   fprintf(stderr, "sending response: %s", fResponseBuffer);
00391 #endif
00392   send(fClientSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);
00393 
00394   if (strcmp(cmdName, "SETUP") == 0 && fStreamAfterSETUP) {
00395     // The client has asked for streaming to commence now, rather than after a
00396     // subsequent "PLAY" command.  So, simulate the effect of a "PLAY" command:
00397     handleCmd_withinSession("PLAY", urlPreSuffix, urlSuffix, cseq,
00398                             (char const*)fRequestBuffer);
00399   }
00400 
00401   resetRequestBuffer(); // to prepare for any subsequent request
00402   if (!fSessionIsActive) delete this;
00403 }
00404 
00405 // Handler routines for specific RTSP commands:
00406 
00407 // Generate a "Date:" header for use in a RTSP response:
00408 static char const* dateHeader() {
00409   static char buf[200];
00410 #if !defined(_WIN32_WCE)
00411   time_t tt = time(NULL);
00412   strftime(buf, sizeof buf, "Date: %a, %b %d %Y %H:%M:%S GMT\r\n", gmtime(&tt));
00413 #else
00414   // WinCE apparently doesn't have "time()", "strftime()", or "gmtime()",
00415   // so generate the "Date:" header a different, WinCE-specific way.
00416   // (Thanks to Pierre l'Hussiez for this code)
00417   SYSTEMTIME SystemTime;
00418   GetSystemTime(&SystemTime);
00419   WCHAR dateFormat[] = L"ddd, MMM dd yyyy";
00420   WCHAR timeFormat[] = L"HH:mm:ss GMT\r\n";
00421   WCHAR inBuf[200];
00422   DWORD locale = LOCALE_NEUTRAL;
00423   
00424   int ret = GetDateFormat(locale, 0, &SystemTime,
00425                           (LPTSTR)dateFormat, (LPTSTR)inBuf, sizeof inBuf);
00426   inBuf[ret - 1] = ' ';
00427   ret = GetTimeFormat(locale, 0, &SystemTime,
00428                       (LPTSTR)timeFormat,
00429                       (LPTSTR)inBuf + ret, (sizeof inBuf) - ret);
00430   wcstombs(buf, inBuf, wcslen(inBuf));
00431 #endif
00432   return buf;
00433 }
00434 
00435 static char const* allowedCommandNames
00436   = "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE";
00437 
00438 void RTSPServer::RTSPClientSession::handleCmd_bad(char const* /*cseq*/) {
00439   // Don't do anything with "cseq", because it might be nonsense
00440   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00441            "RTSP/1.0 400 Bad Request\r\n%sAllow: %s\r\n\r\n",
00442            dateHeader(), allowedCommandNames);
00443 }
00444 
00445 void RTSPServer::RTSPClientSession::handleCmd_notSupported(char const* cseq) {
00446   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00447            "RTSP/1.0 405 Method Not Allowed\r\nCSeq: %s\r\n%sAllow: %s\r\n\r\n",
00448            cseq, dateHeader(), allowedCommandNames);
00449 }
00450 
00451 void RTSPServer::RTSPClientSession::handleCmd_notFound(char const* cseq) {
00452   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00453            "RTSP/1.0 404 Stream Not Found\r\nCSeq: %s\r\n%s\r\n",
00454            cseq, dateHeader());
00455   fSessionIsActive = False; // triggers deletion of ourself after responding
00456 }
00457 
00458 void RTSPServer::RTSPClientSession::handleCmd_unsupportedTransport(char const* cseq) {
00459   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00460            "RTSP/1.0 461 Unsupported Transport\r\nCSeq: %s\r\n%s\r\n",
00461            cseq, dateHeader());
00462   fSessionIsActive = False; // triggers deletion of ourself after responding
00463 }
00464 
00465 void RTSPServer::RTSPClientSession::handleCmd_OPTIONS(char const* cseq) {
00466   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00467            "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sPublic: %s\r\n\r\n",
00468            cseq, dateHeader(), allowedCommandNames);
00469 }
00470 
00471 void RTSPServer::RTSPClientSession
00472 ::handleCmd_DESCRIBE(char const* cseq, char const* urlSuffix,
00473                      char const* fullRequestStr) {
00474   char* sdpDescription = NULL;
00475   char* rtspURL = NULL;
00476   do {
00477       if (!authenticationOK("DESCRIBE", cseq, urlSuffix, fullRequestStr))
00478           break;
00479 
00480     // We should really check that the request contains an "Accept:" #####
00481     // for "application/sdp", because that's what we're sending back #####
00482 
00483     // Begin by looking up the "ServerMediaSession" object for the
00484     // specified "urlSuffix":
00485     ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlSuffix);
00486     if (session == NULL) {
00487       handleCmd_notFound(cseq);
00488       break;
00489     }
00490 
00491     // Then, assemble a SDP description for this session:
00492     sdpDescription = session->generateSDPDescription();
00493     if (sdpDescription == NULL) {
00494       // This usually means that a file name that was specified for a
00495       // "ServerMediaSubsession" does not exist.
00496       snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00497                "RTSP/1.0 404 File Not Found, Or In Incorrect Format\r\n"
00498                "CSeq: %s\r\n"
00499                "%s\r\n",
00500                cseq,
00501                dateHeader());
00502      break;
00503     }
00504     unsigned sdpDescriptionSize = strlen(sdpDescription);
00505 
00506     // Also, generate our RTSP URL, for the "Content-Base:" header
00507     // (which is necessary to ensure that the correct URL gets used in
00508     // subsequent "SETUP" requests).
00509     rtspURL = fOurServer.rtspURL(session, fClientSocket);
00510 
00511     snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00512              "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
00513              "%s"
00514              "Content-Base: %s/\r\n"
00515              "Content-Type: application/sdp\r\n"
00516              "Content-Length: %d\r\n\r\n"
00517              "%s",
00518              cseq,
00519              dateHeader(),
00520              rtspURL,
00521              sdpDescriptionSize,
00522              sdpDescription);
00523   } while (0);
00524 
00525   delete[] sdpDescription;
00526   delete[] rtspURL;
00527 }
00528 
00529 typedef enum StreamingMode {
00530   RTP_UDP,
00531   RTP_TCP,
00532   RAW_UDP
00533 } StreamingMode;
00534 
00535 static void parseTransportHeader(char const* buf,
00536                                  StreamingMode& streamingMode,
00537                                  char*& streamingModeString,
00538                                  char*& destinationAddressStr,
00539                                  u_int8_t& destinationTTL,
00540                                  portNumBits& clientRTPPortNum, // if UDP
00541                                  portNumBits& clientRTCPPortNum, // if UDP
00542                                  unsigned char& rtpChannelId, // if TCP
00543                                  unsigned char& rtcpChannelId // if TCP
00544                                  ) {
00545   // Initialize the result parameters to default values:
00546   streamingMode = RTP_UDP;
00547   streamingModeString = NULL;
00548   destinationAddressStr = NULL;
00549   destinationTTL = 255;
00550   clientRTPPortNum = 0;
00551   clientRTCPPortNum = 1; 
00552   rtpChannelId = rtcpChannelId = 0xFF;
00553 
00554   portNumBits p1, p2;
00555   unsigned ttl, rtpCid, rtcpCid;
00556 
00557   // First, find "Transport:"
00558   while (1) {
00559     if (*buf == '\0') return; // not found
00560     if (_strncasecmp(buf, "Transport: ", 11) == 0) break;
00561     ++buf;
00562   }
00563 
00564   // Then, run through each of the fields, looking for ones we handle:
00565   char const* fields = buf + 11;
00566   char* field = strDupSize(fields);
00567   while (sscanf(fields, "%[^;]", field) == 1) {
00568     if (strcmp(field, "RTP/AVP/TCP") == 0) {
00569       streamingMode = RTP_TCP;
00570     } else if (strcmp(field, "RAW/RAW/UDP") == 0 ||
00571                strcmp(field, "MP2T/H2221/UDP") == 0) {
00572       streamingMode = RAW_UDP;
00573       streamingModeString = strDup(field);
00574     } else if (_strncasecmp(field, "destination=", 12) == 0) {
00575       delete[] destinationAddressStr;
00576       destinationAddressStr = strDup(field+12);
00577     } else if (sscanf(field, "ttl%u", &ttl) == 1) {
00578       destinationTTL = (u_int8_t)ttl;
00579     } else if (sscanf(field, "client_port=%hu-%hu", &p1, &p2) == 2) {
00580         clientRTPPortNum = p1;
00581         clientRTCPPortNum = p2;
00582     } else if (sscanf(field, "client_port=%hu", &p1) == 1) {
00583         clientRTPPortNum = p1;
00584         clientRTCPPortNum = streamingMode == RAW_UDP ? 0 : p1 + 1;
00585     } else if (sscanf(field, "interleaved=%u-%u", &rtpCid, &rtcpCid) == 2) {
00586       rtpChannelId = (unsigned char)rtpCid;
00587       rtcpChannelId = (unsigned char)rtcpCid;
00588     }
00589 
00590     fields += strlen(field);
00591     while (*fields == ';') ++fields; // skip over separating ';' chars
00592     if (*fields == '\0' || *fields == '\r' || *fields == '\n') break;
00593   }
00594   delete[] field;
00595 }
00596 
00597 static Boolean parsePlayNowHeader(char const* buf) {
00598   // Find "x-playNow:" header, if present
00599   while (1) {
00600     if (*buf == '\0') return False; // not found
00601     if (_strncasecmp(buf, "x-playNow:", 10) == 0) break;
00602     ++buf;
00603   }
00604 
00605   return True;
00606 }
00607 
00608 void RTSPServer::RTSPClientSession
00609 ::handleCmd_SETUP(char const* cseq,
00610                   char const* urlPreSuffix, char const* urlSuffix,
00611                   char const* fullRequestStr) {
00612   // "urlPreSuffix" should be the session (stream) name, and
00613   // "urlSuffix" should be the subsession (track) name.
00614   char const* streamName = urlPreSuffix;
00615   char const* trackId = urlSuffix;
00616 
00617   // Check whether we have existing session state, and, if so, whether it's
00618   // for the session that's named in "streamName".  (Note that we don't
00619   // support more than one concurrent session on the same client connection.) #####
00620   if (fOurServerMediaSession != NULL
00621       && strcmp(streamName, fOurServerMediaSession->streamName()) != 0) {
00622     fOurServerMediaSession = NULL;
00623   }
00624   if (fOurServerMediaSession == NULL) {
00625     // Set up this session's state.
00626 
00627     // Look up the "ServerMediaSession" object for the specified stream:
00628     if (streamName[0] != '\0' ||
00629         fOurServer.lookupServerMediaSession("") != NULL) { // normal case
00630     } else { // weird case: there was no track id in the URL
00631       streamName = urlSuffix;
00632       trackId = NULL;
00633     }
00634     fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName);
00635     if (fOurServerMediaSession == NULL) {
00636       handleCmd_notFound(cseq);
00637       return;
00638     }
00639 
00640     fOurServerMediaSession->incrementReferenceCount();
00641 
00642     // Set up our array of states for this session's subsessions (tracks):
00643     reclaimStreamStates();
00644     ServerMediaSubsessionIterator iter(*fOurServerMediaSession);
00645     for (fNumStreamStates = 0; iter.next() != NULL; ++fNumStreamStates) {}
00646     fStreamStates = new struct streamState[fNumStreamStates];
00647     iter.reset();
00648     ServerMediaSubsession* subsession;
00649     for (unsigned i = 0; i < fNumStreamStates; ++i) {
00650       subsession = iter.next();
00651       fStreamStates[i].subsession = subsession;
00652       fStreamStates[i].streamToken = NULL; // for now; reset by SETUP later
00653     }
00654   }
00655 
00656   // Look up information for the specified subsession (track):
00657   ServerMediaSubsession* subsession = NULL;
00658   unsigned streamNum;
00659   if (trackId != NULL && trackId[0] != '\0') { // normal case
00660     for (streamNum = 0; streamNum < fNumStreamStates; ++streamNum) {
00661       subsession = fStreamStates[streamNum].subsession;
00662       if (subsession != NULL && strcmp(trackId, subsession->trackId()) == 0) break;
00663     }
00664     if (streamNum >= fNumStreamStates) {
00665       // The specified track id doesn't exist, so this request fails:
00666       handleCmd_notFound(cseq);
00667       return;
00668     }
00669   } else {
00670     // Weird case: there was no track id in the URL.
00671     // This works only if we have only one subsession:
00672     if (fNumStreamStates != 1) {
00673       handleCmd_bad(cseq);
00674       return;
00675     }
00676     streamNum = 0;
00677     subsession = fStreamStates[streamNum].subsession;
00678   }
00679   // ASSERT: subsession != NULL
00680 
00681   // Look for a "Transport:" header in the request string,
00682   // to extract client parameters:
00683   StreamingMode streamingMode;
00684   char* streamingModeString = NULL; // set when RAW_UDP streaming is specified
00685   char* clientsDestinationAddressStr;
00686   u_int8_t clientsDestinationTTL;
00687   portNumBits clientRTPPortNum, clientRTCPPortNum;
00688   unsigned char rtpChannelId, rtcpChannelId;
00689   parseTransportHeader(fullRequestStr, streamingMode, streamingModeString,
00690                        clientsDestinationAddressStr, clientsDestinationTTL,
00691                        clientRTPPortNum, clientRTCPPortNum,
00692                        rtpChannelId, rtcpChannelId);
00693   if (streamingMode == RTP_TCP && rtpChannelId == 0xFF) {
00694     // TCP streaming was requested, but with no "interleaving=" fields.
00695     // (QuickTime Player sometimes does this.)  Set the RTP and RTCP channel ids to
00696     // proper values:
00697     rtpChannelId = fTCPStreamIdCount; rtcpChannelId = fTCPStreamIdCount+1;
00698   }
00699   fTCPStreamIdCount += 2;
00700 
00701   Port clientRTPPort(clientRTPPortNum);
00702   Port clientRTCPPort(clientRTCPPortNum);
00703 
00704   // Next, check whether a "Range:" header is present in the request.
00705   // This isn't legal, but some clients do this to combine "SETUP" and "PLAY":
00706   float rangeStart = 0.0, rangeEnd = 0.0;
00707   fStreamAfterSETUP = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd) ||
00708                       parsePlayNowHeader(fullRequestStr);
00709 
00710   // Then, get server parameters from the 'subsession':
00711   int tcpSocketNum = streamingMode == RTP_TCP ? fClientSocket : -1;
00712   netAddressBits destinationAddress = 0;
00713   u_int8_t destinationTTL = 255;
00714 #ifdef RTSP_ALLOW_CLIENT_DESTINATION_SETTING
00715   if (clientsDestinationAddressStr != NULL) {
00716     // Use the client-provided "destination" address.
00717     // Note: This potentially allows the server to be used in denial-of-service
00718     // attacks, so don't enable this code unless you're sure that clients are
00719     // trusted.
00720     destinationAddress = our_inet_addr(clientsDestinationAddressStr);
00721   }
00722   // Also use the client-provided TTL.
00723   destinationTTL = clientsDestinationTTL;
00724 #endif
00725   delete[] clientsDestinationAddressStr;
00726   Port serverRTPPort(0);
00727   Port serverRTCPPort(0);
00728   subsession->getStreamParameters(fOurSessionId, fClientAddr.sin_addr.s_addr,
00729                                   clientRTPPort, clientRTCPPort,
00730                                   tcpSocketNum, rtpChannelId, rtcpChannelId,
00731                                   destinationAddress, destinationTTL, fIsMulticast,
00732                                   serverRTPPort, serverRTCPPort,
00733                                   fStreamStates[streamNum].streamToken);
00734   struct in_addr destinationAddr; destinationAddr.s_addr = destinationAddress;
00735   char* destAddrStr = strDup(our_inet_ntoa(destinationAddr));
00736   struct sockaddr_in sourceAddr; SOCKLEN_T namelen = sizeof sourceAddr;
00737   getsockname(fClientSocket, (struct sockaddr*)&sourceAddr, &namelen);
00738   char* sourceAddrStr = strDup(our_inet_ntoa(sourceAddr.sin_addr));
00739   if (fIsMulticast) {
00740     switch (streamingMode) {
00741     case RTP_UDP:
00742         snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00743                  "RTSP/1.0 200 OK\r\n"
00744                  "CSeq: %s\r\n"
00745                  "%s"
00746                  "Transport: RTP/AVP;multicast;destination=%s;source=%s;port=%d-%d;ttl=%d\r\n"
00747                  "Session: %d\r\n\r\n",
00748                  cseq,
00749                  dateHeader(),
00750                  destAddrStr, sourceAddrStr, ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()), destinationTTL,
00751                  fOurSessionId);
00752         break;
00753     case RTP_TCP:
00754         // multicast streams can't be sent via TCP
00755         handleCmd_unsupportedTransport(cseq);
00756         break;
00757     case RAW_UDP:
00758         snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00759                  "RTSP/1.0 200 OK\r\n"
00760                  "CSeq: %s\r\n"
00761                  "%s"
00762                  "Transport: %s;multicast;destination=%s;source=%s;port=%d;ttl=%d\r\n"
00763                  "Session: %d\r\n\r\n",
00764                  cseq,
00765                  dateHeader(),
00766                  streamingModeString, destAddrStr, sourceAddrStr, ntohs(serverRTPPort.num()), destinationTTL,
00767                  fOurSessionId);
00768         break;
00769     }
00770   } else {
00771     switch (streamingMode) {
00772     case RTP_UDP: {
00773       snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00774                "RTSP/1.0 200 OK\r\n"
00775                "CSeq: %s\r\n"
00776                "%s"
00777                "Transport: RTP/AVP;unicast;destination=%s;source=%s;client_port=%d-%d;server_port=%d-%d\r\n"
00778                "Session: %d\r\n\r\n",
00779                cseq,
00780                dateHeader(),
00781                destAddrStr, sourceAddrStr, ntohs(clientRTPPort.num()), ntohs(clientRTCPPort.num()), ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()),
00782                fOurSessionId);
00783       break;
00784     }
00785     case RTP_TCP: {
00786       snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00787                "RTSP/1.0 200 OK\r\n"
00788                "CSeq: %s\r\n"
00789                "%s"
00790                "Transport: RTP/AVP/TCP;unicast;destination=%s;source=%s;interleaved=%d-%d\r\n"
00791                "Session: %d\r\n\r\n",
00792                cseq,
00793                dateHeader(),
00794                destAddrStr, sourceAddrStr, rtpChannelId, rtcpChannelId,
00795                fOurSessionId);
00796       break;
00797     }
00798     case RAW_UDP: {
00799       snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00800                "RTSP/1.0 200 OK\r\n"
00801                "CSeq: %s\r\n"
00802                "%s"
00803                "Transport: %s;unicast;destination=%s;source=%s;client_port=%d;server_port=%d\r\n"
00804                "Session: %d\r\n\r\n",
00805                cseq,
00806                dateHeader(),
00807                streamingModeString, destAddrStr, sourceAddrStr, ntohs(clientRTPPort.num()), ntohs(serverRTPPort.num()),
00808                fOurSessionId);
00809       break;
00810     }
00811     }
00812   }
00813   delete[] destAddrStr; delete[] sourceAddrStr; delete[] streamingModeString;
00814 }
00815 
00816 void RTSPServer::RTSPClientSession
00817 ::handleCmd_withinSession(char const* cmdName,
00818                           char const* urlPreSuffix, char const* urlSuffix,
00819                           char const* cseq, char const* fullRequestStr) {
00820   // This will either be:
00821   // - a non-aggregated operation, if "urlPreSuffix" is the session (stream)
00822   //   name and "urlSuffix" is the subsession (track) name, or
00823   // - a aggregated operation, if "urlSuffix" is the session (stream) name,
00824   //   or "urlPreSuffix" is the session (stream) name, and "urlSuffix"
00825   //   is empty.
00826   // First, figure out which of these it is:
00827   if (fOurServerMediaSession == NULL) { // There wasn't a previous SETUP!
00828     handleCmd_notSupported(cseq);
00829     return;
00830   }
00831   ServerMediaSubsession* subsession;
00832   if (urlSuffix[0] != '\0' &&
00833       strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0) {
00834     // Non-aggregated operation.
00835     // Look up the media subsession whose track id is "urlSuffix":
00836     ServerMediaSubsessionIterator iter(*fOurServerMediaSession);
00837     while ((subsession = iter.next()) != NULL) {
00838       if (strcmp(subsession->trackId(), urlSuffix) == 0) break; // success
00839     }
00840     if (subsession == NULL) { // no such track!
00841       handleCmd_notFound(cseq);
00842       return;
00843     }
00844   } else if (strcmp(fOurServerMediaSession->streamName(), urlSuffix) == 0 ||
00845              strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0) {
00846     // Aggregated operation
00847     subsession = NULL;
00848   } else { // the request doesn't match a known stream and/or track at all!
00849     handleCmd_notFound(cseq);
00850     return;
00851   }
00852 
00853   if (strcmp(cmdName, "TEARDOWN") == 0) {
00854     handleCmd_TEARDOWN(subsession, cseq);
00855   } else if (strcmp(cmdName, "PLAY") == 0) {
00856     handleCmd_PLAY(subsession, cseq, fullRequestStr);
00857   } else if (strcmp(cmdName, "PAUSE") == 0) {
00858     handleCmd_PAUSE(subsession, cseq);
00859   } else if (strcmp(cmdName, "GET_PARAMETER") == 0) {
00860     handleCmd_GET_PARAMETER(subsession, cseq, fullRequestStr);
00861   }
00862 }
00863 
00864 void RTSPServer::RTSPClientSession
00865 ::handleCmd_TEARDOWN(ServerMediaSubsession* /*subsession*/, char const* cseq) {
00866   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00867            "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%s\r\n",
00868            cseq, dateHeader());
00869   fSessionIsActive = False; // triggers deletion of ourself after responding
00870 }
00871 
00872 static Boolean parseScaleHeader(char const* buf, float& scale) {
00873   // Initialize the result parameter to a default value:
00874   scale = 1.0;
00875 
00876   // First, find "Scale:"
00877   while (1) {
00878     if (*buf == '\0') return False; // not found
00879     if (_strncasecmp(buf, "Scale: ", 7) == 0) break;
00880     ++buf;
00881   }
00882 
00883   // Then, run through each of the fields, looking for ones we handle:
00884   char const* fields = buf + 7;
00885   while (*fields == ' ') ++fields;
00886   float sc;
00887   if (sscanf(fields, "%f", &sc) == 1) {
00888     scale = sc;
00889   } else {
00890     return False; // The header is malformed
00891   }
00892 
00893   return True;
00894 }
00895 
00896 void RTSPServer::RTSPClientSession
00897   ::handleCmd_PLAY(ServerMediaSubsession* subsession, char const* cseq,
00898                    char const* fullRequestStr) {
00899   char* rtspURL = fOurServer.rtspURL(fOurServerMediaSession, fClientSocket);
00900   unsigned rtspURLSize = strlen(rtspURL);
00901 
00903   float scale;
00904   Boolean sawScaleHeader = parseScaleHeader(fullRequestStr, scale);
00905 
00906   // Try to set the stream's scale factor to this value:
00907   if (subsession == NULL /*aggregate op*/) {
00908     fOurServerMediaSession->testScaleFactor(scale);
00909   } else {
00910     subsession->testScaleFactor(scale);
00911   }
00912 
00913   char buf[100];
00914   char* scaleHeader;
00915   if (!sawScaleHeader) {
00916     buf[0] = '\0'; // Because we didn't see a Scale: header, don't send one back
00917   } else {
00918     sprintf(buf, "Scale: %f\r\n", scale);
00919   }
00920   scaleHeader = strDup(buf);
00921 
00923   float rangeStart = 0.0, rangeEnd = 0.0;
00924   Boolean sawRangeHeader = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd);
00925 
00926   // Use this information, plus the stream's duration (if known), to create
00927   // our own "Range:" header, for the response:
00928   float duration = subsession == NULL /*aggregate op*/
00929     ? fOurServerMediaSession->duration() : subsession->duration();
00930   if (duration < 0.0) {
00931     // We're an aggregate PLAY, but the subsessions have different durations.
00932     // Use the largest of these durations in our header
00933     duration = -duration;
00934   }
00935 
00936   if (rangeEnd < 0.0 || rangeEnd > duration) rangeEnd = duration;
00937   if (rangeStart < 0.0) {
00938     rangeStart = 0.0;
00939   } else if (rangeEnd > 0.0 && scale > 0.0 && rangeStart > rangeEnd) {
00940     rangeStart = rangeEnd;
00941   }
00942 
00943   char* rangeHeader;
00944   if (!sawRangeHeader) {
00945     buf[0] = '\0'; // Because we didn't see a Range: header, don't send one back
00946   } else if (rangeEnd == 0.0 && scale >= 0.0) {
00947     sprintf(buf, "Range: npt=%.3f-\r\n", rangeStart);
00948   } else {
00949     sprintf(buf, "Range: npt=%.3f-%.3f\r\n", rangeStart, rangeEnd);
00950   }
00951   rangeHeader = strDup(buf);
00952 
00953   // Create a "RTP-Info:" line.  It will get filled in from each subsession's state:
00954   char const* rtpInfoFmt =
00955     "%s" // "RTP-Info:", plus any preceding rtpInfo items
00956     "%s" // comma separator, if needed 
00957     "url=%s/%s"
00958     ";seq=%d"
00959 #ifdef RTPINFO_INCLUDE_RTPTIME
00960     ";rtptime=%u"
00961 #endif
00962     ;
00963   unsigned rtpInfoFmtSize = strlen(rtpInfoFmt);
00964   char* rtpInfo = strDup("RTP-Info: ");
00965   unsigned i, numRTPInfoItems = 0;
00966 
00967   // Do any required seeking/scaling on each subsession, before starting streaming:
00968   for (i = 0; i < fNumStreamStates; ++i) {
00969     if (subsession == NULL /* means: aggregated operation */
00970         || subsession == fStreamStates[i].subsession) {
00971       if (sawScaleHeader) {
00972         fStreamStates[i].subsession->setStreamScale(fOurSessionId,
00973                                                     fStreamStates[i].streamToken,
00974                                                     scale);
00975       }
00976       if (sawRangeHeader) {
00977         fStreamStates[i].subsession->seekStream(fOurSessionId,
00978                                                 fStreamStates[i].streamToken,
00979                                                 rangeStart);
00980       }
00981     }
0