📄 rtspserver.cpp
字号:
// Look up the media subsession whose track id is "urlSuffix": ServerMediaSubsessionIterator iter(*fOurServerMediaSession); while ((subsession = iter.next()) != NULL) { if (strcmp(subsession->trackId(), urlSuffix) == 0) break; // success } if (subsession == NULL) { // no such track! handleCmd_notFound(cseq); return; } } else if (strcmp(fOurServerMediaSession->streamName(), urlSuffix) == 0 || strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0) { // Aggregated operation subsession = NULL; } else { // the request doesn't match a known stream and/or track at all! handleCmd_notFound(cseq); return; } if (strcmp(cmdName, "TEARDOWN") == 0) { handleCmd_TEARDOWN(subsession, cseq); } else if (strcmp(cmdName, "PLAY") == 0) { handleCmd_PLAY(subsession, cseq, fullRequestStr); } else if (strcmp(cmdName, "PAUSE") == 0) { handleCmd_PAUSE(subsession, cseq); } else if (strcmp(cmdName, "GET_PARAMETER") == 0) { handleCmd_GET_PARAMETER(subsession, cseq, fullRequestStr); }}void RTSPServer::RTSPClientSession::handleCmd_TEARDOWN(ServerMediaSubsession* /*subsession*/, char const* cseq) { snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%s\r\n", cseq, dateHeader()); fSessionIsActive = False; // triggers deletion of ourself after responding}static Boolean parseScaleHeader(char const* buf, float& scale) { // Initialize the result parameter to a default value: scale = 1.0; // First, find "Scale:" while (1) { if (*buf == '\0') return False; // not found if (_strncasecmp(buf, "Scale: ", 7) == 0) break; ++buf; } // Then, run through each of the fields, looking for ones we handle: char const* fields = buf + 7; while (*fields == ' ') ++fields; float sc; if (sscanf(fields, "%f", &sc) == 1) { scale = sc; } else { return False; // The header is malformed } return True;}void RTSPServer::RTSPClientSession ::handleCmd_PLAY(ServerMediaSubsession* subsession, char const* cseq, char const* fullRequestStr) { char* rtspURL = fOurServer.rtspURL(fOurServerMediaSession, fClientSocket); unsigned rtspURLSize = strlen(rtspURL); //// Parse the client's "Scale:" header, if any: float scale; Boolean sawScaleHeader = parseScaleHeader(fullRequestStr, scale); // 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 = 0.0, rangeEnd = 0.0; 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, (TaskFunc*)noteClientLiveness, this, 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* urlSuffix, char const* fullRequestStr) { if (!fOurServer.specialClientAccessCheck(fClientSocket, fClientAddr, urlSuffix)) { snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 401 Unauthorized\r\n" "CSeq: %s\r\n" "%s" "\r\n", cseq, dateHeader()); return False; } // 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;}void RTSPServer::RTSPClientSession::noteLiveness() { if (fOurServer.fReclamationTestSeconds > 0) { envir().taskScheduler() .rescheduleDelayedTask(fLivenessCheckTask, fOurServer.fReclamationTestSeconds*1000000, (TaskFunc*)livenessTimeoutTask, this); }}void RTSPServer::RTSPClientSession::noteClientLiveness(RTSPClientSession* clientSession) { clientSession->noteLiveness();}void RTSPServer::RTSPClientSession::livenessTimeoutTask(RTSPClientSession* clientSession) { // If this gets called, the client session is assumed to have timed out, // so delete it: // However, we don't timeout multicast sessions, because to do so would require // closing all client sessions that have requested the stream - not just this one. // Also, the multicast stream itself would usually not be halted, in any case. if (clientSession->isMulticast()) return;#ifdef DEBUG fprintf(stderr, "RTSP client session from %s has timed out (due to inactivity)\n", our_inet_ntoa(clientSession->fClientAddr.sin_addr));#endif delete clientSession;}////////// UserAuthenticationDatabase implementation //////////UserAuthenticationDatabase::UserAuthenticationDatabase(char const* realm, Boolean passwordsAreMD5) : fTable(HashTable::create(STRING_HASH_KEYS)), fRealm(strDup(realm == NULL ? "LIVE555 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 + -