27 #include "BasicUsageEnvironment.hh"
29 #include "RTSPClient.hh"
34 #define REQUEST_STREAMING_OVER_TCP false
37 #define RTSP_CLIENT_VERBOSITY_LEVEL 1
40 #define RTSP_SINK_BUFFER_SIZE 1024
44 #undef DEBUG_PRINT_EACH_RECEIVED_FRAME
45 #define DEBUG_PRINT_EACH_RECEIVED_FRAME 0
52 OurRTSPClient * openURL(UsageEnvironment& env,
char const* progName,
char const* rtspURL);
54 static unsigned rtspClientCount = 0;
55 static char rtspEventLoopWatchVariable = 0;
56 static Print* rtspOutput =
nullptr;
57 static uint32_t rtspSinkReceiveBufferSize = 0;
58 static bool rtspUseTCP = REQUEST_STREAMING_OVER_TCP;
72 AudioClientRTSP(uint32_t receiveBufferSize = RTSP_SINK_BUFFER_SIZE,
bool useTCP=REQUEST_STREAMING_OVER_TCP,
bool blocking =
false) {
73 setBufferSize(receiveBufferSize);
74 useTCP ? setTCP() : setUDP();
75 setBlocking(blocking);
78 void setBufferSize(
int size){
79 audiotools_rtsp::rtspSinkReceiveBufferSize = size;
83 audiotools_rtsp::rtspUseTCP =
true;
87 audiotools_rtsp::rtspUseTCP =
false;
90 void setBlocking(
bool flag){
95 void setLogin(
const char* ssid,
const char* password){
97 this->password = password;
102 audiotools_rtsp::rtspOutput = &out;
111 scheduler = BasicTaskScheduler::createNew();
112 env = BasicUsageEnvironment::createNew(*scheduler);
116 rtsp_client = audiotools_rtsp::openURL(*env,
"RTSPClient", url);
119 if (is_blocking) env->taskScheduler().doEventLoop(&audiotools_rtsp::rtspEventLoopWatchVariable);
128 if (audiotools_rtsp::rtspEventLoopWatchVariable==0) scheduler->SingleStep();
132 audiotools_rtsp::rtspEventLoopWatchVariable = 1;
137 bool is_blocking =
false;
146 UsageEnvironment* env=
nullptr;
147 BasicTaskScheduler* scheduler=
nullptr;
148 const char* ssid=
nullptr;
149 const char* password =
nullptr;
150 bool is_blocking =
false;
154 if(WiFi.status() != WL_CONNECTED && ssid!=
nullptr && password!=
nullptr){
156 WiFi.begin(ssid, password);
157 while(WiFi.status() != WL_CONNECTED){
162 Serial.print(
"Local Address: ");
163 Serial.println(WiFi.localIP());
165 return WiFi.status() == WL_CONNECTED;
180 void continueAfterDESCRIBE(RTSPClient* rtspClient,
int resultCode,
182 void continueAfterSETUP(RTSPClient* rtspClient,
int resultCode,
184 void continueAfterPLAY(RTSPClient* rtspClient,
int resultCode,
188 void subsessionAfterPlaying(
191 void subsessionByeHandler(
void* clientData,
char const* reason);
193 void streamTimerHandler(
void* clientData);
198 void setupNextSubsession(RTSPClient* rtspClient);
201 void shutdownStream(RTSPClient* rtspClient,
int exitCode = 1);
205 UsageEnvironment& operator<<(UsageEnvironment& env,
206 const RTSPClient& rtspClient) {
207 return env <<
"[URL:\"" << rtspClient.url() <<
"\"]: ";
212 UsageEnvironment& operator<<(UsageEnvironment& env,
213 const MediaSubsession& subsession) {
214 return env << subsession.mediumName() <<
"/" << subsession.codecName();
223 MediaSubsessionIterator* iter;
224 MediaSession* session;
225 MediaSubsession* subsession;
226 TaskToken streamTimerTask;
240 static OurRTSPClient* createNew(UsageEnvironment& env,
char const* rtspURL,
241 int verbosityLevel = 0,
242 char const* applicationName = NULL,
243 portNumBits tunnelOverHTTPPortNum = 0);
246 OurRTSPClient(UsageEnvironment& env,
char const* rtspURL,
int verbosityLevel,
247 char const* applicationName, portNumBits tunnelOverHTTPPortNum);
266 UsageEnvironment& env,
269 char const* streamId = NULL);
272 OurSink(UsageEnvironment& env, MediaSubsession& subsession,
273 char const* streamId);
277 static void afterGettingFrame(
void* clientData,
unsigned frameSize,
278 unsigned numTruncatedBytes,
279 struct timeval presentationTime,
280 unsigned durationInMicroseconds);
281 void afterGettingFrame(
unsigned frameSize,
unsigned numTruncatedBytes,
282 struct timeval presentationTime,
283 unsigned durationInMicroseconds);
287 virtual Boolean continuePlaying();
290 u_int8_t* fReceiveBuffer;
291 MediaSubsession& fSubsession;
295 OurRTSPClient* openURL(UsageEnvironment& env,
char const* progName,
char const* rtspURL) {
300 env, rtspURL, RTSP_CLIENT_VERBOSITY_LEVEL, progName);
301 if (rtspClient == NULL) {
302 env <<
"Failed to create a RTSP client for URL \"" << rtspURL
303 <<
"\": " << env.getResultMsg() <<
"\n";
314 rtspClient->sendDescribeCommand(continueAfterDESCRIBE);
320 void continueAfterDESCRIBE(RTSPClient* rtspClient,
int resultCode,
321 char* resultString) {
323 UsageEnvironment& env = rtspClient->envir();
324 StreamClientState& scs = ((OurRTSPClient*)rtspClient)->scs;
326 if (resultCode != 0) {
327 env << *rtspClient <<
"Failed to get a SDP description: " << resultString
329 delete[] resultString;
333 char*
const sdpDescription = resultString;
334 env << *rtspClient <<
"Got a SDP description:\n" << sdpDescription <<
"\n";
337 scs.session = MediaSession::createNew(env, sdpDescription);
338 delete[] sdpDescription;
339 if (scs.session == NULL) {
341 <<
"Failed to create a MediaSession object from the SDP description: "
342 << env.getResultMsg() <<
"\n";
344 }
else if (!scs.session->hasSubsessions()) {
346 <<
"This session has no media subsessions (i.e., no \"m=\" lines)\n";
354 scs.iter =
new MediaSubsessionIterator(*scs.session);
355 setupNextSubsession(rtspClient);
360 shutdownStream(rtspClient);
363 void setupNextSubsession(RTSPClient* rtspClient) {
364 UsageEnvironment& env = rtspClient->envir();
365 StreamClientState& scs = ((OurRTSPClient*)rtspClient)->scs;
367 scs.subsession = scs.iter->next();
368 if (scs.subsession != NULL) {
369 if (!scs.subsession->initiate()) {
370 env << *rtspClient <<
"Failed to initiate the \"" << *scs.subsession
371 <<
"\" subsession: " << env.getResultMsg() <<
"\n";
375 env << *rtspClient <<
"Initiated the \"" << *scs.subsession
376 <<
"\" subsession (";
377 if (scs.subsession->rtcpIsMuxed()) {
378 env <<
"client port " << scs.subsession->clientPortNum();
380 env <<
"client ports " << scs.subsession->clientPortNum() <<
"-"
381 << scs.subsession->clientPortNum() + 1;
386 rtspClient->sendSetupCommand(*scs.subsession, continueAfterSETUP, False,
394 if (scs.session->absStartTime() != NULL) {
397 rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY,
398 scs.session->absStartTime(),
399 scs.session->absEndTime());
401 scs.duration = scs.session->playEndTime() - scs.session->playStartTime();
402 rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY);
406 void continueAfterSETUP(RTSPClient* rtspClient,
int resultCode,
407 char* resultString) {
409 UsageEnvironment& env = rtspClient->envir();
410 StreamClientState& scs = ((OurRTSPClient*)rtspClient)->scs;
412 if (resultCode != 0) {
413 env << *rtspClient <<
"Failed to set up the \"" << *scs.subsession
414 <<
"\" subsession: " << resultString <<
"\n";
418 env << *rtspClient <<
"Set up the \"" << *scs.subsession
419 <<
"\" subsession (";
420 if (scs.subsession->rtcpIsMuxed()) {
421 env <<
"client port " << scs.subsession->clientPortNum();
423 env <<
"client ports " << scs.subsession->clientPortNum() <<
"-"
424 << scs.subsession->clientPortNum() + 1;
433 scs.subsession->sink =
434 OurSink::createNew(env, *scs.subsession, rtspClient->url());
436 if (scs.subsession->sink == NULL) {
437 env << *rtspClient <<
"Failed to create a data sink for the \""
438 << *scs.subsession <<
"\" subsession: " << env.getResultMsg() <<
"\n";
442 env << *rtspClient <<
"Created a data sink for the \"" << *scs.subsession
443 <<
"\" subsession\n";
444 scs.subsession->miscPtr =
447 scs.subsession->sink->startPlaying(*(scs.subsession->readSource()),
448 subsessionAfterPlaying, scs.subsession);
451 if (scs.subsession->rtcpInstance() != NULL) {
452 scs.subsession->rtcpInstance()->setByeWithReasonHandler(
453 subsessionByeHandler, scs.subsession);
456 delete[] resultString;
459 setupNextSubsession(rtspClient);
462 void continueAfterPLAY(RTSPClient* rtspClient,
int resultCode,
463 char* resultString) {
464 Boolean success = False;
467 UsageEnvironment& env = rtspClient->envir();
468 StreamClientState& scs = ((OurRTSPClient*)rtspClient)->scs;
470 if (resultCode != 0) {
471 env << *rtspClient <<
"Failed to start playing session: " << resultString
482 if (scs.duration > 0) {
483 unsigned const delaySlop =
486 scs.duration += delaySlop;
487 unsigned uSecsToDelay = (unsigned)(scs.duration * 1000000);
488 scs.streamTimerTask = env.taskScheduler().scheduleDelayedTask(
489 uSecsToDelay, (TaskFunc*)streamTimerHandler, rtspClient);
492 env << *rtspClient <<
"Started playing session";
493 if (scs.duration > 0) {
494 env <<
" (for up to " << scs.duration <<
" seconds)";
500 delete[] resultString;
504 shutdownStream(rtspClient);
510 void subsessionAfterPlaying(
void* clientData) {
511 MediaSubsession* subsession = (MediaSubsession*)clientData;
512 RTSPClient* rtspClient = (RTSPClient*)(subsession->miscPtr);
515 Medium::close(subsession->sink);
516 subsession->sink = NULL;
519 MediaSession& session = subsession->parentSession();
520 MediaSubsessionIterator iter(session);
521 while ((subsession = iter.next()) != NULL) {
522 if (subsession->sink != NULL)
return;
526 shutdownStream(rtspClient);
529 void subsessionByeHandler(
void* clientData,
char const* reason) {
530 MediaSubsession* subsession = (MediaSubsession*)clientData;
531 RTSPClient* rtspClient = (RTSPClient*)subsession->miscPtr;
532 UsageEnvironment& env = rtspClient->envir();
534 env << *rtspClient <<
"Received RTCP \"BYE\"";
535 if (reason != NULL) {
536 env <<
" (reason:\"" << reason <<
"\")";
537 delete[] (
char*)reason;
539 env <<
" on \"" << *subsession <<
"\" subsession\n";
542 subsessionAfterPlaying(subsession);
545 void streamTimerHandler(
void* clientData) {
546 OurRTSPClient* rtspClient = (OurRTSPClient*)clientData;
547 StreamClientState& scs = rtspClient->scs;
549 scs.streamTimerTask = NULL;
552 shutdownStream(rtspClient);
555 void shutdownStream(RTSPClient* rtspClient,
int exitCode) {
556 UsageEnvironment& env = rtspClient->envir();
557 StreamClientState& scs = ((OurRTSPClient*)rtspClient)->scs;
560 if (scs.session != NULL) {
561 Boolean someSubsessionsWereActive = False;
562 MediaSubsessionIterator iter(*scs.session);
563 MediaSubsession* subsession;
565 while ((subsession = iter.next()) != NULL) {
566 if (subsession->sink != NULL) {
567 Medium::close(subsession->sink);
568 subsession->sink = NULL;
570 if (subsession->rtcpInstance() != NULL) {
571 subsession->rtcpInstance()->setByeHandler(
576 someSubsessionsWereActive = True;
580 if (someSubsessionsWereActive) {
583 rtspClient->sendTeardownCommand(*scs.session, NULL);
587 env << *rtspClient <<
"Closing the stream.\n";
588 Medium::close(rtspClient);
592 if (--rtspClientCount == 0) {
599 rtspEventLoopWatchVariable = 1;
606 OurRTSPClient* OurRTSPClient::createNew(UsageEnvironment& env,
607 char const* rtspURL,
int verbosityLevel,
608 char const* applicationName,
609 portNumBits tunnelOverHTTPPortNum) {
610 return new OurRTSPClient(env, rtspURL, verbosityLevel, applicationName,
611 tunnelOverHTTPPortNum);
614 OurRTSPClient::OurRTSPClient(UsageEnvironment& env,
char const* rtspURL,
615 int verbosityLevel,
char const* applicationName,
616 portNumBits tunnelOverHTTPPortNum)
617 : RTSPClient(env, rtspURL, verbosityLevel, applicationName,
618 tunnelOverHTTPPortNum, -1) {}
620 OurRTSPClient::~OurRTSPClient() {}
624 StreamClientState::StreamClientState()
628 streamTimerTask(NULL),
631 StreamClientState::~StreamClientState() {
633 if (session != NULL) {
636 UsageEnvironment& env = session->envir();
638 env.taskScheduler().unscheduleDelayedTask(streamTimerTask);
639 Medium::close(session);
645 OurSink* OurSink::createNew(UsageEnvironment& env,
646 MediaSubsession& subsession,
647 char const* streamId) {
648 return new OurSink(env, subsession, streamId);
651 OurSink::OurSink(UsageEnvironment& env, MediaSubsession& subsession,
652 char const* streamId)
653 : MediaSink(env), fSubsession(subsession) {
654 fStreamId = strDup(streamId);
655 fReceiveBuffer =
new u_int8_t[rtspSinkReceiveBufferSize];
658 OurSink::~OurSink() {
659 delete[] fReceiveBuffer;
663 void OurSink::afterGettingFrame(
void* clientData,
unsigned frameSize,
664 unsigned numTruncatedBytes,
665 struct timeval presentationTime,
666 unsigned durationInMicroseconds) {
667 OurSink* sink = (OurSink*)clientData;
668 sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime,
669 durationInMicroseconds);
672 void OurSink::afterGettingFrame(
unsigned frameSize,
673 unsigned numTruncatedBytes,
674 struct timeval presentationTime,
678 #ifdef DEBUG_PRINT_EACH_RECEIVED_FRAME
679 if (fStreamId != NULL) envir() <<
"Stream \"" << fStreamId <<
"\"; ";
680 envir() << fSubsession.mediumName() <<
"/" << fSubsession.codecName()
681 <<
":\tReceived " << frameSize <<
" bytes";
682 if (numTruncatedBytes > 0)
683 envir() <<
" (with " << numTruncatedBytes <<
" bytes truncated)";
684 char uSecsStr[6 + 1];
686 snprintf(uSecsStr,7 ,
"%06u", (
unsigned)presentationTime.tv_usec);
687 envir() <<
".\tPresentation time: " << (int)presentationTime.tv_sec <<
"."
689 if (fSubsession.rtpSource() != NULL &&
690 !fSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP()) {
694 #ifdef DEBUG_PRINT_NPT
695 envir() <<
"\tNPT: " << fSubsession.getNormalPlayTime(presentationTime);
702 size_t writtenSize = rtspOutput->write(fReceiveBuffer, frameSize);
703 assert(writtenSize == frameSize);
710 Boolean OurSink::continuePlaying() {
711 if (fSource == NULL)
return False;
715 fSource->getNextFrame(fReceiveBuffer, rtspSinkReceiveBufferSize,
716 afterGettingFrame,
this, onSourceClosure,
this);