Example #1
0
bool CRTSPClient::setupStreams()
{
  //setup streams
  XBMC->Log(LOG_DEBUG, "CRTSPClient::setupStreams()");
  Boolean madeProgress=False;
  MediaSubsessionIterator iter(*m_session);
  MediaSubsession *subsession;

  while ((subsession = iter.next()) != NULL) 
  {
    if (subsession->clientPortNum() == 0) continue; // port # was not set

    if (!clientSetupSubsession(m_ourClient, subsession, streamUsingTCP)) 
    {
      XBMC->Log(LOG_DEBUG,  "Failed to setup %s %s %s" ,subsession->mediumName(),subsession->codecName(),m_env->getResultMsg() );;
    } 
    else 
    {
      XBMC->Log(LOG_DEBUG,  "Setup %s %s %d %d" ,subsession->mediumName(),subsession->codecName(),subsession->clientPortNum(),subsession->clientPortNum()+1);;
      madeProgress = True;
    }
  }
  if (!madeProgress) 
  {
    shutdown();
    return false;
  }
  return true;
}
Example #2
0
BOOL setupStreams( unsigned *pResponseCode /*= NULL*/ ) 
{
	MediaSubsessionIterator iter(*session);
	MediaSubsession *subsession;
	Boolean madeProgress = False;

	BOOL bResult = TRUE;
	while ((subsession = iter.next()) != NULL) 
	{
		if (subsession->clientPortNum() == 0) continue; // port # was not set

		if ( !clientSetupSubsession(ourClient, subsession, streamUsingTCP, pResponseCode ) ) 
		{
			*env << "Failed to setup \"" << subsession->mediumName()
				<< "/" << subsession->codecName()
				<< "\" subsession: " << env->getResultMsg() << "\n";
			
			bResult = FALSE;
		}
		else 
		{
			*env << "Setup \"" << subsession->mediumName()
				<< "/" << subsession->codecName()
				<< "\" subsession (client ports " << subsession->clientPortNum()
				<< "-" << subsession->clientPortNum()+1 << ")\n";
			madeProgress = True;

			bResult = TRUE;
		}
	}
	//if (!madeProgress) 
	//	return bResult;
	return bResult;
}
Example #3
0
DefaultSink::DefaultSink(UsageEnvironment & env,
                         MediaSubsession & subsession,
                         char const * sink_url,
                         char const * stream_id)
        : MediaSink(env),
          _subsession(subsession),
          _receive_buffer(RECEIVE_BUFFER_SIZE),
          _stream_id(stream_id),
          _writer(libc2rtsp::sink::SinkFactory().gen(sink_url)),
          _verbose(true),
          _sprop_parameter_sets(),
          _have_written_first_frame(false)
{
    if (::strcmp(subsession.codecName(), "H264") == 0) {
        // For H.264 video stream, we use a special sink that adds 'start codes',
        // and (at the start) the SPS and PPS NAL units:
        _sprop_parameter_sets.emplace_back(std::string(subsession.fmtp_spropparametersets()));

    } else if (::strcmp(subsession.codecName(), "H265") == 0) {
        // For H.265 video stream, we use a special sink that adds 'start codes',
        // and (at the start) the VPS, SPS, and PPS NAL units:
        _sprop_parameter_sets.emplace_back(std::string(subsession.fmtp_spropvps())); // VPS
        _sprop_parameter_sets.emplace_back(std::string(subsession.fmtp_spropsps())); // SPS
        _sprop_parameter_sets.emplace_back(std::string(subsession.fmtp_sproppps())); // PPS

    } else {
        crLogE("DefaultSink::DefaultSink() Unsupported subsession: {}/{}",
               subsession.mediumName(), subsession.codecName());
        throw std::bad_alloc();
    }
}
ProxyServerMediaSubsession
::ProxyServerMediaSubsession(MediaSubsession& mediaSubsession,
			     portNumBits initialPortNum, Boolean multiplexRTCPWithRTP)
  : OnDemandServerMediaSubsession(mediaSubsession.parentSession().envir(), True/*reuseFirstSource*/,
				  initialPortNum, multiplexRTCPWithRTP),
    fClientMediaSubsession(mediaSubsession), fCodecName(strDup(mediaSubsession.codecName())),
    fNext(NULL), fHaveSetupStream(False) {
}
Example #5
0
void setupStreams() {
  MediaSubsessionIterator iter(*session);
  MediaSubsession *subsession;
  Boolean madeProgress = False;

  while ((subsession = iter.next()) != NULL) {
    if (subsession->clientPortNum() == 0) continue; // port # was not set

    if (!clientSetupSubsession(ourClient, subsession, streamUsingTCP)) {
      *env << "Failed to setup \"" << subsession->mediumName()
		    << "/" << subsession->codecName()
		    << "\" subsession: " << env->getResultMsg() << "\n";
    } else {
      *env << "Setup \"" << subsession->mediumName()
		    << "/" << subsession->codecName()
		    << "\" subsession (client ports " << subsession->clientPortNum()
		    << "-" << subsession->clientPortNum()+1 << ")\n";
      madeProgress = True;
    }
  }
  if (!madeProgress) shutdown();
}
Example #6
0
void subsessionByeHandler(void* clientData) {
  struct timeval timeNow;
  gettimeofday(&timeNow, NULL);
  unsigned secsDiff = timeNow.tv_sec - startTime.tv_sec;

  MediaSubsession* subsession = (MediaSubsession*)clientData;
  *env << "Received RTCP \"BYE\" on \"" << subsession->mediumName()
	<< "/" << subsession->codecName()
	<< "\" subsession (after " << secsDiff
	<< " seconds)\n";

  // Act now as if the subsession had closed:
  subsessionAfterPlaying(subsession);
}
void ProxyServerMediaSession::continueAfterDESCRIBE(char const* sdpDescription) {
    describeCompletedFlag = 1;

    // Create a (client) "MediaSession" object from the stream's SDP description ("resultString"), then iterate through its
    // "MediaSubsession" objects, to set up corresponding "ServerMediaSubsession" objects that we'll use to serve the stream's tracks.
    do {
        fClientMediaSession = MediaSession::createNew(envir(), sdpDescription);
        if (fClientMediaSession == NULL) break;

        MediaSubsessionIterator iter(*fClientMediaSession);
        for (MediaSubsession* mss = iter.next(); mss != NULL; mss = iter.next()) {
            ServerMediaSubsession* smss = new ProxyServerMediaSubsession(*mss);
            addSubsession(smss);
            if (fVerbosityLevel > 0) {
                envir() << *this << " added new \"ProxyServerMediaSubsession\" for "
                        << mss->protocolName() << "/" << mss->mediumName() << "/" << mss->codecName() << " track\n";
            }
        }
    } while (0);
}
bool MtkRTSPClient::handDescription(char* resultString)
{
	CHECK_NULL_COND(resultString, false);

	
	char* sdpDescription = resultString;
	//LOG_DEBUG("SDP description:%s", sdpDescription);
	
	// Create a media session object from this SDP description:
	session = MediaSession::createNew(*env, sdpDescription);
	if (session == NULL) 
	{
			LOG_ERR("Failed to create a MediaSession object from the SDP description: %s", env->getResultMsg());
			return false;
	} 
	if (!session->hasSubsessions())
	{
			LOG_ERR("This session has no media subsessions (i.e., \"m=\" lines)");
			Medium::close(session);
			session = NULL;
			return false;
	}

	/*
	 *TO DO:GET THE TIME RANGE
	 */
	fStartTime = session->playStartTime();
	if (fStartTime < 0)
	{
		fStartTime = 0.0f;
	}

	fEndTime= session->playEndTime();
	if (fEndTime <= 0)
	{
		fEndTime = -1.0f;
	}

	{
		/*send setup requesst count*/
		iSetupCount = 0;
	}
	
	// Then, setup the "RTPSource"s for the session:
	MediaSubsessionIterator iter(*(session));
	MediaSubsession *subsession = NULL;
	RtspReqSender *senderSave = pRtspReqSender->getNext();
	if (senderSave == NULL)
	{
		LOG_ERR("error");
		return false;
	}
	CmdSenderDecorator *senderMove = pRtspReqSender;
	
	while ((subsession = iter.next()) != NULL)
	{
		if (!subsession->initiate(-1))
		{
			LOG_ERR("warning");
			continue;
		}

		if (subsession->rtpSource() != NULL)
		{
#if 0			
			// 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);
#endif
#if 0
			// Set the RTP source's OS socket buffer size as appropriate - either if we were explicitly asked (using -B),
		  	// or if the desired FileSink buffer size happens to be larger than the current OS socket buffer size.
		  	// (The latter case is a heuristic, on the assumption that if the user asked for a large FileSink buffer size,
		  	// then the input data rate may be large enough to justify increasing the OS socket buffer size also.)
			int socketNum = subsession->rtpSource()->RTPgs()->socketNum();
			unsigned curBufferSize = getReceiveBufferSize(*env, socketNum);
			LOG_DEBUG("old receive buffer size:%d", curBufferSize);
			if (fileSinkBufferSize > curBufferSize) 
			{
			    unsigned newBufferSize = setReceiveBufferTo(*env, socketNum, fileSinkBufferSize);
				LOG_DEBUG("new receive buffer size:%d", newBufferSize);
			}
#else		
			int socketNum = subsession->rtpSource()->RTPgs()->socketNum();
		 	unsigned newBufferSize = setReceiveBufferTo(*env, socketNum, maxBufSize);
			LOG_DEBUG("new receive buffer size:%d", newBufferSize);
#endif
		}
		
		if (subsession->readSource() == NULL) 
		{
			LOG_ERR("warning");
			continue; // was not initiated
		}

		/*
		 *TO DO:SET UP SUBSESSION
		 */
		SetupSender *setupSender = new SetupSender(*senderSave);
		if (setupSender == NULL)
		{
			LOG_ERR("warning");
			continue;
		}
        
		sender->RecordSender(setupSender);
		senderMove->setNext(setupSender);
		senderMove = setupSender;
		setupSender->setRspHandler(respHandler);
		setupSender->setSubsession(subsession);

		if (bUseTcp == true)
		{
			if (subsession->clientPortNum() != 0)
			{
				LOG_DEBUG("sub session %p using tcp port :%d!", subsession, subsession->clientPortNum());
				setupSender->setParam(false, true, false);
			}
		}

		iSetupCount++;
					
		LOG_DEBUG("subsession, name:%s, codec:%s", subsession->mediumName(), subsession->codecName());		
	}

	return true;
}
Example #9
0
void printQOSData(int exitCode) {
  *env << "begin_QOS_statistics\n";
  
  // Print out stats for each active subsession:
  qosMeasurementRecord* curQOSRecord = qosRecordHead;
  if (session != NULL) {
    MediaSubsessionIterator iter(*session);
    MediaSubsession* subsession;
    while ((subsession = iter.next()) != NULL) {
      RTPSource* src = subsession->rtpSource();
      if (src == NULL) continue;
      
      *env << "subsession\t" << subsession->mediumName()
	   << "/" << subsession->codecName() << "\n";
      
      unsigned numPacketsReceived = 0, numPacketsExpected = 0;
      
      if (curQOSRecord != NULL) {
	numPacketsReceived = curQOSRecord->totNumPacketsReceived;
	numPacketsExpected = curQOSRecord->totNumPacketsExpected;
      }
      *env << "num_packets_received\t" << numPacketsReceived << "\n";
      *env << "num_packets_lost\t" << int(numPacketsExpected - numPacketsReceived) << "\n";
      
      if (curQOSRecord != NULL) {
	unsigned secsDiff = curQOSRecord->measurementEndTime.tv_sec
	  - curQOSRecord->measurementStartTime.tv_sec;
	int usecsDiff = curQOSRecord->measurementEndTime.tv_usec
	  - curQOSRecord->measurementStartTime.tv_usec;
	double measurementTime = secsDiff + usecsDiff/1000000.0;
	*env << "elapsed_measurement_time\t" << measurementTime << "\n";
	
	*env << "kBytes_received_total\t" << curQOSRecord->kBytesTotal << "\n";
	
	*env << "measurement_sampling_interval_ms\t" << qosMeasurementIntervalMS << "\n";
	
	if (curQOSRecord->kbits_per_second_max == 0) {
	  // special case: we didn't receive any data:
	  *env <<
	    "kbits_per_second_min\tunavailable\n"
	    "kbits_per_second_ave\tunavailable\n"
	    "kbits_per_second_max\tunavailable\n";
	} else {
	  *env << "kbits_per_second_min\t" << curQOSRecord->kbits_per_second_min << "\n";
	  *env << "kbits_per_second_ave\t"
	       << (measurementTime == 0.0 ? 0.0 : 8*curQOSRecord->kBytesTotal/measurementTime) << "\n";
	  *env << "kbits_per_second_max\t" << curQOSRecord->kbits_per_second_max << "\n";
	}
	
	*env << "packet_loss_percentage_min\t" << 100*curQOSRecord->packet_loss_fraction_min << "\n";
	double packetLossFraction = numPacketsExpected == 0 ? 1.0
	  : 1.0 - numPacketsReceived/(double)numPacketsExpected;
	if (packetLossFraction < 0.0) packetLossFraction = 0.0;
	*env << "packet_loss_percentage_ave\t" << 100*packetLossFraction << "\n";
	*env << "packet_loss_percentage_max\t"
	     << (packetLossFraction == 1.0 ? 100.0 : 100*curQOSRecord->packet_loss_fraction_max) << "\n";
	
	RTPReceptionStatsDB::Iterator statsIter(src->receptionStatsDB());
	// Assume that there's only one SSRC source (usually the case):
	RTPReceptionStats* stats = statsIter.next(True);
	if (stats != NULL) {
	  *env << "inter_packet_gap_ms_min\t" << stats->minInterPacketGapUS()/1000.0 << "\n";
	  struct timeval totalGaps = stats->totalInterPacketGaps();
	  double totalGapsMS = totalGaps.tv_sec*1000.0 + totalGaps.tv_usec/1000.0;
	  unsigned totNumPacketsReceived = stats->totNumPacketsReceived();
	  *env << "inter_packet_gap_ms_ave\t"
	       << (totNumPacketsReceived == 0 ? 0.0 : totalGapsMS/totNumPacketsReceived) << "\n";
	  *env << "inter_packet_gap_ms_max\t" << stats->maxInterPacketGapUS()/1000.0 << "\n";
	}
	
	curQOSRecord = curQOSRecord->fNext;
      }
    }
  }

  *env << "end_QOS_statistics\n";
  delete qosRecordHead;
}
Example #10
0
void continueAfterDESCRIBE(RTSPClient*, int resultCode, char* resultString) {
  if (resultCode != 0) {
    *env << "Failed to get a SDP description from URL \"" << streamURL << "\": " << resultString << "\n";
    shutdown();
  }

  char* sdpDescription = resultString;
  *env << "Opened URL \"" << streamURL << "\", returning a SDP description:\n" << sdpDescription << "\n";

  // Create a media session object from this SDP description:
  session = MediaSession::createNew(*env, sdpDescription);
  delete[] sdpDescription;
  if (session == NULL) {
    *env << "Failed to create a MediaSession object from the SDP description: " << env->getResultMsg() << "\n";
    shutdown();
  } else if (!session->hasSubsessions()) {
    *env << "This session has no media subsessions (i.e., \"m=\" lines)\n";
    shutdown();
  }

  // Then, setup the "RTPSource"s for the session:
  MediaSubsessionIterator iter(*session);
  MediaSubsession *subsession;
  Boolean madeProgress = False;
  char const* singleMediumToTest = singleMedium;
  while ((subsession = iter.next()) != NULL) {
    // If we've asked to receive only a single medium, then check this now:
    if (singleMediumToTest != NULL) {
      if (strcmp(subsession->mediumName(), singleMediumToTest) != 0) {
		  *env << "Ignoring \"" << subsession->mediumName()
			  << "/" << subsession->codecName()
			  << "\" subsession, because we've asked to receive a single " << singleMedium
			  << " session only\n";
	continue;
      } else {
	// Receive this subsession only
	singleMediumToTest = "xxxxx";
	    // this hack ensures that we get only 1 subsession of this type
      }
    }

    if (desiredPortNum != 0) {
      subsession->setClientPortNum(desiredPortNum);
      desiredPortNum += 2;
    }

    if (createReceivers) {
      if (!subsession->initiate(simpleRTPoffsetArg)) {
	*env << "Unable to create receiver for \"" << subsession->mediumName()
	     << "/" << subsession->codecName()
	     << "\" subsession: " << env->getResultMsg() << "\n";
      } else {
	*env << "Created receiver for \"" << subsession->mediumName()
	     << "/" << subsession->codecName()
	     << "\" subsession (client ports " << subsession->clientPortNum()
	     << "-" << subsession->clientPortNum()+1 << ")\n";
	madeProgress = True;
	
	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 - either if we were explicitly asked (using -B),
	  // or if the desired FileSink buffer size happens to be larger than the current OS socket buffer size.
	  // (The latter case is a heuristic, on the assumption that if the user asked for a large FileSink buffer size,
	  // then the input data rate may be large enough to justify increasing the OS socket buffer size also.)
	  int socketNum = subsession->rtpSource()->RTPgs()->socketNum();
	  unsigned curBufferSize = getReceiveBufferSize(*env, socketNum);
	  if (socketInputBufferSize > 0 || fileSinkBufferSize > curBufferSize) {
	    unsigned newBufferSize = socketInputBufferSize > 0 ? socketInputBufferSize : fileSinkBufferSize;
	    newBufferSize = setReceiveBufferTo(*env, socketNum, newBufferSize);
	    if (socketInputBufferSize > 0) { // The user explicitly asked for the new socket buffer size; announce it:
	      *env << "Changed socket receive buffer size for the \""
		   << subsession->mediumName()
		   << "/" << subsession->codecName()
		   << "\" subsession from "
		   << curBufferSize << " to "
		   << newBufferSize << " bytes\n";
	    }
	  }
	}
      }
    } else {
      if (subsession->clientPortNum() == 0) {
	*env << "No client port was specified for the \""
	     << subsession->mediumName()
	     << "/" << subsession->codecName()
	     << "\" subsession.  (Try adding the \"-p <portNum>\" option.)\n";
      } else {
		madeProgress = True;
      }
    }
  }
  if (!madeProgress) shutdown();

  // Perform additional 'setup' on each subsession, before playing them:
  setupStreams();
}
Example #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;
}
Example #12
0
int CMediaNet::MediaNet_Thread( void * pThisVoid )
{
	CMediaNet *pThis = ( CMediaNet* )pThisVoid;

	do 
	{
		// 开始初始化.
		pThis->SetRtspStatus( RTSPStatus_Init );

		// Begin by setting up our usage environment:
		TaskScheduler* scheduler = BasicTaskScheduler::createNew();
		env = BasicUsageEnvironment::createNew(*scheduler);

		progName = "M_CU";

		string strUrl = pThis->m_strRTSPUrlA;

		gettimeofday(&startTime, NULL);

		unsigned short desiredPortNum = 0;

		// unfortunately we can't use getopt() here, as Windoze doesn't have it

		// Create our client object:
		ourClient = createClient(*env, verbosityLevel, progName);
		if (ourClient == NULL) 
		{
			*env << "Failed to create " << clientProtocolName
				<< " client: " << env->getResultMsg() << "\n";

			pThis->SetRtspStatus( RTSPStatus_Error_Connect_Srv );
			break;
		}

		// 开始获取Opition.
		pThis->SetRtspStatus( RTSPStatus_Opitiion );
		// Begin by sending an "OPTIONS" command:
		char* optionsResponse
			= getOptionsResponse(ourClient, pThis->m_strRTSPUrlA.c_str(), username, password);

		if (optionsResponse == NULL) 
		{
			*env << clientProtocolName << " \"OPTIONS\" request failed: "
				<< env->getResultMsg() << "\n";

			pThis->SetRtspStatus( RTSPStatus_Error_Connect_Srv );
			break;
		} 
		else 
		{
			*env << clientProtocolName << " \"OPTIONS\" request returned: "
				<< optionsResponse << "\n";
		}
		if( optionsResponse )
		{
			delete[] optionsResponse;
		}
			

		// 开始获取Description.
		// Open the URL, to get a SDP description:
		pThis->SetRtspStatus( RTSPStatus_Description );
		char* sdpDescription
			= getSDPDescriptionFromURL(ourClient, pThis->m_strRTSPUrlA.c_str(), username, password,
			proxyServerName, proxyServerPortNum,
			desiredPortNum);
		if (sdpDescription == NULL) 
		{
			*env << "Failed to get a SDP description from URL \"" << pThis->m_strRTSPUrlA.c_str()
				<< "\": " << env->getResultMsg() << "\n";
			pThis->SetRtspStatus( RTSPStatus_Error_Connect_Srv );
			break;
		}

		*env << "Opened URL \"" << pThis->m_strRTSPUrlA.c_str()
			<< "\", returning a SDP description:\n" << sdpDescription << "\n";

		// Create a media session object from this SDP description:
		session = MediaSession::createNew(*env, sdpDescription);
		delete[] sdpDescription;
		if (session == NULL) 
		{
			*env << "Failed to create a MediaSession object from the SDP description: " << env->getResultMsg() << "\n";
			pThis->SetRtspStatus( RTSPStatus_Error_Connect_Srv );
			break;
		} 
		else if (!session->hasSubsessions()) 
		{
			*env << "This session has no media subsessions (i.e., \"m=\" lines)\n";
			pThis->SetRtspStatus( RTSPStatus_Error_Connect_Srv );
			break;
		}

		// Then, setup the "RTPSource"s for the session:
		MediaSubsessionIterator iter(*session);
		MediaSubsession *subsession;
		Boolean madeProgress = False;
		char const* singleMediumToTest = singleMedium;
		while ((subsession = iter.next()) != NULL) 
		{
			// If we've asked to receive only a single medium, then check this now:
			if (singleMediumToTest != NULL) 
			{
				if (strcmp(subsession->mediumName(), singleMediumToTest) != 0) 
				{
					*env << "Ignoring \"" << subsession->mediumName()
						<< "/" << subsession->codecName()
						<< "\" subsession, because we've asked to receive a single " << singleMedium
						<< " session only\n";
					continue;
				} 
				else 
				{
					// Receive this subsession only
					singleMediumToTest = "xxxxx";
					// this hack ensures that we get only 1 subsession of this type
				}
			}

			desiredPortNum = 0;
			if (desiredPortNum != 0) 
			{
				subsession->setClientPortNum(desiredPortNum);
				desiredPortNum += 2;
			}

			if (true) 
			{
				if (!subsession->initiate(simpleRTPoffsetArg)) 
				{
					*env << "Unable to create receiver for \"" << subsession->mediumName()
						<< "/" << subsession->codecName()
						<< "\" subsession: " << env->getResultMsg() << "\n";
				} 
				else 
				{
					*env << "Created receiver for \"" << subsession->mediumName()
						<< "/" << subsession->codecName()
						<< "\" subsession (client ports " << subsession->clientPortNum()
						<< "-" << subsession->clientPortNum()+1 << ")\n";
					madeProgress = True;

					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);

						if (socketInputBufferSize > 0) 
						{
							// Set the RTP source's input buffer size as specified:
							int socketNum
								= subsession->rtpSource()->RTPgs()->socketNum();
							unsigned curBufferSize
								= getReceiveBufferSize(*env, socketNum);
							unsigned newBufferSize
								= setReceiveBufferTo(*env, socketNum, socketInputBufferSize);
							*env << "Changed socket receive buffer size for the \""
								<< subsession->mediumName()
								<< "/" << subsession->codecName()
								<< "\" subsession from "
								<< curBufferSize << " to "
								<< newBufferSize << " bytes\n";
						}
					}
				}
			} 
			else 
			{
				mcu::tlog << _T( "Use port: " ) << (int)subsession->clientPortNum() << endl;
				if (subsession->clientPortNum() == 0) 
				{
					*env << "No client port was specified for the \""
						<< subsession->mediumName()
						<< "/" << subsession->codecName()
						<< "\" subsession.  (Try adding the \"-p <portNum>\" option.)\n";
				} 
				else 
				{
					madeProgress = True;
				}
			}
		}
		if (!madeProgress) 
			break;

		// Perform additional 'setup' on each subsession, before playing them:
		pThis->SetRtspStatus( RTSPStatus_Setup );
		unsigned nResponseCode = NULL;
		BOOL bSetupSuccess = setupStreams( &nResponseCode );
		if ( !bSetupSuccess )
		{
			// setup失败!
			if ( RTSPResp_Error_Server_Full == nResponseCode )
			{
				pThis->SetRtspStatus( RTSPStatus_Error_Server_Full );
			}
			else
			{
				pThis->SetRtspStatus( RTSPStatus_Idle );
			}
			break;
		}
		// Create output files:
		
		if ( true  ) 
		{
				// Create and start "FileSink"s for each subsession: 
				madeProgress = False;
				iter.reset();
				while ((subsession = iter.next()) != NULL) 
				{
					if (subsession->readSource() == NULL) continue; // was not initiated

					MediaSink *pDecodeSink = 0;
					if (strcmp(subsession->mediumName(), "video") == 0 )
					{
						int nBandWidth = subsession->GetBandWidth();

						if ( strcmp(subsession->codecName(), "MP4V-ES") == 0 )
						{
							CMpeg4StreamDecodeSink *pMsds = CMpeg4StreamDecodeSink::CreateNew( *env, 20000, nBandWidth );
							 pDecodeSink = pMsds;
							
						}
						else if ( strcmp( subsession->codecName(), "H264" ) == 0 )
						{
							 CH264StreamDecodeSink *pHsds = CH264StreamDecodeSink::CreateNew( *env, 20000, nBandWidth );
							 pDecodeSink = pHsds;
						}
						else
						{
							continue;
						}
					}				

					subsession->sink = pDecodeSink;
					if (subsession->sink == NULL) 
					{
						*env << "Failed to create CH264StreamDecodeSink \""  << "\n";
					} 


					subsession->sink->startPlaying(*(subsession->readSource()),
						subsessionAfterPlaying,
						subsession);

					// Also set a handler to be called if a RTCP "BYE" arrives
					// for this subsession:
					if (subsession->rtcpInstance() != NULL) 
					{
						subsession->rtcpInstance()->setByeHandler(subsessionByeHandler,
							subsession);
					}

					// 发送NAT探测包。
					unsigned char temp[112] = {0};
					temp[0] = 0x80;
					subsession->rtpSource()->RTPgs()->output( *env, 0,temp, 112 );

					madeProgress = True;
				}
			}


		// Finally, start playing each subsession, to start the data flow:
		pThis->SetRtspStatus( RTSPStatus_Play );
		startPlayingStreams();


		pThis->SetRtspStatus( RTSPStatus_Running );
		// 传入结束标志指针。 
		env->taskScheduler().doEventLoop( &pThis->m_runFlag ); 

		pThis->SetRtspStatus( RTSPStatus_Idle );

	} while(0);	

	return 0;
}
Example #13
0
extern "C" demuxer_t* demux_open_rtp(demuxer_t* demuxer) {
  struct MPOpts *opts = demuxer->opts;
  Boolean success = False;
  do {
    TaskScheduler* scheduler = BasicTaskScheduler::createNew();
    if (scheduler == NULL) break;
    UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);
    if (env == NULL) break;

    RTSPClient* rtspClient = NULL;
    SIPClient* sipClient = NULL;

    if (demuxer == NULL || demuxer->stream == NULL) break;  // shouldn't happen
    demuxer->stream->eof = 0; // just in case

    // Look at the stream's 'priv' field to see if we were initiated
    // via a SDP description:
    char* sdpDescription = (char*)(demuxer->stream->priv);
    if (sdpDescription == NULL) {
      // We weren't given a SDP description directly, so assume that
      // we were given a RTSP or SIP URL:
      char const* protocol = demuxer->stream->streaming_ctrl->url->protocol;
      char const* url = demuxer->stream->streaming_ctrl->url->url;
      extern int verbose;
      if (strcmp(protocol, "rtsp") == 0) {
	rtspClient = RTSPClient::createNew(*env, verbose, "MPlayer");
	if (rtspClient == NULL) {
	  fprintf(stderr, "Failed to create RTSP client: %s\n",
		  env->getResultMsg());
	  break;
	}
	sdpDescription = openURL_rtsp(rtspClient, url);
      } else { // SIP
	unsigned char desiredAudioType = 0; // PCMU (use 3 for GSM)
	sipClient = SIPClient::createNew(*env, desiredAudioType, NULL,
					 verbose, "MPlayer");
	if (sipClient == NULL) {
	  fprintf(stderr, "Failed to create SIP client: %s\n",
		  env->getResultMsg());
	  break;
	}
	sipClient->setClientStartPortNum(8000);
	sdpDescription = openURL_sip(sipClient, url);
      }

      if (sdpDescription == NULL) {
	fprintf(stderr, "Failed to get a SDP description from URL \"%s\": %s\n",
		url, env->getResultMsg());
	break;
      }
    }

    // Now that we have a SDP description, create a MediaSession from it:
    MediaSession* mediaSession = MediaSession::createNew(*env, sdpDescription);
    if (mediaSession == NULL) break;


    // Create a 'RTPState' structure containing the state that we just created,
    // and store it in the demuxer's 'priv' field, for future reference:
    RTPState* rtpState = new RTPState;
    rtpState->sdpDescription = sdpDescription;
    rtpState->rtspClient = rtspClient;
    rtpState->sipClient = sipClient;
    rtpState->mediaSession = mediaSession;
    rtpState->audioBufferQueue = rtpState->videoBufferQueue = NULL;
    rtpState->flags = 0;
    rtpState->firstSyncTime.tv_sec = rtpState->firstSyncTime.tv_usec = 0;
    demuxer->priv = rtpState;

    int audiofound = 0, videofound = 0;
    // Create RTP receivers (sources) for each subsession:
    MediaSubsessionIterator iter(*mediaSession);
    MediaSubsession* subsession;
    unsigned desiredReceiveBufferSize;
    while ((subsession = iter.next()) != NULL) {
      // Ignore any subsession that's not audio or video:
      if (strcmp(subsession->mediumName(), "audio") == 0) {
	if (audiofound) {
	  fprintf(stderr, "Additional subsession \"audio/%s\" skipped\n", subsession->codecName());
	  continue;
	}
	desiredReceiveBufferSize = 100000;
      } else if (strcmp(subsession->mediumName(), "video") == 0) {
	if (videofound) {
	  fprintf(stderr, "Additional subsession \"video/%s\" skipped\n", subsession->codecName());
	  continue;
	}
	desiredReceiveBufferSize = 2000000;
      } else {
	continue;
      }

      if (rtsp_port)
          subsession->setClientPortNum (rtsp_port);

      if (!subsession->initiate()) {
	fprintf(stderr, "Failed to initiate \"%s/%s\" RTP subsession: %s\n", subsession->mediumName(), subsession->codecName(), env->getResultMsg());
      } else {
	fprintf(stderr, "Initiated \"%s/%s\" RTP subsession on port %d\n", subsession->mediumName(), subsession->codecName(), subsession->clientPortNum());

	// Set the OS's socket receive buffer sufficiently large to avoid
	// incoming packets getting dropped between successive reads from this
	// subsession's demuxer.  Depending on the bitrate(s) that you expect,
	// you may wish to tweak the "desiredReceiveBufferSize" values above.
	int rtpSocketNum = subsession->rtpSource()->RTPgs()->socketNum();
	int receiveBufferSize
	  = increaseReceiveBufferTo(*env, rtpSocketNum,
				    desiredReceiveBufferSize);
	if (verbose > 0) {
	  fprintf(stderr, "Increased %s socket receive buffer to %d bytes \n",
		  subsession->mediumName(), receiveBufferSize);
	}

	if (rtspClient != NULL) {
	  // Issue a RTSP "SETUP" command on the chosen subsession:
	  if (!rtspClient->setupMediaSubsession(*subsession, False,
						rtsp_transport_tcp)) break;
	  if (!strcmp(subsession->mediumName(), "audio"))
	    audiofound = 1;
	  if (!strcmp(subsession->mediumName(), "video"))
            videofound = 1;
	}
      }
    }

    if (rtspClient != NULL) {
      // Issue a RTSP aggregate "PLAY" command on the whole session:
      if (!rtspClient->playMediaSession(*mediaSession)) break;
    } else if (sipClient != NULL) {
      sipClient->sendACK(); // to start the stream flowing
    }

    // Now that the session is ready to be read, do additional
    // MPlayer codec-specific initialization on each subsession:
    iter.reset();
    while ((subsession = iter.next()) != NULL) {
      if (subsession->readSource() == NULL) continue; // not reading this

      unsigned flags = 0;
      if (strcmp(subsession->mediumName(), "audio") == 0) {
	rtpState->audioBufferQueue
	  = new ReadBufferQueue(subsession, demuxer, "audio");
	rtpState->audioBufferQueue->otherQueue = &(rtpState->videoBufferQueue);
	rtpCodecInitialize_audio(demuxer, subsession, flags);
      } else if (strcmp(subsession->mediumName(), "video") == 0) {
	rtpState->videoBufferQueue
	  = new ReadBufferQueue(subsession, demuxer, "video");
	rtpState->videoBufferQueue->otherQueue = &(rtpState->audioBufferQueue);
	rtpCodecInitialize_video(demuxer, subsession, flags);
      }
      rtpState->flags |= flags;
    }
    success = True;
  } while (0);
  if (!success) return NULL; // an error occurred

  // Hack: If audio and video are demuxed together on a single RTP stream,
  // then create a new "demuxer_t" structure to allow the higher-level
  // code to recognize this:
  if (demux_is_multiplexed_rtp_stream(demuxer)) {
    stream_t* s = new_ds_stream(demuxer->video);
    demuxer_t* od = demux_open(opts, s, DEMUXER_TYPE_UNKNOWN,
			       opts->audio_id, opts->video_id, opts->sub_id,
                               NULL);
    demuxer = new_demuxers_demuxer(od, od, od);
  }

  return demuxer;
}
Example #14
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 #15
0
int main(int argc, char** argv) {
  // Begin by setting up our usage environment:
  TaskScheduler* scheduler = BasicTaskScheduler::createNew();
  env = BasicUsageEnvironment::createNew(*scheduler);

  progName = argv[0];

  gettimeofday(&startTime, NULL);

#ifdef USE_SIGNALS
  // Allow ourselves to be shut down gracefully by a SIGHUP or a SIGUSR1:
  signal(SIGHUP, signalHandlerShutdown);
  signal(SIGUSR1, signalHandlerShutdown);
#endif

  unsigned short desiredPortNum = 0;

  // unfortunately we can't use getopt() here, as Windoze doesn't have it
  while (argc > 2) {
    char* const opt = argv[1];
    if (opt[0] != '-') usage();
    switch (opt[1]) {

    case 'p': { // specify start port number
      int portArg;
      if (sscanf(argv[2], "%d", &portArg) != 1) {
	usage();
      }
      if (portArg <= 0 || portArg >= 65536 || portArg&1) {
	*env << "bad port number: " << portArg
		<< " (must be even, and in the range (0,65536))\n";
	usage();
      }
      desiredPortNum = (unsigned short)portArg;
      ++argv; --argc;
      break;
    }

    case 'r': { // do not receive data (instead, just 'play' the stream(s))
      createReceivers = False;
      break;
    }

    case 'q': { // output a QuickTime file (to stdout)
      outputQuickTimeFile = True;
      break;
    }

    case '4': { // output a 'mp4'-format file (to stdout)
      outputQuickTimeFile = True;
      generateMP4Format = True;
      break;
    }

    case 'i': { // output an AVI file (to stdout)
      outputAVIFile = True;
      break;
    }

    case 'I': { // specify input interface...
      NetAddressList addresses(argv[2]);
      if (addresses.numAddresses() == 0) {
	*env << "Failed to find network address for \"" << argv[2] << "\"";
	break;
      }
      ReceivingInterfaceAddr = *(unsigned*)(addresses.firstAddress()->data());
      ++argv; --argc;
      break;
    }

    case 'a': { // receive/record an audio stream only
      audioOnly = True;
      singleMedium = "audio";
      break;
    }

    case 'v': { // receive/record a video stream only
      videoOnly = True;
      singleMedium = "video";
      break;
    }

    case 'V': { // disable verbose output
      verbosityLevel = 0;
      break;
    }

    case 'd': { // specify duration, or how much to delay after end time
      float arg;
      if (sscanf(argv[2], "%g", &arg) != 1) {
	usage();
      }
      if (argv[2][0] == '-') { // not "arg<0", in case argv[2] was "-0"
	// a 'negative' argument was specified; use this for "durationSlop":
	duration = 0; // use whatever's in the SDP
	durationSlop = -arg;
      } else {
	duration = arg;
	durationSlop = 0;
      }
      ++argv; --argc;
      break;
    }

    case 'D': { // specify maximum number of seconds to wait for packets:
      if (sscanf(argv[2], "%u", &interPacketGapMaxTime) != 1) {
	usage();
      }
      ++argv; --argc;
      break;
    }

    case 'c': { // play continuously
      playContinuously = True;
      break;
    }

    case 'S': { // specify an offset to use with "SimpleRTPSource"s
      if (sscanf(argv[2], "%d", &simpleRTPoffsetArg) != 1) {
	usage();
      }
      if (simpleRTPoffsetArg < 0) {
	*env << "offset argument to \"-S\" must be >= 0\n";
	usage();
      }
      ++argv; --argc;
      break;
    }

    case 'O': { // Don't send an "OPTIONS" request before "DESCRIBE"
      sendOptionsRequest = False;
      break;
    }

    case 'o': { // Send only the "OPTIONS" request to the server
      sendOptionsRequestOnly = True;
      break;
    }

    case 'm': { // output multiple files - one for each frame
      oneFilePerFrame = True;
      break;
    }

    case 'n': { // notify the user when the first data packet arrives
      notifyOnPacketArrival = True;
      break;
    }

    case 't': {
      // stream RTP and RTCP over the TCP 'control' connection
      if (controlConnectionUsesTCP) {
	streamUsingTCP = True;
      } else {
	usage();
      }
      break;
    }

    case 'T': {
      // stream RTP and RTCP over a HTTP connection
      if (controlConnectionUsesTCP) {
	if (argc > 3 && argv[2][0] != '-') {
	  // The next argument is the HTTP server port number:
	  if (sscanf(argv[2], "%hu", &tunnelOverHTTPPortNum) == 1
	      && tunnelOverHTTPPortNum > 0) {
	    ++argv; --argc;
	    break;
	  }
	}
      }

      // If we get here, the option was specified incorrectly:
      usage();
      break;
    }

    case 'u': { // specify a username and password
      username = argv[2];
      password = argv[3];
      argv+=2; argc-=2;
      if (allowProxyServers && argc > 3 && argv[2][0] != '-') {
	// The next argument is the name of a proxy server:
	proxyServerName = argv[2];
	++argv; --argc;

	if (argc > 3 && argv[2][0] != '-') {
	  // The next argument is the proxy server port number:
	  if (sscanf(argv[2], "%hu", &proxyServerPortNum) != 1) {
	    usage();
	  }
	  ++argv; --argc;
	}
      }
      break;
    }

    case 'A': { // specify a desired audio RTP payload format
      unsigned formatArg;
      if (sscanf(argv[2], "%u", &formatArg) != 1
	  || formatArg >= 96) {
	usage();
      }
      desiredAudioRTPPayloadFormat = (unsigned char)formatArg;
      ++argv; --argc;
      break;
    }

    case 'M': { // specify a MIME subtype for a dynamic RTP payload type
      mimeSubtype = argv[2];
      if (desiredAudioRTPPayloadFormat==0) desiredAudioRTPPayloadFormat =96;
      ++argv; --argc;
      break;
    }

    case 'w': { // specify a width (pixels) for an output QuickTime or AVI movie
      if (sscanf(argv[2], "%hu", &movieWidth) != 1) {
	usage();
      }
      movieWidthOptionSet = True;
      ++argv; --argc;
      break;
    }

    case 'h': { // specify a height (pixels) for an output QuickTime or AVI movie
      if (sscanf(argv[2], "%hu", &movieHeight) != 1) {
	usage();
      }
      movieHeightOptionSet = True;
      ++argv; --argc;
      break;
    }

    case 'f': { // specify a frame rate (per second) for an output QT or AVI movie
      if (sscanf(argv[2], "%u", &movieFPS) != 1) {
	usage();
      }
      movieFPSOptionSet = True;
      ++argv; --argc;
      break;
    }

    case 'F': { // specify a prefix for the audio and video output files
      fileNamePrefix = argv[2];
      ++argv; --argc;
      break;
    }

    case 'b': { // specify the size of buffers for "FileSink"s
      if (sscanf(argv[2], "%u", &fileSinkBufferSize) != 1) {
	usage();
      }
      ++argv; --argc;
      break;
    }

    case 'B': { // specify the size of input socket buffers
      if (sscanf(argv[2], "%u", &socketInputBufferSize) != 1) {
	usage();
      }
      ++argv; --argc;
      break;
    }

    // Note: The following option is deprecated, and may someday be removed:
    case 'l': { // try to compensate for packet loss by repeating frames
      packetLossCompensate = True;
      break;
    }

    case 'y': { // synchronize audio and video streams
      syncStreams = True;
      break;
    }

    case 'H': { // generate hint tracks (as well as the regular data tracks)
      generateHintTracks = True;
      break;
    }

    case 'Q': { // output QOS measurements
      qosMeasurementIntervalMS = 1000; // default: 1 second

      if (argc > 3 && argv[2][0] != '-') {
	// The next argument is the measurement interval,
	// in multiples of 100 ms
	if (sscanf(argv[2], "%u", &qosMeasurementIntervalMS) != 1) {
	  usage();
	}
	qosMeasurementIntervalMS *= 100;
	++argv; --argc;
      }
      break;
    }

    case 's': { // specify initial seek time (trick play)
      double arg;
      if (sscanf(argv[2], "%lg", &arg) != 1 || arg < 0) {
	usage();
      }
      initialSeekTime = arg;
      ++argv; --argc;
      break;
    }

    case 'z': { // scale (trick play)
      float arg;
      if (sscanf(argv[2], "%g", &arg) != 1 || arg == 0.0f) {
	usage();
      }
      scale = arg;
      ++argv; --argc;
      break;
    }

    default: {
      usage();
      break;
    }
    }

    ++argv; --argc;
  }
  if (argc != 2) usage();
  if (outputQuickTimeFile && outputAVIFile) {
    *env << "The -i and -q (or -4) flags cannot both be used!\n";
    usage();
  }
  Boolean outputCompositeFile = outputQuickTimeFile || outputAVIFile;
  if (!createReceivers && outputCompositeFile) {
    *env << "The -r and -q (or -4 or -i) flags cannot both be used!\n";
    usage();
  }
  if (outputCompositeFile && !movieWidthOptionSet) {
    *env << "Warning: The -q, -4 or -i option was used, but not -w.  Assuming a video width of "
	 << movieWidth << " pixels\n";
  }
  if (outputCompositeFile && !movieHeightOptionSet) {
    *env << "Warning: The -q, -4 or -i option was used, but not -h.  Assuming a video height of "
	 << movieHeight << " pixels\n";
  }
  if (outputCompositeFile && !movieFPSOptionSet) {
    *env << "Warning: The -q, -4 or -i option was used, but not -f.  Assuming a video frame rate of "
	 << movieFPS << " frames-per-second\n";
  }
  if (audioOnly && videoOnly) {
    *env << "The -a and -v flags cannot both be used!\n";
    usage();
  }
  if (sendOptionsRequestOnly && !sendOptionsRequest) {
    *env << "The -o and -O flags cannot both be used!\n";
    usage();
  }
  if (tunnelOverHTTPPortNum > 0) {
    if (streamUsingTCP) {
      *env << "The -t and -T flags cannot both be used!\n";
      usage();
    } else {
      streamUsingTCP = True;
    }
  }
  if (!createReceivers && notifyOnPacketArrival) {
    *env << "Warning: Because we're not receiving stream data, the -n flag has no effect\n";
  }
  if (durationSlop < 0) {
    // This parameter wasn't set, so use a default value.
    // If we're measuring QOS stats, then don't add any slop, to avoid
    // having 'empty' measurement intervals at the end.
    durationSlop = qosMeasurementIntervalMS > 0 ? 0.0 : 5.0;
  }

  char* url = argv[1];

  // Create our client object:
  ourClient = createClient(*env, verbosityLevel, progName);
  if (ourClient == NULL) {
    *env << "Failed to create " << clientProtocolName
		<< " client: " << env->getResultMsg() << "\n";
    shutdown();
  }

  if (sendOptionsRequest) {
    // Begin by sending an "OPTIONS" command:
    char* optionsResponse
      = getOptionsResponse(ourClient, url, username, password);
    if (sendOptionsRequestOnly) {
      if (optionsResponse == NULL) {
	*env << clientProtocolName << " \"OPTIONS\" request failed: "
	     << env->getResultMsg() << "\n";
      } else {
	*env << clientProtocolName << " \"OPTIONS\" request returned: "
	     << optionsResponse << "\n";
      }
      shutdown();
    }
    delete[] optionsResponse;
  }

  // Open the URL, to get a SDP description:
  char* sdpDescription
    = getSDPDescriptionFromURL(ourClient, url, username, password,
			       proxyServerName, proxyServerPortNum,
			       desiredPortNum);
  if (sdpDescription == NULL) {
    *env << "Failed to get a SDP description from URL \"" << url
		<< "\": " << env->getResultMsg() << "\n";
    shutdown();
  }

  *env << "Opened URL \"" << url
	  << "\", returning a SDP description:\n" << sdpDescription << "\n";

  // Create a media session object from this SDP description:
  session = MediaSession::createNew(*env, sdpDescription);
  delete[] sdpDescription;
  if (session == NULL) {
    *env << "Failed to create a MediaSession object from the SDP description: " << env->getResultMsg() << "\n";
    shutdown();
  } else if (!session->hasSubsessions()) {
    *env << "This session has no media subsessions (i.e., \"m=\" lines)\n";
    shutdown();
  }

  // Then, setup the "RTPSource"s for the session:
  MediaSubsessionIterator iter(*session);
  MediaSubsession *subsession;
  Boolean madeProgress = False;
  char const* singleMediumToTest = singleMedium;
  while ((subsession = iter.next()) != NULL) {
    // If we've asked to receive only a single medium, then check this now:
    if (singleMediumToTest != NULL) {
      if (strcmp(subsession->mediumName(), singleMediumToTest) != 0) {
		  *env << "Ignoring \"" << subsession->mediumName()
			  << "/" << subsession->codecName()
			  << "\" subsession, because we've asked to receive a single " << singleMedium
			  << " session only\n";
	continue;
      } else {
	// Receive this subsession only
	singleMediumToTest = "xxxxx";
	    // this hack ensures that we get only 1 subsession of this type
      }
    }

    if (desiredPortNum != 0) {
      subsession->setClientPortNum(desiredPortNum);
      desiredPortNum += 2;
    }

    if (createReceivers) {
      if (!subsession->initiate(simpleRTPoffsetArg)) {
	*env << "Unable to create receiver for \"" << subsession->mediumName()
	     << "/" << subsession->codecName()
	     << "\" subsession: " << env->getResultMsg() << "\n";
      } else {
	*env << "Created receiver for \"" << subsession->mediumName()
	     << "/" << subsession->codecName()
	     << "\" subsession (client ports " << subsession->clientPortNum()
	     << "-" << subsession->clientPortNum()+1 << ")\n";
	madeProgress = True;
	
	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 - either if we were explicitly asked (using -B),
	  // or if the desired FileSink buffer size happens to be larger than the current OS socket buffer size.
	  // (The latter case is a heuristic, on the assumption that if the user asked for a large FileSink buffer size,
	  // then the input data rate may be large enough to justify increasing the OS socket buffer size also.)
	  int socketNum = subsession->rtpSource()->RTPgs()->socketNum();
	  unsigned curBufferSize = getReceiveBufferSize(*env, socketNum);
	  if (socketInputBufferSize > 0 || fileSinkBufferSize > curBufferSize) {
	    unsigned newBufferSize = socketInputBufferSize > 0 ? socketInputBufferSize : fileSinkBufferSize;
	    newBufferSize = setReceiveBufferTo(*env, socketNum, newBufferSize);
	    if (socketInputBufferSize > 0) { // The user explicitly asked for the new socket buffer size; announce it:
	      *env << "Changed socket receive buffer size for the \""
		   << subsession->mediumName()
		   << "/" << subsession->codecName()
		   << "\" subsession from "
		   << curBufferSize << " to "
		   << newBufferSize << " bytes\n";
	    }
	  }
	}
      }
    } else {
      if (subsession->clientPortNum() == 0) {
	*env << "No client port was specified for the \""
	     << subsession->mediumName()
	     << "/" << subsession->codecName()
	     << "\" subsession.  (Try adding the \"-p <portNum>\" option.)\n";
      } else {
		madeProgress = True;
      }
    }
  }
  if (!madeProgress) shutdown();

  // Perform additional 'setup' on each subsession, before playing them:
  setupStreams();

  // Create output files:
  if (createReceivers) {
    if (outputQuickTimeFile) {
      // Create a "QuickTimeFileSink", to write to 'stdout':
      qtOut = QuickTimeFileSink::createNew(*env, *session, "stdout",
					   fileSinkBufferSize,
					   movieWidth, movieHeight,
					   movieFPS,
					   packetLossCompensate,
					   syncStreams,
					   generateHintTracks,
					   generateMP4Format);
      if (qtOut == NULL) {
	*env << "Failed to create QuickTime file sink for stdout: " << env->getResultMsg();
	shutdown();
      }

      qtOut->startPlaying(sessionAfterPlaying, NULL);
    } else if (outputAVIFile) {
      // Create an "AVIFileSink", to write to 'stdout':
      aviOut = AVIFileSink::createNew(*env, *session, "stdout",
				      fileSinkBufferSize,
				      movieWidth, movieHeight,
				      movieFPS,
				      packetLossCompensate);
      if (aviOut == NULL) {
	*env << "Failed to create AVI file sink for stdout: " << env->getResultMsg();
	shutdown();
      }

      aviOut->startPlaying(sessionAfterPlaying, NULL);
    } else {
      // Create and start "FileSink"s for each subsession:
      madeProgress = False;
      iter.reset();
      while ((subsession = iter.next()) != NULL) {
	if (subsession->readSource() == NULL) continue; // was not initiated

	// Create an output file for each desired stream:
	char outFileName[1000];
	if (singleMedium == NULL) {
	  // Output file name is
	  //     "<filename-prefix><medium_name>-<codec_name>-<counter>"
	  static unsigned streamCounter = 0;
	  snprintf(outFileName, sizeof outFileName, "%s%s-%s-%d",
		   fileNamePrefix, subsession->mediumName(),
		   subsession->codecName(), ++streamCounter);
	} else {
	  sprintf(outFileName, "stdout");
	}
	FileSink* fileSink;
	if (strcmp(subsession->mediumName(), "audio") == 0 &&
	    (strcmp(subsession->codecName(), "AMR") == 0 ||
	     strcmp(subsession->codecName(), "AMR-WB") == 0)) {
	  // For AMR audio streams, we use a special sink that inserts AMR frame hdrs:
	  fileSink = AMRAudioFileSink::createNew(*env, outFileName,
						 fileSinkBufferSize, oneFilePerFrame);
	} else if (strcmp(subsession->mediumName(), "video") == 0 &&
	    (strcmp(subsession->codecName(), "H264") == 0)) {
	  // For H.264 video stream, we use a special sink that insert start_codes:
	  fileSink = H264VideoFileSink::createNew(*env, outFileName,
						 fileSinkBufferSize, oneFilePerFrame);
	} else {
	  // Normal case:
	  fileSink = FileSink::createNew(*env, outFileName,
					 fileSinkBufferSize, oneFilePerFrame);
	}
	subsession->sink = fileSink;
	if (subsession->sink == NULL) {
	  *env << "Failed to create FileSink for \"" << outFileName
		  << "\": " << env->getResultMsg() << "\n";
	} else {
	  if (singleMedium == NULL) {
	    *env << "Created output file: \"" << outFileName << "\"\n";
	  } else {
	    *env << "Outputting data from the \"" << subsession->mediumName()
			<< "/" << subsession->codecName()
			<< "\" subsession to 'stdout'\n";
	  }

	  if (strcmp(subsession->mediumName(), "video") == 0 &&
	      strcmp(subsession->codecName(), "MP4V-ES") == 0 &&
	      subsession->fmtp_config() != NULL) {
	    // For MPEG-4 video RTP streams, the 'config' information
	    // from the SDP description contains useful VOL etc. headers.
	    // Insert this data at the front of the output file:
	    unsigned configLen;
	    unsigned char* configData
	      = parseGeneralConfigStr(subsession->fmtp_config(), configLen);
	    struct timeval timeNow;
	    gettimeofday(&timeNow, NULL);
	    fileSink->addData(configData, configLen, timeNow);
	    delete[] configData;
	  }

	  subsession->sink->startPlaying(*(subsession->readSource()),
					 subsessionAfterPlaying,
					 subsession);

	  // Also set a handler to be called if a RTCP "BYE" arrives
	  // for this subsession:
	  if (subsession->rtcpInstance() != NULL) {
	    subsession->rtcpInstance()->setByeHandler(subsessionByeHandler,
						      subsession);
	  }

	  madeProgress = True;
	}
      }
      if (!madeProgress) shutdown();
    }
  }

  // Finally, start playing each subsession, to start the data flow:

  startPlayingStreams();

  env->taskScheduler().doEventLoop(); // does not return

  return 0; // only to prevent compiler warning
}
Example #16
0
bool CRTSPClient::OpenStream(char* url)
{
  XBMC->Log(LOG_DEBUG, "CRTSPClient::OpenStream()");
  m_session=NULL;
  
  strcpy(m_url,url);
  // Open the URL, to get a SDP description: 
  char* sdpDescription= getSDPDescriptionFromURL(m_ourClient, url, ""/*username*/, ""/*password*/,""/*proxyServerName*/, 0/*proxyServerPortNum*/,1234/*desiredPortNum*/);
  if (sdpDescription == NULL) 
  {
    XBMC->Log(LOG_DEBUG, "Failed to get a SDP description from URL %s %s",url ,m_env->getResultMsg() );
    shutdown();
    return false;
  }
  XBMC->Log(LOG_DEBUG, "Opened URL %s %s",url,sdpDescription);

  char* range=strstr(sdpDescription,"a=range:npt=");
  if (range!=NULL)
  {
    char *pStart = range+strlen("a=range:npt=");
    char *pEnd = strstr(range,"-") ;
    if (pEnd!=NULL)
    {
      pEnd++ ;
      double Start=atof(pStart) ;
      double End=atof(pEnd) ;

      XBMC->Log(LOG_DEBUG, "rangestart:%f rangeend:%f", Start,End);
      m_duration=(long) ((End-Start)*1000.0);
    }
  }
  // Create a media session object from this SDP description:
  m_session = MediaSession::createNew(*m_env, sdpDescription);
  delete[] sdpDescription;
  if (m_session == NULL) 
  {
    XBMC->Log(LOG_DEBUG, "Failed to create a MediaSession object from the SDP description:%s ",m_env->getResultMsg());
    shutdown();
    return false;
  } 
  else if (!m_session->hasSubsessions()) 
  {
    XBMC->Log(LOG_DEBUG, "This session has no media subsessions");
    shutdown();
    return false;
  }

  // Then, setup the "RTPSource"s for the session:
  MediaSubsessionIterator iter(*m_session);
  MediaSubsession *subsession;
  Boolean madeProgress = False;
  char const* singleMediumToTest = singleMedium;
  while ((subsession = iter.next()) != NULL) 
  {
    // If we've asked to receive only a single medium, then check this now:
    if (singleMediumToTest != NULL) 
    {
      if (strcmp(subsession->mediumName(), singleMediumToTest) != 0) 
      {
        XBMC->Log(LOG_DEBUG, "Ignoring %s %s %s" , subsession->mediumName(),subsession->codecName(),singleMedium);
        continue;
      } 
      else 
      {
        // Receive this subsession only
        singleMediumToTest = "xxxxx";
        // this hack ensures that we get only 1 subsession of this type
      }
    }
    if (desiredPortNum != 0) 
    {
      subsession->setClientPortNum(desiredPortNum);
      desiredPortNum += 2;
    }

    if (createReceivers) 
    {
      if (!subsession->initiate(simpleRTPoffsetArg)) 
      {
        XBMC->Log(LOG_DEBUG, "Unable to create receiver for %s %s %s" ,subsession->mediumName(),subsession->codecName(),m_env->getResultMsg());
      } 
      else 
      {
        XBMC->Log(LOG_DEBUG, "Created receiver for type=%s codec=%s ports: %d %d " ,subsession->mediumName(),subsession->codecName(),subsession->clientPortNum(),subsession->clientPortNum()+1 );
        madeProgress = True;

        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:
          
          int socketNum= subsession->rtpSource()->RTPgs()->socketNum();
          XBMC->Log(LOG_DEBUG, "rtsp:increaseReceiveBufferTo to 2000000 for s:%d",socketNum);
          increaseReceiveBufferTo( *m_env, socketNum, 2000000 );

          unsigned const thresh = 1000000; // 1 second 
          subsession->rtpSource()->setPacketReorderingThresholdTime(thresh);

          if (socketInputBufferSize > 0) 
          {
            // Set the RTP source's input buffer size as specified:
            int socketNum= subsession->rtpSource()->RTPgs()->socketNum();
            unsigned curBufferSize= getReceiveBufferSize(*m_env, socketNum);
            unsigned newBufferSize= setReceiveBufferTo(*m_env, socketNum, socketInputBufferSize);
            XBMC->Log(LOG_DEBUG,  "Changed socket receive buffer size for the %s %s %d %d",
            subsession->mediumName(),subsession->codecName(),curBufferSize,newBufferSize);
          }
        }
      }
    } 
    else 
    {
      if (subsession->clientPortNum() == 0) 
      {
        XBMC->Log(LOG_DEBUG, "No client port was specified for the %s %s",subsession->mediumName(),subsession->codecName());
      } 
      else 
      {
        madeProgress = True;
      }
    }
  }
  if (!madeProgress) 
  {
    shutdown();
    return false;
  }
  
  // Perform additional 'setup' on each subsession, before playing them:
  if (!setupStreams())
  {
    return false;
  }

  // Create output files:
  // Create and start "FileSink"s for each subsession:
  madeProgress = False;
  iter.reset();
  while ((subsession = iter.next()) != NULL) 
  {
    if (subsession->readSource() == NULL) continue; // was not initiated
    
    // Mediaportal:
    CMemorySink* fileSink= CMemorySink::createNew(*m_env, *m_buffer, fileSinkBufferSize);
    // XBMC test via file:
    //FileSink* fileSink = FileSink::createNew(*m_env, m_outFileName, fileSinkBufferSize, false); //oneFilePerFrame

    subsession->sink = fileSink;
    if (subsession->sink == NULL) 
    {
      XBMC->Log(LOG_DEBUG, "Failed to create FileSink %s",m_env->getResultMsg());
      shutdown();
      return false;
    } 
    XBMC->Log(LOG_DEBUG, "Created output sink: %s", m_outFileName);
    subsession->sink->startPlaying(*(subsession->readSource()),my_subsessionAfterPlaying,subsession);
    
    // Also set a handler to be called if a RTCP "BYE" arrives
    // for this subsession:
    if (subsession->rtcpInstance() != NULL) 
    {
      subsession->rtcpInstance()->setByeHandler(my_subsessionByeHandler,subsession);
    }
    madeProgress = True;
  }

  return true;
}
ProxyServerMediaSubsession::ProxyServerMediaSubsession(MediaSubsession& mediaSubsession)
  : OnDemandServerMediaSubsession(mediaSubsession.parentSession().envir(), True/*reuseFirstSource*/),
    fClientMediaSubsession(mediaSubsession), fCodecName(strDup(mediaSubsession.codecName())),
    fNext(NULL), fHaveSetupStream(False) {
}
bool MtkRTSPClient::handSetup(char* resultString)
{
	CHECK_NULL_COND(session, false); 
	CHECK_NULL_COND(rtsp::env, false);

	bool bSuccess = false;
	
	// Then, setup the "RTPSource"s for the session:
	MediaSubsessionIterator iter(*(session));
	MediaSubsession *subsession = NULL;
	while ((subsession = iter.next()) != NULL) 
	{					
		if (subsession->readSource() == NULL) 
		{
			LOG_ERR("warning");
			continue; // was not initiated
		}

		if (subsession->sink != NULL)/*already be set*/
		{
			continue;
		}

		unsigned int type = getBufType(subsession);
		if (type == 0)
		{
			LOG_ERR("error type=%d", type);
			continue;
		}
		
		{
			iSetupCount--;
			/*set mediay info*/
			setMediaInfo(subsession, type);
		}

		CmpbSink *sink = NULL;
		if ((type != mediatype_audio) && (strcmp(subsession->codecName(), "H264") == 0))
		{
			sink = CmpbH264Sink::createNew(*env, *subsession, type, fileSinkBufferSize);
		}
        else if ((type == mediatype_audio) && 
                    ((stMediaInfo.audioCodec == MEDIACODEC_AC3) || 
                     (stMediaInfo.audioCodec == MEDIACODEC_EAC3) ||
                     (stMediaInfo.audioCodec == MEDIACODEC_MPEG4_GENERIC)))
		{
			sink = CmpbAACSink::createNew(*env, *subsession, type, fileSinkBufferSize);
		}
        else if ((type == mediatype_audio) && (stMediaInfo.audioCodec == MEDIACODEC_MP4A_LATM))
		{
			sink = CmpbLATMSink::createNew(*env, *subsession, type, fileSinkBufferSize);
		}
		else
		{
			sink = CmpbSink::createNew(*env, *subsession, type, fileSinkBufferSize);
		}
		subsession->sink = sink;
		if (subsession->sink == NULL) 
		{
			LOG_ERR("error!"); 
		} 
		else 
		{		
#if 0 /*this should be remove to cmpb sink*/           
			if ((type != mediatype_audio) && (strcmp(subsession->codecName(), "MP4V-ES") == 0)
				&& (subsession->fmtp_config() != NULL)) 
			{
			    // For MPEG-4 video RTP streams, the 'config' information
			    // from the SDP description contains useful VOL etc. headers.
			    // Insert this data at the front of the output file:
			    unsigned configLen;
			    unsigned char* configData
			      = parseGeneralConfigStr(subsession->fmtp_config(), configLen);
			    struct timeval timeNow;
			    gettimeofday(&timeNow, NULL);
			    sink->sendData(configData, configLen, timeNow);
			    delete[] configData;
		  	}
#endif			
			subsession->sink->startPlaying(*(subsession->readSource()),
												subsessionAfterPlaying,
													subsession);
			// Also set a handler to be called if a RTCP "BYE" arrives
			// for this subsession:
			if (subsession->rtcpInstance() != NULL) 
			{
				subsession->rtcpInstance()->setByeHandler(subsessionAfterPlaying, subsession);
			}

			bSuccess = true;
		}

		break;

	}

	if (iSetupCount == 0)
	{
		mediaInfoReady(); 
	}

	return bSuccess ;
}
void continueAfterDESCRIBE(RTSPClient*, int resultCode, char* resultString) {
    ALOG(TX_LOG_INFO, TAG,"continueAfterDESCRIBE\n");
  if (resultCode != 0) {
      ALOG(TX_LOG_INFO, TAG,"Failed to get a SDP description for the URL %s, %s\n", streamURL, resultString);
    *env << "Failed to get a SDP description for the URL \"" << streamURL << "\": " << resultString << "\n";
    delete[] resultString;
    shutdown();
  }

  char* sdpDescription = resultString;
  *env << "Opened URL \"" << streamURL << "\", returning a SDP description:\n" << sdpDescription << "\n";

  // Create a media session object from this SDP description:
  session = MediaSession::createNew(*env, sdpDescription);
  delete[] sdpDescription;
  if (session == NULL) {
      ALOG(TX_LOG_INFO, TAG, "Failed to create a MediaSession object from the SDP description:");
    shutdown();
  } else if (!session->hasSubsessions()) {
      ALOG(TX_LOG_INFO, TAG, "This session has no media subsessions (i.e., no \"m=\" lines)\n");
    shutdown();
  }

  // Then, setup the "RTPSource"s for the session:
  MediaSubsessionIterator iter(*session);
  MediaSubsession *subsession;
  Boolean madeProgress = False;
  char const* singleMediumToTest = singleMedium;
  while ((subsession = iter.next()) != NULL) {
    // If we've asked to receive only a single medium, then check this now:
    if (singleMediumToTest != NULL) {
      if (strcmp(subsession->mediumName(), singleMediumToTest) != 0) {
          ALOG(TX_LOG_INFO, TAG, "Ignoring codecName = %s\n", subsession->codecName());
		  *env << "Ignoring \"" << subsession->mediumName()
			  << "/" << subsession->codecName()
			  << "\" subsession, because we've asked to receive a single " << singleMedium
			  << " session only\n";
	continue;
      } else {
	// Receive this subsession only
	singleMediumToTest = "xxxxx";
	    // this hack ensures that we get only 1 subsession of this type
      }
    }

    if (desiredPortNum != 0) {
      subsession->setClientPortNum(desiredPortNum);
      desiredPortNum += 2;
    }

    if (createReceivers) {
      if (!subsession->initiate(simpleRTPoffsetArg)) {
          ALOG(TX_LOG_INFO, TAG, "Unable to create receiver for\n");
	*env << "Unable to create receiver for \"" << subsession->mediumName()
	     << "/" << subsession->codecName()
	     << "\" subsession: " << env->getResultMsg() << "\n";
      } else {
      ALOG(TX_LOG_INFO, TAG, "Created receiver\n");
	*env << "Created receiver for \"" << subsession->mediumName()
	     << "/" << subsession->codecName() << "\" subsession (";
	if (subsession->rtcpIsMuxed()) {
        ALOG(TX_LOG_INFO, TAG, "subsession->rtcpIsMuxed()  client port = %d\n", subsession->clientPortNum());
	  *env << "client port " << subsession->clientPortNum();
	} else {
        ALOG(TX_LOG_INFO, TAG, "subsession->rtcpIsMuxed(),,,,else");
	  *env << "client ports " << subsession->clientPortNum()
	       << "-" << subsession->clientPortNum()+1;
	}
	*env << ")\n";
	madeProgress = True;

	if (subsession->rtpSource() != NULL) {
	  unsigned const thresh = 1000000; // 1 second
	  subsession->rtpSource()->setPacketReorderingThresholdTime(thresh);

	  int socketNum = subsession->rtpSource()->RTPgs()->socketNum();
	  unsigned curBufferSize = getReceiveBufferSize(*env, socketNum);
	  if (socketInputBufferSize > 0 || fileSinkBufferSize > curBufferSize) {
	    unsigned newBufferSize = socketInputBufferSize > 0 ? socketInputBufferSize : fileSinkBufferSize;
	    newBufferSize = setReceiveBufferTo(*env, socketNum, newBufferSize);
	    if (socketInputBufferSize > 0) {
            ALOG(TX_LOG_INFO, TAG, "socketInputBufferSize > 0  Changed socket receive buffer size for the\n");
	      *env << "Changed socket receive buffer size for the \""
		   << subsession->mediumName()
		   << "/" << subsession->codecName()
		   << "\" subsession from "
		   << curBufferSize << " to "
		   << newBufferSize << " bytes\n";
	    }
	  }
	}
      }
    } else {
          ALOG(TX_LOG_INFO, TAG, "socketInputBufferSize > 0=====else\n");
      if (subsession->clientPortNum() == 0) {
	*env << "No client port was specified for the \""
	     << subsession->mediumName()
	     << "/" << subsession->codecName()
	     << "\" subsession.  (Try adding the \"-p <portNum>\" option.)\n";
      } else {
		madeProgress = True;
      }
    }
  }
  if (!madeProgress)
      {
          ALOG(TX_LOG_INFO, TAG,"(!madeProgress)");
          shutdown();
      }
  // Perform additional 'setup' on each subsession, before playing them:
  setupStreams();
}