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
00029
00030 RTSPClient* RTSPClient::createNew(UsageEnvironment& env, char const* rtspURL,
00031 int verbosityLevel,
00032 char const* applicationName,
00033 portNumBits tunnelOverHTTPPortNum) {
00034 return new RTSPClient(env, rtspURL,
00035 verbosityLevel, applicationName, tunnelOverHTTPPortNum);
00036 }
00037
00038 unsigned RTSPClient::sendDescribeCommand(responseHandler* responseHandler, Authenticator* authenticator) {
00039 if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00040 return sendRequest(new RequestRecord(++fCSeq, "DESCRIBE", responseHandler));
00041 }
00042
00043 unsigned RTSPClient::sendOptionsCommand(responseHandler* responseHandler, Authenticator* authenticator) {
00044 if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00045 return sendRequest(new RequestRecord(++fCSeq, "OPTIONS", responseHandler));
00046 }
00047
00048 unsigned RTSPClient::sendAnnounceCommand(char const* sdpDescription, responseHandler* responseHandler, Authenticator* authenticator) {
00049 if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00050 return sendRequest(new RequestRecord(++fCSeq, "ANNOUNCE", responseHandler, NULL, NULL, False, 0.0, 0.0, 0.0, sdpDescription));
00051 }
00052
00053 unsigned RTSPClient::sendSetupCommand(MediaSubsession& subsession, responseHandler* responseHandler,
00054 Boolean streamOutgoing, Boolean streamUsingTCP, Boolean forceMulticastOnUnspecified,
00055 Authenticator* authenticator) {
00056 if (fTunnelOverHTTPPortNum != 0) streamUsingTCP = True;
00057 if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00058
00059 u_int32_t booleanFlags = 0;
00060 if (streamUsingTCP) booleanFlags |= 0x1;
00061 if (streamOutgoing) booleanFlags |= 0x2;
00062 if (forceMulticastOnUnspecified) booleanFlags |= 0x4;
00063 return sendRequest(new RequestRecord(++fCSeq, "SETUP", responseHandler, NULL, &subsession, booleanFlags));
00064 }
00065
00066 unsigned RTSPClient::sendPlayCommand(MediaSession& session, responseHandler* responseHandler,
00067 double start, double end, float scale,
00068 Authenticator* authenticator) {
00069 if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00070 return sendRequest(new RequestRecord(++fCSeq, "PLAY", responseHandler, &session, NULL, 0, start, end, scale));
00071 }
00072
00073 unsigned RTSPClient::sendPlayCommand(MediaSubsession& subsession, responseHandler* responseHandler,
00074 double start, double end, float scale,
00075 Authenticator* authenticator) {
00076 if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00077 return sendRequest(new RequestRecord(++fCSeq, "PLAY", responseHandler, NULL, &subsession, 0, start, end, scale));
00078 }
00079
00080 unsigned RTSPClient::sendPauseCommand(MediaSession& session, responseHandler* responseHandler, Authenticator* authenticator) {
00081 if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00082 return sendRequest(new RequestRecord(++fCSeq, "PAUSE", responseHandler, &session));
00083 }
00084
00085 unsigned RTSPClient::sendPauseCommand(MediaSubsession& subsession, responseHandler* responseHandler, Authenticator* authenticator) {
00086 if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00087 return sendRequest(new RequestRecord(++fCSeq, "PAUSE", responseHandler, NULL, &subsession));
00088 }
00089
00090 unsigned RTSPClient::sendRecordCommand(MediaSession& session, responseHandler* responseHandler, Authenticator* authenticator) {
00091 if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00092 return sendRequest(new RequestRecord(++fCSeq, "RECORD", responseHandler, &session));
00093 }
00094
00095 unsigned RTSPClient::sendRecordCommand(MediaSubsession& subsession, responseHandler* responseHandler, Authenticator* authenticator) {
00096 if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00097 return sendRequest(new RequestRecord(++fCSeq, "RECORD", responseHandler, NULL, &subsession));
00098 }
00099
00100 unsigned RTSPClient::sendTeardownCommand(MediaSession& session, responseHandler* responseHandler, Authenticator* authenticator) {
00101 if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00102 return sendRequest(new RequestRecord(++fCSeq, "TEARDOWN", responseHandler, &session));
00103 }
00104
00105 unsigned RTSPClient::sendTeardownCommand(MediaSubsession& subsession, responseHandler* responseHandler, Authenticator* authenticator) {
00106 if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00107 return sendRequest(new RequestRecord(++fCSeq, "TEARDOWN", responseHandler, NULL, &subsession));
00108 }
00109
00110 unsigned RTSPClient::sendSetParameterCommand(MediaSession& session, responseHandler* responseHandler,
00111 char const* parameterName, char const* parameterValue,
00112 Authenticator* authenticator) {
00113 if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00114 char* paramString = new char[strlen(parameterName) + strlen(parameterValue) + 10];
00115 sprintf(paramString, "%s: %s\r\n", parameterName, parameterValue);
00116 unsigned result = sendRequest(new RequestRecord(++fCSeq, "SET_PARAMETER", responseHandler, &session, NULL, False, 0.0, 0.0, 0.0, paramString));
00117 delete[] paramString;
00118 return result;
00119 }
00120
00121 unsigned RTSPClient::sendGetParameterCommand(MediaSession& session, responseHandler* responseHandler, char const* parameterName,
00122 Authenticator* authenticator) {
00123 if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00124
00125
00126
00127
00128
00129 unsigned parameterNameLen = parameterName == NULL ? 0 : strlen(parameterName);
00130 char* paramString = new char[parameterNameLen + 3];
00131 if (parameterName == NULL) {
00132 paramString[0] = '\0';
00133 } else {
00134 sprintf(paramString, "%s\r\n", parameterName);
00135 }
00136 unsigned result = sendRequest(new RequestRecord(++fCSeq, "GET_PARAMETER", responseHandler, &session, NULL, False, 0.0, 0.0, 0.0, paramString));
00137 delete[] paramString;
00138 return result;
00139 }
00140
00141 Boolean RTSPClient::changeResponseHandler(unsigned cseq, responseHandler* newResponseHandler) {
00142
00143 RequestRecord* request;
00144 if ((request = fRequestsAwaitingConnection.findByCSeq(cseq)) != NULL
00145 || (request = fRequestsAwaitingHTTPTunneling.findByCSeq(cseq)) != NULL
00146 || (request = fRequestsAwaitingResponse.findByCSeq(cseq)) != NULL) {
00147 request->handler() = newResponseHandler;
00148 return True;
00149 }
00150
00151 return False;
00152 }
00153
00154 Boolean RTSPClient::lookupByName(UsageEnvironment& env,
00155 char const* instanceName,
00156 RTSPClient*& resultClient) {
00157 resultClient = NULL;
00158
00159 Medium* medium;
00160 if (!Medium::lookupByName(env, instanceName, medium)) return False;
00161
00162 if (!medium->isRTSPClient()) {
00163 env.setResultMsg(instanceName, " is not a RTSP client");
00164 return False;
00165 }
00166
00167 resultClient = (RTSPClient*)medium;
00168 return True;
00169 }
00170
00171 Boolean RTSPClient::parseRTSPURL(UsageEnvironment& env, char const* url,
00172 char*& username, char*& password,
00173 NetAddress& address,
00174 portNumBits& portNum,
00175 char const** urlSuffix) {
00176 do {
00177
00178 char const* prefix = "rtsp://";
00179 unsigned const prefixLength = 7;
00180 if (_strncasecmp(url, prefix, prefixLength) != 0) {
00181 env.setResultMsg("URL is not of the form \"", prefix, "\"");
00182 break;
00183 }
00184
00185 unsigned const parseBufferSize = 100;
00186 char parseBuffer[parseBufferSize];
00187 char const* from = &url[prefixLength];
00188
00189
00190
00191 username = password = NULL;
00192 char const* colonPasswordStart = NULL;
00193 char const* p;
00194 for (p = from; *p != '\0' && *p != '/'; ++p) {
00195 if (*p == ':' && colonPasswordStart == NULL) {
00196 colonPasswordStart = p;
00197 } else if (*p == '@') {
00198
00199 if (colonPasswordStart == NULL) colonPasswordStart = p;
00200
00201 char const* usernameStart = from;
00202 unsigned usernameLen = colonPasswordStart - usernameStart;
00203 username = new char[usernameLen + 1] ;
00204 for (unsigned i = 0; i < usernameLen; ++i) username[i] = usernameStart[i];
00205 username[usernameLen] = '\0';
00206
00207 char const* passwordStart = colonPasswordStart;
00208 if (passwordStart < p) ++passwordStart;
00209 unsigned passwordLen = p - passwordStart;
00210 password = new char[passwordLen + 1];
00211 for (unsigned j = 0; j < passwordLen; ++j) password[j] = passwordStart[j];
00212 password[passwordLen] = '\0';
00213
00214 from = p + 1;
00215 break;
00216 }
00217 }
00218
00219
00220 char* to = &parseBuffer[0];
00221 unsigned i;
00222 for (i = 0; i < parseBufferSize; ++i) {
00223 if (*from == '\0' || *from == ':' || *from == '/') {
00224
00225 *to = '\0';
00226 break;
00227 }
00228 *to++ = *from++;
00229 }
00230 if (i == parseBufferSize) {
00231 env.setResultMsg("URL is too long");
00232 break;
00233 }
00234
00235 NetAddressList addresses(parseBuffer);
00236 if (addresses.numAddresses() == 0) {
00237 env.setResultMsg("Failed to find network address for \"",
00238 parseBuffer, "\"");
00239 break;
00240 }
00241 address = *(addresses.firstAddress());
00242
00243 portNum = 554;
00244 char nextChar = *from;
00245 if (nextChar == ':') {
00246 int portNumInt;
00247 if (sscanf(++from, "%d", &portNumInt) != 1) {
00248 env.setResultMsg("No port number follows ':'");
00249 break;
00250 }
00251 if (portNumInt < 1 || portNumInt > 65535) {
00252 env.setResultMsg("Bad port number");
00253 break;
00254 }
00255 portNum = (portNumBits)portNumInt;
00256 while (*from >= '0' && *from <= '9') ++from;
00257 }
00258
00259
00260 if (urlSuffix != NULL) *urlSuffix = from;
00261
00262 return True;
00263 } while (0);
00264
00265 return False;
00266 }
00267
00268 void RTSPClient::setUserAgentString(char const* userAgentName) {
00269 if (userAgentName == NULL) return;
00270
00271
00272 char const* const formatStr = "User-Agent: %s\r\n";
00273 unsigned const headerSize = strlen(formatStr) + strlen(userAgentName);
00274 delete[] fUserAgentHeaderStr;
00275 fUserAgentHeaderStr = new char[headerSize];
00276 sprintf(fUserAgentHeaderStr, formatStr, userAgentName);
00277 fUserAgentHeaderStrLen = strlen(fUserAgentHeaderStr);
00278 }
00279
00280 unsigned RTSPClient::responseBufferSize = 20000;
00281
00282 RTSPClient::RTSPClient(UsageEnvironment& env, char const* rtspURL,
00283 int verbosityLevel, char const* applicationName,
00284 portNumBits tunnelOverHTTPPortNum)
00285 : Medium(env),
00286 fVerbosityLevel(verbosityLevel), fTunnelOverHTTPPortNum(tunnelOverHTTPPortNum),
00287 fUserAgentHeaderStr(NULL), fUserAgentHeaderStrLen(0), fInputSocketNum(-1), fOutputSocketNum(-1), fServerAddress(0), fCSeq(1),
00288 fBaseURL(NULL), fTCPStreamIdCount(0), fLastSessionId(NULL), fSessionTimeoutParameter(0),
00289 fSessionCookieCounter(0), fHTTPTunnelingConnectionIsPending(False) {
00290 setBaseURL(rtspURL);
00291
00292 fResponseBuffer = new char[responseBufferSize+1];
00293 resetResponseBuffer();
00294
00295
00296 char const* const libName = "LIVE555 Streaming Media v";
00297 char const* const libVersionStr = LIVEMEDIA_LIBRARY_VERSION_STRING;
00298 char const* libPrefix; char const* libSuffix;
00299 if (applicationName == NULL || applicationName[0] == '\0') {
00300 applicationName = libPrefix = libSuffix = "";
00301 } else {
00302 libPrefix = " (";
00303 libSuffix = ")";
00304 }
00305 unsigned userAgentNameSize
00306 = strlen(applicationName) + strlen(libPrefix) + strlen(libName) + strlen(libVersionStr) + strlen(libSuffix) + 1;
00307 char* userAgentName = new char[userAgentNameSize];
00308 sprintf(userAgentName, "%s%s%s%s%s", applicationName, libPrefix, libName, libVersionStr, libSuffix);
00309 setUserAgentString(userAgentName);
00310 delete[] userAgentName;
00311 }
00312
00313 RTSPClient::~RTSPClient() {
00314 reset();
00315
00316 delete[] fResponseBuffer;
00317 delete[] fUserAgentHeaderStr;
00318 }
00319
00320 Boolean RTSPClient::isRTSPClient() const {
00321 return True;
00322 }
00323
00324 void RTSPClient::reset() {
00325 resetTCPSockets();
00326 resetResponseBuffer();
00327 fServerAddress = 0;
00328
00329 setBaseURL(NULL);
00330
00331 fCurrentAuthenticator.reset();
00332
00333 delete[] fLastSessionId; fLastSessionId = NULL;
00334 }
00335
00336 void RTSPClient::resetTCPSockets() {
00337 if (fInputSocketNum >= 0) {
00338 envir().taskScheduler().disableBackgroundHandling(fInputSocketNum);
00339 ::closeSocket(fInputSocketNum);
00340 if (fOutputSocketNum != fInputSocketNum) {
00341 envir().taskScheduler().disableBackgroundHandling(fOutputSocketNum);
00342 ::closeSocket(fOutputSocketNum);
00343 }
00344 }
00345 fInputSocketNum = fOutputSocketNum = -1;
00346 }
00347
00348 void RTSPClient::resetResponseBuffer() {
00349 fResponseBytesAlreadySeen = 0;
00350 fResponseBufferBytesLeft = responseBufferSize;
00351 }
00352
00353 void RTSPClient::setBaseURL(char const* url) {
00354 delete[] fBaseURL; fBaseURL = strDup(url);
00355 }
00356
00357 int RTSPClient::openConnection() {
00358 do {
00359
00360
00361 char* username;
00362 char* password;
00363 NetAddress destAddress;
00364 portNumBits urlPortNum;
00365 char const* urlSuffix;
00366 if (!parseRTSPURL(envir(), fBaseURL, username, password, destAddress, urlPortNum, &urlSuffix)) break;
00367 portNumBits destPortNum = fTunnelOverHTTPPortNum == 0 ? urlPortNum : fTunnelOverHTTPPortNum;
00368 if (username != NULL || password != NULL) {
00369 fCurrentAuthenticator.setUsernameAndPassword(username, password);
00370 delete[] username;
00371 delete[] password;
00372 }
00373
00374
00375 fInputSocketNum = fOutputSocketNum = setupStreamSocket(envir(), 0);
00376 if (fInputSocketNum < 0) break;
00377
00378
00379 fServerAddress = *(netAddressBits*)(destAddress.data());
00380 int connectResult = connectToServer(fInputSocketNum, destPortNum);
00381 if (connectResult < 0) break;
00382 else if (connectResult > 0) {
00383
00384 envir().taskScheduler().setBackgroundHandling(fInputSocketNum, SOCKET_READABLE,
00385 (TaskScheduler::BackgroundHandlerProc*)&incomingDataHandler, this);
00386 }
00387 return connectResult;
00388 } while (0);
00389
00390 resetTCPSockets();
00391 return -1;
00392 }
00393
00394 int RTSPClient::connectToServer(int socketNum, portNumBits remotePortNum) {
00395 MAKE_SOCKADDR_IN(remoteName, fServerAddress, htons(remotePortNum));
00396 if (fVerbosityLevel >= 1) {
00397 envir() << "Opening connection to " << AddressString(remoteName).val() << ", port " << remotePortNum << "...\n";
00398 }
00399 if (connect(socketNum, (struct sockaddr*) &remoteName, sizeof remoteName) != 0) {
00400 int const err = envir().getErrno();
00401 if (err == EINPROGRESS || err == EWOULDBLOCK) {
00402
00403 envir().taskScheduler().setBackgroundHandling(socketNum, SOCKET_WRITABLE|SOCKET_EXCEPTION,
00404 (TaskScheduler::BackgroundHandlerProc*)&connectionHandler, this);
00405 return 0;
00406 }
00407 envir().setResultErrMsg("connect() failed: ");
00408 if (fVerbosityLevel >= 1) envir() << "..." << envir().getResultMsg() << "\n";
00409 return -1;
00410 }
00411 if (fVerbosityLevel >= 1) envir() << "...local connection opened\n";
00412
00413 return 1;
00414 }
00415
00416 char*
00417 RTSPClient::createAuthenticatorString(char const* cmd, char const* url) {
00418 Authenticator& auth = fCurrentAuthenticator;
00419 if (auth.realm() != NULL && auth.username() != NULL && auth.password() != NULL) {
00420
00421 char* authenticatorStr;
00422 if (auth.nonce() != NULL) {
00423 char const* const authFmt =
00424 "Authorization: Digest username=\"%s\", realm=\"%s\", "
00425 "nonce=\"%s\", uri=\"%s\", response=\"%s\"\r\n";
00426 char const* response = auth.computeDigestResponse(cmd, url);
00427 unsigned authBufSize = strlen(authFmt)
00428 + strlen(auth.username()) + strlen(auth.realm())
00429 + strlen(auth.nonce()) + strlen(url) + strlen(response);
00430 authenticatorStr = new char[authBufSize];
00431 sprintf(authenticatorStr, authFmt,
00432 auth.username(), auth.realm(),
00433 auth.nonce(), url, response);
00434 auth.reclaimDigestResponse(response);
00435 } else {
00436 char const* const authFmt = "Authorization: Basic %s\r\n";
00437
00438 unsigned usernamePasswordLength = strlen(auth.username()) + 1 + strlen(auth.password());
00439 char* usernamePassword = new char[usernamePasswordLength+1];
00440 sprintf(usernamePassword, "%s:%s", auth.username(), auth.password());
00441
00442 char* response = base64Encode(usernamePassword, usernamePasswordLength);
00443 unsigned const authBufSize = strlen(authFmt) + strlen(response) + 1;
00444 authenticatorStr = new char[authBufSize];
00445 sprintf(authenticatorStr, authFmt, response);
00446 delete[] response; delete[] usernamePassword;
00447 }
00448
00449 return authenticatorStr;
00450 }
00451
00452
00453 return strDup("");
00454 }
00455
00456 static char* createSessionString(char const* sessionId) {
00457 char* sessionStr;
00458 if (sessionId != NULL) {
00459 sessionStr = new char[20+strlen(sessionId)];
00460 sprintf(sessionStr, "Session: %s\r\n", sessionId);
00461 } else {
00462 sessionStr = strDup("");
00463 }
00464 return sessionStr;
00465 }
00466
00467 static char* createScaleString(float scale, float currentScale) {
00468 char buf[100];
00469 if (scale == 1.0f && currentScale == 1.0f) {
00470
00471 buf[0] = '\0';
00472 } else {
00473 Locale l("C", Numeric);
00474 sprintf(buf, "Scale: %f\r\n", scale);
00475 }
00476
00477 return strDup(buf);
00478 }
00479
00480 static char* createRangeString(double start, double end) {
00481 char buf[100];
00482 if (start < 0) {
00483
00484 buf[0] = '\0';
00485 } else if (end < 0) {
00486
00487 Locale l("C", Numeric);
00488 sprintf(buf, "Range: npt=%.3f-\r\n", start);
00489 } else {
00490
00491 Locale l("C", Numeric);
00492 sprintf(buf, "Range: npt=%.3f-%.3f\r\n", start, end);
00493 }
00494
00495 return strDup(buf);
00496 }
00497
00498 unsigned RTSPClient::sendRequest(RequestRecord* request) {
00499 char* cmd = NULL;
00500 do {
00501 Boolean connectionIsPending = False;
00502 if (!fRequestsAwaitingConnection.isEmpty()) {
00503
00504 connectionIsPending = True;
00505 } else if (fInputSocketNum < 0) {
00506 int connectResult = openConnection();
00507 if (connectResult < 0) break;
00508 else if (connectResult == 0) {
00509
00510 connectionIsPending = True;
00511 }
00512 }
00513 if (connectionIsPending) {
00514 fRequestsAwaitingConnection.enqueue(request);
00515 return request->cseq();
00516 }
00517
00518
00519 if (fTunnelOverHTTPPortNum != 0 && strcmp(request->commandName(), "GET") != 0 && fOutputSocketNum == fInputSocketNum) {
00520 if (!setupHTTPTunneling1()) break;
00521 fRequestsAwaitingHTTPTunneling.enqueue(request);
00522 return request->cseq();
00523 }
00524
00525
00526
00527
00528
00529 char* cmdURL = fBaseURL;
00530 Boolean cmdURLWasAllocated = False;
00531
00532 char const* protocolStr = "RTSP/1.0";
00533
00534 char* extraHeaders = (char*)"";
00535 Boolean extraHeadersWereAllocated = False;
00536
00537 char* contentLengthHeader = (char*)"";
00538 Boolean contentLengthHeaderWasAllocated = False;
00539
00540 char const* contentStr = request->contentStr();
00541 if (contentStr == NULL) contentStr = "";
00542 unsigned contentStrLen = strlen(contentStr);
00543 if (contentStrLen > 0) {
00544 char const* contentLengthHeaderFmt =
00545 "Content-Length: %d\r\n";
00546 unsigned contentLengthHeaderSize = strlen(contentLengthHeaderFmt)
00547 + 20 ;
00548 contentLengthHeader = new char[contentLengthHeaderSize];
00549 sprintf(contentLengthHeader, contentLengthHeaderFmt, contentStrLen);
00550 contentLengthHeaderWasAllocated = True;
00551 }
00552
00553 if (strcmp(request->commandName(), "DESCRIBE") == 0) {
00554 extraHeaders = (char*)"Accept: application/sdp\r\n";
00555 } else if (strcmp(request->commandName(), "OPTIONS") == 0) {
00556 } else if (strcmp(request->commandName(), "ANNOUNCE") == 0) {
00557 extraHeaders = (char*)"Content-Type: application/sdp\r\n";
00558 } else if (strcmp(request->commandName(), "SETUP") == 0) {
00559 MediaSubsession& subsession = *request->subsession();
00560 Boolean streamUsingTCP = (request->booleanFlags()&0x1) != 0;
00561 Boolean streamOutgoing = (request->booleanFlags()&0x2) != 0;
00562 Boolean forceMulticastOnUnspecified = (request->booleanFlags()&0x4) != 0;
00563
00564 char const *prefix, *separator, *suffix;
00565 constructSubsessionURL(subsession, prefix, separator, suffix);
00566
00567 char const* transportFmt;
00568 if (strcmp(subsession.protocolName(), "UDP") == 0) {
00569 suffix = "";
00570 transportFmt = "Transport: RAW/RAW/UDP%s%s%s=%d-%d\r\n";
00571 } else {
00572 transportFmt = "Transport: RTP/AVP%s%s%s=%d-%d\r\n";
00573 }
00574
00575 cmdURL = new char[strlen(prefix) + strlen(separator) + strlen(suffix) + 1];
00576 cmdURLWasAllocated = True;
00577 sprintf(cmdURL, "%s%s%s", prefix, separator, suffix);
00578
00579
00580 char const* transportTypeStr;
00581 char const* modeStr = streamOutgoing ? ";mode=receive" : "";
00582
00583 char const* portTypeStr;
00584 portNumBits rtpNumber, rtcpNumber;
00585 if (streamUsingTCP) {
00586 transportTypeStr = "/TCP;unicast";
00587 portTypeStr = ";interleaved";
00588 rtpNumber = fTCPStreamIdCount++;
00589 rtcpNumber = fTCPStreamIdCount++;
00590 } else {
00591 unsigned connectionAddress = subsession.connectionEndpointAddress();
00592 Boolean requestMulticastStreaming
00593 = IsMulticastAddress(connectionAddress) || (connectionAddress == 0 && forceMulticastOnUnspecified);
00594 transportTypeStr = requestMulticastStreaming ? ";multicast" : ";unicast";
00595 portTypeStr = ";client_port";
00596 rtpNumber = subsession.clientPortNum();
00597 if (rtpNumber == 0) {
00598 envir().setResultMsg("Client port number unknown\n");
00599 delete[] cmdURL;
00600 break;
00601 }
00602 rtcpNumber = rtpNumber + 1;
00603 }
00604 unsigned transportSize = strlen(transportFmt)
00605 + strlen(transportTypeStr) + strlen(modeStr) + strlen(portTypeStr) + 2*5 ;
00606 char* transportStr = new char[transportSize];
00607 sprintf(transportStr, transportFmt,
00608 transportTypeStr, modeStr, portTypeStr, rtpNumber, rtcpNumber);
00609
00610
00611 char* sessionStr = createSessionString(fLastSessionId);
00612
00613
00614 extraHeaders = new char[transportSize + strlen(sessionStr)];
00615 extraHeadersWereAllocated = True;
00616 sprintf(extraHeaders, "%s%s", transportStr, sessionStr);
00617 delete[] transportStr; delete[] sessionStr;
00618 } else if (strcmp(request->commandName(), "GET") == 0 || strcmp(request->commandName(), "POST") == 0) {
00619
00620
00621 char* username;
00622 char* password;
00623 NetAddress destAddress;
00624 portNumBits urlPortNum;
00625 if (!parseRTSPURL(envir(), fBaseURL, username, password, destAddress, urlPortNum, (char const**)&cmdURL)) break;
00626 if (cmdURL[0] == '\0') cmdURL = (char*)"/";
00627 delete[] username;
00628 delete[] password;
00629
00630 protocolStr = "HTTP/1.0";
00631
00632 if (strcmp(request->commandName(), "GET") == 0) {
00633
00634 struct {
00635 struct timeval timestamp;
00636 unsigned counter;
00637 } seedData;
00638 gettimeofday(&seedData.timestamp, NULL);
00639 seedData.counter = ++fSessionCookieCounter;
00640 our_MD5Data((unsigned char*)(&seedData), sizeof seedData, fSessionCookie);
00641
00642 fSessionCookie[23] = '\0';
00643
00644 char const* const extraHeadersFmt =
00645 "x-sessioncookie: %s\r\n"
00646 "Accept: application/x-rtsp-tunnelled\r\n"
00647 "Pragma: no-cache\r\n"
00648 "Cache-Control: no-cache\r\n";
00649 unsigned extraHeadersSize = strlen(extraHeadersFmt)
00650 + strlen(fSessionCookie);
00651 extraHeaders = new char[extraHeadersSize];
00652 extraHeadersWereAllocated = True;
00653 sprintf(extraHeaders, extraHeadersFmt,
00654 fSessionCookie);
00655 } else {
00656 char const* const extraHeadersFmt =
00657 "x-sessioncookie: %s\r\n"
00658 "Content-Type: application/x-rtsp-tunnelled\r\n"
00659 "Pragma: no-cache\r\n"
00660 "Cache-Control: no-cache\r\n"
00661 "Content-Length: 32767\r\n"
00662 "Expires: Sun, 9 Jan 1972 00:00:00 GMT\r\n";
00663 unsigned extraHeadersSize = strlen(extraHeadersFmt)
00664 + strlen(fSessionCookie);
00665 extraHeaders = new char[extraHeadersSize];
00666 extraHeadersWereAllocated = True;
00667 sprintf(extraHeaders, extraHeadersFmt,
00668 fSessionCookie);
00669 }
00670 } else {
00671
00672 if (fLastSessionId == NULL) {
00673 envir().setResultMsg("No RTSP session is currently in progress\n");
00674 break;
00675 }
00676
00677 char const* sessionId;
00678 float originalScale;
00679 if (request->session() != NULL) {
00680
00681 cmdURL = (char*)sessionURL(*request->session());
00682
00683 sessionId = fLastSessionId;
00684 originalScale = request->session()->scale();
00685 } else {
00686
00687 char const *prefix, *separator, *suffix;
00688 constructSubsessionURL(*request->subsession(), prefix, separator, suffix);
00689 cmdURL = new char[strlen(prefix) + strlen(separator) + strlen(suffix) + 1];
00690 cmdURLWasAllocated = True;
00691 sprintf(cmdURL, "%s%s%s", prefix, separator, suffix);
00692
00693 sessionId = request->subsession()->sessionId();
00694 originalScale = request->subsession()->scale();
00695 }
00696
00697 if (strcmp(request->commandName(), "PLAY") == 0) {
00698
00699 char* sessionStr = createSessionString(sessionId);
00700 char* scaleStr = createScaleString(request->scale(), originalScale);
00701 char* rangeStr = createRangeString(request->start(), request->end());
00702 extraHeaders = new char[strlen(sessionStr) + strlen(scaleStr) + strlen(rangeStr) + 1];
00703 extraHeadersWereAllocated = True;
00704 sprintf(extraHeaders, "%s%s%s", sessionStr, scaleStr, rangeStr);
00705 delete[] sessionStr; delete[] scaleStr; delete[] rangeStr;
00706 } else {
00707
00708 extraHeaders = createSessionString(sessionId);
00709 extraHeadersWereAllocated = True;
00710 }
00711 }
00712
00713 char* authenticatorStr = createAuthenticatorString(request->commandName(), fBaseURL);
00714
00715 char const* const cmdFmt =
00716 "%s %s %s\r\n"
00717 "CSeq: %d\r\n"
00718 "%s"
00719 "%s"
00720 "%s"
00721 "%s"
00722 "\r\n"
00723 "%s";
00724 unsigned cmdSize = strlen(cmdFmt)
00725 + strlen(request->commandName()) + strlen(cmdURL) + strlen(protocolStr)
00726 + 20
00727 + strlen(authenticatorStr)
00728 + fUserAgentHeaderStrLen
00729 + strlen(extraHeaders)
00730 + strlen(contentLengthHeader)
00731 + contentStrLen;
00732 cmd = new char[cmdSize];
00733 sprintf(cmd, cmdFmt,
00734 request->commandName(), cmdURL, protocolStr,
00735 request->cseq(),
00736 authenticatorStr,
00737 fUserAgentHeaderStr,
00738 extraHeaders,
00739 contentLengthHeader,
00740 contentStr);
00741 delete[] authenticatorStr;
00742 if (cmdURLWasAllocated) delete[] cmdURL;
00743 if (extraHeadersWereAllocated) delete[] extraHeaders;
00744 if (contentLengthHeaderWasAllocated) delete[] contentLengthHeader;
00745
00746 if (fVerbosityLevel >= 1) envir() << "Sending request: " << cmd << "\n";
00747
00748 if (fTunnelOverHTTPPortNum != 0 && strcmp(request->commandName(), "GET") != 0 && strcmp(request->commandName(), "POST") != 0) {
00749
00750
00751 char* origCmd = cmd;
00752 cmd = base64Encode(origCmd, strlen(cmd));
00753 if (fVerbosityLevel >= 1) envir() << "\tThe request was base-64 encoded to: " << cmd << "\n\n";
00754 delete[] origCmd;
00755 }
00756
00757 if (send(fOutputSocketNum, cmd, strlen(cmd), 0) < 0) {
00758 char const* errFmt = "%s send() failed: ";
00759 unsigned const errLength = strlen(errFmt) + strlen(request->commandName());
00760 char* err = new char[errLength];
00761 sprintf(err, errFmt, request->commandName());
00762 envir().setResultErrMsg(err);
00763 delete[] err;
00764 break;
00765 }
00766
00767
00768 fRequestsAwaitingResponse.enqueue(request);
00769
00770 delete[] cmd;
00771 return request->cseq();
00772 } while (0);
00773
00774
00775 delete[] cmd;
00776 handleRequestError(request);
00777 delete request;
00778 return 0;
00779 }
00780
00781 void RTSPClient::handleRequestError(RequestRecord* request) {
00782 int resultCode = -envir().getErrno();
00783 if (resultCode == 0) {
00784
00785 #if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
00786 resultCode = -WSAENOTCONN;
00787 #else
00788 resultCode = -ENOTCONN;
00789 #endif
00790 }
00791 if (request->handler() != NULL) (*request->handler())(this, resultCode, strDup(envir().getResultMsg()));
00792 }
00793
00794 Boolean RTSPClient
00795 ::parseResponseCode(char const* line, unsigned& responseCode, char const*& responseString) {
00796 if (sscanf(line, "RTSP/%*s%u", &responseCode) != 1 &&
00797 sscanf(line, "HTTP/%*s%u", &responseCode) != 1) return False;
00798
00799
00800
00801
00802 responseString = line;
00803 while (responseString[0] != '\0' && responseString[0] != ' ' && responseString[0] != '\t') ++responseString;
00804 while (responseString[0] != '\0' && (responseString[0] == ' ' || responseString[0] == '\t')) ++responseString;
00805
00806 return True;
00807 }
00808
00809 void RTSPClient::handleIncomingRequest() {
00810
00811 char cmdName[RTSP_PARAM_STRING_MAX];
00812 char urlPreSuffix[RTSP_PARAM_STRING_MAX];
00813 char urlSuffix[RTSP_PARAM_STRING_MAX];
00814 char cseq[RTSP_PARAM_STRING_MAX];
00815 unsigned contentLength;
00816 if (!parseRTSPRequestString(fResponseBuffer, fResponseBytesAlreadySeen,
00817 cmdName, sizeof cmdName,
00818 urlPreSuffix, sizeof urlPreSuffix,
00819 urlSuffix, sizeof urlSuffix,
00820 cseq, sizeof cseq,
00821 contentLength)) {
00822 return;
00823 } else {
00824 if (fVerbosityLevel >= 1) {
00825 envir() << "Received incoming RTSP request: " << fResponseBuffer << "\n";
00826 }
00827 char tmpBuf[2*RTSP_PARAM_STRING_MAX];
00828 snprintf((char*)tmpBuf, sizeof tmpBuf,
00829 "RTSP/1.0 405 Method Not Allowed\r\nCSeq: %s\r\n\r\n", cseq);
00830 send(fOutputSocketNum, tmpBuf, strlen(tmpBuf), 0);
00831 }
00832 }
00833
00834 Boolean RTSPClient::checkForHeader(char const* line, char const* headerName, unsigned headerNameLength, char const*& headerParams) {
00835 if (_strncasecmp(line, headerName, headerNameLength) != 0) return False;
00836
00837
00838 unsigned paramIndex = headerNameLength;
00839 while (line[paramIndex] != '\0' && (line[paramIndex] == ' ' || line[paramIndex] == '\t')) ++paramIndex;
00840 if (&line[paramIndex] == '\0') return False;
00841
00842 headerParams = &line[paramIndex];
00843 return True;
00844 }
00845
00846 Boolean RTSPClient::parseTransportParams(char const* paramsStr,
00847 char*& serverAddressStr, portNumBits& serverPortNum,
00848 unsigned char& rtpChannelId, unsigned char& rtcpChannelId) {
00849
00850 serverAddressStr = NULL;
00851 serverPortNum = 0;
00852 rtpChannelId = rtcpChannelId = 0xFF;
00853
00854 char* foundServerAddressStr = NULL;
00855 Boolean foundServerPortNum = False;
00856 portNumBits clientPortNum = 0;
00857 Boolean foundClientPortNum = False;
00858 Boolean foundChannelIds = False;
00859 unsigned rtpCid, rtcpCid;
00860 Boolean isMulticast = True;
00861 char* foundDestinationStr = NULL;
00862 portNumBits multicastPortNumRTP, multicastPortNumRTCP;
00863 Boolean foundMulticastPortNum = False;
00864
00865
00866 char const* fields = paramsStr;
00867 char* field = strDupSize(fields);
00868 while (sscanf(fields, "%[^;]", field) == 1) {
00869 if (sscanf(field, "server_port=%hu", &serverPortNum) == 1) {
00870 foundServerPortNum = True;
00871 } else if (sscanf(field, "client_port=%hu", &clientPortNum) == 1) {
00872 foundClientPortNum = True;
00873 } else if (_strncasecmp(field, "source=", 7) == 0) {
00874 delete[] foundServerAddressStr;
00875 foundServerAddressStr = strDup(field+7);
00876 } else if (sscanf(field, "interleaved=%u-%u", &rtpCid, &rtcpCid) == 2) {
00877 rtpChannelId = (unsigned char)rtpCid;
00878 rtcpChannelId = (unsigned char)rtcpCid;
00879 foundChannelIds = True;
00880 } else if (strcmp(field, "unicast") == 0) {
00881 isMulticast = False;
00882 } else if (_strncasecmp(field, "destination=", 12) == 0) {
00883 delete[] foundDestinationStr;
00884 foundDestinationStr = strDup(field+12);
00885 } else if (sscanf(field, "port=%hu-%hu", &multicastPortNumRTP, &multicastPortNumRTCP) == 2 ||
00886 sscanf(field, "port=%hu", &multicastPortNumRTP) == 1) {
00887 foundMulticastPortNum = True;
00888 }
00889
00890 fields += strlen(field);
00891 while (fields[0] == ';') ++fields;
00892 if (fields[0] == '\0') break;
00893 }
00894 delete[] field;
00895
00896
00897
00898
00899 if (isMulticast && foundDestinationStr != NULL && foundMulticastPortNum) {
00900 delete[] foundServerAddressStr;
00901 serverAddressStr = foundDestinationStr;
00902 serverPortNum = multicastPortNumRTP;
00903 return True;
00904 }
00905 delete[] foundDestinationStr;
00906
00907
00908
00909
00910
00911
00912 if (foundChannelIds || foundServerPortNum || foundClientPortNum) {
00913 if (foundClientPortNum && !foundServerPortNum) {
00914 serverPortNum = clientPortNum;
00915 }
00916 serverAddressStr = foundServerAddressStr;
00917 return True;
00918 }
00919
00920 delete[] foundServerAddressStr;
00921 return False;
00922 }
00923
00924 Boolean RTSPClient::parseScaleParam(char const* paramStr, float& scale) {
00925 Locale l("C", Numeric);
00926 return sscanf(paramStr, "%f", &scale) == 1;
00927 }
00928
00929 Boolean RTSPClient::parseRTPInfoParams(char const*& paramsStr, u_int16_t& seqNum, u_int32_t& timestamp) {
00930 while (paramsStr[0] == ',') ++paramsStr;
00931
00932
00933 char* field = strDupSize(paramsStr);
00934
00935 while (sscanf(paramsStr, "%[^;,]", field) == 1) {
00936 if (sscanf(field, "seq=%hu", &seqNum) == 1 ||
00937 sscanf(field, "rtptime=%u", ×tamp) == 1) {
00938 }
00939
00940 paramsStr += strlen(field);
00941 if (paramsStr[0] == '\0' || paramsStr[0] == ',') break;
00942
00943 ++paramsStr;
00944 }
00945
00946 delete[] field;
00947 return True;
00948 }
00949
00950 Boolean RTSPClient::handleSETUPResponse(MediaSubsession& subsession, char const* sessionParamsStr, char const* transportParamsStr,
00951 Boolean streamUsingTCP) {
00952 char* sessionId = new char[responseBufferSize];
00953 Boolean success = False;
00954 do {
00955
00956 if (sessionParamsStr == NULL || sscanf(sessionParamsStr, "%[^;]", sessionId) != 1) {
00957 envir().setResultMsg("Missing or bad \"Session:\" header");
00958 break;
00959 }
00960 subsession.setSessionId(sessionId);
00961 delete[] fLastSessionId; fLastSessionId = strDup(sessionId);
00962
00963
00964 char const* afterSessionId = sessionParamsStr + strlen(sessionId);
00965 int timeoutVal;
00966 if (sscanf(afterSessionId, "; timeout = %d", &timeoutVal) == 1) {
00967 fSessionTimeoutParameter = timeoutVal;
00968 }
00969
00970
00971 char* serverAddressStr;
00972 portNumBits serverPortNum;
00973 unsigned char rtpChannelId, rtcpChannelId;
00974 if (!parseTransportParams(transportParamsStr, serverAddressStr, serverPortNum, rtpChannelId, rtcpChannelId)) {
00975 envir().setResultMsg("Missing or bad \"Transport:\" header");
00976 break;
00977 }
00978 delete[] subsession.connectionEndpointName();
00979 subsession.connectionEndpointName() = serverAddressStr;
00980 subsession.serverPortNum = serverPortNum;
00981 subsession.rtpChannelId = rtpChannelId;
00982 subsession.rtcpChannelId = rtcpChannelId;
00983
00984 if (streamUsingTCP) {
00985
00986 if (subsession.rtpSource() != NULL) {
00987 subsession.rtpSource()->setStreamSocket(fInputSocketNum, subsession.rtpChannelId);
00988 subsession.rtpSource()->setServerRequestAlternativeByteHandler(fInputSocketNum, handleAlternativeRequestByte, this);
00989 }
00990 if (subsession.rtcpInstance() != NULL) subsession.rtcpInstance()->setStreamSocket(fInputSocketNum, subsession.rtcpChannelId);
00991 } else {
00992
00993
00994 netAddressBits destAddress = subsession.connectionEndpointAddress();
00995 if (destAddress == 0) destAddress = fServerAddress;
00996 subsession.setDestinations(destAddress);
00997
00998
00999
01000
01001 if (subsession.rtpSource() != NULL) {
01002 Groupsock* gs = subsession.rtpSource()->RTPgs();
01003 if (gs != NULL) {
01004 u_int32_t dummy = 0xFEEDFACE;
01005 unsigned const numDummyPackets = 2;
01006 for (unsigned i = 0; i < numDummyPackets; ++i) gs->output(envir(), 255, (unsigned char*)&dummy, sizeof dummy);
01007 }
01008 }
01009 }
01010
01011 success = True;
01012 } while (0);
01013
01014 delete[] sessionId;
01015 return success;
01016 }
01017
01018 Boolean RTSPClient::handlePLAYResponse(MediaSession& session, MediaSubsession& subsession,
01019 char const* scaleParamsStr, char const* rangeParamsStr, char const* rtpInfoParamsStr) {
01020 Boolean scaleOK = False, rangeOK = False;
01021 do {
01022 if (&session != NULL) {
01023
01024 if (scaleParamsStr != NULL && !parseScaleParam(scaleParamsStr, session.scale())) break;
01025 scaleOK = True;
01026 if (rangeParamsStr != NULL && !parseRangeParam(rangeParamsStr, session.playStartTime(), session.playEndTime())) break;
01027 rangeOK = True;
01028
01029 u_int16_t seqNum; u_int32_t timestamp;
01030 if (rtpInfoParamsStr != NULL) {
01031 if (!parseRTPInfoParams(rtpInfoParamsStr, seqNum, timestamp)) break;
01032
01033 MediaSubsessionIterator iter(session);
01034 MediaSubsession* subsession;
01035 while ((subsession = iter.next()) != NULL) {
01036 subsession->rtpInfo.seqNum = seqNum;
01037 subsession->rtpInfo.timestamp = timestamp;
01038 subsession->rtpInfo.infoIsNew = True;
01039
01040 if (!parseRTPInfoParams(rtpInfoParamsStr, seqNum, timestamp)) break;
01041 }
01042 }
01043 } else {
01044
01045 if (scaleParamsStr != NULL && !parseScaleParam(scaleParamsStr, subsession.scale())) break;
01046 scaleOK = True;
01047 if (rangeParamsStr != NULL && !parseRangeParam(rangeParamsStr, subsession._playStartTime(), subsession._playEndTime())) break;
01048 rangeOK = True;
01049
01050 u_int16_t seqNum; u_int32_t timestamp;
01051 if (rtpInfoParamsStr != NULL) {
01052 if (!parseRTPInfoParams(rtpInfoParamsStr, seqNum, timestamp)) break;
01053 subsession.rtpInfo.seqNum = seqNum;
01054 subsession.rtpInfo.timestamp = timestamp;
01055 subsession.rtpInfo.infoIsNew = True;
01056 }
01057 }
01058
01059 return True;
01060 } while (0);
01061
01062
01063 if (!scaleOK) {
01064 envir().setResultMsg("Bad \"Scale:\" header");
01065 } else if (!rangeOK) {
01066 envir().setResultMsg("Bad \"Range:\" header");
01067 } else {
01068 envir().setResultMsg("Bad \"RTP-Info:\" header");
01069 }
01070 return False;
01071 }
01072
01073 Boolean RTSPClient::handleTEARDOWNResponse(MediaSession& , MediaSubsession& ) {
01074
01075 return True;
01076 }
01077
01078 Boolean RTSPClient::handleGET_PARAMETERResponse(char const* parameterName, char*& resultValueString) {
01079 do {
01080
01081 if (parameterName != NULL && parameterName[0] != '\0') {
01082 if (parameterName[1] == '\0') break;
01083
01084 unsigned parameterNameLen = strlen(parameterName);
01085
01086 parameterNameLen -= 2;
01087 if (_strncasecmp(resultValueString, parameterName, parameterNameLen) != 0) {
01088
01089 resultValueString[0] = '\0';
01090 return True;
01091 }
01092 resultValueString += parameterNameLen;
01093 if (resultValueString[0] == ':') ++resultValueString;
01094 while (resultValueString[0] == ' ' || resultValueString[0] == '\t') ++resultValueString;
01095 }
01096
01097
01098 unsigned resultLen = strlen(resultValueString);
01099 while (resultLen > 0 && (resultValueString[resultLen-1] == '\r' || resultValueString[resultLen-1] == '\n')) --resultLen;
01100 resultValueString[resultLen] = '\0';
01101
01102 return True;
01103 } while (0);
01104
01105
01106 envir().setResultMsg("Bad \"GET_PARAMETER\" response");
01107 return False;
01108 }
01109
01110 Boolean RTSPClient::handleAuthenticationFailure(char const* paramsStr) {
01111 if (paramsStr == NULL) return False;
01112
01113
01114 Boolean alreadyHadRealm = fCurrentAuthenticator.realm() != NULL;
01115 char* realm = strDupSize(paramsStr);
01116 char* nonce = strDupSize(paramsStr);
01117 Boolean success = True;
01118 if (sscanf(paramsStr, "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"", realm, nonce) == 2) {
01119 fCurrentAuthenticator.setRealmAndNonce(realm, nonce);
01120 } else if (sscanf(paramsStr, "Basic realm=\"%[^\"]\"", realm) == 1) {
01121 fCurrentAuthenticator.setRealmAndNonce(realm, NULL);
01122 } else {
01123 success = False;
01124 }
01125 delete[] realm; delete[] nonce;
01126
01127 if (alreadyHadRealm || fCurrentAuthenticator.username() == NULL || fCurrentAuthenticator.password() == NULL) {
01128
01129
01130 success = False;
01131 }
01132
01133 return success;
01134 }
01135
01136 Boolean RTSPClient::resendCommand(RequestRecord* request) {
01137 if (fVerbosityLevel >= 1) envir() << "Resending...\n";
01138 if (request != NULL && strcmp(request->commandName(), "GET") != 0) request->cseq() = ++fCSeq;
01139 return sendRequest(request) != 0;
01140 }
01141
01142 char const* RTSPClient::sessionURL(MediaSession const& session) const {
01143 char const* url = session.controlPath();
01144 if (url == NULL || strcmp(url, "*") == 0) url = fBaseURL;
01145
01146 return url;
01147 }
01148
01149 void RTSPClient::handleAlternativeRequestByte(void* rtspClient, u_int8_t requestByte) {
01150 ((RTSPClient*)rtspClient)->handleAlternativeRequestByte1(requestByte);
01151 }
01152
01153 void RTSPClient::handleAlternativeRequestByte1(u_int8_t requestByte) {
01154 fResponseBuffer[fResponseBytesAlreadySeen] = requestByte;
01155 handleResponseBytes(1);
01156 }
01157
01158 static Boolean isAbsoluteURL(char const* url) {
01159
01160
01161 while (*url != '\0' && *url != '/') {
01162 if (*url == ':') return True;
01163 ++url;
01164 }
01165
01166 return False;
01167 }
01168
01169 void RTSPClient::constructSubsessionURL(MediaSubsession const& subsession,
01170 char const*& prefix,
01171 char const*& separator,
01172 char const*& suffix) {
01173
01174
01175
01176
01177
01178
01179
01180
01181
01182
01183
01184 prefix = sessionURL(subsession.parentSession());
01185 if (prefix == NULL) prefix = "";
01186
01187 suffix = subsession.controlPath();
01188 if (suffix == NULL) suffix = "";
01189
01190 if (isAbsoluteURL(suffix)) {
01191 prefix = separator = "";
01192 } else {
01193 unsigned prefixLen = strlen(prefix);
01194 separator = (prefixLen == 0 || prefix[prefixLen-1] == '/' || suffix[0] == '/') ? "" : "/";
01195 }
01196 }
01197
01198 Boolean RTSPClient::setupHTTPTunneling1() {
01199
01200
01201 if (fVerbosityLevel >= 1) {
01202 envir() << "Requesting RTSP-over-HTTP tunneling (on port " << fTunnelOverHTTPPortNum << ")\n\n";
01203 }
01204
01205
01206 return sendRequest(new RequestRecord(1, "GET", responseHandlerForHTTP_GET)) != 0;
01207 }
01208
01209 void RTSPClient::responseHandlerForHTTP_GET(RTSPClient* rtspClient, int responseCode, char* responseString) {
01210 if (rtspClient != NULL) rtspClient->responseHandlerForHTTP_GET1(responseCode, responseString);
01211 }
01212
01213 void RTSPClient::responseHandlerForHTTP_GET1(int responseCode, char* responseString) {
01214 RequestRecord* request;
01215 do {
01216 if (responseCode != 0) break;
01217
01218
01219
01220 fOutputSocketNum = setupStreamSocket(envir(), 0);
01221 if (fOutputSocketNum < 0) break;
01222
01223 fHTTPTunnelingConnectionIsPending = True;
01224 int connectResult = connectToServer(fOutputSocketNum, fTunnelOverHTTPPortNum);
01225 if (connectResult < 0) break;
01226 else if (connectResult == 0) {
01227
01228
01229 while ((request = fRequestsAwaitingHTTPTunneling.dequeue()) != NULL) {
01230 fRequestsAwaitingConnection.enqueue(request);
01231 }
01232 return;
01233 }
01234
01235
01236 if (!setupHTTPTunneling2()) break;
01237
01238
01239 while ((request = fRequestsAwaitingHTTPTunneling.dequeue()) != NULL) {
01240 sendRequest(request);
01241 }
01242 return;
01243 } while (0);
01244
01245
01246 fHTTPTunnelingConnectionIsPending = False;
01247 while ((request = fRequestsAwaitingHTTPTunneling.dequeue()) != NULL) {
01248 handleRequestError(request);
01249 delete request;
01250 }
01251 resetTCPSockets();
01252 }
01253
01254 Boolean RTSPClient::setupHTTPTunneling2() {
01255 fHTTPTunnelingConnectionIsPending = False;
01256
01257
01258 return sendRequest(new RequestRecord(1, "POST", NULL)) != 0;
01259 }
01260
01261 void RTSPClient::connectionHandler(void* instance, int ) {
01262 RTSPClient* client = (RTSPClient*)instance;
01263 client->connectionHandler1();
01264 }
01265
01266 void RTSPClient::connectionHandler1() {
01267
01268 envir().taskScheduler().disableBackgroundHandling(fOutputSocketNum);
01269 envir().taskScheduler().setBackgroundHandling(fInputSocketNum, SOCKET_READABLE,
01270 (TaskScheduler::BackgroundHandlerProc*)&incomingDataHandler, this);
01271
01272
01273
01274 RequestQueue tmpRequestQueue;
01275 RequestRecord* request;
01276 while ((request = fRequestsAwaitingConnection.dequeue()) != NULL) {
01277 tmpRequestQueue.enqueue(request);
01278 }
01279
01280
01281 do {
01282 int err = 0;
01283 SOCKLEN_T len = sizeof err;
01284 if (getsockopt(fInputSocketNum, SOL_SOCKET, SO_ERROR, (char*)&err, &len) < 0 || err != 0) {
01285 envir().setResultErrMsg("Connection to server failed: ", err);
01286 if (fVerbosityLevel >= 1) envir() << "..." << envir().getResultMsg() << "\n";
01287 break;
01288 }
01289
01290
01291 if (fVerbosityLevel >= 1) envir() << "...remote connection opened\n";
01292 if (fHTTPTunnelingConnectionIsPending && !setupHTTPTunneling2()) break;
01293
01294
01295 while ((request = tmpRequestQueue.dequeue()) != NULL) {
01296 sendRequest(request);
01297 }
01298 return;
01299 } while (0);
01300
01301
01302 while ((request = tmpRequestQueue.dequeue()) != NULL) {
01303 handleRequestError(request);
01304 delete request;
01305 }
01306 resetTCPSockets();
01307 }
01308
01309 void RTSPClient::incomingDataHandler(void* instance, int ) {
01310 RTSPClient* client = (RTSPClient*)instance;
01311 client->incomingDataHandler1();
01312 }
01313
01314 void RTSPClient::incomingDataHandler1() {
01315 struct sockaddr_in dummy;
01316
01317 int bytesRead = readSocket(envir(), fInputSocketNum, (unsigned char*)&fResponseBuffer[fResponseBytesAlreadySeen], fResponseBufferBytesLeft, dummy);
01318 handleResponseBytes(bytesRead);
01319 }
01320
01321 static char* getLine(char* startOfLine) {
01322
01323 for (char* ptr = startOfLine; *ptr != '\0'; ++ptr) {
01324
01325 if (*ptr == '\r' || *ptr == '\n') {
01326
01327 if (*ptr == '\r') {
01328 *ptr++ = '\0';
01329 if (*ptr == '\n') ++ptr;
01330 } else {
01331 *ptr++ = '\0';
01332 }
01333 return ptr;
01334 }
01335 }
01336
01337 return NULL;
01338 }
01339
01340 void RTSPClient::handleResponseBytes(int newBytesRead) {
01341 do {
01342 if (newBytesRead > 0 && (unsigned)newBytesRead < fResponseBufferBytesLeft) break;
01343
01344 if ((unsigned)newBytesRead >= fResponseBufferBytesLeft) {
01345
01346 envir().setResultMsg("RTSP response was truncated. Increase \"RTSPClient::responseBufferSize\"");
01347 }
01348
01349
01350 RequestRecord* request;
01351 while ((request = fRequestsAwaitingResponse.dequeue()) != NULL) {
01352 handleRequestError(request);
01353 delete request;
01354
01355 if (newBytesRead > 0) break;
01356 }
01357
01358 if (newBytesRead <= 0) resetTCPSockets();
01359 resetResponseBuffer();
01360 return;
01361 } while (0);
01362
01363 fResponseBufferBytesLeft -= newBytesRead;
01364 fResponseBytesAlreadySeen += newBytesRead;
01365 fResponseBuffer[fResponseBytesAlreadySeen] = '\0';
01366 if (fVerbosityLevel >= 1 && newBytesRead > 1) envir() << "Received " << newBytesRead << " new bytes of response data.\n";
01367
01368
01369
01370 Boolean endOfHeaders = False;
01371 if (fResponseBytesAlreadySeen > 3) {
01372 char const* const ptrEnd = &fResponseBuffer[fResponseBytesAlreadySeen-3];
01373 char const* ptr = fResponseBuffer;
01374 while (ptr < ptrEnd) {
01375 if (*ptr++ == '\r' && *ptr++ == '\n' && *ptr++ == '\r' && *ptr++ == '\n') {
01376
01377 endOfHeaders = True;
01378 break;
01379 }
01380 }
01381 }
01382
01383 if (!endOfHeaders) return;
01384
01385
01386
01387
01388 char* headerDataCopy;
01389 unsigned responseCode = 200;
01390 char const* responseStr = NULL;
01391 RequestRecord* foundRequest = NULL;
01392 char const* sessionParamsStr = NULL;
01393 char const* transportParamsStr = NULL;
01394 char const* scaleParamsStr = NULL;
01395 char const* rangeParamsStr = NULL;
01396 char const* rtpInfoParamsStr = NULL;
01397 char const* wwwAuthenticateParamsStr = NULL;
01398 char const* publicParamsStr = NULL;
01399 char* bodyStart = NULL;
01400 unsigned numBodyBytes = 0;
01401 Boolean responseSuccess = False;
01402 do {
01403 headerDataCopy = new char[responseBufferSize];
01404 strncpy(headerDataCopy, fResponseBuffer, fResponseBytesAlreadySeen);
01405 headerDataCopy[fResponseBytesAlreadySeen] = '\0';
01406
01407 char* lineStart = headerDataCopy;
01408 char* nextLineStart = getLine(lineStart);
01409 if (!parseResponseCode(lineStart, responseCode, responseStr)) {
01410
01411 handleIncomingRequest();
01412 break;
01413 }
01414
01415
01416 Boolean reachedEndOfHeaders;
01417 unsigned cseq = 0;
01418 unsigned contentLength = 0;
01419
01420 while (1) {
01421 reachedEndOfHeaders = True;
01422 lineStart = nextLineStart;
01423 if (lineStart == NULL) break;
01424
01425 nextLineStart = getLine(lineStart);
01426 if (lineStart[0] == '\0') break;
01427 reachedEndOfHeaders = False;
01428
01429 char const* headerParamsStr;
01430 if (checkForHeader(lineStart, "CSeq:", 5, headerParamsStr)) {
01431 if (sscanf(headerParamsStr, "%u", &cseq) != 1 || cseq <= 0) {
01432 envir().setResultMsg("Bad \"CSeq:\" header: \"", lineStart, "\"");
01433 break;
01434 }
01435
01436 RequestRecord* request;
01437 while ((request = fRequestsAwaitingResponse.dequeue()) != NULL) {
01438 if (request->cseq() < cseq) {
01439
01440 delete request;
01441 } else if (request->cseq() == cseq) {
01442
01443 foundRequest = request;
01444 break;
01445 } else {
01446
01447 break;
01448 }
01449 }
01450 } else if (checkForHeader(lineStart, "Content-Length:", 15, headerParamsStr)) {
01451 if (sscanf(headerParamsStr, "%u", &contentLength) != 1) {
01452 envir().setResultMsg("Bad \"Content-Length:\" header: \"", lineStart, "\"");
01453 break;
01454 }
01455 } else if (checkForHeader(lineStart, "Content-Base:", 13, headerParamsStr)) {
01456 setBaseURL(headerParamsStr);
01457 } else if (checkForHeader(lineStart, "Session:", 8, sessionParamsStr)) {
01458 } else if (checkForHeader(lineStart, "Transport:", 10, transportParamsStr)) {
01459 } else if (checkForHeader(lineStart, "Scale:", 6, scaleParamsStr)) {
01460 } else if (checkForHeader(lineStart, "Range:", 6, rangeParamsStr)) {
01461 } else if (checkForHeader(lineStart, "RTP-Info:", 9, rtpInfoParamsStr)) {
01462 } else if (checkForHeader(lineStart, "WWW-Authenticate:", 17, headerParamsStr)) {
01463
01464
01465 if (wwwAuthenticateParamsStr == NULL || _strncasecmp(headerParamsStr, "Digest", 6) == 0) {
01466 wwwAuthenticateParamsStr = headerParamsStr;
01467 }
01468 } else if (checkForHeader(lineStart, "Public:", 7, publicParamsStr)) {
01469 } else if (checkForHeader(lineStart, "Allow:", 6, publicParamsStr)) {
01470
01471 } else if (checkForHeader(lineStart, "Location:", 9, headerParamsStr)) {
01472 setBaseURL(headerParamsStr);
01473 }
01474 }
01475 if (!reachedEndOfHeaders) break;
01476
01477 if (foundRequest == NULL) {
01478
01479 foundRequest = fRequestsAwaitingResponse.dequeue();
01480 }
01481
01482
01483 unsigned bodyOffset = nextLineStart - headerDataCopy;
01484 bodyStart = &fResponseBuffer[bodyOffset];
01485 numBodyBytes = fResponseBytesAlreadySeen - bodyOffset;
01486 if (contentLength > numBodyBytes) {
01487
01488 unsigned numExtraBytesNeeded = contentLength - numBodyBytes;
01489 unsigned remainingBufferSize = responseBufferSize - fResponseBytesAlreadySeen;
01490 if (numExtraBytesNeeded > remainingBufferSize) {
01491 char tmpBuf[200];
01492 sprintf(tmpBuf, "Response buffer size (%d) is too small for \"Content-Length:\" %d (need a buffer size of >= %d bytes\n",
01493 responseBufferSize, contentLength, fResponseBytesAlreadySeen + numExtraBytesNeeded);
01494 envir().setResultMsg(tmpBuf);
01495 break;
01496 }
01497
01498 if (fVerbosityLevel >= 1) {
01499 envir() << "Have received " << fResponseBytesAlreadySeen << " total bytes of a "
01500 << (foundRequest != NULL ? foundRequest->commandName() : "(unknown)")
01501 << " RTSP response; awaiting " << numExtraBytesNeeded << " bytes more.\n";
01502 }
01503 delete[] headerDataCopy;
01504 if (foundRequest != NULL) fRequestsAwaitingResponse.putAtHead(foundRequest);
01505 return;
01506 }
01507
01508
01509 if (fVerbosityLevel >= 1) {
01510 envir() << "Received a complete "
01511 << (foundRequest != NULL ? foundRequest->commandName() : "(unknown)")
01512 << " response:\n" << fResponseBuffer << "\n";
01513 }
01514
01515 if (foundRequest != NULL) {
01516 Boolean needToResendCommand = False;
01517 if (responseCode == 200) {
01518
01519 if (strcmp(foundRequest->commandName(), "SETUP") == 0) {
01520 if (!handleSETUPResponse(*foundRequest->subsession(), sessionParamsStr, transportParamsStr, foundRequest->booleanFlags()&0x1)) break;
01521 } else if (strcmp(foundRequest->commandName(), "PLAY") == 0) {
01522 if (!handlePLAYResponse(*foundRequest->session(), *foundRequest->subsession(), scaleParamsStr, rangeParamsStr, rtpInfoParamsStr)) break;
01523 } else if (strcmp(foundRequest->commandName(), "TEARDOWN") == 0) {
01524 if (!handleTEARDOWNResponse(*foundRequest->session(), *foundRequest->subsession())) break;
01525 } else if (strcmp(foundRequest->commandName(), "GET_PARAMETER") == 0) {
01526 if (!handleGET_PARAMETERResponse(foundRequest->contentStr(), bodyStart)) break;
01527 }
01528 } else if (responseCode == 401 && handleAuthenticationFailure(wwwAuthenticateParamsStr)) {
01529
01530 needToResendCommand = True;
01531
01532 if (strcmp(foundRequest->commandName(), "GET") == 0) {
01533
01534
01535
01536 resetTCPSockets();
01537 }
01538 } else if (responseCode == 301 || responseCode == 302) {
01539 resetTCPSockets();
01540 needToResendCommand = True;
01541 }
01542
01543 if (needToResendCommand) {
01544 resetResponseBuffer();
01545 if (!resendCommand(foundRequest)) break;
01546 delete[] headerDataCopy;
01547 return;
01548 }
01549 }
01550
01551 responseSuccess = True;
01552 } while (0);
01553
01554
01555 resetResponseBuffer();
01556 if (foundRequest != NULL && foundRequest->handler() != NULL) {
01557 int resultCode;
01558 char* resultString;
01559 if (responseSuccess) {
01560 if (responseCode == 200) {
01561 resultCode = 0;
01562 resultString = numBodyBytes != 0 ? strDup(bodyStart) : strDup(publicParamsStr);
01563
01564 } else {
01565 resultCode = responseCode;
01566 resultString = strDup(responseStr);
01567 envir().setResultMsg(responseStr);
01568 }
01569 (*foundRequest->handler())(this, resultCode, resultString);
01570 } else {
01571
01572 handleRequestError(foundRequest);
01573 }
01574 }
01575 delete foundRequest;
01576 delete[] headerDataCopy;
01577 }
01578
01579
01581
01582 RTSPClient::RequestRecord::RequestRecord(unsigned cseq, char const* commandName, responseHandler* handler,
01583 MediaSession* session, MediaSubsession* subsession, u_int32_t booleanFlags,
01584 double start, double end, float scale, char const* contentStr)
01585 : fNext(NULL), fCSeq(cseq), fCommandName(commandName), fSession(session), fSubsession(subsession), fBooleanFlags(booleanFlags),
01586 fStart(start), fEnd(end), fScale(scale), fContentStr(strDup(contentStr)), fHandler(handler) {
01587 }
01588
01589 RTSPClient::RequestRecord::~RequestRecord() {
01590
01591 delete fNext;
01592
01593 delete[] fContentStr;
01594 }
01595
01596
01598
01599 RTSPClient::RequestQueue::RequestQueue()
01600 : fHead(NULL), fTail(NULL) {
01601 }
01602
01603 RTSPClient::RequestQueue::~RequestQueue() {
01604 delete fHead;
01605 }
01606
01607 void RTSPClient::RequestQueue::enqueue(RequestRecord* request) {
01608 if (fTail == NULL) {
01609 fHead = request;
01610 } else {
01611 fTail->next() = request;
01612 }
01613 fTail = request;
01614 }
01615
01616 RTSPClient::RequestRecord* RTSPClient::RequestQueue::dequeue() {
01617 RequestRecord* request = fHead;
01618 if (fHead == fTail) {
01619 fHead = NULL;
01620 fTail = NULL;
01621 } else {
01622 fHead = fHead->next();
01623 }
01624 if (request != NULL) request->next() = NULL;
01625 return request;
01626 }
01627
01628 void RTSPClient::RequestQueue::putAtHead(RequestRecord* request) {
01629 request->next() = fHead;
01630 fHead = request;
01631 if (fTail == NULL) {
01632 fTail = request;
01633 }
01634 }
01635
01636 RTSPClient::RequestRecord* RTSPClient::RequestQueue::findByCSeq(unsigned cseq) {
01637 RequestRecord* request;
01638 for (request = fHead; request != NULL; request = request->next()) {
01639 if (request->cseq() == cseq) return request;
01640 }
01641 return NULL;
01642 }
01643
01644
01645 #ifdef RTSPCLIENT_SYNCHRONOUS_INTERFACE
01646
01647 RTSPClient* RTSPClient::createNew(UsageEnvironment& env,
01648 int verbosityLevel,
01649 char const* applicationName,
01650 portNumBits tunnelOverHTTPPortNum) {
01651 return new RTSPClient(env, NULL,
01652 verbosityLevel, applicationName, tunnelOverHTTPPortNum);
01653 }
01654
01655 char* RTSPClient::describeURL(char const* url, Authenticator* authenticator,
01656 Boolean allowKasennaProtocol, int timeout) {
01657
01658
01659
01660
01661
01662 char* username; char* password;
01663 if (authenticator == NULL
01664 && parseRTSPURLUsernamePassword(url, username, password)) {
01665 char* result = describeWithPassword(url, username, password, allowKasennaProtocol, timeout);
01666 delete[] username; delete[] password;
01667 return result;
01668 }
01669
01670 setBaseURL(url);
01671 fWatchVariableForSyncInterface = 0;
01672 fTimeoutTask = NULL;
01673 if (timeout > 0) {
01674
01675
01676
01677 fTimeoutTask = envir().taskScheduler().scheduleDelayedTask(timeout*1000000, timeoutHandlerForSyncInterface, this);
01678 }
01679 (void)sendDescribeCommand(responseHandlerForSyncInterface, authenticator);
01680
01681
01682 envir().taskScheduler().doEventLoop(&fWatchVariableForSyncInterface);
01683 if (fResultCode == 0) return fResultString;
01684 delete[] fResultString;
01685 return NULL;
01686 }
01687
01688 char* RTSPClient::describeWithPassword(char const* url,
01689 char const* username, char const* password,
01690 Boolean allowKasennaProtocol, int timeout) {
01691 Authenticator authenticator(username, password);
01692 return describeURL(url, &authenticator, allowKasennaProtocol, timeout);
01693 }
01694
01695 char* RTSPClient::sendOptionsCmd(char const* url,
01696 char* username, char* password,
01697 Authenticator* authenticator,
01698 int timeout) {
01699 char* result = NULL;
01700 Boolean haveAllocatedAuthenticator = False;
01701 if (authenticator == NULL) {
01702
01703
01704 if (username == NULL && password == NULL
01705 && parseRTSPURLUsernamePassword(url, username, password)) {
01706 Authenticator newAuthenticator(username,password);
01707 result = sendOptionsCmd(url, username, password, &newAuthenticator, timeout);
01708 delete[] username; delete[] password;
01709 return result;
01710 } else if (username != NULL && password != NULL) {
01711
01712 authenticator = new Authenticator(username,password);
01713 haveAllocatedAuthenticator = True;
01714
01715 result = sendOptionsCmd(url, username, password, authenticator, timeout);
01716 if (result != NULL) {
01717 delete authenticator;
01718 return result;
01719 }
01720
01721
01722 if (authenticator->realm() == NULL) {
01723
01724 delete authenticator;
01725 return NULL;
01726 }
01727 }
01728 }
01729
01730 setBaseURL(url);
01731 fWatchVariableForSyncInterface = 0;
01732 fTimeoutTask = NULL;
01733 if (timeout > 0) {
01734
01735
01736
01737 fTimeoutTask = envir().taskScheduler().scheduleDelayedTask(timeout*1000000, timeoutHandlerForSyncInterface, this);
01738 }
01739 (void)sendOptionsCommand(responseHandlerForSyncInterface, authenticator);
01740 if (haveAllocatedAuthenticator) delete authenticator;
01741
01742
01743 envir().taskScheduler().doEventLoop(&fWatchVariableForSyncInterface);
01744 if (fResultCode == 0) return fResultString;
01745 delete[] fResultString;
01746 return NULL;
01747 }
01748
01749 Boolean RTSPClient::announceSDPDescription(char const* url,
01750 char const* sdpDescription,
01751 Authenticator* authenticator,
01752 int timeout) {
01753 setBaseURL(url);
01754 fWatchVariableForSyncInterface = 0;
01755 fTimeoutTask = NULL;
01756 if (timeout > 0) {
01757
01758
01759
01760 fTimeoutTask = envir().taskScheduler().scheduleDelayedTask(timeout*1000000, timeoutHandlerForSyncInterface, this);
01761 }
01762 (void)sendAnnounceCommand(sdpDescription, responseHandlerForSyncInterface, authenticator);
01763
01764
01765 envir().taskScheduler().doEventLoop(&fWatchVariableForSyncInterface);
01766 delete[] fResultString;
01767 return fResultCode == 0;
01768 }
01769
01770 Boolean RTSPClient
01771 ::announceWithPassword(char const* url, char const* sdpDescription,
01772 char const* username, char const* password, int timeout) {
01773 Authenticator authenticator(username,password);
01774 return announceSDPDescription(url, sdpDescription, &authenticator, timeout);
01775 }
01776
01777 Boolean RTSPClient::setupMediaSubsession(MediaSubsession& subsession,
01778 Boolean streamOutgoing,
01779 Boolean streamUsingTCP,
01780 Boolean forceMulticastOnUnspecified) {
01781 fWatchVariableForSyncInterface = 0;
01782 fTimeoutTask = NULL;
01783 (void)sendSetupCommand(subsession, responseHandlerForSyncInterface, streamOutgoing, streamUsingTCP, forceMulticastOnUnspecified);
01784
01785
01786 envir().taskScheduler().doEventLoop(&fWatchVariableForSyncInterface);
01787 delete[] fResultString;
01788 return fResultCode == 0;
01789 }
01790
01791 Boolean RTSPClient::playMediaSession(MediaSession& session,
01792 double start, double end, float scale) {
01793 fWatchVariableForSyncInterface = 0;
01794 fTimeoutTask = NULL;
01795 (void)sendPlayCommand(session, responseHandlerForSyncInterface, start, end, scale);
01796
01797
01798 envir().taskScheduler().doEventLoop(&fWatchVariableForSyncInterface);
01799 delete[] fResultString;
01800 return fResultCode == 0;
01801 }
01802
01803 Boolean RTSPClient::playMediaSubsession(MediaSubsession& subsession,
01804 double start, double end, float scale,
01805 Boolean ) {
01806
01807
01808 fWatchVariableForSyncInterface = 0;
01809 fTimeoutTask = NULL;
01810 (void)sendPlayCommand(subsession, responseHandlerForSyncInterface, start, end, scale);
01811
01812
01813 envir().taskScheduler().doEventLoop(&fWatchVariableForSyncInterface);
01814 delete[] fResultString;
01815 return fResultCode == 0;
01816 }
01817
01818 Boolean RTSPClient::pauseMediaSession(MediaSession& session) {
01819 fWatchVariableForSyncInterface = 0;
01820 fTimeoutTask = NULL;
01821 (void)sendPauseCommand(session, responseHandlerForSyncInterface);
01822
01823
01824 envir().taskScheduler().doEventLoop(&fWatchVariableForSyncInterface);
01825 delete[] fResultString;
01826 return fResultCode == 0;
01827 }
01828
01829 Boolean RTSPClient::pauseMediaSubsession(MediaSubsession& subsession) {
01830 fWatchVariableForSyncInterface = 0;
01831 fTimeoutTask = NULL;
01832 (void)sendPauseCommand(subsession, responseHandlerForSyncInterface);
01833
01834
01835 envir().taskScheduler().doEventLoop(&fWatchVariableForSyncInterface);
01836 delete[] fResultString;
01837 return fResultCode == 0;
01838 }
01839
01840 Boolean RTSPClient::recordMediaSubsession(MediaSubsession& subsession) {
01841 fWatchVariableForSyncInterface = 0;
01842 fTimeoutTask = NULL;
01843 (void)sendRecordCommand(subsession, responseHandlerForSyncInterface);
01844
01845
01846 envir().taskScheduler().doEventLoop(&fWatchVariableForSyncInterface);
01847 delete[] fResultString;
01848 return fResultCode == 0;
01849 }
01850
01851 Boolean RTSPClient::setMediaSessionParameter(MediaSession& session,
01852 char const* parameterName,
01853 char const* parameterValue) {
01854 fWatchVariableForSyncInterface = 0;
01855 fTimeoutTask = NULL;
01856 (void)sendSetParameterCommand(session, responseHandlerForSyncInterface, parameterName, parameterValue);
01857
01858
01859 envir().taskScheduler().doEventLoop(&fWatchVariableForSyncInterface);
01860 delete[] fResultString;
01861 return fResultCode == 0;
01862 }
01863
01864 Boolean RTSPClient::getMediaSessionParameter(MediaSession& session,
01865 char const* parameterName,
01866 char*& parameterValue) {
01867 fWatchVariableForSyncInterface = 0;
01868 fTimeoutTask = NULL;
01869 (void)sendGetParameterCommand(session, responseHandlerForSyncInterface, parameterName);
01870
01871
01872 envir().taskScheduler().doEventLoop(&fWatchVariableForSyncInterface);
01873 parameterValue = fResultString;
01874 return fResultCode == 0;
01875 }
01876
01877 Boolean RTSPClient::teardownMediaSession(MediaSession& session) {
01878 fWatchVariableForSyncInterface = 0;
01879 fTimeoutTask = NULL;
01880 (void)sendTeardownCommand(session, NULL);
01881 return True;
01882 }
01883
01884 Boolean RTSPClient::teardownMediaSubsession(MediaSubsession& subsession) {
01885 fWatchVariableForSyncInterface = 0;
01886 fTimeoutTask = NULL;
01887 (void)sendTeardownCommand(subsession, NULL);
01888 return True;
01889 }
01890
01891 Boolean RTSPClient::parseRTSPURLUsernamePassword(char const* url,
01892 char*& username,
01893 char*& password) {
01894 username = password = NULL;
01895 do {
01896
01897 char const* prefix = "rtsp://";
01898 unsigned const prefixLength = 7;
01899 if (_strncasecmp(url, prefix, prefixLength) != 0) break;
01900
01901
01902 unsigned usernameIndex = prefixLength;
01903 unsigned colonIndex = 0, atIndex = 0;
01904 for (unsigned i = usernameIndex; url[i] != '\0' && url[i] != '/'; ++i) {
01905 if (url[i] == ':' && colonIndex == 0) {
01906 colonIndex = i;
01907 } else if (url[i] == '@') {
01908 atIndex = i;
01909 break;
01910 }
01911 }
01912 if (atIndex == 0) break;
01913
01914 char* urlCopy = strDup(url);
01915 urlCopy[atIndex] = '\0';
01916 if (colonIndex > 0) {
01917 urlCopy[colonIndex] = '\0';
01918 password = strDup(&urlCopy[colonIndex+1]);
01919 } else {
01920 password = strDup("");
01921 }
01922 username = strDup(&urlCopy[usernameIndex]);
01923 delete[] urlCopy;
01924
01925 return True;
01926 } while (0);
01927
01928 return False;
01929 }
01930
01931 void RTSPClient::responseHandlerForSyncInterface(RTSPClient* rtspClient, int responseCode, char* responseString) {
01932 if (rtspClient != NULL) rtspClient->responseHandlerForSyncInterface1(responseCode, responseString);
01933 }
01934
01935 void RTSPClient::responseHandlerForSyncInterface1(int responseCode, char* responseString) {
01936
01937 if (fTimeoutTask != NULL) envir().taskScheduler().unscheduleDelayedTask(fTimeoutTask);
01938
01939
01940 fResultCode = responseCode;
01941 fResultString = responseString;
01942
01943
01944 fWatchVariableForSyncInterface = ~0;
01945 }
01946
01947 void RTSPClient::timeoutHandlerForSyncInterface(void* rtspClient) {
01948 if (rtspClient != NULL) ((RTSPClient*)rtspClient)->timeoutHandlerForSyncInterface1();
01949 }
01950
01951 void RTSPClient::timeoutHandlerForSyncInterface1() {
01952
01953
01954
01955 changeResponseHandler(fCSeq, NULL);
01956 fTimeoutTask = NULL;
01957
01958
01959 fResultCode = ~0;
01960 fResultString = NULL;
01961
01962
01963 fWatchVariableForSyncInterface = ~0;
01964 }
01965
01966 #endif