📄 rtspclient.cpp
字号:
} if (fKasennaContentType != NULL && strcmp(fKasennaContentType, "PARTNER_41_MPEG-4") == 0) { char* describeSDP = describeURL(url, authenticator, True); delete[] currentWord; delete[] cmd; return describeSDP; } unsigned char byte1 = fServerAddress & 0x000000ff; unsigned char byte2 = (fServerAddress & 0x0000ff00) >> 8; unsigned char byte3 = (fServerAddress & 0x00ff0000) >> 16; unsigned char byte4 = (fServerAddress & 0xff000000) >> 24; char const* sdpFmt = "v=0\r\n" "o=NoSpacesAllowed 1 1 IN IP4 %u.%u.%u.%u\r\n" "s=%s\r\n" "c=IN IP4 %u.%u.%u.%u\r\n" "t=0 0\r\n" "a=control:*\r\n" "a=range:npt=0-%llu\r\n" "m=video 1554 RAW/RAW/UDP 33\r\n" "a=control:trackID=%d\r\n"; unsigned sdpBufSize = strlen(sdpFmt) + 4*3 // IP address + strlen(url) + 20 // max int length + 20; // max int length char* sdpBuf = new char[sdpBufSize]; sprintf(sdpBuf, sdpFmt, byte1, byte2, byte3, byte4, url, byte1, byte2, byte3, byte4, 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, Boolean allowKasennaProtocol) { Authenticator authenticator; authenticator.setUsernameAndPassword(username, password); char* describeResult = describeURL(url, &authenticator, allowKasennaProtocol); 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, allowKasennaProtocol); if (describeResult != NULL) { // The authenticator worked, so use it in future requests: fCurrentAuthenticator = authenticator; } return describeResult;}char* RTSPClient::sendOptionsCmd(char const* url, char* username, char* password, Authenticator* authenticator) { char* result = NULL; char* cmd = NULL; Boolean haveAllocatedAuthenticator = False; do { if (authenticator == NULL) { // First, check whether "url" contains a username:password to be used // (and no username,password pair was supplied separately): if (username == NULL && password == NULL && parseRTSPURLUsernamePassword(url, username, password)) { Authenticator authenticator; authenticator.setUsernameAndPassword(username, password); result = sendOptionsCmd(url, username, password, &authenticator); delete[] username; delete[] password; // they were dynamically allocated break; } else if (username != NULL && password != NULL) { // Use the separately supplied username and password: authenticator = new Authenticator; haveAllocatedAuthenticator = True; authenticator->setUsernameAndPassword(username, password); result = sendOptionsCmd(url, username, password, authenticator); if (result != NULL) break; // We are already authorized // The "realm" field should have been filled in: if (authenticator->realm() == NULL) { // We haven't been given enough information to try again, so fail: break; } // Try again: } } if (!openConnectionFromURL(url, authenticator)) break; // Send the OPTIONS command: // First, construct an authenticator string: char* authenticatorStr = createAuthenticatorString(authenticator, "OPTIONS", url); char* const cmdFmt = "OPTIONS %s RTSP/1.0\r\n" "CSeq: %d\r\n" "%s" "%s"#ifdef SUPPORT_REAL_RTSP REAL_OPTIONS_HEADERS#endif "\r\n"; unsigned cmdSize = strlen(cmdFmt) + strlen(url) + 20 /* max int len */ + strlen(authenticatorStr) + fUserAgentHeaderStrSize; cmd = new char[cmdSize]; sprintf(cmd, cmdFmt, url, ++fCSeq, authenticatorStr, fUserAgentHeaderStr); delete[] authenticatorStr; 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, False /*don't check for response code 200*/)) break; if (responseCode != 200) { checkForAuthenticationFailure(responseCode, nextLineStart, authenticator); envir().setResultMsg("cannot handle OPTIONS response: ", firstLine); 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; if (haveAllocatedAuthenticator) delete authenticator; 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;}char const* RTSPClient::sessionURL(MediaSession const& session) const { char const* url = session.controlPath(); if (url == NULL || strcmp(url, "*") == 0) url = fBaseURL; return url;}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 "sessionURL()" // doesn't end with a "/", and "subsession.controlPath()" is relative. // The right thing would have been to truncate "sessionURL()" back to the // rightmost "/", and then add "subsession.controlPath()". // In practice, though, each "DESCRIBE" response typically contains // a "Content-Base:" header that consists of "sessionURL()" 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 = sessionURL(subsession.parentSession()); 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, authenticator)) 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, Boolean forceMulticastOnUnspecified) { char* cmd = NULL; char* setupStr = NULL; if (fServerIsMicrosoft) { // Microsoft doesn't send the right endTime on live streams. Correct this: char *tmpStr = subsession.parentSession().mediaSessionType(); if (tmpStr != NULL && strncmp(tmpStr, "broadcast", 9) == 0) { subsession.parentSession().playEndTime() = 0.0; } } 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(); Boolean requestMulticastStreaming = IsMulticastAddress(connectionAddress) || (connectionAddress == 0 && forceMulticastOnUnspecified); transportTypeStr = requestMulticastStreaming ? ";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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -