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 1028 of file playCommon.cpp.

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

Referenced by continueAfterPLAY().

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

void checkForPacketArrival ( void *  clientData  ) 

Definition at line 1174 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().

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

void checkInterPacketGaps ( void *  clientData  ) 

Definition at line 1232 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().

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

void closeMediaSinks (  ) 

Definition at line 854 of file playCommon.cpp.

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

Referenced by continueAfterTEARDOWN().

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

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

Definition at line 533 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.

Referenced by continueAfterOPTIONS(), and openURL().

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

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

Definition at line 518 of file playCommon.cpp.

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

Referenced by main().

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

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

Definition at line 801 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().

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

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

Definition at line 637 of file playCommon.cpp.

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

Referenced by setupNextSubsession(), and setupStreams().

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

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

Definition at line 1156 of file playCommon.cpp.

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

Referenced by shutdown().

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

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       username = argv[2];
00301       password = argv[3];
00302       argv+=2; argc-=2;
00303       if (allowProxyServers && argc > 3 && argv[2][0] != '-') {
00304         // The next argument is the name of a proxy server:
00305         proxyServerName = argv[2];
00306         ++argv; --argc;
00307 
00308         if (argc > 3 && argv[2][0] != '-') {
00309           // The next argument is the proxy server port number:
00310           if (sscanf(argv[2], "%hu", &proxyServerPortNum) != 1) {
00311             usage();
00312           }
00313           ++argv; --argc;
00314         }
00315       }
00316 
00317       ourAuthenticator = new Authenticator(username, password);
00318       break;
00319     }
00320 
00321     case 'A': { // specify a desired audio RTP payload format
00322       unsigned formatArg;
00323       if (sscanf(argv[2], "%u", &formatArg) != 1
00324           || formatArg >= 96) {
00325         usage();
00326       }
00327       desiredAudioRTPPayloadFormat = (unsigned char)formatArg;
00328       ++argv; --argc;
00329       break;
00330     }
00331 
00332     case 'M': { // specify a MIME subtype for a dynamic RTP payload type
00333       mimeSubtype = argv[2];
00334       if (desiredAudioRTPPayloadFormat==0) desiredAudioRTPPayloadFormat =96;
00335       ++argv; --argc;
00336       break;
00337     }
00338 
00339     case 'w': { // specify a width (pixels) for an output QuickTime or AVI movie
00340       if (sscanf(argv[2], "%hu", &movieWidth) != 1) {
00341         usage();
00342       }
00343       movieWidthOptionSet = True;
00344       ++argv; --argc;
00345       break;
00346     }
00347 
00348     case 'h': { // specify a height (pixels) for an output QuickTime or AVI movie
00349       if (sscanf(argv[2], "%hu", &movieHeight) != 1) {
00350         usage();
00351       }
00352       movieHeightOptionSet = True;
00353       ++argv; --argc;
00354       break;
00355     }
00356 
00357     case 'f': { // specify a frame rate (per second) for an output QT or AVI movie
00358       if (sscanf(argv[2], "%u", &movieFPS) != 1) {
00359         usage();
00360       }
00361       movieFPSOptionSet = True;
00362       ++argv; --argc;
00363       break;
00364     }
00365 
00366     case 'F': { // specify a prefix for the audio and video output files
00367       fileNamePrefix = argv[2];
00368       ++argv; --argc;
00369       break;
00370     }
00371 
00372     case 'b': { // specify the size of buffers for "FileSink"s
00373       if (sscanf(argv[2], "%u", &fileSinkBufferSize) != 1) {
00374         usage();
00375       }
00376       ++argv; --argc;
00377       break;
00378     }
00379 
00380     case 'B': { // specify the size of input socket buffers
00381       if (sscanf(argv[2], "%u", &socketInputBufferSize) != 1) {
00382         usage();
00383       }
00384       ++argv; --argc;
00385       break;
00386     }
00387 
00388     // Note: The following option is deprecated, and may someday be removed:
00389     case 'l': { // try to compensate for packet loss by repeating frames
00390       packetLossCompensate = True;
00391       break;
00392     }
00393 
00394     case 'y': { // synchronize audio and video streams
00395       syncStreams = True;
00396       break;
00397     }
00398 
00399     case 'H': { // generate hint tracks (as well as the regular data tracks)
00400       generateHintTracks = True;
00401       break;
00402     }
00403 
00404     case 'Q': { // output QOS measurements
00405       qosMeasurementIntervalMS = 1000; // default: 1 second
00406 
00407       if (argc > 3 && argv[2][0] != '-') {
00408         // The next argument is the measurement interval,
00409         // in multiples of 100 ms
00410         if (sscanf(argv[2], "%u", &qosMeasurementIntervalMS) != 1) {
00411           usage();
00412         }
00413         qosMeasurementIntervalMS *= 100;
00414         ++argv; --argc;
00415       }
00416       break;
00417     }
00418 
00419     case 's': { // specify initial seek time (trick play)
00420       double arg;
00421       if (sscanf(argv[2], "%lg", &arg) != 1 || arg < 0) {
00422         usage();
00423       }
00424       initialSeekTime = arg;
00425       ++argv; --argc;
00426       break;
00427     }
00428 
00429     case 'z': { // scale (trick play)
00430       float arg;
00431       if (sscanf(argv[2], "%g", &arg) != 1 || arg == 0.0f) {
00432         usage();
00433       }
00434       scale = arg;
00435       ++argv; --argc;
00436       break;
00437     }
00438 
00439     default: {
00440       usage();
00441       break;
00442     }
00443     }
00444 
00445     ++argv; --argc;
00446   }
00447   if (argc != 2) usage();
00448   if (outputQuickTimeFile && outputAVIFile) {
00449     *env << "The -i and -q (or -4) flags cannot both be used!\n";
00450     usage();
00451   }
00452   Boolean outputCompositeFile = outputQuickTimeFile || outputAVIFile;
00453   if (!createReceivers && outputCompositeFile) {
00454     *env << "The -r and -q (or -4 or -i) flags cannot both be used!\n";
00455     usage();
00456   }
00457   if (outputCompositeFile && !movieWidthOptionSet) {
00458     *env << "Warning: The -q, -4 or -i option was used, but not -w.  Assuming a video width of "
00459          << movieWidth << " pixels\n";
00460   }
00461   if (outputCompositeFile && !movieHeightOptionSet) {
00462     *env << "Warning: The -q, -4 or -i option was used, but not -h.  Assuming a video height of "
00463          << movieHeight << " pixels\n";
00464   }
00465   if (outputCompositeFile && !movieFPSOptionSet) {
00466     *env << "Warning: The -q, -4 or -i option was used, but not -f.  Assuming a video frame rate of "
00467          << movieFPS << " frames-per-second\n";
00468   }
00469   if (audioOnly && videoOnly) {
00470     *env << "The -a and -v flags cannot both be used!\n";
00471     usage();
00472   }
00473   if (sendOptionsRequestOnly && !sendOptionsRequest) {
00474     *env << "The -o and -O flags cannot both be used!\n";
00475     usage();
00476   }
00477   if (tunnelOverHTTPPortNum > 0) {
00478     if (streamUsingTCP) {
00479       *env << "The -t and -T flags cannot both be used!\n";
00480       usage();
00481     } else {
00482       streamUsingTCP = True;
00483     }
00484   }
00485   if (!createReceivers && notifyOnPacketArrival) {
00486     *env << "Warning: Because we're not receiving stream data, the -n flag has no effect\n";
00487   }
00488   if (durationSlop < 0) {
00489     // This parameter wasn't set, so use a default value.
00490     // If we're measuring QOS stats, then don't add any slop, to avoid
00491     // having 'empty' measurement intervals at the end.
00492     durationSlop = qosMeasurementIntervalMS > 0 ? 0.0 : 5.0;
00493   }
00494 
00495   streamURL = argv[1];
00496 
00497   // Create our client object:
00498   ourClient = createClient(*env, streamURL, verbosityLevel, progName);
00499   if (ourClient == NULL) {
00500     *env << "Failed to create " << clientProtocolName
00501                 << " client: " << env->getResultMsg() << "\n";
00502     shutdown();
00503   }
00504 
00505   if (sendOptionsRequest) {
00506     // Begin by sending an "OPTIONS" command:
00507     getOptions(continueAfterOPTIONS);
00508   } else {
00509     continueAfterOPTIONS(NULL, 0, NULL);
00510   }
00511 
00512   // All subsequent activity takes place within the event loop:
00513   env->taskScheduler().doEventLoop(); // does not return
00514 
00515   return 0; // only to prevent compiler warning
00516 }

