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 break;
00198 }
00199
00200 char* const sdpDescription = resultString;
00201 env << *rtspClient << "Got a SDP description:\n" << sdpDescription << "\n";
00202
00203
00204 scs.session = MediaSession::createNew(env, sdpDescription);
00205 delete[] sdpDescription;
00206 if (scs.session == NULL) {
00207 env << *rtspClient << "Failed to create a MediaSession object from the SDP description: " << env.getResultMsg() << "\n";
00208 break;
00209 } else if (!scs.session->hasSubsessions()) {
00210 env << *rtspClient << "This session has no media subsessions (i.e., no \"m=\" lines)\n";
00211 break;
00212 }
00213
00214
00215
00216
00217 scs.iter = new MediaSubsessionIterator(*scs.session);
00218 setupNextSubsession(rtspClient);
00219 return;
00220 } while (0);
00221
00222
00223 shutdownStream(rtspClient);
00224 }
00225
00226 void setupNextSubsession(RTSPClient* rtspClient) {
00227 UsageEnvironment& env = rtspClient->envir();
00228 StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs;
00229
00230 scs.subsession = scs.iter->next();
00231 if (scs.subsession != NULL) {
00232 if (!scs.subsession->initiate()) {
00233 env << *rtspClient << "Failed to initiate the \"" << *scs.subsession << "\" subsession: " << env.getResultMsg() << "\n";
00234 setupNextSubsession(rtspClient);
00235 } else {
00236 env << *rtspClient << "Initiated the \"" << *scs.subsession
00237 << "\" subsession (client ports " << scs.subsession->clientPortNum() << "-" << scs.subsession->clientPortNum()+1 << ")\n";
00238
00239
00240 rtspClient->sendSetupCommand(*scs.subsession, continueAfterSETUP);
00241 }
00242 return;
00243 }
00244
00245
00246 scs.duration = scs.session->playEndTime() - scs.session->playStartTime();
00247 rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY);
00248 }
00249
00250 void continueAfterSETUP(RTSPClient* rtspClient, int resultCode, char* resultString) {
00251 do {
00252 UsageEnvironment& env = rtspClient->envir();
00253 StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs;
00254
00255 if (resultCode != 0) {
00256 env << *rtspClient << "Failed to set up the \"" << *scs.subsession << "\" subsession: " << env.getResultMsg() << "\n";
00257 break;
00258 }
00259
00260 env << *rtspClient << "Set up the \"" << *scs.subsession
00261 << "\" subsession (client ports " << scs.subsession->clientPortNum() << "-" << scs.subsession->clientPortNum()+1 << ")\n";
00262
00263
00264
00265
00266
00267 scs.subsession->sink = DummySink::createNew(env, *scs.subsession, rtspClient->url());
00268
00269 if (scs.subsession->sink == NULL) {
00270 env << *rtspClient << "Failed to create a data sink for the \"" << *scs.subsession
00271 << "\" subsession: " << env.getResultMsg() << "\n";
00272 break;
00273 }
00274
00275 env << *rtspClient << "Created a data sink for the \"" << *scs.subsession << "\" subsession\n";
00276 scs.subsession->miscPtr = rtspClient;
00277 scs.subsession->sink->startPlaying(*(scs.subsession->readSource()),
00278 subsessionAfterPlaying, scs.subsession);
00279
00280 if (scs.subsession->rtcpInstance() != NULL) {
00281 scs.subsession->rtcpInstance()->setByeHandler(subsessionByeHandler, scs.subsession);
00282 }
00283 } while (0);
00284
00285
00286 setupNextSubsession(rtspClient);
00287 }
00288
00289 void continueAfterPLAY(RTSPClient* rtspClient, int resultCode, char* resultString) {
00290 do {
00291 UsageEnvironment& env = rtspClient->envir();
00292 StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs;
00293
00294 if (resultCode != 0) {
00295 env << *rtspClient << "Failed to start playing session: " << resultString << "\n";
00296 break;
00297 }
00298
00299
00300
00301
00302
00303 if (scs.duration > 0) {
00304 unsigned const delaySlop = 2;
00305 scs.duration += delaySlop;
00306 unsigned uSecsToDelay = (unsigned)(scs.duration*1000000);
00307 scs.streamTimerTask = env.taskScheduler().scheduleDelayedTask(uSecsToDelay, (TaskFunc*)streamTimerHandler, rtspClient);
00308 }
00309
00310 env << *rtspClient << "Started playing session";
00311 if (scs.duration > 0) {
00312 env << " (for up to " << scs.duration << " seconds)";
00313 }
00314 env << "...\n";
00315
00316 return;
00317 } while (0);
00318
00319
00320 shutdownStream(rtspClient);
00321 }
00322
00323
00324
00325
00326 void subsessionAfterPlaying(void* clientData) {
00327 MediaSubsession* subsession = (MediaSubsession*)clientData;
00328 RTSPClient* rtspClient = (RTSPClient*)(subsession->miscPtr);
00329
00330
00331 Medium::close(subsession->sink);
00332 subsession->sink = NULL;
00333
00334
00335 MediaSession& session = subsession->parentSession();
00336 MediaSubsessionIterator iter(session);
00337 while ((subsession = iter.next()) != NULL) {
00338 if (subsession->sink != NULL) return;
00339 }
00340
00341
00342 shutdownStream(rtspClient);
00343 }
00344
00345 void subsessionByeHandler(void* clientData) {
00346 MediaSubsession* subsession = (MediaSubsession*)clientData;
00347 RTSPClient* rtspClient = (RTSPClient*)subsession->miscPtr;
00348 UsageEnvironment& env = rtspClient->envir();
00349
00350 env << *rtspClient << "Received RTCP \"BYE\" on \"" << *subsession << "\" subsession\n";
00351
00352
00353 subsessionAfterPlaying(subsession);
00354 }
00355
00356 void streamTimerHandler(void* clientData) {
00357 ourRTSPClient* rtspClient = (ourRTSPClient*)clientData;
00358 StreamClientState& scs = rtspClient->scs;
00359
00360 scs.streamTimerTask = NULL;
00361
00362
00363 shutdownStream(rtspClient);
00364 }
00365
00366 void shutdownStream(RTSPClient* rtspClient, int exitCode) {
00367 UsageEnvironment& env = rtspClient->envir();
00368 StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs;
00369
00370
00371 if (scs.session != NULL) {
00372 Boolean someSubsessionsWereActive = False;
00373 MediaSubsessionIterator iter(*scs.session);
00374 MediaSubsession* subsession;
00375
00376 while ((subsession = iter.next()) != NULL) {
00377 if (subsession->sink != NULL) {
00378 Medium::close(subsession->sink);
00379 subsession->sink = NULL;
00380
00381 if (subsession->rtcpInstance() != NULL) {
00382 subsession->rtcpInstance()->setByeHandler(NULL, NULL);
00383 }
00384
00385 someSubsessionsWereActive = True;
00386 }
00387 }
00388
00389 if (someSubsessionsWereActive) {
00390
00391
00392 rtspClient->sendTeardownCommand(*scs.session, NULL);
00393 }
00394 }
00395
00396 env << *rtspClient << "Closing the stream.\n";
00397 Medium::close(rtspClient);
00398
00399
00400 if (--rtspClientCount == 0) {
00401
00402
00403
00404 exit(exitCode);
00405 }
00406 }
00407
00408
00409
00410
00411 ourRTSPClient* ourRTSPClient::createNew(UsageEnvironment& env, char const* rtspURL,
00412 int verbosityLevel, char const* applicationName, portNumBits tunnelOverHTTPPortNum) {
00413 return new ourRTSPClient(env, rtspURL, verbosityLevel, applicationName, tunnelOverHTTPPortNum);
00414 }
00415
00416 ourRTSPClient::ourRTSPClient(UsageEnvironment& env, char const* rtspURL,
00417 int verbosityLevel, char const* applicationName, portNumBits tunnelOverHTTPPortNum)
00418 : RTSPClient(env,rtspURL, verbosityLevel, applicationName, tunnelOverHTTPPortNum) {
00419 }
00420
00421 ourRTSPClient::~ourRTSPClient() {
00422 }
00423
00424
00425
00426
00427 StreamClientState::StreamClientState()
00428 : iter(NULL), session(NULL), subsession(NULL), streamTimerTask(NULL), duration(0.0) {
00429 }
00430
00431 StreamClientState::~StreamClientState() {
00432 delete iter;
00433 if (session != NULL) {
00434
00435 UsageEnvironment& env = session->envir();
00436
00437 env.taskScheduler().unscheduleDelayedTask(streamTimerTask);
00438 Medium::close(session);
00439 }
00440 }
00441
00442
00443
00444
00445
00446
00447 #define DUMMY_SINK_RECEIVE_BUFFER_SIZE 100000
00448
00449 DummySink* DummySink::createNew(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId) {
00450 return new DummySink(env, subsession, streamId);
00451 }
00452
00453 DummySink::DummySink(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId)
00454 : MediaSink(env),
00455 fSubsession(subsession) {
00456 fStreamId = strDup(streamId);
00457 fReceiveBuffer = new u_int8_t[DUMMY_SINK_RECEIVE_BUFFER_SIZE];
00458 }
00459
00460 DummySink::~DummySink() {
00461 delete[] fReceiveBuffer;
00462 delete[] fStreamId;
00463 }
00464
00465 void DummySink::afterGettingFrame(void* clientData, unsigned frameSize, unsigned numTruncatedBytes,
00466 struct timeval presentationTime, unsigned durationInMicroseconds) {
00467 DummySink* sink = (DummySink*)clientData;
00468 sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime, durationInMicroseconds);
00469 }
00470
00471
00472 #define DEBUG_PRINT_EACH_RECEIVED_FRAME 1
00473
00474 void DummySink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
00475 struct timeval presentationTime, unsigned ) {
00476
00477 #ifdef DEBUG_PRINT_EACH_RECEIVED_FRAME
00478 if (fStreamId != NULL) envir() << "Stream \"" << fStreamId << "\"; ";
00479 envir() << fSubsession.mediumName() << "/" << fSubsession.codecName() << ":\tReceived " << frameSize << " bytes";
00480 if (numTruncatedBytes > 0) envir() << " (with " << numTruncatedBytes << " bytes truncated)";
00481 char uSecsStr[6+1];
00482 sprintf(uSecsStr, "%06u", (unsigned)presentationTime.tv_usec);
00483 envir() << ".\tPresentation time: " << (unsigned)presentationTime.tv_sec << "." << uSecsStr;
00484 if (fSubsession.rtpSource() != NULL && !fSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP()) {
00485 envir() << "!";
00486 }
00487 envir() << "\n";
00488 #endif
00489
00490
00491 continuePlaying();
00492 }
00493
00494 Boolean DummySink::continuePlaying() {
00495 if (fSource == NULL) return False;
00496
00497
00498 fSource->getNextFrame(fReceiveBuffer, DUMMY_SINK_RECEIVE_BUFFER_SIZE,
00499 afterGettingFrame, this,
00500 onSourceClosure, this);
00501 return True;
00502 }