📄 rtspserver.cpp.save
字号:
cseq, dateHeader(), allowedCommandNames);}void RTSPServer::RTSPClientSession::handleCmd_DESCRIBE(char const* cseq, char const* urlSuffix, char const* fullRequestStr) { char* sdpDescription = NULL; char* rtspURL = NULL; do { if (!authenticationOK("DESCRIBE", cseq, fullRequestStr)) break; // We should really check that the request contains an "Accept:" ##### // for "application/sdp", because that's what we're sending back ##### // Begin by looking up the "ServerMediaSession" object for the // specified "urlSuffix": ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlSuffix); if (session == NULL) { handleCmd_notFound(cseq); break; } // Then, assemble a SDP description for this session: sdpDescription = session->generateSDPDescription(); if (sdpDescription == NULL) { // This usually means that a file name that was specified for a // "ServerMediaSubsession" does not exist. snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 404 File Not Found, Or In Incorrect Format\r\n" "CSeq: %s\r\n" "%s\r\n", cseq, dateHeader()); break; } unsigned sdpDescriptionSize = strlen(sdpDescription); // Also, generate our RTSP URL, for the "Content-Base:" header // (which is necessary to ensure that the correct URL gets used in // subsequent "SETUP" requests). rtspURL = fOurServer.rtspURL(session); snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\nCSeq: %s\r\n" "%s" "Content-Base: %s/\r\n" "Content-Type: application/sdp\r\n" "Content-Length: %d\r\n\r\n" "%s", cseq, dateHeader(), rtspURL, sdpDescriptionSize, sdpDescription); } while (0); delete[] sdpDescription; delete[] rtspURL;}typedef enum StreamingMode { RTP_UDP, RTP_TCP, RAW_UDP};static void parseTransportHeader(char const* buf, StreamingMode& streamingMode, char*& streamingModeString, char*& destinationAddressStr, u_int8_t& destinationTTL, portNumBits& clientRTPPortNum, // if UDP portNumBits& clientRTCPPortNum, // if UDP unsigned char& rtpChannelId, // if TCP unsigned char& rtcpChannelId // if TCP ) { // Initialize the result parameters to default values: streamingMode = RTP_UDP; streamingModeString = NULL; destinationAddressStr = NULL; destinationTTL = 255; clientRTPPortNum = 0; clientRTCPPortNum = 1; rtpChannelId = rtcpChannelId = 0xFF; portNumBits p1, p2; unsigned ttl, rtpCid, rtcpCid; // First, find "Transport:" while (1) { if (*buf == '\0') return; // not found if (_strncasecmp(buf, "Transport: ", 11) == 0) break; ++buf; } // Then, run through each of the fields, looking for ones we handle: char const* fields = buf + 11; char* field = strDupSize(fields); while (sscanf(fields, "%[^;]", field) == 1) { if (strcmp(field, "RTP/AVP/TCP") == 0) { streamingMode = RTP_TCP; } else if (strcmp(field, "RAW/RAW/UDP") == 0 || strcmp(field, "MP2T/H2221/UDP") == 0) { streamingMode = RAW_UDP; streamingModeString = strDup(field); } else if (_strncasecmp(field, "destination=", 12) == 0) { delete[] destinationAddressStr; destinationAddressStr = strDup(field+12); } else if (sscanf(field, "ttl%u", &ttl) == 1) { destinationTTL = (u_int8_t)ttl; } else if (sscanf(field, "client_port=%hu-%hu", &p1, &p2) == 2) { clientRTPPortNum = p1; clientRTCPPortNum = p2; } else if (sscanf(field, "client_port=%hu", &p1) == 1) { clientRTPPortNum = p1; clientRTCPPortNum = streamingMode == RAW_UDP ? 0 : p1 + 1; } else if (sscanf(field, "interleaved=%u-%u", &rtpCid, &rtcpCid) == 2) { rtpChannelId = (unsigned char)rtpCid; rtcpChannelId = (unsigned char)rtcpCid; } fields += strlen(field); while (*fields == ';') ++fields; // skip over separating ';' chars if (*fields == '\0' || *fields == '\r' || *fields == '\n') break; } delete[] field;}static Boolean parseRangeHeader(char const* buf, float& rangeStart, float& rangeEnd) { // Initialize the result parameters to default values: rangeStart = rangeEnd = 0.0; // First, find "Range:" while (1) { if (*buf == '\0') return False; // not found if (_strncasecmp(buf, "Range: ", 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 start, end; if (sscanf(fields, "npt = %f - %f", &start, &end) == 2) { rangeStart = start; rangeEnd = end; } else if (sscanf(fields, "npt = %f -", &start) == 1) { rangeStart = start; } else { return False; // The header is malformed } return True;}void RTSPServer::RTSPClientSession::handleCmd_SETUP(char const* cseq, char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr) { // "urlPreSuffix" should be the session (stream) name, and // "urlSuffix" should be the subsession (track) name. char const* streamName = urlPreSuffix; char const* trackId = urlSuffix; // Check whether we have existing session state, and, if so, whether it's // for the session that's named in "streamName". (Note that we don't // support more than one concurrent session on the same client connection.) ##### if (fOurServerMediaSession != NULL && strcmp(streamName, fOurServerMediaSession->streamName()) != 0) { fOurServerMediaSession = NULL; } if (fOurServerMediaSession == NULL) { // Set up this session's state. // Look up the "ServerMediaSession" object for the specified stream: if (streamName[0] != '\0' || fOurServer.lookupServerMediaSession("") != NULL) { // normal case } else { // weird case: there was no track id in the URL streamName = urlSuffix; trackId = NULL; } fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName); if (fOurServerMediaSession == NULL) { handleCmd_notFound(cseq); return; } fOurServerMediaSession->incrementReferenceCount(); // Set up our array of states for this session's subsessions (tracks): reclaimStreamStates(); ServerMediaSubsessionIterator iter(*fOurServerMediaSession); for (fNumStreamStates = 0; iter.next() != NULL; ++fNumStreamStates) {} fStreamStates = new struct streamState[fNumStreamStates]; iter.reset(); ServerMediaSubsession* subsession; for (unsigned i = 0; i < fNumStreamStates; ++i) { subsession = iter.next(); fStreamStates[i].subsession = subsession; fStreamStates[i].streamToken = NULL; // for now; reset by SETUP later } } // Look up information for the specified subsession (track): ServerMediaSubsession* subsession = NULL; unsigned streamNum; if (trackId != NULL) { // normal case for (streamNum = 0; streamNum < fNumStreamStates; ++streamNum) { subsession = fStreamStates[streamNum].subsession; if (subsession != NULL && strcmp(trackId, subsession->trackId()) == 0) break; } if (streamNum >= fNumStreamStates) { // The specified track id doesn't exist, so this request fails: handleCmd_notFound(cseq); return; } } else { // Weird case: there was no track id in the URL. // This works only if we have only one subsession: if (fNumStreamStates != 1) { handleCmd_bad(cseq); return; } streamNum = 0; subsession = fStreamStates[streamNum].subsession; } // ASSERT: subsession != NULL // Look for a "Transport:" header in the request string, // to extract client parameters: StreamingMode streamingMode; char* streamingModeString; // set when RAW_UDP streaming is specified char* clientsDestinationAddressStr; u_int8_t clientsDestinationTTL; portNumBits clientRTPPortNum, clientRTCPPortNum; unsigned char rtpChannelId, rtcpChannelId; parseTransportHeader(fullRequestStr, streamingMode, streamingModeString, clientsDestinationAddressStr, clientsDestinationTTL, clientRTPPortNum, clientRTCPPortNum, rtpChannelId, rtcpChannelId); if (streamingMode == RTP_TCP && rtpChannelId == 0xFF) { // TCP streaming was requested, but with no "interleaving=" fields. // (QuickTime Player sometimes does this.) Set the RTP and RTCP channel ids to // proper values: rtpChannelId = fTCPStreamIdCount; rtcpChannelId = fTCPStreamIdCount+1; } fTCPStreamIdCount += 2; Port clientRTPPort(clientRTPPortNum); Port clientRTCPPort(clientRTCPPortNum); // Next, check whether a "Range:" header is present in the request. // This isn't legal, but some clients do this to combine "SETUP" and "PLAY": float rangeStart, rangeEnd; fStreamAfterSETUP = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd); // Then, get server parameters from the 'subsession': int tcpSocketNum = streamingMode == RTP_TCP ? fClientSocket : -1; netAddressBits destinationAddress = 0; u_int8_t destinationTTL = 255;#ifdef RTSP_ALLOW_CLIENT_DESTINATION_SETTING if (clientsDestinationAddressStr != NULL) { // Use the client-provided "destination" address. // Note: This potentially allows the server to be used in denial-of-service // attacks, so don't enable this code unless you're sure that clients are // trusted. destinationAddress = our_inet_addr(clientsDestinationAddressStr); } // Also use the client-provided TTL. destinationTTL = clientsDestinationTTL;#endif delete[] clientsDestinationAddressStr; Boolean isMulticast; Port serverRTPPort(0); Port serverRTCPPort(0); subsession->getStreamParameters(fOurSessionId, fClientAddr.sin_addr.s_addr, clientRTPPort, clientRTCPPort, tcpSocketNum, rtpChannelId, rtcpChannelId, destinationAddress, destinationTTL, isMulticast, serverRTPPort, serverRTCPPort, fStreamStates[streamNum].streamToken); struct in_addr destinationAddr; destinationAddr.s_addr = destinationAddress; if (isMulticast) { if (streamingMode == RTP_TCP) { // multicast streams can't be sent via TCP handleCmd_unsupportedTransport(cseq); return; } snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\n" "CSeq: %s\r\n" "%s" "Transport: RTP/AVP;multicast;destination=%s;port=%d;ttl=%d\r\n" "Session: %d\r\n\r\n", cseq, dateHeader(), our_inet_ntoa(destinationAddr), ntohs(serverRTPPort.num()), destinationTTL, fOurSessionId); } else { switch (streamingMode) { case RTP_UDP: { snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\n" "CSeq: %s\r\n" "%s" "Transport: RTP/AVP;unicast;destination=%s;client_port=%d-%d;server_port=%d-%d\r\n" "Session: %d\r\n\r\n", cseq, dateHeader(), our_inet_ntoa(destinationAddr), ntohs(clientRTPPort.num()), ntohs(clientRTCPPort.num()), ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()), fOurSessionId); break; } case RTP_TCP: { snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\n" "CSeq: %s\r\n" "%s" "Transport: RTP/AVP/TCP;unicast;destination=%s;interleaved=%d-%d\r\n" "Session: %d\r\n\r\n", cseq, dateHeader(), our_inet_ntoa(destinationAddr), rtpChannelId, rtcpChannelId, fOurSessionId); break; } case RAW_UDP: { snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\n" "CSeq: %s\r\n" "%s" "Transport: %s;unicast;destination=%s;client_port=%d;server_port=%d\r\n" "Session: %d\r\n\r\n", cseq, dateHeader(), streamingModeString, our_inet_ntoa(destinationAddr), ntohs(clientRTPPort.num()), ntohs(serverRTPPort.num()), fOurSessionId); delete[] streamingModeString; break; } } }}void RTSPServer::RTSPClientSession::handleCmd_withinSession(char const* cmdName, char const* urlPreSuffix, char const* urlSuffix, char const* cseq, char const* fullRequestStr) { // This will either be: // - a non-aggregated operation, if "urlPreSuffix" is the session (stream) // name and "urlSuffix" is the subsession (track) name, or // - a aggregated operation, if "urlSuffix" is the session (stream) name, // or "urlPreSuffix" is the session (stream) name, and "urlSuffix" // is empty. // First, figure out which of these it is: if (fOurServerMediaSession == NULL) { // There wasn't a previous SETUP! handleCmd_notSupported(cseq); return; } ServerMediaSubsession* subsession; if (urlSuffix[0] != '\0' && strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0) { // Non-aggregated operation. // 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); unsigned rtspURLSize = strlen(rtspURL); //// Parse the client's "Scale:" header, if any: float scale; Boolean sawScaleHeader = parseScaleHeader(fullRequestStr, scale);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -