testProgs/playCommon.cpp File Reference

#include "playCommon.hh"
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"
#include <signal.h>

Include dependency graph for playCommon.cpp:

Go to the source code of this file.

Data Structures

class  qosMeasurementRecord

Defines

#define USE_SIGNALS   1

Functions

void continueAfterOPTIONS (RTSPClient *client, int resultCode, char *resultString)
void continueAfterDESCRIBE (RTSPClient *client, int resultCode, char *resultString)
void continueAfterSETUP (RTSPClient *client, int resultCode, char *resultString)
void continueAfterPLAY (RTSPClient *client, int resultCode, char *resultString)
void continueAfterTEARDOWN (RTSPClient *client, int resultCode, char *resultString)
void setupStreams ()
void closeMediaSinks ()
void subsessionAfterPlaying (void *clientData)
void subsessionByeHandler (void *clientData)
void sessionAfterPlaying (void *clientData=NULL)
void sessionTimerHandler (void *clientData)
void shutdown (int exitCode=1)
void signalHandlerShutdown (int sig)
void checkForPacketArrival (void *clientData)
void checkInterPacketGaps (void *clientData)
void beginQOSMeasurement ()
void usage ()
int main (int argc, char **argv)
static void periodicQOSMeasurement (void *clientData)
static void scheduleNextQOSMeasurement ()
void printQOSData (int exitCode)

Variables

char const * progName
UsageEnvironmentenv
MediumourClient = NULL
AuthenticatorourAuthenticator = NULL
char const * streamURL = NULL
MediaSessionsession = NULL
TaskToken sessionTimerTask = NULL
TaskToken arrivalCheckTimerTask = NULL
TaskToken interPacketGapCheckTimerTask = NULL
TaskToken qosMeasurementTimerTask = NULL
Boolean createReceivers = True
Boolean outputQuickTimeFile = False
Boolean generateMP4Format = False
QuickTimeFileSinkqtOut = NULL
Boolean outputAVIFile = False
AVIFileSinkaviOut = NULL
Boolean audioOnly = False
Boolean videoOnly = False
char const * singleMedium = NULL
int verbosityLevel = 1
double duration = 0
double durationSlop = -1.0
double initialSeekTime = 0.0f
float scale = 1.0f
double endTime
unsigned interPacketGapMaxTime = 0
unsigned totNumPacketsReceived = ~0
Boolean playContinuously = False
int simpleRTPoffsetArg = -1
Boolean sendOptionsRequest = True
Boolean sendOptionsRequestOnly = False
Boolean oneFilePerFrame = False
Boolean notifyOnPacketArrival = False
Boolean streamUsingTCP = False
unsigned short desiredPortNum = 0
portNumBits tunnelOverHTTPPortNum = 0
char * username = NULL
char * password = NULL
char * proxyServerName = NULL
unsigned short proxyServerPortNum = 0
unsigned char desiredAudioRTPPayloadFormat = 0
char * mimeSubtype = NULL
unsigned short movieWidth = 240
Boolean movieWidthOptionSet = False
unsigned short movieHeight = 180
Boolean movieHeightOptionSet = False
unsigned movieFPS = 15
Boolean movieFPSOptionSet = False
char const * fileNamePrefix = ""
unsigned fileSinkBufferSize = 100000
unsigned socketInputBufferSize = 0
Boolean packetLossCompensate = False
Boolean syncStreams = False
Boolean generateHintTracks = False
unsigned qosMeasurementIntervalMS = 0
timeval startTime
MediaSubsessionsubsession
Boolean madeProgress = False
static qosMeasurementRecordqosRecordHead = NULL
static unsigned nextQOSMeasurementUSecs
Boolean areAlreadyShuttingDown = False
int shutdownExitCode


Define Documentation

#define USE_SIGNALS   1

Definition at line 32 of file playCommon.cpp.


Function Documentation

void beginQOSMeasurement (  ) 

Definition at line 1029 of file playCommon.cpp.

References qosMeasurementRecord::fNext, iter, MediaSubsessionIterator::next(), nextQOSMeasurementUSecs, NULL, qosRecordHead, MediaSubsession::rtpSource(), scheduleNextQOSMeasurement(), session, and subsession.

Referenced by continueAfterPLAY().

01029                            {
01030   // Set up a measurement record for each active subsession:
01031   struct timeval startTime;
01032   gettimeofday(&startTime, NULL);
01033   nextQOSMeasurementUSecs = startTime.tv_sec*1000000 + startTime.tv_usec;
01034   qosMeasurementRecord* qosRecordTail = NULL;
01035   MediaSubsessionIterator iter(*session);
01036   MediaSubsession* subsession;
01037   while ((subsession = iter.next()) != NULL) {
01038     RTPSource* src = subsession->rtpSource();
01039     if (src == NULL) continue;
01040 
01041     qosMeasurementRecord* qosRecord
01042       = new qosMeasurementRecord(startTime, src);
01043     if (qosRecordHead == NULL) qosRecordHead = qosRecord;
01044     if (qosRecordTail != NULL) qosRecordTail->fNext = qosRecord;
01045     qosRecordTail  = qosRecord;
01046   }
01047 
01048   // Then schedule the first of the periodic measurements:
01049   scheduleNextQOSMeasurement();
01050 }

void checkForPacketArrival ( void *  clientData  ) 

Definition at line 1175 of file playCommon.cpp.

References arrivalCheckTimerTask, aviOut, env, RTPSource::hasBeenSynchronizedUsingRTCP(), iter, MediaSubsessionIterator::next(), notifyOnPacketArrival, NULL, RTPReceptionStatsDB::numActiveSourcesSinceLastReset(), AVIFileSink::numActiveSubsessions(), QuickTimeFileSink::numActiveSubsessions(), qtOut, RTPSource::receptionStatsDB(), MediaSubsession::rtpSource(), TaskScheduler::scheduleDelayedTask(), session, subsession, syncStreams, and UsageEnvironment::taskScheduler().

Referenced by continueAfterPLAY().

01175                                                  {
01176   if (!notifyOnPacketArrival) return; // we're not checking
01177 
01178   // Check each subsession, to see whether it has received data packets:
01179   unsigned numSubsessionsChecked = 0;
01180   unsigned numSubsessionsWithReceivedData = 0;
01181   unsigned numSubsessionsThatHaveBeenSynced = 0;
01182 
01183   MediaSubsessionIterator iter(*session);
01184   MediaSubsession* subsession;
01185   while ((subsession = iter.next()) != NULL) {
01186     RTPSource* src = subsession->rtpSource();
01187     if (src == NULL) continue;
01188     ++numSubsessionsChecked;
01189 
01190     if (src->receptionStatsDB().numActiveSourcesSinceLastReset() > 0) {
01191       // At least one data packet has arrived
01192       ++numSubsessionsWithReceivedData;
01193     }
01194     if (src->hasBeenSynchronizedUsingRTCP()) {
01195       ++numSubsessionsThatHaveBeenSynced;
01196     }
01197   }
01198 
01199   unsigned numSubsessionsToCheck = numSubsessionsChecked;
01200   // Special case for "QuickTimeFileSink"s and "AVIFileSink"s:
01201   // They might not use all of the input sources:
01202   if (qtOut != NULL) {
01203     numSubsessionsToCheck = qtOut->numActiveSubsessions();
01204   } else if (aviOut != NULL) {
01205     numSubsessionsToCheck = aviOut->numActiveSubsessions();
01206   }
01207 
01208   Boolean notifyTheUser;
01209   if (!syncStreams) {
01210     notifyTheUser = numSubsessionsWithReceivedData > 0; // easy case
01211   } else {
01212     notifyTheUser = numSubsessionsWithReceivedData >= numSubsessionsToCheck
01213       && numSubsessionsThatHaveBeenSynced == numSubsessionsChecked;
01214     // Note: A subsession with no active sources is considered to be synced
01215   }
01216   if (notifyTheUser) {
01217     struct timeval timeNow;
01218     gettimeofday(&timeNow, NULL);
01219         char timestampStr[100];
01220         sprintf(timestampStr, "%ld%03ld", timeNow.tv_sec, (long)(timeNow.tv_usec/1000));
01221     *env << (syncStreams ? "Synchronized d" : "D")
01222                 << "ata packets have begun arriving [" << timestampStr << "]\007\n";
01223     return;
01224   }
01225 
01226   // No luck, so reschedule this check again, after a delay:
01227   int uSecsToDelay = 100000; // 100 ms
01228   arrivalCheckTimerTask
01229     = env->taskScheduler().scheduleDelayedTask(uSecsToDelay,
01230                                (TaskFunc*)checkForPacketArrival, NULL);
01231 }

void checkInterPacketGaps ( void *  clientData  ) 

Definition at line 1233 of file playCommon.cpp.

References env, interPacketGapCheckTimerTask, interPacketGapMaxTime, iter, MediaSubsessionIterator::next(), NULL, RTPSource::receptionStatsDB(), MediaSubsession::rtpSource(), TaskScheduler::scheduleDelayedTask(), session, sessionAfterPlaying(), subsession, UsageEnvironment::taskScheduler(), totNumPacketsReceived, and RTPReceptionStatsDB::totNumPacketsReceived().

Referenced by continueAfterPLAY().

01233                                                 {
01234   if (interPacketGapMaxTime == 0) return; // we're not checking
01235 
01236   // Check each subsession, counting up how many packets have been received:
01237   unsigned newTotNumPacketsReceived = 0;
01238 
01239   MediaSubsessionIterator iter(*session);
01240   MediaSubsession* subsession;
01241   while ((subsession = iter.next()) != NULL) {
01242     RTPSource* src = subsession->rtpSource();
01243     if (src == NULL) continue;
01244     newTotNumPacketsReceived += src->receptionStatsDB().totNumPacketsReceived();
01245   }
01246 
01247   if (newTotNumPacketsReceived == totNumPacketsReceived) {
01248     // No additional packets have been received since the last time we
01249     // checked, so end this stream:
01250     *env << "Closing session, because we stopped receiving packets.\n";
01251     interPacketGapCheckTimerTask = NULL;
01252     sessionAfterPlaying();
01253   } else {
01254     totNumPacketsReceived = newTotNumPacketsReceived;
01255     // Check again, after the specified delay:
01256     interPacketGapCheckTimerTask
01257       = env->taskScheduler().scheduleDelayedTask(interPacketGapMaxTime*1000000,
01258                                  (TaskFunc*)checkInterPacketGaps, NULL);
01259   }
01260 }

void closeMediaSinks (  ) 

Definition at line 855 of file playCommon.cpp.

References aviOut, Medium::close(), iter, MediaSubsessionIterator::next(), NULL, qtOut, session, MediaSubsession::sink, and subsession.

Referenced by continueAfterTEARDOWN().

00855                        {
00856   Medium::close(qtOut);
00857   Medium::close(aviOut);
00858 
00859   if (session == NULL) return;
00860   MediaSubsessionIterator iter(*session);
00861   MediaSubsession* subsession;
00862   while ((subsession = iter.next()) != NULL) {
00863     Medium::close(subsession->sink);
00864     subsession->sink = NULL;
00865   }
00866 }

void continueAfterDESCRIBE ( RTSPClient client,
int  resultCode,
char *  resultString 
)

Definition at line 534 of file playCommon.cpp.

References MediaSubsession::clientPortNum(), MediaSubsession::codecName(), MediaSession::createNew(), createReceivers, desiredPortNum, env, False, fileSinkBufferSize, getReceiveBufferSize(), UsageEnvironment::getResultMsg(), MediaSession::hasSubsessions(), MediaSubsession::initiate(), iter, madeProgress, MediaSubsession::mediumName(), MediaSubsessionIterator::next(), NULL, RTPSource::RTPgs(), MediaSubsession::rtpSource(), session, MediaSubsession::setClientPortNum(), RTPSource::setPacketReorderingThresholdTime(), setReceiveBufferTo(), setupStreams(), shutdown(), simpleRTPoffsetArg, singleMedium, socketInputBufferSize, Socket::socketNum(), streamURL, subsession, and True.

00534                                                                             {
00535   if (resultCode != 0) {
00536     *env << "Failed to get a SDP description for the URL \"" << streamURL << "\": " << resultString << "\n";
00537     shutdown();
00538   }
00539 
00540   char* sdpDescription = resultString;
00541   *env << "Opened URL \"" << streamURL << "\", returning a SDP description:\n" << sdpDescription << "\n";
00542 
00543   // Create a media session object from this SDP description:
00544   session = MediaSession::createNew(*env, sdpDescription);
00545   delete[] sdpDescription;
00546   if (session == NULL) {
00547     *env << "Failed to create a MediaSession object from the SDP description: " << env->getResultMsg() << "\n";
00548     shutdown();
00549   } else if (!session->hasSubsessions()) {
00550     *env << "This session has no media subsessions (i.e., no \"m=\" lines)\n";
00551     shutdown();
00552   }
00553 
00554   // Then, setup the "RTPSource"s for the session:
00555   MediaSubsessionIterator iter(*session);
00556   MediaSubsession *subsession;
00557   Boolean madeProgress = False;
00558   char const* singleMediumToTest = singleMedium;
00559   while ((subsession = iter.next()) != NULL) {
00560     // If we've asked to receive only a single medium, then check this now:
00561     if (singleMediumToTest != NULL) {
00562       if (strcmp(subsession->mediumName(), singleMediumToTest) != 0) {
00563                   *env << "Ignoring \"" << subsession->mediumName()
00564                           << "/" << subsession->codecName()
00565                           << "\" subsession, because we've asked to receive a single " << singleMedium
00566                           << " session only\n";
00567         continue;
00568       } else {
00569         // Receive this subsession only
00570         singleMediumToTest = "xxxxx";
00571             // this hack ensures that we get only 1 subsession of this type
00572       }
00573     }
00574 
00575     if (desiredPortNum != 0) {
00576       subsession->setClientPortNum(desiredPortNum);
00577       desiredPortNum += 2;
00578     }
00579 
00580     if (createReceivers) {
00581       if (!subsession->initiate(simpleRTPoffsetArg)) {
00582         *env << "Unable to create receiver for \"" << subsession->mediumName()
00583              << "/" << subsession->codecName()
00584              << "\" subsession: " << env->getResultMsg() << "\n";
00585       } else {
00586         *env << "Created receiver for \"" << subsession->mediumName()
00587              << "/" << subsession->codecName()
00588              << "\" subsession (client ports " << subsession->clientPortNum()
00589              << "-" << subsession->clientPortNum()+1 << ")\n";
00590         madeProgress = True;
00591         
00592         if (subsession->rtpSource() != NULL) {
00593           // Because we're saving the incoming data, rather than playing
00594           // it in real time, allow an especially large time threshold
00595           // (1 second) for reordering misordered incoming packets:
00596           unsigned const thresh = 1000000; // 1 second
00597           subsession->rtpSource()->setPacketReorderingThresholdTime(thresh);
00598           
00599           // Set the RTP source's OS socket buffer size as appropriate - either if we were explicitly asked (using -B),
00600           // or if the desired FileSink buffer size happens to be larger than the current OS socket buffer size.
00601           // (The latter case is a heuristic, on the assumption that if the user asked for a large FileSink buffer size,
00602           // then the input data rate may be large enough to justify increasing the OS socket buffer size also.)
00603           int socketNum = subsession->rtpSource()->RTPgs()->socketNum();
00604           unsigned curBufferSize = getReceiveBufferSize(*env, socketNum);
00605           if (socketInputBufferSize > 0 || fileSinkBufferSize > curBufferSize) {
00606             unsigned newBufferSize = socketInputBufferSize > 0 ? socketInputBufferSize : fileSinkBufferSize;
00607             newBufferSize = setReceiveBufferTo(*env, socketNum, newBufferSize);
00608             if (socketInputBufferSize > 0) { // The user explicitly asked for the new socket buffer size; announce it:
00609               *env << "Changed socket receive buffer size for the \""
00610                    << subsession->mediumName()
00611                    << "/" << subsession->codecName()
00612                    << "\" subsession from "
00613                    << curBufferSize << " to "
00614                    << newBufferSize << " bytes\n";
00615             }
00616           }
00617         }
00618       }
00619     } else {
00620       if (subsession->clientPortNum() == 0) {
00621         *env << "No client port was specified for the \""
00622              << subsession->mediumName()
00623              << "/" << subsession->codecName()
00624              << "\" subsession.  (Try adding the \"-p <portNum>\" option.)\n";
00625       } else {
00626                 madeProgress = True;
00627       }
00628     }
00629   }
00630   if (!madeProgress) shutdown();
00631 
00632   // Perform additional 'setup' on each subsession, before playing them:
00633   setupStreams();
00634 }

void continueAfterOPTIONS ( RTSPClient client,
int  resultCode,
char *  resultString 
)

Definition at line 519 of file playCommon.cpp.

References clientProtocolName, continueAfterDESCRIBE(), env, getSDPDescription(), sendOptionsRequestOnly, and shutdown().

00519                                                                            {
00520   if (sendOptionsRequestOnly) {
00521     if (resultCode != 0) {
00522       *env << clientProtocolName << " \"OPTIONS\" request failed: " << resultString << "\n";
00523     } else {
00524       *env << clientProtocolName << " \"OPTIONS\" request returned: " << resultString << "\n";
00525     }
00526     shutdown();
00527   }
00528   delete[] resultString;
00529 
00530   // Next, get a SDP description for the stream:
00531   getSDPDescription(continueAfterDESCRIBE);
00532 }

void continueAfterPLAY ( RTSPClient client,
int  resultCode,
char *  resultString 
)

Definition at line 802 of file playCommon.cpp.

References beginQOSMeasurement(), checkForPacketArrival(), checkInterPacketGaps(), createReceivers, duration, durationSlop, endTime, env, False, initialSeekTime, NULL, MediaSession::playEndTime(), MediaSession::playStartTime(), qosMeasurementIntervalMS, scale, TaskScheduler::scheduleDelayedTask(), session, sessionTimerHandler(), sessionTimerTask, shutdown(), UsageEnvironment::taskScheduler(), and True.

Referenced by sessionAfterPlaying(), setupNextSubsession(), and setupStreams().

00802                                                                         {
00803   if (resultCode != 0) {
00804     *env << "Failed to start playing session: " << resultString << "\n";
00805     shutdown();
00806   } else {
00807     *env << "Started playing session\n";
00808   }
00809 
00810   if (qosMeasurementIntervalMS > 0) {
00811     // Begin periodic QOS measurements:
00812     beginQOSMeasurement();
00813   }
00814 
00815   // Figure out how long to delay (if at all) before shutting down, or
00816   // repeating the playing
00817   Boolean timerIsBeingUsed = False;
00818   double secondsToDelay = duration;
00819   if (duration > 0) {
00820     // First, adjust "duration" based on any change to the play range (that was specified in the "PLAY" response):
00821     double rangeAdjustment = (session->playEndTime() - session->playStartTime()) - (endTime - initialSeekTime);
00822     if (duration + rangeAdjustment > 0.0) duration += rangeAdjustment;
00823 
00824     timerIsBeingUsed = True;
00825     double absScale = scale > 0 ? scale : -scale; // ASSERT: scale != 0
00826     secondsToDelay = duration/absScale + durationSlop;
00827 
00828     int64_t uSecsToDelay = (int64_t)(secondsToDelay*1000000.0);
00829     sessionTimerTask = env->taskScheduler().scheduleDelayedTask(uSecsToDelay, (TaskFunc*)sessionTimerHandler, (void*)NULL);
00830   }
00831 
00832   char const* actionString
00833     = createReceivers? "Receiving streamed data":"Data is being streamed";
00834   if (timerIsBeingUsed) {
00835     *env << actionString
00836                 << " (for up to " << secondsToDelay
00837                 << " seconds)...\n";
00838   } else {
00839 #ifdef USE_SIGNALS
00840     pid_t ourPid = getpid();
00841     *env << actionString
00842                 << " (signal with \"kill -HUP " << (int)ourPid
00843                 << "\" or \"kill -USR1 " << (int)ourPid
00844                 << "\" to terminate)...\n";
00845 #else
00846     *env << actionString << "...\n";
00847 #endif
00848   }
00849 
00850   // Watch for incoming packets (if desired):
00851   checkForPacketArrival(NULL);
00852   checkInterPacketGaps(NULL);
00853 }

void continueAfterSETUP ( RTSPClient client,
int  resultCode,
char *  resultString 
)

Definition at line 638 of file playCommon.cpp.

References MediaSubsession::clientPortNum(), MediaSubsession::codecName(), env, UsageEnvironment::getResultMsg(), madeProgress, MediaSubsession::mediumName(), setupStreams(), subsession, and True.

00638                                                                          {
00639   if (resultCode == 0) {
00640       *env << "Setup \"" << subsession->mediumName()
00641            << "/" << subsession->codecName()
00642            << "\" subsession (client ports " << subsession->clientPortNum()
00643            << "-" << subsession->clientPortNum()+1 << ")\n";
00644       madeProgress = True;
00645   } else {
00646     *env << "Failed to setup \"" << subsession->mediumName()
00647          << "/" << subsession->codecName()
00648          << "\" subsession: " << env->getResultMsg() << "\n";
00649   }
00650 
00651   // Set up the next subsession, if any:
00652   setupStreams();
00653 }

void continueAfterTEARDOWN ( RTSPClient client,
int  resultCode,
char *  resultString 
)

Definition at line 1157 of file playCommon.cpp.

References Medium::close(), closeMediaSinks(), exit, ourAuthenticator, ourClient, session, and shutdownExitCode.

Referenced by shutdown().

01157                                                                                     {
01158   // Now that we've stopped any more incoming data from arriving, close our output files:
01159   closeMediaSinks();
01160   Medium::close(session);
01161 
01162   // Finally, shut down our client:
01163   delete ourAuthenticator;
01164   Medium::close(ourClient);
01165 
01166   // Adios...
01167   exit(shutdownExitCode);
01168 }

int main ( int  argc,
char **  argv 
)

Definition at line 124 of file playCommon.cpp.

References allowProxyServers, audioOnly, clientProtocolName, continueAfterOPTIONS(), controlConnectionUsesTCP, createClient(), BasicUsageEnvironment::createNew(), BasicTaskScheduler::createNew(), createReceivers, NetAddress::data(), desiredAudioRTPPayloadFormat, desiredPortNum, TaskScheduler::doEventLoop(), duration, durationSlop, env, False, fileNamePrefix, fileSinkBufferSize, NetAddressList::firstAddress(), generateHintTracks, generateMP4Format, getOptions(), UsageEnvironment::getResultMsg(), initialSeekTime, interPacketGapMaxTime, mimeSubtype, movieFPS, movieFPSOptionSet, movieHeight, movieHeightOptionSet, movieWidth, movieWidthOptionSet, notifyOnPacketArrival, NULL, NetAddressList::numAddresses(), oneFilePerFrame, ourAuthenticator, ourClient, outputAVIFile, outputQuickTimeFile, packetLossCompensate, password, playContinuously, progName, proxyServerName, proxyServerPortNum, qosMeasurementIntervalMS, ReceivingInterfaceAddr, scale, sendOptionsRequest, sendOptionsRequestOnly, shutdown(), signalHandlerShutdown(), simpleRTPoffsetArg, singleMedium, socketInputBufferSize, startTime, streamURL, streamUsingTCP, syncStreams, UsageEnvironment::taskScheduler(), True, tunnelOverHTTPPortNum, usage(), username, verbosityLevel, and videoOnly.

00124                                 {
00125   // Begin by setting up our usage environment:
00126   TaskScheduler* scheduler = BasicTaskScheduler::createNew();
00127   env = BasicUsageEnvironment::createNew(*scheduler);
00128 
00129   progName = argv[0];
00130 
00131   gettimeofday(&startTime, NULL);
00132 
00133 #ifdef USE_SIGNALS
00134   // Allow ourselves to be shut down gracefully by a SIGHUP or a SIGUSR1:
00135   signal(SIGHUP, signalHandlerShutdown);
00136   signal(SIGUSR1, signalHandlerShutdown);
00137 #endif
00138 
00139   // unfortunately we can't use getopt() here, as Windoze doesn't have it
00140   while (argc > 2) {
00141     char* const opt = argv[1];
00142     if (opt[0] != '-') usage();
00143     switch (opt[1]) {
00144 
00145     case 'p': { // specify start port number
00146       int portArg;
00147       if (sscanf(argv[2], "%d", &portArg) != 1) {
00148         usage();
00149       }
00150       if (portArg <= 0 || portArg >= 65536 || portArg&1) {
00151         *env << "bad port number: " << portArg
00152                 << " (must be even, and in the range (0,65536))\n";
00153         usage();
00154       }
00155       desiredPortNum = (unsigned short)portArg;
00156       ++argv; --argc;
00157       break;
00158     }
00159 
00160     case 'r': { // do not receive data (instead, just 'play' the stream(s))
00161       createReceivers = False;
00162       break;
00163     }
00164 
00165     case 'q': { // output a QuickTime file (to stdout)
00166       outputQuickTimeFile = True;
00167       break;
00168     }
00169 
00170     case '4': { // output a 'mp4'-format file (to stdout)
00171       outputQuickTimeFile = True;
00172       generateMP4Format = True;
00173       break;
00174     }
00175 
00176     case 'i': { // output an AVI file (to stdout)
00177       outputAVIFile = True;
00178       break;
00179     }
00180 
00181     case 'I': { // specify input interface...
00182       NetAddressList addresses(argv[2]);
00183       if (addresses.numAddresses() == 0) {
00184         *env << "Failed to find network address for \"" << argv[2] << "\"";
00185         break;
00186       }
00187       ReceivingInterfaceAddr = *(unsigned*)(addresses.firstAddress()->data());
00188       ++argv; --argc;
00189       break;
00190     }
00191 
00192     case 'a': { // receive/record an audio stream only
00193       audioOnly = True;
00194       singleMedium = "audio";
00195       break;
00196     }
00197 
00198     case 'v': { // receive/record a video stream only
00199       videoOnly = True;
00200       singleMedium = "video";
00201       break;
00202     }
00203 
00204     case 'V': { // disable verbose output
00205       verbosityLevel = 0;
00206       break;
00207     }
00208 
00209     case 'd': { // specify duration, or how much to delay after end time
00210       float arg;
00211       if (sscanf(argv[2], "%g", &arg) != 1) {
00212         usage();
00213       }
00214       if (argv[2][0] == '-') { // not "arg<0", in case argv[2] was "-0"
00215         // a 'negative' argument was specified; use this for "durationSlop":
00216         duration = 0; // use whatever's in the SDP
00217         durationSlop = -arg;
00218       } else {
00219         duration = arg;
00220         durationSlop = 0;
00221       }
00222       ++argv; --argc;
00223       break;
00224     }
00225 
00226     case 'D': { // specify maximum number of seconds to wait for packets:
00227       if (sscanf(argv[2], "%u", &interPacketGapMaxTime) != 1) {
00228         usage();
00229       }
00230       ++argv; --argc;
00231       break;
00232     }
00233 
00234     case 'c': { // play continuously
00235       playContinuously = True;
00236       break;
00237     }
00238 
00239     case 'S': { // specify an offset to use with "SimpleRTPSource"s
00240       if (sscanf(argv[2], "%d", &simpleRTPoffsetArg) != 1) {
00241         usage();
00242       }
00243       if (simpleRTPoffsetArg < 0) {
00244         *env << "offset argument to \"-S\" must be >= 0\n";
00245         usage();
00246       }
00247       ++argv; --argc;
00248       break;
00249     }
00250 
00251     case 'O': { // Don't send an "OPTIONS" request before "DESCRIBE"
00252       sendOptionsRequest = False;
00253       break;
00254     }
00255 
00256     case 'o': { // Send only the "OPTIONS" request to the server
00257       sendOptionsRequestOnly = True;
00258       break;
00259     }
00260 
00261     case 'm': { // output multiple files - one for each frame
00262       oneFilePerFrame = True;
00263       break;
00264     }
00265 
00266     case 'n': { // notify the user when the first data packet arrives
00267       notifyOnPacketArrival = True;
00268       break;
00269     }
00270 
00271     case 't': {
00272       // stream RTP and RTCP over the TCP 'control' connection
00273       if (controlConnectionUsesTCP) {
00274         streamUsingTCP = True;
00275       } else {
00276         usage();
00277       }
00278       break;
00279     }
00280 
00281     case 'T': {
00282       // stream RTP and RTCP over a HTTP connection
00283       if (controlConnectionUsesTCP) {
00284         if (argc > 3 && argv[2][0] != '-') {
00285           // The next argument is the HTTP server port number:
00286           if (sscanf(argv[2], "%hu", &tunnelOverHTTPPortNum) == 1
00287               && tunnelOverHTTPPortNum > 0) {
00288             ++argv; --argc;
00289             break;
00290           }
00291         }
00292       }
00293 
00294       // If we get here, the option was specified incorrectly:
00295       usage();
00296       break;
00297     }
00298 
00299     case 'u': { // specify a username and password
00300       if (argc < 4) usage(); // there's no argv[3] (for the "password")
00301       username = argv[2];
00302       password = argv[3];
00303       argv+=2; argc-=2;
00304       if (allowProxyServers && argc > 3 && argv[2][0] != '-') {
00305         // The next argument is the name of a proxy server:
00306         proxyServerName = argv[2];
00307         ++argv; --argc;
00308 
00309         if (argc > 3 && argv[2][0] != '-') {
00310           // The next argument is the proxy server port number:
00311           if (sscanf(argv[2], "%hu", &proxyServerPortNum) != 1) {
00312             usage();
00313           }
00314           ++argv; --argc;
00315         }
00316       }
00317 
00318       ourAuthenticator = new Authenticator(username, password);
00319       break;
00320     }
00321 
00322     case 'A': { // specify a desired audio RTP payload format
00323       unsigned formatArg;
00324       if (sscanf(argv[2], "%u", &formatArg) != 1
00325           || formatArg >= 96) {
00326         usage();
00327       }
00328       desiredAudioRTPPayloadFormat = (unsigned char)formatArg;
00329       ++argv; --argc;
00330       break;
00331     }
00332 
00333     case 'M': { // specify a MIME subtype for a dynamic RTP payload type
00334       mimeSubtype = argv[2];
00335       if (desiredAudioRTPPayloadFormat==0) desiredAudioRTPPayloadFormat =96;
00336       ++argv; --argc;
00337       break;
00338     }
00339 
00340     case 'w': { // specify a width (pixels) for an output QuickTime or AVI movie
00341       if (sscanf(argv[2], "%hu", &movieWidth) != 1) {
00342         usage();
00343       }
00344       movieWidthOptionSet = True;
00345       ++argv; --argc;
00346       break;
00347     }
00348 
00349     case 'h': { // specify a height (pixels) for an output QuickTime or AVI movie
00350       if (sscanf(argv[2], "%hu", &movieHeight) != 1) {
00351         usage();
00352       }
00353       movieHeightOptionSet = True;
00354       ++argv; --argc;
00355       break;
00356     }
00357 
00358     case 'f': { // specify a frame rate (per second) for an output QT or AVI movie
00359       if (sscanf(argv[2], "%u", &movieFPS) != 1) {
00360         usage();
00361       }
00362       movieFPSOptionSet = True;
00363       ++argv; --argc;
00364       break;
00365     }
00366 
00367     case 'F': { // specify a prefix for the audio and video output files
00368       fileNamePrefix = argv[2];
00369       ++argv; --argc;
00370       break;
00371     }
00372 
00373     case 'b': { // specify the size of buffers for "FileSink"s
00374       if (sscanf(argv[2], "%u", &fileSinkBufferSize) != 1) {
00375         usage();
00376       }
00377       ++argv; --argc;
00378       break;
00379     }
00380 
00381     case 'B': { // specify the size of input socket buffers
00382       if (sscanf(argv[2], "%u", &socketInputBufferSize) != 1) {
00383         usage();
00384       }
00385       ++argv; --argc;
00386       break;
00387     }
00388 
00389     // Note: The following option is deprecated, and may someday be removed:
00390     case 'l': { // try to compensate for packet loss by repeating frames
00391       packetLossCompensate = True;
00392       break;
00393     }
00394 
00395     case 'y': { // synchronize audio and video streams
00396       syncStreams = True;
00397       break;
00398     }
00399 
00400     case 'H': { // generate hint tracks (as well as the regular data tracks)
00401       generateHintTracks = True;
00402       break;
00403     }
00404 
00405     case 'Q': { // output QOS measurements
00406       qosMeasurementIntervalMS = 1000; // default: 1 second
00407 
00408       if (argc > 3 && argv[2][0] != '-') {
00409         // The next argument is the measurement interval,
00410         // in multiples of 100 ms
00411         if (sscanf(argv[2], "%u", &qosMeasurementIntervalMS) != 1) {
00412           usage();
00413         }
00414         qosMeasurementIntervalMS *= 100;
00415         ++argv; --argc;
00416       }
00417       break;
00418     }
00419 
00420     case 's': { // specify initial seek time (trick play)
00421       double arg;
00422       if (sscanf(argv[2], "%lg", &arg) != 1 || arg < 0) {
00423         usage();
00424       }
00425       initialSeekTime = arg;
00426       ++argv; --argc;
00427       break;
00428     }
00429 
00430     case 'z': { // scale (trick play)
00431       float arg;
00432       if (sscanf(argv[2], "%g", &arg) != 1 || arg == 0.0f) {
00433         usage();
00434       }
00435       scale = arg;
00436       ++argv; --argc;
00437       break;
00438     }
00439 
00440     default: {
00441       usage();
00442       break;
00443     }
00444     }
00445 
00446     ++argv; --argc;
00447   }
00448   if (argc != 2) usage(); // there must be exactly one "rtsp://" URL at the end
00449   if (outputQuickTimeFile && outputAVIFile) {
00450     *env << "The -i and -q (or -4) options cannot both be used!\n";
00451     usage();
00452   }
00453   Boolean outputCompositeFile = outputQuickTimeFile || outputAVIFile;
00454   if (!createReceivers && outputCompositeFile) {
00455     *env << "The -r and -q (or -4 or -i) options cannot both be used!\n";
00456     usage();
00457   }
00458   if (outputCompositeFile && !movieWidthOptionSet) {
00459     *env << "Warning: The -q, -4 or -i option was used, but not -w.  Assuming a video width of "
00460          << movieWidth << " pixels\n";
00461   }
00462   if (outputCompositeFile && !movieHeightOptionSet) {
00463     *env << "Warning: The -q, -4 or -i option was used, but not -h.  Assuming a video height of "
00464          << movieHeight << " pixels\n";
00465   }
00466   if (outputCompositeFile && !movieFPSOptionSet) {
00467     *env << "Warning: The -q, -4 or -i option was used, but not -f.  Assuming a video frame rate of "
00468          << movieFPS << " frames-per-second\n";
00469   }
00470   if (audioOnly && videoOnly) {
00471     *env << "The -a and -v options cannot both be used!\n";
00472     usage();
00473   }
00474   if (sendOptionsRequestOnly && !sendOptionsRequest) {
00475     *env << "The -o and -O options cannot both be used!\n";
00476     usage();
00477   }
00478   if (tunnelOverHTTPPortNum > 0) {
00479     if (streamUsingTCP) {
00480       *env << "The -t and -T options cannot both be used!\n";
00481       usage();
00482     } else {
00483       streamUsingTCP = True;
00484     }
00485   }
00486   if (!createReceivers && notifyOnPacketArrival) {
00487     *env << "Warning: Because we're not receiving stream data, the -n flag has no effect\n";
00488   }
00489   if (durationSlop < 0) {
00490     // This parameter wasn't set, so use a default value.
00491     // If we're measuring QOS stats, then don't add any slop, to avoid
00492     // having 'empty' measurement intervals at the end.
00493     durationSlop = qosMeasurementIntervalMS > 0 ? 0.0 : 5.0;
00494   }
00495 
00496   streamURL = argv[1];
00497 
00498   // Create our client object:
00499   ourClient = createClient(*env, streamURL, verbosityLevel, progName);
00500   if (ourClient == NULL) {
00501     *env << "Failed to create " << clientProtocolName
00502                 << " client: " << env->getResultMsg() << "\n";
00503     shutdown();
00504   }
00505 
00506   if (sendOptionsRequest) {
00507     // Begin by sending an "OPTIONS" command:
00508     getOptions(continueAfterOPTIONS);
00509   } else {
00510     continueAfterOPTIONS(NULL, 0, NULL);
00511   }
00512 
00513   // All subsequent activity takes place within the event loop:
00514   env->taskScheduler().doEventLoop(); // does not return
00515 
00516   return 0; // only to prevent compiler warning
00517 }

static void periodicQOSMeasurement ( void *  clientData  )  [static]

Definition at line 977 of file playCommon.cpp.

References NULL, qosRecordHead, and scheduleNextQOSMeasurement().

Referenced by scheduleNextQOSMeasurement().

00977                                                          {
00978   struct timeval timeNow;
00979   gettimeofday(&timeNow, NULL);
00980 
00981   for (qosMeasurementRecord* qosRecord = qosRecordHead;
00982        qosRecord != NULL; qosRecord = qosRecord->fNext) {
00983     qosRecord->periodicQOSMeasurement(timeNow);
00984   }
00985 
00986   // Do this again later:
00987   scheduleNextQOSMeasurement();
00988 }

void printQOSData ( int  exitCode  ) 

Definition at line 1052 of file playCommon.cpp.

References MediaSubsession::codecName(), env, qosMeasurementRecord::fNext, iter, qosMeasurementRecord::kbits_per_second_max, qosMeasurementRecord::kbits_per_second_min, qosMeasurementRecord::kBytesTotal, qosMeasurementRecord::measurementEndTime, MediaSubsession::mediumName(), MediaSubsessionIterator::next(), NULL, qosMeasurementRecord::packet_loss_fraction_max, qosMeasurementRecord::packet_loss_fraction_min, qosMeasurementIntervalMS, qosRecordHead, RTPSource::receptionStatsDB(), MediaSubsession::rtpSource(), session, subsession, qosMeasurementRecord::totNumPacketsExpected, totNumPacketsReceived, qosMeasurementRecord::totNumPacketsReceived, and True.

Referenced by shutdown().

01052                                 {
01053   *env << "begin_QOS_statistics\n";
01054   
01055   // Print out stats for each active subsession:
01056   qosMeasurementRecord* curQOSRecord = qosRecordHead;
01057   if (session != NULL) {
01058     MediaSubsessionIterator iter(*session);
01059     MediaSubsession* subsession;
01060     while ((subsession = iter.next()) != NULL) {
01061       RTPSource* src = subsession->rtpSource();
01062       if (src == NULL) continue;
01063       
01064       *env << "subsession\t" << subsession->mediumName()
01065            << "/" << subsession->codecName() << "\n";
01066       
01067       unsigned numPacketsReceived = 0, numPacketsExpected = 0;
01068       
01069       if (curQOSRecord != NULL) {
01070         numPacketsReceived = curQOSRecord->totNumPacketsReceived;
01071         numPacketsExpected = curQOSRecord->totNumPacketsExpected;
01072       }
01073       *env << "num_packets_received\t" << numPacketsReceived << "\n";
01074       *env << "num_packets_lost\t" << int(numPacketsExpected - numPacketsReceived) << "\n";
01075       
01076       if (curQOSRecord != NULL) {
01077         unsigned secsDiff = curQOSRecord->measurementEndTime.tv_sec
01078           - curQOSRecord->measurementStartTime.tv_sec;
01079         int usecsDiff = curQOSRecord->measurementEndTime.tv_usec
01080           - curQOSRecord->measurementStartTime.tv_usec;
01081         double measurementTime = secsDiff + usecsDiff/1000000.0;
01082         *env << "elapsed_measurement_time\t" << measurementTime << "\n";
01083         
01084         *env << "kBytes_received_total\t" << curQOSRecord->kBytesTotal << "\n";
01085         
01086         *env << "measurement_sampling_interval_ms\t" << qosMeasurementIntervalMS << "\n";
01087         
01088         if (curQOSRecord->kbits_per_second_max == 0) {
01089           // special case: we didn't receive any data:
01090           *env <<
01091             "kbits_per_second_min\tunavailable\n"
01092             "kbits_per_second_ave\tunavailable\n"
01093             "kbits_per_second_max\tunavailable\n";
01094         } else {
01095           *env << "kbits_per_second_min\t" << curQOSRecord->kbits_per_second_min << "\n";
01096           *env << "kbits_per_second_ave\t"
01097                << (measurementTime == 0.0 ? 0.0 : 8*curQOSRecord->kBytesTotal/measurementTime) << "\n";
01098           *env << "kbits_per_second_max\t" << curQOSRecord->kbits_per_second_max << "\n";
01099         }
01100         
01101         *env << "packet_loss_percentage_min\t" << 100*curQOSRecord->packet_loss_fraction_min << "\n";
01102         double packetLossFraction = numPacketsExpected == 0 ? 1.0
01103           : 1.0 - numPacketsReceived/(double)numPacketsExpected;
01104         if (packetLossFraction < 0.0) packetLossFraction = 0.0;
01105         *env << "packet_loss_percentage_ave\t" << 100*packetLossFraction << "\n";
01106         *env << "packet_loss_percentage_max\t"
01107              << (packetLossFraction == 1.0 ? 100.0 : 100*curQOSRecord->packet_loss_fraction_max) << "\n";
01108         
01109         RTPReceptionStatsDB::Iterator statsIter(src->receptionStatsDB());
01110         // Assume that there's only one SSRC source (usually the case):
01111         RTPReceptionStats* stats = statsIter.next(True);
01112         if (stats != NULL) {
01113           *env << "inter_packet_gap_ms_min\t" << stats->minInterPacketGapUS()/1000.0 << "\n";
01114           struct timeval totalGaps = stats->totalInterPacketGaps();
01115           double totalGapsMS = totalGaps.tv_sec*1000.0 + totalGaps.tv_usec/1000.0;
01116           unsigned totNumPacketsReceived = stats->totNumPacketsReceived();
01117           *env << "inter_packet_gap_ms_ave\t"
01118                << (totNumPacketsReceived == 0 ? 0.0 : totalGapsMS/totNumPacketsReceived) << "\n";
01119           *env << "inter_packet_gap_ms_max\t" << stats->maxInterPacketGapUS()/1000.0 << "\n";
01120         }
01121         
01122         curQOSRecord = curQOSRecord->fNext;
01123       }
01124     }
01125   }
01126 
01127   *env << "end_QOS_statistics\n";
01128   delete qosRecordHead;
01129 }

static void scheduleNextQOSMeasurement (  )  [static]

Definition at line 965 of file playCommon.cpp.

References env, nextQOSMeasurementUSecs, NULL, periodicQOSMeasurement(), qosMeasurementIntervalMS, qosMeasurementTimerTask, TaskScheduler::scheduleDelayedTask(), and UsageEnvironment::taskScheduler().

Referenced by beginQOSMeasurement(), and periodicQOSMeasurement().

