/********** This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. (See .) This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA **********/ // Copyright (c) 1996-2020, Live Networks, Inc. All rights reserved // A SIP client test program that opens a SIP URL argument, // and extracts the data from each incoming RTP stream. #include "playCommon.hh" #include "SIPClient.hh" 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; } SIPClient* ourSIPClient = NULL; Medium* createClient(UsageEnvironment& env, char const* /*url*/, int verbosityLevel, char const* applicationName) { // First, trim any directory prefixes from "applicationName": char const* suffix = &applicationName[strlen(applicationName)]; while (suffix != applicationName) { if (*suffix == '/' || *suffix == '\\') { applicationName = ++suffix; break; } --suffix; } extern unsigned char desiredAudioRTPPayloadFormat; extern char* mimeSubtype; return ourSIPClient = SIPClient::createNew(env, desiredAudioRTPPayloadFormat, mimeSubtype, verbosityLevel, applicationName); } // The followign function is implemented, but is not used for "playSIP": void assignClient(Medium* /*client*/) { } void getOptions(RTSPClient::responseHandler* afterFunc) { ourSIPClient->envir().setResultMsg("NOT SUPPORTED IN CLIENT"); afterFunc(NULL, -1, strDup(ourSIPClient->envir().getResultMsg())); } void getSDPDescription(RTSPClient::responseHandler* afterFunc) { extern char* proxyServerName; if (proxyServerName != NULL) { // Tell the SIP client about the proxy: NetAddressList addresses(proxyServerName); if (addresses.numAddresses() == 0) { ourSIPClient->envir() << "Failed to find network address for \"" << proxyServerName << "\"\n"; } else { NetAddress address = *(addresses.firstAddress()); unsigned proxyServerAddress // later, allow for IPv6 ##### = *(unsigned*)(address.data()); extern unsigned short proxyServerPortNum; if (proxyServerPortNum == 0) proxyServerPortNum = 5060; // default ourSIPClient->setProxyServer(proxyServerAddress, proxyServerPortNum); } } extern unsigned short desiredPortNum; unsigned short clientStartPortNum = desiredPortNum; if (clientStartPortNum == 0) clientStartPortNum = 8000; // default ourSIPClient->setClientStartPortNum(clientStartPortNum); extern char const* streamURL; char const* username = ourAuthenticator == NULL ? NULL : ourAuthenticator->username(); char const* password = ourAuthenticator == NULL ? NULL : ourAuthenticator->password(); char* result; if (username != NULL && password != NULL) { result = ourSIPClient->inviteWithPassword(streamURL, username, password); } else { result = ourSIPClient->invite(streamURL); } int resultCode = result == NULL ? -1 : 0; afterFunc(NULL, resultCode, strDup(result)); } void setupSubsession(MediaSubsession* subsession, Boolean /*streamUsingTCP*/, Boolean /*forceMulticastOnUnspecified*/,RTSPClient::responseHandler* afterFunc) { subsession->setSessionId("mumble"); // anything that's non-NULL will work ////////// BEGIN hack code that should really be implemented in SIPClient ////////// // Parse the "Transport:" header parameters: // We do not send audio, but we need port for RTCP char* serverAddressStr; portNumBits serverPortNum; unsigned char rtpChannelId, rtcpChannelId; rtpChannelId = rtcpChannelId = 0xff; serverPortNum = 0; serverAddressStr = NULL; char* sdp = strDup(ourSIPClient->getInviteSdpReply()); char* lineStart; char* nextLineStart = sdp; while (1) { lineStart = nextLineStart; if (lineStart == NULL) { break; } nextLineStart = getLine(lineStart); char* toTagStr = strDupSize(lineStart); if (sscanf(lineStart, "m=audio %[^/\r\n]", toTagStr) == 1) { sscanf(toTagStr, "%hu", &serverPortNum); } else if (sscanf(lineStart, "c=IN IP4 %[^/\r\n]", toTagStr) == 1) { serverAddressStr = strDup(toTagStr); } delete[] toTagStr; } if(sdp != NULL) { delete[] sdp; } delete[] subsession->connectionEndpointName(); subsession->connectionEndpointName() = serverAddressStr; subsession->serverPortNum = serverPortNum; subsession->rtpChannelId = rtpChannelId; subsession->rtcpChannelId = rtcpChannelId; // Set the RTP and RTCP sockets' destination address and port from the information in the SETUP response (if present): netAddressBits destAddress = subsession->connectionEndpointAddress(); if (destAddress != 0) { subsession->setDestinations(destAddress); } ////////// END hack code that should really be implemented in SIPClient ////////// afterFunc(NULL, 0, NULL); } void startPlayingSession(MediaSession* /*session*/, double /*start*/, double /*end*/, float /*scale*/, RTSPClient::responseHandler* afterFunc) { if (ourSIPClient->sendACK()) { //##### This isn't quite right, because we should really be allowing //##### for the possibility of this ACK getting lost, by retransmitting //##### it *each time* we get a 2xx response from the server. afterFunc(NULL, 0, NULL); } else { afterFunc(NULL, -1, strDup(ourSIPClient->envir().getResultMsg())); } } void startPlayingSession(MediaSession* /*session*/, const char* /*start*/, const char* /*end*/, float /*scale*/, RTSPClient::responseHandler* afterFunc) { startPlayingSession(NULL,(double)0,(double)0,0,afterFunc); } void tearDownSession(MediaSession* /*session*/, RTSPClient::responseHandler* afterFunc) { if (ourSIPClient == NULL || ourSIPClient->sendBYE()) { afterFunc(NULL, 0, NULL); } else { afterFunc(NULL, -1, strDup(ourSIPClient->envir().getResultMsg())); } } void setUserAgentString(char const* userAgentString) { ourSIPClient->setUserAgentString(userAgentString); } Boolean allowProxyServers = True; Boolean controlConnectionUsesTCP = False; Boolean supportCodecSelection = True; char const* clientProtocolName = "SIP";