arduino-audio-tools
Loading...
Searching...
No Matches
RTSPServer.h
1/*
2 * Author: Phil Schatzmann
3 *
4 * Based on Micro-RTSP library:
5 * https://github.com/geeksville/Micro-RTSP
6 * https://github.com/Tomp0801/Micro-RTSP-Audio
7 *
8 */
9
10#pragma once
11#include "RTSPSession.h"
12#ifdef ESP32
13#include <WiFi.h>
14#include <esp_wifi.h>
15#include "AudioTools/Concurrency/RTOS.h"
16#endif
17
18namespace audio_tools {
19
46template <typename Platform>
48 public:
50
70 RTSPServer(streamer_t& streamer, int port = 8554, int core = 1) {
71 this->streamer = &streamer;
72 this->port = port;
73 this->core = core;
74 // Create tasks with specified core
75 serverTask.create("RTSPServerThread", 10000, 5, core);
76 sessionTask.create("RTSPSessionTask", 8000, 8, core);
77 }
78
83
94 void setOnSessionPath(bool (*cb)(const char* path, void* ref), void* ref = nullptr) {
95 onSessionPathCb = cb;
96 onSessionPathRef = ref;
97 }
98
99#if defined(USE_RTSP_LOGIN)
100
116 bool begin(const char* ssid, const char* password) {
117 // Start Wifi
118 WiFi.begin(ssid, password);
119 while (WiFi.status() != WL_CONNECTED) {
120 delay(500);
121 Serial.print(".");
122 }
123 WiFi.setSleep(WIFI_PS_NONE);
124
125 Serial.println();
126 Serial.print("connect to rtsp://");
127 Serial.print(WiFi.localIP());
128 Serial.print(":");
129 Serial.println(port);
130 Serial.println();
131 return begin();
132 }
133
134#endif
135
137 bool begin() {
138 bool ok = runAsync();
139 if (!ok) {
140 LOGE("Couldn't start RTSP server");
141 }
142 return ok;
143 }
144
145
150 audio_tools::Task& getTaskHandle() { return serverTask; };
151
156 int clientCount() { return client_count;}
157
161 operator bool() { return client_count > 0;}
162
167 void setSessionTimoutMs(unsigned long ms){ sessionTimeoutMs = ms;}
168
169 protected:
170 int port; // port that the RTSP Server listens on
171 int core; // the core number the RTSP runs on (platform-specific)
172 audio_tools::Task serverTask{"RTSPServerThread", 15000, 5, core};
173 audio_tools::Task sessionTask{"RTSPSessionTask", 15000, 8, core};
174 typename Platform::TcpServerType* server = nullptr; // platform server
175 typename Platform::TcpClientType client; // RTSP client connection (value)
176 int client_count = 0; // number of connected clients
177 streamer_t* streamer = nullptr; // RTSPAudioStreamer object that acts as a
178 // source for data streams
179 bool (*onSessionPathCb)(const char*, void*) = nullptr; // session path callback
180 void* onSessionPathRef = nullptr;
181 unsigned long sessionTimeoutMs = 20000; // 20 seconds
182
197 bool runAsync() {
198 LOGI("Running RTSP server on port %d", port);
199 streamer->initAudioSource();
200 if (server == nullptr) {
201 server = Platform::createServer(port);
202 LOGI("RTSP server started on port %d", port);
203 }
204 if (!serverTask.begin([this]() { serverThreadLoop(); })) {
205 LOGE("Couldn't start server thread");
206 return false;
207 }
208 return true;
209 }
210
223 unsigned long lastCheck = millis();
224
225 LOGD("Server thread listening... (numClients: %d)", client_count);
226
227 // only allow one client at a time
228 if (client_count == 0) {
229 auto newClient = Platform::getAvailableClient(server);
230 if (newClient.connected()) {
231 client = newClient; // copy/move assign
232 LOGI("Client connected");
233 if (!sessionTask.begin([this]() { sessionThreadLoop(); })) {
234 LOGE("Couldn't start sessionThread");
235 Platform::closeSocket(&client);
236 } else {
237 client_count++;
238 LOGI("Number of clients: %d", client_count);
239 }
240 }
241 } else {
242 LOGD("Waiting for current session to end (numClients: %d)", client_count);
243 }
244 int time = 200 - (millis() - lastCheck);
245 if (time < 0) time = 0;
246 delay(time);
247 }
248
252 void stop() {
253 LOGI("Stopping RTSP server");
254
255 // Stop tasks
256 sessionTask.end();
257 serverTask.end();
258
259 // Close sockets
260 if (server) {
261 // server->stop(); // not supported by EthernetServer
262 delete server;
263 server = nullptr;
264 }
265
266 client_count = 0;
267 LOGI("RTSP server stopped");
268 }
269
284 typename Platform::TcpClientType* s = &client;
285 unsigned long lastCheck = millis();
286
287 // TODO check if everything is ok to go
288 LOGD("RTSP Task running");
289
290 // our threads RTSP session and state
291 RtspSession<Platform>* rtsp = new RtspSession<Platform>(*s, *streamer);
292 if (onSessionPathCb) {
293 rtsp->setOnSessionPath(onSessionPathCb, onSessionPathRef);
294 }
295 assert(rtsp != nullptr);
296
297 LOGI("Session ready");
298
299
300 // Session timeout logic
301 unsigned long lastRequestTime = millis();
302
303 while (rtsp->isSessionOpen()) {
304 uint32_t timeout = 30;
305 bool gotRequest = rtsp->handleRequests(timeout);
306 if (gotRequest) {
307 lastRequestTime = millis();
308 LOGD("Request handling successful");
309 } else {
310 LOGI("Request handling timed out or no data yet");
311 }
312
313 // If streaming, check for session timeout
314 if (rtsp->isStreaming()) {
315 if ((millis() - lastRequestTime) > sessionTimeoutMs) {
316 LOGW("Session timeout: no client request received for 20 seconds, closing session");
317 break;
318 }
319 }
320
321 int time = timeout - (millis() - lastCheck);
322 if (time < 0) time = 0;
323 delay(time);
324 }
325
326 LOGI("Session loop exited - session no longer open");
327
328 // cleanup when session ends
329 LOGI("sessionThread stopped, cleaning up");
330 delete rtsp;
331
332 // Clean up client socket
333 if (client.connected()) {
334 Platform::closeSocket(&client);
335 }
336
337 // Add delay to ensure complete cleanup before accepting new connections
338 delay(500);
339
340 client_count--;
341 LOGI("Session cleaned up: (numClients: %d)", client_count);
342
343 // end the task - this should be the last thing we do
344 sessionTask.end();
345 }
346};
347
348} // namespace audio_tools
RTSPAudioStreamerBase - Core RTP Audio Streaming Engine.
Definition RTSPAudioStreamer.h:50
RTSP Server - Multi-client Audio Streaming Server.
Definition RTSPServer.h:47
int clientCount()
Get the number of connected clients.
Definition RTSPServer.h:156
bool runAsync()
Start RTSP server asynchronously.
Definition RTSPServer.h:197
~RTSPServer()
Destructor - ensures proper cleanup of server resources.
Definition RTSPServer.h:82
bool begin()
Definition RTSPServer.h:137
void setSessionTimoutMs(unsigned long ms)
Set the session timeout in milliseconds.
Definition RTSPServer.h:167
void serverThreadLoop()
Main server thread loop - listens for RTSP client connections.
Definition RTSPServer.h:222
void stop()
Stop the RTSP server and cleanup resources.
Definition RTSPServer.h:252
void sessionThreadLoop()
Client session thread loop - handles RTSP protocol for individual clients.
Definition RTSPServer.h:283
void setOnSessionPath(bool(*cb)(const char *path, void *ref), void *ref=nullptr)
Set a callback to receive the RTSP URL path for each new session. The callback is forwarded to every ...
Definition RTSPServer.h:94
audio_tools::Task & getTaskHandle()
Get the server task handle.
Definition RTSPServer.h:150
RTSPServer(streamer_t &streamer, int port=8554, int core=1)
Construct RTSP server.
Definition RTSPServer.h:70
RTSP Session Handler - Individual Client Protocol Management.
Definition RTSPSession.h:77
bool handleRequests(uint32_t readTimeoutMs)
Process incoming RTSP requests from the client.
Definition RTSPSession.h:148
void setOnSessionPath(bool(*cb)(const char *path, void *ref), void *ref=nullptr)
Set a callback to receive the RTSP URL path that opened the session. The callback is invoked once,...
Definition RTSPSession.h:225
FreeRTOS task.
Definition Task.h:21
bool create(const char *name, int stackSize, int priority=1, int core=-1)
If you used the empty constructor, you need to call create!
Definition Task.h:32
void end()
suspends the task
Definition Task.h:65
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
uint32_t millis()
Returns the milliseconds since the start.
Definition Time.h:12