Exemplo n.º 1
0
Groupsock* GroupsockLookupTable::AddNew(UsageEnvironment& env,
					netAddressBits groupAddress,
					netAddressBits sourceFilterAddress,
					Port port, u_int8_t ttl) {
  Groupsock* groupsock;
  do {
    struct in_addr groupAddr; groupAddr.s_addr = groupAddress;
    if (sourceFilterAddress == netAddressBits(~0)) {
      // regular, ISM groupsock
      groupsock = new Groupsock(env, groupAddr, port, ttl);
    } else {
      // SSM groupsock
      struct in_addr sourceFilterAddr;
      sourceFilterAddr.s_addr = sourceFilterAddress;
      groupsock = new Groupsock(env, groupAddr, sourceFilterAddr, port);
    }

    if (groupsock == NULL || groupsock->socketNum() < 0) break;

    if (!setGroupsockBySocket(env, groupsock->socketNum(), groupsock)) break;

    fTable.Add(groupAddress, sourceFilterAddress, port, (void*)groupsock);
  } while (0);

  return groupsock;
}
void StreamState::endPlaying(Destinations* dests, char *videotype, char *audiotype) {

	Debug(ckite_log_message, "StreamState::endPlaying\n");
	if (dests->isTCP) {
		if (fRTPSink != NULL) {
			if (strcmp(videotype, "store") == 0 || strcmp(audiotype, "store") == 0) {
			}else{
				fRTPSink->removeStreamSocket(dests->tcpSocketNum, dests->rtpChannelId);
			}
		}
		if (fRTCPInstance != NULL) {
			if (strcmp(videotype, "store") == 0 || strcmp(audiotype, "store") == 0) {
			}else{
				fRTCPInstance->removeStreamSocket(dests->tcpSocketNum, dests->rtcpChannelId);
			}
			fRTCPInstance->setSpecificRRHandler(dests->tcpSocketNum, dests->rtcpChannelId,
					NULL, NULL);
		}
	} else {
		// Tell the RTP and RTCP 'groupsocks' to stop using these destinations:
		if (fRTPgs != NULL) fRTPgs->removeDestination(dests->addr, dests->rtpPort);
		if (fRTCPgs != NULL) fRTCPgs->removeDestination(dests->addr, dests->rtcpPort);
		if (fRTCPInstance != NULL) {
			fRTCPInstance->setSpecificRRHandler(dests->addr.s_addr, dests->rtcpPort,
					NULL, NULL);
		}
	}
}
Exemplo n.º 3
0
static Boolean unsetGroupsockBySocket(Groupsock const* groupsock) {
  do {
    if (groupsock == NULL) break;

    int sock = groupsock->socketNum();
    // Make sure "sock" is in bounds:
    if (sock < 0) break;

    HashTable* sockets = getSocketTable(groupsock->env());
    if (sockets == NULL) break;

    Groupsock* gs = (Groupsock*)sockets->Lookup((char*)(long)sock);
    if (gs == NULL || gs != groupsock) break;
    sockets->Remove((char*)(long)sock);

    if (sockets->IsEmpty()) {
      // We can also delete the table (to reclaim space):
      delete sockets;
      (gs->env()).groupsockPriv = NULL;
    }

    return True;
  } while (0);

  return False;
}
void PassiveServerMediaSubsession
::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) {
  isMulticast = True;
  Groupsock& gs = fRTPSink.groupsockBeingUsed();
  if (destinationTTL == 255) destinationTTL = gs.ttl();
  if (destinationAddress == 0) { // normal case
    destinationAddress = gs.groupAddress().s_addr;
  } else { // use the client-specified destination address instead:
    struct in_addr destinationAddr; destinationAddr.s_addr = destinationAddress;
    gs.changeDestinationParameters(destinationAddr, 0, destinationTTL);
    if (fRTCPInstance != NULL) {
      Groupsock* rtcpGS = fRTCPInstance->RTCPgs();
      rtcpGS->changeDestinationParameters(destinationAddr, 0, destinationTTL);
    }
  }
  serverRTPPort = gs.port();
  if (fRTCPInstance != NULL) {
    Groupsock* rtcpGS = fRTCPInstance->RTCPgs();
    serverRTCPPort = rtcpGS->port();
  }
  streamToken = NULL; // not used
}
void StreamState
::startPlaying(Destinations* dests,
				 TaskFunc* rtcpRRHandler, void* rtcpRRHandlerClientData) {
	if (dests == NULL) return;
	
	//skip play action if it's already being played
	if (!fAreCurrentlyPlaying && fMediaSource != NULL) {
		if (fRTPSink != NULL) {
			fRTPSink->startPlaying(*fMediaSource, afterPlayingStreamState, this);
			fAreCurrentlyPlaying = True;
		} else if (fUDPSink != NULL) {
			fUDPSink->startPlaying(*fMediaSource, afterPlayingStreamState, this);
			fAreCurrentlyPlaying = True;
		}
		createWorkingThread();
	}

	if (fRTCPInstance == NULL && fRTPSink != NULL) {
		// Create (and start) a 'RTCP instance' for this RTP sink:
		fRTCPInstance
	//		= RTCPInstance::createNew(fRTPSink->envir(), fRTCPgs,
			= RTCPInstance::createNew(fRtcpEnv, fRTCPgs,
			fTotalBW, (unsigned char*)fMaster.fCNAME,
			fRTPSink, NULL /* we're a server */);
		// Note: This starts RTCP running automatically
	}

	if (dests->isTCP) {
		// Change RTP and RTCP to use the TCP socket instead of UDP:
		if (fRTPSink != NULL) {
			fRTPSink->addStreamSocket(dests->tcpSocketNum, dests->rtpChannelId);
		}
		if (fRTCPInstance != NULL) {
			fRTCPInstance->addStreamSocket(dests->tcpSocketNum, dests->rtcpChannelId);
			fRTCPInstance->setSpecificRRHandler(dests->tcpSocketNum, dests->rtcpChannelId,
				rtcpRRHandler, rtcpRRHandlerClientData);
		}
	} else {
		// Tell the RTP and RTCP 'groupsocks' about this destination
		// (in case they don't already have it):
		//	printf("addDestination %s\n", inet_ntoa(dests->addr));	//jay
		if (fRTPgs != NULL) fRTPgs->addDestination(dests->addr, dests->rtpPort);
		if (fRTCPgs != NULL) fRTCPgs->addDestination(dests->addr, dests->rtcpPort);
		if (fRTCPInstance != NULL) {
			if (!IsMulticastAddress(dests->addr.s_addr)) {
				fRTCPInstance->setSpecificRRHandler(dests->addr.s_addr, dests->rtcpPort,
				rtcpRRHandler, rtcpRRHandlerClientData);
			}
		}
	}
}
Exemplo n.º 6
0
static void
NATHolePunch(RTPSource *rtpsrc, MediaSubsession *subsession) {
	Groupsock *gs = NULL;
	int s;
	struct sockaddr_in sin;
	unsigned char buf[1] = { 0x00 };
#ifdef WIN32
	int sinlen = sizeof(sin);
#else
	socklen_t sinlen = sizeof(sin);
#endif
	if(rtspconf->sin.sin_addr.s_addr == 0
	|| rtspconf->sin.sin_addr.s_addr == INADDR_NONE) {
		rtsperror("NAT hole punching: no server address available.\n");
		return;
	}
	if(rtpsrc == NULL) {
		rtsperror("NAT hole punching: no RTPSource available.\n");
		return;
	}
	if(subsession == NULL) {
		rtsperror("NAT hole punching: no subsession available.\n");
		return;
	}
	gs = rtpsrc->RTPgs();
	if(gs == NULL) {
		rtsperror("NAT hole punching: no Groupsock available.\n");
		return;
	}
	//
	s = gs->socketNum();
	if(getsockname(s, (struct sockaddr*) &sin, &sinlen) < 0) {
		rtsperror("NAT hole punching: getsockname - %s.\n", strerror(errno));
		return;
	}
	rtsperror("NAT hole punching: fd=%d, local-port=%d/%d server-port=%d\n",
		s, ntohs(sin.sin_port), subsession->clientPortNum(), subsession->serverPortNum);
	//
	bzero(&sin, sizeof(sin));
	sin.sin_addr = rtspconf->sin.sin_addr;
	sin.sin_port = htons(subsession->serverPortNum);
	// send 5 packets
	// XXX: use const char * for buf pointer to work with Windows
	sendto(s, (const char *) buf, 1, 0, (struct sockaddr*) &sin, sizeof(sin)); usleep(5000);
	sendto(s, (const char *) buf, 1, 0, (struct sockaddr*) &sin, sizeof(sin)); usleep(5000);
	sendto(s, (const char *) buf, 1, 0, (struct sockaddr*) &sin, sizeof(sin)); usleep(5000);
	sendto(s, (const char *) buf, 1, 0, (struct sockaddr*) &sin, sizeof(sin)); usleep(5000);
	sendto(s, (const char *) buf, 1, 0, (struct sockaddr*) &sin, sizeof(sin)); usleep(5000);
	//
	return;
}
void StreamState
::startPlaying(Destinations* dests,
	       TaskFunc* rtcpRRHandler, void* rtcpRRHandlerClientData,
	       ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,
	       void* serverRequestAlternativeByteHandlerClientData) {
  if (dests == NULL) return;

  if (fRTCPInstance == NULL && fRTPSink != NULL) {
    // Create (and start) a 'RTCP instance' for this RTP sink:
    fRTCPInstance
      = RTCPInstance::createNew(fRTPSink->envir(), fRTCPgs,
				fTotalBW, (unsigned char*)fMaster.fCNAME,
				fRTPSink, NULL /* we're a server */);
        // Note: This starts RTCP running automatically
  }

  if (dests->isTCP) {
    // Change RTP and RTCP to use the TCP socket instead of UDP:
    if (fRTPSink != NULL) {
      fRTPSink->addStreamSocket(dests->tcpSocketNum, dests->rtpChannelId);
      fRTPSink->setServerRequestAlternativeByteHandler(dests->tcpSocketNum, serverRequestAlternativeByteHandler, serverRequestAlternativeByteHandlerClientData);
    }
    if (fRTCPInstance != NULL) {
      fRTCPInstance->addStreamSocket(dests->tcpSocketNum, dests->rtcpChannelId);
      fRTCPInstance->setSpecificRRHandler(dests->tcpSocketNum, dests->rtcpChannelId,
					  rtcpRRHandler, rtcpRRHandlerClientData);
    }
  } else {
    // Tell the RTP and RTCP 'groupsocks' about this destination
    // (in case they don't already have it):
    if (fRTPgs != NULL) fRTPgs->addDestination(dests->addr, dests->rtpPort);
    if (fRTCPgs != NULL) fRTCPgs->addDestination(dests->addr, dests->rtcpPort);
    if (fRTCPInstance != NULL) {
      fRTCPInstance->setSpecificRRHandler(dests->addr.s_addr, dests->rtcpPort,
					  rtcpRRHandler, rtcpRRHandlerClientData);
    }
  }

  if (!fAreCurrentlyPlaying && fMediaSource != NULL) {
    if (fRTPSink != NULL) {
      fRTPSink->startPlaying(*fMediaSource, afterPlayingStreamState, this);
      fAreCurrentlyPlaying = True;
    } else if (fUDPSink != NULL) {
      fUDPSink->startPlaying(*fMediaSource, afterPlayingStreamState, this);
      fAreCurrentlyPlaying = True;
    }
  }
}
Exemplo n.º 8
0
void addFaceSubstreams0(void*)
{
	int portCounter = 0;
	for (int j = 0; j < cubemap->getEyesCount(); j++)
	{
		Cubemap* eye = cubemap->getEye(j);

		for (int i = 0; i < eye->getFacesCount(); i++)
		{
			faceStreams.push_back(FrameStreamState());
			FrameStreamState* state = &faceStreams.back();

			state->content = eye->getFace(i)->getContent();

			Port rtpPort(FACE0_RTP_PORT_NUM + portCounter);
			portCounter += 2;
			Groupsock* rtpGroupsock = new Groupsock(*env, destinationAddress, rtpPort, TTL);
			//rtpGroupsock->multicastSendOnly(); // we're a SSM source

			setReceiveBufferTo(*env, rtpGroupsock->socketNum(), bufferSize);

			// Create a 'H264 Video RTP' sink from the RTP 'groupsock':
			state->sink = H264VideoRTPSink::createNew(*env, rtpGroupsock, 96);

			ServerMediaSubsession* subsession = PassiveServerMediaSubsession::createNew(*state->sink);

			cubemapSMS->addSubsession(subsession);

			RawPixelSource* source = RawPixelSource::createNew(*env,
				state->content,
				avgBitRate);

			source->setOnSentNALU    (boost::bind(&onSentNALU,     _1, _2, _3, j, i));
			source->setOnEncodedFrame(boost::bind(&onEncodedFrame, _1, j, i));

			state->source = H264VideoStreamDiscreteFramer::createNew(*env,
				source);

			state->sink->startPlaying(*state->source, NULL, NULL);

			std::cout << "Streaming face " << i << " (" << ((j == 0) ? "left" : "right") << ") on port " << ntohs(rtpPort.num()) << " ..." << std::endl;
		}
	}
    
    announceStream(rtspServer, cubemapSMS, cubemapStreamName);
}
void StreamState::endPlaying(Destinations* dests) {
  if (dests->isTCP) {
    if (fRTPSink != NULL) {
      fRTPSink->removeStreamSocket(dests->tcpSocketNum, dests->rtpChannelId);
    }
    if (fRTCPInstance != NULL) {
      fRTCPInstance->removeStreamSocket(dests->tcpSocketNum, dests->rtcpChannelId);
      fRTCPInstance->setSpecificRRHandler(dests->tcpSocketNum, dests->rtcpChannelId,
					  NULL, NULL);
    }
  } else {
    // Tell the RTP and RTCP 'groupsocks' to stop using these destinations:
    if (fRTPgs != NULL) fRTPgs->removeDestination(dests->addr, dests->rtpPort);
    if (fRTCPgs != NULL) fRTCPgs->removeDestination(dests->addr, dests->rtcpPort);
    if (fRTCPInstance != NULL) {
      fRTCPInstance->setSpecificRRHandler(dests->addr.s_addr, dests->rtcpPort,
					  NULL, NULL);
    }
  }
}
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:
    serverRTPPort = ((StreamState*)fLastStreamToken)->serverRTPPort();
    serverRTCPPort = ((StreamState*)fLastStreamToken)->serverRTCPPort();
    ++((StreamState*)fLastStreamToken)->referenceCount();
    streamToken = fLastStreamToken;
  } else {
    // Normal case: 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(envir()); // 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(envir()); // 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:
    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
    destinations = new Destinations(destinationAddr, clientRTPPort, clientRTCPPort);
  } else { // TCP
    destinations = new Destinations(tcpSocketNum, rtpChannelId, rtcpChannelId);
  }
  fDestinationsHashTable->Add((char const*)clientSessionId, destinations);
}
Exemplo n.º 11
0
int CMPIPTV_RTSP::OpenConnection(void)
{
  this->logger.Log(LOGGER_INFO, METHOD_START_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME);
  this->isRtspSessionSetup = false;

  // LIVE555 works with char, not with TCHAR
  char *tempRtspUrl = ConvertToMultiByte(this->rtspUrl);
  if (tempRtspUrl == NULL)
  {
    return STATUS_ERROR;
  }

  // start LIVE555 worker thread
  this->rtspSchedulerThreadHandle = CreateThread( 
    NULL,                                   // default security attributes
    0,                                      // use default stack size  
    &CMPIPTV_RTSP::RtspSchedulerWorker,     // thread function name
    this,                                   // argument to thread function 
    0,                                      // use default creation flags 
    &this->rtspSchedulerThreadId);          // returns the thread identifier
  if (this->rtspSchedulerThreadHandle == NULL)
  {
    this->logger.Log(LOGGER_ERROR, _T("%s: %s: failed to create RTSP scheduler thread, error = %i"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, GetLastError());
    return STATUS_ERROR;
  }

  this->rtspClient = MPRTSPClient::createNew(this, *this->rtspEnvironment, tempRtspUrl);
  FREE_MEM(tempRtspUrl);
  if (
    this->rtspClient == NULL ||
    SendRtspCommand(METHOD_OPEN_CONNECTION_NAME, _T("OPTIONS")) != STATUS_OK ||
    SendRtspCommand(METHOD_OPEN_CONNECTION_NAME, _T("DESCRIBE")) != STATUS_OK
  )
  {
    CloseConnection();
    return STATUS_ERROR;
  }

  this->rtspSession = MediaSession::createNew(*this->rtspEnvironment, this->rtspResponseResultString);
  if (this->rtspSession == NULL || !this->rtspSession->hasSubsessions())
  {
    this->LogRtspMessage(LOGGER_ERROR, METHOD_OPEN_CONNECTION_NAME, this->rtspSession == NULL ? _T("failed to create session") : _T("session doesn't have sub-sessions"));
    CloseConnection();
    return STATUS_ERROR;
  }

  // Setup the RTP source for the session. Only one sub-session expected/supported.
  MediaSubsessionIterator iter(*this->rtspSession);
  MediaSubsession *subsession = NULL;
  FramedSource *rtspSource = NULL;
  while ((subsession = iter.next()) != NULL)
  {
#ifdef _MBCS
    TCHAR *subSessionName = ConvertToMultiByteA(subsession->mediumName());
    TCHAR *subSessionCodecName = ConvertToMultiByteA(subsession->codecName());
#else
    TCHAR *subSessionName = ConvertToUnicodeA(subsession->mediumName());
    TCHAR *subSessionCodecName = ConvertToUnicodeA(subsession->codecName());
#endif

    if (_tcsncicmp(subSessionName, _T("video"), 5) != 0 || _tcsncicmp(subSessionCodecName, _T("MP2T"), 4) != 0)
    {
      TCHAR *message = FormatString(_T("sub-session medium or codec not supported, medium = %s, codec = %s"), subSessionName, subSessionCodecName);
      this->LogRtspMessage(LOGGER_ERROR, METHOD_OPEN_CONNECTION_NAME, message);
      FREE_MEM(message);
      FREE_MEM(subSessionName);
      FREE_MEM(subSessionCodecName);
      continue;
    }

    // If a client port is configured, find a free pair of ports in the range.
    // The first port is used for RTP; the second port is used for RTCP. Once
    // we find one free port, we assume the next one is also free.
    if (this->rtspRtpClientPortRangeStart > 0)
    {
      struct in_addr destinationAddress;
      destinationAddress.s_addr = our_inet_addr("127.0.0.1");
      unsigned int port = this->rtspRtpClientPortRangeStart;
      Groupsock *groupsock = NULL;
      do
      {
        this->logger.Log(LOGGER_VERBOSE, _T("%s: %s: RTP client port %u"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, port);

        // special construction force not reuse same UDP port
        {
          NoReuse noReuse(*this->rtspEnvironment);
          groupsock = new Groupsock(*this->rtspEnvironment, destinationAddress, port, 1);
        }

        if (groupsock == NULL || groupsock->socketNum() == -1)
        {
          this->logger.Log(LOGGER_WARNING, _T("%s: %s: RTP client port %u occupied, trying next even port"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, port);
          port += 2;
          if (groupsock != NULL)
          {
            delete groupsock;
            groupsock = NULL;
          }
        }
      }
      while ((groupsock == NULL) && (port <= this->rtspRtpClientPortRangeEnd));
      // Did we find a free port? If not, we fall back to a random port chosen
      // by LIVE555.
      if (groupsock != NULL)
      {
        delete groupsock;
        groupsock = NULL;
        subsession->setClientPortNum(port);
      }
    }

    if (!subsession->initiate() || subsession->rtpSource() == NULL)
    {
      TCHAR *message = FormatString(_T("failed to create receiver for sub-session, medium = %s, codec = %s"), subSessionName, subSessionCodecName);
      this->LogRtspMessage(LOGGER_ERROR, METHOD_OPEN_CONNECTION_NAME, message);
      FREE_MEM(message);
      FREE_MEM(subSessionName);
      FREE_MEM(subSessionCodecName);
      continue;
    }

    this->logger.Log(LOGGER_VERBOSE, _T("%s: %s: created receiver for sub-session, medium = %s, codec = %s"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName, subSessionCodecName);
    FREE_MEM(subSessionName);
    FREE_MEM(subSessionCodecName);

    // set session ID, doesn't matter what
    subsession->setSessionId(subsession->mediumName());

    // because we're saving the incoming data, rather than playing
    // it in real time, allow an especially large time threshold
    // for reordering misordered incoming packets:
    subsession->rtpSource()->setPacketReorderingThresholdTime(1000000); // 1 second

    // set the RTP source's OS socket buffer size as appropriate
    int socketNum = subsession->rtpSource()->RTPgs()->socketNum();
    unsigned int currentBufferSize = getReceiveBufferSize(*this->rtspEnvironment, socketNum);
    if (this->defaultBufferSize > currentBufferSize)
    {
      setReceiveBufferTo(*this->rtspEnvironment, socketNum, this->defaultBufferSize);
      unsigned setBufferSize = getReceiveBufferSize(*this->rtspEnvironment, socketNum);
      if (setBufferSize == this->defaultBufferSize)
      {
        this->logger.Log(LOGGER_VERBOSE, _T("%s: %s: set buffer size for sub-session, previous size = %i, requested size = %i, current size = %i"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName, currentBufferSize, this->defaultBufferSize, setBufferSize);
      }
      else
      {
        this->logger.Log(LOGGER_WARNING, _T("%s: %s: failed to set buffer size for sub-session, previous size = %i, requested size = %i, current size = %i"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName, currentBufferSize, this->defaultBufferSize, setBufferSize);
      }
    }

    if (SendRtspCommand(METHOD_OPEN_CONNECTION_NAME, _T("SETUP"), subsession) != STATUS_OK)
    {
      CloseConnection();
      return STATUS_ERROR;
    }
    rtspSource = subsession->rtpSource();
    break;
  }

  // If we don't have an RTSP source then we can't continue.
  if (rtspSource == NULL)
  {
    CloseConnection();
    return STATUS_ERROR;
  }

  this->isRtspSessionSetup = true;
  if (SendRtspCommand(METHOD_OPEN_CONNECTION_NAME, _T("PLAY")) != STATUS_OK)
  {
    CloseConnection();
    return STATUS_ERROR;
  }

  // create UDP socket and start playing
  struct in_addr destinationAddress;
  destinationAddress.s_addr = our_inet_addr("127.0.0.1");

  unsigned int port = this->rtspUdpPortRangeStart;
  do
  {
    this->logger.Log(LOGGER_VERBOSE, _T("%s: %s: UDP port %u"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, port);

    // special construction force not reuse same UDP port
    {
      NoReuse noReuse(*this->rtspEnvironment);
      this->rtspUdpGroupsock = new Groupsock(*this->rtspEnvironment, destinationAddress, port, 1);
    }

    if (this->rtspUdpGroupsock == NULL || this->rtspUdpGroupsock->socketNum() == -1)
    {
      this->logger.Log(LOGGER_WARNING, _T("%s: %s: UDP port %u occupied, trying another port"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, port);
      port++;
      if (this->rtspUdpGroupsock != NULL)
      {
        delete this->rtspUdpGroupsock;
        this->rtspUdpGroupsock = NULL;
      }
    }
  }
  while ((this->rtspUdpGroupsock == NULL) && (port <= this->rtspUdpPortRangeEnd));

  if (this->rtspUdpGroupsock == NULL)
  {
    this->logger.Log(LOGGER_ERROR, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("failed to create UDP socket, no free port"));
    CloseConnection();
    return STATUS_ERROR;
  }

  this->rtspUdpSink = BasicUDPSink::createNew(*this->rtspEnvironment, this->rtspUdpGroupsock, this->rtspUdpSinkMaxPayloadSize);
  if (this->rtspUdpSink == NULL)
  {
    this->logger.Log(LOGGER_ERROR, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("failed to create UDP sink"));
    CloseConnection();
    return STATUS_ERROR;
  }

  if (!this->rtspUdpSink->startPlaying(*rtspSource, NULL, NULL))
  {
    this->LogRtspMessage(LOGGER_ERROR, METHOD_OPEN_CONNECTION_NAME, _T("failed to start UDP sink"));
    CloseConnection();
    return STATUS_ERROR;
  }

  this->logger.Log(LOGGER_INFO, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("streaming started"));

  // create a UDP connection to the local stream
  TCHAR *url = FormatString(_T("udp://@127.0.0.1:%u"), port);
  if (
    url == NULL ||
    this->CMPIPTV_UDP::ParseUrl(url, NULL) != STATUS_OK ||
    this->CMPIPTV_UDP::OpenConnection() != STATUS_OK
  )
  {
    FREE_MEM(url);
    this->logger.Log(LOGGER_INFO, METHOD_END_FAIL_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME);
    CloseConnection();
    return STATUS_ERROR;
  }

  FREE_MEM(url);
  this->logger.Log(LOGGER_INFO, METHOD_END_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME);
  return STATUS_OK;
}