arduino-audio-tools
Loading...
Searching...
No Matches
RTSPSession.h
1/*
2 * Author: Phil Schatzmann
3 *
4 * Based on Micro-RTSP library:
5 * https://github.com/Tomp0801/Micro-RTSP-Audio
6 * https://github.com/geeksville/Micro-RTSP
7 *
8 */
9
10#pragma once
11
12#include <stdio.h>
13
14#include <ctime>
15
16#include "AudioTools/CoreAudio/AudioBasic/Collections/Vector.h"
17#include "RTSPAudioStreamer.h"
18#include "RTSPPlatform.h"
19
21#define RTSP_BUFFER_SIZE 10000
23#define RTSP_PARAM_STRING_MAX 100
25#define MAX_HOSTNAME_LEN 256
27#define RTSP_RESPONSE_BUFFER_SIZE 2251
29#define RTSP_SDP_BUFFER_SIZE 1024
31#define RTSP_URL_BUFFER_SIZE 1024
33#define RTSP_SMALL_BUFFER_SIZE 256
34
35namespace audio_tools {
36
39 RTSP_OPTIONS,
40 RTSP_DESCRIBE,
41 RTSP_SETUP,
42 RTSP_PLAY,
43 RTSP_PAUSE,
44 RTSP_TEARDOWN,
45 RTSP_UNKNOWN
46};
47
77template <typename Platform>
79 public:
98 RtspSession(typename Platform::TcpClientType& aClient,
100 : m_Client(aClient), m_Streamer(&aStreamer) {
101 m_RtspClient = &m_Client;
102 m_RtspSessionID = random(65536); // create a session ID
103 LOGI("RTSP session created");
104 }
105
115 LOGI("RTSP session destructor");
116
117 // Ensure streaming is stopped and resources are released
118 if (m_streaming && m_Streamer) {
119 LOGI("Final cleanup: stopping streamer in destructor");
120 m_Streamer->stop();
121 m_Streamer->releaseUdpTransport();
122 m_streaming = false;
123 }
124
125 // Close the client socket (check connection using client method directly)
126 if (m_RtspClient && m_RtspClient->connected()) {
127 Platform::closeSocket(m_RtspClient);
128 }
129
130 LOGI("RTSP session cleanup completed");
131 }
132
149 bool handleRequests(uint32_t readTimeoutMs) {
150 LOGD("handleRequests");
151 // initlaize buffers and state if not already done
152 init();
153
154 if (!m_sessionOpen) {
155 delay(100); // give some time to close down
156 return false; // Already closed down
157 }
158
159 memset(mRecvBuf.data(), 0x00, RTSP_BUFFER_SIZE);
160 int res = readSocket(m_RtspClient, mRecvBuf.data(), RTSP_BUFFER_SIZE,
161 readTimeoutMs);
162 if (res > 0) {
163 // we filter away everything which seems not to be an RTSP command:
164 // O-ption, D-escribe, S-etup, P-lay, T-eardown
165 if ((mRecvBuf[0] == 'O') || (mRecvBuf[0] == 'D') ||
166 (mRecvBuf[0] == 'S') || (mRecvBuf[0] == 'P') ||
167 (mRecvBuf[0] == 'T')) {
168 RTSP_CMD_TYPES C = handleRtspRequest(mRecvBuf.data(), res);
169 // TODO this should go in the handling functions
170 if (C == RTSP_PLAY) {
171 m_streaming = true;
172 } else if (C == RTSP_PAUSE) {
173 m_streaming = false;
174 } else if (C == RTSP_TEARDOWN) {
175 m_sessionOpen = false; // Session ended by TEARDOWN
176
177 // Properly cleanup streaming on TEARDOWN command
178 if (m_streaming && m_Streamer) {
179 LOGI("Stopping streamer due to TEARDOWN");
180 m_Streamer->stop();
181 m_Streamer->releaseUdpTransport();
182 m_streaming = false;
183 }
184 }
185 }
186 return true;
187 } else if (res == 0) {
188 LOGW("client closed socket, exiting");
189 m_sessionOpen = false; // Session ended by client disconnect
190
191 // CRITICAL: Properly cleanup streaming when client disconnects
192 if (m_streaming && m_Streamer) {
193 LOGI("Stopping streamer due to client disconnect");
194 m_Streamer->stop();
195 m_Streamer->releaseUdpTransport();
196 m_streaming = false;
197 }
198
199 return false; // Return false to indicate session should end
200 } else {
201 // Timeout on read
202 // printf("RTSP read timeout\n");
203 return false;
204 }
205 }
206
207 bool isSessionOpen() { return m_sessionOpen; }
208
209 bool isStreaming() { return m_streaming; }
210
211 protected:
212 const char* STD_URL_PRE_SUFFIX = "trackID";
213
214 // global session state parameters
215 int m_RtspSessionID;
216 typename Platform::TcpClientType m_Client;
217 typename Platform::TcpClientType* m_RtspClient =
218 nullptr; // RTSP socket of session
219 int m_StreamID = -1; // number of simulated stream of that session
220 uint16_t m_ClientRTPPort; // client port for UDP based RTP transport
221 uint16_t m_ClientRTCPPort; // client port for UDP based RTCP transport
222 RTSPAudioStreamerBase<Platform>* m_Streamer =
223 nullptr; // the UDP streamer of that session
224
225 // parameters of the last received RTSP request
226 RTSP_CMD_TYPES m_RtspCmdType; // command type (if any) of the current request
227 audio_tools::Vector<char> m_URLPreSuffix; // stream name pre suffix
228 audio_tools::Vector<char> m_URLSuffix; // stream name suffix
229 audio_tools::Vector<char> m_CSeq; // RTSP command sequence number
230 audio_tools::Vector<char> m_URLHostPort; // host:port part of the URL
231 unsigned m_ContentLength; // SDP string size
232 uint16_t m_RtpClientPort =
233 0; // RTP receiver port on client (in host byte order!)
234 uint16_t m_RtcpClientPort =
235 0; // RTCP receiver port on client (in host byte order!)
236 // Transport parsing (TCP interleaved)
237 bool m_TransportIsTcp = false;
238 int m_InterleavedRtp = -1;
239 int m_InterleavedRtcp = -1;
241 m_Response; // Note: we assume single threaded, this large buf we
242 // keep off of the tiny stack
248 audio_tools::Vector<char> mCurRequest;
250 bool m_is_init = false;
251 bool m_streaming = false;
252 volatile bool m_sessionOpen = true;
253
261 void init() {
262 if (m_is_init) return;
263 LOGD("init");
264
265 // Reset session state for clean initialization
266 m_streaming = false;
267 m_sessionOpen = true;
268
269 // initialize buffers if not already done
270 if (mRecvBuf.size() == 0) {
271 mRecvBuf.resize(RTSP_BUFFER_SIZE);
272 }
273 if (mCurRequest.size() == 0) {
274 mCurRequest.resize(RTSP_BUFFER_SIZE);
275 }
276 if (m_URLPreSuffix.size() == 0) {
277 m_URLPreSuffix.resize(RTSP_PARAM_STRING_MAX);
278 }
279 if (m_URLSuffix.size() == 0) {
280 m_URLSuffix.resize(RTSP_PARAM_STRING_MAX);
281 }
282 if (m_CSeq.size() == 0) {
283 m_CSeq.resize(RTSP_PARAM_STRING_MAX);
284 }
285 if (m_URLHostPort.size() == 0) {
286 m_URLHostPort.resize(MAX_HOSTNAME_LEN);
287 }
288 if (m_Response.size() == 0) {
289 m_Response.resize(RTSP_RESPONSE_BUFFER_SIZE);
290 }
291 if (m_SDPBuf.size() == 0) {
292 m_SDPBuf.resize(RTSP_SDP_BUFFER_SIZE);
293 }
294 if (m_URLBuf.size() == 0) {
295 m_URLBuf.resize(RTSP_URL_BUFFER_SIZE);
296 }
297 if (m_Buf1.size() == 0) {
298 m_Buf1.resize(RTSP_SMALL_BUFFER_SIZE);
299 }
300 if (m_Buf2.size() == 0) {
301 m_Buf2.resize(RTSP_SMALL_BUFFER_SIZE);
302 }
303 if (m_CmdName.size() == 0) {
304 m_CmdName.resize(RTSP_PARAM_STRING_MAX);
305 }
306
307 m_RtspCmdType = RTSP_UNKNOWN;
308 memset(m_URLPreSuffix.data(), 0x00, m_URLPreSuffix.size());
309 memset(m_URLSuffix.data(), 0x00, m_URLSuffix.size());
310 memset(m_CSeq.data(), 0x00, m_CSeq.size());
311 memset(m_URLHostPort.data(), 0x00, m_URLHostPort.size());
312 m_ContentLength = 0;
313 m_TransportIsTcp = false;
314 m_InterleavedRtp = -1;
315 m_InterleavedRtcp = -1;
316 m_is_init = true;
317 }
318
326 RTSP_CMD_TYPES handleRtspRequest(char const* aRequest,
327 unsigned aRequestSize) {
328 if (parseRtspRequest(aRequest, aRequestSize)) {
329 switch (m_RtspCmdType) {
330 case RTSP_OPTIONS: {
332 break;
333 }
334 case RTSP_DESCRIBE: {
336 break;
337 }
338 case RTSP_SETUP: {
340 break;
341 }
342 case RTSP_PLAY: {
344 break;
345 }
346 case RTSP_PAUSE: {
348 break;
349 }
350 case RTSP_TEARDOWN: {
352 break;
353 }
354 default: {
355 }
356 }
357 }
358 return m_RtspCmdType;
359 }
360
368 bool parseRtspRequest(char const* aRequest, unsigned aRequestSize) {
369 LOGI("aRequest: ------------------------\n%s\n-------------------------",
370 aRequest);
371
372 const unsigned CurRequestSize = aRequestSize;
373 memcpy(mCurRequest.data(), aRequest, aRequestSize);
374
375 // 1) Ports and transport
376 parseClientPorts(mCurRequest.data());
377 parseTransportHeader(mCurRequest.data());
378
379 // 2) Command + URL host/parts
380 unsigned idxAfterCmd = 0;
381 if (!parseCommandName(mCurRequest.data(), CurRequestSize, idxAfterCmd))
382 return false;
383 determineCommandType();
384 parseUrlHostPortAndSuffix(mCurRequest.data(), CurRequestSize, idxAfterCmd);
385
386 // 3) CSeq and Content-Length
387 if (!parseCSeq(mCurRequest.data(), CurRequestSize, idxAfterCmd))
388 return false;
389 parseContentLength(mCurRequest.data(), CurRequestSize, idxAfterCmd);
390
391 // 4) Client preference toggle (User-Agent / URL)
392 detectClientHeaderPreference(mCurRequest.data());
393
394 return true;
395 }
396
397 // ---- Parsing helpers ----
398 void parseClientPorts(char* req) {
399 char* ClientPortPtr = strstr(req, "client_port");
400 if (!ClientPortPtr) return;
401 char* lineEnd = strstr(ClientPortPtr, "\r\n");
402 if (!lineEnd) return;
403 char* CP = m_Response.data();
404 memset(CP, 0, m_Response.size());
405 char saved = lineEnd[0];
406 lineEnd[0] = '\0';
407 strcpy(CP, ClientPortPtr);
408 char* eq = strstr(CP, "=");
409 if (eq) {
410 ++eq;
411 strcpy(CP, eq);
412 char* dash = strstr(CP, "-");
413 if (dash) {
414 dash[0] = '\0';
415 m_ClientRTPPort = atoi(CP);
416 m_ClientRTCPPort = m_ClientRTPPort + 1;
417 }
418 }
419 lineEnd[0] = saved;
420 }
421
422 void parseTransportHeader(char* req) {
423 char* TransportPtr = strstr(req, "Transport:");
424 if (!TransportPtr) return;
425 char* lineEnd = strstr(TransportPtr, "\r\n");
426 if (!lineEnd) return;
427 char* CP = m_Response.data();
428 memset(CP, 0, m_Response.size());
429 char saved = lineEnd[0];
430 lineEnd[0] = '\0';
431 strncpy(CP, TransportPtr, m_Response.size() - 1);
432 CP[m_Response.size() - 1] = '\0';
433 if (strstr(CP, "RTP/AVP/TCP") || strstr(CP, "/TCP")) m_TransportIsTcp = true;
434 char* inter = strstr(CP, "interleaved=");
435 if (inter) {
436 inter += strlen("interleaved=");
437 int a = -1, b = -1;
438 if (sscanf(inter, "%d-%d", &a, &b) == 2) {
439 m_InterleavedRtp = a;
440 m_InterleavedRtcp = b;
441 } else if (sscanf(inter, "%d,%d", &a, &b) == 2) {
442 m_InterleavedRtp = a;
443 m_InterleavedRtcp = b;
444 } else if (sscanf(inter, "%d", &a) == 1) {
445 m_InterleavedRtp = a;
446 m_InterleavedRtcp = a + 1;
447 }
448 }
449 lineEnd[0] = saved;
450 }
451
452 bool parseCommandName(char* req, unsigned reqSize, unsigned& outIdx) {
453 bool ok = false;
454 unsigned i;
455 for (i = 0; i < m_CmdName.size() - 1 && i < reqSize; ++i) {
456 char c = req[i];
457 if (c == ' ' || c == '\t') {
458 ok = true;
459 break;
460 }
461 m_CmdName[i] = c;
462 }
463 m_CmdName[i] = '\0';
464 if (!ok) {
465 LOGE("failed to parse RTSP");
466 return false;
467 }
468 LOGI("RTSP received %s", m_CmdName.data());
469 outIdx = i;
470 return true;
471 }
472
473 void determineCommandType() {
474 if (strstr(m_CmdName.data(), "OPTIONS"))
475 m_RtspCmdType = RTSP_OPTIONS;
476 else if (strstr(m_CmdName.data(), "DESCRIBE"))
477 m_RtspCmdType = RTSP_DESCRIBE;
478 else if (strstr(m_CmdName.data(), "SETUP"))
479 m_RtspCmdType = RTSP_SETUP;
480 else if (strstr(m_CmdName.data(), "PLAY"))
481 m_RtspCmdType = RTSP_PLAY;
482 else if (strstr(m_CmdName.data(), "PAUSE"))
483 m_RtspCmdType = RTSP_PAUSE;
484 else if (strstr(m_CmdName.data(), "TEARDOWN"))
485 m_RtspCmdType = RTSP_TEARDOWN;
486 else
487 LOGE("Error: Unsupported Command received (%s)!", m_CmdName.data());
488 }
489
490 void parseUrlHostPortAndSuffix(char* req, unsigned reqSize, unsigned& i) {
491 unsigned j = i + 1;
492 while (j < reqSize && (req[j] == ' ' || req[j] == '\t')) ++j;
493 for (; (int)j < (int)(reqSize - 8); ++j) {
494 if ((req[j] == 'r' || req[j] == 'R') && (req[j + 1] == 't' || req[j + 1] == 'T') &&
495 (req[j + 2] == 's' || req[j + 2] == 'S') && (req[j + 3] == 'p' || req[j + 3] == 'P') &&
496 req[j + 4] == ':' && req[j + 5] == '/') {
497 j += 6;
498 if (req[j] == '/') {
499 ++j;
500 unsigned uidx = 0;
501 while (j < reqSize && req[j] != '/' && req[j] != ' ' && uidx < m_URLHostPort.size() - 1) {
502 m_URLHostPort[uidx++] = req[j++];
503 }
504 } else {
505 --j;
506 }
507 i = j;
508 break;
509 }
510 }
511 LOGD("m_URLHostPort: %s", m_URLHostPort.data());
512
513 bool ok = false;
514 for (unsigned k = i + 1; (int)k < (int)(reqSize - 5); ++k) {
515 if (req[k] == 'R' && req[k + 1] == 'T' && req[k + 2] == 'S' && req[k + 3] == 'P' && req[k + 4] == '/') {
516 while (--k >= i && req[k] == ' ') {}
517 unsigned k1 = k;
518 while (k1 > i && req[k1] != '=') --k1;
519 if (k - k1 + 1 <= m_URLSuffix.size()) {
520 unsigned n = 0, k2 = k1 + 1;
521 while (k2 <= k) m_URLSuffix[n++] = req[k2++];
522 m_URLSuffix[n] = '\0';
523 if (k1 - i <= m_URLPreSuffix.size()) ok = true;
524 n = 0;
525 k2 = i + 1;
526 while (k2 <= k1 - 1) m_URLPreSuffix[n++] = req[k2++];
527 m_URLPreSuffix[n] = '\0';
528 i = k + 7;
529 }
530 break;
531 }
532 }
533 LOGD("m_URLSuffix: %s", m_URLSuffix.data());
534 LOGD("m_URLPreSuffix: %s", m_URLPreSuffix.data());
535 LOGD("URL Suffix parse succeeded: %i", ok);
536 }
537
538 bool parseCSeq(char* req, unsigned reqSize, unsigned startIdx) {
539 bool ok = false;
540 for (unsigned j = startIdx; (int)j < (int)(reqSize - 5); ++j) {
541 if (req[j] == 'C' && req[j + 1] == 'S' && req[j + 2] == 'e' && req[j + 3] == 'q' && req[j + 4] == ':') {
542 j += 5;
543 while (j < reqSize && (req[j] == ' ' || req[j] == '\t')) ++j;
544 unsigned n;
545 for (n = 0; n < m_CSeq.size() - 1 && j < reqSize; ++n, ++j) {
546 char c = req[j];
547 if (c == '\r' || c == '\n') {
548 ok = true;
549 break;
550 }
551 m_CSeq[n] = c;
552 }
553 m_CSeq[n] = '\0';
554 break;
555 }
556 }
557 LOGD("Look for CSeq success: %i", ok);
558 return ok;
559 }
560
561 void parseContentLength(char* req, unsigned reqSize, unsigned startIdx) {
562 for (unsigned j = startIdx; (int)j < (int)(reqSize - 15); ++j) {
563 if (req[j] == 'C' && req[j + 1] == 'o' && req[j + 2] == 'n' && req[j + 3] == 't' &&
564 req[j + 4] == 'e' && req[j + 5] == 'n' && req[j + 6] == 't' && req[j + 7] == '-' &&
565 (req[j + 8] == 'L' || req[j + 8] == 'l') && req[j + 9] == 'e' && req[j + 10] == 'n' &&
566 req[j + 11] == 'g' && req[j + 12] == 't' && req[j + 13] == 'h' && req[j + 14] == ':') {
567 j += 15;
568 while (j < reqSize && (req[j] == ' ' || req[j] == '\t')) ++j;
569 unsigned num;
570 if (sscanf(&req[j], "%u", &num) == 1) m_ContentLength = num;
571 }
572 }
573 }
574
575 void detectClientHeaderPreference(char* req) {
576 char* ua = strstr(req, "User-Agent:");
577 bool want_rfc2250 = false;
578 if (ua) {
579 if (strcasestr(ua, "ffmpeg") || strcasestr(ua, "ffplay") || strcasestr(ua, "libavformat") ||
580 strcasestr(ua, "Lavf")) {
581 want_rfc2250 = true;
582 }
583 if (strcasestr(ua, "vlc")) want_rfc2250 = false;
584 }
585 char* qm = strchr(req, '?');
586 if (qm) {
587 if (strstr(qm, "mpa_hdr=1")) want_rfc2250 = true;
588 if (strstr(qm, "mpa_hdr=0")) want_rfc2250 = false;
589 }
590 if (m_Streamer && m_Streamer->getAudioSource()) {
591 RTSPFormat& fmt = m_Streamer->getAudioSource()->getFormat();
592 fmt.setUseRfc2250Header(want_rfc2250);
593 }
594 }
595
600 snprintf(m_Response.data(), m_Response.size(),
601 "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
602 "Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE\r\n\r\n",
603 m_CSeq.data());
604
605 sendSocket(m_RtspClient, m_Response.data(), strlen(m_Response.data()));
606 }
607
612 // check whether we know a stream with the URL which is requested
613 m_StreamID = -1; // invalid URL
614 // find Stream ID
615 if (strcmp(m_URLPreSuffix.data(), STD_URL_PRE_SUFFIX) == 0) {
616 char* end;
617 m_StreamID = strtol(m_URLSuffix.data(), &end, 10);
618 if (*end != '\0') m_StreamID = -1;
619 }
620
621 // simulate DESCRIBE server response
622 char* ColonPtr;
623 strncpy(m_Buf1.data(), m_URLHostPort.data(), 256);
624 ColonPtr = strstr(m_Buf1.data(), ":");
625 if (ColonPtr != nullptr) ColonPtr[0] = 0x00;
626
627 snprintf(
628 m_SDPBuf.data(), m_SDPBuf.size(),
629 "v=0\r\n" // SDP Version
630 "o=- %d 0 IN IP4 %s\r\n"
631 "%s"
632 "a=control:%s=0",
633 rand() & 0xFF, m_Buf1.data(),
634 m_Streamer->getAudioSource()->getFormat().format(m_Buf2.data(), 256),
635 STD_URL_PRE_SUFFIX);
636
637 snprintf(m_URLBuf.data(), m_URLBuf.size(), "rtsp://%s",
638 m_URLHostPort.data());
639
640 snprintf(m_Response.data(), m_Response.size(),
641 "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
642 "%s\r\n"
643 "Content-Base: %s/\r\n"
644 "Content-Type: application/sdp\r\n"
645 "Content-Length: %d\r\n\r\n"
646 "%s",
647 m_CSeq.data(), dateHeader(), m_URLBuf.data(),
648 (int)strlen(m_SDPBuf.data()), m_SDPBuf.data());
649
650 // LOGI("handleRtspDescribe: %s", (const char*)m_Response.data());
651 Serial.println("------------------------------");
652 Serial.println((const char*)m_Response.data());
653 Serial.println("------------------------------");
654 sendSocket(m_RtspClient, m_Response.data(), strlen(m_Response.data()));
655 }
656
661 // Build response depending on requested transport
662 if (m_TransportIsTcp) {
663 // Initialize TCP interleaved transport on same RTSP TCP socket
664 int ch0 = (m_InterleavedRtp >= 0) ? m_InterleavedRtp : 0;
665 int ch1 = (m_InterleavedRtcp >= 0) ? m_InterleavedRtcp : (ch0 + 1);
666 m_Streamer->initTcpInterleavedTransport(m_RtspClient, ch0, ch1);
667
668 // Reply with interleaved channels
669 snprintf(m_Buf1.data(), m_Buf1.size(),
670 "RTP/AVP/TCP;unicast;interleaved=%d-%d", ch0, ch1);
671 } else {
672 // init RTSP Session transport type (UDP) and ports for UDP transport
673 initTransport(m_ClientRTPPort, m_ClientRTCPPort);
674 // UDP Transport response with client/server ports and ssrc
675 snprintf(m_Buf1.data(), m_Buf1.size(),
676 "RTP/AVP;unicast;client_port=%i-%i;server_port=%i-%i;ssrc=%08X",
677 m_ClientRTPPort, m_ClientRTCPPort,
678 m_Streamer->getRtpServerPort(), m_Streamer->getRtcpServerPort(),
679 (unsigned)m_Streamer->currentSsrc());
680 }
681 snprintf(m_Response.data(), m_Response.size(),
682 "RTSP/1.0 200 OK\r\n"
683 "CSeq: %s\r\n"
684 "%s\r\n"
685 "Session: %i\r\n"
686 "Transport: %s\r\n"
687 "\r\n",
688 m_CSeq.data(), dateHeader(), m_RtspSessionID, m_Buf1.data());
689
690 Serial.println("------------------------------");
691 Serial.println((char*)m_Response.data());
692 Serial.println("------------------------------");
693
694 sendSocket(m_RtspClient, m_Response.data(), strlen(m_Response.data()));
695 }
696
701 // Build RTP-Info to help clients (e.g., VLC) synchronize
702 // URL base
703 snprintf(m_URLBuf.data(), m_URLBuf.size(), "rtsp://%s/%s=0",
704 m_URLHostPort.data(), STD_URL_PRE_SUFFIX);
705
706 // Current seq/timestamp from streamer (values used for next packet)
707 uint16_t seq = m_Streamer ? m_Streamer->currentSeq() : 0;
708 uint32_t rtptime = m_Streamer ? m_Streamer->currentRtpTimestamp() : 0;
709
710 // PLAY response with Range, Session and RTP-Info
711 snprintf(m_Response.data(), m_Response.size(),
712 "RTSP/1.0 200 OK\r\n"
713 "CSeq: %s\r\n"
714 "Range: npt=0.000-\r\n"
715 "Session: %i\r\n"
716 "RTP-Info: url=%s;seq=%u;rtptime=%u\r\n\r\n",
717 m_CSeq.data(), m_RtspSessionID, m_URLBuf.data(), (unsigned)seq,
718 (unsigned)rtptime);
719
720 Serial.println("------------------------------");
721 Serial.println((char*)m_Response.data());
722 Serial.println("------------------------------");
723
724 sendSocket(m_RtspClient, m_Response.data(), strlen(m_Response.data()));
725
726 m_Streamer->start();
727 }
728
734 if (m_streaming && m_Streamer) {
735 m_Streamer->stop();
736 }
737 snprintf(m_Response.data(), m_Response.size(),
738 "RTSP/1.0 200 OK\r\n"
739 "CSeq: %s\r\n"
740 "Session: %i\r\n\r\n",
741 m_CSeq.data(), m_RtspSessionID);
742 sendSocket(m_RtspClient, m_Response.data(), strlen(m_Response.data()));
743 }
744
749 m_Streamer->stop();
750
751 // simulate SETUP server response
752 snprintf(m_Response.data(), m_Response.size(),
753 "RTSP/1.0 200 OK\r\n"
754 "CSeq: %s\r\n\r\n",
755 m_CSeq.data());
756
757 sendSocket(m_RtspClient, m_Response.data(), strlen(m_Response.data()));
758
759 m_sessionOpen = false;
760 }
761
766 int getStreamID() { return m_StreamID; }
767
773 void initTransport(u_short aRtpPort, u_short aRtcpPort) {
774 m_RtpClientPort = aRtpPort;
775 m_RtcpClientPort = aRtcpPort;
776
777 IPAddress clientIP;
778 uint16_t clientPort;
779 Platform::getSocketPeerAddr(m_RtspClient, &clientIP, &clientPort);
780
781 m_Streamer->initUdpTransport(clientIP, m_RtpClientPort);
782 }
783
784 typename Platform::TcpClientType*& getClient() { return m_RtspClient; }
785
786 uint16_t getRtpClientPort() { return m_RtpClientPort; }
787
796 inline int readSocket(typename Platform::TcpClientType* sock, char* buf,
797 size_t buflen, int timeoutmsec) {
798 return Platform::readSocket(sock, buf, buflen, timeoutmsec);
799 }
800
808 inline ssize_t sendSocket(typename Platform::TcpClientType* sock,
809 const void* buf, size_t len) {
810 return Platform::sendSocket(sock, buf, len);
811 }
812
817 char const* dateHeader() {
818 static char buf[200];
819 time_t tt = time(NULL);
820 strftime(buf, sizeof(buf), "Date: %a, %b %d %Y %H:%M:%S GMT", gmtime(&tt));
821 return buf;
822 }
823};
824
825} // namespace audio_tools
RTSPAudioStreamerBase - Core RTP Audio Streaming Engine.
Definition RTSPAudioStreamer.h:50
RTSP Session Handler - Individual Client Protocol Management.
Definition RTSPSession.h:78
void init()
Definition RTSPSession.h:261
int readSocket(typename Platform::TcpClientType *sock, char *buf, size_t buflen, int timeoutmsec)
Inline helper to read from socket.
Definition RTSPSession.h:796
void initTransport(u_short aRtpPort, u_short aRtcpPort)
Definition RTSPSession.h:773
ssize_t sendSocket(typename Platform::TcpClientType *sock, const void *buf, size_t len)
Inline helper to send data over socket.
Definition RTSPSession.h:808
void handleRtspSetup()
Definition RTSPSession.h:660
void handleRtspDescribe()
Definition RTSPSession.h:611
bool parseRtspRequest(char const *aRequest, unsigned aRequestSize)
Definition RTSPSession.h:368
RtspSession(typename Platform::TcpClientType &aClient, RTSPAudioStreamerBase< Platform > &aStreamer)
Construct RTSP session for a connected client.
Definition RTSPSession.h:98
int getStreamID()
Definition RTSPSession.h:766
void handleRtspTeardown()
Definition RTSPSession.h:748
RTSP_CMD_TYPES handleRtspRequest(char const *aRequest, unsigned aRequestSize)
Definition RTSPSession.h:326
~RtspSession()
Destructor - cleanup session resources.
Definition RTSPSession.h:114
void handleRtspPlay()
Definition RTSPSession.h:700
void handleRtspOption()
Definition RTSPSession.h:599
bool handleRequests(uint32_t readTimeoutMs)
Process incoming RTSP requests from the client.
Definition RTSPSession.h:149
char const * dateHeader()
Definition RTSPSession.h:817
void handleRtspPause()
Definition RTSPSession.h:733
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
RTSP_CMD_TYPES
Supported RTSP command types.
Definition RTSPSession.h:38