46template <
typename TcpClient,
typename UdpSocket>
93 if (factor <= 0.0f) factor = 1.0f;
94 float step = 1.0f / factor;
146 bool connected =
false;
152 LOGW(
"RTSPClient: connect attempt %u failed", (
unsigned)(attempt + 1));
156 LOGE(
"RTSPClient: TCP connect failed");
159 m_tcp.setNoDelay(
true);
173 return fail(
"OPTIONS failed");
175 LOGW(
"RTSPClient: retrying OPTIONS");
182 const char* describeExtra =
"Accept: application/sdp\r\n";
186 return fail(
"DESCRIBE failed");
203 char transportHdr[128];
204 snprintf(transportHdr,
sizeof(transportHdr),
205 "Transport: RTP/AVP;unicast;client_port=%u-%u\r\n",
211 snprintf(transportHdr,
sizeof(transportHdr),
212 "Transport: RTP/AVP/UDP;unicast;client_port=%u-%u\r\n",
217 return fail(
"SETUP failed");
231 char sessionHdr[128];
232 snprintf(sessionHdr,
sizeof(sessionHdr),
"Session: %s\r\n",
m_sessionId);
239 LOGW(
"RTSPClient: proceeding without PLAY response (RTP detected)");
241 return fail(
"PLAY failed");
261 if (
m_tcp.connected()) {
262 char sessionHdr[128];
264 snprintf(sessionHdr,
sizeof(sessionHdr),
"Session: %s\r\n",
318 return "audio/adpcm";
332 if (strcasecmp(
m_encoding,
"L16") == 0)
return "audio/L16";
333 if (strcasecmp(
m_encoding,
"L8") == 0)
return "audio/L8";
334 if (strcasecmp(
m_encoding,
"PCMU") == 0)
return "audio/PCMU";
335 if (strcasecmp(
m_encoding,
"PCMA") == 0)
return "audio/PCMA";
336 if (strcasecmp(
m_encoding,
"GSM") == 0)
return "audio/gsm";
337 if (strcasecmp(
m_encoding,
"MPA") == 0)
return "audio/mpeg";
338 if (strcasecmp(
m_encoding,
"MPEG4-GENERIC") == 0)
return "audio/aac";
339 if (strcasecmp(
m_encoding,
"OPUS") == 0)
return "audio/opus";
340 if (strcasecmp(
m_encoding,
"DVI4") == 0)
return "audio/adpcm";
359 char sessionHdr[128];
360 snprintf(sessionHdr,
sizeof(sessionHdr),
"Session: %s\r\n",
m_sessionId);
417 const char* m =
mime();
419 LOGI(
"Selecting decoder: %s", m);
432 LOGI(
"copy: %d -> %d", (
int)n, (
int)written);
524 const char* p = path;
527 size_t avail =
sizeof(
m_baseUrl) - used - 1;
528 if (avail > 0) strncat(
m_baseUrl, p, avail);
531 if (used > 0 &&
m_baseUrl[used - 1] !=
'/') {
543 for (uint16_t p = 5004; p < 65000; p += 2) {
544 if (
m_udp.begin(p)) {
545 LOGI(
"RTSPClient: bound UDP RTP port %u", (
unsigned)p);
555 LOGE(
"RTSPClient: %s", msg);
565 char sessionHdr[128];
567 snprintf(sessionHdr,
sizeof(sessionHdr),
"Session: %s\r\n",
m_sessionId);
572 sizeof(
m_hdrBuf),
nullptr, 0,
true);
580 if (length <= 12)
return length;
582 uint8_t cc = data[0] & 0x0F;
598 LOGI(
"Still have unprocessed data");
603 int packetSize =
m_udp.parsePacket();
604 if (packetSize <= 0) {
605 LOGD(
"packet size: %d", packetSize);
614 LOGE(
"packet too small: %d", n);
621 if (payloadOffset >= (
size_t)n) {
622 LOGW(
"no payload: %d", n);
634 for (
int i = 0; i < 2; ++i) {
645 uint32_t start =
millis();
646 while ((
millis() - start) < ms) {
647 int packetSize =
m_udp.parsePacket();
648 if (packetSize > 0) {
671 const char* extraHeaders,
size_t extraLen,
672 char* outHeaders,
size_t outHeadersLen,
char* outBody,
673 size_t outBodyLen,
bool quiet =
false) {
676 int reqLen = snprintf(
677 reqStart,
sizeof(reqStart),
678 "%s %s RTSP/1.0\r\nCSeq: %u\r\nUser-Agent: ArduinoAudioTools\r\n",
679 method, url, (
unsigned)
m_cseq++);
680 if (reqLen <= 0)
return false;
683 if (
tcpWrite((
const uint8_t*)reqStart, reqLen) != (
size_t)reqLen) {
687 if (extraHeaders && extraLen) {
688 if (
tcpWrite((
const uint8_t*)extraHeaders, extraLen) != extraLen) {
693 const char*
end =
"\r\n";
699 LOGE(
"TCP write failed");
705 memset(outHeaders, 0, outHeadersLen);
708 if (!quiet)
LOGE(
"RTSPClient: header read timeout");
714 if (outBody && outBodyLen && contentLen > 0) {
715 int toRead = contentLen;
716 if (toRead >= (
int)outBodyLen) toRead = (int)outBodyLen - 1;
717 int got =
readExact((uint8_t*)outBody, toRead, 2000);
718 if (got < 0)
return false;
725 uint32_t timeoutMs = 3000) {
726 uint32_t start =
millis();
729 while ((
millis() - start) < timeoutMs && used < (
int)buflen - 1) {
730 int avail =
m_tcp.available();
735 int n =
m_tcp.read((uint8_t*)buf + used, 1);
737 char c = buf[used++];
740 state = (c ==
'\r') ? 1 : 0;
743 state = (c ==
'\n') ? 2 : 0;
746 state = (c ==
'\r') ? 3 : 0;
749 state = (c ==
'\n') ? 4 : 0;
762 int readExact(uint8_t* out,
int len, uint32_t timeoutMs) {
763 uint32_t start =
millis();
765 while (got < len && (
millis() - start) < timeoutMs) {
766 int a =
m_tcp.available();
771 int n =
m_tcp.read(out + got, len - got);
774 return (got == len) ? got : got;
778 const char* p = strcasestr(headers,
"Content-Length:");
781 if (sscanf(p,
"Content-Length: %d", &len) == 1)
return len;
786 const char* p = strcasestr(headers,
"Session:");
789 while (*p ==
' ' || *p ==
'\t') ++p;
791 while (*p && *p !=
'\r' && *p !=
'\n' && *p !=
';' &&
799 const char* t = strcasestr(headers,
"Transport:");
801 const char* s = strcasestr(t,
"server_port=");
803 s += strlen(
"server_port=");
805 if (sscanf(s,
"%d-%d", &a, &b) == 2) {
814 while ((p = strcasestr(p,
"a=rtpmap:")) !=
nullptr) {
817 if (sscanf(p,
"%d", &pt) != 1)
continue;
818 const char* space = strchr(p,
' ');
819 if (!space)
continue;
823 while (space[i] && space[i] !=
'/' && space[i] !=
'\r' &&
824 space[i] !=
'\n' && i <
sizeof(
m_encoding) - 1) {
829 int rate = 0, ch = 0;
830 const char* afterEnc = space + i;
831 if (*afterEnc ==
'/') {
833 if (sscanf(afterEnc,
"%d/%d", &rate, &ch) < 1) {
842 }
else if (strcasecmp(
m_encoding,
"L8") == 0) {
856 if (!headers)
return;
857 const char* p = strcasestr(headers,
"Content-Base:");
859 p += strlen(
"Content-Base:");
860 while (*p ==
' ' || *p ==
'\t') ++p;
862 while (*p && *p !=
'\r' && *p !=
'\n' && i <
sizeof(
m_contentBase) - 1) {
879 const char* audio = strcasestr(sdp,
"\nm=audio ");
880 const char* searchStart = sdp;
881 const char* searchEnd =
nullptr;
885 const char* nextm = strcasestr(audio + 1,
"\nm=");
886 searchEnd = nextm ? nextm : (sdp + strlen(sdp));
890 searchEnd = sdp + strlen(sdp);
892 const char* p = searchStart;
893 while (p && p < searchEnd) {
894 const char* ctrl = strcasestr(p,
"a=control:");
895 if (!ctrl || ctrl >= searchEnd)
break;
896 ctrl += strlen(
"a=control:");
899 while (ctrl[i] && ctrl[i] !=
'\r' && ctrl[i] !=
'\n' &&
910 if (!url)
return false;
911 return (strncasecmp(url,
"rtsp://", 7) == 0) ||
912 (strncasecmp(url,
"rtsps://", 8) == 0);
927 size_t blen = strlen(base);
931 for (; pos <
sizeof(tmp) - 1 && pos < blen; ++pos) tmp[pos] = base[pos];
932 if (pos > 0 && tmp[pos - 1] !=
'/' && pos <
sizeof(tmp) - 1)
936 if (*ctrl ==
'/') ++ctrl;
937 while (*ctrl && pos <
sizeof(tmp) - 1) tmp[pos++] = *ctrl++;
If you want to use the framework w/o Arduino you need to provide the implementation of a couple of cl...
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGD(...)
Definition AudioLoggerIDF.h:27
#define LOGE(...)
Definition AudioLoggerIDF.h:30