📄 rtspclient.cpp
字号:
unsigned extraHeadersSize = strlen(extraHeadersFmt) + strlen(fSessionCookie); extraHeaders = new char[extraHeadersSize]; extraHeadersWereAllocated = True; sprintf(extraHeaders, extraHeadersFmt, fSessionCookie); } else { // "POST" protocolStr = "HTTP/1.0"; char const* const extraHeadersFmt = "x-sessioncookie: %s\r\n" "Content-Type: application/x-rtsp-tunnelled\r\n" "Pragma: no-cache\r\n" "Cache-Control: no-cache\r\n" "Content-Length: 32767\r\n" "Expires: Sun, 9 Jan 1972 00:00:00 GMT\r\n"; unsigned extraHeadersSize = strlen(extraHeadersFmt) + strlen(fSessionCookie); extraHeaders = new char[extraHeadersSize]; extraHeadersWereAllocated = True; sprintf(extraHeaders, extraHeadersFmt, fSessionCookie); } } else { // "PLAY", "PAUSE", "TEARDOWN", "RECORD", "SET_PARAMETER", "GET_PARAMETER" // First, make sure that we have a RTSP session in progress if (fLastSessionId == NULL) { envir().setResultMsg("No RTSP session is currently in progress\n"); break; } char const* sessionId; float originalScale; if (request->session() != NULL) { // Session-level operation cmdURL = (char*)sessionURL(*request->session()); sessionId = fLastSessionId; originalScale = request->session()->scale(); } else { // Media-level operation char const *prefix, *separator, *suffix; constructSubsessionURL(*request->subsession(), prefix, separator, suffix); cmdURL = new char[strlen(prefix) + strlen(separator) + strlen(suffix) + 1]; cmdURLWasAllocated = True; sprintf(cmdURL, "%s%s%s", prefix, separator, suffix); sessionId = request->subsession()->sessionId; originalScale = request->subsession()->scale(); } if (strcmp(request->commandName(), "PLAY") == 0) { // Create "Session:", "Scale:", and "Range:" headers; these make up the 'extra headers': char* sessionStr = createSessionString(sessionId); char* scaleStr = createScaleString(request->scale(), originalScale); char* rangeStr = createRangeString(request->start(), request->end()); extraHeaders = new char[strlen(sessionStr) + strlen(scaleStr) + strlen(rangeStr) + 1]; extraHeadersWereAllocated = True; sprintf(extraHeaders, "%s%s%s", sessionStr, scaleStr, rangeStr); delete[] sessionStr; delete[] scaleStr; delete[] rangeStr; } else { // Create a "Session:" header; this makes up our 'extra headers': extraHeaders = createSessionString(sessionId); extraHeadersWereAllocated = True; } } char* authenticatorStr = createAuthenticatorString(request->commandName(), fBaseURL); char const* const cmdFmt = "%s %s %s\r\n" "CSeq: %d\r\n" "%s" "%s" "%s" "%s" "\r\n" "%s"; unsigned cmdSize = strlen(cmdFmt) + strlen(request->commandName()) + strlen(cmdURL) + strlen(protocolStr) + 20 /* max int len */ + strlen(authenticatorStr) + fUserAgentHeaderStrLen + strlen(extraHeaders) + strlen(contentLengthHeader) + contentStrLen; cmd = new char[cmdSize]; sprintf(cmd, cmdFmt, request->commandName(), cmdURL, protocolStr, request->cseq(), authenticatorStr, fUserAgentHeaderStr, extraHeaders, contentLengthHeader, contentStr); delete[] authenticatorStr; if (cmdURLWasAllocated) delete[] cmdURL; if (extraHeadersWereAllocated) delete[] extraHeaders; if (contentLengthHeaderWasAllocated) delete[] contentLengthHeader; if (fVerbosityLevel >= 1) envir() << "Sending request: " << cmd << "\n"; if (fTunnelOverHTTPPortNum != 0 && strcmp(request->commandName(), "GET") != 0 && strcmp(request->commandName(), "POST") != 0) { // When we're tunneling RTSP-over-HTTP, we Base-64-encode the request before we send it. // (However, we don't do this for the HTTP "GET" and "POST" commands that we use to set up the tunnel.) char* origCmd = cmd; cmd = base64Encode(origCmd, strlen(cmd)); if (fVerbosityLevel >= 1) envir() << "\tThe request was base-64 encoded to: " << cmd << "\n\n"; delete[] origCmd; } if (send(fOutputSocketNum, cmd, strlen(cmd), 0) < 0) { char const* errFmt = "%s send() failed: "; unsigned const errLength = strlen(errFmt) + strlen(request->commandName()); char* err = new char[errLength]; sprintf(err, errFmt, request->commandName()); envir().setResultErrMsg(err); delete[] err; break; } // The command send succeeded, so enqueue the request record, so that its response (when it comes) can be handled: fRequestsAwaitingResponse.enqueue(request); delete[] cmd; return request->cseq(); } while (0); // An error occurred, so call the response handler immediately (indicating the error): delete[] cmd; handleRequestError(request); delete request; return 0;}void RTSPClient::handleRequestError(RequestRecord* request) { int resultCode = -envir().getErrno(); if (resultCode == 0) { // Choose some generic error code instead:#if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4) resultCode = -WSAENOTCONN;#else resultCode = -ENOTCONN;#endif } if (request->handler() != NULL) (*request->handler())(this, resultCode, strDup(envir().getResultMsg()));}Boolean RTSPClient::parseResponseCode(char const* line, unsigned& responseCode, char const*& responseString, Boolean& responseIsHTTP) { responseIsHTTP = False; // by default if (sscanf(line, "RTSP/%*s%u", &responseCode) != 1) { if (sscanf(line, "HTTP/%*s%u", &responseCode) != 1) return False; responseIsHTTP = True; // Note: We check for HTTP responses as well as RTSP responses, both in order to setup RTSP-over-HTTP tunneling, // and so that we get back a meaningful error if the client tried to mistakenly send a RTSP command to a HTTP-only server. } // Use everything after the RTSP/* as the response string: responseString = line; while (responseString[0] != '\0' && responseString[0] != ' ' && responseString[0] != '\t') ++responseString; while (responseString[0] != '\0' && (responseString[0] == ' ' || responseString[0] == '\t')) ++responseString; // skip whitespace return True;}void RTSPClient::handleIncomingRequest() { // Parse the request string into command name and 'CSeq', then 'handle' the command (by responding that we don't support it): char cmdName[RTSP_PARAM_STRING_MAX]; char urlPreSuffix[RTSP_PARAM_STRING_MAX]; char urlSuffix[RTSP_PARAM_STRING_MAX]; char cseq[RTSP_PARAM_STRING_MAX]; if (!parseRTSPRequestString(fResponseBuffer, fResponseBytesAlreadySeen, cmdName, sizeof cmdName, urlPreSuffix, sizeof urlPreSuffix, urlSuffix, sizeof urlSuffix, cseq, sizeof cseq)) { return; } else { if (fVerbosityLevel >= 1) { envir() << "Received incoming RTSP request: " << fResponseBuffer << "\n"; } char tmpBuf[2*RTSP_PARAM_STRING_MAX]; snprintf((char*)tmpBuf, sizeof tmpBuf, "RTSP/1.0 405 Method Not Allowed\r\nCSeq: %s\r\n\r\n", cseq); send(fOutputSocketNum, tmpBuf, strlen(tmpBuf), 0); }}Boolean RTSPClient::checkForHeader(char const* line, char const* headerName, unsigned headerNameLength, char const*& headerParams) { if (_strncasecmp(line, headerName, headerNameLength) != 0) return False; // The line begins with the desired header name. Trim off any whitespace, and return the header parameters: unsigned paramIndex = headerNameLength; while (line[paramIndex] != '\0' && (line[paramIndex] == ' ' || line[paramIndex] == '\t')) ++paramIndex; if (&line[paramIndex] == '\0') return False; // the header is assumed to be bad if it has no parameters headerParams = &line[paramIndex]; return True;}Boolean RTSPClient::parseTransportParams(char const* paramsStr, char*& serverAddressStr, portNumBits& serverPortNum, unsigned char& rtpChannelId, unsigned char& rtcpChannelId) { // Initialize the return parameters to 'not found' values: serverAddressStr = NULL; serverPortNum = 0; rtpChannelId = rtcpChannelId = 0xFF; char* foundServerAddressStr = NULL; Boolean foundServerPortNum = False; portNumBits clientPortNum = 0; Boolean foundClientPortNum = False; Boolean foundChannelIds = False; unsigned rtpCid, rtcpCid; Boolean isMulticast = True; // by default char* foundDestinationStr = NULL; portNumBits multicastPortNumRTP, multicastPortNumRTCP; Boolean foundMulticastPortNum = False; // Run through each of the parameters, looking for ones that we handle: char const* fields = paramsStr; char* field = strDupSize(fields); while (sscanf(fields, "%[^;]", field) == 1) { if (sscanf(field, "server_port=%hu", &serverPortNum) == 1) { foundServerPortNum = True; } else if (sscanf(field, "client_port=%hu", &clientPortNum) == 1) { foundClientPortNum = True; } else if (_strncasecmp(field, "source=", 7) == 0) { delete[] foundServerAddressStr; foundServerAddressStr = strDup(field+7); } else if (sscanf(field, "interleaved=%u-%u", &rtpCid, &rtcpCid) == 2) { rtpChannelId = (unsigned char)rtpCid; rtcpChannelId = (unsigned char)rtcpCid; foundChannelIds = True; } else if (strcmp(field, "unicast") == 0) { isMulticast = False; } else if (_strncasecmp(field, "destination=", 12) == 0) { delete[] foundDestinationStr; foundDestinationStr = strDup(field+12); } else if (sscanf(field, "port=%hu-%hu", &multicastPortNumRTP, &multicastPortNumRTCP) == 2) { foundMulticastPortNum = True; } fields += strlen(field); while (fields[0] == ';') ++fields; // skip over all leading ';' chars if (fields[0] == '\0') break; } delete[] field; // If we're multicast, and have a "destination=" (multicast) address, then use this // as the 'server' address (because some weird servers don't specify the multicast // address earlier, in the "DESCRIBE" response's SDP: if (isMulticast && foundDestinationStr != NULL && foundMulticastPortNum) { delete[] foundServerAddressStr; serverAddressStr = foundDestinationStr; serverPortNum = multicastPortNumRTP; return True; } delete[] foundDestinationStr; // We have a valid "Transport:" header if any of the following are true: // - We saw a "interleaved=" field, indicating RTP/RTCP-over-TCP streaming, or // - We saw a "server_port=" field, or // - We saw a "client_port=" field. // If we didn't also see a "server_port=" field, then the server port is assumed to be the same as the client port. if (foundChannelIds || foundServerPortNum || foundClientPortNum) { if (foundClientPortNum && !foundServerPortNum) { serverPortNum = clientPortNum; } serverAddressStr = foundServerAddressStr; return True; } delete[] foundServerAddressStr; return False;}Boolean RTSPClient::parseScaleParam(char const* paramStr, float& scale) { Locale l("C", LC_NUMERIC); return sscanf(paramStr, "%f", &scale) == 1;}Boolean RTSPClient::parseRTPInfoParams(char const*& paramsStr, u_int16_t& seqNum, u_int32_t& timestamp) { while (paramsStr[0] == ',') ++paramsStr; // "paramsStr" now consists of a ';'-separated list of parameters, ending with ',' or '\0'. char* field = strDupSize(paramsStr); while (sscanf(paramsStr, "%[^;,]", field) == 1) { if (sscanf(field, "seq=%hu", &seqNum) == 1 || sscanf(field, "rtptime=%u", ×tamp) == 1) { } paramsStr += strlen(field); if (paramsStr[0] == '\0' || paramsStr[0] == ',') break; // ASSERT: paramsStr[0] == ';' ++paramsStr; // skip over the ';' } delete[] field; return True;}Boolean RTSPClient::handleSETUPResponse(MediaSubsession& subsession, char const* sessionParamsStr, char const* transportParamsStr, Boolean streamUsingTCP) { char* sessionId = new char[responseBufferSize]; // ensures we have enough space Boolean success = False; do { // Check for a session id: if (sessionParamsStr == NULL || sscanf(sessionParamsStr, "%[^;]", sessionId) != 1) { envir().setResultMsg("Missing or bad \"Session:\" header"); break; } subsession.sessionId = strDup(sessionId); delete[] fLastSessionId; fLastSessionId = strDup(sessionId); // Also look for an optional "; timeout = " parameter following this: char const* afterSessionId = sessionParamsStr + strlen(sessionId); int timeoutVal; if (sscanf(afterSessionId, "; timeout = %d", &timeoutVal) == 1) { fSessionTimeoutParameter = timeoutVal; } // Parse the "Transport:" header parameters: char* serverAddressStr; portNumBits serverPortNum; unsigned char rtpChannelId, rtcpChannelId; if (!parseTransportParams(transportParamsStr, serverAddressStr, serverPortNum, rtpChannelId, rtcpChannelId)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -