00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "playCommon.hh"
00021 #include "BasicUsageEnvironment.hh"
00022 #include "GroupsockHelper.hh"
00023 #ifdef SUPPORT_REAL_RTSP
00024 #include "../RealRTSP/include/RealRTSP.hh"
00025 #endif
00026
00027 #if defined(__WIN32__) || defined(_WIN32)
00028 #define snprintf _snprintf
00029 #else
00030 #include <signal.h>
00031 #define USE_SIGNALS 1
00032 #endif
00033
00034
00035 void setupStreams();
00036 void startPlayingStreams();
00037 void tearDownStreams();
00038 void closeMediaSinks();
00039 void subsessionAfterPlaying(void* clientData);
00040 void subsessionByeHandler(void* clientData);
00041 void sessionAfterPlaying(void* clientData = NULL);
00042 void sessionTimerHandler(void* clientData);
00043 void shutdown(int exitCode = 1);
00044 void signalHandlerShutdown(int sig);
00045 void checkForPacketArrival(void* clientData);
00046 void checkInterPacketGaps(void* clientData);
00047 void beginQOSMeasurement();
00048
00049 char const* progName;
00050 UsageEnvironment* env;
00051 Medium* ourClient = NULL;
00052 MediaSession* session = NULL;
00053 TaskToken sessionTimerTask = NULL;
00054 TaskToken arrivalCheckTimerTask = NULL;
00055 TaskToken interPacketGapCheckTimerTask = NULL;
00056 TaskToken qosMeasurementTimerTask = NULL;
00057 Boolean createReceivers = True;
00058 Boolean outputQuickTimeFile = False;
00059 Boolean generateMP4Format = False;
00060 QuickTimeFileSink* qtOut = NULL;
00061 Boolean outputAVIFile = False;
00062 AVIFileSink* aviOut = NULL;
00063 Boolean audioOnly = False;
00064 Boolean videoOnly = False;
00065 char const* singleMedium = NULL;
00066 int verbosityLevel = 1;
00067 double duration = 0;
00068 double durationSlop = -1.0;
00069 double initialSeekTime = 0.0f;
00070 double scale = 1.0f;
00071 unsigned interPacketGapMaxTime = 0;
00072 unsigned totNumPacketsReceived = ~0;
00073 Boolean playContinuously = False;
00074 int simpleRTPoffsetArg = -1;
00075 Boolean sendOptionsRequest = True;
00076 Boolean sendOptionsRequestOnly = False;
00077 Boolean oneFilePerFrame = False;
00078 Boolean notifyOnPacketArrival = False;
00079 Boolean streamUsingTCP = False;
00080 portNumBits tunnelOverHTTPPortNum = 0;
00081 char* username = NULL;
00082 char* password = NULL;
00083 char* proxyServerName = NULL;
00084 unsigned short proxyServerPortNum = 0;
00085 unsigned char desiredAudioRTPPayloadFormat = 0;
00086 char* mimeSubtype = NULL;
00087 unsigned short movieWidth = 240;
00088 Boolean movieWidthOptionSet = False;
00089 unsigned short movieHeight = 180;
00090 Boolean movieHeightOptionSet = False;
00091 unsigned movieFPS = 15;
00092 Boolean movieFPSOptionSet = False;
00093 char* fileNamePrefix = "";
00094 unsigned fileSinkBufferSize = 20000;
00095 unsigned socketInputBufferSize = 0;
00096 Boolean packetLossCompensate = False;
00097 Boolean syncStreams = False;
00098 Boolean generateHintTracks = False;
00099 unsigned qosMeasurementIntervalMS = 0;
00100 unsigned statusCode = 0;
00101
00102 struct timeval startTime;
00103
00104 void usage() {
00105 *env << "Usage: " << progName
00106 << " [-p <startPortNum>] [-r|-q|-4|-i] [-a|-v] [-V] [-d <duration>] [-D <max-inter-packet-gap-time> [-c] [-S <offset>] [-n] [-O]"
00107 << (controlConnectionUsesTCP ? " [-t|-T <http-port>]" : "")
00108 << " [-u <username> <password>"
00109 << (allowProxyServers ? " [<proxy-server> [<proxy-server-port>]]" : "")
00110 << "]" << (supportCodecSelection ? " [-A <audio-codec-rtp-payload-format-code>|-M <mime-subtype-name>]" : "")
00111 << " [-s <initial-seek-time>] [-z <scale>]"
00112 << " [-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";
00113
00114 shutdown();
00115 }
00116
00117 int main(int argc, char** argv) {
00118
00119 TaskScheduler* scheduler = BasicTaskScheduler::createNew();
00120 env = BasicUsageEnvironment::createNew(*scheduler);
00121
00122 progName = argv[0];
00123
00124 gettimeofday(&startTime, NULL);
00125
00126 #ifdef USE_SIGNALS
00127
00128 signal(SIGHUP, signalHandlerShutdown);
00129 signal(SIGUSR1, signalHandlerShutdown);
00130 #endif
00131
00132 unsigned short desiredPortNum = 0;
00133
00134
00135 while (argc > 2) {
00136 char* const opt = argv[1];
00137 if (opt[0] != '-') usage();
00138 switch (opt[1]) {
00139
00140 case 'p': {
00141 int portArg;
00142 if (sscanf(argv[2], "%d", &portArg) != 1) {
00143 usage();
00144 }
00145 if (portArg <= 0 || portArg >= 65536 || portArg&1) {
00146 *env << "bad port number: " << portArg
00147 << " (must be even, and in the range (0,65536))\n";
00148 usage();
00149 }
00150 desiredPortNum = (unsigned short)portArg;
00151 ++argv; --argc;
00152 break;
00153 }
00154
00155 case 'r': {
00156 createReceivers = False;
00157 break;
00158 }
00159
00160 case 'q': {
00161 outputQuickTimeFile = True;
00162 break;
00163 }
00164
00165 case '4': {
00166 outputQuickTimeFile = True;
00167 generateMP4Format = True;
00168 break;
00169 }
00170
00171 case 'i': {
00172 outputAVIFile = True;
00173 break;
00174 }
00175
00176 case 'I': {
00177 NetAddressList addresses(argv[2]);
00178 if (addresses.numAddresses() == 0) {
00179 *env << "Failed to find network address for \"" << argv[2] << "\"";
00180 break;
00181 }
00182 ReceivingInterfaceAddr = *(unsigned*)(addresses.firstAddress()->data());
00183 ++argv; --argc;
00184 break;
00185 }
00186
00187 case 'a': {
00188 audioOnly = True;
00189 singleMedium = "audio";
00190 break;
00191 }
00192
00193 case 'v': {
00194 videoOnly = True;
00195 singleMedium = "video";
00196 break;
00197 }
00198
00199 case 'V': {
00200 verbosityLevel = 0;
00201 break;
00202 }
00203
00204 case 'd': {
00205 float arg;
00206 if (sscanf(argv[2], "%g", &arg) != 1) {
00207 usage();
00208 }
00209 if (argv[2][0] == '-') {
00210
00211 duration = 0;
00212 durationSlop = -arg;
00213 } else {
00214 duration = arg;
00215 durationSlop = 0;
00216 }
00217 ++argv; --argc;
00218 break;
00219 }
00220
00221 case 'D': {
00222 if (sscanf(argv[2], "%u", &interPacketGapMaxTime) != 1) {
00223 usage();
00224 }
00225 ++argv; --argc;
00226 break;
00227 }
00228
00229 case 'c': {
00230 playContinuously = True;
00231 break;
00232 }
00233
00234 case 'S': {
00235 if (sscanf(argv[2], "%d", &simpleRTPoffsetArg) != 1) {
00236 usage();
00237 }
00238 if (simpleRTPoffsetArg < 0) {
00239 *env << "offset argument to \"-S\" must be >= 0\n";
00240 usage();
00241 }
00242 ++argv; --argc;
00243 break;
00244 }
00245
00246 case 'O': {
00247 sendOptionsRequest = False;
00248 break;
00249 }
00250
00251 case 'o': {
00252 sendOptionsRequestOnly = True;
00253 break;
00254 }
00255
00256 case 'm': {
00257 oneFilePerFrame = True;
00258 break;
00259 }
00260
00261 case 'n': {
00262 notifyOnPacketArrival = True;
00263 break;
00264 }
00265
00266 case 't': {
00267
00268 if (controlConnectionUsesTCP) {
00269 streamUsingTCP = True;
00270 } else {
00271 usage();
00272 }
00273 break;
00274 }
00275
00276 case 'T': {
00277
00278 if (controlConnectionUsesTCP) {
00279 if (argc > 3 && argv[2][0] != '-') {
00280
00281 if (sscanf(argv[2], "%hu", &tunnelOverHTTPPortNum) == 1
00282 && tunnelOverHTTPPortNum > 0) {
00283 ++argv; --argc;
00284 break;
00285 }
00286 }
00287 }
00288
00289
00290 usage();
00291 break;
00292 }
00293
00294 case 'u': {
00295 username = argv[2];
00296 password = argv[3];
00297 argv+=2; argc-=2;
00298 if (allowProxyServers && argc > 3 && argv[2][0] != '-') {
00299
00300 proxyServerName = argv[2];
00301 ++argv; --argc;
00302
00303 if (argc > 3 && argv[2][0] != '-') {
00304
00305 if (sscanf(argv[2], "%hu", &proxyServerPortNum) != 1) {
00306 usage();
00307 }
00308 ++argv; --argc;
00309 }
00310 }
00311 break;
00312 }
00313
00314 case 'A': {
00315 unsigned formatArg;
00316 if (sscanf(argv[2], "%u", &formatArg) != 1
00317 || formatArg >= 96) {
00318 usage();
00319 }
00320 desiredAudioRTPPayloadFormat = (unsigned char)formatArg;
00321 ++argv; --argc;
00322 break;
00323 }
00324
00325 case 'M': {
00326 mimeSubtype = argv[2];
00327 if (desiredAudioRTPPayloadFormat==0) desiredAudioRTPPayloadFormat =96;
00328 ++argv; --argc;
00329 break;
00330 }
00331
00332 case 'w': {
00333 if (sscanf(argv[2], "%hu", &movieWidth) != 1) {
00334 usage();
00335 }
00336 movieWidthOptionSet = True;
00337 ++argv; --argc;
00338 break;
00339 }
00340
00341 case 'h': {
00342 if (sscanf(argv[2], "%hu", &movieHeight) != 1) {
00343 usage();
00344 }
00345 movieHeightOptionSet = True;
00346 ++argv; --argc;
00347 break;
00348 }
00349
00350 case 'f': {
00351 if (sscanf(argv[2], "%u", &movieFPS) != 1) {
00352 usage();
00353 }
00354 movieFPSOptionSet = True;
00355 ++argv; --argc;
00356 break;
00357 }
00358
00359 case 'F': {
00360 fileNamePrefix = argv[2];
00361 ++argv; --argc;
00362 break;
00363 }
00364
00365 case 'b': {
00366 if (sscanf(argv[2], "%u", &fileSinkBufferSize) != 1) {
00367 usage();
00368 }
00369 ++argv; --argc;
00370 break;
00371 }
00372
00373 case 'B': {
00374 if (sscanf(argv[2], "%u", &socketInputBufferSize) != 1) {
00375 usage();
00376 }
00377 ++argv; --argc;
00378 break;
00379 }
00380
00381
00382 case 'l': {
00383 packetLossCompensate = True;
00384 break;
00385 }
00386
00387 case 'y': {
00388 syncStreams = True;
00389 break;
00390 }
00391
00392 case 'H': {
00393 generateHintTracks = True;
00394 break;
00395 }
00396
00397 case 'Q': {
00398 qosMeasurementIntervalMS = 1000;
00399
00400 if (argc > 3 && argv[2][0] != '-') {
00401
00402
00403 if (sscanf(argv[2], "%u", &qosMeasurementIntervalMS) != 1) {
00404 usage();
00405 }
00406 qosMeasurementIntervalMS *= 100;
00407 ++argv; --argc;
00408 }
00409 break;
00410 }
00411
00412 case 's': {
00413 float arg;
00414 if (sscanf(argv[2], "%g", &arg) != 1 || arg < 0) {
00415 usage();
00416 }
00417 initialSeekTime = arg;
00418 ++argv; --argc;
00419 break;
00420 }
00421
00422 case 'z': {
00423 float arg;
00424 if (sscanf(argv[2], "%g", &arg) != 1 || arg == 0.0f) {
00425 usage();
00426 }
00427 scale = arg;
00428 ++argv; --argc;
00429 break;
00430 }
00431
00432 default: {
00433 usage();
00434 break;
00435 }
00436 }
00437
00438 ++argv; --argc;
00439 }
00440 if (argc != 2) usage();
00441 if (outputQuickTimeFile && outputAVIFile) {
00442 *env << "The -i and -q (or -4) flags cannot both be used!\n";
00443 usage();
00444 }
00445 Boolean outputCompositeFile = outputQuickTimeFile || outputAVIFile;
00446 if (!createReceivers && outputCompositeFile) {
00447 *env << "The -r and -q (or -4 or -i) flags cannot both be used!\n";
00448 usage();
00449 }
00450 if (outputCompositeFile && !movieWidthOptionSet) {
00451 *env << "Warning: The -q, -4 or -i option was used, but not -w. Assuming a video width of "
00452 << movieWidth << " pixels\n";
00453 }
00454 if (outputCompositeFile && !movieHeightOptionSet) {
00455 *env << "Warning: The -q, -4 or -i option was used, but not -h. Assuming a video height of "
00456 << movieHeight << " pixels\n";
00457 }
00458 if (outputCompositeFile && !movieFPSOptionSet) {
00459 *env << "Warning: The -q, -4 or -i option was used, but not -f. Assuming a video frame rate of "
00460 << movieFPS << " frames-per-second\n";
00461 }
00462 if (audioOnly && videoOnly) {
00463 *env << "The -a and -v flags cannot both be used!\n";
00464 usage();
00465 }
00466 if (sendOptionsRequestOnly && !sendOptionsRequest) {
00467 *env << "The -o and -O flags cannot both be used!\n";
00468 usage();
00469 }
00470 if (tunnelOverHTTPPortNum > 0) {
00471 if (streamUsingTCP) {
00472 *env << "The -t and -T flags cannot both be used!\n";
00473 usage();
00474 } else {
00475 streamUsingTCP = True;
00476 }
00477 }
00478 if (!createReceivers && notifyOnPacketArrival) {
00479 *env << "Warning: Because we're not receiving stream data, the -n flag has no effect\n";
00480 }
00481 if (durationSlop < 0) {
00482
00483
00484
00485 durationSlop = qosMeasurementIntervalMS > 0 ? 0.0 : 5.0;
00486 }
00487
00488 char* url = argv[1];
00489
00490
00491 ourClient = createClient(*env, verbosityLevel, progName);
00492 if (ourClient == NULL) {
00493 *env << "Failed to create " << clientProtocolName
00494 << " client: " << env->getResultMsg() << "\n";
00495 shutdown();
00496 }
00497
00498 if (sendOptionsRequest) {
00499
00500 char* optionsResponse
00501 = getOptionsResponse(ourClient, url, username, password);
00502 if (sendOptionsRequestOnly) {
00503 if (optionsResponse == NULL) {
00504 *env << clientProtocolName << " \"OPTIONS\" request failed: "
00505 << env->getResultMsg() << "\n";
00506 } else {
00507 *env << clientProtocolName << " \"OPTIONS\" request returned: "
00508 << optionsResponse << "\n";
00509 }
00510 shutdown();
00511 }
00512 delete[] optionsResponse;
00513 }
00514
00515
00516 char* sdpDescription
00517 = getSDPDescriptionFromURL(ourClient, url, username, password,
00518 proxyServerName, proxyServerPortNum,
00519 desiredPortNum);
00520 if (sdpDescription == NULL) {
00521 *env << "Failed to get a SDP description from URL \"" << url
00522 << "\": " << env->getResultMsg() << "\n";
00523 shutdown();
00524 }
00525
00526 *env << "Opened URL \"" << url
00527 << "\", returning a SDP description:\n" << sdpDescription << "\n";
00528
00529
00530 session = MediaSession::createNew(*env, sdpDescription);
00531 delete[] sdpDescription;
00532 if (session == NULL) {
00533 *env << "Failed to create a MediaSession object from the SDP description: " << env->getResultMsg() << "\n";
00534 shutdown();
00535 } else if (!session->hasSubsessions()) {
00536 *env << "This session has no media subsessions (i.e., \"m=\" lines)\n";
00537 shutdown();
00538 }
00539
00540
00541 MediaSubsessionIterator iter(*session);
00542 MediaSubsession *subsession;
00543 Boolean madeProgress = False;
00544 char const* singleMediumToTest = singleMedium;
00545 while ((subsession = iter.next()) != NULL) {
00546
00547 if (singleMediumToTest != NULL) {
00548 if (strcmp(subsession->mediumName(), singleMediumToTest) != 0) {
00549 *env << "Ignoring \"" << subsession->mediumName()
00550 << "/" << subsession->codecName()
00551 << "\" subsession, because we've asked to receive a single " << singleMedium
00552 << " session only\n";
00553 continue;
00554 } else {
00555
00556 singleMediumToTest = "xxxxx";
00557
00558 }
00559 }
00560
00561 if (desiredPortNum != 0) {
00562 subsession->setClientPortNum(desiredPortNum);
00563 desiredPortNum += 2;
00564 }
00565
00566 if (createReceivers) {
00567 if (!subsession->initiate(simpleRTPoffsetArg)) {
00568 *env << "Unable to create receiver for \"" << subsession->mediumName()
00569 << "/" << subsession->codecName()
00570 << "\" subsession: " << env->getResultMsg() << "\n";
00571 } else {
00572 *env << "Created receiver for \"" << subsession->mediumName()
00573 << "/" << subsession->codecName()
00574 << "\" subsession (client ports " << subsession->clientPortNum()
00575 << "-" << subsession->clientPortNum()+1 << ")\n";
00576 madeProgress = True;
00577
00578 if (subsession->rtpSource() != NULL) {
00579
00580
00581
00582 unsigned const thresh = 1000000;
00583 subsession->rtpSource()->setPacketReorderingThresholdTime(thresh);
00584
00585 if (socketInputBufferSize > 0) {
00586
00587 int socketNum
00588 = subsession->rtpSource()->RTPgs()->socketNum();
00589 unsigned curBufferSize
00590 = getReceiveBufferSize(*env, socketNum);
00591 unsigned newBufferSize
00592 = setReceiveBufferTo(*env, socketNum, socketInputBufferSize);
00593 *env << "Changed socket receive buffer size for the \""
00594 << subsession->mediumName()
00595 << "/" << subsession->codecName()
00596 << "\" subsession from "
00597 << curBufferSize << " to "
00598 << newBufferSize << " bytes\n";
00599 }
00600 }
00601 }
00602 } else {
00603 if (subsession->clientPortNum() == 0) {
00604 *env << "No client port was specified for the \""
00605 << subsession->mediumName()
00606 << "/" << subsession->codecName()
00607 << "\" subsession. (Try adding the \"-p <portNum>\" option.)\n";
00608 } else {
00609 madeProgress = True;
00610 }
00611 }
00612 }
00613 if (!madeProgress) shutdown();
00614
00615
00616 setupStreams();
00617
00618
00619 if (createReceivers) {
00620 if (outputQuickTimeFile) {
00621
00622 qtOut = QuickTimeFileSink::createNew(*env, *session, "stdout",
00623 fileSinkBufferSize,
00624 movieWidth, movieHeight,
00625 movieFPS,
00626 packetLossCompensate,
00627 syncStreams,
00628 generateHintTracks,
00629 generateMP4Format);
00630 if (qtOut == NULL) {
00631 *env << "Failed to create QuickTime file sink for stdout: " << env->getResultMsg();
00632 shutdown();
00633 }
00634
00635 qtOut->startPlaying(sessionAfterPlaying, NULL);
00636 } else if (outputAVIFile) {
00637
00638 aviOut = AVIFileSink::createNew(*env, *session, "stdout",
00639 fileSinkBufferSize,
00640 movieWidth, movieHeight,
00641 movieFPS,
00642 packetLossCompensate);
00643 if (aviOut == NULL) {
00644 *env << "Failed to create AVI file sink for stdout: " << env->getResultMsg();
00645 shutdown();
00646 }
00647
00648 aviOut->startPlaying(sessionAfterPlaying, NULL);
00649 #ifdef SUPPORT_REAL_RTSP
00650 } else if (session->isRealNetworksRDT) {
00651
00652
00653 char outFileName[1000];
00654 if (singleMedium == NULL) {
00655 snprintf(outFileName, sizeof outFileName, "%soutput.rm", fileNamePrefix);
00656 } else {
00657
00658 sprintf(outFileName, "stdout");
00659 }
00660 FileSink* fileSink = FileSink::createNew(*env, outFileName,
00661 fileSinkBufferSize, oneFilePerFrame);
00662
00663
00664
00665 unsigned headerSize;
00666 unsigned char* headerData = RealGenerateRMFFHeader(session, headerSize);
00667 struct timeval timeNow;
00668 gettimeofday(&timeNow, NULL);
00669 fileSink->addData(headerData, headerSize, timeNow);
00670 delete[] headerData;
00671
00672
00673
00674
00675 iter.reset();
00676 madeProgress = False;
00677 while ((subsession = iter.next()) != NULL) {
00678 if (subsession->readSource() == NULL) continue;
00679
00680 fileSink->startPlaying(*(subsession->readSource()),
00681 subsessionAfterPlaying, subsession);
00682 madeProgress = True;
00683 break;
00684 }
00685 if (!madeProgress) shutdown();
00686 #endif
00687 } else {
00688
00689 madeProgress = False;
00690 iter.reset();
00691 while ((subsession = iter.next()) != NULL) {
00692 if (subsession->readSource() == NULL) continue;
00693
00694
00695 char outFileName[1000];
00696 if (singleMedium == NULL) {
00697
00698
00699 static unsigned streamCounter = 0;
00700 snprintf(outFileName, sizeof outFileName, "%s%s-%s-%d",
00701 fileNamePrefix, subsession->mediumName(),
00702 subsession->codecName(), ++streamCounter);
00703 } else {
00704 sprintf(outFileName, "stdout");
00705 }
00706 FileSink* fileSink;
00707 if (strcmp(subsession->mediumName(), "audio") == 0 &&
00708 (strcmp(subsession->codecName(), "AMR") == 0 ||
00709 strcmp(subsession->codecName(), "AMR-WB") == 0)) {
00710
00711 fileSink = AMRAudioFileSink::createNew(*env, outFileName,
00712 fileSinkBufferSize, oneFilePerFrame);
00713 } else if (strcmp(subsession->mediumName(), "video") == 0 &&
00714 (strcmp(subsession->codecName(), "H264") == 0)) {
00715
00716 fileSink = H264VideoFileSink::createNew(*env, outFileName,
00717 fileSinkBufferSize, oneFilePerFrame);
00718 } else {
00719
00720 fileSink = FileSink::createNew(*env, outFileName,
00721 fileSinkBufferSize, oneFilePerFrame);
00722 }
00723 subsession->sink = fileSink;
00724 if (subsession->sink == NULL) {
00725 *env << "Failed to create FileSink for \"" << outFileName
00726 << "\": " << env->getResultMsg() << "\n";
00727 } else {
00728 if (singleMedium == NULL) {
00729 *env << "Created output file: \"" << outFileName << "\"\n";
00730 } else {
00731 *env << "Outputting data from the \"" << subsession->mediumName()
00732 << "/" << subsession->codecName()
00733 << "\" subsession to 'stdout'\n";
00734 }
00735
00736 if (strcmp(subsession->mediumName(), "video") == 0 &&
00737 strcmp(subsession->codecName(), "MP4V-ES") == 0 &&
00738 subsession->fmtp_config() != NULL) {
00739
00740
00741
00742 unsigned configLen;
00743 unsigned char* configData
00744 = parseGeneralConfigStr(subsession->fmtp_config(), configLen);
00745 struct timeval timeNow;
00746 gettimeofday(&timeNow, NULL);
00747 fileSink->addData(configData, configLen, timeNow);
00748 delete[] configData;
00749 }
00750
00751 subsession->sink->startPlaying(*(subsession->readSource()),
00752 subsessionAfterPlaying,
00753 subsession);
00754
00755
00756
00757 if (subsession->rtcpInstance() != NULL) {
00758 subsession->rtcpInstance()->setByeHandler(subsessionByeHandler,
00759 subsession);
00760 }
00761
00762 madeProgress = True;
00763 }
00764 }
00765 if (!madeProgress) shutdown();
00766 }
00767 }
00768
00769
00770
00771 startPlayingStreams();
00772
00773 env->taskScheduler().doEventLoop();
00774
00775 return 0;
00776 }
00777
00778
00779 void setupStreams() {
00780 MediaSubsessionIterator iter(*session);
00781 MediaSubsession *subsession;
00782 Boolean madeProgress = False;
00783
00784 while ((subsession = iter.next()) != NULL) {
00785 if (subsession->clientPortNum() == 0) continue;
00786
00787 if (!clientSetupSubsession(ourClient, subsession, streamUsingTCP)) {
00788 *env << "Failed to setup \"" << subsession->mediumName()
00789 << "/" << subsession->codecName()
00790 << "\" subsession: " << env->getResultMsg() << "\n";
00791 } else {
00792 *env << "Setup \"" << subsession->mediumName()
00793 << "/" << subsession->codecName()
00794 << "\" subsession (client ports " << subsession->clientPortNum()
00795 << "-" << subsession->clientPortNum()+1 << ")\n";
00796 madeProgress = True;
00797 }
00798 }
00799 if (!madeProgress) shutdown();
00800 }
00801
00802 void startPlayingStreams() {
00803 if (duration == 0) {
00804 if (scale > 0) duration = session->playEndTime() - initialSeekTime;
00805 else if (scale < 0) duration = initialSeekTime;
00806 }
00807 if (duration < 0) duration = 0.0;
00808
00809 if (!clientStartPlayingSession(ourClient, session)) {
00810 *env << "Failed to start playing session: " << env->getResultMsg() << "\n";
00811 shutdown();
00812 } else {
00813 *env << "Started playing session\n";
00814 }
00815
00816 if (qosMeasurementIntervalMS > 0) {
00817
00818 beginQOSMeasurement();
00819 }
00820
00821
00822
00823 Boolean timerIsBeingUsed = False;
00824 double secondsToDelay = duration;
00825 if (duration > 0) {
00826 double const maxDelayTime
00827 = (double)( ((unsigned)0x7FFFFFFF)/1000000.0 );
00828 if (