00965                                          {
00966   nextQOSMeasurementUSecs += qosMeasurementIntervalMS*1000;
00967   struct timeval timeNow;
00968   gettimeofday(&timeNow, NULL);
00969   unsigned timeNowUSecs = timeNow.tv_sec*1000000 + timeNow.tv_usec;
00970   unsigned usecsToDelay = nextQOSMeasurementUSecs - timeNowUSecs;
00971      // Note: This works even when nextQOSMeasurementUSecs wraps around
00972 
00973   qosMeasurementTimerTask = env->taskScheduler().scheduleDelayedTask(
00974      usecsToDelay, (TaskFunc*)periodicQOSMeasurement, (void*)NULL);
00975 }

void sessionAfterPlaying ( void *  clientData = NULL  ) 

Definition at line 900 of file playCommon.cpp.

References arrivalCheckTimerTask, continueAfterPLAY(), endTime, env, initialSeekTime, interPacketGapCheckTimerTask, NULL, playContinuously, qosMeasurementTimerTask, scale, session, sessionTimerTask, shutdown(), startPlayingSession(), UsageEnvironment::taskScheduler(), totNumPacketsReceived, and TaskScheduler::unscheduleDelayedTask().

Referenced by checkInterPacketGaps(), sessionTimerHandler(), setupStreams(), and subsessionAfterPlaying().

00900                                                {
00901   if (!playContinuously) {
00902     shutdown(0);
00903   } else {
00904     // We've been asked to play the stream(s) over again.
00905     // First, reset state from the current session:
00906     if (env != NULL) {
00907       env->taskScheduler().unscheduleDelayedTask(sessionTimerTask);
00908       env->taskScheduler().unscheduleDelayedTask(arrivalCheckTimerTask);
00909       env->taskScheduler().unscheduleDelayedTask(interPacketGapCheckTimerTask);
00910       env->taskScheduler().unscheduleDelayedTask(qosMeasurementTimerTask);
00911     }
00912     totNumPacketsReceived = ~0;
00913 
00914     startPlayingSession(session, initialSeekTime, endTime, scale, continueAfterPLAY);
00915   }
00916 }

void sessionTimerHandler ( void *  clientData  ) 

Definition at line 918 of file playCommon.cpp.

References NULL, sessionAfterPlaying(), and sessionTimerTask.

Referenced by continueAfterPLAY().

00918                                                {
00919   sessionTimerTask = NULL;
00920 
00921   sessionAfterPlaying();
00922 }

void setupStreams (  ) 

Definition at line 655 of file playCommon.cpp.

References FileSink::addData(), aviOut, MediaSubsession::clientPortNum(), MediaSubsession::codecName(), continueAfterPLAY(), continueAfterSETUP(), FileSink::createNew(), H264VideoFileSink::createNew(), AMRAudioFileSink::createNew(), AVIFileSink::createNew(), QuickTimeFileSink::createNew(), createReceivers, duration, endTime, env, False, fileNamePrefix, fileSinkBufferSize, MediaSubsession::fmtp_config(), MediaSubsession::fmtp_spropparametersets(), generateHintTracks, generateMP4Format, UsageEnvironment::getResultMsg(), initialSeekTime, iter, madeProgress, MediaSubsession::mediumName(), movieFPS, movieHeight, movieWidth, MediaSubsessionIterator::next(), NULL, oneFilePerFrame, outputAVIFile, outputQuickTimeFile, packetLossCompensate, parseGeneralConfigStr(), MediaSession::playEndTime(), qtOut, MediaSubsession::readSource(), MediaSubsession::rtcpInstance(), scale, session, sessionAfterPlaying(), RTCPInstance::setByeHandler(), setupSubsession(), shutdown(), singleMedium, MediaSubsession::sink, MediaSink::startPlaying(), AVIFileSink::startPlaying(), QuickTimeFileSink::startPlaying(), startPlayingSession(), streamUsingTCP, subsession, subsessionAfterPlaying(), subsessionByeHandler(), syncStreams, and True.

Referenced by continueAfterDESCRIBE(), and continueAfterSETUP().

00655                     {
00656   static MediaSubsessionIterator* setupIter = NULL;
00657   if (setupIter == NULL) setupIter = new MediaSubsessionIterator(*session);
00658   while ((subsession = setupIter->next()) != NULL) {
00659     // We have another subsession left to set up:
00660     if (subsession->clientPortNum() == 0) continue; // port # was not set
00661 
00662     setupSubsession(subsession, streamUsingTCP, continueAfterSETUP);
00663     return;
00664   }
00665 
00666   // We're done setting up subsessions.
00667   delete setupIter;
00668   if (!madeProgress) shutdown();
00669 
00670   // Create output files:
00671   if (createReceivers) {
00672     if (outputQuickTimeFile) {
00673       // Create a "QuickTimeFileSink", to write to 'stdout':
00674       qtOut = QuickTimeFileSink::createNew(*env, *session, "stdout",
00675                                            fileSinkBufferSize,
00676                                            movieWidth, movieHeight,
00677                                            movieFPS,
00678                                            packetLossCompensate,
00679                                            syncStreams,
00680                                            generateHintTracks,
00681                                            generateMP4Format);
00682       if (qtOut == NULL) {
00683         *env << "Failed to create QuickTime file sink for stdout: " << env->getResultMsg();
00684         shutdown();
00685       }
00686 
00687       qtOut->startPlaying(sessionAfterPlaying, NULL);
00688     } else if (outputAVIFile) {
00689       // Create an "AVIFileSink", to write to 'stdout':
00690       aviOut = AVIFileSink::createNew(*env, *session, "stdout",
00691                                       fileSinkBufferSize,
00692                                       movieWidth, movieHeight,
00693                                       movieFPS,
00694                                       packetLossCompensate);
00695       if (aviOut == NULL) {
00696         *env << "Failed to create AVI file sink for stdout: " << env->getResultMsg();
00697         shutdown();
00698       }
00699 
00700       aviOut->startPlaying(sessionAfterPlaying, NULL);
00701     } else {
00702       // Create and start "FileSink"s for each subsession:
00703       madeProgress = False;
00704       MediaSubsessionIterator iter(*session);
00705       while ((subsession = iter.next()) != NULL) {
00706         if (subsession->readSource() == NULL) continue; // was not initiated
00707 
00708         // Create an output file for each desired stream:
00709         char outFileName[1000];
00710         if (singleMedium == NULL) {
00711           // Output file name is
00712           //     "<filename-prefix><medium_name>-<codec_name>-<counter>"
00713           static unsigned streamCounter = 0;
00714           snprintf(outFileName, sizeof outFileName, "%s%s-%s-%d",
00715                    fileNamePrefix, subsession->mediumName(),
00716                    subsession->codecName(), ++streamCounter);
00717         } else {
00718           sprintf(outFileName, "stdout");
00719         }
00720         FileSink* fileSink;
00721         if (strcmp(subsession->mediumName(), "audio") == 0 &&
00722             (strcmp(subsession->codecName(), "AMR") == 0 ||
00723              strcmp(subsession->codecName(), "AMR-WB") == 0)) {
00724           // For AMR audio streams, we use a special sink that inserts AMR frame hdrs:
00725           fileSink = AMRAudioFileSink::createNew(*env, outFileName,
00726                                                  fileSinkBufferSize, oneFilePerFrame);
00727         } else if (strcmp(subsession->mediumName(), "video") == 0 &&
00728             (strcmp(subsession->codecName(), "H264") == 0)) {
00729           // For H.264 video stream, we use a special sink that insert start_codes:
00730           fileSink = H264VideoFileSink::createNew(*env, outFileName,
00731                                                   subsession->fmtp_spropparametersets(),
00732                                                   fileSinkBufferSize, oneFilePerFrame);
00733         } else {
00734           // Normal case:
00735           fileSink = FileSink::createNew(*env, outFileName,
00736                                          fileSinkBufferSize, oneFilePerFrame);
00737         }
00738         subsession->sink = fileSink;
00739         if (subsession->sink == NULL) {
00740           *env << "Failed to create FileSink for \"" << outFileName
00741                   << "\": " << env->getResultMsg() << "\n";
00742         } else {
00743           if (singleMedium == NULL) {
00744             *env << "Created output file: \"" << outFileName << "\"\n";
00745           } else {
00746             *env << "Outputting data from the \"" << subsession->mediumName()
00747                         << "/" << subsession->codecName()
00748                         << "\" subsession to 'stdout'\n";
00749           }
00750 
00751           if (strcmp(subsession->mediumName(), "video") == 0 &&
00752               strcmp(subsession->codecName(), "MP4V-ES") == 0 &&
00753               subsession->fmtp_config() != NULL) {
00754             // For MPEG-4 video RTP streams, the 'config' information
00755             // from the SDP description contains useful VOL etc. headers.
00756             // Insert this data at the front of the output file:
00757             unsigned configLen;
00758             unsigned char* configData
00759               = parseGeneralConfigStr(subsession->fmtp_config(), configLen);
00760             struct timeval timeNow;
00761             gettimeofday(&timeNow, NULL);
00762             fileSink->addData(configData, configLen, timeNow);
00763             delete[] configData;
00764           }
00765 
00766           subsession->sink->startPlaying(*(subsession->readSource()),
00767                                          subsessionAfterPlaying,
00768                                          subsession);
00769 
00770           // Also set a handler to be called if a RTCP "BYE" arrives
00771           // for this subsession:
00772           if (subsession->rtcpInstance() != NULL) {
00773             subsession->rtcpInstance()->setByeHandler(subsessionByeHandler, subsession);
00774           }
00775 
00776           madeProgress = True;
00777         }
00778       }
00779       if (!madeProgress) shutdown();
00780     }
00781   }
00782 
00783   // Finally, start playing each subsession, to start the data flow:
00784   if (duration == 0) {
00785     if (scale > 0) duration = session->playEndTime() - initialSeekTime; // use SDP end time
00786     else if (scale < 0) duration = initialSeekTime;
00787   }
00788   if (duration < 0) duration = 0.0;
00789 
00790   endTime = initialSeekTime;
00791   if (scale > 0) {
00792     if (duration <= 0) endTime = -1.0f;
00793     else endTime = initialSeekTime + duration;
00794   } else {
00795     endTime = initialSeekTime - duration;
00796     if (endTime < 0) endTime = 0.0f;
00797   }
00798 
00799   startPlayingSession(session, initialSeekTime, endTime, scale, continueAfterPLAY);
00800 }