static void periodicQOSMeasurement ( void *  clientData  )  [static]

Definition at line 976 of file playCommon.cpp.

References NULL, qosRecordHead, and scheduleNextQOSMeasurement().

Referenced by scheduleNextQOSMeasurement().

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

void printQOSData ( int  exitCode  ) 

Definition at line 1051 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().

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

static void scheduleNextQOSMeasurement (  )  [static]

Definition at line 964 of file playCommon.cpp.

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

Referenced by beginQOSMeasurement(), and periodicQOSMeasurement().

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

void sessionAfterPlaying ( void *  clientData = NULL  ) 

Definition at line 899 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().

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

void sessionTimerHandler ( void *  clientData  ) 

Definition at line 917 of file playCommon.cpp.

References NULL, sessionAfterPlaying(), and sessionTimerTask.

Referenced by continueAfterPLAY().

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

void setupStreams (  ) 

Definition at line 654 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().

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

void shutdown ( int  exitCode = 1  ) 

Definition at line 1132 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().

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

void signalHandlerShutdown ( int  sig  ) 

Definition at line 1169 of file playCommon.cpp.

References env, and shutdown().

Referenced by main().

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

void subsessionAfterPlaying ( void *  clientData  ) 

Definition at line 867 of file playCommon.cpp.

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

Referenced by setupStreams(), and subsessionByeHandler().

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

void subsessionByeHandler ( void *  clientData  ) 

Definition at line 884 of file playCommon.cpp.

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

Referenced by setupStreams().

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

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 1130 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 636 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 962 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 958 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 1131 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 635 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 Feb 2 23:54:12 2012 for live by  doxygen 1.5.2