⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rtspclient.cpp

📁 流媒体传输协议的实现代码,非常有用.可以支持rtsp mms等流媒体传输协议
💻 CPP
📖 第 1 页 / 共 5 页
字号:
/**********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 + -