void shutdown ( int  exitCode = 1  ) 

Definition at line 1133 of file playCommon.cpp.

References areAlreadyShuttingDown, arrivalCheckTimerTask, continueAfterTEARDOWN(), env, interPacketGapCheckTimerTask, NULL, printQOSData(), qosMeasurementIntervalMS, qosMeasurementTimerTask, session, sessionTimerTask, shutdownExitCode, UsageEnvironment::taskScheduler(), tearDownSession(), True, and TaskScheduler::unscheduleDelayedTask().

Referenced by continueAfterDESCRIBE(), continueAfterOPTIONS(), continueAfterPLAY(), main(), sessionAfterPlaying(), setupStreams(), signalHandlerShutdown(), and usage().

01133                             {
01134   if (areAlreadyShuttingDown) return; // in case we're called after receiving a RTCP "BYE" while in the middle of a "TEARDOWN".
01135   areAlreadyShuttingDown = True;
01136 
01137   shutdownExitCode = exitCode;
01138   if (env != NULL) {
01139     env->taskScheduler().unscheduleDelayedTask(sessionTimerTask);
01140     env->taskScheduler().unscheduleDelayedTask(arrivalCheckTimerTask);
01141     env->taskScheduler().unscheduleDelayedTask(interPacketGapCheckTimerTask);
01142     env->taskScheduler().unscheduleDelayedTask(qosMeasurementTimerTask);
01143   }
01144 
01145   if (qosMeasurementIntervalMS > 0) {
01146     printQOSData(exitCode);
01147   }
01148 
01149   // Teardown, then shutdown, any outstanding RTP/RTCP subsessions
01150   if (session != NULL) {
01151     tearDownSession(session, continueAfterTEARDOWN);
01152   } else {
01153     continueAfterTEARDOWN(NULL, 0, NULL);
01154   }
01155 }

void signalHandlerShutdown ( int  sig  ) 

Definition at line 1170 of file playCommon.cpp.

References env, and shutdown().

Referenced by main().

01170                                         {
01171   *env << "Got shutdown signal\n";
01172   shutdown(0);
01173 }

void subsessionAfterPlaying ( void *  clientData  ) 

Definition at line 868 of file playCommon.cpp.

References Medium::close(), iter, MediaSubsessionIterator::next(), NULL, MediaSubsession::parentSession(), session, sessionAfterPlaying(), MediaSubsession::sink, and subsession.

Referenced by setupStreams(), and subsessionByeHandler().

00868                                               {
00869   // Begin by closing this media subsession's stream:
00870   MediaSubsession* subsession = (MediaSubsession*)clientData;
00871   Medium::close(subsession->sink);
00872   subsession->sink = NULL;
00873 
00874   // Next, check whether *all* subsessions' streams have now been closed:
00875   MediaSession& session = subsession->parentSession();
00876   MediaSubsessionIterator iter(session);
00877   while ((subsession = iter.next()) != NULL) {
00878     if (subsession->sink != NULL) return; // this subsession is still active
00879   }
00880 
00881   // All subsessions' streams have now been closed
00882   sessionAfterPlaying();
00883 }

void subsessionByeHandler ( void *  clientData  ) 

Definition at line 885 of file playCommon.cpp.

References MediaSubsession::codecName(), env, MediaSubsession::mediumName(), NULL, startTime, subsession, and subsessionAfterPlaying().

Referenced by setupStreams().

00885                                             {
00886   struct timeval timeNow;
00887   gettimeofday(&timeNow, NULL);
00888   unsigned secsDiff = timeNow.tv_sec - startTime.tv_sec;
00889 
00890   MediaSubsession* subsession = (MediaSubsession*)clientData;
00891   *env << "Received RTCP \"BYE\" on \"" << subsession->mediumName()
00892         << "/" << subsession->codecName()
00893         << "\" subsession (after " << secsDiff
00894         << " seconds)\n";
00895 
00896   // Act now as if the subsession had closed:
00897   subsessionAfterPlaying(subsession);
00898 }

void usage (  ) 

Definition at line 112 of file playCommon.cpp.

References allowProxyServers, controlConnectionUsesTCP, env, progName, shutdown(), and supportCodecSelection.

00112              {
00113   *env << "Usage: " << progName
00114        << " [-p <startPortNum>] [-r|-q|-4|-i] [-a|-v] [-V] [-d <duration>] [-D <max-inter-packet-gap-time> [-c] [-S <offset>] [-n] [-O]"
00115            << (controlConnectionUsesTCP ? " [-t|-T <http-port>]" : "")
00116        << " [-u <username> <password>"
00117            << (allowProxyServers ? " [<proxy-server> [<proxy-server-port>]]" : "")
00118        << "]" << (supportCodecSelection ? " [-A <audio-codec-rtp-payload-format-code>|-M <mime-subtype-name>]" : "")
00119        << " [-s <initial-seek-time>] [-z <scale>]"
00120        << " [-w <width> -h <height>] [-f <frames-per-second>] [-y] [-H] [-Q [<measurement-interval>]] [-F <filename-prefix>] [-b <file-sink-buffer-size>] [-B <input-socket-buffer-size>] [-I <input-interface-ip-address>] [-m] <url> (or " << progName << " -o [-V] <url>)\n";
00121   shutdown();
00122 }


Variable Documentation

Boolean areAlreadyShuttingDown = False

Definition at line 1131 of file playCommon.cpp.

Referenced by shutdown().

TaskToken arrivalCheckTimerTask = NULL

Definition at line 61 of file playCommon.cpp.

Referenced by checkForPacketArrival(), sessionAfterPlaying(), and shutdown().

Boolean audioOnly = False

Definition at line 70 of file playCommon.cpp.

Referenced by main().

AVIFileSink* aviOut = NULL

Definition at line 69 of file playCommon.cpp.

Referenced by checkForPacketArrival(), closeMediaSinks(), and setupStreams().

Boolean createReceivers = True

Definition at line 64 of file playCommon.cpp.

Referenced by continueAfterDESCRIBE(), continueAfterPLAY(), main(), and setupStreams().

unsigned char desiredAudioRTPPayloadFormat = 0

Definition at line 94 of file playCommon.cpp.

Referenced by createClient(), and main().

unsigned short desiredPortNum = 0

Definition at line 88 of file playCommon.cpp.

Referenced by continueAfterDESCRIBE(), getSDPDescription(), and main().

double duration = 0

Definition at line 74 of file playCommon.cpp.

double durationSlop = -1.0

Definition at line 75 of file playCommon.cpp.

Referenced by continueAfterPLAY(), and main().

double endTime

Definition at line 78 of file playCommon.cpp.

Referenced by continueAfterPLAY(), sessionAfterPlaying(), and setupStreams().

UsageEnvironment* env

Definition at line 55 of file playCommon.cpp.

char const* fileNamePrefix = ""

Definition at line 102 of file playCommon.cpp.

Referenced by main(), and setupStreams().

unsigned fileSinkBufferSize = 100000

Definition at line 103 of file playCommon.cpp.

Referenced by continueAfterDESCRIBE(), main(), and setupStreams().

Boolean generateHintTracks = False

Definition at line 107 of file playCommon.cpp.

Referenced by main(), and setupStreams().

Boolean generateMP4Format = False

Definition at line 66 of file playCommon.cpp.

Referenced by main(), and setupStreams().

double initialSeekTime = 0.0f

Definition at line 76 of file playCommon.cpp.

Referenced by continueAfterPLAY(), main(), sessionAfterPlaying(), and setupStreams().

TaskToken interPacketGapCheckTimerTask = NULL

Definition at line 62 of file playCommon.cpp.

Referenced by checkInterPacketGaps(), sessionAfterPlaying(), and shutdown().

unsigned interPacketGapMaxTime = 0

Definition at line 79 of file playCommon.cpp.

Referenced by checkInterPacketGaps(), and main().

Boolean madeProgress = False

Definition at line 637 of file playCommon.cpp.

Referenced by continueAfterDESCRIBE(), continueAfterSETUP(), and setupStreams().

char* mimeSubtype = NULL

Definition at line 95 of file playCommon.cpp.

Referenced by createClient(), and main().

unsigned movieFPS = 15

Definition at line 100 of file playCommon.cpp.

Referenced by main(), and setupStreams().

Boolean movieFPSOptionSet = False

Definition at line 101 of file playCommon.cpp.

Referenced by main().

unsigned short movieHeight = 180

Definition at line 98 of file playCommon.cpp.

Referenced by main(), and setupStreams().

Boolean movieHeightOptionSet = False

Definition at line 99 of file playCommon.cpp.

Referenced by main().

unsigned short movieWidth = 240

Definition at line 96 of file playCommon.cpp.

Referenced by main(), and setupStreams().

Boolean movieWidthOptionSet = False

Definition at line 97 of file playCommon.cpp.

Referenced by main().

unsigned nextQOSMeasurementUSecs [static]

Definition at line 963 of file playCommon.cpp.

Referenced by beginQOSMeasurement(), and scheduleNextQOSMeasurement().

Boolean notifyOnPacketArrival = False

Definition at line 86 of file playCommon.cpp.

Referenced by checkForPacketArrival(), and main().

Boolean oneFilePerFrame = False

Definition at line 85 of file playCommon.cpp.

Referenced by main(), and setupStreams().

Authenticator* ourAuthenticator = NULL

Definition at line 57 of file playCommon.cpp.

Referenced by continueAfterTEARDOWN(), getOptions(), getSDPDescription(), main(), setupSubsession(), startPlayingSession(), and tearDownSession().

Medium* ourClient = NULL

Definition at line 56 of file playCommon.cpp.

Referenced by continueAfterTEARDOWN(), and main().

Boolean outputAVIFile = False

Definition at line 68 of file playCommon.cpp.

Referenced by main(), and setupStreams().

Boolean outputQuickTimeFile = False

Definition at line 65 of file playCommon.cpp.

Referenced by main(), and setupStreams().

Boolean packetLossCompensate = False

Definition at line 105 of file playCommon.cpp.

Referenced by main(), and setupStreams().

char* password = NULL

Definition at line 91 of file playCommon.cpp.

Referenced by RTSPServer::RTSPClientSession::authenticationOK(), getSDPDescription(), SIPClient::invite(), main(), RTSPClient::openConnection(), UserAuthenticationDatabase::removeUserRecord(), RTSPClient::sendRequest(), and UserAuthenticationDatabase::~UserAuthenticationDatabase().

Boolean playContinuously = False

Definition at line 81 of file playCommon.cpp.

Referenced by main(), and sessionAfterPlaying().

char const* progName

Definition at line 54 of file playCommon.cpp.

Referenced by main(), and usage().

char* proxyServerName = NULL

Definition at line 92 of file playCommon.cpp.

Referenced by getSDPDescription(), and main().

unsigned short proxyServerPortNum = 0

Definition at line 93 of file playCommon.cpp.

Referenced by getSDPDescription(), and main().

unsigned qosMeasurementIntervalMS = 0

Definition at line 108 of file playCommon.cpp.

Referenced by continueAfterPLAY(), main(), printQOSData(), scheduleNextQOSMeasurement(), and shutdown().

TaskToken qosMeasurementTimerTask = NULL

Definition at line 63 of file playCommon.cpp.

Referenced by scheduleNextQOSMeasurement(), sessionAfterPlaying(), and shutdown().

qosMeasurementRecord* qosRecordHead = NULL [static]

Definition at line 959 of file playCommon.cpp.

Referenced by beginQOSMeasurement(), periodicQOSMeasurement(), and printQOSData().

QuickTimeFileSink* qtOut = NULL

Definition at line 67 of file playCommon.cpp.

Referenced by checkForPacketArrival(), closeMediaSinks(), and setupStreams().

float scale = 1.0f

Definition at line 77 of file playCommon.cpp.

Referenced by continueAfterPLAY(), RTSPServer::RTSPClientSession::handleCmd_PLAY(), main(), sessionAfterPlaying(), and setupStreams().

Boolean sendOptionsRequest = True

Definition at line 83 of file playCommon.cpp.

Referenced by main().

Boolean sendOptionsRequestOnly = False

Definition at line 84 of file playCommon.cpp.

Referenced by continueAfterOPTIONS(), and main().

MediaSession* session = NULL

Definition at line 59 of file playCommon.cpp.

Referenced by beginQOSMeasurement(), checkForPacketArrival(), checkInterPacketGaps(), closeMediaSinks(), continueAfterDESCRIBE(), continueAfterPLAY(), continueAfterTEARDOWN(), RTSPServer::RTSPClientSession::handleAlternativeRequestByte(), RTSPServer::RTSPClientSession::handleCmd_DESCRIBE(), RTSPServerSupportingHTTPStreaming::RTSPClientSessionSupportingHTTPStreaming::handleHTTPCmd_StreamingGET(), RTSPClient::handlePLAYResponse(), RTSPServer::RTSPClientSession::incomingRequestHandler(), printQOSData(), RTSPClient::sendGetParameterCommand(), RTSPClient::sendPauseCommand(), RTSPClient::sendPlayCommand(), RTSPClient::sendRecordCommand(), RTSPClient::sendSetParameterCommand(), RTSPClient::sendTeardownCommand(), sessionAfterPlaying(), RTSPClient::sessionURL(), setupStreams(), shutdown(), startPlayingSession(), subsessionAfterPlaying(), and tearDownSession().

TaskToken sessionTimerTask = NULL

Definition at line 60 of file playCommon.cpp.

Referenced by continueAfterPLAY(), sessionAfterPlaying(), sessionTimerHandler(), and shutdown().

int shutdownExitCode

Definition at line 1132 of file playCommon.cpp.

Referenced by continueAfterTEARDOWN(), and shutdown().

int simpleRTPoffsetArg = -1

Definition at line 82 of file playCommon.cpp.

Referenced by continueAfterDESCRIBE(), and main().

char const* singleMedium = NULL

Definition at line 72 of file playCommon.cpp.

Referenced by continueAfterDESCRIBE(), main(), and setupStreams().

unsigned socketInputBufferSize = 0

Definition at line 104 of file playCommon.cpp.

Referenced by continueAfterDESCRIBE(), and main().

struct timeval startTime

Definition at line 110 of file playCommon.cpp.

Referenced by main(), and subsessionByeHandler().

char const* streamURL = NULL

Definition at line 58 of file playCommon.cpp.

Referenced by continueAfterDESCRIBE(), getSDPDescription(), and main().

Boolean streamUsingTCP = False

Definition at line 87 of file playCommon.cpp.

Referenced by main(), RTSPClient::sendRequest(), and setupStreams().

MediaSubsession* subsession

Definition at line 636 of file playCommon.cpp.

Boolean syncStreams = False

Definition at line 106 of file playCommon.cpp.

Referenced by checkForPacketArrival(), main(), and setupStreams().

unsigned totNumPacketsReceived = ~0

Definition at line 80 of file playCommon.cpp.

Referenced by checkInterPacketGaps(), qosMeasurementRecord::periodicQOSMeasurement(), printQOSData(), and sessionAfterPlaying().

portNumBits tunnelOverHTTPPortNum = 0

Definition at line 89 of file playCommon.cpp.

Referenced by createClient(), and main().

char* username = NULL

Definition at line 90 of file playCommon.cpp.

Referenced by RTSPServer::RTSPClientSession::authenticationOK(), getSDPDescription(), SIPClient::invite(), main(), RTSPClient::openConnection(), and RTSPClient::sendRequest().

int verbosityLevel = 1

Definition at line 73 of file playCommon.cpp.

Referenced by main().

Boolean videoOnly = False

Definition at line 71 of file playCommon.cpp.

Referenced by main().


Generated on Thu May 17 07:14:36 2012 for live by  doxygen 1.5.2