00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "liveMedia.hh"
00024 #include "BasicUsageEnvironment.hh"
00025
00026
00027
00028
00029 void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString);
00030 void continueAfterSETUP(RTSPClient* rtspClient, int resultCode, char* resultString);
00031 void continueAfterPLAY(RTSPClient* rtspClient, int resultCode, char* resultString);
00032
00033
00034 void subsessionAfterPlaying(void* clientData);
00035 void subsessionByeHandler(void* clientData);
00036 void streamTimerHandler(void* clientData);
00037
00038
00039
00040 void openURL(UsageEnvironment& env, char const* progName, char const* rtspURL);
00041
00042
00043 void setupNextSubsession(RTSPClient* rtspClient);
00044
00045
00046 void shutdownStream(RTSPClient* rtspClient, int exitCode = 1);
00047
00048
00049 UsageEnvironment& operator<<(UsageEnvironment& env, const RTSPClient& rtspClient) {
00050 return env << "[URL:\"" << rtspClient.url() << "\"]: ";
00051 }
00052
00053
00054 UsageEnvironment& operator<<(UsageEnvironment& env, const MediaSubsession& subsession) {
00055 return env << subsession.mediumName() << "/" << subsession.codecName();
00056 }
00057
00058 void usage(UsageEnvironment& env, char const* progName) {
00059 env << "Usage: " << progName << " <rtsp-url-1> ... <rtsp-url-N>\n";
00060 env << "\t(where each <rtsp-url-i> is a \"rtsp://\" URL)\n";
00061 }
00062
00063 char eventLoopWatchVariable = 0;
00064
00065 int main(int argc, char** argv) {
00066
00067 TaskScheduler* scheduler = BasicTaskScheduler::createNew();
00068 UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);
00069
00070
00071 if (argc < 2) {
00072 usage(*env, argv[0]);
00073 return 1;
00074 }
00075
00076
00077 for (int i = 1; i <= argc-1; ++i) {
00078 openURL(*env, argv[0], argv[i]);
00079 }
00080
00081
00082 env->taskScheduler().doEventLoop(&eventLoopWatchVariable);
00083
00084
00085 return 0;
00086
00087
00088
00089
00090
00091
00092
00093
00094 }
00095
00096
00097
00098 class StreamClientState {
00099 public:
00100 StreamClientState();
00101 virtual ~StreamClientState();
00102
00103 public:
00104 MediaSubsessionIterator* iter;
00105 MediaSession* session;
00106 MediaSubsession* subsession;
00107 TaskToken streamTimerTask;
00108 double duration;
00109 };
00110
00111
00112
00113
00114
00115
00116 class ourRTSPClient: public RTSPClient {
00117 public:
00118 static ourRTSPClient* createNew(UsageEnvironment& env, char const* rtspURL,
00119 int verbosityLevel = 0,
00120 char const* applicationName = NULL,
00121 portNumBits tunnelOverHTTPPortNum = 0);
00122
00123 protected:
00124 ourRTSPClient(UsageEnvironment& env, char const* rtspURL,
00125 int verbosityLevel, char const* applicationName, portNumBits tunnelOverHTTPPortNum);
00126
00127 virtual ~ourRTSPClient();
00128
00129 public:
00130 StreamClientState scs;
00131 };
00132
00133
00134
00135
00136
00137
00138 class DummySink: public MediaSink {
00139 public:
00140 static DummySink* createNew(UsageEnvironment& env,
00141 MediaSubsession& subsession,
00142 char const* streamId = NULL);
00143
00144 private:
00145 DummySink(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId);
00146
00147 virtual ~DummySink();
00148
00149 static void afterGettingFrame(void* clientData, unsigned frameSize,
00150 unsigned numTruncatedBytes,
00151 struct timeval presentationTime,
00152 unsigned durationInMicroseconds);
00153 void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
00154 struct timeval presentationTime, unsigned durationInMicroseconds);
00155
00156 private:
00157
00158 virtual Boolean continuePlaying();
00159
00160 private:
00161 u_int8_t* fReceiveBuffer;
00162 MediaSubsession& fSubsession;
00163 char* fStreamId;
00164 };
00165
00166 #define RTSP_CLIENT_VERBOSITY_LEVEL 1 // by default, print verbose output from each "RTSPClient"
00167
00168 static unsigned rtspClientCount = 0;
00169
00170 void openURL(UsageEnvironment& env, char const* progName, char const* rtspURL) {
00171
00172
00173 RTSPClient* rtspClient = ourRTSPClient::createNew(env, rtspURL, RTSP_CLIENT_VERBOSITY_LEVEL, progName);
00174 if (rtspClient == NULL) {
00175 env << "Failed to create a RTSP client for URL \"" << rtspURL << "\": " << env.getResultMsg() << "\n";
00176 return;
00177 }
00178
00179 ++rtspClientCount;
00180
00181
00182
00183
00184 rtspClient->sendDescribeCommand(continueAfterDESCRIBE);
00185 }
00186
00187
00188
00189
00190 void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString) {
00191 do {
00192 UsageEnvironment& env = rtspClient->envir();
00193 StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs;
00194
00195 if (resultCode != 0) {
00196 env << *rtspClient << "Failed to get a SDP description: " << resultString << "\n";
00197 delete[] resultString;
00198 break;
00199 }
00200
00201 char* const sdpDescription = resultString;
00202 env << *rtspClient << "Got a SDP description:\n" << sdpDescription << "\n";
00203
00204
00205 scs.session = MediaSession::createNew(env, sdpDescription);
00206 delete[] sdpDescription;
00207 if (scs.session == NULL) {
00208 env << *rtspClient << "Failed to create a MediaSession object from the SDP description: " << env.getResultMsg() << "\n";
00209 break;
00210 } else if (!scs.session->hasSubsessions()) {
00211 env << *rtspClient << "This session has no media subsessions (i.e., no \"m=\" lines)\n";
00212 break;
00213 }
00214
00215
00216
00217
00218 scs.iter = new MediaSubsessionIterator(*scs.session);
00219 setupNextSubsession(rtspClient);
00220 return;
00221 } while (0);
00222
00223
00224 shutdownStream(rtspClient);
00225 }
00226
00227
00228
00229 #define REQUEST_STREAMING_OVER_TCP False
00230
00231 void setupNextSubsession(RTSPClient* rtspClient) {
00232 UsageEnvironment& env = rtspClient->envir();
00233 StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs;
00234
00235 scs.subsession = scs.iter->next();
00236 if (scs.subsession != NULL) {
00237 if (!scs.subsession->initiate()) {
00238 env << *rtspClient << "Failed to initiate the \"" << *scs.subsession << "\" subsession: " << env.getResultMsg() << "\n";
00239 setupNextSubsession(rtspClient);
00240 } else {
00241 env << *rtspClient << "Initiated the \"" << *scs.subsession
00242 << "\" subsession (client ports " << scs.subsession->clientPortNum() << "-" << scs.subsession->clientPortNum()+1 << ")\n";
00243
00244
00245 rtspClient->sendSetupCommand(*scs.subsession, continueAfterSETUP, False, REQUEST_STREAMING_OVER_TCP);
00246 }
00247 return;
00248 }
00249
00250
00251 if (scs.session->absStartTime() != NULL) {
00252
00253 rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY, scs.session->absStartTime(), scs.session->absEndTime());
00254 } else {
00255 scs.duration = scs.session->playEndTime() - scs.session->playStartTime();
00256 rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY);
00257 }
00258 }
00259
00260 void continueAfterSETUP(RTSPClient* rtspClient, int resultCode, char* resultString) {
00261 do {
00262 UsageEnvironment& env = rtspClient->envir();
00263 StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs;
00264
00265 if (resultCode != 0) {
00266 env << *rtspClient << "Failed to set up the \"" << *scs.subsession << "\" subsession: " << resultString << "\n";
00267 break;
00268 }
00269
00270 env << *rtspClient << "Set up the \"" << *scs.subsession
00271 << "\" subsession (client ports " << scs.subsession->clientPortNum() << "-" << scs.subsession->clientPortNum()+1 << ")\n";
00272
00273
00274
00275
00276
00277 scs.subsession->sink = DummySink::createNew(env, *scs.subsession, rtspClient->url());
00278
00279 if (scs.subsession->sink == NULL) {
00280 env << *rtspClient << "Failed to create a data sink for the \"" << *scs.subsession
00281 << "\" subsession: " << env.getResultMsg() << "\n";
00282 break;
00283 }
00284
00285 env << *rtspClient << "Created a data sink for the \"" << *scs.subsession << "\" subsession\n";
00286 scs.subsession->miscPtr = rtspClient;
00287 scs.subsession->sink->startPlaying(*(scs.subsession->readSource()),
00288 subsessionAfterPlaying, scs.subsession);
00289
00290 if (scs.subsession->rtcpInstance() != NULL) {
00291 scs.subsession->rtcpInstance()->setByeHandler(subsessionByeHandler, scs.subsession);
00292 }
00293 } while (0);
00294 delete[] resultString;
00295
00296
00297 setupNextSubsession(rtspClient);
00298 }
00299
00300 void continueAfterPLAY(RTSPClient* rtspClient, int resultCode, char* resultString) {
00301 Boolean success = False;
00302
00303 do {
00304 UsageEnvironment& env = rtspClient->envir();
00305 StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs;
00306
00307 if (resultCode != 0) {
00308 env << *rtspClient << "Failed to start playing session: " << resultString << "\n";
00309 break;
00310 }
00311
00312
00313
00314
00315
00316 if (scs.duration > 0) {
00317 unsigned const delaySlop = 2;
00318 scs.duration += delaySlop;
00319 unsigned uSecsToDelay = (unsigned)(scs.duration*1000000);
00320 scs.streamTimerTask = env.taskScheduler().scheduleDelayedTask(uSecsToDelay, (TaskFunc*)streamTimerHandler, rtspClient);
00321 }
00322
00323 env << *rtspClient << "Started playing session";
00324 if (scs.duration > 0) {
00325 env << " (for up to " << scs.duration << " seconds)";
00326 }
00327 env << "...\n";
00328
00329 success = True;
00330 } while (0);
00331 delete[] resultString;
00332
00333 if (!success) {
00334
00335 shutdownStream(rtspClient);
00336 }
00337 }
00338
00339
00340
00341
00342 void subsessionAfterPlaying(void* clientData) {
00343 MediaSubsession* subsession = (MediaSubsession*)clientData;
00344 RTSPClient* rtspClient = (RTSPClient*)(subsession->miscPtr);
00345
00346
00347 Medium::close(subsession->sink);
00348 subsession->sink = NULL;
00349
00350
00351 MediaSession& session = subsession->parentSession();
00352 MediaSubsessionIterator iter(session);
00353 while ((subsession = iter.next()) != NULL) {
00354 if (subsession->sink != NULL) return;
00355 }
00356
00357
00358 shutdownStream(rtspClient);
00359 }
00360
00361 void subsessionByeHandler(void* clientData) {
00362 MediaSubsession* subsession = (MediaSubsession*)clientData;
00363 RTSPClient* rtspClient = (RTSPClient*)subsession->miscPtr;
00364 UsageEnvironment& env = rtspClient->envir();
00365
00366 env << *rtspClient << "Received RTCP \"BYE\" on \"" << *subsession << "\" subsession\n";
00367
00368
00369 subsessionAfterPlaying(subsession);
00370 }
00371
00372 void streamTimerHandler(void* clientData) {
00373 ourRTSPClient* rtspClient = (ourRTSPClient*)clientData;
00374 StreamClientState& scs = rtspClient->scs;
00375
00376 scs.streamTimerTask = NULL;
00377
00378
00379 shutdownStream(rtspClient);
00380 }
00381
00382 void shutdownStream(RTSPClient* rtspClient, int exitCode) {
00383 UsageEnvironment& env = rtspClient->envir();
00384 StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs;
00385
00386
00387 if (scs.session != NULL) {
00388 Boolean someSubsessionsWereActive = False;
00389 MediaSubsessionIterator iter(*scs.session);
00390 MediaSubsession* subsession;
00391
00392 while ((subsession = iter.next()) != NULL) {
00393 if (subsession->sink != NULL) {
00394 Medium::close(subsession->sink);
00395 subsession->sink = NULL;
00396
00397 if (subsession->rtcpInstance() != NULL) {
00398 subsession->rtcpInstance()->setByeHandler(NULL, NULL);
00399 }
00400
00401 someSubsessionsWereActive = True;
00402 }
00403 }
00404
00405 if (someSubsessionsWereActive) {
00406
00407
00408 rtspClient->sendTeardownCommand(*scs.session, NULL);
00409 }
00410 }
00411
00412 env << *rtspClient << "Closing the stream.\n";
00413 Medium::close(rtspClient);
00414
00415
00416 if (--rtspClientCount == 0) {
00417
00418
00419
00420 exit(exitCode);
00421 }
00422 }
00423
00424
00425
00426
00427 ourRTSPClient* ourRTSPClient::createNew(UsageEnvironment& env, char const* rtspURL,
00428 int verbosityLevel, char const* applicationName, portNumBits tunnelOverHTTPPortNum) {
00429 return new ourRTSPClient(env, rtspURL, verbosityLevel, applicationName, tunnelOverHTTPPortNum);
00430 }
00431
00432 ourRTSPClient::ourRTSPClient(UsageEnvironment& env, char const* rtspURL,
00433 int verbosityLevel, char const* applicationName, portNumBits tunnelOverHTTPPortNum)
00434 : RTSPClient(env,rtspURL, verbosityLevel, applicationName, tunnelOverHTTPPortNum) {
00435 }
00436
00437 ourRTSPClient::~ourRTSPClient() {
00438 }
00439
00440
00441
00442
00443 StreamClientState::StreamClientState()
00444 : iter(NULL), session(NULL), subsession(NULL), streamTimerTask(NULL), duration(0.0) {
00445 }
00446
00447 StreamClientState::~StreamClientState() {
00448 delete iter;
00449 if (session != NULL) {
00450
00451 UsageEnvironment& env = session->envir();
00452
00453 env.taskScheduler().unscheduleDelayedTask(streamTimerTask);
00454 Medium::close(session);
00455 }
00456 }
00457
00458
00459
00460
00461
00462
00463 #define DUMMY_SINK_RECEIVE_BUFFER_SIZE 100000
00464
00465 DummySink* DummySink::createNew(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId) {
00466 return new DummySink(env, subsession, streamId);
00467 }
00468
00469 DummySink::DummySink(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId)
00470 : MediaSink(env),
00471 fSubsession(subsession) {
00472 fStreamId = strDup(streamId);
00473 fReceiveBuffer = new u_int8_t[DUMMY_SINK_RECEIVE_BUFFER_SIZE];
00474 }
00475
00476 DummySink::~DummySink() {
00477 delete[] fReceiveBuffer;
00478 delete[] fStreamId;
00479 }
00480
00481 void DummySink::afterGettingFrame(void* clientData, unsigned frameSize, unsigned numTruncatedBytes,
00482 struct timeval presentationTime, unsigned durationInMicroseconds) {
00483 DummySink* sink = (DummySink*)clientData;
00484 sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime, durationInMicroseconds);
00485 }
00486
00487
00488 #define DEBUG_PRINT_EACH_RECEIVED_FRAME 1
00489
00490 void DummySink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
00491 struct timeval presentationTime, unsigned ) {
00492
00493 #ifdef DEBUG_PRINT_EACH_RECEIVED_FRAME
00494 if (fStreamId != NULL) envir() << "Stream \"" << fStreamId << "\"; ";
00495 envir() << fSubsession.mediumName() << "/" << fSubsession.codecName() << ":\tReceived " << frameSize << " bytes";
00496 if (numTruncatedBytes > 0) envir() << " (with " << numTruncatedBytes << " bytes truncated)";
00497 char uSecsStr[6+1];
00498 sprintf(uSecsStr, "%06u", (unsigned)presentationTime.tv_usec);
00499 envir() << ".\tPresentation time: " << (int)presentationTime.tv_sec << "." << uSecsStr;
00500 if (fSubsession.rtpSource() != NULL && !fSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP()) {
00501 envir() << "!";
00502 }
00503 #ifdef DEBUG_PRINT_NPT
00504 envir() << "\tNPT: " << fSubsession.getNormalPlayTime(presentationTime);
00505 #endif
00506 envir() << "\n";
00507 #endif
00508
00509
00510 continuePlaying();
00511 }
00512
00513 Boolean DummySink::continuePlaying() {
00514 if (fSource == NULL) return False;
00515
00516
00517 fSource->getNextFrame(fReceiveBuffer, DUMMY_SINK_RECEIVE_BUFFER_SIZE,
00518 afterGettingFrame, this,
00519 onSourceClosure, this);
00520 return True;
00521 }