25#include "AudioLogger.h"
28#include "BasicUsageEnvironment.hh"
30#include "RTSPClient.hh"
35#define REQUEST_STREAMING_OVER_TCP false
38#define RTSP_CLIENT_VERBOSITY_LEVEL 1
41#define RTSP_SINK_BUFFER_SIZE 1024
45#undef DEBUG_PRINT_EACH_RECEIVED_FRAME
46#define DEBUG_PRINT_EACH_RECEIVED_FRAME 0
53OurRTSPClient * openURL(UsageEnvironment& env,
char const* progName,
char const* rtspURL);
55static unsigned rtspClientCount = 0;
56static char rtspEventLoopWatchVariable = 0;
57static Print* rtspOutput =
nullptr;
58static uint32_t rtspSinkReceiveBufferSize = 0;
59static bool rtspUseTCP = REQUEST_STREAMING_OVER_TCP;
73 AudioClientRTSP(uint32_t receiveBufferSize = RTSP_SINK_BUFFER_SIZE,
bool useTCP=REQUEST_STREAMING_OVER_TCP,
bool blocking =
false) {
74 setBufferSize(receiveBufferSize);
75 useTCP ? setTCP() : setUDP();
76 setBlocking(blocking);
79 void setBufferSize(
int size){
80 audiotools_rtsp::rtspSinkReceiveBufferSize = size;
84 audiotools_rtsp::rtspUseTCP =
true;
88 audiotools_rtsp::rtspUseTCP =
false;
91 void setBlocking(
bool flag){
96 void setLogin(
const char* ssid,
const char* password){
98 this->password = password;
96 void setLogin(
const char* ssid,
const char* password) {
…}
103 audiotools_rtsp::rtspOutput = &out;
112 scheduler = BasicTaskScheduler::createNew();
113 env = BasicUsageEnvironment::createNew(*scheduler);
117 rtsp_client = audiotools_rtsp::openURL(*env,
"RTSPClient", url);
120 if (is_blocking) env->taskScheduler().doEventLoop(&audiotools_rtsp::rtspEventLoopWatchVariable);
129 if (audiotools_rtsp::rtspEventLoopWatchVariable==0) scheduler->SingleStep();
133 audiotools_rtsp::rtspEventLoopWatchVariable = 1;
138 bool is_blocking =
false;
147 UsageEnvironment* env=
nullptr;
148 BasicTaskScheduler* scheduler=
nullptr;
149 const char* ssid=
nullptr;
150 const char* password =
nullptr;
151 bool is_blocking =
false;
155 if(WiFi.status() != WL_CONNECTED && ssid!=
nullptr && password!=
nullptr){
157 WiFi.begin(ssid, password);
158 while(WiFi.status() != WL_CONNECTED){
163 Serial.print(
"Local Address: ");
164 Serial.println(WiFi.localIP());
166 return WiFi.status() == WL_CONNECTED;
181void continueAfterDESCRIBE(RTSPClient* rtspClient,
int resultCode,
183void continueAfterSETUP(RTSPClient* rtspClient,
int resultCode,
185void continueAfterPLAY(RTSPClient* rtspClient,
int resultCode,
189void subsessionAfterPlaying(
192void subsessionByeHandler(
void* clientData,
char const* reason);
194void streamTimerHandler(
void* clientData);
199void setupNextSubsession(RTSPClient* rtspClient);
202void shutdownStream(RTSPClient* rtspClient,
int exitCode = 1);
206UsageEnvironment& operator<<(UsageEnvironment& env,
207 const RTSPClient& rtspClient) {
208 return env <<
"[URL:\"" << rtspClient.url() <<
"\"]: ";
213UsageEnvironment& operator<<(UsageEnvironment& env,
214 const MediaSubsession& subsession) {
215 return env << subsession.mediumName() <<
"/" << subsession.codecName();
224 MediaSubsessionIterator* iter;
225 MediaSession* session;
226 MediaSubsession* subsession;
227 TaskToken streamTimerTask;
241 static OurRTSPClient* createNew(UsageEnvironment& env,
char const* rtspURL,
242 int verbosityLevel = 0,
243 char const* applicationName = NULL,
244 portNumBits tunnelOverHTTPPortNum = 0);
247 OurRTSPClient(UsageEnvironment& env,
char const* rtspURL,
int verbosityLevel,
248 char const* applicationName, portNumBits tunnelOverHTTPPortNum);
267 UsageEnvironment& env,
270 char const* streamId = NULL);
273 OurSink(UsageEnvironment& env, MediaSubsession& subsession,
274 char const* streamId);
278 static void afterGettingFrame(
void* clientData,
unsigned frameSize,
279 unsigned numTruncatedBytes,
280 struct timeval presentationTime,
281 unsigned durationInMicroseconds);
282 void afterGettingFrame(
unsigned frameSize,
unsigned numTruncatedBytes,
283 struct timeval presentationTime,
284 unsigned durationInMicroseconds);
288 virtual Boolean continuePlaying();
291 u_int8_t* fReceiveBuffer;
292 MediaSubsession& fSubsession;
296OurRTSPClient* openURL(UsageEnvironment& env,
char const* progName,
char const* rtspURL) {
301 env, rtspURL, RTSP_CLIENT_VERBOSITY_LEVEL, progName);
302 if (rtspClient == NULL) {
303 env <<
"Failed to create a RTSP client for URL \"" << rtspURL
304 <<
"\": " << env.getResultMsg() <<
"\n";
315 rtspClient->sendDescribeCommand(continueAfterDESCRIBE);
321void continueAfterDESCRIBE(RTSPClient* rtspClient,
int resultCode,
322 char* resultString) {
324 UsageEnvironment& env = rtspClient->envir();
325 StreamClientState& scs = ((OurRTSPClient*)rtspClient)->scs;
327 if (resultCode != 0) {
328 env << *rtspClient <<
"Failed to get a SDP description: " << resultString
330 delete[] resultString;
334 char*
const sdpDescription = resultString;
335 env << *rtspClient <<
"Got a SDP description:\n" << sdpDescription <<
"\n";
338 scs.session = MediaSession::createNew(env, sdpDescription);
339 delete[] sdpDescription;
340 if (scs.session == NULL) {
342 <<
"Failed to create a MediaSession object from the SDP description: "
343 << env.getResultMsg() <<
"\n";
345 }
else if (!scs.session->hasSubsessions()) {
347 <<
"This session has no media subsessions (i.e., no \"m=\" lines)\n";
355 scs.iter =
new MediaSubsessionIterator(*scs.session);
356 setupNextSubsession(rtspClient);
361 shutdownStream(rtspClient);
364void setupNextSubsession(RTSPClient* rtspClient) {
365 UsageEnvironment& env = rtspClient->envir();
366 StreamClientState& scs = ((OurRTSPClient*)rtspClient)->scs;
368 scs.subsession = scs.iter->next();
369 if (scs.subsession != NULL) {
370 if (!scs.subsession->initiate()) {
371 env << *rtspClient <<
"Failed to initiate the \"" << *scs.subsession
372 <<
"\" subsession: " << env.getResultMsg() <<
"\n";
376 env << *rtspClient <<
"Initiated the \"" << *scs.subsession
377 <<
"\" subsession (";
378 if (scs.subsession->rtcpIsMuxed()) {
379 env <<
"client port " << scs.subsession->clientPortNum();
381 env <<
"client ports " << scs.subsession->clientPortNum() <<
"-"
382 << scs.subsession->clientPortNum() + 1;
387 rtspClient->sendSetupCommand(*scs.subsession, continueAfterSETUP, False,
395 if (scs.session->absStartTime() != NULL) {
398 rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY,
399 scs.session->absStartTime(),
400 scs.session->absEndTime());
402 scs.duration = scs.session->playEndTime() - scs.session->playStartTime();
403 rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY);
407void continueAfterSETUP(RTSPClient* rtspClient,
int resultCode,
408 char* resultString) {
410 UsageEnvironment& env = rtspClient->envir();
411 StreamClientState& scs = ((OurRTSPClient*)rtspClient)->scs;
413 if (resultCode != 0) {
414 env << *rtspClient <<
"Failed to set up the \"" << *scs.subsession
415 <<
"\" subsession: " << resultString <<
"\n";
419 env << *rtspClient <<
"Set up the \"" << *scs.subsession
420 <<
"\" subsession (";
421 if (scs.subsession->rtcpIsMuxed()) {
422 env <<
"client port " << scs.subsession->clientPortNum();
424 env <<
"client ports " << scs.subsession->clientPortNum() <<
"-"
425 << scs.subsession->clientPortNum() + 1;
434 scs.subsession->sink =
435 OurSink::createNew(env, *scs.subsession, rtspClient->url());
437 if (scs.subsession->sink == NULL) {
438 env << *rtspClient <<
"Failed to create a data sink for the \""
439 << *scs.subsession <<
"\" subsession: " << env.getResultMsg() <<
"\n";
443 env << *rtspClient <<
"Created a data sink for the \"" << *scs.subsession
444 <<
"\" subsession\n";
445 scs.subsession->miscPtr =
448 scs.subsession->sink->startPlaying(*(scs.subsession->readSource()),
449 subsessionAfterPlaying, scs.subsession);
452 if (scs.subsession->rtcpInstance() != NULL) {
453 scs.subsession->rtcpInstance()->setByeWithReasonHandler(
454 subsessionByeHandler, scs.subsession);
457 delete[] resultString;
460 setupNextSubsession(rtspClient);
463void continueAfterPLAY(RTSPClient* rtspClient,
int resultCode,
464 char* resultString) {
465 Boolean success = False;
468 UsageEnvironment& env = rtspClient->envir();
469 StreamClientState& scs = ((OurRTSPClient*)rtspClient)->scs;
471 if (resultCode != 0) {
472 env << *rtspClient <<
"Failed to start playing session: " << resultString
483 if (scs.duration > 0) {
484 unsigned const delaySlop =
487 scs.duration += delaySlop;
488 unsigned uSecsToDelay = (unsigned)(scs.duration * 1000000);
489 scs.streamTimerTask = env.taskScheduler().scheduleDelayedTask(
490 uSecsToDelay, (TaskFunc*)streamTimerHandler, rtspClient);
493 env << *rtspClient <<
"Started playing session";
494 if (scs.duration > 0) {
495 env <<
" (for up to " << scs.duration <<
" seconds)";
501 delete[] resultString;
505 shutdownStream(rtspClient);
511void subsessionAfterPlaying(
void* clientData) {
512 MediaSubsession* subsession = (MediaSubsession*)clientData;
513 RTSPClient* rtspClient = (RTSPClient*)(subsession->miscPtr);
516 Medium::close(subsession->sink);
517 subsession->sink = NULL;
520 MediaSession& session = subsession->parentSession();
521 MediaSubsessionIterator iter(session);
522 while ((subsession = iter.next()) != NULL) {
523 if (subsession->sink != NULL)
return;
527 shutdownStream(rtspClient);
530void subsessionByeHandler(
void* clientData,
char const* reason) {
531 MediaSubsession* subsession = (MediaSubsession*)clientData;
532 RTSPClient* rtspClient = (RTSPClient*)subsession->miscPtr;
533 UsageEnvironment& env = rtspClient->envir();
535 env << *rtspClient <<
"Received RTCP \"BYE\"";
536 if (reason != NULL) {
537 env <<
" (reason:\"" << reason <<
"\")";
538 delete[] (
char*)reason;
540 env <<
" on \"" << *subsession <<
"\" subsession\n";
543 subsessionAfterPlaying(subsession);
546void streamTimerHandler(
void* clientData) {
547 OurRTSPClient* rtspClient = (OurRTSPClient*)clientData;
548 StreamClientState& scs = rtspClient->scs;
550 scs.streamTimerTask = NULL;
553 shutdownStream(rtspClient);
556void shutdownStream(RTSPClient* rtspClient,
int exitCode) {
557 UsageEnvironment& env = rtspClient->envir();
558 StreamClientState& scs = ((OurRTSPClient*)rtspClient)->scs;
561 if (scs.session != NULL) {
562 Boolean someSubsessionsWereActive = False;
563 MediaSubsessionIterator iter(*scs.session);
564 MediaSubsession* subsession;
566 while ((subsession = iter.next()) != NULL) {
567 if (subsession->sink != NULL) {
568 Medium::close(subsession->sink);
569 subsession->sink = NULL;
571 if (subsession->rtcpInstance() != NULL) {
572 subsession->rtcpInstance()->setByeHandler(
577 someSubsessionsWereActive = True;
581 if (someSubsessionsWereActive) {
584 rtspClient->sendTeardownCommand(*scs.session, NULL);
588 env << *rtspClient <<
"Closing the stream.\n";
589 Medium::close(rtspClient);
593 if (--rtspClientCount == 0) {
600 rtspEventLoopWatchVariable = 1;
607OurRTSPClient* OurRTSPClient::createNew(UsageEnvironment& env,
608 char const* rtspURL,
int verbosityLevel,
609 char const* applicationName,
610 portNumBits tunnelOverHTTPPortNum) {
611 return new OurRTSPClient(env, rtspURL, verbosityLevel, applicationName,
612 tunnelOverHTTPPortNum);
615OurRTSPClient::OurRTSPClient(UsageEnvironment& env,
char const* rtspURL,
616 int verbosityLevel,
char const* applicationName,
617 portNumBits tunnelOverHTTPPortNum)
618 : RTSPClient(env, rtspURL, verbosityLevel, applicationName,
619 tunnelOverHTTPPortNum, -1) {}
621OurRTSPClient::~OurRTSPClient() {}
625StreamClientState::StreamClientState()
629 streamTimerTask(NULL),
632StreamClientState::~StreamClientState() {
634 if (session != NULL) {
637 UsageEnvironment& env = session->envir();
639 env.taskScheduler().unscheduleDelayedTask(streamTimerTask);
640 Medium::close(session);
646OurSink* OurSink::createNew(UsageEnvironment& env,
647 MediaSubsession& subsession,
648 char const* streamId) {
649 return new OurSink(env, subsession, streamId);
652OurSink::OurSink(UsageEnvironment& env, MediaSubsession& subsession,
653 char const* streamId)
654 : MediaSink(env), fSubsession(subsession) {
655 fStreamId = strDup(streamId);
656 fReceiveBuffer =
new u_int8_t[rtspSinkReceiveBufferSize];
660 delete[] fReceiveBuffer;
664void OurSink::afterGettingFrame(
void* clientData,
unsigned frameSize,
665 unsigned numTruncatedBytes,
666 struct timeval presentationTime,
667 unsigned durationInMicroseconds) {
668 OurSink* sink = (OurSink*)clientData;
669 sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime,
670 durationInMicroseconds);
673void OurSink::afterGettingFrame(
unsigned frameSize,
674 unsigned numTruncatedBytes,
675 struct timeval presentationTime,
679#ifdef DEBUG_PRINT_EACH_RECEIVED_FRAME
680 if (fStreamId != NULL) envir() <<
"Stream \"" << fStreamId <<
"\"; ";
681 envir() << fSubsession.mediumName() <<
"/" << fSubsession.codecName()
682 <<
":\tReceived " << frameSize <<
" bytes";
683 if (numTruncatedBytes > 0)
684 envir() <<
" (with " << numTruncatedBytes <<
" bytes truncated)";
685 char uSecsStr[6 + 1];
687 snprintf(uSecsStr,7 ,
"%06u", (
unsigned)presentationTime.tv_usec);
688 envir() <<
".\tPresentation time: " << (int)presentationTime.tv_sec <<
"."
690 if (fSubsession.rtpSource() != NULL &&
691 !fSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP()) {
695#ifdef DEBUG_PRINT_NPT
696 envir() <<
"\tNPT: " << fSubsession.getNormalPlayTime(presentationTime);
703 size_t writtenSize = rtspOutput->write(fReceiveBuffer, frameSize);
704 assert(writtenSize == frameSize);
711Boolean OurSink::continuePlaying() {
712 if (fSource == NULL)
return False;
716 fSource->getNextFrame(fReceiveBuffer, rtspSinkReceiveBufferSize,
717 afterGettingFrame,
this, onSourceClosure,
this);