ESP_IOT v2.5
IOT ESP Coding
CRtspSession.cpp
Go to the documentation of this file.
1#include "CRtspSession.h"
2#include "../../../Defines.h"
3
4#ifdef USE_CAMERA_MODULE
5
6#include <stdio.h>
7#include <time.h>
8
9CRtspSession::CRtspSession(SOCKET aRtspClient, CStreamer * aStreamer) : m_RtspClient(aRtspClient),m_Streamer(aStreamer)
10{
11 printf("Creating RTSP session\n");
12 Init();
13
14 m_RtspSessionID = getRandom(); // create a session ID
15 m_RtspSessionID |= 0x80000000;
16 m_StreamID = -1;
17 m_ClientRTPPort = 0;
18 m_ClientRTCPPort = 0;
19 m_TcpTransport = false;
20 m_streaming = false;
21 m_stopped = false;
22};
23
24CRtspSession::~CRtspSession()
25{
26 closesocket(m_RtspClient);
27};
28
29void CRtspSession::Init()
30{
31 m_RtspCmdType = RTSP_UNKNOWN;
32 memset(m_URLPreSuffix, 0x00, sizeof(m_URLPreSuffix));
33 memset(m_URLSuffix, 0x00, sizeof(m_URLSuffix));
34 memset(m_CSeq, 0x00, sizeof(m_CSeq));
35 memset(m_URLHostPort, 0x00, sizeof(m_URLHostPort));
36 m_ContentLength = 0;
37};
38
39bool CRtspSession::ParseRtspRequest(char const * aRequest, unsigned aRequestSize)
40{
41 char CmdName[RTSP_PARAM_STRING_MAX];
42 static char CurRequest[RTSP_BUFFER_SIZE]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
43 unsigned CurRequestSize;
44
45 Init();
46 CurRequestSize = aRequestSize;
47 memcpy(CurRequest,aRequest,aRequestSize);
48
49 // check whether the request contains information about the RTP/RTCP UDP client ports (SETUP command)
50 char * ClientPortPtr;
51 char * TmpPtr;
52 static char CP[1024];
53 char * pCP;
54
55 ClientPortPtr = strstr(CurRequest,"client_port");
56 if (ClientPortPtr != nullptr)
57 {
58 TmpPtr = strstr(ClientPortPtr,"\r\n");
59 if (TmpPtr != nullptr)
60 {
61 TmpPtr[0] = 0x00;
62 strcpy(CP,ClientPortPtr);
63 pCP = strstr(CP,"=");
64 if (pCP != nullptr)
65 {
66 pCP++;
67 strcpy(CP,pCP);
68 pCP = strstr(CP,"-");
69 if (pCP != nullptr)
70 {
71 pCP[0] = 0x00;
72 m_ClientRTPPort = atoi(CP);
73 m_ClientRTCPPort = m_ClientRTPPort + 1;
74 };
75 };
76 };
77 };
78
79 // Read everything up to the first space as the command name
80 bool parseSucceeded = false;
81 unsigned i;
82 for (i = 0; i < sizeof(CmdName)-1 && i < CurRequestSize; ++i)
83 {
84 char c = CurRequest[i];
85 if (c == ' ' || c == '\t')
86 {
87 parseSucceeded = true;
88 break;
89 }
90 CmdName[i] = c;
91 }
92 CmdName[i] = '\0';
93 if (!parseSucceeded) {
94 printf("failed to parse RTSP\n");
95 return false;
96 }
97
98 printf("RTSP received %s\n", CmdName);
99
100 // find out the command type
101 if (strstr(CmdName,"OPTIONS") != nullptr) m_RtspCmdType = RTSP_OPTIONS; else
102 if (strstr(CmdName,"DESCRIBE") != nullptr) m_RtspCmdType = RTSP_DESCRIBE; else
103 if (strstr(CmdName,"SETUP") != nullptr) m_RtspCmdType = RTSP_SETUP; else
104 if (strstr(CmdName,"PLAY") != nullptr) m_RtspCmdType = RTSP_PLAY; else
105 if (strstr(CmdName,"TEARDOWN") != nullptr) m_RtspCmdType = RTSP_TEARDOWN;
106
107 // check whether the request contains transport information (UDP or TCP)
108 if (m_RtspCmdType == RTSP_SETUP)
109 {
110 TmpPtr = strstr(CurRequest,"RTP/AVP/TCP");
111 if (TmpPtr != nullptr) m_TcpTransport = true; else m_TcpTransport = false;
112 };
113
114 // Skip over the prefix of any "rtsp://" or "rtsp:/" URL that follows:
115 unsigned j = i+1;
116 while (j < CurRequestSize && (CurRequest[j] == ' ' || CurRequest[j] == '\t')) ++j; // skip over any additional white space
117 for (; (int)j < (int)(CurRequestSize-8); ++j)
118 {
119 if ((CurRequest[j] == 'r' || CurRequest[j] == 'R') &&
120 (CurRequest[j+1] == 't' || CurRequest[j+1] == 'T') &&
121 (CurRequest[j+2] == 's' || CurRequest[j+2] == 'S') &&
122 (CurRequest[j+3] == 'p' || CurRequest[j+3] == 'P') &&
123 CurRequest[j+4] == ':' && CurRequest[j+5] == '/')
124 {
125 j += 6;
126 if (CurRequest[j] == '/')
127 { // This is a "rtsp://" URL; skip over the host:port part that follows:
128 ++j;
129 unsigned uidx = 0;
130 while (j < CurRequestSize && CurRequest[j] != '/' && CurRequest[j] != ' ' && uidx < sizeof(m_URLHostPort) - 1)
131 { // extract the host:port part of the URL here
132 m_URLHostPort[uidx] = CurRequest[j];
133 uidx++;
134 ++j;
135 };
136 }
137 else --j;
138 i = j;
139 break;
140 }
141 }
142
143 // Look for the URL suffix (before the following "RTSP/"):
144 parseSucceeded = false;
145 for (unsigned k = i+1; (int)k < (int)(CurRequestSize-5); ++k)
146 {
147 if (CurRequest[k] == 'R' && CurRequest[k+1] == 'T' &&
148 CurRequest[k+2] == 'S' && CurRequest[k+3] == 'P' &&
149 CurRequest[k+4] == '/')
150 {
151 while (--k >= i && CurRequest[k] == ' ') {}
152 unsigned k1 = k;
153 while (k1 > i && CurRequest[k1] != '/') --k1;
154 if (k - k1 + 1 > sizeof(m_URLSuffix)) return false;
155 unsigned n = 0, k2 = k1+1;
156
157 while (k2 <= k) m_URLSuffix[n++] = CurRequest[k2++];
158 m_URLSuffix[n] = '\0';
159
160 if (k1 - i > sizeof(m_URLPreSuffix)) return false;
161 n = 0; k2 = i + 1;
162 while (k2 <= k1 - 1) m_URLPreSuffix[n++] = CurRequest[k2++];
163 m_URLPreSuffix[n] = '\0';
164 i = k + 7;
165 parseSucceeded = true;
166 break;
167 }
168 }
169 if (!parseSucceeded) return false;
170
171 // Look for "CSeq:", skip whitespace, then read everything up to the next \r or \n as 'CSeq':
172 parseSucceeded = false;
173 for (j = i; (int)j < (int)(CurRequestSize-5); ++j)
174 {
175 if (CurRequest[j] == 'C' && CurRequest[j+1] == 'S' &&
176 CurRequest[j+2] == 'e' && CurRequest[j+3] == 'q' &&
177 CurRequest[j+4] == ':')
178 {
179 j += 5;
180 while (j < CurRequestSize && (CurRequest[j] == ' ' || CurRequest[j] == '\t')) ++j;
181 unsigned n;
182 for (n = 0; n < sizeof(m_CSeq)-1 && j < CurRequestSize; ++n,++j)
183 {
184 char c = CurRequest[j];
185 if (c == '\r' || c == '\n')
186 {
187 parseSucceeded = true;
188 break;
189 }
190 m_CSeq[n] = c;
191 }
192 m_CSeq[n] = '\0';
193 break;
194 }
195 }
196 if (!parseSucceeded) return false;
197
198 // Also: Look for "Content-Length:" (optional)
199 for (j = i; (int)j < (int)(CurRequestSize-15); ++j)
200 {
201 if (CurRequest[j] == 'C' && CurRequest[j+1] == 'o' &&
202 CurRequest[j+2] == 'n' && CurRequest[j+3] == 't' &&
203 CurRequest[j+4] == 'e' && CurRequest[j+5] == 'n' &&
204 CurRequest[j+6] == 't' && CurRequest[j+7] == '-' &&
205 (CurRequest[j+8] == 'L' || CurRequest[j+8] == 'l') &&
206 CurRequest[j+9] == 'e' && CurRequest[j+10] == 'n' &&
207 CurRequest[j+11] == 'g' && CurRequest[j+12] == 't' &&
208 CurRequest[j+13] == 'h' && CurRequest[j+14] == ':')
209 {
210 j += 15;
211 while (j < CurRequestSize && (CurRequest[j] == ' ' || CurRequest[j] == '\t')) ++j;
212 unsigned num;
213 if (sscanf(&CurRequest[j], "%u", &num) == 1) m_ContentLength = num;
214 }
215 }
216 return true;
217};
218
219RTSP_CMD_TYPES CRtspSession::Handle_RtspRequest(char const * aRequest, unsigned aRequestSize)
220{
221 if (ParseRtspRequest(aRequest,aRequestSize))
222 {
223 switch (m_RtspCmdType)
224 {
225 case RTSP_OPTIONS: { Handle_RtspOPTION(); break; };
226 case RTSP_DESCRIBE: { Handle_RtspDESCRIBE(); break; };
227 case RTSP_SETUP: { Handle_RtspSETUP(); break; };
228 case RTSP_PLAY: { Handle_RtspPLAY(); break; };
229 default: {};
230 };
231 };
232 return m_RtspCmdType;
233};
234
235void CRtspSession::Handle_RtspOPTION()
236{
237 static char Response[1024]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
238
239 snprintf(Response,sizeof(Response),
240 "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
241 "Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE\r\n\r\n",m_CSeq);
242
243 socketsend(m_RtspClient,Response,strlen(Response));
244}
245
246void CRtspSession::Handle_RtspDESCRIBE()
247{
248 static char Response[1024]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
249 static char SDPBuf[1024];
250 static char URLBuf[1024];
251
252 // check whether we know a stream with the URL which is requested
253 m_StreamID = -1; // invalid URL
254 if ((strcmp(m_URLPreSuffix,"mjpeg") == 0) && (strcmp(m_URLSuffix,"1") == 0)) m_StreamID = 0; else
255 if ((strcmp(m_URLPreSuffix,"mjpeg") == 0) && (strcmp(m_URLSuffix,"2") == 0)) m_StreamID = 1;
256 if (m_StreamID == -1)
257 { // Stream not available
258 snprintf(Response,sizeof(Response),
259 "RTSP/1.0 404 Stream Not Found\r\nCSeq: %s\r\n%s\r\n",
260 m_CSeq,
261 DateHeader());
262
263 socketsend(m_RtspClient,Response,strlen(Response));
264 return;
265 };
266
267 // simulate DESCRIBE server response
268 static char OBuf[256];
269 char * ColonPtr;
270 strcpy(OBuf,m_URLHostPort);
271 ColonPtr = strstr(OBuf,":");
272 if (ColonPtr != nullptr) ColonPtr[0] = 0x00;
273
274 snprintf(SDPBuf,sizeof(SDPBuf),
275 "v=0\r\n"
276 "o=- %d 1 IN IP4 %s\r\n"
277 "s=\r\n"
278 "t=0 0\r\n" // start / stop - 0 -> unbounded and permanent session
279 "m=video 0 RTP/AVP 26\r\n" // currently we just handle UDP sessions
280 // "a=x-dimensions: 640,480\r\n"
281 "c=IN IP4 0.0.0.0\r\n",
282 rand(),
283 OBuf);
284 char StreamName[64];
285 switch (m_StreamID)
286 {
287 case 0: strcpy(StreamName,"mjpeg/1"); break;
288 case 1: strcpy(StreamName,"mjpeg/2"); break;
289 };
290 snprintf(URLBuf,sizeof(URLBuf),
291 "rtsp://%s/%s",
292 m_URLHostPort,
293 StreamName);
294 snprintf(Response,sizeof(Response),
295 "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
296 "%s\r\n"
297 "Content-Base: %s/\r\n"
298 "Content-Type: application/sdp\r\n"
299 "Content-Length: %d\r\n\r\n"
300 "%s",
301 m_CSeq,
302 DateHeader(),
303 URLBuf,
304 (int) strlen(SDPBuf),
305 SDPBuf);
306
307 socketsend(m_RtspClient,Response,strlen(Response));
308}
309
310void CRtspSession::Handle_RtspSETUP()
311{
312 static char Response[1024];
313 static char Transport[255];
314
315 // init RTP streamer transport type (UDP or TCP) and ports for UDP transport
316 m_Streamer->InitTransport(m_ClientRTPPort,m_ClientRTCPPort,m_TcpTransport);
317
318 // simulate SETUP server response
319 if (m_TcpTransport)
320 snprintf(Transport,sizeof(Transport),"RTP/AVP/TCP;unicast;interleaved=0-1");
321 else
322 snprintf(Transport,sizeof(Transport),
323 "RTP/AVP;unicast;destination=127.0.0.1;source=127.0.0.1;client_port=%i-%i;server_port=%i-%i",
324 m_ClientRTPPort,
325 m_ClientRTCPPort,
326 m_Streamer->GetRtpServerPort(),
327 m_Streamer->GetRtcpServerPort());
328 snprintf(Response,sizeof(Response),
329 "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
330 "%s\r\n"
331 "Transport: %s\r\n"
332 "Session: %i\r\n\r\n",
333 m_CSeq,
334 DateHeader(),
335 Transport,
336 m_RtspSessionID);
337
338 socketsend(m_RtspClient,Response,strlen(Response));
339}
340
341void CRtspSession::Handle_RtspPLAY()
342{
343 static char Response[1024];
344
345 // simulate SETUP server response
346 snprintf(Response,sizeof(Response),
347 "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
348 "%s\r\n"
349 "Range: npt=0.000-\r\n"
350 "Session: %i\r\n"
351 "RTP-Info: url=rtsp://127.0.0.1:8554/mjpeg/1/track1\r\n\r\n",
352 m_CSeq,
353 DateHeader(),
354 m_RtspSessionID);
355
356 socketsend(m_RtspClient,Response,strlen(Response));
357}
358
359char const * CRtspSession::DateHeader()
360{
361 static char buf[200];
362 time_t tt = time(NULL);
363 strftime(buf, sizeof buf, "Date: %a, %b %d %Y %H:%M:%S GMT", gmtime(&tt));
364 return buf;
365}
366
367int CRtspSession::GetStreamID()
368{
369 return m_StreamID;
370};
371
372
373
374/**
375 Read from our socket, parsing commands as possible.
376 */
377bool CRtspSession::handleRequests(uint32_t readTimeoutMs)
378{
379 if(m_stopped)
380 return false; // Already closed down
381
382 static char RecvBuf[RTSP_BUFFER_SIZE]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
383
384 memset(RecvBuf,0x00,sizeof(RecvBuf));
385 int res = socketread(m_RtspClient,RecvBuf,sizeof(RecvBuf), readTimeoutMs);
386 if(res > 0) {
387 // we filter away everything which seems not to be an RTSP command: O-ption, D-escribe, S-etup, P-lay, T-eardown
388 if ((RecvBuf[0] == 'O') || (RecvBuf[0] == 'D') || (RecvBuf[0] == 'S') || (RecvBuf[0] == 'P') || (RecvBuf[0] == 'T'))
389 {
390 RTSP_CMD_TYPES C = Handle_RtspRequest(RecvBuf,res);
391 if (C == RTSP_PLAY)
392 m_streaming = true;
393 else if (C == RTSP_TEARDOWN)
394 m_stopped = true;
395 }
396 return true;
397 }
398 else if(res == 0) {
399 printf("client closed socket, exiting\n");
400 m_stopped = true;
401 return true;
402 }
403 else {
404 // Timeout on read
405
406 return false;
407 }
408}
409
410void CRtspSession::broadcastCurrentFrame(uint32_t curMsec) {
411 // Send a frame
412 if (m_streaming && !m_stopped) {
413 // printf("serving a frame\n");
414 m_Streamer->streamImage(curMsec);
415 }
416}
417
418#endif