📄 rtspserver.cpp.save
字号:
// Try to set the stream's scale factor to this value: if (subsession == NULL /*aggregate op*/) { fOurServerMediaSession->testScaleFactor(scale); } else { subsession->testScaleFactor(scale); } char buf[100]; char* scaleHeader; if (!sawScaleHeader) { buf[0] = '\0'; // Because we didn't see a Scale: header, don't send one back } else { sprintf(buf, "Scale: %f\r\n", scale); } scaleHeader = strDup(buf); //// Parse the client's "Range:" header, if any: float rangeStart, rangeEnd; Boolean sawRangeHeader = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd); // Use this information, plus the stream's duration (if known), to create // our own "Range:" header, for the response: float duration = subsession == NULL /*aggregate op*/ ? fOurServerMediaSession->duration() : subsession->duration(); if (duration < 0.0) { // We're an aggregate PLAY, but the subsessions have different durations. // Use the largest of these durations in our header duration = -duration; } if (rangeEnd < 0.0 || rangeEnd > duration) rangeEnd = duration; if (rangeStart < 0.0) { rangeStart = 0.0; } else if (rangeEnd > 0.0 && scale > 0.0 && rangeStart > rangeEnd) { rangeStart = rangeEnd; } char* rangeHeader; if (!sawRangeHeader) { buf[0] = '\0'; // Because we didn't see a Range: header, don't send one back } else if (rangeEnd == 0.0 && scale >= 0.0) { sprintf(buf, "Range: npt=%.3f-\r\n", rangeStart); } else { sprintf(buf, "Range: npt=%.3f-%.3f\r\n", rangeStart, rangeEnd); } rangeHeader = strDup(buf); // Create a "RTP-Info:" line. It will get filled in from each subsession's state: char const* rtpInfoFmt = "%s" // "RTP-Info:", plus any preceding rtpInfo items "%s" // comma separator, if needed "url=%s/%s" ";seq=%d"#ifdef RTPINFO_INCLUDE_RTPTIME ";rtptime=%u"#endif ; unsigned rtpInfoFmtSize = strlen(rtpInfoFmt); char* rtpInfo = strDup("RTP-Info: "); unsigned i, numRTPInfoItems = 0; // Do any required seeking/scaling on each subsession, before starting streaming: for (i = 0; i < fNumStreamStates; ++i) { if (subsession == NULL /* means: aggregated operation */ || subsession == fStreamStates[i].subsession) { if (sawScaleHeader) { fStreamStates[i].subsession->setStreamScale(fOurSessionId, fStreamStates[i].streamToken, scale); } if (sawRangeHeader) { fStreamStates[i].subsession->seekStream(fOurSessionId, fStreamStates[i].streamToken, rangeStart); } } } // Now, start streaming: for (i = 0; i < fNumStreamStates; ++i) { if (subsession == NULL /* means: aggregated operation */ || subsession == fStreamStates[i].subsession) { unsigned short rtpSeqNum = 0; unsigned rtpTimestamp = 0; fStreamStates[i].subsession->startStream(fOurSessionId, fStreamStates[i].streamToken, rtpSeqNum, rtpTimestamp); const char *urlSuffix = fStreamStates[i].subsession->trackId(); char* prevRTPInfo = rtpInfo; unsigned rtpInfoSize = rtpInfoFmtSize + strlen(prevRTPInfo) + 1 + rtspURLSize + strlen(urlSuffix) + 5 /*max unsigned short len*/#ifdef RTPINFO_INCLUDE_RTPTIME + 10 /*max unsigned (32-bit) len*/#endif + 2 /*allows for trailing \r\n at final end of string*/; rtpInfo = new char[rtpInfoSize]; sprintf(rtpInfo, rtpInfoFmt, prevRTPInfo, numRTPInfoItems++ == 0 ? "" : ",", rtspURL, urlSuffix, rtpSeqNum#ifdef RTPINFO_INCLUDE_RTPTIME ,rtpTimestamp#endif ); delete[] prevRTPInfo; } } if (numRTPInfoItems == 0) { rtpInfo[0] = '\0'; } else { unsigned rtpInfoLen = strlen(rtpInfo); rtpInfo[rtpInfoLen] = '\r'; rtpInfo[rtpInfoLen+1] = '\n'; rtpInfo[rtpInfoLen+2] = '\0'; } // Fill in the response: snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\n" "CSeq: %s\r\n" "%s" "%s" "%s" "Session: %d\r\n" "%s\r\n", cseq, dateHeader(), scaleHeader, rangeHeader, fOurSessionId, rtpInfo); delete[] rtpInfo; delete[] rangeHeader; delete[] scaleHeader; delete[] rtspURL;}void RTSPServer::RTSPClientSession ::handleCmd_PAUSE(ServerMediaSubsession* subsession, char const* cseq) { for (unsigned i = 0; i < fNumStreamStates; ++i) { if (subsession == NULL /* means: aggregated operation */ || subsession == fStreamStates[i].subsession) { fStreamStates[i].subsession->pauseStream(fOurSessionId, fStreamStates[i].streamToken); } } snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sSession: %d\r\n\r\n", cseq, dateHeader(), fOurSessionId);}void RTSPServer::RTSPClientSession::handleCmd_GET_PARAMETER(ServerMediaSubsession* subsession, char const* cseq, char const* /*fullRequestStr*/) { // We implement "GET_PARAMETER" just as a 'keep alive', // and send back an empty response: snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sSession: %d\r\n\r\n", cseq, dateHeader(), fOurSessionId);}static Boolean parseAuthorizationHeader(char const* buf, char const*& username, char const*& realm, char const*& nonce, char const*& uri, char const*& response) { // Initialize the result parameters to default values: username = realm = nonce = uri = response = NULL; // First, find "Authorization:" while (1) { if (*buf == '\0') return False; // not found if (_strncasecmp(buf, "Authorization: Digest ", 22) == 0) break; ++buf; } // Then, run through each of the fields, looking for ones we handle: char const* fields = buf + 22; while (*fields == ' ') ++fields; char* parameter = strDupSize(fields); char* value = strDupSize(fields); while (1) { value[0] = '\0'; if (sscanf(fields, "%[^=]=\"%[^\"]\"", parameter, value) != 2 && sscanf(fields, "%[^=]=\"\"", parameter) != 1) { break; } if (strcmp(parameter, "username") == 0) { username = strDup(value); } else if (strcmp(parameter, "realm") == 0) { realm = strDup(value); } else if (strcmp(parameter, "nonce") == 0) { nonce = strDup(value); } else if (strcmp(parameter, "uri") == 0) { uri = strDup(value); } else if (strcmp(parameter, "response") == 0) { response = strDup(value); } fields += strlen(parameter) + 2 /*="*/ + strlen(value) + 1 /*"*/; while (*fields == ',' || *fields == ' ') ++fields; // skip over any separating ',' and ' ' chars if (*fields == '\0' || *fields == '\r' || *fields == '\n') break; } delete[] parameter; delete[] value; return True;}Boolean RTSPServer::RTSPClientSession::authenticationOK(char const* cmdName, char const* cseq, char const* fullRequestStr) { // If we weren't set up with an authentication database, we're OK: if (fOurServer.fAuthDB == NULL) return True; char const* username = NULL; char const* realm = NULL; char const* nonce = NULL; char const* uri = NULL; char const* response = NULL; Boolean success = False; do { // To authenticate, we first need to have a nonce set up // from a previous attempt: if (fCurrentAuthenticator.nonce() == NULL) break; // Next, the request needs to contain an "Authorization:" header, // containing a username, (our) realm, (our) nonce, uri, // and response string: if (!parseAuthorizationHeader(fullRequestStr, username, realm, nonce, uri, response) || username == NULL || realm == NULL || strcmp(realm, fCurrentAuthenticator.realm()) != 0 || nonce == NULL || strcmp(nonce, fCurrentAuthenticator.nonce()) != 0 || uri == NULL || response == NULL) { break; } // Next, the username has to be known to us: char const* password = fOurServer.fAuthDB->lookupPassword(username);#ifdef DEBUG fprintf(stderr, "lookupPassword(%s) returned password %s\n", username, password);#endif if (password == NULL) break; fCurrentAuthenticator. setUsernameAndPassword(username, password, fOurServer.fAuthDB->passwordsAreMD5()); // Finally, compute a digest response from the information that we have, // and compare it to the one that we were given: char const* ourResponse = fCurrentAuthenticator.computeDigestResponse(cmdName, uri); success = (strcmp(ourResponse, response) == 0); fCurrentAuthenticator.reclaimDigestResponse(ourResponse); } while (0); delete[] (char*)username; delete[] (char*)realm; delete[] (char*)nonce; delete[] (char*)uri; delete[] (char*)response; if (success) return True; // If we get here, there was some kind of authentication failure. // Send back a "401 Unauthorized" response, with a new random nonce: fCurrentAuthenticator.setRealmAndRandomNonce(fOurServer.fAuthDB->realm()); snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 401 Unauthorized\r\n" "CSeq: %s\r\n" "%s" "WWW-Authenticate: Digest realm=\"%s\", nonce=\"%s\"\r\n\r\n", cseq, dateHeader(), fCurrentAuthenticator.realm(), fCurrentAuthenticator.nonce()); return False;}BooleanRTSPServer::RTSPClientSession ::parseRequestString(char const* reqStr, unsigned reqStrSize, char* resultCmdName, unsigned resultCmdNameMaxSize, char* resultURLPreSuffix, unsigned resultURLPreSuffixMaxSize, char* resultURLSuffix, unsigned resultURLSuffixMaxSize, char* resultCSeq, unsigned resultCSeqMaxSize) { // This parser is currently rather dumb; it should be made smarter ##### // Read everything up to the first space as the command name: Boolean parseSucceeded = False; unsigned i; for (i = 0; i < resultCmdNameMaxSize-1 && i < reqStrSize; ++i) { char c = reqStr[i]; if (c == ' ' || c == '\t') { parseSucceeded = True; break; } resultCmdName[i] = c; } resultCmdName[i] = '\0'; if (!parseSucceeded) return False; // Skip over the prefix of any "rtsp://" or "rtsp:/" URL that follows: unsigned j = i+1; while (j < reqStrSize && (reqStr[j] == ' ' || reqStr[j] == '\t')) ++j; // skip over any additional white space for (j = i+1; j < reqStrSize-8; ++j) { if ((reqStr[j] == 'r' || reqStr[j] == 'R') && (reqStr[j+1] == 't' || reqStr[j+1] == 'T') && (reqStr[j+2] == 's' || reqStr[j+2] == 'S') && (reqStr[j+3] == 'p' || reqStr[j+3] == 'P') && reqStr[j+4] == ':' && reqStr[j+5] == '/') { j += 6; if (reqStr[j] == '/') { // This is a "rtsp://" URL; skip over the host:port part that follows: ++j; while (j < reqStrSize && reqStr[j] != '/' && reqStr[j] != ' ') ++j; } else { // This is a "rtsp:/" URL; back up to the "/": --j; } i = j; break; } } // Look for the URL suffix (before the following "RTSP/"): parseSucceeded = False; for (unsigned k = i+1; k < reqStrSize-5; ++k) { if (reqStr[k] == 'R' && reqStr[k+1] == 'T' && reqStr[k+2] == 'S' && reqStr[k+3] == 'P' && reqStr[k+4] == '/') { while (--k >= i && reqStr[k] == ' ') {} // go back over all spaces before "RTSP/" unsigned k1 = k; while (k1 > i && reqStr[k1] != '/' && reqStr[k1] != ' ') --k1; // the URL suffix comes from [k1+1,k] // Copy "resultURLSuffix": if (k - k1 + 1 > resultURLSuffixMaxSize) return False; // there's no room unsigned n = 0, k2 = k1+1; while (k2 <= k) resultURLSuffix[n++] = reqStr[k2++]; resultURLSuffix[n] = '\0'; // Also look for the URL 'pre-suffix' before this: unsigned k3 = --k1; while (k3 > i && reqStr[k3] != '/' && reqStr[k3] != ' ') --k3; // the URL pre-suffix comes from [k3+1,k1] // Copy "resultURLPreSuffix": if (k1 - k3 + 1 > resultURLPreSuffixMaxSize) return False; // there's no room n = 0; k2 = k3+1; while (k2 <= k1) resultURLPreSuffix[n++] = reqStr[k2++]; resultURLPreSuffix[n] = '\0'; i = k + 7; // to go past " RTSP/" parseSucceeded = True; break; } } if (!parseSucceeded) return False; // Look for "CSeq:", skip whitespace, // then read everything up to the next \r or \n as 'CSeq': parseSucceeded = False; for (j = i; j < reqStrSize-5; ++j) { if (reqStr[j] == 'C' && reqStr[j+1] == 'S' && reqStr[j+2] == 'e' && reqStr[j+3] == 'q' && reqStr[j+4] == ':') { j += 5; unsigned n; while (j < reqStrSize && (reqStr[j] == ' ' || reqStr[j] == '\t')) ++j; for (n = 0; n < resultCSeqMaxSize-1 && j < reqStrSize; ++n,++j) { char c = reqStr[j]; if (c == '\r' || c == '\n') { parseSucceeded = True; break; } resultCSeq[n] = c; } resultCSeq[n] = '\0'; break; } } if (!parseSucceeded) return False; return True;}void RTSPServer::RTSPClientSession::livenessTimeoutTask(RTSPClientSession* clientSession) { // If this gets called, the client session is assumed to have timed out, // so delete it: delete clientSession;}void RTSPServer::RTSPClientSession::noteClientLiveness() { if (fOurServer.fReclamationTestSeconds > 0) { envir().taskScheduler() .rescheduleDelayedTask(fLivenessCheckTask, fOurServer.fReclamationTestSeconds*1000000, (TaskFunc*)livenessTimeoutTask, this); }}////////// UserAuthenticationDatabase implementation //////////UserAuthenticationDatabase::UserAuthenticationDatabase(char const* realm, Boolean passwordsAreMD5) : fTable(HashTable::create(STRING_HASH_KEYS)), fRealm(strDup(realm == NULL ? "LIVE.COM Streaming Media" : realm)), fPasswordsAreMD5(passwordsAreMD5) {}UserAuthenticationDatabase::~UserAuthenticationDatabase() { delete[] fRealm; delete fTable;}void UserAuthenticationDatabase::addUserRecord(char const* username, char const* password) { fTable->Add(username, (void*)(strDup(password)));}void UserAuthenticationDatabase::removeUserRecord(char const* username) { char* password = (char*)(fTable->Lookup(username)); fTable->Remove(username); delete[] password;}char const* UserAuthenticationDatabase::lookupPassword(char const* username) { return (char const*)(fTable->Lookup(username));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -