Boolean AVIFileSink::continuePlaying() { // Run through each of our input session's 'subsessions', // asking for a frame from each one: Boolean haveActiveSubsessions = False; MediaSubsessionIterator iter(fInputSession); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { FramedSource* subsessionSource = subsession->readSource(); if (subsessionSource == NULL) continue; if (subsessionSource->isCurrentlyAwaitingData()) continue; AVISubsessionIOState* ioState = (AVISubsessionIOState*)(subsession->miscPtr); if (ioState == NULL) continue; haveActiveSubsessions = True; unsigned char* toPtr = ioState->fBuffer->dataEnd(); unsigned toSize = ioState->fBuffer->bytesAvailable(); subsessionSource->getNextFrame(toPtr, toSize, afterGettingFrame, ioState, onSourceClosure, ioState); } if (!haveActiveSubsessions) { envir().setResultMsg("No subsessions are currently active"); return False; } return True; }
void ProxyRTSPClient::checkInterPacketGaps_() { if (fInterPacketGapMaxTime == 0) return; // we're not checking // Check each subsession, counting up how many packets have been received: unsigned newTotNumPacketsReceived = 0; MediaSubsessionIterator iter(*fOurServerMediaSession.fClientMediaSession); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { RTPSource* src = subsession->rtpSource(); if (src == NULL) continue; newTotNumPacketsReceived += src->receptionStatsDB().totNumPacketsReceived(); } //envir() << *this << "::doLivenessCheck fTotNumPacketsReceived: " << fTotNumPacketsReceived // << ", newTotNumPacketsReceived: " << newTotNumPacketsReceived << "\n"; if (newTotNumPacketsReceived == fTotNumPacketsReceived) { // No additional packets have been received since the last time we // checked, so end this stream: // *env << "Closing session, because we stopped receiving packets.\n"; if (fVerbosityLevel > 0) { envir() << *this << "::doLivenessCheck last packet received: >" << fInterPacketGapMaxTime << " seconds ago. Resetting session\n"; } continueAfterLivenessCommand(1/*hack*/, fServerSupportsGetParameter); } else { fTotNumPacketsReceived = newTotNumPacketsReceived; // Check again, after the specified delay: fInterPacketGapsTask = envir().taskScheduler().scheduleDelayedTask(fInterPacketGapMaxTime*MILLION, checkInterPacketGaps, this); } }
void checkForPacketArrival(void* /*clientData*/) { if (!notifyOnPacketArrival) return; // we're not checking // Check each subsession, to see whether it has received data packets: unsigned numSubsessionsChecked = 0; unsigned numSubsessionsWithReceivedData = 0; unsigned numSubsessionsThatHaveBeenSynced = 0; MediaSubsessionIterator iter(*session); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { RTPSource* src = subsession->rtpSource(); if (src == NULL) continue; ++numSubsessionsChecked; if (src->receptionStatsDB().numActiveSourcesSinceLastReset() > 0) { // At least one data packet has arrived ++numSubsessionsWithReceivedData; } if (src->hasBeenSynchronizedUsingRTCP()) ++numSubsessionsThatHaveBeenSynced; } unsigned numSubsessionsToCheck = numSubsessionsChecked; // Special case for "QuickTimeFileSink"s and "AVIFileSink"s: // They might not use all of the input sources: if (qtOut != NULL) numSubsessionsToCheck = qtOut->numActiveSubsessions(); else if (aviOut != NULL) numSubsessionsToCheck = aviOut->numActiveSubsessions(); Boolean notifyTheUser; if (!syncStreams) notifyTheUser = numSubsessionsWithReceivedData > 0; // easy case else { notifyTheUser = numSubsessionsWithReceivedData >= numSubsessionsToCheck && numSubsessionsThatHaveBeenSynced == numSubsessionsChecked; // Note: A subsession with no active sources is considered to be synced } if (notifyTheUser) { struct timeval timeNow; gettimeofday(&timeNow, NULL); char timestampStr[100]; sprintf(timestampStr, "%ld%03ld", timeNow.tv_sec, (long)(timeNow.tv_usec/1000)); *env << (syncStreams ? "Synchronized d" : "D") << "ata packets have begun arriving [" << timestampStr << "]\007\n"; return; } // No luck, so reschedule this check again, after a delay: int uSecsToDelay = 100000; // 100 ms arrivalCheckTimerTask = env->taskScheduler().scheduleDelayedTask(uSecsToDelay, (TaskFunc*)checkForPacketArrival, NULL); }
void Live555ClientEngine::onStop() { if (session != nullptr) { MediaSubsession* subSession = nullptr; bool someSubsessionsWereActive = false; for (MediaSubsessionIterator iterator(*session); subSession != nullptr; subSession = iterator.next()) { if (subSession->sink != nullptr) { Medium::close(subSession->sink); subSession->sink = nullptr; if (subSession->rtcpInstance() != nullptr) { // in case the server sends a RTCP "BYE" while handling "TEARDOWN" subSession->rtcpInstance()->setByeHandler(nullptr, nullptr); } someSubsessionsWereActive = true; } } if (someSubsessionsWereActive) { rtspClient->sendTeardownCommand(*session, nullptr); } } Medium::close(rtspClient); Medium::close(session); }
Boolean MediaSession ::initiateByMediaType(char const* mimeType, MediaSubsession*& resultSubsession, int useSpecialRTPoffset) { // Look through this session's subsessions for media that match "mimeType" resultSubsession = NULL; MediaSubsessionIterator iter(*this); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { Boolean wasAlreadyInitiated = subsession->readSource() != NULL; if (!wasAlreadyInitiated) { // Try to create a source for this subsession: if (!subsession->initiate(useSpecialRTPoffset)) return False; } // Make sure the source's MIME type is one that we handle: if (strcmp(subsession->readSource()->MIMEtype(), mimeType) != 0) { if (!wasAlreadyInitiated) subsession->deInitiate(); continue; } resultSubsession = subsession; break; // use this } if (resultSubsession == NULL) { envir().setResultMsg("Session has no usable media subsession"); return False; } return True; }
void beginQOSMeasurement() { // Set up a measurement record for each active subsession: struct timeval startTime; gettimeofday(&startTime, NULL); nextQOSMeasurementUSecs = startTime.tv_sec*1000000 + startTime.tv_usec; qosMeasurementRecord* qosRecordTail = NULL; MediaSubsessionIterator iter(*session); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { RTPSource* src = subsession->rtpSource(); #ifdef SUPPORT_REAL_RTSP if (session->isRealNetworksRDT) src = (RTPSource*)(subsession->readSource()); // hack #endif if (src == NULL) continue; qosMeasurementRecord* qosRecord = new qosMeasurementRecord(startTime, src); if (qosRecordHead == NULL) qosRecordHead = qosRecord; if (qosRecordTail != NULL) qosRecordTail->fNext = qosRecord; qosRecordTail = qosRecord; } // Then schedule the first of the periodic measurements: scheduleNextQOSMeasurement(); }
int CAimer39RTSPClient::GetStreamType( unsigned int nStreamNum, STREAM_TYPE & Type ) { StreamClientState& scs = m_pRTSPClient->scs; // alias MediaSubsessionIterator iter(*scs.session); MediaSubsession* subsession = NULL; int iStreamCnt = 0; iter.reset(); while ( ( subsession = iter.next() ) != NULL ) { if ( strcmp( subsession->mediumName(), "video" ) == 0 || strcmp( subsession->mediumName(), "VIDEO" ) == 0 ) { Type = STREAM_VIDEO; } else if ( strcmp( subsession->mediumName(), "audio" ) == 0 || strcmp( subsession->mediumName(), "AUDIO" ) == 0 ) { Type = STREAM_AUDIO; } else { Type = STREAM_UNKNOWN; } if (nStreamNum == iStreamCnt) break; ++iStreamCnt; } iter.reset(); return 0; }
AVIFileSink::~AVIFileSink() { completeOutputFile(); // Then, stop streaming and delete each active "AVISubsessionIOState": MediaSubsessionIterator iter(fInputSession); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { if (subsession->readSource() != NULL) subsession->readSource()->stopGettingFrames(); AVISubsessionIOState* ioState = (AVISubsessionIOState*)(subsession->miscPtr); if (ioState == NULL) continue; delete ioState; } // Then, delete the index records: AVIIndexRecord* cur = fIndexRecordsHead; while (cur != NULL) { AVIIndexRecord* next = cur->next(); delete cur; cur = next; } // Finally, close our output file: CloseOutputFile(fOutFid); }
void checkInterPacketGaps(void* /*clientData*/) { if (interPacketGapMaxTime == 0) return; // we're not checking // Check each subsession, counting up how many packets have been received: unsigned newTotNumPacketsReceived = 0; MediaSubsessionIterator iter(*session); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { RTPSource* src = subsession->rtpSource(); if (src == NULL) continue; newTotNumPacketsReceived += src->receptionStatsDB().totNumPacketsReceived(); } if (newTotNumPacketsReceived == totNumPacketsReceived) { // No additional packets have been received since the last time we // checked, so end this stream: *env << "Closing session, because we stopped receiving packets.\n"; interPacketGapCheckTimerTask = NULL; sessionAfterPlaying(); } else { totNumPacketsReceived = newTotNumPacketsReceived; // Check again, after the specified delay: interPacketGapCheckTimerTask = env->taskScheduler().scheduleDelayedTask(interPacketGapMaxTime*1000000, (TaskFunc*)checkInterPacketGaps, NULL); } }
void shutdownStream(RTSPClient* rtspClient, int exitCode) { ourRTSPClient* rtsp = (ourRTSPClient*)rtspClient; if (exitCode != 0) { //eventLoopWatchVariable = 1; //int nID = rtsp->m_nID; IPNC_CloseStream(rtsp->m_nID); return; } UsageEnvironment& env = rtspClient->envir(); // alias StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias // First, check whether any subsessions have still to be closed: if (scs.session != NULL) { Boolean someSubsessionsWereActive = False; MediaSubsessionIterator iter(*scs.session); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { if (subsession->sink != NULL) { Medium::close(subsession->sink); subsession->sink = NULL; if (subsession->rtcpInstance() != NULL) { subsession->rtcpInstance()->setByeHandler(NULL, NULL); // in case the server sends a RTCP "BYE" while handling "TEARDOWN" } someSubsessionsWereActive = True; } } if (someSubsessionsWereActive) { // Send a RTSP "TEARDOWN" command, to tell the server to shutdown the stream. // Don't bother handling the response to the "TEARDOWN". rtspClient->sendTeardownCommand(*scs.session, NULL); } } env << *rtspClient << "Closing the stream.\n"; Medium::close(rtspClient); rtsp = NULL; rtspClient = NULL; // Note that this will also cause this stream's "StreamClientState" structure to get reclaimed. //if (--rtspClientCount == 0) { // The final stream has ended, so exit the application now. // (Of course, if you're embedding this code into your own application, you might want to comment this out, // and replace it with "eventLoopWatchVariable = 1;", so that we leave the LIVE555 event loop, and continue running "main()".) //exit(exitCode); //eventLoopWatchVariable = 1; //CXAgent::Instance().do_exit(); } }
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) { }
int CAimer39RTSPClient::GetVideoFPS( unsigned int nStreamNum ) { MediaSubsession * subsession = findSubSessionByStreamNum( nStreamNum ); if ( NULL == subsession ) return -1; IS_VIDEO_SUBS_R( subsession, -1 ); return (int)subsession->videoFPS(); }
char* MediaSession::absEndTime() const { if (fAbsEndTime != NULL) return fAbsEndTime; // If a subsession has an 'absolute' end time, then use that: MediaSubsessionIterator iter(*this); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { if (subsession->_absEndTime() != NULL) return subsession->_absEndTime(); } return NULL; }
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); }
AVIFileSink::AVIFileSink(UsageEnvironment& env, MediaSession& inputSession, char const* outputFileName, unsigned bufferSize, unsigned short movieWidth, unsigned short movieHeight, unsigned movieFPS, Boolean packetLossCompensate) : Medium(env), fInputSession(inputSession), fIndexRecordsHead(NULL), fIndexRecordsTail(NULL), fNumIndexRecords(0), fBufferSize(bufferSize), fPacketLossCompensate(packetLossCompensate), fAreCurrentlyBeingPlayed(False), fNumSubsessions(0), fNumBytesWritten(0), fHaveCompletedOutputFile(False), fMovieWidth(movieWidth), fMovieHeight(movieHeight), fMovieFPS(movieFPS) { fOutFid = OpenOutputFile(env, outputFileName); if (fOutFid == NULL) return; // Set up I/O state for each input subsession: MediaSubsessionIterator iter(fInputSession); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { // Ignore subsessions without a data source: FramedSource* subsessionSource = subsession->readSource(); if (subsessionSource == NULL) continue; // If "subsession's" SDP description specified screen dimension // or frame rate parameters, then use these. if (subsession->videoWidth() != 0) { fMovieWidth = subsession->videoWidth(); } if (subsession->videoHeight() != 0) { fMovieHeight = subsession->videoHeight(); } if (subsession->videoFPS() != 0) { fMovieFPS = subsession->videoFPS(); } AVISubsessionIOState* ioState = new AVISubsessionIOState(*this, *subsession); subsession->miscPtr = (void*)ioState; // Also set a 'BYE' handler for this subsession's RTCP instance: if (subsession->rtcpInstance() != NULL) { subsession->rtcpInstance()->setByeHandler(onRTCPBye, ioState); } ++fNumSubsessions; } // Begin by writing an AVI header: addFileHeader_AVI(); }
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; }
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(); } }
void subsessionAfterPlaying(void* clientData) { // Begin by closing this media subsession's stream: MediaSubsession* subsession = (MediaSubsession*)clientData; Medium::close(subsession->sink); subsession->sink = NULL; // Next, check whether *all* subsessions' streams have now been closed: MediaSession& session = subsession->parentSession(); MediaSubsessionIterator iter(session); while ((subsession = iter.next()) != NULL) { if (subsession->sink != NULL) return; // this subsession is still active } // All subsessions' streams have now been closed sessionAfterPlaying(); }
void subsessionAfterPlaying(void* clientData) { MediaSubsession* subsession = (MediaSubsession*)clientData; RTSPClient* rtspClient = (RTSPClient*)(subsession->miscPtr); // Begin by closing this subsession's stream: Medium::close(subsession->sink); subsession->sink = NULL; // Next, check whether *all* subsessions' streams have now been closed: MediaSession& session = subsession->parentSession(); MediaSubsessionIterator iter(session); while ((subsession = iter.next()) != NULL) { if (subsession->sink != NULL) return; // this subsession is still active } // All subsessions' streams have now been closed, so shutdown the client: shutdownStream(rtspClient, 1); }
bool MtkRTSPClient::IsPacketArrived() { MediaSubsessionIterator iter(*session); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { RTPSource* src = subsession->rtpSource(); if (src == NULL) continue; if (src->receptionStatsDB().numActiveSourcesSinceLastReset() > 0) { return true; } } return false; }
void CAimer39RTSPClient::shutdownStream( RTSPClient* rtspClient ) { UsageEnvironment& env = rtspClient->envir(); // alias StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias CAimer39RTSPClient * arc = findClient( (ourRTSPClient*)rtspClient ); if ( NULL == arc ) { env << "some how the system in to a dangerous situation!" << "\n"; return; } // First, check whether any subsessions have still to be closed: if (scs.session != NULL) { Boolean someSubsessionsWereActive = False; MediaSubsessionIterator iter(*scs.session); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { if (subsession->sink != NULL) { Medium::close(subsession->sink); subsession->sink = NULL; if (subsession->rtcpInstance() != NULL) { subsession->rtcpInstance()->setByeHandler(NULL, NULL); // in case the server sends a RTCP "BYE" while handling "TEARDOWN" } someSubsessionsWereActive = True; } } if (someSubsessionsWereActive) { // Send a RTSP "TEARDOWN" command, to tell the server to shutdown the stream. // Don't bother handling the response to the "TEARDOWN". rtspClient->sendTeardownCommand(*scs.session, NULL); } } env << *rtspClient << "Closing the stream.\n"; arc->m_bIsShutDown = true; if (arc->m_pFinishCallback) arc->m_pFinishCallback(arc->m_pCallBackParam, arc); //call back inform stream over Medium::close(rtspClient); // Note that this will also cause this stream's "StreamClientState" structure to get reclaimed. }
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 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; }
void beginQOSMeasurement(MediaSession *session, TaskToken qosMeasurementTimerTask, unsigned int qosMeasurementIntervalMS, UsageEnvironment *env) { // Set up a measurement record for each active subsession: struct timeval startTime; gettimeofday(&startTime, NULL); nextQOSMeasurementUSecs = startTime.tv_sec*1000000 + startTime.tv_usec; qosMeasurementRecord* qosRecordTail = NULL; MediaSubsessionIterator iter(*session); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { RTPSource* src = subsession->rtpSource(); if (src == NULL) continue; qosMeasurementRecord* qosRecord = new qosMeasurementRecord(startTime, src); if (qosRecordHead == NULL) qosRecordHead = qosRecord; if (qosRecordTail != NULL) qosRecordTail->fNext = qosRecord; qosRecordTail = qosRecord; } // Then schedule the first of the periodic measurements: scheduleNextQOSMeasurement(qosMeasurementTimerTask, qosMeasurementIntervalMS, env); }
AVISubsessionIOState::AVISubsessionIOState(AVIFileSink& sink, MediaSubsession& subsession) : fOurSink(sink), fOurSubsession(subsession), fMaxBytesPerSecond(0), fIsVideo(False), fIsAudio(False), fIsByteSwappedAudio(False), fNumFrames(0) { fBuffer = new SubsessionBuffer(fOurSink.fBufferSize); fPrevBuffer = sink.fPacketLossCompensate ? new SubsessionBuffer(fOurSink.fBufferSize) : NULL; FramedSource* subsessionSource = subsession.readSource(); fOurSourceIsActive = subsessionSource != NULL; fPrevPresentationTime.tv_sec = 0; fPrevPresentationTime.tv_usec = 0; }
// Used to shut down and close a stream (including its "RTSPClient" object): void StreamClient::shutdownStream() { // First, check whether any subsessions have still to be closed: if (_state.session != nullptr) { Boolean someSubsessionsWereActive = False; MediaSubsessionIterator iter(*_state.session); MediaSubsession * subsession = nullptr; while ((subsession = iter.next()) != nullptr) { if (subsession->sink != nullptr) { Medium::close(subsession->sink); subsession->sink = nullptr; if (subsession->rtcpInstance() != nullptr) { subsession->rtcpInstance()->setByeHandler(nullptr, nullptr); // in case the server sends a RTCP "BYE" while handling "TEARDOWN" } someSubsessionsWereActive = True; } } if (someSubsessionsWereActive) { sendTeardown(*_state.session); } } crLogN("StreamClient::shutdownStream() Closing the stream."); Medium::close(this); // Note that this will also cause this stream's "StreamClientState" structure to get reclaimed. // if (--rtspClientCount == 0) { // // The final stream has ended, so exit the application now. // // (Of course, if you're embedding this code into your own application, you might want to comment this out, // // and replace it with "eventLoopWatchVariable = 1;", so that we leave the LIVE555 event loop, and continue running "main()".) // exit(exitCode); // } exit(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(); }
ProxyServerMediaSubsession::ProxyServerMediaSubsession(MediaSubsession& mediaSubsession) : OnDemandServerMediaSubsession(mediaSubsession.parentSession().envir(), True/*reuseFirstSource*/), fClientMediaSubsession(mediaSubsession), fNext(NULL), fHaveSetupStream(False) { }
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; }
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(); }