예제 #1
0
UsageEnvironment& operator<<(UsageEnvironment& s, const Groupsock& g) {
  UsageEnvironment& s1 = s << timestampString() << " Groupsock("
			   << g.socketNum() << ": "
			   << our_inet_ntoa(g.groupAddress())
			   << ", " << g.port() << ", ";
  if (g.isSSM()) {
    return s1 << "SSM source: "
	      <<  our_inet_ntoa(g.sourceFilterAddress()) << ")";
  } else {
    return s1 << (unsigned)(g.ttl()) << ")";
  }
}
예제 #2
0
int main(int argc, char** argv) {
  // Begin by setting up our usage environment:
  TaskScheduler* scheduler = BasicTaskScheduler::createNew();
  UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);


  // Create a 'groupsock' for the input multicast group,port:
  char const* sessionAddressStr = "224.2.127.254";
  struct in_addr sessionAddress;
  sessionAddress.s_addr = our_inet_addr(sessionAddressStr);

  const Port port(9875);
  const unsigned char ttl = 0; // we're only reading from this mcast group

  Groupsock inputGroupsock(*env, sessionAddress, port, ttl);

  // Start reading and printing incoming packets
  // (Because this is the only thing we do, we can just do this
  // synchronously, in a loop, so we don't need to set up an asynchronous
  // event handler like we do in most of the other test programs.)
  unsigned packetSize;
  struct sockaddr_in fromAddress;
  while (inputGroupsock.handleRead(packet, maxPacketSize,
				   packetSize, fromAddress)) {
    printf("\n[packet from %s (%d bytes)]\n",
	   our_inet_ntoa(fromAddress.sin_addr), packetSize);

    // Ignore the first 8 bytes (SAP header).
    if (packetSize < 8) {
      *env << "Ignoring short packet from "
	   << our_inet_ntoa(fromAddress.sin_addr) << "%s!\n";
      continue;
    }

    // convert "application/sdp\0" -> "application/sdp\0x20"
    // or all other nonprintable characters to blank, except new line
    unsigned idx = 8;
    while (idx < packetSize) {
      if (packet[idx] < 0x20 && packet[idx] != '\n') packet[idx] = 0x20;
      idx++;
    }

    packet[packetSize] = '\0'; // just in case
    printf((char*)(packet+8));
  }

  return 0; // only to prevent compiler warning
}
예제 #3
0
void RTSPServer::incomingConnectionHandler1() {
  struct sockaddr_in clientAddr;
  SOCKLEN_T clientAddrLen = sizeof clientAddr;
//  printf("incomingConnectionHandler1\n");	//jay
  int clientSocket = accept(fServerSocket, (struct sockaddr*)&clientAddr,
					&clientAddrLen);
  if (clientSocket < 0) {
	  int err = envir().getErrno();
	  if (err != EWOULDBLOCK) {
			  envir().setResultErrMsg("accept() failed: ");
		}
	  return;
	}
  makeSocketNonBlocking(clientSocket);
  increaseSendBufferTo(envir(), clientSocket, 50*1024);

#if defined(DEBUG) || defined(DEBUG_CONNECTIONS)
  envir() << "accept()ed connection from " << our_inet_ntoa(clientAddr.sin_addr) << '\n';
#endif

	// Create a new object for this RTSP session.
	// (Choose a random 32-bit integer for the session id (it will be encoded as a 8-digit hex number).  We don't bother checking for
	//  a collision; the probability of two concurrent sessions getting the same session id is very low.)
  unsigned sessionId = (unsigned)our_random();
	(void)createNewClientSession(sessionId, clientSocket, clientAddr);
}
예제 #4
0
Boolean Groupsock::handleRead(unsigned char* buffer, unsigned bufferMaxSize,
			      unsigned& bytesRead,
			      struct sockaddr_in& fromAddress) {
  // Read data from the socket, and relay it across any attached tunnels
  //##### later make this code more general - independent of tunnels

  bytesRead = 0;

  int maxBytesToRead = bufferMaxSize - TunnelEncapsulationTrailerMaxSize;
  int numBytes = readSocket(env(), socketNum(),
			    buffer, maxBytesToRead, fromAddress);
  if (numBytes < 0) {
    if (DebugLevel >= 0) { // this is a fatal error
      env().setResultMsg("Groupsock read failed: ",
			 env().getResultMsg());
    }
    return False;
  }

  // If we're a SSM group, make sure the source address matches:
  if (isSSM()
      && fromAddress.sin_addr.s_addr != sourceFilterAddress().s_addr) {
    return True;
  }

  // We'll handle this data.
  // Also write it (with the encapsulation trailer) to each member,
  // unless the packet was originally sent by us to begin with.
  bytesRead = numBytes;

  int numMembers = 0;
  if (!wasLoopedBackFromUs(env(), fromAddress)) {
    statsIncoming.countPacket(numBytes);
    statsGroupIncoming.countPacket(numBytes);
    numMembers =
      outputToAllMembersExcept(NULL, ttl(),
			       buffer, bytesRead,
			       fromAddress.sin_addr.s_addr);
    if (numMembers > 0) {
      statsRelayedIncoming.countPacket(numBytes);
      statsGroupRelayedIncoming.countPacket(numBytes);
    }
  }
  if (DebugLevel >= 3) {
    env() << *this << ": read " << bytesRead << " bytes from ";
    env() << our_inet_ntoa(fromAddress.sin_addr);
    if (numMembers > 0) {
      env() << "; relayed to " << numMembers << " members";
    }
    env() << "\n";
  }

  return True;
}
예제 #5
0
void RTSPServer::RTSPClientSession
::livenessTimeoutTask(RTSPClientSession* clientSession) {
	// If this gets called, the client session is assumed to have timed out,
	// so delete it:
	if (!clientSession->isMulticast()) {
#ifdef DEBUG
		fprintf(stderr, "RTSP client session from %s has timed out (due to inactivity)\n", our_inet_ntoa(clientSession->fClientAddr.sin_addr));
#endif
		delete clientSession;
	}
}
char const*
PassiveServerMediaSubsession::sdpLines() {
  if (fSDPLines == NULL ) {
    // Construct a set of SDP lines that describe this subsession:
    // Use the components from "rtpSink":
    Groupsock const& gs = fRTPSink.groupsockBeingUsed();
    struct in_addr const& ipAddress = gs.groupAddress();
    unsigned short portNum = ntohs(gs.port().num());
    unsigned char ttl = gs.ttl();
    unsigned char rtpPayloadType = fRTPSink.rtpPayloadType();
    char const* mediaType = fRTPSink.sdpMediaType();
    char* rtpmapLine = fRTPSink.rtpmapLine();
    char const* rangeLine = rangeSDPLine();
    char const* auxSDPLine = fRTPSink.auxSDPLine();
    if (auxSDPLine == NULL) auxSDPLine = "";

    char* const ipAddressStr = strDup(our_inet_ntoa(ipAddress));

    char const* const sdpFmt =
      "m=%s %d RTP/AVP %d\r\n"
      "c=IN IP4 %s/%d\r\n"
      "%s"
      "%s"
      "%s"
      "a=control:%s\r\n";
    unsigned sdpFmtSize = strlen(sdpFmt)
      + strlen(mediaType) + 5 /* max short len */ + 3 /* max char len */
      + strlen(ipAddressStr) + 3 /* max char len */
      + strlen(rtpmapLine)
      + strlen(rangeLine)
      + strlen(auxSDPLine)
      + strlen(trackId());
    char* sdpLines = new char[sdpFmtSize];
    sprintf(sdpLines, sdpFmt,
	    mediaType, // m= <media>
	    portNum, // m= <port>
	    rtpPayloadType, // m= <fmt list>
	    ipAddressStr, // c= <connection address>
	    ttl, // c= TTL
	    rtpmapLine, // a=rtpmap:... (if present)
	    rangeLine, // a=range:... (if present)
	    auxSDPLine, // optional extra SDP line
	    trackId()); // a=control:<track-id>
    delete[] ipAddressStr; delete[] (char*)rangeLine; delete[] rtpmapLine;

    fSDPLines = strDup(sdpLines);
    delete[] sdpLines;
  }

  return fSDPLines;
}
void OnDemandServerMediaSubsession
::setSDPLinesFromRTPSink(RTPSink *rtpSink, FramedSource *inputSource, unsigned estBitrate)
{
    if (rtpSink == NULL) return;

    char const *mediaType = rtpSink->sdpMediaType();
    unsigned char rtpPayloadType = rtpSink->rtpPayloadType();
    struct in_addr serverAddrForSDP;
    serverAddrForSDP.s_addr = fServerAddressForSDP;
    char *const ipAddressStr = strDup(our_inet_ntoa(serverAddrForSDP));
    char *rtpmapLine = rtpSink->rtpmapLine();
    char const *rangeLine = rangeSDPLine();
    char const *auxSDPLine = getAuxSDPLine(rtpSink, inputSource);
    if (auxSDPLine == NULL) auxSDPLine = "";

    char const *const sdpFmt =
        "m=%s %u RTP/AVP %d\r\n"
        "c=IN IP4 %s\r\n"
        "b=AS:%u\r\n"
        "%s"
        "%s"
        "%s"
        "a=control:%s\r\n";
    unsigned sdpFmtSize = strlen(sdpFmt)
                          + strlen(mediaType) + 5 /* max short len */ + 3 /* max char len */
                          + strlen(ipAddressStr)
                          + 20 /* max int len */
                          + strlen(rtpmapLine)
                          + strlen(rangeLine)
                          + strlen(auxSDPLine)
                          + strlen(trackId());
    char *sdpLines = new char[sdpFmtSize];
    sprintf(sdpLines, sdpFmt,
            mediaType, // m= <media>
            fPortNumForSDP, // m= <port>
            rtpPayloadType, // m= <fmt list>
            ipAddressStr, // c= address
            estBitrate, // b=AS:<bandwidth>
            rtpmapLine, // a=rtpmap:... (if present)
            rangeLine, // a=range:... (if present)
            auxSDPLine, // optional extra SDP line
            trackId()); // a=control:<track-id>
    delete[] (char *)rangeLine;
    delete[] rtpmapLine;
    delete[] ipAddressStr;

    fSDPLines = strDup(sdpLines);
    delete[] sdpLines;
}
예제 #8
0
char* RTSPServer::rtspURLPrefix(int clientSocket) const {
  struct sockaddr_in ourAddress;
  if (clientSocket < 0) {
		// Use our default IP address in the URL:
	  ourAddress.sin_addr.s_addr = ReceivingInterfaceAddr != 0
			? ReceivingInterfaceAddr
			: ourIPAddress(envir()); // hack
	} else {
	  SOCKLEN_T namelen = sizeof ourAddress;
	  getsockname(clientSocket, (struct sockaddr*)&ourAddress, &namelen);
	}

  char urlBuffer[100]; // more than big enough for "rtsp://<ip-address>:<port>/"

  portNumBits portNumHostOrder = ntohs(fServerPort.num());
  if (portNumHostOrder == 554 /* the default port number */) {
	  sprintf(urlBuffer, "rtsp://%s/", our_inet_ntoa(ourAddress.sin_addr));
	} else {
	  sprintf(urlBuffer, "rtsp://%s:%hu/",
		  our_inet_ntoa(ourAddress.sin_addr), portNumHostOrder);
	}

  return strDup(urlBuffer);
}
예제 #9
0
void RTSPServer::RTSPClientSession
::livenessTimeoutTask(RTSPClientSession* clientSession) {
  // If this gets called, the client session is assumed to have timed out,
  // so delete it:

  // However, we don't timeout multicast sessions, because to do so would require
  // closing all client sessions that have requested the stream - not just this one.
  // Also, the multicast stream itself would usually not be halted, in any case.
  if (clientSession->isMulticast()) return;

#ifdef DEBUG
  fprintf(stderr, "RTSP client session from %s has timed out (due to inactivity)\n", our_inet_ntoa(clientSession->fClientAddr.sin_addr));
#endif
  delete clientSession;
}
예제 #10
0
RTSPServer::RTSPClientSession* RTSPServer::attachClientSession(int clientSocket, IRTSPClientSessionObserver* observer) {
  if (clientSocket < 0) {
    return NULL;
  }

  struct sockaddr_in clientAddr;
  SOCKLEN_T clientAddrLen = sizeof clientAddr;
  if (getpeername(clientSocket, (struct sockaddr*)&clientAddr, &clientAddrLen) < 0) 
    return NULL;

#if defined(DEBUG) || defined(DEBUG_CONNECTIONS)
  fprintf(stderr, "attached client session from %s\n", our_inet_ntoa(clientAddr.sin_addr));
#endif

  // Create a new object for this RTSP session:
  RTSPClientSession* clientSession = new RTSPClientSession(*this, ++fSessionIdCounter,
			clientSocket, clientAddr, true, observer);

  return clientSession;
}
예제 #11
0
void RTSPServer::incomingConnectionHandler1() {
  struct sockaddr_in clientAddr;
  SOCKLEN_T clientAddrLen = sizeof clientAddr;
  int clientSocket = accept(fServerSocket, (struct sockaddr*)&clientAddr,
			    &clientAddrLen);
  if (clientSocket < 0) {
    int err = envir().getErrno();
    if (err != EWOULDBLOCK) {
        envir().setResultErrMsg("accept() failed: ");
    }
    return;
  }
  makeSocketNonBlocking(clientSocket);
  increaseSendBufferTo(envir(), clientSocket, 50*1024);

#if defined(DEBUG) || defined(DEBUG_CONNECTIONS)
  fprintf(stderr, "accept()ed connection from %s\n", our_inet_ntoa(clientAddr.sin_addr));
#endif

  // Create a new object for this RTSP session:
  new RTSPClientSession(*this, ++fSessionIdCounter,
			clientSocket, clientAddr);
}
char *ServerMediaSession::generateSDPDescription()
{
    struct in_addr ipAddress;
    ipAddress.s_addr = ourIPAddress(envir());
    char *const ipAddressStr = strDup(our_inet_ntoa(ipAddress));
    unsigned ipAddressStrSize = strlen(ipAddressStr);

    // For a SSM sessions, we need a "a=source-filter: incl ..." line also:
    char *sourceFilterLine;
    if (fIsSSM)
    {
        char const *const sourceFilterFmt =
            "a=source-filter: incl IN IP4 * %s\r\n"
            "a=rtcp-unicast: reflection\r\n";
        unsigned const sourceFilterFmtSize = strlen(sourceFilterFmt) + ipAddressStrSize + 1;

        sourceFilterLine = new char[sourceFilterFmtSize];
        sprintf(sourceFilterLine, sourceFilterFmt, ipAddressStr);
    }
    else
    {
        sourceFilterLine = strDup("");
    }

    char *rangeLine = NULL; // for now
    char *sdp = NULL; // for now

    do
    {
        // Count the lengths of each subsession's media-level SDP lines.
        // (We do this first, because the call to "subsession->sdpLines()"
        // causes correct subsession 'duration()'s to be calculated later.)
        unsigned sdpLength = 0;
        ServerMediaSubsession *subsession;
        for (subsession = fSubsessionsHead; subsession != NULL;
                subsession = subsession->fNext)
        {
            char const *sdpLines = subsession->sdpLines();
            if (sdpLines == NULL) break; // the media's not available
            sdpLength += strlen(sdpLines);
        }
        if (subsession != NULL) break; // an error occurred

        // Unless subsessions have differing durations, we also have a "a=range:" line:
        float dur = duration();
        if (dur == 0.0)
        {
            rangeLine = strDup("a=range:npt=0-\r\n");
        }
        else if (dur > 0.0)
        {
            char buf[100];
            sprintf(buf, "a=range:npt=0-%.3f\r\n", dur);
            rangeLine = strDup(buf);
        }
        else     // subsessions have differing durations, so "a=range:" lines go there
        {
            rangeLine = strDup("");
        }

        char const *const sdpPrefixFmt =
            "v=0\r\n"
            "o=- %ld%06ld %d IN IP4 %s\r\n"
            "s=%s\r\n"
            "i=%s\r\n"
            "t=0 0\r\n"
            "a=tool:%s%s\r\n"
            "a=type:broadcast\r\n"
            "a=control:*\r\n"
            "%s"
            "%s"
            "a=x-qt-text-nam:%s\r\n"
            "a=x-qt-text-inf:%s\r\n"
            "%s";
        sdpLength += strlen(sdpPrefixFmt)
                     + 20 + 6 + 20 + ipAddressStrSize
                     + strlen(fDescriptionSDPString)
                     + strlen(fInfoSDPString)
                     + strlen(libNameStr) + strlen(libVersionStr)
                     + strlen(sourceFilterLine)
                     + strlen(rangeLine)
                     + strlen(fDescriptionSDPString)
                     + strlen(fInfoSDPString)
                     + strlen(fMiscSDPLines);
        sdp = new char[sdpLength];
        if (sdp == NULL) break;

        // Generate the SDP prefix (session-level lines):
        sprintf(sdp, sdpPrefixFmt,
                fCreationTime.tv_sec, fCreationTime.tv_usec, // o= <session id>
                1, // o= <version> // (needs to change if params are modified)
                ipAddressStr, // o= <address>
                fDescriptionSDPString, // s= <description>
                fInfoSDPString, // i= <info>
                libNameStr, libVersionStr, // a=tool:
                sourceFilterLine, // a=source-filter: incl (if a SSM session)
                rangeLine, // a=range: line
                fDescriptionSDPString, // a=x-qt-text-nam: line
                fInfoSDPString, // a=x-qt-text-inf: line
                fMiscSDPLines); // miscellaneous session SDP lines (if any)

        // Then, add the (media-level) lines for each subsession:
        char *mediaSDP = sdp;
        for (subsession = fSubsessionsHead; subsession != NULL;
                subsession = subsession->fNext)
        {
            mediaSDP += strlen(mediaSDP);
            sprintf(mediaSDP, "%s", subsession->sdpLines());
        }
    }
    while (0);

    delete[] rangeLine;
    delete[] sourceFilterLine;
    delete[] ipAddressStr;
    return sdp;
}
예제 #13
0
void RTSPServer::RTSPClientSession
::handleCmd_SETUP(char const* cseq,
		  char const* urlPreSuffix, char const* urlSuffix,
		  char const* fullRequestStr) {
	// "urlPreSuffix" should be the session (stream) name, and
	// "urlSuffix" should be the subsession (track) name.
  char const* streamName = urlPreSuffix;
  char const* trackId = urlSuffix;

	// Check whether we have existing session state, and, if so, whether it's
	// for the session that's named in "streamName".	(Note that we don't
	// support more than one concurrent session on the same client connection.) #####
  if (fOurServerMediaSession != NULL
			&& strcmp(streamName, fOurServerMediaSession->streamName()) != 0) {
	  fOurServerMediaSession = NULL;
	}
  if (fOurServerMediaSession == NULL) {
		// Set up this session's state.

		// Look up the "ServerMediaSession" object for the specified stream:
	  if (streamName[0] != '\0' ||
	fOurServer.lookupServerMediaSession("") != NULL) { // normal case
		} else { // weird case: there was no track id in the URL
		  streamName = urlSuffix;
		  trackId = NULL;
		}
	  fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName);
	  if (fOurServerMediaSession == NULL) {
		  handleCmd_notFound(cseq);
		  return;
		}

	  fOurServerMediaSession->incrementReferenceCount();

		// Set up our array of states for this session's subsessions (tracks):
	  reclaimStreamStates();
	  ServerMediaSubsessionIterator iter(*fOurServerMediaSession);
	  for (fNumStreamStates = 0; iter.next() != NULL; ++fNumStreamStates) {}
	  fStreamStates = new struct streamState[fNumStreamStates];
	  iter.reset();
	  ServerMediaSubsession* subsession;
	  for (unsigned i = 0; i < fNumStreamStates; ++i) {
		  subsession = iter.next();
		  fStreamStates[i].subsession = subsession;
		  fStreamStates[i].streamToken = NULL; // for now; reset by SETUP later
		}
	}

	// Look up information for the specified subsession (track):
  ServerMediaSubsession* subsession = NULL;
  unsigned streamNum;
  if (trackId != NULL && trackId[0] != '\0') { // normal case
	  for (streamNum = 0; streamNum < fNumStreamStates; ++streamNum) {
		  subsession = fStreamStates[streamNum].subsession;
		  if (subsession != NULL && strcmp(trackId, subsession->trackId()) == 0) break;
		}
	  if (streamNum >= fNumStreamStates) {
			// The specified track id doesn't exist, so this request fails:
		  handleCmd_notFound(cseq);
		  return;
		}
	} else {
		// Weird case: there was no track id in the URL.
		// This works only if we have only one subsession:
	  if (fNumStreamStates != 1) {
		  handleCmd_bad(cseq);
		  return;
		}
	  streamNum = 0;
	  subsession = fStreamStates[streamNum].subsession;
	}
	// ASSERT: subsession != NULL

	// Look for a "Transport:" header in the request string,
	// to extract client parameters:
  StreamingMode streamingMode;
  char* streamingModeString = NULL; // set when RAW_UDP streaming is specified
  char* clientsDestinationAddressStr;
  u_int8_t clientsDestinationTTL;
  portNumBits clientRTPPortNum, clientRTCPPortNum;
  unsigned char rtpChannelId, rtcpChannelId;
  parseTransportHeader(fullRequestStr, streamingMode, streamingModeString, fIsMulticast,
					 clientsDestinationAddressStr, clientsDestinationTTL,
					 clientRTPPortNum, clientRTCPPortNum,
					 rtpChannelId, rtcpChannelId);
  if (streamingMode == RTP_TCP && rtpChannelId == 0xFF) {
		// TCP streaming was requested, but with no "interleaving=" fields.
		// (QuickTime Player sometimes does this.)  Set the RTP and RTCP channel ids to
		// proper values:
	  rtpChannelId = fTCPStreamIdCount; rtcpChannelId = fTCPStreamIdCount+1;
	}
  fTCPStreamIdCount += 2;

  Port clientRTPPort(clientRTPPortNum);
  Port clientRTCPPort(clientRTCPPortNum);

	// Next, check whether a "Range:" header is present in the request.
	// This isn't legal, but some clients do this to combine "SETUP" and "PLAY":
  double rangeStart = 0.0, rangeEnd = 0.0;
  fStreamAfterSETUP = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd) ||
										  parsePlayNowHeader(fullRequestStr);

	// Then, get server parameters from the 'subsession':
  int tcpSocketNum = streamingMode == RTP_TCP ? fClientSocket : -1;
  netAddressBits destinationAddress = 0;
  u_int8_t destinationTTL = 5;
#ifdef RTSP_ALLOW_CLIENT_DESTINATION_SETTING
  if (clientsDestinationAddressStr != NULL) {
		// Use the client-provided "destination" address.
		// Note: This potentially allows the server to be used in denial-of-service
		// attacks, so don't enable this code unless you're sure that clients are
		// trusted.
	  destinationAddress = our_inet_addr(clientsDestinationAddressStr);
	}
	// Also use the client-provided TTL.
  destinationTTL = clientsDestinationTTL;
#endif
  delete[] clientsDestinationAddressStr;
  Port serverRTPPort(0);
  Port serverRTCPPort(0);
  subsession->getStreamParameters(fOurSessionId, fClientAddr.sin_addr.s_addr,
				  clientRTPPort, clientRTCPPort,
				  tcpSocketNum, rtpChannelId, rtcpChannelId,
				  destinationAddress, destinationTTL, fIsMulticast,
				  serverRTPPort, serverRTCPPort,
				  fStreamStates[streamNum].streamToken);
  struct in_addr destinationAddr; destinationAddr.s_addr = destinationAddress;
  char* destAddrStr = strDup(our_inet_ntoa(destinationAddr));
  struct sockaddr_in sourceAddr; SOCKLEN_T namelen = sizeof sourceAddr;
  getsockname(fClientSocket, (struct sockaddr*)&sourceAddr, &namelen);
  char* sourceAddrStr = strDup(our_inet_ntoa(sourceAddr.sin_addr));
  if (fIsMulticast) {
	  switch (streamingMode) {
	  case RTP_UDP:
			  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
								 "RTSP/1.0 200 OK\r\n"
								 "CSeq: %s\r\n"
								 "%s"
								 "Transport: RTP/AVP;multicast;destination=%s;source=%s;port=%d-%d;ttl=%d\r\n"
								 "Session: %08X\r\n\r\n",
								 cseq,
								 dateHeader(),
								 destAddrStr, sourceAddrStr, ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()), destinationTTL,
								 fOurSessionId);
			  break;
	  case RTP_TCP:
				// multicast streams can't be sent via TCP
			  handleCmd_unsupportedTransport(cseq);
			  break;
	  case RAW_UDP:
			  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
								 "RTSP/1.0 200 OK\r\n"
								 "CSeq: %s\r\n"
								 "%s"
								 "Transport: %s;multicast;destination=%s;source=%s;port=%d;ttl=%d\r\n"
								 "Session: %08X\r\n\r\n",
								 cseq,
								 dateHeader(),
								 streamingModeString, destAddrStr, sourceAddrStr, ntohs(serverRTPPort.num()), destinationTTL,
								 fOurSessionId);
			  break;
		}
	} else {
	  switch (streamingMode) {
	  case RTP_UDP: {
		  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
				 "RTSP/1.0 200 OK\r\n"
				 "CSeq: %s\r\n"
				 "%s"
				 "Transport: RTP/AVP;unicast;destination=%s;source=%s;client_port=%d-%d;server_port=%d-%d\r\n"
				 "Session: %08X\r\n\r\n",
				 cseq,
				 dateHeader(),
				 destAddrStr, sourceAddrStr, ntohs(clientRTPPort.num()), ntohs(clientRTCPPort.num()), ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()),
				 fOurSessionId);
		  break;
		}
	  case RTP_TCP: {
		  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
				 "RTSP/1.0 200 OK\r\n"
				 "CSeq: %s\r\n"
				 "%s"
				 "Transport: RTP/AVP/TCP;unicast;destination=%s;source=%s;interleaved=%d-%d\r\n"
				 "Session: %08X\r\n\r\n",
				 cseq,
				 dateHeader(),
				 destAddrStr, sourceAddrStr, rtpChannelId, rtcpChannelId,
				 fOurSessionId);
		  break;
		}
	  case RAW_UDP: {
		  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
				 "RTSP/1.0 200 OK\r\n"
				 "CSeq: %s\r\n"
				 "%s"
				 "Transport: %s;unicast;destination=%s;source=%s;client_port=%d;server_port=%d\r\n"
				 "Session: %08X\r\n\r\n",
				 cseq,
				 dateHeader(),
				 streamingModeString, destAddrStr, sourceAddrStr, ntohs(clientRTPPort.num()), ntohs(serverRTPPort.num()),
				 fOurSessionId);
		  break;
		}
		}
	}
  delete[] destAddrStr; delete[] sourceAddrStr; delete[] streamingModeString;
}
//处理setup命令时使用 void RTSPServer::RTSPClientSession::handleCmd_SETUP()
void OnDemandServerMediaSubsession::getStreamParameters(
              unsigned clientSessionId,
		      netAddressBits clientAddress,
		      Port const& clientRTPPort,
		      Port const& clientRTCPPort,
		      int tcpSocketNum,
		      unsigned char rtpChannelId,
		      unsigned char rtcpChannelId,
		      netAddressBits& destinationAddress,
		      u_int8_t& /*destinationTTL*/,
		      Boolean& isMulticast,
		      Port& serverRTPPort,
		      Port& serverRTCPPort,
		      void*& streamToken) {
  if (destinationAddress == 0) destinationAddress = clientAddress;
  struct in_addr destinationAddr; destinationAddr.s_addr = destinationAddress;
  isMulticast = False;

  if (fLastStreamToken != NULL && fReuseFirstSource) {
    // Special case: Rather than creating a new 'StreamState',
    // we reuse the one that we've already created:
    DEBUG_LOG(INF, "Reuse first source");
    serverRTPPort = ((StreamState*)fLastStreamToken)->serverRTPPort();
    serverRTCPPort = ((StreamState*)fLastStreamToken)->serverRTCPPort();
    ++((StreamState*)fLastStreamToken)->referenceCount();
    streamToken = fLastStreamToken;
  } else {
    // Normal case: Create a new media source:
    DEBUG_LOG(INF, "Create a new media source");
    unsigned streamBitrate;
    FramedSource* mediaSource
      = createNewStreamSource(clientSessionId, streamBitrate);

    // Create 'groupsock' and 'sink' objects for the destination,
    // using previously unused server port numbers:
    RTPSink* rtpSink;
    BasicUDPSink* udpSink;
    Groupsock* rtpGroupsock;
    Groupsock* rtcpGroupsock;
    portNumBits serverPortNum;
    if (clientRTCPPort.num() == 0) {
      // We're streaming raw UDP (not RTP). Create a single groupsock:
      NoReuse dummy; // ensures that we skip over ports that are already in use
      for (serverPortNum = fInitialPortNum; ; ++serverPortNum) {
	struct in_addr dummyAddr; dummyAddr.s_addr = 0;

	serverRTPPort = serverPortNum;
	rtpGroupsock = new Groupsock(envir(), dummyAddr, serverRTPPort, 255);
	if (rtpGroupsock->socketNum() >= 0) break; // success
      }

      rtcpGroupsock = NULL;
      rtpSink = NULL;
      udpSink = BasicUDPSink::createNew(envir(), rtpGroupsock);
    } else {
      // Normal case: We're streaming RTP (over UDP or TCP).  Create a pair of
      // groupsocks (RTP and RTCP), with adjacent port numbers (RTP port number even):
      NoReuse dummy; // ensures that we skip over ports that are already in use
      for (portNumBits serverPortNum = fInitialPortNum; ; serverPortNum += 2) {
	struct in_addr dummyAddr; dummyAddr.s_addr = 0;

	serverRTPPort = serverPortNum;
	rtpGroupsock = new Groupsock(envir(), dummyAddr, serverRTPPort, 255);
	if (rtpGroupsock->socketNum() < 0) {
	  delete rtpGroupsock;
	  continue; // try again
	}

	serverRTCPPort = serverPortNum+1;
	rtcpGroupsock = new Groupsock(envir(), dummyAddr, serverRTCPPort, 255);
	if (rtcpGroupsock->socketNum() < 0) {
	  delete rtpGroupsock;
	  delete rtcpGroupsock;
	  continue; // try again
	}

	break; // success
      }

      unsigned char rtpPayloadType = 96 + trackNumber()-1; // if dynamic
      rtpSink = createNewRTPSink(rtpGroupsock, rtpPayloadType, mediaSource);
      udpSink = NULL;
    }

    // Turn off the destinations for each groupsock.  They'll get set later
    // (unless TCP is used instead):
    if (rtpGroupsock != NULL) rtpGroupsock->removeAllDestinations();
    if (rtcpGroupsock != NULL) rtcpGroupsock->removeAllDestinations();

    if (rtpGroupsock != NULL) {
      // Try to use a big send buffer for RTP -  at least 0.1 second of
      // specified bandwidth and at least 50 KB
      unsigned rtpBufSize = streamBitrate * 25 / 2; // 1 kbps * 0.1 s = 12.5 bytes
      if (rtpBufSize < 50 * 1024) rtpBufSize = 50 * 1024;
      increaseSendBufferTo(envir(), rtpGroupsock->socketNum(), rtpBufSize);
    }

    // Set up the state of the stream.  The stream will get started later:
    DEBUG_LOG(INF, "Set up the state of the stream. The stream will get started later");
    streamToken = fLastStreamToken
      = new StreamState(*this, serverRTPPort, serverRTCPPort, rtpSink, udpSink,
			streamBitrate, mediaSource,
			rtpGroupsock, rtcpGroupsock);
  }

  // Record these destinations as being for this client session id:
  Destinations* destinations;
  if (tcpSocketNum < 0) 
  { // UDP
    DEBUG_LOG(INF, "Create UDP Destinations: destinationAddr=%s, clientRTPPort=%d, clientRTCPPort=%d",
      our_inet_ntoa(destinationAddr), ntohs(clientRTPPort.num()), ntohs(clientRTCPPort.num()));
    destinations = new Destinations(destinationAddr, clientRTPPort, clientRTCPPort);
  } 
  else 
  { // TCP
    DEBUG_LOG(INF, "Create TCP Destinations: tcpSocketNum=%d, rtpChannelId=%d, rtcpChannelId=%d",
      tcpSocketNum, rtpChannelId, rtcpChannelId);
    destinations = new Destinations(tcpSocketNum, rtpChannelId, rtcpChannelId);
  }
  fDestinationsHashTable->Add((char const*)clientSessionId, destinations);
}
예제 #15
0
Boolean DarwinInjector
::setDestination(char const* remoteRTSPServerNameOrAddress,
		 char const* remoteFileName,
		 char const* sessionName,
		 char const* sessionInfo,
		 portNumBits remoteRTSPServerPortNumber,
		 char const* remoteUserName,
		 char const* remotePassword,
		 char const* sessionAuthor,
		 char const* sessionCopyright,
		 int timeout) {
  char* sdp = NULL;
  char* url = NULL;
  Boolean success = False; // until we learn otherwise

  do {
    // Begin by creating our RTSP client object:
    fRTSPClient = RTSPClient::createNew(envir(), fVerbosityLevel, fApplicationName);
    if (fRTSPClient == NULL) break;

    // Get the remote RTSP server's IP address:
    struct in_addr addr;
    {
      NetAddressList addresses(remoteRTSPServerNameOrAddress);
      if (addresses.numAddresses() == 0) break;
      NetAddress const* address = addresses.firstAddress();
      addr.s_addr = *(unsigned*)(address->data());
    }
    char const* remoteRTSPServerAddressStr = our_inet_ntoa(addr);

    // Construct a SDP description for the session that we'll be streaming:
    char const* const sdpFmt =
      "v=0\r\n"
      "o=- %u %u IN IP4 127.0.0.1\r\n"
      "s=%s\r\n"
      "i=%s\r\n"
      "c=IN IP4 %s\r\n"
      "t=0 0\r\n"
      "a=x-qt-text-nam:%s\r\n"
      "a=x-qt-text-inf:%s\r\n"
      "a=x-qt-text-cmt:source application:%s\r\n"
      "a=x-qt-text-aut:%s\r\n"
      "a=x-qt-text-cpy:%s\r\n";
      // plus, %s for each substream SDP
    unsigned sdpLen = strlen(sdpFmt)
      + 20 /* max int len */ + 20 /* max int len */
      + strlen(sessionName)
      + strlen(sessionInfo)
      + strlen(remoteRTSPServerAddressStr)
      + strlen(sessionName)
      + strlen(sessionInfo)
      + strlen(fApplicationName)
      + strlen(sessionAuthor)
      + strlen(sessionCopyright)
      + fSubstreamSDPSizes;
    unsigned const sdpSessionId = our_random();
    unsigned const sdpVersion = sdpSessionId;
    sdp = new char[sdpLen];
    sprintf(sdp, sdpFmt,
	    sdpSessionId, sdpVersion, // o= line
	    sessionName, // s= line
	    sessionInfo, // i= line
	    remoteRTSPServerAddressStr, // c= line
	    sessionName, // a=x-qt-text-nam: line
	    sessionInfo, // a=x-qt-text-inf: line
	    fApplicationName, // a=x-qt-text-cmt: line
	    sessionAuthor, // a=x-qt-text-aut: line
	    sessionCopyright // a=x-qt-text-cpy: line
	    );
    char* p = &sdp[strlen(sdp)];
    SubstreamDescriptor* ss;
    for (ss = fHeadSubstream; ss != NULL; ss = ss->next()) {
      sprintf(p, "%s", ss->sdpLines());
      p += strlen(p);
    }

    // Construct a RTSP URL for the remote stream:
    char const* const urlFmt = "rtsp://%s:%u/%s";
    unsigned urlLen
      = strlen(urlFmt) + strlen(remoteRTSPServerNameOrAddress) + 5 /* max short len */ + strlen(remoteFileName);
    url = new char[urlLen];
    sprintf(url, urlFmt, remoteRTSPServerNameOrAddress, remoteRTSPServerPortNumber, remoteFileName);

    // Do a RTSP "ANNOUNCE" with this SDP description:
    Boolean announceSuccess;
    if (remoteUserName[0] != '\0' || remotePassword[0] != '\0') {
      announceSuccess
	= fRTSPClient->announceWithPassword(url, sdp, remoteUserName, remotePassword, timeout);
    } else {
      announceSuccess = fRTSPClient->announceSDPDescription(url, sdp, NULL, timeout);
    }
    if (!announceSuccess) break;

    // Tell the remote server to start receiving the stream from us.
    // (To do this, we first create a "MediaSession" object from the SDP description.)
    fSession = MediaSession::createNew(envir(), sdp);
    if (fSession == NULL) break;

    ss = fHeadSubstream;
    MediaSubsessionIterator iter(*fSession);
    MediaSubsession* subsession;
    ss = fHeadSubstream;
    unsigned streamChannelId = 0;
    while ((subsession = iter.next()) != NULL) {
      if (!subsession->initiate()) break;

      if (!fRTSPClient->setupMediaSubsession(*subsession,
					     True /*streamOutgoing*/,
					     True /*streamUsingTCP*/)) {
	break;
      }

      // Tell this subsession's RTPSink and RTCPInstance to use
      // the RTSP TCP connection:
      ss->rtpSink()->setStreamSocket(fRTSPClient->socketNum(), streamChannelId++);
      if (ss->rtcpInstance() != NULL) {
	ss->rtcpInstance()->setStreamSocket(fRTSPClient->socketNum(),
					    streamChannelId++);
      }
      ss = ss->next();
    }
    if (subsession != NULL) break; // an error occurred above

    // Tell the RTSP server to start:
    if (!fRTSPClient->playMediaSession(*fSession)) break;

    // Finally, make sure that the output TCP buffer is a reasonable size:
    increaseSendBufferTo(envir(), fRTSPClient->socketNum(), 100*1024);

    success = True;
  } while (0);

  delete[] sdp;
  delete[] url;
  return success;
}
예제 #16
0
파일: RTCP.cpp 프로젝트: 499276369/ohbee
void RTCPInstance::incomingReportHandler1() {
  unsigned char* pkt = fInBuf;
  unsigned packetSize;
  struct sockaddr_in fromAddress;
  int typeOfPacket = PACKET_UNKNOWN_TYPE;

  do {
    int tcpReadStreamSocketNum = fRTCPInterface.nextTCPReadStreamSocketNum();
    unsigned char tcpReadStreamChannelId = fRTCPInterface.nextTCPReadStreamChannelId();
    if (!fRTCPInterface.handleRead(pkt, maxPacketSize,
				   packetSize, fromAddress)) {
      break;
    }

    // Ignore the packet if it was looped-back from ourself:
    if (RTCPgs()->wasLoopedBackFromUs(envir(), fromAddress)) {
      // However, we still want to handle incoming RTCP packets from
      // *other processes* on the same machine.  To distinguish this
      // case from a true loop-back, check whether we've just sent a
      // packet of the same size.  (This check isn't perfect, but it seems
      // to be the best we can do.)
      if (fHaveJustSentPacket && fLastPacketSentSize == packetSize) {
	// This is a true loop-back:
	fHaveJustSentPacket = False;
	break; // ignore this packet
      }
    }

    if (fIsSSMSource) {
      // This packet was received via unicast.  'Reflect' it by resending
      // it to the multicast group.
      // NOTE: Denial-of-service attacks are possible here.
      // Users of this software may wish to add their own,
      // application-specific mechanism for 'authenticating' the
      // validity of this packet before reflecting it.
      fRTCPInterface.sendPacket(pkt, packetSize);
      fHaveJustSentPacket = True;
      fLastPacketSentSize = packetSize;
    }

#ifdef DEBUG
    fprintf(stderr, "[%p]saw incoming RTCP packet (from address %s, port %d)\n", this, our_inet_ntoa(fromAddress.sin_addr), ntohs(fromAddress.sin_port));
    unsigned char* p = pkt;
    for (unsigned i = 0; i < packetSize; ++i) {
      if (i%4 == 0) fprintf(stderr, " ");
      fprintf(stderr, "%02x", p[i]);
    }
    fprintf(stderr, "\n");
#endif
    int totPacketSize = IP_UDP_HDR_SIZE + packetSize;

    // Check the RTCP packet for validity:
    // It must at least contain a header (4 bytes), and this header
    // must be version=2, with no padding bit, and a payload type of
    // SR (200) or RR (201):
    if (packetSize < 4) break;
    unsigned rtcpHdr = ntohl(*(unsigned*)pkt);
    if ((rtcpHdr & 0xE0FE0000) != (0x80000000 | (RTCP_PT_SR<<16))) {
#ifdef DEBUG
      fprintf(stderr, "rejected bad RTCP packet: header 0x%08x\n", rtcpHdr);
#endif
      break;
    }

    // Process each of the individual RTCP 'subpackets' in (what may be)
    // a compound RTCP packet.
    unsigned reportSenderSSRC = 0;
    Boolean packetOK = False;
    while (1) {
      unsigned rc = (rtcpHdr>>24)&0x1F;
      unsigned pt = (rtcpHdr>>16)&0xFF;
      unsigned length = 4*(rtcpHdr&0xFFFF); // doesn't count hdr
      ADVANCE(4); // skip over the header
      if (length > packetSize) break;

      // Assume that each RTCP subpacket begins with a 4-byte SSRC:
      if (length < 4) break; length -= 4;
      reportSenderSSRC = ntohl(*(unsigned*)pkt); ADVANCE(4);

      Boolean subPacketOK = False;
      switch (pt) {
        case RTCP_PT_SR: {
#ifdef DEBUG
	  fprintf(stderr, "SR\n");
#endif
	  if (length < 20) break; length -= 20;

	  // Extract the NTP timestamp, and note this:
	  unsigned NTPmsw = ntohl(*(unsigned*)pkt); ADVANCE(4);
	  unsigned NTPlsw = ntohl(*(unsigned*)pkt); ADVANCE(4);
	  unsigned rtpTimestamp = ntohl(*(unsigned*)pkt); ADVANCE(4);
	  if (fSource != NULL) {
	    RTPReceptionStatsDB& receptionStats
	      = fSource->receptionStatsDB();
	    receptionStats.noteIncomingSR(reportSenderSSRC,
					  NTPmsw, NTPlsw, rtpTimestamp);
	  }
	  ADVANCE(8); // skip over packet count, octet count

	  // If a 'SR handler' was set, call it now:
	  if (fSRHandlerTask != NULL) (*fSRHandlerTask)(fSRHandlerClientData);

	  // The rest of the SR is handled like a RR (so, no "break;" here)
	}
        case RTCP_PT_RR: {
#ifdef DEBUG
	  fprintf(stderr, "RR\n");
#endif
	  unsigned reportBlocksSize = rc*(6*4);
	  if (length < reportBlocksSize) break;
	  length -= reportBlocksSize;

          if (fSink != NULL) {
	    // Use this information to update stats about our transmissions:
            RTPTransmissionStatsDB& transmissionStats = fSink->transmissionStatsDB();
            for (unsigned i = 0; i < rc; ++i) {
              unsigned senderSSRC = ntohl(*(unsigned*)pkt); ADVANCE(4);
              // We care only about reports about our own transmission, not others'
              if (senderSSRC == fSink->SSRC()) {
                unsigned lossStats = ntohl(*(unsigned*)pkt); ADVANCE(4);
                unsigned highestReceived = ntohl(*(unsigned*)pkt); ADVANCE(4);
                unsigned jitter = ntohl(*(unsigned*)pkt); ADVANCE(4);
                unsigned timeLastSR = ntohl(*(unsigned*)pkt); ADVANCE(4);
                unsigned timeSinceLastSR = ntohl(*(unsigned*)pkt); ADVANCE(4);
                transmissionStats.noteIncomingRR(reportSenderSSRC, fromAddress,
						 lossStats,
						 highestReceived, jitter,
						 timeLastSR, timeSinceLastSR);
              } else {
                ADVANCE(4*5);
              }
            }
          } else {
            ADVANCE(reportBlocksSize);
          }

	  if (pt == RTCP_PT_RR) { // i.e., we didn't fall through from 'SR'
	    // If a 'RR handler' was set, call it now:

	    // Specific RR handler:
	    if (fSpecificRRHandlerTable != NULL) {
	      netAddressBits fromAddr;
	      portNumBits fromPortNum;
	      if (tcpReadStreamSocketNum < 0) {
		// Normal case: We read the RTCP packet over UDP
		fromAddr = fromAddress.sin_addr.s_addr;
		fromPortNum = ntohs(fromAddress.sin_port);
	      } else {
		// Special case: We read the RTCP packet over TCP (interleaved)
		// Hack: Use the TCP socket and channel id to look up the handler
		fromAddr = tcpReadStreamSocketNum;
		fromPortNum = tcpReadStreamChannelId;
	      }
	      Port fromPort(fromPortNum);
	      RRHandlerRecord* rrHandler
		= (RRHandlerRecord*)(fSpecificRRHandlerTable->Lookup(fromAddr, (~0), fromPort));
	      if (rrHandler != NULL) {
		if (rrHandler->rrHandlerTask != NULL) {
		  (*(rrHandler->rrHandlerTask))(rrHandler->rrHandlerClientData);
		}
	      }
	    }

	    // General RR handler:
	    if (fRRHandlerTask != NULL) (*fRRHandlerTask)(fRRHandlerClientData);
	  }

	  subPacketOK = True;
	  typeOfPacket = PACKET_RTCP_REPORT;
	  break;
	}
        case RTCP_PT_BYE: {
#ifdef DEBUG
	  fprintf(stderr, "BYE\n");
#endif
	  // If a 'BYE handler' was set, call it now:
	  TaskFunc* byeHandler = fByeHandlerTask;
	  if (byeHandler != NULL
	      && (!fByeHandleActiveParticipantsOnly
		  || (fSource != NULL
		      && fSource->receptionStatsDB().lookup(reportSenderSSRC) != NULL)
		  || (fSink != NULL
		      && fSink->transmissionStatsDB().lookup(reportSenderSSRC) != NULL))) {
	    fByeHandlerTask = NULL;
	        // we call this only once by default
	    (*byeHandler)(fByeHandlerClientData);
	  }

	  // We should really check for & handle >1 SSRCs being present #####

	  subPacketOK = True;
	  typeOfPacket = PACKET_BYE;
	  break;
	}
	// Later handle SDES, APP, and compound RTCP packets #####
        default:
#ifdef DEBUG
	  fprintf(stderr, "UNSUPPORTED TYPE(0x%x)\n", pt);
#endif
	  subPacketOK = True;
	  break;
      }
      if (!subPacketOK) break;

      // need to check for (& handle) SSRC collision! #####

#ifdef DEBUG
      fprintf(stderr, "validated RTCP subpacket (type %d): %d, %d, %d, 0x%08x\n", typeOfPacket, rc, pt, length, reportSenderSSRC);
#endif

      // Skip over any remaining bytes in this subpacket:
      ADVANCE(length);

      // Check whether another RTCP 'subpacket' follows:
      if (packetSize == 0) {
	packetOK = True;
	break;
      } else if (packetSize < 4) {
#ifdef DEBUG
	fprintf(stderr, "extraneous %d bytes at end of RTCP packet!\n", packetSize);
#endif
	break;
      }
      rtcpHdr = ntohl(*(unsigned*)pkt);
      if ((rtcpHdr & 0xC0000000) != 0x80000000) {
#ifdef DEBUG
	fprintf(stderr, "bad RTCP subpacket: header 0x%08x\n", rtcpHdr);
#endif
	break;
      }
    }

    if (!packetOK) {
#ifdef DEBUG
      fprintf(stderr, "rejected bad RTCP subpacket: header 0x%08x\n", rtcpHdr);
#endif
      break;
    } else {
#ifdef DEBUG
      fprintf(stderr, "validated entire RTCP packet\n");
#endif
    }

    onReceive(typeOfPacket, totPacketSize, reportSenderSSRC);
  } while (0);
}
예제 #17
0
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 = ourIPAddress(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 = "LIVE555 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();
}