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

📄 sipclient.cpp

📁 流媒体传输协议的实现代码,非常有用.可以支持rtsp mms等流媒体传输协议
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/**********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 SIP client// Implementation#include "SIPClient.hh"#include "GroupsockHelper.hh"#if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)#define _strncasecmp _strnicmp#else#define _strncasecmp strncasecmp#endif////////// SIPClient //////////SIPClient* SIPClient::createNew(UsageEnvironment& env,	    unsigned char desiredAudioRTPPayloadFormat,	    char const* mimeSubtype,	    int verbosityLevel, char const* applicationName) {  return new SIPClient(env, desiredAudioRTPPayloadFormat, mimeSubtype,		       verbosityLevel, applicationName);}SIPClient::SIPClient(UsageEnvironment& env,		     unsigned char desiredAudioRTPPayloadFormat,		     char const* mimeSubtype,		     int verbosityLevel, char const* applicationName)  : Medium(env),    fT1(500000 /* 500 ms */),    fDesiredAudioRTPPayloadFormat(desiredAudioRTPPayloadFormat),    fVerbosityLevel(verbosityLevel),    fCSeq(0), fURL(NULL), fURLSize(0),    fToTagStr(NULL), fToTagStrSize(0),    fUserName(NULL), fUserNameSize(0),    fInviteSDPDescription(NULL), fInviteCmd(NULL), fInviteCmdSize(0){  if (mimeSubtype == NULL) mimeSubtype = "";  fMIMESubtype = strDup(mimeSubtype);  fMIMESubtypeSize = strlen(fMIMESubtype);  if (applicationName == NULL) applicationName = "";  fApplicationName = strDup(applicationName);  fApplicationNameSize = strlen(fApplicationName);  struct in_addr ourAddress;  ourAddress.s_addr = ourSourceAddressForMulticast(env); // hack  fOurAddressStr = strDup(our_inet_ntoa(ourAddress));  fOurAddressStrSize = strlen(fOurAddressStr);  fOurSocket = new Groupsock(env, ourAddress, 0, 255);  if (fOurSocket == NULL) {    env << "ERROR: Failed to create socket for addr "	<< our_inet_ntoa(ourAddress) << ": "	<< env.getResultMsg() << "\n";  }  // Now, find out our source port number.  Hack: Do this by first trying to  // send a 0-length packet, so that the "getSourcePort()" call will work.  fOurSocket->output(envir(), 255, (unsigned char*)"", 0);  Port srcPort(0);  getSourcePort(env, fOurSocket->socketNum(), srcPort);  if (srcPort.num() != 0) {    fOurPortNum = ntohs(srcPort.num());  } else {    // No luck.  Try again using a default port number:    fOurPortNum = 5060;    delete fOurSocket;    fOurSocket = new Groupsock(env, ourAddress, fOurPortNum, 255);    if (fOurSocket == NULL) {      env << "ERROR: Failed to create socket for addr "	  << our_inet_ntoa(ourAddress) << ", port "	  << fOurPortNum << ": "	  << env.getResultMsg() << "\n";    }  }        // Set various headers to be used in each request:  char const* formatStr;  unsigned headerSize;  // Set the "User-Agent:" header:  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 = ")";  }  formatStr = "User-Agent: %s%s%s%s%s\r\n";  headerSize    = strlen(formatStr) + fApplicationNameSize + strlen(libPrefix)    + strlen(libName) + strlen(libVersionStr) + strlen(libSuffix);  fUserAgentHeaderStr = new char[headerSize];  sprintf(fUserAgentHeaderStr, formatStr,	  applicationName, libPrefix, libName, libVersionStr, libSuffix);  fUserAgentHeaderStrSize = strlen(fUserAgentHeaderStr);  reset();}SIPClient::~SIPClient() {  reset();  delete[] fUserAgentHeaderStr;  delete fOurSocket;  delete[] (char*)fOurAddressStr;  delete[] (char*)fApplicationName;  delete[] (char*)fMIMESubtype;}void SIPClient::reset() {  fWorkingAuthenticator = NULL;  delete[] fInviteCmd; fInviteCmd = NULL; fInviteCmdSize = 0;  delete[] fInviteSDPDescription; fInviteSDPDescription = NULL;  delete[] (char*)fUserName; fUserName = strDup(fApplicationName);  fUserNameSize = strlen(fUserName);  fValidAuthenticator.reset();  delete[] (char*)fToTagStr; fToTagStr = NULL; fToTagStrSize = 0;  fServerPortNum = 0;  fServerAddress.s_addr = 0;  delete[] (char*)fURL; fURL = NULL; fURLSize = 0;}void SIPClient::setProxyServer(unsigned proxyServerAddress,			       portNumBits proxyServerPortNum) {  fServerAddress.s_addr = proxyServerAddress;  fServerPortNum = proxyServerPortNum;  if (fOurSocket != NULL) {    fOurSocket->changeDestinationParameters(fServerAddress,					    fServerPortNum, 255);  }}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* SIPClient::invite(char const* url, Authenticator* authenticator) {  // First, check whether "url" contains a username:password to be used:  fInviteStatusCode = 0;  char* username; char* password;  if (authenticator == NULL      && parseSIPURLUsernamePassword(url, username, password)) {    char* result = inviteWithPassword(url, username, password);    delete[] username; delete[] password; // they were dynamically allocated    return result;  }  if (!processURL(url)) return NULL;  delete[] (char*)fURL; fURL = strDup(url);  fURLSize = strlen(fURL);  fCallId = our_random();  fFromTag = our_random();  return invite1(authenticator);}char* SIPClient::invite1(Authenticator* authenticator) {  do {    // Send the INVITE command:    // First, construct an authenticator string:    fValidAuthenticator.reset();    fWorkingAuthenticator = authenticator;    char* authenticatorStr      = createAuthenticatorString(fWorkingAuthenticator, "INVITE", fURL);    // Then, construct the SDP description to be sent in the INVITE:    char* rtpmapLine;    unsigned rtpmapLineSize;    if (fMIMESubtypeSize > 0) {      char* const rtpmapFmt = 	"a=rtpmap:%u %s/8000\r\n";      unsigned rtpmapFmtSize = strlen(rtpmapFmt)	+ 3 /* max char len */ + fMIMESubtypeSize;      rtpmapLine = new char[rtpmapFmtSize];      sprintf(rtpmapLine, rtpmapFmt,	      fDesiredAudioRTPPayloadFormat, fMIMESubtype);      rtpmapLineSize = strlen(rtpmapLine);    } else {      // Static payload type => no "a=rtpmap:" line      rtpmapLine = strDup("");      rtpmapLineSize = 0;    }    char* const inviteSDPFmt =       "v=0\r\n"      "o=- %u %u IN IP4 %s\r\n"      "s=%s session\r\n"      "c=IN IP4 %s\r\n"      "t=0 0\r\n"      "m=audio %u RTP/AVP %u\r\n"      "%s";    unsigned inviteSDPFmtSize = strlen(inviteSDPFmt)      + 20 /* max int len */ + 20 + fOurAddressStrSize      + fApplicationNameSize      + fOurAddressStrSize      + 5 /* max short len */ + 3 /* max char len */      + rtpmapLineSize;    delete[] fInviteSDPDescription;    fInviteSDPDescription = new char[inviteSDPFmtSize];    sprintf(fInviteSDPDescription, inviteSDPFmt,	    fCallId, fCSeq, fOurAddressStr,	    fApplicationName,	    fOurAddressStr,	    fClientStartPortNum, fDesiredAudioRTPPayloadFormat,	    rtpmapLine);    unsigned inviteSDPSize = strlen(fInviteSDPDescription);    delete[] rtpmapLine;    char* const cmdFmt =      "INVITE %s SIP/2.0\r\n"      "From: %s <sip:%s@%s>;tag=%u\r\n"      "Via: SIP/2.0/UDP %s:%u\r\n"      "To: %s\r\n"      "Contact: sip:%s@%s:%u\r\n"      "Call-ID: %u@%s\r\n"      "CSeq: %d INVITE\r\n"      "Content-Type: application/sdp\r\n"      "%s" /* Proxy-Authorization: line (if any) */      "%s" /* User-Agent: line */      "Content-length: %d\r\n\r\n"      "%s";    unsigned inviteCmdSize = strlen(cmdFmt)      + fURLSize      + 2*fUserNameSize + fOurAddressStrSize + 20 /* max int len */      + fOurAddressStrSize + 5 /* max port len */      + fURLSize      + fUserNameSize + fOurAddressStrSize + 5      + 20 + fOurAddressStrSize      + 20      + strlen(authenticatorStr)      + fUserAgentHeaderStrSize      + 20      + inviteSDPSize;    delete[] fInviteCmd; fInviteCmd = new char[inviteCmdSize];    sprintf(fInviteCmd, cmdFmt,	    fURL,	    fUserName, fUserName, fOurAddressStr, fFromTag,	    fOurAddressStr, fOurPortNum,	    fURL,	    fUserName, fOurAddressStr, fOurPortNum,	    fCallId, fOurAddressStr,	    ++fCSeq,	    authenticatorStr,	    fUserAgentHeaderStr,	    inviteSDPSize,	    fInviteSDPDescription);    fInviteCmdSize = strlen(fInviteCmd);    delete[] authenticatorStr;    // Before sending the "INVITE", arrange to handle any response packets,    // and set up timers:    fInviteClientState = Calling;    fEventLoopStopFlag = 0;    TaskScheduler& sched = envir().taskScheduler(); // abbrev.    sched.turnOnBackgroundReadHandling(fOurSocket->socketNum(),				       &inviteResponseHandler, this);    fTimerALen = 1*fT1; // initially    fTimerACount = 0; // initially    fTimerA = sched.scheduleDelayedTask(fTimerALen, timerAHandler, this);    fTimerB = sched.scheduleDelayedTask(64*fT1, timerBHandler, this);    fTimerD = NULL; // for now    if (!sendINVITE()) break;    // Enter the event loop, to handle response packets, and timeouts:    envir().taskScheduler().doEventLoop(&fEventLoopStopFlag);    // We're finished with this "INVITE".    // Turn off response handling and timers:    sched.turnOffBackgroundReadHandling(fOurSocket->socketNum());    sched.unscheduleDelayedTask(fTimerA);    sched.unscheduleDelayedTask(fTimerB);    sched.unscheduleDelayedTask(fTimerD);    // NOTE: We return the SDP description that we used in the "INVITE",    // not the one that we got from the server.    // ##### Later: match the codecs in the response (offer, answer) #####    if (fInviteSDPDescription != NULL) {      return strDup(fInviteSDPDescription);    }  } while (0);  fInviteStatusCode = 2;  return NULL;}void SIPClient::inviteResponseHandler(void* clientData, int /*mask*/) {  SIPClient* client = (SIPClient*)clientData;  unsigned responseCode = client->getResponseCode();  client->doInviteStateMachine(responseCode);}// Special 'response codes' that represent timers expiring:unsigned const timerAFires = 0xAAAAAAAA;unsigned const timerBFires = 0xBBBBBBBB;unsigned const timerDFires = 0xDDDDDDDD;void SIPClient::timerAHandler(void* clientData) {  SIPClient* client = (SIPClient*)clientData;  if (client->fVerbosityLevel >= 1) {    client->envir() << "RETRANSMISSION " << ++client->fTimerACount		    << ", after " << client->fTimerALen/1000000.0		    << " additional seconds\n";  }  client->doInviteStateMachine(timerAFires);}void SIPClient::timerBHandler(void* clientData) {  SIPClient* client = (SIPClient*)clientData;  if (client->fVerbosityLevel >= 1) {    client->envir() << "RETRANSMISSION TIMEOUT, after "		    << 64*client->fT1/1000000.0 << " seconds\n";    fflush(stderr);  }  client->doInviteStateMachine(timerBFires);}void SIPClient::timerDHandler(void* clientData) {  SIPClient* client = (SIPClient*)clientData;  if (client->fVerbosityLevel >= 1) {    client->envir() << "TIMER D EXPIRED\n";  }  client->doInviteStateMachine(timerDFires);}void SIPClient::doInviteStateMachine(unsigned responseCode) {  // Implement the state transition diagram (RFC 3261, Figure 5)  TaskScheduler& sched = envir().taskScheduler(); // abbrev.  switch (fInviteClientState) {    case Calling: {      if (responseCode == timerAFires) {	// Restart timer A (with double the timeout interval):	fTimerALen *= 2;	fTimerA	  = sched.scheduleDelayedTask(fTimerALen, timerAHandler, this);	fInviteClientState = Calling;	if (!sendINVITE()) doInviteStateTerminated(0);      } else {	// Turn off timers A & B before moving to a new state: 	sched.unscheduleDelayedTask(fTimerA);	sched.unscheduleDelayedTask(fTimerB);		if (responseCode == timerBFires) {	  envir().setResultMsg("No response from server");	  doInviteStateTerminated(0);	} else if (responseCode >= 100 && responseCode <= 199) {	  fInviteClientState = Proceeding;	} else if (responseCode >= 200 && responseCode <= 299) {	  doInviteStateTerminated(responseCode);	} else if (responseCode >= 400 && responseCode <= 499) {	  doInviteStateTerminated(responseCode);	      // this isn't what the spec says, but it seems right...	} else if (responseCode >= 300 && responseCode <= 699) {	  fInviteClientState = Completed;	  fTimerD	    = sched.scheduleDelayedTask(32000000, timerDHandler, this);	  if (!sendACK()) doInviteStateTerminated(0);	}      }      break;    }    case Proceeding: {      if (responseCode >= 100 && responseCode <= 199) {	fInviteClientState = Proceeding;      } else if (responseCode >= 200 && responseCode <= 299) {	doInviteStateTerminated(responseCode);      } else if (responseCode >= 400 && responseCode <= 499) {	doInviteStateTerminated(responseCode);	    // this isn't what the spec says, but it seems right...      } else if (responseCode >= 300 && responseCode <= 699) {	fInviteClientState = Completed;	fTimerD = sched.scheduleDelayedTask(32000000, timerDHandler, this);	if (!sendACK()) doInviteStateTerminated(0);      }      break;    }    case Completed: {      if (responseCode == timerDFires) {	envir().setResultMsg("Transaction terminated");	doInviteStateTerminated(0);      } else if (responseCode >= 300 && responseCode <= 699) {	fInviteClientState = Completed;	if (!sendACK()) doInviteStateTerminated(0);      }      break;    }    case Terminated: {	doInviteStateTerminated(responseCode);	break;    }  }}void SIPClient::doInviteStateTerminated(unsigned responseCode) {  fInviteClientState = Terminated; // FWIW...  if (responseCode < 200 || responseCode > 299) {    // We failed, so return NULL;    delete[] fInviteSDPDescription; fInviteSDPDescription = NULL;  }  // Unblock the event loop:  fEventLoopStopFlag = ~0;}Boolean SIPClient::sendINVITE() {  if (!sendRequest(fInviteCmd, fInviteCmdSize)) {    envir().setResultErrMsg("INVITE send() failed: ");    return False;  }  return True;}unsigned SIPClient::getResponseCode() {  unsigned responseCode = 0;  do {    // Get the response from the server:    unsigned const readBufSize = 10000;    char readBuffer[readBufSize+1]; char* readBuf = readBuffer;    char* firstLine = NULL;    char* nextLineStart = NULL;    unsigned bytesRead = getResponse(readBuf, readBufSize);    if (bytesRead < 0) break;    if (fVerbosityLevel >= 1) {      envir() << "Received INVITE response: " << readBuf << "\n";    }    // Inspect the first line to get the response code:    firstLine = readBuf;    nextLineStart = getLine(firstLine);    if (!parseResponseCode(firstLine, responseCode)) break;    if (responseCode != 200) {      if (responseCode >= 400 && responseCode <= 499	  && fWorkingAuthenticator != NULL) {	// We have an authentication failure, so fill in

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -