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 "Locale.hh"
00025 #include <GroupsockHelper.hh>
00026 #include "our_md5.h"
00027 #ifdef SUPPORT_REAL_RTSP
00028 #include "../RealRTSP/include/RealRTSP.hh"
00029 #endif
00030
00032
00033 RTSPClient* RTSPClient::createNew(UsageEnvironment& env,
00034 int verbosityLevel,
00035 char const* applicationName,
00036 portNumBits tunnelOverHTTPPortNum) {
00037 return new RTSPClient(env, verbosityLevel,
00038 applicationName, tunnelOverHTTPPortNum);
00039 }
00040
00041 Boolean RTSPClient::lookupByName(UsageEnvironment& env,
00042 char const* instanceName,
00043 RTSPClient*& resultClient) {
00044 resultClient = NULL;
00045
00046 Medium* medium;
00047 if (!Medium::lookupByName(env, instanceName, medium)) return False;
00048
00049 if (!medium->isRTSPClient()) {
00050 env.setResultMsg(instanceName, " is not a RTSP client");
00051 return False;
00052 }
00053
00054 resultClient = (RTSPClient*)medium;
00055 return True;
00056 }
00057
00058 unsigned RTSPClient::fCSeq = 0;
00059
00060 RTSPClient::RTSPClient(UsageEnvironment& env,
00061 int verbosityLevel, char const* applicationName,
00062 portNumBits tunnelOverHTTPPortNum)
00063 : Medium(env),
00064 fVerbosityLevel(verbosityLevel),
00065 fTunnelOverHTTPPortNum(tunnelOverHTTPPortNum),
00066 fInputSocketNum(-1), fOutputSocketNum(-1), fServerAddress(0),
00067 fBaseURL(NULL), fTCPStreamIdCount(0), fLastSessionId(NULL),
00068 fSessionTimeoutParameter(0),
00069 #ifdef SUPPORT_REAL_RTSP
00070 fRealChallengeStr(NULL), fRealETagStr(NULL),
00071 #endif
00072 fServerIsKasenna(False), fKasennaContentType(NULL),
00073 fServerIsMicrosoft(False)
00074 {
00075 fResponseBufferSize = 20000;
00076 fResponseBuffer = new char[fResponseBufferSize+1];
00077
00078
00079 char const* const libName = "LIVE555 Streaming Media v";
00080 char const* const libVersionStr = LIVEMEDIA_LIBRARY_VERSION_STRING;
00081 char const* libPrefix; char const* libSuffix;
00082 if (applicationName == NULL || applicationName[0] == '\0') {
00083 applicationName = libPrefix = libSuffix = "";
00084 } else {
00085 libPrefix = " (";
00086 libSuffix = ")";
00087 }
00088 char const* const formatStr = "User-Agent: %s%s%s%s%s\r\n";
00089 unsigned headerSize
00090 = strlen(formatStr) + strlen(applicationName) + strlen(libPrefix)
00091 + strlen(libName) + strlen(libVersionStr) + strlen(libSuffix);
00092 fUserAgentHeaderStr = new char[headerSize];
00093 sprintf(fUserAgentHeaderStr, formatStr,
00094 applicationName, libPrefix, libName, libVersionStr, libSuffix);
00095 fUserAgentHeaderStrSize = strlen(fUserAgentHeaderStr);
00096 }
00097
00098 void RTSPClient::setUserAgentString(char const* userAgentStr) {
00099 if (userAgentStr == NULL) return;
00100
00101
00102 char const* const formatStr = "User-Agent: %s\r\n";
00103 unsigned const headerSize = strlen(formatStr) + strlen(userAgentStr) + 1;
00104 delete[] fUserAgentHeaderStr;
00105 fUserAgentHeaderStr = new char[headerSize];
00106 sprintf(fUserAgentHeaderStr, formatStr, userAgentStr);
00107 fUserAgentHeaderStrSize = strlen(fUserAgentHeaderStr);
00108 }
00109
00110 RTSPClient::~RTSPClient() {
00111 envir().taskScheduler().turnOffBackgroundReadHandling(fInputSocketNum);
00112 reset();
00113
00114 delete[] fResponseBuffer;
00115 delete[] fUserAgentHeaderStr;
00116 }
00117
00118 Boolean RTSPClient::isRTSPClient() const {
00119 return True;
00120 }
00121
00122 void RTSPClient::resetTCPSockets() {
00123 if (fInputSocketNum >= 0) {
00124 ::closeSocket(fInputSocketNum);
00125 if (fOutputSocketNum != fInputSocketNum) ::closeSocket(fOutputSocketNum);
00126 }
00127 fInputSocketNum = fOutputSocketNum = -1;
00128 }
00129
00130 void RTSPClient::reset() {
00131 resetTCPSockets();
00132 fServerAddress = 0;
00133
00134 delete[] fBaseURL; fBaseURL = NULL;
00135
00136 fCurrentAuthenticator.reset();
00137
00138 delete[] fKasennaContentType; fKasennaContentType = NULL;
00139 #ifdef SUPPORT_REAL_RTSP
00140 delete[] fRealChallengeStr; fRealChallengeStr = NULL;
00141 delete[] fRealETagStr; fRealETagStr = NULL;
00142 #endif
00143 delete[] fLastSessionId; fLastSessionId = NULL;
00144 }
00145
00146 static char* getLine(char* startOfLine) {
00147
00148 for (char* ptr = startOfLine; *ptr != '\0'; ++ptr) {
00149
00150 if (*ptr == '\r' || *ptr == '\n') {
00151
00152 if (*ptr == '\r') {
00153 *ptr++ = '\0';
00154 if (*ptr == '\n') ++ptr;
00155 } else {
00156 *ptr++ = '\0';
00157 }
00158 return ptr;
00159 }
00160 }
00161
00162 return NULL;
00163 }
00164
00165 char* RTSPClient::describeURL(char const* url, Authenticator* authenticator,
00166 Boolean allowKasennaProtocol) {
00167 char* cmd = NULL;
00168 fDescribeStatusCode = 0;
00169 do {
00170
00171 char* username; char* password;
00172 if (authenticator == NULL
00173 && parseRTSPURLUsernamePassword(url, username, password)) {
00174 char* result = describeWithPassword(url, username, password, allowKasennaProtocol);
00175 delete[] username; delete[] password;
00176 return result;
00177 }
00178
00179 if (!openConnectionFromURL(url, authenticator)) break;
00180
00181
00182
00183
00184 fCurrentAuthenticator.reset();
00185 char* authenticatorStr
00186 = createAuthenticatorString(authenticator, "DESCRIBE", url);
00187
00188 char const* acceptStr = allowKasennaProtocol
00189 ? "Accept: application/x-rtsp-mh, application/sdp\r\n"
00190 : "Accept: application/sdp\r\n";
00191
00192
00193 char* const cmdFmt =
00194 "DESCRIBE %s RTSP/1.0\r\n"
00195 "CSeq: %d\r\n"
00196 "%s"
00197 "%s"
00198 "%s"
00199 #ifdef SUPPORT_REAL_RTSP
00200 REAL_DESCRIBE_HEADERS
00201 #endif
00202 "\r\n";
00203 unsigned cmdSize = strlen(cmdFmt)
00204 + strlen(url)
00205 + 20
00206 + strlen(acceptStr)
00207 + strlen(authenticatorStr)
00208 + fUserAgentHeaderStrSize;
00209 cmd = new char[cmdSize];
00210 sprintf(cmd, cmdFmt,
00211 url,
00212 ++fCSeq,
00213 acceptStr,
00214 authenticatorStr,
00215 fUserAgentHeaderStr);
00216 delete[] authenticatorStr;
00217
00218 if (!sendRequest(cmd, "DESCRIBE")) break;
00219
00220
00221 unsigned bytesRead; unsigned responseCode;
00222 char* firstLine; char* nextLineStart;
00223 if (!getResponse("DESCRIBE", bytesRead, responseCode, firstLine, nextLineStart,
00224 False )) break;
00225
00226
00227
00228 Boolean wantRedirection = False;
00229 char* redirectionURL = NULL;
00230 #ifdef SUPPORT_REAL_RTSP
00231 delete[] fRealETagStr; fRealETagStr = new char[fResponseBufferSize];
00232 #endif
00233 if (responseCode == 301 || responseCode == 302) {
00234 wantRedirection = True;
00235 redirectionURL = new char[fResponseBufferSize];
00236 } else if (responseCode != 200) {
00237 checkForAuthenticationFailure(responseCode, nextLineStart, authenticator);
00238 envir().setResultMsg("cannot handle DESCRIBE response: ", firstLine);
00239 break;
00240 }
00241
00242
00243
00244
00245
00246
00247
00248 char* serverType = new char[fResponseBufferSize];
00249 int contentLength = -1;
00250 char* lineStart;
00251 while (1) {
00252 lineStart = nextLineStart;
00253 if (lineStart == NULL) break;
00254
00255 nextLineStart = getLine(lineStart);
00256 if (lineStart[0] == '\0') break;
00257
00258 if (sscanf(lineStart, "Content-Length: %d", &contentLength) == 1
00259 || sscanf(lineStart, "Content-length: %d", &contentLength) == 1) {
00260 if (contentLength < 0) {
00261 envir().setResultMsg("Bad \"Content-length:\" header: \"",
00262 lineStart, "\"");
00263 break;
00264 }
00265 } else if (strncmp(lineStart, "Content-Base:", 13) == 0) {
00266 int cbIndex = 13;
00267
00268 while (lineStart[cbIndex] == ' ' || lineStart[cbIndex] == '\t') ++cbIndex;
00269 if (lineStart[cbIndex] != '\0') {
00270 delete[] fBaseURL; fBaseURL = strDup(&lineStart[cbIndex]);
00271 }
00272 } else if (sscanf(lineStart, "Server: %s", serverType) == 1) {
00273 if (strncmp(serverType, "Kasenna", 7) == 0) fServerIsKasenna = True;
00274 if (strncmp(serverType, "WMServer", 8) == 0) fServerIsMicrosoft = True;
00275 #ifdef SUPPORT_REAL_RTSP
00276 } else if (sscanf(lineStart, "ETag: %s", fRealETagStr) == 1) {
00277 #endif
00278 } else if (wantRedirection) {
00279 if (sscanf(lineStart, "Location: %s", redirectionURL) == 1) {
00280
00281 if (fVerbosityLevel >= 1) {
00282 envir() << "Redirecting to the new URL \""
00283 << redirectionURL << "\"\n";
00284 }
00285 reset();
00286 char* result = describeURL(redirectionURL, authenticator, allowKasennaProtocol);
00287 delete[] redirectionURL;
00288 delete[] serverType;
00289 delete[] cmd;
00290 return result;
00291 }
00292 }
00293 }
00294 delete[] serverType;
00295
00296
00297 if (wantRedirection) {
00298 envir().setResultMsg("Saw redirection response code, but not a \"Location:\" header");
00299 delete[] redirectionURL;
00300 break;
00301 }
00302 if (lineStart == NULL) {
00303 envir().setResultMsg("no content following header lines: ", fResponseBuffer);
00304 break;
00305 }
00306
00307
00308
00309
00310 char* bodyStart = nextLineStart;
00311 if (contentLength >= 0) {
00312
00313 unsigned numBodyBytes = &firstLine[bytesRead] - bodyStart;
00314 if (contentLength > (int)numBodyBytes) {
00315
00316
00317 unsigned numExtraBytesNeeded = contentLength - numBodyBytes;
00318 unsigned remainingBufferSize
00319 = fResponseBufferSize - (bytesRead + (firstLine - fResponseBuffer));
00320 if (numExtraBytesNeeded > remainingBufferSize) {
00321 char tmpBuf[200];
00322 sprintf(tmpBuf, "Read buffer size (%d) is too small for \"Content-length:\" %d (need a buffer size of >= %d bytes\n",
00323 fResponseBufferSize, contentLength,
00324 fResponseBufferSize + numExtraBytesNeeded - remainingBufferSize);
00325 envir().setResultMsg(tmpBuf);
00326 break;
00327 }
00328
00329
00330 if (fVerbosityLevel >= 1) {
00331 envir() << "Need to read " << numExtraBytesNeeded
00332 << " extra bytes\n";
00333 }
00334 while (numExtraBytesNeeded > 0) {
00335 struct sockaddr_in fromAddress;
00336 char* ptr = &firstLine[bytesRead];
00337 int bytesRead2 = readSocket(envir(), fInputSocketNum, (unsigned char*)ptr,
00338 numExtraBytesNeeded, fromAddress);
00339 if (bytesRead2 < 0) break;
00340 ptr[bytesRead2] = '\0';
00341 if (fVerbosityLevel >= 1) {
00342 envir() << "Read " << bytesRead2 << " extra bytes: "
00343 << ptr << "\n";
00344 }
00345
00346 bytesRead += bytesRead2;
00347 numExtraBytesNeeded -= bytesRead2;
00348 }
00349 if (numExtraBytesNeeded > 0) break;
00350 }
00351
00352
00353
00354
00355 int from, to = 0;
00356 for (from = 0; from < contentLength; ++from) {
00357 if (bodyStart[from] != '\0') {
00358 if (to != from) bodyStart[to] = bodyStart[from];
00359 ++to;
00360 }
00361 }
00362 if (from != to && fVerbosityLevel >= 1) {
00363 envir() << "Warning: " << from-to << " invalid 'NULL' bytes were found in (and removed from) the SDP description.\n";
00364 }
00365 bodyStart[to] = '\0';
00366 }
00367
00369
00370 if (fServerIsKasenna && strncmp(bodyStart, "<MediaDescription>", 18) == 0) {
00371
00372 int videoPid, audioPid;
00373 u_int64_t mh_duration;
00374 char* currentWord = new char[fResponseBufferSize];
00375 delete[] fKasennaContentType;
00376 fKasennaContentType = new char[fResponseBufferSize];
00377 char* currentPos = bodyStart;
00378
00379 while (strcmp(currentWord, "</MediaDescription>") != 0) {
00380 sscanf(currentPos, "%s", currentWord);
00381
00382 if (strcmp(currentWord, "VideoPid") == 0) {
00383 currentPos += strlen(currentWord) + 1;
00384 sscanf(currentPos, "%s", currentWord);
00385 currentPos += strlen(currentWord) + 1;
00386 sscanf(currentPos, "%d", &videoPid);
00387 currentPos += 3;
00388 }
00389
00390 if (strcmp(currentWord, "AudioPid") == 0) {
00391 currentPos += strlen(currentWord) + 1;
00392 sscanf(currentPos, "%s", currentWord);
00393 currentPos += strlen(currentWord) + 1;
00394 sscanf(currentPos, "%d", &audioPid);
00395 currentPos += 3;
00396 }
00397
00398 if (strcmp(currentWord, "Duration") == 0) {
00399 currentPos += strlen(currentWord) + 1;
00400 sscanf(currentPos, "%s", currentWord);
00401 currentPos += strlen(currentWord) + 1;
00402 sscanf(currentPos, "%llu", &mh_duration);
00403 currentPos += 3;
00404 }
00405
00406 if (strcmp(currentWord, "TypeSpecificData") == 0) {
00407 currentPos += strlen(currentWord) + 1;
00408 sscanf(currentPos, "%s", currentWord);
00409 currentPos += strlen(currentWord) + 1;
00410 sscanf(currentPos, "%s", fKasennaContentType);
00411 currentPos += 3;
00412 printf("Kasenna Content Type: %s\n", fKasennaContentType);
00413 }
00414
00415 currentPos += strlen(currentWord) + 1;
00416 }
00417
00418 if (fKasennaContentType != NULL
00419 && strcmp(fKasennaContentType, "PARTNER_41_MPEG-4") == 0) {
00420 char* describeSDP = describeURL(url, authenticator, True);
00421
00422 delete[] currentWord;
00423 delete[] cmd;
00424 return describeSDP;
00425 }
00426
00427 unsigned char byte1 = fServerAddress & 0x000000ff;
00428 unsigned char byte2 = (fServerAddress & 0x0000ff00) >> 8;
00429 unsigned char byte3 = (fServerAddress & 0x00ff0000) >> 16;
00430 unsigned char byte4 = (fServerAddress & 0xff000000) >> 24;
00431
00432 char const* sdpFmt =
00433 "v=0\r\n"
00434 "o=NoSpacesAllowed 1 1 IN IP4 %u.%u.%u.%u\r\n"
00435 "s=%s\r\n"
00436 "c=IN IP4 %u.%u.%u.%u\r\n"
00437 "t=0 0\r\n"
00438 "a=control:*\r\n"
00439 "a=range:npt=0-%llu\r\n"
00440 "m=video 1554 RAW/RAW/UDP 33\r\n"
00441 "a=control:trackID=%d\r\n";
00442 unsigned sdpBufSize = strlen(sdpFmt)
00443 + 4*3
00444 + strlen(url)
00445 + 20
00446 + 20;
00447 char* sdpBuf = new char[sdpBufSize];
00448 sprintf(sdpBuf, sdpFmt,
00449 byte1, byte2, byte3, byte4,
00450 url,
00451 byte1, byte2, byte3, byte4,
00452 mh_duration/1000000,
00453 videoPid);
00454
00455 char* result = strDup(sdpBuf);
00456 delete[] sdpBuf; delete[] currentWord;
00457 delete[] cmd;
00458 return result;
00459 }
00461
00462 delete[] cmd;
00463 return strDup(bodyStart);
00464 } while (0);
00465
00466 delete[] cmd;
00467 if (fDescribeStatusCode == 0) fDescribeStatusCode = 2;
00468 return NULL;
00469 }
00470
00471 char* RTSPClient
00472 ::describeWithPassword(char const* url,
00473 char const* username, char const* password,
00474 Boolean allowKasennaProtocol) {
00475 Authenticator authenticator;
00476 authenticator.setUsernameAndPassword(username, password);
00477 char* describeResult = describeURL(url, &authenticator, allowKasennaProtocol);
00478 if (describeResult != NULL) {
00479
00480 return describeResult;
00481 }
00482
00483
00484 if (authenticator.realm() == NULL) {
00485
00486 return NULL;
00487 }
00488
00489
00490 describeResult = describeURL(url, &authenticator, allowKasennaProtocol);
00491 if (describeResult != NULL) {
00492
00493 fCurrentAuthenticator = authenticator;
00494 }
00495
00496 return describeResult;
00497 }
00498
00499 char* RTSPClient::sendOptionsCmd(char const* url,
00500 char* username, char* password,
00501 Authenticator* authenticator) {
00502 char* result = NULL;
00503 char* cmd = NULL;
00504 Boolean haveAllocatedAuthenticator = False;
00505 do {
00506 if (authenticator == NULL) {
00507
00508
00509 if (username == NULL && password == NULL
00510 && parseRTSPURLUsernamePassword(url, username, password)) {
00511 Authenticator newAuthenticator;
00512 newAuthenticator.setUsernameAndPassword(username, password);
00513 result = sendOptionsCmd(url, username, password, &newAuthenticator);
00514 delete[] username; delete[] password;
00515 break;
00516 } else if (username != NULL && password != NULL) {
00517
00518 authenticator = new Authenticator;
00519 haveAllocatedAuthenticator = True;
00520 authenticator->setUsernameAndPassword(username, password);
00521
00522 result = sendOptionsCmd(url, username, password, authenticator);
00523 if (result != NULL) break;
00524
00525
00526 if (authenticator->realm() == NULL) {
00527
00528 break;
00529 }
00530
00531 }
00532 }
00533
00534 if (!openConnectionFromURL(url, authenticator)) break;
00535
00536
00537
00538
00539 char* authenticatorStr
00540 = createAuthenticatorString(authenticator, "OPTIONS", url);
00541
00542 char* const cmdFmt =
00543 "OPTIONS %s RTSP/1.0\r\n"
00544 "CSeq: %d\r\n"
00545 "%s"
00546 "%s"
00547 #ifdef SUPPORT_REAL_RTSP
00548 REAL_OPTIONS_HEADERS
00549 #endif
00550 "\r\n";
00551 unsigned cmdSize = strlen(cmdFmt)
00552 + strlen(url)
00553 + 20
00554 + strlen(authenticatorStr)
00555 + fUserAgentHeaderStrSize;
00556 cmd = new char[cmdSize];
00557 sprintf(cmd, cmdFmt,
00558 url,
00559 ++fCSeq,
00560 authenticatorStr,
00561 fUserAgentHeaderStr);
00562 delete[] authenticatorStr;
00563
00564 if (!sendRequest(cmd, "OPTIONS")) break;
00565
00566
00567 unsigned bytesRead; unsigned responseCode;
00568 char* firstLine; char* nextLineStart;
00569 if (!getResponse("OPTIONS", bytesRead, responseCode, firstLine, nextLineStart,
00570 False )) break;
00571 if (responseCode != 200) {
00572 checkForAuthenticationFailure(responseCode, nextLineStart, authenticator);
00573 envir().setResultMsg("cannot handle OPTIONS response: ", firstLine);
00574 break;
00575 }
00576
00577
00578 char* lineStart;
00579 while (1) {
00580 lineStart = nextLineStart;
00581 if (lineStart == NULL) break;
00582
00583 nextLineStart = getLine(lineStart);
00584
00585 if (_strncasecmp(lineStart, "Public: ", 8) == 0) {
00586 delete[] result; result = strDup(&lineStart[8]);
00587 #ifdef SUPPORT_REAL_RTSP
00588 } else if (_strncasecmp(lineStart, "RealChallenge1: ", 16) == 0) {
00589 delete[] fRealChallengeStr; fRealChallengeStr = strDup(&lineStart[16]);
00590 #endif
00591 }
00592 }
00593 } while (0);
00594
00595 delete[] cmd;
00596 if (haveAllocatedAuthenticator) delete authenticator;
00597 return result;
00598 }
00599
00600 static Boolean isAbsoluteURL(char const* url) {
00601
00602
00603 while (*url != '\0' && *url != '/') {
00604 if (*url == ':') return True;
00605 ++url;
00606 }
00607
00608 return False;
00609 }
00610
00611 char const* RTSPClient::sessionURL(MediaSession const& session) const {
00612 char const* url = session.controlPath();
00613 if (url == NULL || strcmp(url, "*") == 0) url = fBaseURL;
00614
00615 return url;
00616 }
00617
00618 void RTSPClient::constructSubsessionURL(MediaSubsession const& subsession,
00619 char const*& prefix,
00620 char const*& separator,
00621 char const*& suffix) {
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633 prefix = sessionURL(subsession.parentSession());
00634 if (prefix == NULL) prefix = "";
00635
00636 suffix = subsession.controlPath();
00637 if (suffix == NULL) suffix = "";
00638
00639 if (isAbsoluteURL(suffix)) {
00640 prefix = separator = "";
00641 } else {
00642 unsigned prefixLen = strlen(prefix);
00643 separator = (prefix[prefixLen-1] == '/' || suffix[0] == '/') ? "" : "/";
00644 }
00645 }
00646
00647 Boolean RTSPClient::announceSDPDescription(char const* url,
00648 char const* sdpDescription,
00649 Authenticator* authenticator) {
00650 char* cmd = NULL;
00651 do {
00652 if (!openConnectionFromURL(url, authenticator)) break;
00653
00654
00655
00656
00657 fCurrentAuthenticator.reset();
00658 char* authenticatorStr
00659 = createAuthenticatorString(authenticator, "ANNOUNCE", url);
00660
00661 char* const cmdFmt =
00662 "ANNOUNCE %s RTSP/1.0\r\n"
00663 "CSeq: %d\r\n"
00664 "Content-Type: application/sdp\r\n"
00665 "%s"
00666 "Content-length: %d\r\n\r\n"
00667 "%s";
00668
00669 unsigned sdpSize = strlen(sdpDescription);
00670 unsigned cmdSize = strlen(cmdFmt)
00671 + strlen(url)
00672 + 20
00673 + strlen(authenticatorStr)
00674 + 20
00675 + sdpSize;
00676 cmd = new char[cmdSize];
00677 sprintf(cmd, cmdFmt,
00678 url,
00679 ++fCSeq,
00680 authenticatorStr,
00681 sdpSize,
00682 sdpDescription);
00683 delete[] authenticatorStr;
00684
00685 if (!sendRequest(cmd, "ANNOUNCE")) break;
00686
00687
00688 unsigned bytesRead; unsigned responseCode;
00689 char* firstLine; char* nextLineStart;
00690 if (!getResponse("ANNOUNCE", bytesRead, responseCode, firstLine, nextLineStart,
00691 False )) break;
00692
00693
00694 if (responseCode != 200) {
00695 checkForAuthenticationFailure(responseCode, nextLineStart, authenticator);
00696 envir().setResultMsg("cannot handle ANNOUNCE response: ", firstLine);
00697 break;
00698 }
00699
00700 delete[] cmd;
00701 return True;
00702 } while (0);
00703
00704 delete[] cmd;
00705 return False;
00706 }
00707
00708 Boolean RTSPClient
00709 ::announceWithPassword(char const* url, char const* sdpDescription,
00710 char const* username, char const* password) {
00711 Authenticator authenticator;
00712 authenticator.setUsernameAndPassword(username, password);
00713 if (announceSDPDescription(url, sdpDescription, &authenticator)) {
00714
00715 return True;
00716 }
00717
00718
00719 if (authenticator.realm() == NULL) {
00720
00721 return False;
00722 }
00723
00724
00725 Boolean secondTrySuccess
00726 = announceSDPDescription(url, sdpDescription, &authenticator);
00727
00728 if (secondTrySuccess) {
00729
00730 fCurrentAuthenticator = authenticator;
00731 }
00732
00733 return secondTrySuccess;
00734 }
00735
00736 Boolean RTSPClient::setupMediaSubsession(MediaSubsession& subsession,
00737 Boolean streamOutgoing,
00738 Boolean streamUsingTCP,
00739 Boolean forceMulticastOnUnspecified) {
00740 char* cmd = NULL;
00741 char* setupStr = NULL;
00742
00743 if (fServerIsMicrosoft) {
00744
00745 char *tmpStr = subsession.parentSession().mediaSessionType();
00746 if (tmpStr != NULL && strncmp(tmpStr, "broadcast", 9) == 0) {
00747 subsession.parentSession().playEndTime() = 0.0;
00748 }
00749 }
00750
00751 do {
00752
00753
00754
00755 char* authenticatorStr
00756 = createAuthenticatorString(&fCurrentAuthenticator,
00757 "SETUP", fBaseURL);
00758
00759
00760
00761 char* sessionStr;
00762 if (fLastSessionId != NULL) {
00763 sessionStr = new char[20+strlen(fLastSessionId)];
00764 sprintf(sessionStr, "Session: %s\r\n", fLastSessionId);
00765 } else {
00766 sessionStr = strDup("");
00767 }
00768
00769 char* transportStr = NULL;
00770 #ifdef SUPPORT_REAL_RTSP
00771 if (usingRealNetworksChallengeResponse()) {
00772
00773 char challenge2[64];
00774 char checksum[34];
00775 RealCalculateChallengeResponse(fRealChallengeStr, challenge2, checksum);
00776
00777 char const* etag = fRealETagStr == NULL ? "" : fRealETagStr;
00778
00779 char* transportHeader;
00780 if (subsession.parentSession().isRealNetworksRDT) {
00781 transportHeader = strDup("Transport: x-pn-tng/tcp;mode=play,rtp/avp/unicast;mode=play\r\n");
00782 } else {
00783
00784 char const* transportHeaderFmt
00785 = "Transport: RTP/AVP%s%s=%d-%d\r\n";
00786 char const* transportTypeStr;
00787 char const* portTypeStr;
00788 unsigned short rtpNumber, rtcpNumber;
00789 if (streamUsingTCP) {
00790 transportTypeStr = "/TCP;unicast";
00791 portTypeStr = ";interleaved";
00792 rtpNumber = fTCPStreamIdCount++;
00793 rtcpNumber = fTCPStreamIdCount++;
00794 } else {
00795 unsigned connectionAddress = subsession.connectionEndpointAddress();
00796 Boolean requestMulticastStreaming = IsMulticastAddress(connectionAddress)
00797 || (connectionAddress == 0 && forceMulticastOnUnspecified);
00798 transportTypeStr = requestMulticastStreaming ? ";multicast" : ";unicast";
00799 portTypeStr = ";client_port";
00800 rtpNumber = subsession.clientPortNum();
00801 if (rtpNumber == 0) {
00802 envir().setResultMsg("Client port number unknown\n");
00803 break;
00804 }
00805 rtcpNumber = rtpNumber + 1;
00806 }
00807
00808 unsigned transportHeaderSize = strlen(transportHeaderFmt)
00809 + strlen(transportTypeStr) + strlen(portTypeStr) + 2*5 ;
00810 transportHeader = new char[transportHeaderSize];
00811 sprintf(transportHeader, transportHeaderFmt,
00812 transportTypeStr, portTypeStr, rtpNumber, rtcpNumber);
00813 }
00814 char const* transportFmt =
00815 "%s"
00816 "RealChallenge2: %s, sd=%s\r\n"
00817 "If-Match: %s\r\n";
00818 unsigned transportSize = strlen(transportFmt)
00819 + strlen(transportHeader)
00820 + sizeof challenge2 + sizeof checksum
00821 + strlen(etag);
00822 transportStr = new char[transportSize];
00823 sprintf(transportStr, transportFmt,
00824 transportHeader,
00825 challenge2, checksum,
00826 etag);
00827 delete[] transportHeader;
00828
00829 if (subsession.parentSession().isRealNetworksRDT) {
00830
00831 RealRDTSource* rdtSource
00832 = (RealRDTSource*)(subsession.readSource());
00833 rdtSource->setInputSocket(fInputSocketNum);
00834 }
00835 }
00836 #endif
00837
00838 char const *prefix, *separator, *suffix;
00839 constructSubsessionURL(subsession, prefix, separator, suffix);
00840 char* transportFmt;
00841
00842 if (strcmp(subsession.protocolName(), "UDP") == 0) {
00843 char const* setupFmt = "SETUP %s%s RTSP/1.0\r\n";
00844 unsigned setupSize = strlen(setupFmt)
00845 + strlen(prefix) + strlen (separator);
00846 setupStr = new char[setupSize];
00847 sprintf(setupStr, setupFmt, prefix, separator);
00848
00849 transportFmt = "Transport: RAW/RAW/UDP%s%s%s=%d-%d\r\n";
00850 } else {
00851 char const* setupFmt = "SETUP %s%s%s RTSP/1.0\r\n";
00852 unsigned setupSize = strlen(setupFmt)
00853 + strlen(prefix) + strlen (separator) + strlen(suffix);
00854 setupStr = new char[setupSize];
00855 sprintf(setupStr, setupFmt, prefix, separator, suffix);
00856
00857 transportFmt = "Transport: RTP/AVP%s%s%s=%d-%d\r\n";
00858 }
00859
00860 if (transportStr == NULL) {
00861
00862 char const* transportTypeStr;
00863 char const* modeStr = streamOutgoing ? ";mode=receive" : "";
00864
00865 char const* portTypeStr;
00866 unsigned short rtpNumber, rtcpNumber;
00867 if (streamUsingTCP) {
00868 transportTypeStr = "/TCP;unicast";
00869 portTypeStr = ";interleaved";
00870 rtpNumber = fTCPStreamIdCount++;
00871 rtcpNumber = fTCPStreamIdCount++;
00872 } else {
00873 unsigned connectionAddress = subsession.connectionEndpointAddress();
00874 Boolean requestMulticastStreaming = IsMulticastAddress(connectionAddress)
00875 || (connectionAddress == 0 && forceMulticastOnUnspecified);
00876 transportTypeStr = requestMulticastStreaming ? ";multicast" : ";unicast";
00877 portTypeStr = ";client_port";
00878 rtpNumber = subsession.clientPortNum();
00879 if (rtpNumber == 0) {
00880 envir().setResultMsg("Client port number unknown\n");
00881 delete[] authenticatorStr; delete[] sessionStr; delete[] setupStr;
00882 break;
00883 }
00884 rtcpNumber = rtpNumber + 1;
00885 }
00886
00887 unsigned transportSize = strlen(transportFmt)
00888 + strlen(transportTypeStr) + strlen(modeStr) + strlen(portTypeStr) + 2*5 ;
00889 transportStr = new char[transportSize];
00890 sprintf(transportStr, transportFmt,
00891 transportTypeStr, modeStr, portTypeStr, rtpNumber, rtcpNumber);
00892 }
00893
00894
00895 char* const cmdFmt =
00896 "%s"
00897 "CSeq: %d\r\n"
00898 "%s"
00899 "%s"
00900 "%s"
00901 "%s"
00902 "\r\n";
00903
00904 unsigned cmdSize = strlen(cmdFmt)
00905 + strlen(setupStr)
00906 + 20
00907 + strlen(transportStr)
00908 + strlen(sessionStr)
00909 + strlen(authenticatorStr)
00910 + fUserAgentHeaderStrSize;
00911 cmd = new char[cmdSize];
00912 sprintf(cmd, cmdFmt,
00913 setupStr,
00914 ++fCSeq,
00915 transportStr,
00916 sessionStr,
00917 authenticatorStr,
00918 fUserAgentHeaderStr);
00919 delete[] authenticatorStr; delete[] sessionStr; delete[] setupStr; delete[] transportStr;
00920
00921
00922 if (!sendRequest(cmd, "SETUP")) break;
00923
00924
00925 unsigned bytesRead; unsigned responseCode;
00926 char* firstLine; char* nextLineStart;
00927 if (!getResponse("SETUP", bytesRead, responseCode, firstLine, nextLineStart)) break;
00928
00929
00930
00931
00932 char* lineStart;
00933 char* sessionId = new char[fResponseBufferSize];
00934 unsigned cLength = 0;
00935 while (1) {
00936 lineStart = nextLineStart;
00937 if (lineStart == NULL) break;
00938
00939 nextLineStart = getLine(lineStart);
00940
00941 if (sscanf(lineStart, "Session: %[^;]", sessionId) == 1) {
00942 subsession.sessionId = strDup(sessionId);
00943 delete[] fLastSessionId; fLastSessionId = strDup(sessionId);
00944
00945
00946 char* afterSessionId
00947 = lineStart + strlen(sessionId) + strlen ("Session: ");;
00948 int timeoutVal;
00949 if (sscanf(afterSessionId, "; timeout = %d", &timeoutVal) == 1) {
00950 fSessionTimeoutParameter = timeoutVal;
00951 }
00952 continue;
00953 }
00954
00955 char* serverAddressStr;
00956 portNumBits serverPortNum;
00957 unsigned char rtpChannelId, rtcpChannelId;
00958 if (parseTransportResponse(lineStart,
00959 serverAddressStr, serverPortNum,
00960 rtpChannelId, rtcpChannelId)) {
00961 delete[] subsession.connectionEndpointName();
00962 subsession.connectionEndpointName() = serverAddressStr;
00963 subsession.serverPortNum = serverPortNum;
00964 subsession.rtpChannelId = rtpChannelId;
00965 subsession.rtcpChannelId = rtcpChannelId;
00966 continue;
00967 }
00968
00969
00970
00971 if (sscanf(lineStart, "Content-Length: %d", &cLength) == 1) continue;
00972 }
00973 delete[] sessionId;
00974
00975 if (subsession.sessionId == NULL) {
00976 envir().setResultMsg("\"Session:\" header is missing in the response");
00977 break;
00978 }
00979
00980
00981
00982 if (cLength > 0) {
00983 char* dummyBuf = new char[cLength];
00984 getResponse1(dummyBuf, cLength);
00985 delete[] dummyBuf;
00986 }
00987
00988 if (streamUsingTCP) {
00989
00990
00991 if (subsession.rtpSource() != NULL)
00992 subsession.rtpSource()->setStreamSocket(fInputSocketNum,
00993 subsession.rtpChannelId);
00994 if (subsession.rtcpInstance() != NULL)
00995 subsession.rtcpInstance()->setStreamSocket(fInputSocketNum,
00996 subsession.rtcpChannelId);
00997 } else {
00998
00999
01000
01001 netAddressBits destAddress = subsession.connectionEndpointAddress();
01002 if (destAddress == 0) destAddress = fServerAddress;
01003 subsession.setDestinations(destAddress);
01004 }
01005
01006 delete[] cmd;
01007 return True;
01008 } while (0);
01009
01010 delete[] cmd;
01011 return False;
01012 }
01013
01014 static char* createScaleString(float scale, float currentScale) {
01015 char buf[100];
01016 if (scale == 1.0f && currentScale == 1.0f) {
01017
01018 buf[0] = '\0';
01019 } else {
01020 Locale("C", LC_NUMERIC);
01021 sprintf(buf, "Scale: %f\r\n", scale);
01022 }
01023
01024 return strDup(buf);
01025 }
01026
01027 static char* createRangeString(float start, float end) {
01028 char buf[100];
01029 if (start < 0) {
01030
01031 buf[0] = '\0';
01032 } else if (end < 0) {
01033
01034 Locale("C", LC_NUMERIC);
01035 sprintf(buf, "Range: npt=%.3f-\r\n", start);
01036 } else {
01037