00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "RTSPClient.hh"
00022 #include "RTSPCommon.hh"
00023 #include "Base64.hh"
00024 #include <GroupsockHelper.hh>
00025 #include "our_md5.h"
00026 #ifdef SUPPORT_REAL_RTSP
00027 #include "../RealRTSP/include/RealRTSP.hh"
00028 #endif
00029
00030
00031
00032 #ifdef USE_LOCALE
00033 #include <locale.h>
00034 #else
00035 #ifndef LC_NUMERIC
00036 #define LC_NUMERIC 0
00037 #endif
00038 #endif
00039
00040 class Locale {
00041 public:
00042 Locale(char const* newLocale, int category = LC_NUMERIC)
00043 : fCategory(category) {
00044 #ifdef USE_LOCALE
00045 fPrevLocale = strDup(setlocale(category, NULL));
00046 setlocale(category, newLocale);
00047 #endif
00048 }
00049
00050 virtual ~Locale() {
00051 #ifdef USE_LOCALE
00052 if (fPrevLocale != NULL) {
00053 setlocale(fCategory, fPrevLocale);
00054 delete[] fPrevLocale;
00055 }
00056 #endif
00057 }
00058
00059 private:
00060 int fCategory;
00061 char* fPrevLocale;
00062 };
00063
00064
00065
00067
00068 RTSPClient* RTSPClient::createNew(UsageEnvironment& env,
00069 int verbosityLevel,
00070 char const* applicationName,
00071 portNumBits tunnelOverHTTPPortNum) {
00072 return new RTSPClient(env, verbosityLevel,
00073 applicationName, tunnelOverHTTPPortNum);
00074 }
00075
00076 Boolean RTSPClient::lookupByName(UsageEnvironment& env,
00077 char const* instanceName,
00078 RTSPClient*& resultClient) {
00079 resultClient = NULL;
00080
00081 Medium* medium;
00082 if (!Medium::lookupByName(env, instanceName, medium)) return False;
00083
00084 if (!medium->isRTSPClient()) {
00085 env.setResultMsg(instanceName, " is not a RTSP client");
00086 return False;
00087 }
00088
00089 resultClient = (RTSPClient*)medium;
00090 return True;
00091 }
00092
00093 unsigned RTSPClient::fCSeq = 0;
00094
00095 RTSPClient::RTSPClient(UsageEnvironment& env,
00096 int verbosityLevel, char const* applicationName,
00097 portNumBits tunnelOverHTTPPortNum)
00098 : Medium(env),
00099 fVerbosityLevel(verbosityLevel),
00100 fTunnelOverHTTPPortNum(tunnelOverHTTPPortNum),
00101 fInputSocketNum(-1), fOutputSocketNum(-1), fServerAddress(0),
00102 fBaseURL(NULL), fTCPStreamIdCount(0), fLastSessionId(NULL),
00103 fSessionTimeoutParameter(0),
00104 #ifdef SUPPORT_REAL_RTSP
00105 fRealChallengeStr(NULL), fRealETagStr(NULL),
00106 #endif
00107 fServerIsKasenna(False), fKasennaContentType(NULL),
00108 fServerIsMicrosoft(False)
00109 {
00110 fResponseBufferSize = 20000;
00111 fResponseBuffer = new char[fResponseBufferSize+1];
00112
00113
00114 char const* const libName = "LIVE555 Streaming Media v";
00115 char const* const libVersionStr = LIVEMEDIA_LIBRARY_VERSION_STRING;
00116 char const* libPrefix; char const* libSuffix;
00117 if (applicationName == NULL || applicationName[0] == '\0') {
00118 applicationName = libPrefix = libSuffix = "";
00119 } else {
00120 libPrefix = " (";
00121 libSuffix = ")";
00122 }
00123 char const* const formatStr = "User-Agent: %s%s%s%s%s\r\n";
00124 unsigned headerSize
00125 = strlen(formatStr) + strlen(applicationName) + strlen(libPrefix)
00126 + strlen(libName) + strlen(libVersionStr) + strlen(libSuffix);
00127 fUserAgentHeaderStr = new char[headerSize];
00128 sprintf(fUserAgentHeaderStr, formatStr,
00129 applicationName, libPrefix, libName, libVersionStr, libSuffix);
00130 fUserAgentHeaderStrSize = strlen(fUserAgentHeaderStr);
00131 }
00132
00133 void RTSPClient::setUserAgentString(char const* userAgentStr) {
00134 if (userAgentStr == NULL) return;
00135
00136
00137 char const* const formatStr = "User-Agent: %s\r\n";
00138 unsigned headerSize = strlen(formatStr) + strlen(userAgentStr);
00139 delete[] fUserAgentHeaderStr;
00140 fUserAgentHeaderStr = new char[headerSize];
00141 sprintf(fUserAgentHeaderStr, formatStr, userAgentStr);
00142 fUserAgentHeaderStrSize = strlen(fUserAgentHeaderStr);
00143 }
00144
00145 RTSPClient::~RTSPClient() {
00146 reset();
00147 envir().taskScheduler().turnOffBackgroundReadHandling(fInputSocketNum);
00148 delete[] fResponseBuffer;
00149 delete[] fUserAgentHeaderStr;
00150 }
00151
00152 Boolean RTSPClient::isRTSPClient() const {
00153 return True;
00154 }
00155
00156 void RTSPClient::resetTCPSockets() {
00157 if (fInputSocketNum >= 0) {
00158 ::closeSocket(fInputSocketNum);
00159 if (fOutputSocketNum != fInputSocketNum) ::closeSocket(fOutputSocketNum);
00160 }
00161 fInputSocketNum = fOutputSocketNum = -1;
00162 }
00163
00164 void RTSPClient::reset() {
00165 resetTCPSockets();
00166 fServerAddress = 0;
00167
00168 delete[] fBaseURL; fBaseURL = NULL;
00169
00170 fCurrentAuthenticator.reset();
00171
00172 delete[] fKasennaContentType; fKasennaContentType = NULL;
00173 #ifdef SUPPORT_REAL_RTSP
00174 delete[] fRealChallengeStr; fRealChallengeStr = NULL;
00175 delete[] fRealETagStr; fRealETagStr = NULL;
00176 #endif
00177 delete[] fLastSessionId; fLastSessionId = NULL;
00178 }
00179
00180 static char* getLine(char* startOfLine) {
00181
00182 for (char* ptr = startOfLine; *ptr != '\0'; ++ptr) {
00183 if (*ptr == '\r' || *ptr == '\n') {
00184
00185 *ptr++ = '\0';
00186 if (*ptr == '\n') ++ptr;
00187 return ptr;
00188 }
00189 }
00190
00191 return NULL;
00192 }
00193
00194 char* RTSPClient::describeURL(char const* url, Authenticator* authenticator,
00195 Boolean allowKasennaProtocol) {
00196 char* cmd = NULL;
00197 fDescribeStatusCode = 0;
00198 do {
00199
00200 char* username; char* password;
00201 if (authenticator == NULL
00202 && parseRTSPURLUsernamePassword(url, username, password)) {
00203 char* result = describeWithPassword(url, username, password);
00204 delete[] username; delete[] password;
00205 return result;
00206 }
00207
00208 if (!openConnectionFromURL(url, authenticator)) break;
00209
00210
00211
00212
00213 fCurrentAuthenticator.reset();
00214 char* authenticatorStr
00215 = createAuthenticatorString(authenticator, "DESCRIBE", url);
00216
00217 char const* acceptStr = allowKasennaProtocol
00218 ? "Accept: application/x-rtsp-mh, application/sdp\r\n"
00219 : "Accept: application/sdp\r\n";
00220
00221
00222 char* const cmdFmt =
00223 "DESCRIBE %s RTSP/1.0\r\n"
00224 "CSeq: %d\r\n"
00225 "%s"
00226 "%s"
00227 "%s"
00228 #ifdef SUPPORT_REAL_RTSP
00229 REAL_DESCRIBE_HEADERS
00230 #endif
00231 "\r\n";
00232 unsigned cmdSize = strlen(cmdFmt)
00233 + strlen(url)
00234 + 20
00235 + strlen(acceptStr)
00236 + strlen(authenticatorStr)
00237 + fUserAgentHeaderStrSize;
00238 cmd = new char[cmdSize];
00239 sprintf(cmd, cmdFmt,
00240 url,
00241 ++fCSeq,
00242 acceptStr,
00243 authenticatorStr,
00244 fUserAgentHeaderStr);
00245 delete[] authenticatorStr;
00246
00247 if (!sendRequest(cmd, "DESCRIBE")) break;
00248
00249
00250 unsigned bytesRead; unsigned responseCode;
00251 char* firstLine; char* nextLineStart;
00252 if (!getResponse("DESCRIBE", bytesRead, responseCode, firstLine, nextLineStart,
00253 False )) break;
00254
00255
00256
00257 Boolean wantRedirection = False;
00258 char* redirectionURL = NULL;
00259 #ifdef SUPPORT_REAL_RTSP
00260 delete[] fRealETagStr; fRealETagStr = new char[fResponseBufferSize];
00261 #endif
00262 if (responseCode == 301 || responseCode == 302) {
00263 wantRedirection = True;
00264 redirectionURL = new char[fResponseBufferSize];
00265 } else if (responseCode != 200) {
00266 checkForAuthenticationFailure(responseCode, nextLineStart, authenticator);
00267 envir().setResultMsg("cannot handle DESCRIBE response: ", firstLine);
00268 break;
00269 }
00270
00271
00272
00273
00274
00275
00276 char* serverType = new char[fResponseBufferSize];
00277 int contentLength = -1;
00278 char* lineStart;
00279 while (1) {
00280 lineStart = nextLineStart;
00281 if (lineStart == NULL) break;
00282
00283 nextLineStart = getLine(lineStart);
00284 if (lineStart[0] == '\0') break;
00285
00286 if (sscanf(lineStart, "Content-Length: %d", &contentLength) == 1
00287 || sscanf(lineStart, "Content-length: %d", &contentLength) == 1) {
00288 if (contentLength < 0) {
00289 envir().setResultMsg("Bad \"Content-length:\" header: \"",
00290 lineStart, "\"");
00291 break;
00292 }
00293 } else if (sscanf(lineStart, "Server: %s", serverType) == 1) {
00294 if (strncmp(serverType, "Kasenna", 7) == 0) fServerIsKasenna = True;
00295 if (strncmp(serverType, "WMServer", 8) == 0) fServerIsMicrosoft = True;
00296 #ifdef SUPPORT_REAL_RTSP
00297 } else if (sscanf(lineStart, "ETag: %s", fRealETagStr) == 1) {
00298 #endif
00299 } else if (wantRedirection) {
00300 if (sscanf(lineStart, "Location: %s", redirectionURL) == 1) {
00301
00302 if (fVerbosityLevel >= 1) {
00303 envir() << "Redirecting to the new URL \""
00304 << redirectionURL << "\"\n";
00305 }
00306 reset();
00307 char* result = describeURL(redirectionURL);
00308 delete[] redirectionURL;
00309 delete[] serverType;
00310 delete[] cmd;
00311 return result;
00312 }
00313 }
00314 }
00315 delete[] serverType;
00316
00317
00318 if (wantRedirection) {
00319 envir().setResultMsg("Saw redirection response code, but not a \"Location:\" header");
00320 delete[] redirectionURL;
00321 break;
00322 }
00323 if (lineStart == NULL) {
00324 envir().setResultMsg("no content following header lines: ", fResponseBuffer);
00325 break;
00326 }
00327
00328
00329
00330
00331 char* bodyStart = nextLineStart;
00332 if (contentLength >= 0) {
00333
00334 unsigned numBodyBytes = &firstLine[bytesRead] - bodyStart;
00335 if (contentLength > (int)numBodyBytes) {
00336
00337
00338 unsigned numExtraBytesNeeded = contentLength - numBodyBytes;
00339 unsigned remainingBufferSize
00340 = fResponseBufferSize - (bytesRead + (firstLine - fResponseBuffer));
00341 if (numExtraBytesNeeded > remainingBufferSize) {
00342 char tmpBuf[200];
00343 sprintf(tmpBuf, "Read buffer size (%d) is too small for \"Content-length:\" %d (need a buffer size of >= %d bytes\n",
00344 fResponseBufferSize, contentLength,
00345 fResponseBufferSize + numExtraBytesNeeded - remainingBufferSize);
00346 envir().setResultMsg(tmpBuf);
00347 break;
00348 }
00349
00350
00351 if (fVerbosityLevel >= 1) {
00352 envir() << "Need to read " << numExtraBytesNeeded
00353 << " extra bytes\n";
00354 }
00355 while (numExtraBytesNeeded > 0) {
00356 struct sockaddr_in fromAddress;
00357 char* ptr = &firstLine[bytesRead];
00358 int bytesRead2 = readSocket(envir(), fInputSocketNum, (unsigned char*)ptr,
00359 numExtraBytesNeeded, fromAddress);
00360 if (bytesRead2 < 0) break;
00361 ptr[bytesRead2] = '\0';
00362 if (fVerbosityLevel >= 1) {
00363 envir() << "Read " << bytesRead2 << " extra bytes: "
00364 << ptr << "\n";
00365 }
00366
00367 bytesRead += bytesRead2;
00368 numExtraBytesNeeded -= bytesRead2;
00369 }
00370 if (numExtraBytesNeeded > 0) break;
00371 }
00372
00373
00374
00375
00376 int from, to = 0;
00377 for (from = 0; from < contentLength; ++from) {
00378 if (bodyStart[from] != '\0') {
00379 if (to != from) bodyStart[to] = bodyStart[from];
00380 ++to;
00381 }
00382 }
00383 if (from != to && fVerbosityLevel >= 1) {
00384 envir() << "Warning: " << from-to << " invalid 'NULL' bytes were found in (and removed from) the SDP description.\n";
00385 }
00386 bodyStart[to] = '\0';
00387 }
00388
00390
00391 if (fServerIsKasenna && strncmp(bodyStart, "<MediaDescription>", 18) == 0) {
00392
00393 int videoPid, audioPid;
00394 u_int64_t mh_duration;
00395 char* currentWord = new char[fResponseBufferSize];
00396 delete[] fKasennaContentType;
00397 fKasennaContentType = new char[fResponseBufferSize];
00398 char* currentPos = bodyStart;
00399
00400 while (strcmp(currentWord, "</MediaDescription>") != 0) {
00401 sscanf(currentPos, "%s", currentWord);
00402
00403 if (strcmp(currentWord, "VideoPid") == 0) {
00404 currentPos += strlen(currentWord) + 1;
00405 sscanf(currentPos, "%s", currentWord);
00406 currentPos += strlen(currentWord) + 1;
00407 sscanf(currentPos, "%d", &videoPid);
00408 currentPos += 3;
00409 }
00410
00411 if (strcmp(currentWord, "AudioPid") == 0) {
00412 currentPos += strlen(currentWord) + 1;
00413 sscanf(currentPos, "%s", currentWord);
00414 currentPos += strlen(currentWord) + 1;
00415 sscanf(currentPos, "%d", &audioPid);
00416 currentPos += 3;
00417 }
00418
00419 if (strcmp(currentWord, "Duration") == 0) {
00420 currentPos += strlen(currentWord) + 1;
00421 sscanf(currentPos, "%s", currentWord);
00422 currentPos += strlen(currentWord) + 1;
00423 sscanf(currentPos, "%llu", &mh_duration);
00424 currentPos += 3;
00425 }
00426
00427 if (strcmp(currentWord, "TypeSpecificData") == 0) {
00428 currentPos += strlen(currentWord) + 1;
00429 sscanf(currentPos, "%s", currentWord);
00430 currentPos += strlen(currentWord) + 1;
00431 sscanf(currentPos, "%s", fKasennaContentType);
00432 currentPos += 3;
00433 printf("Kasenna Content Type: %s\n", fKasennaContentType);
00434 }
00435
00436 currentPos += strlen(currentWord) + 1;
00437 }
00438
00439 if (fKasennaContentType != NULL
00440 && strcmp(fKasennaContentType, "PARTNER_41_MPEG-4") == 0) {
00441 char* describeSDP = describeURL(url, authenticator, True);
00442
00443 delete[] currentWord;
00444 delete[] cmd;
00445 return describeSDP;
00446 }
00447
00448 unsigned char byte1 = fServerAddress & 0x000000ff;
00449 unsigned char byte2 = (fServerAddress & 0x0000ff00) >> 8;
00450 unsigned char byte3 = (fServerAddress & 0x00ff0000) >> 16;
00451 unsigned char byte4 = (fServerAddress & 0xff000000) >> 24;
00452
00453 char const* sdpFmt =
00454 "v=0\r\n"
00455 "o=NoSpacesAllowed 1 1 IN IP4 %u.%u.%u.%u\r\n"
00456 "s=%s\r\n"
00457 "c=IN IP4 %u.%u.%u.%u\r\n"
00458 "t=0 0\r\n"
00459 "a=control:*\r\n"
00460 "a=range:npt=0-%llu\r\n"
00461 "m=video 1554 RAW/RAW/UDP 33\r\n"
00462 "a=control:trackID=%d\r\n";
00463 unsigned sdpBufSize = strlen(sdpFmt)
00464 + 4*3
00465 + strlen(url)
00466 + 20
00467 + 20;
00468 char* sdpBuf = new char[sdpBufSize];
00469 sprintf(sdpBuf, sdpFmt,
00470 byte1, byte2, byte3, byte4,
00471 url,
00472 byte1, byte2, byte3, byte4,
00473 mh_duration/1000000,
00474 videoPid);
00475
00476 char* result = strDup(sdpBuf);
00477 delete[] sdpBuf; delete[] currentWord;
00478 delete[] cmd;
00479 return result;
00480 }
00482
00483 delete[] cmd;
00484 return strDup(bodyStart);
00485 } while (0);
00486
00487 delete[] cmd;
00488 if (fDescribeStatusCode == 0) fDescribeStatusCode = 2;
00489 return NULL;
00490 }
00491
00492 char* RTSPClient
00493 ::describeWithPassword(char const* url,
00494 char const* username, char const* password) {
00495 Authenticator authenticator;
00496 authenticator.setUsernameAndPassword(username, password);
00497 char* describeResult = describeURL(url, &authenticator);
00498 if (describeResult != NULL) {
00499
00500 return describeResult;
00501 }
00502
00503
00504 if (authenticator.realm() == NULL) {
00505
00506 return NULL;
00507 }
00508
00509
00510 describeResult = describeURL(url, &authenticator);
00511 if (describeResult != NULL) {
00512
00513 fCurrentAuthenticator = authenticator;
00514 }
00515
00516 return describeResult;
00517 }
00518
00519 char* RTSPClient::sendOptionsCmd(char const* url,
00520 char* username, char* password,
00521 Authenticator* authenticator) {
00522 char* result = NULL;
00523 char* cmd = NULL;
00524 Boolean haveAllocatedAuthenticator = False;
00525 do {
00526 if (authenticator == NULL) {
00527
00528
00529 if (username == NULL && password == NULL
00530 && parseRTSPURLUsernamePassword(url, username, password)) {
00531 Authenticator authenticator;
00532 authenticator.setUsernameAndPassword(username, password);
00533 result = sendOptionsCmd(url, username, password, &authenticator);
00534 delete[] username; delete[] password;
00535 break;
00536 } else if (username != NULL && password != NULL) {
00537
00538 authenticator = new Authenticator;
00539 haveAllocatedAuthenticator = True;
00540 authenticator->setUsernameAndPassword(username, password);
00541
00542 result = sendOptionsCmd(url, username, password, authenticator);
00543 if (result != NULL) break;
00544
00545
00546 if (authenticator->realm() == NULL) {
00547
00548 break;
00549 }
00550
00551 }
00552 }
00553
00554 if (!openConnectionFromURL(url, authenticator)) break;
00555
00556
00557
00558
00559 char* authenticatorStr
00560 = createAuthenticatorString(authenticator, "OPTIONS", url);
00561
00562 char* const cmdFmt =
00563 "OPTIONS %s RTSP/1.0\r\n"
00564 "CSeq: %d\r\n"
00565 "%s"
00566 "%s"
00567 #ifdef SUPPORT_REAL_RTSP
00568 REAL_OPTIONS_HEADERS
00569 #endif
00570 "\r\n";
00571 unsigned cmdSize = strlen(cmdFmt)
00572 + strlen(url)
00573 + 20
00574 + strlen(authenticatorStr)
00575 + fUserAgentHeaderStrSize;
00576 cmd = new char[cmdSize];
00577 sprintf(cmd, cmdFmt,
00578 url,
00579 ++fCSeq,
00580 authenticatorStr,
00581 fUserAgentHeaderStr);
00582 delete[] authenticatorStr;
00583
00584 if (!sendRequest(cmd, "OPTIONS")) break;
00585
00586
00587 unsigned bytesRead; unsigned responseCode;
00588 char* firstLine; char* nextLineStart;
00589 if (!getResponse("OPTIONS", bytesRead, responseCode, firstLine, nextLineStart,
00590 False )) break;
00591 if (responseCode != 200) {
00592 checkForAuthenticationFailure(responseCode, nextLineStart, authenticator);
00593 envir().setResultMsg("cannot handle OPTIONS response: ", firstLine);
00594 break;
00595 }
00596
00597
00598 char* lineStart;
00599 while (1) {
00600 lineStart = nextLineStart;
00601 if (lineStart == NULL) break;
00602
00603 nextLineStart = getLine(lineStart);
00604
00605 if (_strncasecmp(lineStart, "Public: ", 8) == 0) {
00606 delete[] result; result = strDup(&lineStart[8]);
00607 #ifdef SUPPORT_REAL_RTSP
00608 } else if (_strncasecmp(lineStart, "RealChallenge1: ", 16) == 0) {
00609 delete[] fRealChallengeStr; fRealChallengeStr = strDup(&lineStart[16]);
00610 #endif
00611 }
00612 }
00613 } while (0);
00614
00615 delete[] cmd;
00616 if (haveAllocatedAuthenticator) delete authenticator;
00617 return result;
00618 }
00619
00620 static Boolean isAbsoluteURL(char const* url) {
00621
00622
00623 while (*url != '\0' && *url != '/') {
00624 if (*url == ':') return True;
00625 ++url;
00626 }
00627
00628 return False;
00629 }
00630
00631 void RTSPClient::constructSubsessionURL(MediaSubsession const& subsession,
00632 char const*& prefix,
00633 char const*& separator,
00634 char const*& suffix) {
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646 prefix = fBaseURL;
00647 if (prefix == NULL) prefix = "";
00648
00649 suffix = subsession.controlPath();
00650 if (suffix == NULL) suffix = "";
00651
00652 if (isAbsoluteURL(suffix)) {
00653 prefix = separator = "";
00654 } else {
00655 unsigned prefixLen = strlen(prefix);
00656 separator = (prefix[prefixLen-1] == '/' || suffix[0] == '/') ? "" : "/";
00657 }
00658 }
00659
00660 Boolean RTSPClient::announceSDPDescription(char const* url,
00661 char const* sdpDescription,
00662 Authenticator* authenticator) {
00663 char* cmd = NULL;
00664 do {
00665 if (!openConnectionFromURL(url, authenticator)) break;
00666
00667
00668
00669
00670 fCurrentAuthenticator.reset();
00671 char* authenticatorStr
00672 = createAuthenticatorString(authenticator, "ANNOUNCE", url);
00673
00674 char* const cmdFmt =
00675 "ANNOUNCE %s RTSP/1.0\r\n"
00676 "CSeq: %d\r\n"
00677 "Content-Type: application/sdp\r\n"
00678 "%s"
00679 "Content-length: %d\r\n\r\n"
00680 "%s";
00681
00682 unsigned sdpSize = strlen(sdpDescription);
00683 unsigned cmdSize = strlen(cmdFmt)
00684 + strlen(url)
00685 + 20
00686 + strlen(authenticatorStr)
00687 + 20
00688 + sdpSize;
00689 cmd = new char[cmdSize];
00690 sprintf(cmd, cmdFmt,
00691 url,
00692 ++fCSeq,
00693 authenticatorStr,
00694 sdpSize,
00695 sdpDescription);
00696 delete[] authenticatorStr;
00697
00698 if (!sendRequest(cmd, "ANNOUNCE")) break;
00699
00700
00701 unsigned bytesRead; unsigned responseCode;
00702 char* firstLine; char* nextLineStart;
00703 if (!getResponse("ANNOUNCE", bytesRead, responseCode, firstLine, nextLineStart,
00704 False )) break;
00705
00706
00707 if (responseCode != 200) {
00708 checkForAuthenticationFailure(responseCode, nextLineStart, authenticator);
00709 envir().setResultMsg("cannot handle ANNOUNCE response: ", firstLine);
00710 break;
00711 }
00712
00713 delete[] cmd;
00714 return True;
00715 } while (0);
00716
00717 delete[] cmd;
00718 return False;
00719 }
00720
00721 Boolean RTSPClient
00722 ::announceWithPassword(char const* url, char const* sdpDescription,
00723 char const* username, char const* password) {
00724 Authenticator authenticator;
00725 authenticator.setUsernameAndPassword(username, password);
00726 if (announceSDPDescription(url, sdpDescription, &authenticator)) {
00727
00728 return True;
00729 }
00730
00731
00732 if (authenticator.realm() == NULL) {
00733
00734 return False;
00735 }
00736
00737
00738 Boolean secondTrySuccess
00739 = announceSDPDescription(url, sdpDescription, &authenticator);
00740
00741 if (secondTrySuccess) {
00742
00743 fCurrentAuthenticator = authenticator;
00744 }
00745
00746 return secondTrySuccess;
00747 }
00748
00749 Boolean RTSPClient::setupMediaSubsession(MediaSubsession& subsession,
00750 Boolean streamOutgoing,
00751 Boolean streamUsingTCP,
00752 Boolean forceMulticastOnUnspecified) {
00753 char* cmd = NULL;
00754 char* setupStr = NULL;
00755
00756 if (fServerIsMicrosoft) {
00757
00758 char *tmpStr = subsession.parentSession().mediaSessionType();
00759 if (tmpStr != NULL && strncmp(tmpStr, "broadcast", 9) == 0) {
00760 subsession.parentSession().playEndTime() = 0.0;
00761 }
00762 }
00763
00764 do {
00765
00766
00767
00768 char* authenticatorStr
00769 = createAuthenticatorString(&fCurrentAuthenticator,
00770 "SETUP", fBaseURL);
00771
00772
00773
00774 char* sessionStr;
00775 if (fLastSessionId != NULL) {
00776 sessionStr = new char[20+strlen(fLastSessionId)];
00777 sprintf(sessionStr, "Session: %s\r\n", fLastSessionId);
00778 } else {
00779 sessionStr = "";
00780 }
00781
00782 char* transportStr = NULL;
00783 #ifdef SUPPORT_REAL_RTSP
00784 if (usingRealNetworksChallengeResponse()) {
00785
00786 char challenge2[64];
00787 char checksum[34];
00788 RealCalculateChallengeResponse(fRealChallengeStr, challenge2, checksum);
00789
00790 char const* etag = fRealETagStr == NULL ? "" : fRealETagStr;
00791
00792 char* transportHeader;
00793 if (subsession.parentSession().isRealNetworksRDT) {
00794 transportHeader = strDup("Transport: x-pn-tng/tcp;mode=play,rtp/avp/unicast;mode=play\r\n");
00795 } else {
00796
00797 char const* transportHeaderFmt
00798 = "Transport: RTP/AVP%s%s=%d-%d\r\n";
00799 char const* transportTypeStr;
00800 char const* portTypeStr;
00801 unsigned short rtpNumber, rtcpNumber;
00802 if (streamUsingTCP) {
00803 transportTypeStr = "/TCP;unicast";
00804 portTypeStr = ";interleaved";
00805 rtpNumber = fTCPStreamIdCount++;
00806 rtcpNumber = fTCPStreamIdCount++;
00807 } else {
00808 unsigned connectionAddress = subsession.connectionEndpointAddress();
00809 Boolean requestMulticastStreaming = IsMulticastAddress(connectionAddress)
00810 || (connectionAddress == 0 && forceMulticastOnUnspecified);
00811 transportTypeStr = requestMulticastStreaming ? ";multicast" : ";unicast";
00812 portTypeStr = ";client_port";
00813 rtpNumber = subsession.clientPortNum();
00814 if (rtpNumber == 0) {
00815 envir().setResultMsg("Client port number unknown\n");
00816 break;
00817 }
00818 rtcpNumber = rtpNumber + 1;
00819 }
00820
00821 unsigned transportHeaderSize = strlen(transportHeaderFmt)
00822 + strlen(transportTypeStr) + strlen(portTypeStr) + 2*5 ;
00823 transportHeader = new char[transportHeaderSize];
00824 sprintf(transportHeader, transportHeaderFmt,
00825 transportTypeStr, portTypeStr, rtpNumber, rtcpNumber);
00826 }
00827 char const* transportFmt =
00828 "%s"
00829 "RealChallenge2: %s, sd=%s\r\n"
00830 "If-Match: %s\r\n";
00831 unsigned transportSize = strlen(transportFmt)
00832 + strlen(transportHeader)
00833 + sizeof challenge2 + sizeof checksum
00834 + strlen(etag);
00835 transportStr = new char[transportSize];
00836 sprintf(transportStr, transportFmt,
00837 transportHeader,
00838 challenge2, checksum,
00839 etag);
00840 delete[] transportHeader;
00841
00842 if (subsession.parentSession().isRealNetworksRDT) {
00843
00844 RealRDTSource* rdtSource
00845 = (RealRDTSource*)(subsession.readSource());
00846 rdtSource->setInputSocket(fInputSocketNum);
00847 }
00848 }
00849 #endif
00850
00851 char const *prefix, *separator, *suffix;
00852 constructSubsessionURL(subsession, prefix, separator, suffix);
00853 char* transportFmt;
00854
00855 if (strcmp(subsession.protocolName(), "UDP") == 0) {
00856 char const* setupFmt = "SETUP %s%s RTSP/1.0\r\n";
00857 unsigned setupSize = strlen(setupFmt)
00858 + strlen(prefix) + strlen (separator);
00859 setupStr = new char[setupSize];
00860 sprintf(setupStr, setupFmt, prefix, separator);
00861
00862 transportFmt = "Transport: RAW/RAW/UDP%s%s%s=%d-%d\r\n";
00863 } else {
00864 char const* setupFmt = "SETUP %s%s%s RTSP/1.0\r\n";
00865 unsigned setupSize = strlen(setupFmt)
00866 + strlen(prefix) + strlen (separator) + strlen(suffix);
00867 setupStr = new char[setupSize];
00868 sprintf(setupStr, setupFmt, prefix, separator, suffix);
00869
00870 transportFmt = "Transport: RTP/AVP%s%s%s=%d-%d\r\n";
00871 }
00872
00873 if (transportStr == NULL) {
00874
00875 char const* transportTypeStr;
00876 char const* modeStr = streamOutgoing ? ";mode=receive" : "";
00877
00878 char const* portTypeStr;
00879 unsigned short rtpNumber, rtcpNumber;
00880 if (streamUsingTCP) {
00881 transportTypeStr = "/TCP;unicast";
00882 portTypeStr = ";interleaved";
00883 rtpNumber = fTCPStreamIdCount++;
00884 rtcpNumber = fTCPStreamIdCount++;
00885 } else {
00886 unsigned connectionAddress = subsession.connectionEndpointAddress();
00887 Boolean requestMulticastStreaming = IsMulticastAddress(connectionAddress)
00888 || (connectionAddress == 0 && forceMulticastOnUnspecified);
00889 transportTypeStr = requestMulticastStreaming ? ";multicast" : ";unicast";
00890 portTypeStr = ";client_port";
00891 rtpNumber = subsession.clientPortNum();
00892 if (rtpNumber == 0) {
00893 envir().setResultMsg("Client port number unknown\n");
00894 break;
00895 }
00896 rtcpNumber = rtpNumber + 1;
00897 }
00898
00899 unsigned transportSize = strlen(transportFmt)
00900 + strlen(transportTypeStr) + strlen(modeStr) + strlen(portTypeStr) + 2*5 ;
00901 transportStr = new char[transportSize];
00902 sprintf(transportStr, transportFmt,
00903 transportTypeStr, modeStr, portTypeStr, rtpNumber, rtcpNumber);
00904 }
00905
00906
00907 char* const cmdFmt =
00908 "%s"
00909 "CSeq: %d\r\n"
00910 "%s"
00911 "%s"
00912 "%s"
00913 "%s"
00914 "\r\n";
00915
00916 unsigned cmdSize = strlen(cmdFmt)
00917 + strlen(setupStr)
00918 + 20
00919 + strlen(transportStr)
00920 + strlen(sessionStr)
00921 + strlen(authenticatorStr)
00922 + fUserAgentHeaderStrSize;
00923 cmd = new char[cmdSize];
00924 sprintf(cmd, cmdFmt,
00925 setupStr,
00926 ++fCSeq,
00927 transportStr,
00928 sessionStr,
00929 authenticatorStr,
00930 fUserAgentHeaderStr);
00931 delete[] authenticatorStr;
00932 if (sessionStr[0] != '\0') delete[] sessionStr;
00933 delete[] setupStr; delete[] transportStr;
00934
00935
00936 if (!sendRequest(cmd, "SETUP")) break;
00937
00938
00939 unsigned bytesRead; unsigned responseCode;
00940 char* firstLine; char* nextLineStart;
00941 if (!getResponse("SETUP", bytesRead, responseCode, firstLine, nextLineStart)) break;
00942
00943
00944
00945
00946 char* lineStart;
00947 char* sessionId = new char[fResponseBufferSize];
00948 while (1) {
00949 lineStart = nextLineStart;
00950 if (lineStart == NULL) break;
00951
00952 nextLineStart = getLine(lineStart);
00953
00954 if (sscanf(lineStart, "Session: %[^;]", sessionId) == 1) {
00955 subsession.sessionId = strDup(sessionId);
00956 delete[] fLastSessionId; fLastSessionId = strDup(sessionId);
00957
00958
00959 char* afterSessionId
00960 = lineStart + strlen(sessionId) + strlen ("Session: ");;
00961 int timeoutVal;
00962 if (sscanf(afterSessionId, "; timeout = %d", &timeoutVal) == 1) {
00963 fSessionTimeoutParameter = timeoutVal;
00964 }
00965 continue;
00966 }
00967
00968 char* serverAddressStr;
00969 portNumBits serverPortNum;
00970 unsigned char rtpChannelId, rtcpChannelId;
00971 if (parseTransportResponse(lineStart,
00972 serverAddressStr, serverPortNum,
00973 rtpChannelId, rtcpChannelId)) {
00974 delete[] subsession.connectionEndpointName();
00975 subsession.connectionEndpointName() = serverAddressStr;
00976 subsession.serverPortNum = serverPortNum;
00977 subsession.rtpChannelId = rtpChannelId;
00978 subsession.rtcpChannelId = rtcpChannelId;
00979 continue;
00980 }
00981 }
00982 delete[] sessionId;
00983
00984 if (subsession.sessionId == NULL) {
00985 envir().setResultMsg("\"Session:\" header is missing in the response");
00986 break;
00987 }
00988
00989 if (streamUsingTCP) {
00990
00991
00992 if (subsession.rtpSource() != NULL)
00993 subsession.rtpSource()->setStreamSocket(fInputSocketNum,
00994 subsession.rtpChannelId);
00995 if (subsession.rtcpInstance() != NULL)
00996 subsession.rtcpInstance()->setStreamSocket(fInputSocketNum,
00997 subsession.rtcpChannelId);
00998 } else {
00999
01000
01001
01002 subsession.setDestinations(fServerAddress);
01003 }
01004
01005 delete[] cmd;
01006 return True;
01007 } while (0);
01008
01009 delete[] cmd;
01010 return False;
01011 }
01012
01013 static char* createScaleString(float scale, float currentScale) {
01014 char buf[100];
01015 if (scale == 1.0f && currentScale == 1.0f) {
01016
01017 buf[0] = '\0';
01018 } else {
01019 Locale("POSIX");
01020 sprintf(buf, "Scale: %f\r\n", scale);
01021 }
01022
01023 return strDup(buf);
01024 }
01025
01026 static char* createRangeString(float start, float end) {
01027 char buf[100];
01028 if (start < 0) {
01029
01030 buf[0] = '\0';
01031 } else if (end < 0) {
01032
01033 Locale("POSIX");
01034 sprintf(buf, "Range: npt=%.3f-\r\n", start);
01035 } else {
01036
01037 Locale("POSIX");
01038 sprintf(buf, "Range: npt=%.3f-%.3f\r\n", start, end);
01039 }
01040
01041 return strDup(buf);
01042 }
01043
01044 static char const* NoSessionErr = "No RTSP session is currently in progress\n";
01045
01046 Boolean RTSPClient::playMediaSession(MediaSession& session,
01047 float start, float end, float scale) {
01048 #ifdef SUPPORT_REAL_RTSP
01049 if (session.isRealNetworksRDT) {
01050
01051 char* streamRuleString = RealGetSubscribeRuleString(&session);
01052 setMediaSessionParameter(session, "Subscribe", streamRuleString);
01053 delete[] streamRuleString;
01054 }
01055 #endif
01056 char* cmd = NULL;
01057 do {
01058
01059 if (fLastSessionId == NULL) {
01060 envir().setResultMsg(NoSessionErr);
01061 break;
01062 }
01063
01064
01065
01066
01067 char* authenticatorStr
01068 = createAuthenticatorString(&fCurrentAuthenticator, "PLAY", fBaseURL);
01069
01070 char* scaleStr = createScaleString(scale, session.scale());
01071
01072 char* rangeStr = createRangeString(start, end);
01073
01074 char* const cmdFmt =
01075 "PLAY %s RTSP/1.0\r\n"
01076 "CSeq: %d\r\n"
01077 "Session: %s\r\n"
01078 "%s"
01079 "%s"
01080 "%s"
01081 "%s"
01082 "\r\n";
01083
01084 unsigned cmdSize = strlen(cmdFmt)
01085 + strlen(fBaseURL)
01086 + 20
01087 + strlen(fLastSessionId)
01088 + strlen(scaleStr)
01089 + strlen(rangeStr)
01090 + strlen(authenticatorStr)
01091 + fUserAgentHeaderStrSize;
01092 cmd = new char[cmdSize];
01093 sprintf(cmd, cmdFmt,
01094 fBaseURL,
01095 ++fCSeq,
01096 fLastSessionId,
01097 scaleStr,
01098 rangeStr,
01099 authenticatorStr,
01100 fUserAgentHeaderStr);
01101 delete[] scaleStr;
01102 delete[] rangeStr;
01103 delete[] authenticatorStr;
01104
01105 if (!sendRequest(cmd, "PLAY")) break;
01106
01107
01108 unsigned bytesRead; unsigned responseCode;
01109 char* firstLine; char* nextLineStart;
01110 if (!getResponse("PLAY", bytesRead, responseCode, firstLine, nextLineStart)) break;
01111
01112
01113 char* lineStart;
01114 while (1) {
01115 lineStart = nextLineStart;
01116 if (lineStart == NULL) break;
01117
01118 nextLineStart = getLine(lineStart);
01119
01120 if (parseScaleHeader(lineStart, session.scale())) break;
01121 }
01122
01123 if (fTCPStreamIdCount == 0) {
01124
01125 envir().taskScheduler().turnOnBackgroundReadHandling(fInputSocketNum,
01126 (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this);
01127 }
01128
01129 delete[] cmd;
01130 return True;
01131 } while (0);
01132
01133 delete[] cmd;
01134 return False;
01135 }
01136
01137 Boolean RTSPClient::playMediaSubsession(MediaSubsession& subsession,
01138 float start, float end, float scale,
01139 Boolean hackForDSS) {
01140 char* cmd = NULL;
01141 do {
01142
01143 if (subsession.sessionId == NULL) {
01144 envir().setResultMsg(NoSessionErr);
01145 break;
01146 }
01147
01148
01149
01150
01151 char* authenticatorStr
01152 = createAuthenticatorString(&fCurrentAuthenticator, "PLAY", fBaseURL);
01153
01154 char* scaleStr = createScaleString(scale, subsession.scale());
01155
01156 char* rangeStr = createRangeString(start, end);
01157
01158 char* const cmdFmt =
01159 "PLAY %s%s%s RTSP/1.0\r\n"
01160 "CSeq: %d\r\n"
01161 "Session: %s\r\n"
01162 "%s"
01163 "%s"
01164 "%s"
01165 "%s"
01166 "\r\n";
01167
01168 char const *prefix, *separator, *suffix;
01169 constructSubsessionURL(subsession, prefix, separator, suffix);
01170 if (hackForDSS || fServerIsKasenna) {
01171
01172
01173
01174
01175 separator = suffix = "";
01176 }
01177
01178 unsigned cmdSize = strlen(cmdFmt)
01179 + strlen(prefix) + strlen(separator) + strlen(suffix)
01180 + 20
01181 + strlen(subsession.sessionId)
01182 + strlen(scaleStr)
01183 + strlen(rangeStr)
01184 + strlen(authenticatorStr)
01185 + fUserAgentHeaderStrSize;
01186 cmd = new char[cmdSize];
01187 sprintf(cmd, cmdFmt,
01188 prefix, separator, suffix,
01189 ++fCSeq,
01190 subsession.sessionId,
01191 scaleStr,
01192 rangeStr,
01193 authenticatorStr,
01194 fUserAgentHeaderStr);
01195 delete[] scaleStr;
01196 delete[] rangeStr;
01197 delete[] authenticatorStr;
01198
01199 if (!sendRequest(cmd, "PLAY")) break;
01200
01201
01202 unsigned bytesRead; unsigned responseCode;
01203 char* firstLine; char* nextLineStart;
01204 if (!getResponse("PLAY", bytesRead, responseCode, firstLine, nextLineStart)) break;
01205
01206
01207 char* lineStart;
01208 while (1) {
01209 lineStart = nextLineStart;
01210 if (lineStart == NULL) break;
01211
01212 nextLineStart = getLine(lineStart);
01213
01214 if (parseRTPInfoHeader(lineStart,
01215 subsession.rtpInfo.trackId,
01216 subsession.rtpInfo.seqNum,
01217 subsession.rtpInfo.timestamp)) {
01218 continue;
01219 }
01220 if (parseScaleHeader(lineStart, subsession.scale())) continue;
01221 }
01222
01223 delete[] cmd;
01224 return True;
01225 } while (0);
01226
01227 delete[] cmd;
01228 return False;
01229 }
01230
01231 Boolean RTSPClient::pauseMediaSession(MediaSession& session) {
01232 char* cmd = NULL;
01233 do {
01234
01235 if (fLastSessionId == NULL) {
01236 envir().setResultMsg(NoSessionErr);
01237 break;
01238 }
01239
01240
01241
01242
01243 char* authenticatorStr
01244 = createAuthenticatorString(&fCurrentAuthenticator, "PAUSE", fBaseURL);
01245
01246 char* const cmdFmt =
01247 "PAUSE %s RTSP/1.0\r\n"
01248 "CSeq: %d\r\n"
01249 "Session: %s\r\n"
01250 "%s"
01251 "%s"
01252 "\r\n";
01253
01254 unsigned cmdSize = strlen(cmdFmt)
01255 + strlen(fBaseURL)
01256 + 20
01257 + strlen(fLastSessionId)
01258 + strlen(authenticatorStr)
01259 + fUserAgentHeaderStrSize;
01260 cmd = new char[cmdSize];
01261 sprintf(cmd, cmdFmt,
01262 fBaseURL,
01263 ++fCSeq,
01264 fLastSessionId,
01265 authenticatorStr,
01266 fUserAgentHeaderStr);
01267 delete[] authenticatorStr;
01268
01269 if (!sendRequest(cmd, "PAUSE")) break;
01270
01271 if (fTCPStreamIdCount == 0) {
01272
01273 unsigned bytesRead; unsigned responseCode;
01274 char* firstLine; char* nextLineStart;
01275 if (!getResponse("PAUSE", bytesRead, responseCode, firstLine, nextLineStart)) break;
01276 }
01277
01278 delete[] cmd;
01279 return True;
01280 } while (0);
01281
01282 delete[] cmd;
01283 return False;
01284 }
01285
01286 Boolean RTSPClient::pauseMediaSubsession(MediaSubsession& subsession) {
01287 char* cmd = NULL;
01288 do {
01289
01290 if (subsession.sessionId == NULL) {
01291 envir().setResultMsg(NoSessionErr);
01292 break;
01293 }
01294
01295
01296
01297
01298 char* authenticatorStr
01299 = createAuthenticatorString(&fCurrentAuthenticator, "PAUSE", fBaseURL);
01300
01301 char* const cmdFmt =
01302 "PAUSE %s%s%s RTSP/1.0\r\n"
01303 "CSeq: %d\r\n"
01304 "Session: %s\r\n"
01305 "%s"
01306 "%s"
01307 "\r\n";
01308
01309 char const *prefix, *separator, *suffix;
01310 constructSubsessionURL(subsession, prefix, separator, suffix);
01311 if (fServerIsKasenna) separator = suffix = "";
01312
01313 unsigned cmdSize = strlen(cmdFmt)
01314 + strlen(prefix) + strlen(separator) + strlen(suffix)
01315 + 20
01316 + strlen(subsession.sessionId)
01317 + strlen(authenticatorStr)
01318 + fUserAgentHeaderStrSize;
01319 cmd = new char[cmdSize];
01320 sprintf(cmd, cmdFmt,
01321 prefix, separator, suffix,
01322 ++fCSeq,
01323 subsession.sessionId,
01324 authenticatorStr,
01325 fUserAgentHeaderStr);
01326 delete[] authenticatorStr;
01327
01328 if (!sendRequest(cmd, "PAUSE")) break;
01329
01330 if (fTCPStreamIdCount == 0) {
01331
01332 unsigned bytesRead; unsigned responseCode;
01333 char* firstLine; char* nextLineStart;
01334 if (!getResponse("PAUSE", bytesRead, responseCode, firstLine, nextLineStart)) break;
01335 }
01336
01337 delete[] cmd;
01338 return True;
01339 } while (0);
01340
01341 delete[] cmd;
01342 return False;
01343 }
01344
01345 Boolean RTSPClient::recordMediaSubsession(MediaSubsession& subsession) {
01346 char* cmd = NULL;
01347 do {
01348
01349 if (subsession.sessionId == NULL) {
01350 envir().setResultMsg(NoSessionErr);
01351 break;
01352 }
01353
01354
01355
01356
01357 char* authenticatorStr
01358 = createAuthenticatorString(&fCurrentAuthenticator,
01359 "RECORD", fBaseURL);
01360
01361 char* const cmdFmt =
01362 "RECORD %s%s%s RTSP/1.0\r\n"
01363 "CSeq: %d\r\n"
01364 "Session: %s\r\n"
01365 "Range: npt=0-\r\n"
01366 "%s"
01367 "%s"
01368 "\r\n";
01369
01370 char const *prefix, *separator, *suffix;
01371 constructSubsessionURL(subsession, prefix, separator, suffix);
01372
01373 unsigned cmdSize = strlen(cmdFmt)
01374 + strlen(prefix) + strlen(separator) + strlen(suffix)
01375 + 20
01376 + strlen(subsession.sessionId)
01377 + strlen(authenticatorStr)
01378 + fUserAgentHeaderStrSize;
01379 cmd = new char[cmdSize];
01380 sprintf(cmd, cmdFmt,
01381 prefix, separator, suffix,
01382 ++fCSeq,
01383 subsession.sessionId,
01384 authenticatorStr,
01385 fUserAgentHeaderStr);
01386 delete[] authenticatorStr;
01387
01388 if (!sendRequest(cmd, "RECORD")) break;
01389
01390
01391 unsigned bytesRead; unsigned responseCode;
01392 char* firstLine; char* nextLineStart;
01393 if (!getResponse("RECORD", bytesRead, responseCode, firstLine, nextLineStart)) break;
01394
01395 delete[] cmd;
01396 return True;
01397 } while (0);
01398
01399 delete[] cmd;
01400 return False;
01401 }
01402
01403 Boolean RTSPClient::setMediaSessionParameter(MediaSession& ,
01404 char const* parameterName,
01405 char const* parameterValue) {
01406 char* cmd = NULL;
01407 do {
01408
01409 if (fLastSessionId == NULL) {
01410 envir().setResultMsg(NoSessionErr);
01411 break;
01412 }
01413
01414
01415
01416
01417 char* authenticatorStr
01418 = createAuthenticatorString(&fCurrentAuthenticator,
01419 "SET_PARAMETER", fBaseURL);
01420
01421 char* const cmdFmt =
01422 "SET_PARAMETER %s RTSP/1.0\r\n"
01423 "CSeq: %d\r\n"
01424 "Session: %s\r\n"
01425 "%s"
01426 "%s"
01427 "%s: %s\r\n"
01428 "\r\n";
01429
01430 unsigned cmdSize = strlen(cmdFmt)
01431 + strlen(fBaseURL)
01432 + 20
01433 + strlen(fLastSessionId)
01434 + strlen(authenticatorStr)
01435 + fUserAgentHeaderStrSize
01436 + strlen(parameterName) + strlen(parameterValue);
01437 cmd = new char[cmdSize];
01438 sprintf(cmd, cmdFmt,
01439 fBaseURL,
01440 ++fCSeq,
01441 fLastSessionId,
01442 authenticatorStr,
01443 fUserAgentHeaderStr,
01444 parameterName, parameterValue);
01445 delete[] authenticatorStr;
01446
01447 if (!sendRequest(cmd, "SET_PARAMETER")) break;
01448
01449
01450 unsigned bytesRead; unsigned responseCode;
01451 char* firstLine; char* nextLineStart;
01452 if (!getResponse("SET_PARAMETER", bytesRead, responseCode, firstLine, nextLineStart)) break;
01453
01454 delete[] cmd;
01455 return True;
01456 } while (0);
01457
01458 delete[] cmd;
01459 return False;
01460 }
01461
01462 Boolean RTSPClient::getMediaSessionParameter(MediaSession& ,
01463 char const* parameterName,
01464 char*& parameterValue) {
01465 parameterValue = NULL;
01466 Boolean const haveParameterName = parameterName != NULL && parameterName[0] != '\0';
01467 char* cmd = NULL;
01468 do {
01469
01470 if (fLastSessionId == NULL) {
01471 envir().setResultMsg(NoSessionErr);
01472 break;
01473 }
01474
01475
01476
01477 char* authenticatorStr
01478 = createAuthenticatorString(&fCurrentAuthenticator,
01479 "GET_PARAMETER", fBaseURL);
01480
01481 if (haveParameterName) {
01482 char* const cmdFmt =
01483 "GET_PARAMETER %s RTSP/1.0\r\n"
01484 "CSeq: %d\r\n"
01485 "Session: %s\r\n"
01486 "%s"
01487 "%s"
01488 "Content-type: text/parameters\r\n"
01489 "Content-length: %d\r\n\r\n"
01490 "%s\r\n"
01491 "\r\n";
01492
01493 unsigned cmdSize = strlen(cmdFmt)
01494 + strlen(fBaseURL)
01495 + 20
01496 + strlen(fLastSessionId)
01497 + strlen(authenticatorStr)
01498 + fUserAgentHeaderStrSize
01499 + strlen(parameterName);
01500 cmd = new char[cmdSize];
01501 sprintf(cmd, cmdFmt,
01502 fBaseURL,
01503 ++fCSeq,
01504 fLastSessionId,
01505 authenticatorStr,
01506 fUserAgentHeaderStr,
01507 strlen(parameterName)+2,
01508 parameterName);
01509 } else {
01510 char* const cmdFmt =
01511 "GET_PARAMETER %s RTSP/1.0\r\n"
01512 "CSeq: %d\r\n"
01513 "Session: %s\r\n"
01514 "%s"
01515 "%s"
01516 "\r\n";
01517
01518 unsigned cmdSize = strlen(cmdFmt)
01519 + strlen(fBaseURL)
01520 + 20
01521 + strlen(fLastSessionId)
01522 + strlen(authenticatorStr)
01523 + fUserAgentHeaderStrSize;
01524 cmd = new char[cmdSize];
01525 sprintf(cmd, cmdFmt,
01526 fBaseURL,
01527 ++fCSeq,
01528 fLastSessionId,
01529 authenticatorStr,
01530 fUserAgentHeaderStr);
01531 }
01532 delete[] authenticatorStr;
01533
01534 if (!sendRequest(cmd, "GET_PARAMETER")) break;
01535
01536
01537
01538 unsigned bytesRead; unsigned responseCode;
01539 char* firstLine; char* nextLineStart;
01540 if (!getResponse("GET_PARAMETER", bytesRead, responseCode, firstLine,
01541 nextLineStart, False )) break;
01542
01543
01544
01545 if (responseCode != 200) {
01546 envir().setResultMsg("cannot handle GET_PARAMETER response: ", firstLine);
01547 break;
01548 }
01549
01550
01551
01552 char* serverType = new char[fResponseBufferSize];
01553 int contentLength = -1;
01554 char* lineStart;
01555 while (1) {
01556 lineStart = nextLineStart;
01557 if (lineStart == NULL) break;
01558
01559 nextLineStart = getLine(lineStart);
01560 if (lineStart[0] == '\0') break;
01561
01562 if (sscanf(lineStart, "Content-Length: %d", &contentLength) == 1
01563 || sscanf(lineStart, "Content-length: %d", &contentLength) == 1) {
01564 if (contentLength < 0) {
01565 envir().setResultMsg("Bad \"Content-length:\" header: \"",
01566 lineStart, "\"");
01567 break;
01568 }
01569 }
01570 }
01571 delete[] serverType;
01572
01573
01574 if (lineStart == NULL) {
01575 envir().setResultMsg("no content following header lines: ",
01576 fResponseBuffer);
01577 break;
01578 }
01579
01580
01581
01582
01583 char* bodyStart = nextLineStart;
01584 if (contentLength >= 0) {
01585
01586 unsigned numBodyBytes = &firstLine[bytesRead] - bodyStart;
01587 if (contentLength > (int)numBodyBytes) {
01588
01589
01590 unsigned numExtraBytesNeeded = contentLength - numBodyBytes;
01591 unsigned remainingBufferSize
01592 = fResponseBufferSize - (bytesRead + (firstLine - fResponseBuffer));
01593 if (numExtraBytesNeeded > remainingBufferSize) {
01594 char tmpBuf[200];
01595 sprintf(tmpBuf, "Read buffer size (%d) is too small for \"Content-length:\" %d (need a buffer size of >= %d bytes\n",
01596 fResponseBufferSize, contentLength,
01597 fResponseBufferSize + numExtraBytesNeeded - remainingBufferSize);
01598 envir().setResultMsg(tmpBuf);
01599 break;
01600 }
01601
01602
01603 if (fVerbosityLevel >= 1) {
01604 envir() << "Need to read " << numExtraBytesNeeded
01605 << " extra bytes\n";
01606 }
01607 while (numExtraBytesNeeded > 0) {
01608 struct sockaddr_in fromAddress;
01609 char* ptr = &firstLine[bytesRead];
01610 int bytesRead2 = readSocket(envir(), fInputSocketNum, (unsigned char*)ptr,
01611 numExtraBytesNeeded, fromAddress);
01612 if (bytesRead2 < 0) break;
01613 ptr[bytesRead2] = '\0';
01614 if (fVerbosityLevel >= 1) {
01615 envir() << "Read " << bytesRead2 << " extra bytes: "
01616 << ptr << "\n";
01617 }
01618
01619 bytesRead += bytesRead2;
01620 numExtraBytesNeeded -= bytesRead2;
01621 }
01622 if (numExtraBytesNeeded > 0) break;
01623 }
01624 }
01625
01626 if (haveParameterName
01627 && !parseGetParameterHeader(bodyStart, parameterName, parameterValue)) break;
01628
01629 delete[] cmd;
01630 return True;
01631 } while (0);
01632
01633 delete[] cmd;
01634 return False;
01635 }
01636
01637 Boolean RTSPClient::teardownMediaSession(MediaSession& session) {
01638 char* cmd = NULL;
01639 do {
01640
01641 if (fLastSessionId == NULL) {
01642 envir().setResultMsg(NoSessionErr);
01643 break;
01644 }
01645
01646
01647
01648
01649 char* authenticatorStr
01650 = createAuthenticatorString(&fCurrentAuthenticator,
01651 "TEARDOWN", fBaseURL);
01652
01653 char* const cmdFmt =
01654 "TEARDOWN %s RTSP/1.0\r\n"
01655 "CSeq: %d\r\n"
01656 "Session: %s\r\n"
01657 "%s"
01658 "%s"
01659 "\r\n";
01660
01661 unsigned cmdSize = strlen(cmdFmt)
01662 + strlen(fBaseURL)
01663 + 20
01664 + strlen(fLastSessionId)
01665 + strlen(authenticatorStr)
01666 + fUserAgentHeaderStrSize;
01667 cmd = new char[cmdSize];
01668 sprintf(cmd, cmdFmt,
01669 fBaseURL,
01670 ++fCSeq,
01671 fLastSessionId,
01672 authenticatorStr,
01673 fUserAgentHeaderStr);
01674 delete[] authenticatorStr;
01675
01676 if (!sendRequest(cmd, "TEARDOWN")) break;
01677
01678 if (fTCPStreamIdCount == 0) {
01679
01680 unsigned bytesRead; unsigned responseCode;
01681 char* firstLine; char* nextLineStart;
01682 if (!getResponse("TEARDOWN", bytesRead, responseCode, firstLine, nextLineStart)) break;
01683
01684
01685 MediaSubsessionIterator iter(session);
01686 MediaSubsession* subsession;
01687 while ((subsession = iter.next()) != NULL) {
01688 delete[] (char*)subsession->sessionId;
01689 subsession->sessionId = NULL;
01690 }
01691
01692 delete[] fLastSessionId; fLastSessionId = NULL;
01693
01694 }
01695
01696 delete[] cmd;
01697 return True;
01698 } while (0);
01699
01700 delete[] cmd;
01701 return False;
01702 }
01703
01704 Boolean RTSPClient::teardownMediaSubsession(MediaSubsession& subsession) {
01705 char* cmd = NULL;
01706 do {
01707
01708 if (subsession.sessionId == NULL) {
01709 envir().setResultMsg(NoSessionErr);
01710 break;
01711 }
01712
01713
01714
01715
01716 char* authenticatorStr
01717 = createAuthenticatorString(&fCurrentAuthenticator,
01718 "TEARDOWN", fBaseURL);
01719
01720 char* const cmdFmt =
01721 "TEARDOWN %s%s%s RTSP/1.0\r\n"
01722 "CSeq: %d\r\n"
01723 "Session: %s\r\n"
01724 "%s"
01725 "%s"
01726 "\r\n";
01727
01728 char const *prefix, *separator, *suffix;
01729 constructSubsessionURL(subsession, prefix, separator, suffix);
01730
01731 unsigned cmdSize = strlen(cmdFmt)
01732 + strlen(prefix) + strlen(separator) + strlen(suffix)
01733 + 20
01734 + strlen(subsession.sessionId)
01735 + strlen(authenticatorStr)
01736 + fUserAgentHeaderStrSize;
01737 cmd = new char[cmdSize];
01738 sprintf(cmd, cmdFmt,
01739 prefix, separator, suffix,
01740 ++fCSeq,
01741 subsession.sessionId,
01742 authenticatorStr,
01743 fUserAgentHeaderStr);
01744 delete[] authenticatorStr;
01745
01746 if (!sendRequest(cmd, "TEARDOWN")) break;
01747
01748 if (fTCPStreamIdCount == 0) {
01749
01750 unsigned bytesRead; unsigned responseCode;
01751 char* firstLine; char* nextLineStart;
01752 if (!getResponse("TEARDOWN", bytesRead, responseCode, firstLine, nextLineStart)) break;
01753 }
01754
01755 delete[] (char*)subsession.sessionId;
01756 subsession.sessionId = NULL;
01757
01758
01759 delete[] cmd;
01760 return True;
01761 } while (0);
01762
01763 delete[] cmd;
01764 return False;
01765 }
01766
01767 Boolean RTSPClient
01768 ::openConnectionFromURL(char const* url, Authenticator* authenticator) {
01769 do {
01770
01771 delete[] fBaseURL; fBaseURL = strDup(url); if (fBaseURL == NULL) break;
01772
01773
01774
01775 NetAddress destAddress;
01776 portNumBits urlPortNum;
01777 char const* urlSuffix;
01778 if (!parseRTSPURL(envir(), url, destAddress, urlPortNum, &urlSuffix)) break;
01779 portNumBits destPortNum
01780 = fTunnelOverHTTPPortNum == 0 ? urlPortNum : fTunnelOverHTTPPortNum;
01781
01782 if (fInputSocketNum < 0) {
01783
01784 fInputSocketNum = fOutputSocketNum
01785 = setupStreamSocket(envir(), 0, False );
01786 if (fInputSocketNum < 0) break;
01787
01788
01789 fServerAddress = *(unsigned*)(destAddress.data());
01790 MAKE_SOCKADDR_IN(remoteName, fServerAddress, htons(destPortNum));
01791 if (connect(fInputSocketNum, (struct sockaddr*)&remoteName, sizeof remoteName)
01792 != 0) {
01793 envir().setResultErrMsg("connect() failed: ");
01794 break;
01795 }
01796
01797 if (fTunnelOverHTTPPortNum != 0 && !setupHTTPTunneling(urlSuffix, authenticator)) break;
01798 }
01799
01800 return True;
01801 } while (0);
01802
01803 fDescribeStatusCode = 1;
01804 resetTCPSockets();
01805 return False;
01806 }
01807
01808 Boolean RTSPClient::parseRTSPURL(UsageEnvironment& env, char const* url,
01809 NetAddress& address,
01810 portNumBits& portNum,
01811 char const** urlSuffix) {
01812 do {
01813
01814
01815
01816 char const* prefix = "rtsp://";
01817 unsigned const prefixLength = 7;
01818 if (_strncasecmp(url, prefix, prefixLength) != 0) {
01819 env.setResultMsg("URL is not of the form \"", prefix, "\"");
01820 break;
01821 }
01822
01823 unsigned const parseBufferSize = 100;
01824 char parseBuffer[parseBufferSize];
01825 char const* from = &url[prefixLength];
01826
01827
01828
01829
01830
01831 char const* from1 = from;
01832 while (*from1 != '\0' && *from1 != '/') {
01833 if (*from1 == '@') {
01834 from = ++from1;
01835 break;
01836 }
01837 ++from1;
01838 }
01839
01840 char* to = &parseBuffer[0];
01841 unsigned i;
01842 for (i = 0; i < parseBufferSize; ++i) {
01843 if (*from == '\0' || *from == ':' || *from == '/') {
01844
01845 *to = '\0';
01846 break;
01847 }
01848 *to++ = *from++;
01849 }
01850 if (i == parseBufferSize) {
01851 env.setResultMsg("URL is too long");
01852 break;
01853 }
01854
01855 NetAddressList addresses(parseBuffer);
01856 if (addresses.numAddresses() == 0) {
01857 env.setResultMsg("Failed to find network address for \"",
01858 parseBuffer, "\"");
01859 break;
01860 }
01861 address = *(addresses.firstAddress());
01862
01863 portNum = 554;
01864 char nextChar = *from;
01865 if (nextChar == ':') {
01866 int portNumInt;
01867 if (sscanf(++from, "%d", &portNumInt) != 1) {
01868 env.setResultMsg("No port number follows ':'");
01869 break;
01870 }
01871 if (portNumInt < 1 || portNumInt > 65535) {
01872 env.setResultMsg("Bad port number");
01873 break;
01874 }
01875 portNum = (portNumBits)portNumInt;
01876 while (*from >= '0' && *from <= '9') ++from;
01877 }
01878
01879
01880 if (urlSuffix != NULL) *urlSuffix = from;
01881
01882 return True;
01883 } while (0);
01884
01885 return False;
01886 }
01887
01888 Boolean RTSPClient::parseRTSPURLUsernamePassword(char const* url,
01889 char*& username,
01890 char*& password) {
01891 username = password = NULL;
01892 do {
01893
01894 char const* prefix = "rtsp://";
01895 unsigned const prefixLength = 7;
01896 if (_strncasecmp(url, prefix, prefixLength) != 0) break;
01897
01898
01899 unsigned usernameIndex = prefixLength;
01900 unsigned colonIndex = 0, atIndex = 0;
01901 for (unsigned i = usernameIndex; url[i] != '\0' && url[i] != '/'; ++i) {
01902 if (url[i] == ':' && colonIndex == 0) {
01903 colonIndex = i;
01904 } else if (url[i] == '@') {
01905 atIndex = i;
01906 break;
01907 }
01908 }
01909 if (atIndex == 0) break;
01910
01911 char* urlCopy = strDup(url);
01912 urlCopy[atIndex] = '\0';
01913 if (colonIndex > 0) {
01914 urlCopy[colonIndex] = '\0';
01915 password = strDup(&urlCopy[colonIndex+1]);
01916 } else {
01917 password = strDup("");
01918 }
01919 username = strDup(&urlCopy[usernameIndex]);
01920 delete[] urlCopy;
01921
01922 return True;
01923 } while (0);
01924
01925 return False;
01926 }
01927
01928 char*
01929 RTSPClient::createAuthenticatorString(Authenticator const* authenticator,
01930 char const* cmd, char const* url) {
01931 if (authenticator != NULL && authenticator->realm() != NULL
01932 && authenticator->username() != NULL && authenticator->password() != NULL) {
01933
01934 char* authenticatorStr;
01935 if (authenticator->nonce() != NULL) {
01936 char* const authFmt =
01937 "Authorization: Digest username=\"%s\", realm=\"%s\", "
01938 "nonce=\"%s\", uri=\"%s\", response=\"%s\"\r\n";
01939 char const* response = authenticator->computeDigestResponse(cmd, url);
01940 unsigned authBufSize = strlen(authFmt)
01941 + strlen(authenticator->username()) + strlen(authenticator->realm())
01942 + strlen(authenticator->nonce()) + strlen(url) + strlen(response);
01943 authenticatorStr = new char[authBufSize];
01944 sprintf(authenticatorStr, authFmt,
01945 authenticator->username(), authenticator->realm(),
01946 authenticator->nonce(), url, response);
01947 authenticator->reclaimDigestResponse(response);
01948 } else {
01949 char* const authFmt = "Authorization: Basic %s\r\n";
01950 char* usernamePassword
01951 = new char[strlen(authenticator->username())
01952 + strlen(authenticator->password()) + 2];
01953 sprintf(usernamePassword, "%s:%s",
01954 authenticator->username(), authenticator->password());
01955 char* response = base64Encode(usernamePassword);
01956 unsigned authBufSize = strlen(authFmt) + strlen(response);
01957 authenticatorStr = new char[authBufSize];
01958 sprintf(authenticatorStr, authFmt, response);
01959 delete[] response; delete[] usernamePassword;
01960 }
01961
01962 return authenticatorStr;
01963 }
01964
01965 return strDup("");
01966 }
01967
01968 void RTSPClient::checkForAuthenticationFailure(unsigned responseCode,
01969 char*& nextLineStart,
01970 Authenticator* authenticator) {
01971 if (responseCode == 401 && authenticator != NULL) {
01972
01973
01974
01975
01976 char* lineStart;
01977 while (1) {
01978 lineStart = nextLineStart;
01979 if (lineStart == NULL) break;
01980
01981 nextLineStart = getLine(lineStart);
01982 if (lineStart[0] == '\0') break;
01983
01984 char* realm = strDupSize(lineStart);
01985 char* nonce = strDupSize(lineStart);
01986 Boolean foundAuthenticateHeader = False;
01987 if (sscanf(lineStart, "WWW-Authenticate: Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"",
01988 realm, nonce) == 2) {
01989 authenticator->setRealmAndNonce(realm, nonce);
01990 foundAuthenticateHeader = True;
01991 } else if (sscanf(lineStart, "WWW-Authenticate: Basic realm=\"%[^\"]\"",
01992 realm) == 1) {
01993 authenticator->setRealmAndNonce(realm, NULL);
01994 foundAuthenticateHeader = True;
01995 }
01996 delete[] realm; delete[] nonce;
01997 if (foundAuthenticateHeader) break;
01998 }
01999 }
02000 }
02001
02002 Boolean RTSPClient::sendRequest(char const* requestString, char const* tag,
02003 Boolean base64EncodeIfOverHTTP) {
02004 if (fVerbosityLevel >= 1) {
02005 envir() << "Sending request: " << requestString << "\n";
02006 }
02007
02008 char* newRequestString = NULL;
02009 if (fTunnelOverHTTPPortNum != 0 && base64EncodeIfOverHTTP) {
02010 requestString = newRequestString = base64Encode(requestString);
02011 if (fVerbosityLevel >= 1) {
02012 envir() << "\tThe request was base-64 encoded to: " << requestString << "\n\n";
02013 }
02014 }
02015
02016 Boolean result
02017 = send(fOutputSocketNum, requestString, strlen(requestString), 0) >= 0;
02018 delete[] newRequestString;
02019
02020 if (!result) {
02021 if (tag == NULL) tag = "";
02022 char const* errFmt = "%s send() failed: ";
02023 unsigned const errLength = strlen(errFmt) + strlen(tag);
02024 char* err = new char[errLength];
02025 sprintf(err, errFmt, tag);
02026 envir().setResultErrMsg(err);
02027 delete[] err;
02028 }
02029 return result;
02030 }
02031
02032 Boolean RTSPClient::getResponse(char const* tag,
02033 unsigned& bytesRead, unsigned& responseCode,
02034 char*& firstLine, char*& nextLineStart,
02035 Boolean checkFor200Response) {
02036 do {
02037 char* readBuf = fResponseBuffer;
02038 bytesRead = getResponse1(readBuf, fResponseBufferSize);
02039 if (bytesRead == 0) {
02040 envir().setResultErrMsg("Failed to read response: ");
02041 break;
02042 }
02043 if (fVerbosityLevel >= 1) {
02044 envir() << "Received " << tag << " response: " << readBuf << "\n";
02045 }
02046
02047 firstLine = readBuf;
02048 nextLineStart = getLine(firstLine);
02049 if (!parseResponseCode(firstLine, responseCode)) break;
02050
02051
02052 if (responseCode != 200 && checkFor200Response) {
02053 envir().setResultMsg(tag, ": cannot handle response: ", firstLine);
02054 break;
02055 }
02056
02057 return True;
02058 } while (0);
02059
02060
02061 return False;
02062 }
02063
02064 unsigned RTSPClient::getResponse1(char*& responseBuffer,
02065 unsigned responseBufferSize) {
02066 struct sockaddr_in fromAddress;
02067
02068 if (responseBufferSize == 0) return 0;
02069 responseBuffer[0] = '\0';
02070
02071
02072
02073
02074 Boolean success = False;
02075 while (1) {
02076 unsigned char firstByte;
02077 if (readSocket(envir(), fInputSocketNum, &firstByte, 1, fromAddress)
02078 != 1) break;
02079 if (firstByte != '$') {
02080
02081 responseBuffer[0] = firstByte;
02082 success = True;
02083 break;
02084 } else {
02085
02086 unsigned char streamChannelId;
02087 if (readSocket(envir(), fInputSocketNum, &streamChannelId, 1, fromAddress)
02088 != 1) break;
02089
02090 unsigned short size;
02091 if (readSocketExact(envir(), fInputSocketNum, (unsigned char*)&size, 2,
02092 fromAddress) != 2) break;
02093 size = ntohs(size);
02094 if (fVerbosityLevel >= 1) {
02095 envir() << "Discarding interleaved RTP or RTCP packet ("
02096 << size << " bytes, channel id "
02097 << streamChannelId << ")\n";
02098 }
02099
02100 unsigned char* tmpBuffer = new unsigned char[size];
02101 if (tmpBuffer == NULL) break;
02102 unsigned bytesRead = 0;
02103 unsigned bytesToRead = size;
02104 unsigned curBytesRead;
02105 while ((curBytesRead = readSocket(envir(), fInputSocketNum,
02106 &tmpBuffer[bytesRead], bytesToRead,
02107 fromAddress)) > 0) {
02108 bytesRead += curBytesRead;
02109 if (bytesRead >= size) break;
02110 bytesToRead -= curBytesRead;
02111 }
02112 delete[] tmpBuffer;
02113 if (bytesRead != size) break;
02114
02115 success = True;
02116 }
02117 }
02118 if (!success) return 0;
02119
02120
02121
02122
02123 char* p = responseBuffer;
02124 Boolean haveSeenNonCRLF = False;
02125 int bytesRead = 1;
02126 while (bytesRead < (int)responseBufferSize) {
02127 int bytesReadNow
02128 = readSocket(envir(), fInputSocketNum,
02129 (unsigned char*)(responseBuffer+bytesRead),
02130 1, fromAddress);
02131 if (bytesReadNow <= 0) {
02132 envir().setResultMsg("RTSP response was truncated");
02133 break;
02134 }
02135 bytesRead += bytesReadNow;
02136
02137
02138 char* lastToCheck = responseBuffer+bytesRead-4;
02139 if (lastToCheck < responseBuffer) continue;
02140 for (; p <= lastToCheck; ++p) {
02141 if (haveSeenNonCRLF) {
02142 if (*p == '\r' && *(p+1) == '\n' &&
02143 *(p+2) == '\r' && *(p+3) == '\n') {
02144 responseBuffer[bytesRead] = '\0';
02145
02146
02147 while (*responseBuffer == '\r' || *responseBuffer == '\n') {
02148 ++responseBuffer;
02149 --bytesRead;
02150 }
02151 return bytesRead;
02152 }
02153 } else {
02154 if (*p != '\r' && *p != '\n') {
02155 haveSeenNonCRLF = True;
02156 }
02157 }
02158 }
02159 }
02160
02161 envir().setResultMsg("We received a response not ending with <CR><LF><CR><LF>");
02162 return 0;
02163 }
02164
02165 Boolean RTSPClient::parseResponseCode(char const* line,
02166 unsigned& responseCode) {
02167 if (sscanf(line, "%*s%u", &responseCode) != 1) {
02168 envir().setResultMsg("no response code in line: \"", line, "\"");
02169 return False;
02170 }
02171
02172 return True;
02173 }
02174
02175 Boolean RTSPClient::parseTransportResponse(char const* line,
02176 char*& serverAddressStr,
02177 portNumBits& serverPortNum,
02178 unsigned char& rtpChannelId,
02179 unsigned char& rtcpChannelId) {
02180
02181 serverAddressStr = NULL;
02182 serverPortNum = 0;
02183 rtpChannelId = rtcpChannelId = 0xFF;
02184
02185 char* foundServerAddressStr = NULL;
02186 Boolean foundServerPortNum = False;
02187 Boolean foundChannelIds = False;
02188 unsigned rtpCid, rtcpCid;
02189 Boolean isMulticast = True;
02190 char* foundDestinationStr = NULL;
02191 portNumBits multicastPortNumRTP, multicastPortNumRTCP;
02192 Boolean foundMulticastPortNum = False;
02193
02194
02195 if (_strncasecmp(line, "Transport: ", 11) != 0) return False;
02196 line += 11;
02197
02198
02199 char const* fields = line;
02200 char* field = strDupSize(fields);
02201 while (sscanf(fields, "%[^;]", field) == 1) {
02202 if (sscanf(field, "server_port=%hu", &serverPortNum) == 1) {
02203 foundServerPortNum = True;
02204 } else if (_strncasecmp(field, "source=", 7) == 0) {
02205 delete[] foundServerAddressStr;
02206 foundServerAddressStr = strDup(field+7);
02207 } else if (sscanf(field, "interleaved=%u-%u", &rtpCid, &rtcpCid) == 2) {
02208 rtpChannelId = (unsigned char)rtpCid;
02209 rtcpChannelId = (unsigned char)rtcpCid;
02210 foundChannelIds = True;
02211 } else if (strcmp(field, "unicast") == 0) {
02212 isMulticast = False;
02213 } else if (_strncasecmp(field, "destination=", 12) == 0) {
02214 delete[] foundDestinationStr;
02215 foundDestinationStr = strDup(field+12);
02216 } else if (sscanf(field, "port=%hu-%hu",
02217 &multicastPortNumRTP, &multicastPortNumRTCP) == 2) {
02218 foundMulticastPortNum = True;
02219 }
02220
02221 fields += strlen(field);
02222 while (fields[0] == ';') ++fields;
02223 if (fields[0] == '\0') break;
02224 }
02225 delete[] field;
02226
02227
02228
02229
02230 if (isMulticast && foundDestinationStr != NULL && foundMulticastPortNum) {
02231 delete[] foundServerAddressStr;
02232 serverAddressStr = foundDestinationStr;
02233 serverPortNum = multicastPortNumRTP;
02234 return True;
02235 }
02236 delete[] foundDestinationStr;
02237
02238 if (foundServerPortNum || foundChannelIds) {
02239 serverAddressStr = foundServerAddressStr;
02240 return True;
02241 }
02242
02243 delete[] foundServerAddressStr;
02244 return False;
02245 }
02246
02247 Boolean RTSPClient::parseRTPInfoHeader(char const* line,
02248 unsigned& trackId,
02249 u_int16_t& seqNum,
02250 u_int32_t& timestamp) {
02251 if (_strncasecmp(line, "RTP-Info: ", 10) != 0) return False;
02252 line += 10;
02253 char const* fields = line;
02254 char* field = strDupSize(fields);
02255
02256 while (sscanf(fields, "%[^;]", field) == 1) {
02257 if (sscanf(field, "url=trackID=%u", &trackId) == 1 ||
02258 sscanf(field, "url=trackid=%u", &trackId) == 1 ||
02259 sscanf(field, "seq=%hu", &seqNum) == 1 ||
02260 sscanf(field, "rtptime=%u", ×tamp) == 1) {
02261 }
02262
02263 fields += strlen(field);
02264 if (fields[0] == '\0') break;
02265 ++fields;
02266 }
02267
02268 delete[] field;
02269 return True;
02270 }
02271
02272 Boolean RTSPClient::parseScaleHeader(char const* line, float& scale) {
02273 if (_strncasecmp(line, "Scale: ", 7) != 0) return False;
02274 line += 7;
02275
02276 Locale("POSIX");
02277 return sscanf(line, "%f", &scale) == 1;
02278 }
02279
02280 Boolean RTSPClient::parseGetParameterHeader(char const* line,
02281 const char* param,
02282 char*& value) {
02283 if ((param != NULL && param[0] != '\0') &&
02284 (line != NULL && line[0] != '\0')) {
02285 int param_len = strlen(param);
02286 int line_len = strlen(line);
02287
02288 if (_strncasecmp(line, param, param_len) != 0) {
02289 if (fVerbosityLevel >= 1) {
02290 envir() << "Parsing for \""<< param << "\" and didn't find it, return False\n";
02291 }
02292 return False;
02293 }
02294
02295
02296 if (line[line_len-2] == '\r' && line[line_len-1] == '\n') {
02297 line_len -= 2;
02298 }
02299
02300
02301 if (line[param_len] == ':' && line[param_len+1] == ' ') {
02302
02303 if (param[param_len-2] != ':' && param[param_len-1] != ' ') {
02304 if (fVerbosityLevel >= 1) {
02305 envir() << "Found \": \" appended to parameter\n";
02306 }
02307 param_len += 2;
02308 }
02309 }
02310
02311
02312 value = strDup(line+param_len);
02313 return True;
02314 }
02315 return False;
02316 }
02317
02318 Boolean RTSPClient::setupHTTPTunneling(char const* urlSuffix,
02319 Authenticator* authenticator) {
02320 if (fVerbosityLevel >= 1) {
02321 envir() << "Requesting RTSP-over-HTTP tunneling (on port "
02322 << fTunnelOverHTTPPortNum << ")\n\n";
02323 }
02324 if (urlSuffix == NULL || urlSuffix[0] == '\0') urlSuffix = "/";
02325 char* cmd = NULL;
02326
02327 do {
02328
02329 struct {
02330 struct timeval timestamp;
02331 unsigned counter;
02332 } seedData;
02333 gettimeofday(&seedData.timestamp, NULL);
02334 static unsigned counter = 0;
02335 seedData.counter = ++counter;
02336 char sessionCookie[33];
02337 our_MD5Data((unsigned char*)(&seedData), sizeof seedData, sessionCookie);
02338
02339 sessionCookie[23] = '\0';
02340
02341
02342 char* authenticatorStr
02343 = createAuthenticatorString(authenticator, "GET", urlSuffix);
02344
02345
02346 char* const getCmdFmt =
02347 "GET %s HTTP/1.0\r\n"
02348 "%s"
02349 "%s"
02350 "x-sessioncookie: %s\r\n"
02351 "Accept: application/x-rtsp-tunnelled\r\n"
02352 "Pragma: no-cache\r\n"
02353 "Cache-Control: no-cache\r\n"
02354 "\r\n";
02355 unsigned cmdSize = strlen(getCmdFmt)
02356 + strlen(urlSuffix)
02357 + strlen(authenticatorStr)
02358 + fUserAgentHeaderStrSize
02359 + strlen(sessionCookie);
02360 cmd = new char[cmdSize];
02361 sprintf(cmd, getCmdFmt,
02362 urlSuffix,
02363 authenticatorStr,
02364 fUserAgentHeaderStr,
02365 sessionCookie);
02366 delete[] authenticatorStr;
02367 if (!sendRequest(cmd, "HTTP GET", False)) break;
02368
02369
02370 unsigned bytesRead; unsigned responseCode;
02371 char* firstLine; char* nextLineStart;
02372 if (!getResponse("HTTP GET", bytesRead, responseCode, firstLine, nextLineStart,
02373 False )) break;
02374 if (responseCode != 200) {
02375 checkForAuthenticationFailure(responseCode, nextLineStart, authenticator);
02376 envir().setResultMsg("cannot handle HTTP GET response: ", firstLine);
02377 break;
02378 }
02379
02380
02381
02382
02383 fOutputSocketNum = setupStreamSocket(envir(), 0, False );
02384 if (fOutputSocketNum < 0) break;
02385
02386
02387 MAKE_SOCKADDR_IN(remoteName, fServerAddress, htons(fTunnelOverHTTPPortNum));
02388 if (connect(fOutputSocketNum,
02389 (struct sockaddr*)&remoteName, sizeof remoteName) != 0) {
02390 envir().setResultErrMsg("connect() failed: ");
02391 break;
02392 }
02393
02394
02395 authenticatorStr = createAuthenticatorString(authenticator, "POST", urlSuffix);
02396 char* const postCmdFmt =
02397 "POST %s HTTP/1.0\r\n"
02398 "%s"
02399 "%s"
02400 "x-sessioncookie: %s\r\n"
02401 "Content-Type: application/x-rtsp-tunnelled\r\n"
02402 "Pragma: no-cache\r\n"
02403 "Cache-Control: no-cache\r\n"
02404 "Content-Length: 32767\r\n"
02405 "Expires: Sun, 9 Jan 1972 00:00:00 GMT\r\n"
02406 "\r\n";
02407 cmdSize = strlen(postCmdFmt)
02408 + strlen(urlSuffix)
02409 + strlen(authenticatorStr)
02410 + fUserAgentHeaderStrSize
02411 + strlen(sessionCookie);
02412 delete[] cmd; cmd = new char[cmdSize];
02413 sprintf(cmd, postCmdFmt,
02414 urlSuffix,
02415 authenticatorStr,
02416 fUserAgentHeaderStr,
02417 sessionCookie);
02418 delete[] authenticatorStr;
02419 if (!sendRequest(cmd, "HTTP POST", False)) break;
02420
02421
02422
02423 delete[] cmd;
02424 return True;
02425 } while (0);
02426
02427
02428 delete[] cmd;
02429 return False;
02430 }
02431
02432 void RTSPClient::incomingRequestHandler(void* instance, int ) {
02433 RTSPClient* session = (RTSPClient*)instance;
02434 session->incomingRequestHandler1();
02435 }
02436
02437 void RTSPClient::incomingRequestHandler1() {
02438 unsigned bytesRead;
02439 char* readBuf = fResponseBuffer;
02440 bytesRead = getResponse1(readBuf, fResponseBufferSize);
02441 if (bytesRead == 0) {
02442 envir().setResultErrMsg("Failed to read response: ");
02443 return;
02444 }
02445
02446
02447 char cmdName[RTSP_PARAM_STRING_MAX];
02448 char urlPreSuffix[RTSP_PARAM_STRING_MAX];
02449 char urlSuffix[RTSP_PARAM_STRING_MAX];
02450 char cseq[RTSP_PARAM_STRING_MAX];
02451 if (!parseRTSPRequestString((char*)readBuf, bytesRead,
02452 cmdName, sizeof cmdName,
02453 urlPreSuffix, sizeof urlPreSuffix,
02454 urlSuffix, sizeof urlSuffix,
02455 cseq, sizeof cseq)) {
02456 return;
02457 } else {
02458 if (fVerbosityLevel >= 1) {
02459 envir() << "Received request: " << readBuf << "\n";
02460 }
02461 handleCmd_notSupported(cseq);
02462 }
02463 }
02464
02465 void RTSPClient::handleCmd_notSupported(char const* cseq) {
02466 char tmpBuf[512];
02467 snprintf((char*)tmpBuf, sizeof tmpBuf,
02468 "RTSP/1.0 405 Method Not Allowed\r\nCSeq: %s\r\n\r\n", cseq);
02469 send(fOutputSocketNum, tmpBuf, strlen(tmpBuf), 0);
02470 }