📄 rtspclient.cpp
字号:
/**********This library is free software; you can redistribute it and/or modify it underthe terms of the GNU Lesser General Public License as published by theFree Software Foundation; either version 2.1 of the License, or (at youroption) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)This library is distributed in the hope that it will be useful, but WITHOUTANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESSFOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License formore details.You should have received a copy of the GNU Lesser General Public Licensealong with this library; if not, write to the Free Software Foundation, Inc.,59 Temple Place, Suite 330, Boston, MA 02111-1307 USA**********/// "liveMedia"// Copyright (c) 1996-2004 Live Networks, Inc. All rights reserved.// A generic RTSP client// Implementation#include "RTSPClient.hh"#include "GroupsockHelper.hh"#include "our_md5.h"#ifdef SUPPORT_REAL_RTSP#include "../RealRTSP/include/RealRTSP.hh"#endif#if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)#define _strncasecmp _strnicmp#else#define _strncasecmp strncasecmp#endif////////// RTSPClient //////////RTSPClient* RTSPClient::createNew(UsageEnvironment& env, int verbosityLevel, char const* applicationName, portNumBits tunnelOverHTTPPortNum) { return new RTSPClient(env, verbosityLevel, applicationName, tunnelOverHTTPPortNum);}Boolean RTSPClient::lookupByName(UsageEnvironment& env, char const* instanceName, RTSPClient*& resultClient) { resultClient = NULL; // unless we succeed Medium* medium; if (!Medium::lookupByName(env, instanceName, medium)) return False; if (!medium->isRTSPClient()) { env.setResultMsg(instanceName, " is not a RTSP client"); return False; } resultClient = (RTSPClient*)medium; return True;}unsigned RTSPClient::fCSeq = 0;RTSPClient::RTSPClient(UsageEnvironment& env, int verbosityLevel, char const* applicationName, portNumBits tunnelOverHTTPPortNum) : Medium(env), fVerbosityLevel(verbosityLevel), fTunnelOverHTTPPortNum(tunnelOverHTTPPortNum), fInputSocketNum(-1), fOutputSocketNum(-1), fServerAddress(0), fBaseURL(NULL), fTCPStreamIdCount(0), fLastSessionId(NULL),#ifdef SUPPORT_REAL_RTSP fRealChallengeStr(NULL), fRealETagStr(NULL),#endif fServerIsKasenna(False), fKasennaContentType(NULL){ fResponseBufferSize = 20000; fResponseBuffer = new char[fResponseBufferSize+1]; // Set the "User-Agent:" header to use in each request: char const* const libName = "LIVE.COM Streaming Media v"; char const* const libVersionStr = LIVEMEDIA_LIBRARY_VERSION_STRING; char const* libPrefix; char const* libSuffix; if (applicationName == NULL || applicationName[0] == '\0') { applicationName = libPrefix = libSuffix = ""; } else { libPrefix = " ("; libSuffix = ")"; } char const* const formatStr = "User-Agent: %s%s%s%s%s\r\n"; unsigned headerSize = strlen(formatStr) + strlen(applicationName) + strlen(libPrefix) + strlen(libName) + strlen(libVersionStr) + strlen(libSuffix); fUserAgentHeaderStr = new char[headerSize]; sprintf(fUserAgentHeaderStr, formatStr, applicationName, libPrefix, libName, libVersionStr, libSuffix); fUserAgentHeaderStrSize = strlen(fUserAgentHeaderStr);}RTSPClient::~RTSPClient() { reset(); delete[] fResponseBuffer; delete[] fUserAgentHeaderStr;}Boolean RTSPClient::isRTSPClient() const { return True;}void RTSPClient::resetTCPSockets() { if (fInputSocketNum >= 0) { ::closeSocket(fInputSocketNum); if (fOutputSocketNum != fInputSocketNum) ::closeSocket(fOutputSocketNum); } fInputSocketNum = fOutputSocketNum = -1;}void RTSPClient::reset() { resetTCPSockets(); fServerAddress = 0; delete[] fBaseURL; fBaseURL = NULL; fCurrentAuthenticator.reset(); delete[] fKasennaContentType; fKasennaContentType = NULL;#ifdef SUPPORT_REAL_RTSP delete[] fRealChallengeStr; fRealChallengeStr = NULL; delete[] fRealETagStr; fRealETagStr = NULL;#endif delete[] fLastSessionId; fLastSessionId = NULL;}static char* getLine(char* startOfLine) { // returns the start of the next line, or NULL if none for (char* ptr = startOfLine; *ptr != '\0'; ++ptr) { if (*ptr == '\r' || *ptr == '\n') { // We found the end of the line *ptr++ = '\0'; if (*ptr == '\n') ++ptr; return ptr; } } return NULL;}char* RTSPClient::describeURL(char const* url, Authenticator* authenticator, Boolean allowKasennaProtocol) { char* cmd = NULL; fDescribeStatusCode = 0; do { // First, check whether "url" contains a username:password to be used: char* username; char* password; if (authenticator == NULL && parseRTSPURLUsernamePassword(url, username, password)) { char* result = describeWithPassword(url, username, password); delete[] username; delete[] password; // they were dynamically allocated return result; } if (!openConnectionFromURL(url)) break; // Send the DESCRIBE command: // First, construct an authenticator string: fCurrentAuthenticator.reset(); char* authenticatorStr = createAuthenticatorString(authenticator, "DESCRIBE", url); char const* acceptStr = allowKasennaProtocol ? "Accept: application/x-rtsp-mh, application/sdp\r\n" : "Accept: application/sdp\r\n"; // (Later implement more, as specified in the RTSP spec, sec D.1 #####) char* const cmdFmt = "DESCRIBE %s RTSP/1.0\r\n" "CSeq: %d\r\n" "%s" "%s" "%s"#ifdef SUPPORT_REAL_RTSP REAL_DESCRIBE_HEADERS#endif "\r\n"; unsigned cmdSize = strlen(cmdFmt) + strlen(url) + 20 /* max int len */ + strlen(acceptStr) + strlen(authenticatorStr) + fUserAgentHeaderStrSize; cmd = new char[cmdSize]; sprintf(cmd, cmdFmt, url, ++fCSeq, acceptStr, authenticatorStr, fUserAgentHeaderStr); delete[] authenticatorStr; if (!sendRequest(cmd, "DESCRIBE")) break; // Get the response from the server: unsigned bytesRead; unsigned responseCode; char* firstLine; char* nextLineStart; if (!getResponse("DESCRIBE", 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 that // we can handle. Boolean wantRedirection = False; char* redirectionURL = NULL;#ifdef SUPPORT_REAL_RTSP delete[] fRealETagStr; fRealETagStr = new char[fResponseBufferSize];#endif if (responseCode == 301 || responseCode == 302) { wantRedirection = True; redirectionURL = new char[fResponseBufferSize]; // ensures enough space } else if (responseCode != 200) { checkForAuthenticationFailure(responseCode, nextLineStart, authenticator); envir().setResultMsg("cannot handle DESCRIBE response: ", firstLine); break; } // Skip every subsequent header line, until we see a blank line // The remaining data is assumed to be the SDP descriptor that we want. // We should really do some checking on the headers here - e.g., to // check for "Content-type: application/sdp", "Content-base", // "Content-location", "CSeq", etc. ##### char* serverType = new char[fResponseBufferSize]; // ensures enough space int contentLength = -1; char* lineStart; while (1) { lineStart = nextLineStart; if (lineStart == NULL) break; nextLineStart = getLine(lineStart); if (lineStart[0] == '\0') break; // this is a blank line if (sscanf(lineStart, "Content-Length: %d", &contentLength) == 1 || sscanf(lineStart, "Content-length: %d", &contentLength) == 1) { if (contentLength < 0) { envir().setResultMsg("Bad \"Content-length:\" header: \"", lineStart, "\""); break; } } else if (sscanf(lineStart, "Server: %s", serverType) == 1) { if (strncmp(serverType, "Kasenna", 7) == 0) fServerIsKasenna = True;#ifdef SUPPORT_REAL_RTSP } else if (sscanf(lineStart, "ETag: %s", fRealETagStr) == 1) {#endif } else if (wantRedirection) { if (sscanf(lineStart, "Location: %s", redirectionURL) == 1) { // Try again with this URL if (fVerbosityLevel >= 1) { envir() << "Redirecting to the new URL \"" << redirectionURL << "\"\n"; } reset(); char* result = describeURL(redirectionURL); delete[] redirectionURL; delete[] serverType; return result; } } } delete[] serverType; // We're now at the end of the response header lines if (wantRedirection) { envir().setResultMsg("Saw redirection response code, but not a \"Location:\" header"); delete[] redirectionURL; break; } if (lineStart == NULL) { envir().setResultMsg("no content following header lines: ", fResponseBuffer); break; } // Use the remaining data as the SDP descr, but first, check // the "Content-length:" header (if any) that we saw. We may need to // read more data, or we may have extraneous data in the buffer. char* bodyStart = nextLineStart; if (contentLength >= 0) { // We saw a "Content-length:" header unsigned numBodyBytes = &firstLine[bytesRead] - bodyStart; if (contentLength > (int)numBodyBytes) { // We need to read more data. First, make sure we have enough // space for it: unsigned numExtraBytesNeeded = contentLength - numBodyBytes; unsigned remainingBufferSize = fResponseBufferSize - (bytesRead + (firstLine - fResponseBuffer)); if (numExtraBytesNeeded > remainingBufferSize) { char tmpBuf[200]; sprintf(tmpBuf, "Read buffer size (%d) is too small for \"Content-length:\" %d (need a buffer size of >= %d bytes\n", fResponseBufferSize, contentLength, fResponseBufferSize + numExtraBytesNeeded - remainingBufferSize); envir().setResultMsg(tmpBuf); break; } // Keep reading more data until we have enough: if (fVerbosityLevel >= 1) { envir() << "Need to read " << numExtraBytesNeeded << " extra bytes\n"; } while (numExtraBytesNeeded > 0) { struct sockaddr_in fromAddress; char* ptr = &firstLine[bytesRead]; int bytesRead2 = readSocket(envir(), fInputSocketNum, (unsigned char*)ptr, numExtraBytesNeeded, fromAddress); if (bytesRead2 < 0) break; ptr[bytesRead2] = '\0'; if (fVerbosityLevel >= 1) { envir() << "Read " << bytesRead2 << " extra bytes: " << ptr << "\n"; } bytesRead += bytesRead2; numExtraBytesNeeded -= bytesRead2; } if (numExtraBytesNeeded > 0) break; // one of the reads failed } // Remove any '\0' characters from inside the SDP description. // Any such characters would violate the SDP specification, but // some RTSP servers have been known to include them: int from, to = 0; for (from = 0; from < contentLength; ++from) { if (bodyStart[from] != '\0') { if (to != from) bodyStart[to] = bodyStart[from]; ++to; } } if (from != to && fVerbosityLevel >= 1) { envir() << "Warning: " << from-to << " invalid 'NULL' bytes were found in (and removed from) the SDP description.\n"; } bodyStart[to] = '\0'; // trims any extra data } ////////// BEGIN Kasenna BS ////////// // If necessary, handle Kasenna's non-standard BS response: if (fServerIsKasenna && strncmp(bodyStart, "<MediaDescription>", 18) == 0) { // Translate from x-rtsp-mh to sdp int videoPid, audioPid; unsigned mh_duration; char* currentWord = new char[fResponseBufferSize]; // ensures enough space delete[] fKasennaContentType; fKasennaContentType = new char[fResponseBufferSize]; // ensures enough space char* currentPos = bodyStart; while (strcmp(currentWord, "</MediaDescription>") != 0) { sscanf(currentPos, "%s", currentWord); if (strcmp(currentWord, "VideoPid") == 0) { currentPos += strlen(currentWord) + 1; sscanf(currentPos, "%s", currentWord); currentPos += strlen(currentWord) + 1; sscanf(currentPos, "%d", &videoPid); currentPos += 3; } if (strcmp(currentWord, "AudioPid") == 0) { currentPos += strlen(currentWord) + 1; sscanf(currentPos, "%s", currentWord); currentPos += strlen(currentWord) + 1; sscanf(currentPos, "%d", &audioPid); currentPos += 3; } if (strcmp(currentWord, "Duration") == 0) { currentPos += strlen(currentWord) + 1; sscanf(currentPos, "%s", currentWord); currentPos += strlen(currentWord) + 1; sscanf(currentPos, "%d", &mh_duration); currentPos += 3; } if (strcmp(currentWord, "TypeSpecificData") == 0) { currentPos += strlen(currentWord) + 1; sscanf(currentPos, "%s", currentWord); currentPos += strlen(currentWord) + 1; sscanf(currentPos, "%s", fKasennaContentType); currentPos += 3; printf("Kasenna Content Type: %s\n", fKasennaContentType); } currentPos += strlen(currentWord) + 1; } 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-%u\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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -