Example #1
0
int CMPIPTV_RTSP::SendRtspCommand(const TCHAR *method, const TCHAR *command, MediaSubsession *subsession)
{
  this->logger.Log(LOGGER_VERBOSE, _T("%s: %s: send %s command"), PROTOCOL_IMPLEMENTATION_NAME, method, command);
  ResetEvent(this->rtspResponseEvent);
  if (_tcscmp(command, _T("OPTIONS")) == 0)
  {
    this->rtspClient->sendOptionsCommand(&CMPIPTV_RTSP::OnRtspResponseReceived);
  }
  else if (_tcscmp(command, _T("DESCRIBE")) == 0)
  {
    this->rtspClient->sendDescribeCommand(&CMPIPTV_RTSP::OnRtspResponseReceived);
  }
  else if (_tcscmp(command, _T("SETUP")) == 0)
  {
    this->rtspClient->sendSetupCommand(*subsession, &CMPIPTV_RTSP::OnRtspResponseReceived);
  }
  else if (_tcscmp(command, _T("PLAY")) == 0)
  {
    this->rtspClient->sendPlayCommand(*this->rtspSession, &CMPIPTV_RTSP::OnRtspResponseReceived);
  }
  else if (_tcscmp(command, _T("TEARDOWN")) == 0)
  {
    this->rtspClient->sendTeardownCommand(*this->rtspSession, &CMPIPTV_RTSP::OnRtspResponseReceived);
  }
  else
  {
    return STATUS_ERROR;
  }

  if (WaitForSingleObject(this->rtspResponseEvent, this->rtspCommandResponseTimeout) == WAIT_TIMEOUT)
  {
    this->logger.Log(LOGGER_ERROR, _T("%s: %s: %s command timed out"), PROTOCOL_IMPLEMENTATION_NAME, method, command);
    return STATUS_ERROR;
  }
  if (this->rtspResponseResultCode != 0)
  {
#ifdef _MBCS
    TCHAR *convertedRtspResponse = ConvertToMultiByteA(&this->rtspResponseResultString[0]);
#else
    TCHAR *convertedRtspResponse = ConvertToUnicodeA(&this->rtspResponseResultString[0]);
#endif
    this->logger.Log(LOGGER_ERROR, _T("%s: %s: %s command failed, code = %i, response = %s"), PROTOCOL_IMPLEMENTATION_NAME, method, command, this->rtspResponseResultCode, (convertedRtspResponse == NULL) ? _T("unable to get message") : convertedRtspResponse);
    FREE_MEM(convertedRtspResponse);
    return STATUS_ERROR;
  }

#ifdef _MBCS
  TCHAR *convertedRtspResponse = ConvertToMultiByteA(&this->rtspResponseResultString[0]);
#else
  TCHAR *convertedRtspResponse = ConvertToUnicodeA(&this->rtspResponseResultString[0]);
#endif
  this->logger.Log(LOGGER_VERBOSE, _T("%s: %s: %s command succeeded, response = %s"), PROTOCOL_IMPLEMENTATION_NAME, method, command, (convertedRtspResponse == NULL) ? _T("unable to get message") : convertedRtspResponse);
  FREE_MEM(convertedRtspResponse);
  return STATUS_OK;
}
Example #2
0
void CMPIPTV_RTSP::LogFullRtspMessage(unsigned int loggerLevel, const TCHAR* messagePrefix, const char *message)
{
#ifdef _MBCS
  TCHAR *convertedRtspMessage = ConvertToMultiByteA(message);
#else
  TCHAR *convertedRtspMessage = ConvertToUnicodeA(message);
#endif

  this->logger.Log(loggerLevel, _T("%s%s %s"), messagePrefix, (convertedRtspMessage == NULL) ? _T(",") : _T(":"), (convertedRtspMessage == NULL) ? _T("unable to get message") : convertedRtspMessage);
  FREE_MEM(convertedRtspMessage);
}
Example #3
0
void CMPIPTV_RTSP::LogRtspMessage(unsigned int loggerLevel, const TCHAR *method, const TCHAR *message)
{
  const char *lastRtspMessage = this->rtspEnvironment->getResultMsg();

#ifdef _MBCS
  TCHAR *convertedRtspMessage = ConvertToMultiByteA(lastRtspMessage);
#else
  TCHAR *convertedRtspMessage = ConvertToUnicodeA(lastRtspMessage);
#endif

  this->logger.Log(loggerLevel, _T("%s: %s: %s, %s"), PROTOCOL_IMPLEMENTATION_NAME, method, message, (convertedRtspMessage == NULL) ? _T("unable to get message") : convertedRtspMessage);

  FREE_MEM(convertedRtspMessage);
}
Example #4
0
char *CMPIPTV_RTSP::GetLastRtspMessageA(void)
{
  return ConvertToMultiByteA(this->rtspEnvironment->getResultMsg());
}
Example #5
0
int CMPIPTV_RTSP::OpenConnection(void)
{
  this->logger.Log(LOGGER_INFO, METHOD_START_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME);
  int result = STATUS_OK;

  this->rtspClient = RTSPClient::createNew(*this->rtspEnvironment);
  result |= (this->rtspClient == NULL);

  if (result == STATUS_OK)
  {
    // RTSPClient works with char, not with TCHAR
    char *tempRtspUrl = ConvertToMultiByte(this->rtspUrl);
    result |= (tempRtspUrl == NULL);
    if (result == STATUS_OK)
    {
      char* optionsResult = this->rtspClient->sendOptionsCmd(tempRtspUrl, NULL, NULL, NULL, this->receiveDataTimeout / 2000);
      result |= (optionsResult == NULL);

      if (result != STATUS_OK)
      {
        TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("error occured while sending OPTIONS command"));
        this->LogRtspMessage(LOGGER_ERROR, message);
        FREE_MEM(message);
      }
      else
      {
        TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("OPTIONS result"));
        this->LogFullRtspMessage(LOGGER_VERBOSE, message, optionsResult);
        FREE_MEM(message);

        char *describeResult = this->rtspClient->describeURL(tempRtspUrl, NULL, FALSE, this->receiveDataTimeout / 2000);
        result |= (describeResult == NULL);

        if (result != STATUS_OK)
        {
          TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("error occured while sending DESCRIBE command"));
          this->LogRtspMessage(LOGGER_ERROR, message);
          FREE_MEM(message);
        }
        else
        {
          TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("DESCRIBE result"));
          this->LogFullRtspMessage(LOGGER_VERBOSE, message, describeResult);
          FREE_MEM(message);

          this->rtspSession = MediaSession::createNew(*this->rtspEnvironment, describeResult);
          result |= (this->rtspSession == NULL);

          if (result != STATUS_OK)
          {
            TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("error occured while creating new session"));
            this->LogRtspMessage(LOGGER_ERROR, message);
            FREE_MEM(message);
          }
          else
          {
            result |= (!this->rtspSession->hasSubsessions());
            if (result != STATUS_OK)
            {
              this->logger.Log(LOGGER_ERROR, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("session doesn't have subsessions"));
            }
            else
            {
              // Then, setup the "RTPSource"s for the session:
              MediaSubsessionIterator iter(*this->rtspSession);
              MediaSubsession *subsession = NULL;

              while ((result == STATUS_OK) && ((subsession = iter.next()) != NULL))
              {
                char *tempSubSessionName = (char *)subsession->mediumName();
                char *tempSubSessionCodecName = (char *)subsession->codecName();
#ifdef _MBCS
                TCHAR *subSessionName = ConvertToMultiByteA(tempSubSessionName);
                TCHAR *subSessionCodecName = ConvertToMultiByteA(tempSubSessionCodecName);
#else
                TCHAR *subSessionName = ConvertToUnicodeA(tempSubSessionName);
                TCHAR *subSessionCodecName = ConvertToUnicodeA(tempSubSessionCodecName);
#endif
                if (!subsession->initiate())
                {
                  result = STATUS_ERROR;
                  TCHAR *message = FormatString(_T("%s: %s: unable to create receiver for subsession '%s', codec '%s'"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName, subSessionCodecName);
                  this->LogRtspMessage(LOGGER_ERROR, message);
                  FREE_MEM(message);
                }
                else
                {
                  this->logger.Log(LOGGER_VERBOSE, _T("%s: %s: created receiver for subsession '%s', codec '%s'"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName, subSessionCodecName);

                  // set session ID, doesn't matter what
                  subsession->sessionId = tempSubSessionName;

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

                    // 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 subsession '%s' successful, previous size: %i, requested size: %i, current size: %i"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName, currentBufferSize, this->defaultBufferSize, setBufferSize);
                      }
                      else
                      {
                        result = STATUS_ERROR;
                        this->logger.Log(LOGGER_ERROR, _T("%s: %s: set buffer size for subsession '%s' failed, previous size: %i, requested size: %i, current size: %i"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName, currentBufferSize, this->defaultBufferSize, setBufferSize);
                      }
                    }

                    if (_tcsncicmp(subSessionName, _T("audio"), 5) == 0)
                    {
                      // audio
                      this->logger.Log(LOGGER_VERBOSE, _T("%s: %s: audio subsession '%s'"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName);
                      result |= (!rtspClient->setupMediaSubsession(*subsession));

                      if (result != STATUS_OK)
                      {
                        // error occured
                        TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("error while setup subsession"));
                        this->LogRtspMessage(LOGGER_ERROR, message);
                        FREE_MEM(message);
                      }
                      else
                      {
                        this->logger.Log(LOGGER_WARNING, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("subsession audio codec not supported"));
                      }
                    }
                    else if (_tcsncicmp(subSessionName, _T("video"), 5) == 0)
                    {
                      // video
                      this->logger.Log(LOGGER_VERBOSE, _T("%s: %s: video subsession '%s'"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName);
                      result |= (!rtspClient->setupMediaSubsession(*subsession));

                      if (result != STATUS_OK)
                      {
                        // error occured
                        TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("error while setup subsession"));
                        this->LogRtspMessage(LOGGER_ERROR, message);
                        FREE_MEM(message);
                      }
                      else
                      {
                        if (_tcsncicmp(subSessionCodecName, _T("MP2T"), 4) == 0)
                        {
                          // MPEG2 Transport Stream
                          // set new RTSP source
                          this->rtspSource = subsession->rtpSource();

                          if (subsession->rtcpInstance() != NULL)
                          {
                            this->logger.Log(LOGGER_VERBOSE, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("set subsession 'Bye' handler"));
                            subsession->rtcpInstance()->setByeHandler(SubsessionByeHandler, this);
                          }
                        }
                        else if (_tcsncicmp(subSessionCodecName, _T("H264"), 4) == 0)
                        {
                          // H264 codec, HD TV
                          this->logger.Log(LOGGER_ERROR, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("H264 not supported"));
                          result = STATUS_ERROR;
                        }
                        else
                        {
                          // SD TV
                          this->logger.Log(LOGGER_ERROR, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("other subsession video codec than MP2T not supported"));
                          result = STATUS_ERROR;
                        }
                      }
                    }
                    else
                    {
                      this->logger.Log(LOGGER_WARNING, _T("%s: %s: unknown subsession '%s', ignored"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName);
                    }
                  }
                  else
                  {
                    this->logger.Log(LOGGER_WARNING, _T("%s: %s: subsession '%s' doesn't have RTP source"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName);
                  }
                }

                // free subsession name and codec name
                FREE_MEM(subSessionName);
                FREE_MEM(subSessionCodecName);
              }

              // we should have some RTSP source
              result |= (this->rtspSource == NULL);

              if (result == STATUS_OK)
              {
                result |= (!this->rtspClient->playMediaSession(*this->rtspSession));

                if (result != STATUS_OK)
                {
                  // error occured
                  TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("error while playing session"));
                  this->LogRtspMessage(LOGGER_ERROR, message);
                  FREE_MEM(message);
                }
                else
                {
                  // 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->rtspUdpGroupsock = new Groupsock(*this->rtspEnvironment, destinationAddress, port, 1);
                    }

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

                  result |= (this->rtspUdpGroupsock == NULL);
                  if (result != STATUS_OK)
                  {
                    this->logger.Log(LOGGER_ERROR, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("cannot create UDP sink, no free port"));
                  }
                  else
                  {
                    this->rtspUdpSink = BasicUDPSink::createNew(*this->rtspEnvironment, this->rtspUdpGroupsock, this->rtspUdpSinkMaxPayloadSize);
                    result |= (this->rtspUdpSink == NULL);

                    if (result != STATUS_OK)
                    {
                      TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("cannot create UDP sink"));
                      this->LogRtspMessage(LOGGER_ERROR, message);
                      FREE_MEM(message);
                    }
                    else
                    {
                      if (this->rtspUdpSink->startPlaying(*this->rtspSource, NULL, NULL))
                      {
                        this->logger.Log(LOGGER_INFO, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("playing started"));

                        // now create UDP connection
                        TCHAR *url = FormatString(_T("udp://@127.0.0.1:%u"), port);
                        result |= (url == NULL);

                        if (result == STATUS_OK)
                        {
                          // parse UDP url
                          // ParseURL calls ClearSession and IsConnected must return FALSE
                          // in another case will be current RTSP connection closed
                          result = this->CMPIPTV_UDP::ParseUrl(url, NULL);

                          if (result == STATUS_OK)
                          {
                            // connect to UDP url
                            result = this->CMPIPTV_UDP::OpenConnection();
                          }
                        }
                        FREE_MEM(url);
                      }
                      else
                      {
                        result = STATUS_ERROR;
                        TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("error occured while starting playing"));
                        this->LogRtspMessage(LOGGER_ERROR, message);
                        FREE_MEM(message);
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }

      if (optionsResult != NULL)
      {
        delete[] optionsResult;
        optionsResult = NULL;
      }
    }
    FREE_MEM(tempRtspUrl);
  }

  if (result == STATUS_OK)
  {
    // start winsock 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)
    {
      // thread not created
      result = STATUS_ERROR;
      this->logger.Log(LOGGER_ERROR, _T("%s: %s: cannot create RTSP scheduler thread, error: %i"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, GetLastError());
    }
  }

  if (result != STATUS_OK)
  {
    // if failed opening connection, than close connection
    this->CloseConnection();
  }

  this->logger.Log(LOGGER_INFO, (result == STATUS_OK) ? METHOD_END_FORMAT : METHOD_END_FAIL_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME);
  return (result == STATUS_OK) ? STATUS_OK : STATUS_ERROR;
}
Example #6
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;
}