📄 rtspclient.cpp
字号:
+ 20; // max int length char* sdpBuf = new char[sdpBufSize]; sprintf(sdpBuf, sdpFmt, byte1, byte2, byte3, byte4, url, byte1, byte2, byte3, byte4, (unsigned)(mh_duration/1000000), videoPid); char* result = strDup(sdpBuf); delete[] sdpBuf; delete[] currentWord; delete[] cmd; return result; } ////////// END Kasenna BS ////////// delete[] cmd; return strDup(bodyStart); } while (0); delete[] cmd; if (fDescribeStatusCode == 0) fDescribeStatusCode = 2; return NULL;}char* RTSPClient::describeWithPassword(char const* url, char const* username, char const* password) { Authenticator authenticator; authenticator.setUsernameAndPassword(username, password); char* describeResult = describeURL(url, &authenticator); if (describeResult != NULL) { // We are already authorized return describeResult; } // The "realm" field should have been filled in: if (authenticator.realm() == NULL) { // We haven't been given enough information to try again, so fail: return NULL; } // Try again: describeResult = describeURL(url, &authenticator); if (describeResult != NULL) { // The authenticator worked, so use it in future requests: fCurrentAuthenticator = authenticator; } return describeResult;}char* RTSPClient::sendOptionsCmd(char const* url) { char* result = NULL; char* cmd = NULL; do { if (!openConnectionFromURL(url)) break; // Send the OPTIONS command: char* const cmdFmt = "OPTIONS %s RTSP/1.0\r\n" "CSeq: %d\r\n" "%s"#ifdef SUPPORT_REAL_RTSP REAL_OPTIONS_HEADERS#endif "\r\n"; unsigned cmdSize = strlen(cmdFmt) + strlen(url) + 20 /* max int len */ + fUserAgentHeaderStrSize; cmd = new char[cmdSize]; sprintf(cmd, cmdFmt, url, ++fCSeq, fUserAgentHeaderStr); if (!sendRequest(cmd, "OPTIONS")) break; // Get the response from the server: unsigned bytesRead; unsigned responseCode; char* firstLine; char* nextLineStart; if (!getResponse("OPTIONS", bytesRead, responseCode, firstLine, nextLineStart)) break; // Look for a "Public:" header (which will contain our result str): char* lineStart; while (1) { lineStart = nextLineStart; if (lineStart == NULL) break; nextLineStart = getLine(lineStart); if (_strncasecmp(lineStart, "Public: ", 8) == 0) { delete[] result; result = strDup(&lineStart[8]);#ifdef SUPPORT_REAL_RTSP } else if (_strncasecmp(lineStart, "RealChallenge1: ", 16) == 0) { delete[] fRealChallengeStr; fRealChallengeStr = strDup(&lineStart[16]);#endif } } } while (0); delete[] cmd; return result;}static Boolean isAbsoluteURL(char const* url) { // Assumption: "url" is absolute if it contains a ':', before any // occurrence of '/' while (*url != '\0' && *url != '/') { if (*url == ':') return True; ++url; } return False;}void RTSPClient::constructSubsessionURL(MediaSubsession const& subsession, char const*& prefix, char const*& separator, char const*& suffix) { // Figure out what the URL describing "subsession" will look like. // The URL is returned in three parts: prefix; separator; suffix //##### NOTE: This code doesn't really do the right thing if "fBaseURL" // doesn't end with a "/", and "subsession.controlPath()" is relative. // The right thing would have been to truncate "fBaseURL" back to the // rightmost "/", and then add "subsession.controlPath()". // In practice, though, each "DESCRIBE" response typically contains // a "Content-Base:" header that consists of "fBaseURL" followed by // a "/", in which case this code ends up giving the correct result. // However, we should really fix this code to do the right thing, and // also check for and use the "Content-Base:" header appropriately. ##### prefix = fBaseURL; if (prefix == NULL) prefix = ""; suffix = subsession.controlPath(); if (suffix == NULL) suffix = ""; if (isAbsoluteURL(suffix)) { prefix = separator = ""; } else { unsigned prefixLen = strlen(prefix); separator = (prefix[prefixLen-1] == '/' || suffix[0] == '/') ? "" : "/"; }}Boolean RTSPClient::announceSDPDescription(char const* url, char const* sdpDescription, Authenticator* authenticator) { char* cmd = NULL; do { if (!openConnectionFromURL(url)) break; // Send the ANNOUNCE command: // First, construct an authenticator string: fCurrentAuthenticator.reset(); char* authenticatorStr = createAuthenticatorString(authenticator, "ANNOUNCE", url); char* const cmdFmt = "ANNOUNCE %s RTSP/1.0\r\n" "CSeq: %d\r\n" "Content-Type: application/sdp\r\n" "%s" "Content-length: %d\r\n\r\n" "%s"; // Note: QTSS hangs if an "ANNOUNCE" contains a "User-Agent:" field (go figure), so don't include one here unsigned sdpSize = strlen(sdpDescription); unsigned cmdSize = strlen(cmdFmt) + strlen(url) + 20 /* max int len */ + strlen(authenticatorStr) + 20 /* max int len */ + sdpSize; cmd = new char[cmdSize]; sprintf(cmd, cmdFmt, url, ++fCSeq, authenticatorStr, sdpSize, sdpDescription); delete[] authenticatorStr; if (!sendRequest(cmd, "ANNOUNCE")) break; // Get the response from the server: unsigned bytesRead; unsigned responseCode; char* firstLine; char* nextLineStart; if (!getResponse("ANNOUNCE", bytesRead, responseCode, firstLine, nextLineStart, False /*don't check for response code 200*/)) break; // Inspect the first line to check whether it's a result code 200 if (responseCode != 200) { checkForAuthenticationFailure(responseCode, nextLineStart, authenticator); envir().setResultMsg("cannot handle ANNOUNCE response: ", firstLine); break; } delete[] cmd; return True; } while (0); delete[] cmd; return False;}Boolean RTSPClient::announceWithPassword(char const* url, char const* sdpDescription, char const* username, char const* password) { Authenticator authenticator; authenticator.setUsernameAndPassword(username, password); if (announceSDPDescription(url, sdpDescription, &authenticator)) { // We are already authorized return True; } // The "realm" field should have been filled in: if (authenticator.realm() == NULL) { // We haven't been given enough information to try again, so fail: return False; } // Try again: Boolean secondTrySuccess = announceSDPDescription(url, sdpDescription, &authenticator); if (secondTrySuccess) { // The authenticator worked, so use it in future requests: fCurrentAuthenticator = authenticator; } return secondTrySuccess;}Boolean RTSPClient::setupMediaSubsession(MediaSubsession& subsession, Boolean streamOutgoing, Boolean streamUsingTCP) { char* cmd = NULL; char* setupStr = NULL; do { // Construct the SETUP command: // First, construct an authenticator string: char* authenticatorStr = createAuthenticatorString(&fCurrentAuthenticator, "SETUP", fBaseURL); // When sending more than one "SETUP" request, include a "Session:" // header in the 2nd and later "SETUP"s. char* sessionStr; if (fLastSessionId != NULL) { sessionStr = new char[20+strlen(fLastSessionId)]; sprintf(sessionStr, "Session: %s\r\n", fLastSessionId); } else { sessionStr = ""; } char* transportStr = NULL;#ifdef SUPPORT_REAL_RTSP if (usingRealNetworksChallengeResponse()) { // Use a special "Transport:" header, and also add a 'challenge response'. char challenge2[64]; char checksum[34]; RealCalculateChallengeResponse(fRealChallengeStr, challenge2, checksum); char const* etag = fRealETagStr == NULL ? "" : fRealETagStr; char* transportHeader; if (subsession.parentSession().isRealNetworksRDT) { transportHeader = strDup("Transport: x-pn-tng/tcp;mode=play,rtp/avp/unicast;mode=play\r\n"); } else { // Use a regular "Transport:" header: char const* transportHeaderFmt = "Transport: RTP/AVP%s%s=%d-%d\r\n"; char const* transportTypeStr; char const* portTypeStr; unsigned short rtpNumber, rtcpNumber; if (streamUsingTCP) { // streaming over the RTSP connection transportTypeStr = "/TCP;unicast"; portTypeStr = ";interleaved"; rtpNumber = fTCPStreamIdCount++; rtcpNumber = fTCPStreamIdCount++; } else { // normal RTP streaming unsigned connectionAddress = subsession.connectionEndpointAddress(); transportTypeStr = IsMulticastAddress(connectionAddress) ? ";multicast" : ";unicast"; portTypeStr = ";client_port"; rtpNumber = subsession.clientPortNum(); if (rtpNumber == 0) { envir().setResultMsg("Client port number unknown\n"); break; } rtcpNumber = rtpNumber + 1; } unsigned transportHeaderSize = strlen(transportHeaderFmt) + strlen(transportTypeStr) + strlen(portTypeStr) + 2*5 /* max port len */; transportHeader = new char[transportHeaderSize]; sprintf(transportHeader, transportHeaderFmt, transportTypeStr, portTypeStr, rtpNumber, rtcpNumber); } char const* transportFmt = "%s" "RealChallenge2: %s, sd=%s\r\n" "If-Match: %s\r\n"; unsigned transportSize = strlen(transportFmt) + strlen(transportHeader) + sizeof challenge2 + sizeof checksum + strlen(etag); transportStr = new char[transportSize]; sprintf(transportStr, transportFmt, transportHeader, challenge2, checksum, etag); delete[] transportHeader; if (subsession.parentSession().isRealNetworksRDT) { // Also, tell the RDT source to use the RTSP TCP socket: RealRDTSource* rdtSource = (RealRDTSource*)(subsession.readSource()); rdtSource->setInputSocket(fInputSocketNum); } }#endif char const *prefix, *separator, *suffix; constructSubsessionURL(subsession, prefix, separator, suffix); char* transportFmt; if (strcmp(subsession.protocolName(), "UDP") == 0) { char const* setupFmt = "SETUP %s%s RTSP/1.0\r\n"; unsigned setupSize = strlen(setupFmt) + strlen(prefix) + strlen (separator); setupStr = new char[setupSize]; sprintf(setupStr, setupFmt, prefix, separator); transportFmt = "Transport: RAW/RAW/UDP%s%s%s=%d-%d\r\n"; } else { char const* setupFmt = "SETUP %s%s%s RTSP/1.0\r\n"; unsigned setupSize = strlen(setupFmt) + strlen(prefix) + strlen (separator) + strlen(suffix); setupStr = new char[setupSize]; sprintf(setupStr, setupFmt, prefix, separator, suffix); transportFmt = "Transport: RTP/AVP%s%s%s=%d-%d\r\n"; } if (transportStr == NULL) { // Construct a "Transport:" header. char const* transportTypeStr; char const* modeStr = streamOutgoing ? ";mode=receive" : ""; // Note: I think the above is nonstandard, but DSS wants it this way char const* portTypeStr; unsigned short rtpNumber, rtcpNumber; if (streamUsingTCP) { // streaming over the RTSP connection transportTypeStr = "/TCP;unicast"; portTypeStr = ";interleaved"; rtpNumber = fTCPStreamIdCount++; rtcpNumber = fTCPStreamIdCount++; } else { // normal RTP streaming unsigned connectionAddress = subsession.connectionEndpointAddress(); transportTypeStr = IsMulticastAddress(connectionAddress) ? ";multicast" : ";unicast"; portTypeStr = ";client_port"; rtpNumber = subsession.clientPortNum(); if (rtpNumber == 0) { envir().setResultMsg("Client port number unknown\n"); break; } rtcpNumber = rtpNumber + 1; } unsigned transportSize = strlen(transportFmt) + strlen(transportTypeStr) + strlen(modeStr) + strlen(portTypeStr) + 2*5 /* max port len */; transportStr = new char[transportSize]; sprintf(transportStr, transportFmt, transportTypeStr, modeStr, portTypeStr, rtpNumber, rtcpNumber); } // (Later implement more, as specified in the RTSP spec, sec D.1 #####) char* const cmdFmt = "%s" "CSeq: %d\r\n" "%s" "%s" "%s" "%s" "\r\n"; unsigned cmdSize = strlen(cmdFmt) + strlen(setupStr) + 20 /* max int len */ + strlen(transportStr) + strlen(sessionStr) + strlen(authenticatorStr) + fUserAgentHeaderStrSize; cmd = new char[cmdSize]; sprintf(cmd, cmdFmt, setupStr, ++fCSeq, transportStr, sessionStr, authenticatorStr, fUserAgentHeaderStr); delete[] authenticatorStr; if (sessionStr[0] != '\0') delete[] sessionStr; delete[] setupStr; delete[] transportStr; // And then send it: if (!sendRequest(cmd, "SETUP")) break; // Get the response from the server: unsigned bytesRead; unsigned responseCode; char* firstLine; char* nextLineStart; if (!getResponse("SETUP", bytesRead, responseCode, firstLine, nextLineStart)) break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -