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);
425 const char* m =
mime();
427 LOGI(
"Selecting decoder: %s", m);
440 LOGI(
"copy: %d -> %d", (
int)n, (
int)written);
532 const char* p = path;
535 size_t avail =
sizeof(
m_baseUrl) - used - 1;
536 if (avail > 0) strncat(
m_baseUrl, p, avail);
539 if (used > 0 &&
m_baseUrl[used - 1] !=
'/') {
551 for (uint16_t p = 5004; p < 65000; p += 2) {
552 if (
m_udp.begin(p)) {
553 LOGI(
"RTSPClient: bound UDP RTP port %u", (
unsigned)p);
563 LOGE(
"RTSPClient: %s", msg);
573 char sessionHdr[128];
575 snprintf(sessionHdr,
sizeof(sessionHdr),
"Session: %s\r\n",
m_sessionId);
580 sizeof(
m_hdrBuf),
nullptr, 0,
true);
588 if (length <= 12)
return length;
590 uint8_t cc = data[0] & 0x0F;
606 LOGI(
"Still have unprocessed data");
611 int packetSize =
m_udp.parsePacket();
612 if (packetSize <= 0) {
613 LOGD(
"packet size: %d", packetSize);
622 LOGE(
"packet too small: %d", n);
629 if (payloadOffset >= (
size_t)n) {
630 LOGW(
"no payload: %d", n);
642 for (
int i = 0; i < 2; ++i) {
653 uint32_t start =
millis();
654 while ((
millis() - start) < ms) {
655 int packetSize =
m_udp.parsePacket();
656 if (packetSize > 0) {
679 const char* extraHeaders,
size_t extraLen,
680 char* outHeaders,
size_t outHeadersLen,
char* outBody,
681 size_t outBodyLen,
bool quiet =
false) {
684 int reqLen = snprintf(
685 reqStart,
sizeof(reqStart),
686 "%s %s RTSP/1.0\r\nCSeq: %u\r\nUser-Agent: ArduinoAudioTools\r\n",
687 method, url, (
unsigned)
m_cseq++);
688 if (reqLen <= 0)
return false;
691 if (
tcpWrite((
const uint8_t*)reqStart, reqLen) != (
size_t)reqLen) {
695 if (extraHeaders && extraLen) {
696 if (
tcpWrite((
const uint8_t*)extraHeaders, extraLen) != extraLen) {
701 const char*
end =
"\r\n";
707 LOGE(
"TCP write failed");
713 memset(outHeaders, 0, outHeadersLen);
716 if (!quiet)
LOGE(
"RTSPClient: header read timeout");
722 if (outBody && outBodyLen && contentLen > 0) {
723 int toRead = contentLen;
724 if (toRead >= (
int)outBodyLen) toRead = (int)outBodyLen - 1;
725 int got =
readExact((uint8_t*)outBody, toRead, 2000);
726 if (got < 0)
return false;
733 uint32_t timeoutMs = 3000) {
734 uint32_t start =
millis();
737 while ((
millis() - start) < timeoutMs && used < (
int)buflen - 1) {
738 int avail =
m_tcp.available();
743 int n =
m_tcp.read((uint8_t*)buf + used, 1);
745 char c = buf[used++];
748 state = (c ==
'\r') ? 1 : 0;
751 state = (c ==
'\n') ? 2 : 0;
754 state = (c ==
'\r') ? 3 : 0;
757 state = (c ==
'\n') ? 4 : 0;
770 int readExact(uint8_t* out,
int len, uint32_t timeoutMs) {
771 uint32_t start =
millis();
773 while (got < len && (
millis() - start) < timeoutMs) {
774 int a =
m_tcp.available();
779 int n =
m_tcp.read(out + got, len - got);
782 return (got == len) ? got : got;
786 const char* p = strcasestr(headers,
"Content-Length:");
789 if (sscanf(p,
"Content-Length: %d", &len) == 1)
return len;
794 const char* p = strcasestr(headers,
"Session:");
797 while (*p ==
' ' || *p ==
'\t') ++p;
799 while (*p && *p !=
'\r' && *p !=
'\n' && *p !=
';' &&
807 const char* t = strcasestr(headers,
"Transport:");
809 const char* s = strcasestr(t,
"server_port=");
811 s += strlen(
"server_port=");
813 if (sscanf(s,
"%d-%d", &a, &b) == 2) {
822 while ((p = strcasestr(p,
"a=rtpmap:")) !=
nullptr) {
825 if (sscanf(p,
"%d", &pt) != 1)
continue;
826 const char* space = strchr(p,
' ');
827 if (!space)
continue;
831 while (space[i] && space[i] !=
'/' && space[i] !=
'\r' &&
832 space[i] !=
'\n' && i <
sizeof(
m_encoding) - 1) {
837 int rate = 0, ch = 0;
838 const char* afterEnc = space + i;
839 if (*afterEnc ==
'/') {
841 if (sscanf(afterEnc,
"%d/%d", &rate, &ch) < 1) {
850 }
else if (strcasecmp(
m_encoding,
"L8") == 0) {
864 if (!headers)
return;
865 const char* p = strcasestr(headers,
"Content-Base:");
867 p += strlen(
"Content-Base:");
868 while (*p ==
' ' || *p ==
'\t') ++p;
870 while (*p && *p !=
'\r' && *p !=
'\n' && i <
sizeof(
m_contentBase) - 1) {
887 const char* audio = strcasestr(sdp,
"\nm=audio ");
888 const char* searchStart = sdp;
889 const char* searchEnd =
nullptr;
893 const char* nextm = strcasestr(audio + 1,
"\nm=");
894 searchEnd = nextm ? nextm : (sdp + strlen(sdp));
898 searchEnd = sdp + strlen(sdp);
900 const char* p = searchStart;
901 while (p && p < searchEnd) {
902 const char* ctrl = strcasestr(p,
"a=control:");
903 if (!ctrl || ctrl >= searchEnd)
break;
904 ctrl += strlen(
"a=control:");
907 while (ctrl[i] && ctrl[i] !=
'\r' && ctrl[i] !=
'\n' &&
918 if (!url)
return false;
919 return (strncasecmp(url,
"rtsp://", 7) == 0) ||
920 (strncasecmp(url,
"rtsps://", 8) == 0);
935 size_t blen = strlen(base);
939 for (; pos <
sizeof(tmp) - 1 && pos < blen; ++pos) tmp[pos] = base[pos];
940 if (pos > 0 && tmp[pos - 1] !=
'/' && pos <
sizeof(tmp) - 1)
944 if (*ctrl ==
'/') ++ctrl;
945 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