1 /*
2 * GPAC - Multimedia Framework C SDK
3 *
4 * Authors: Jean Le Feuvre
5 * Copyright (c) Telecom ParisTech 2000-2012
6 * All rights reserved
7 *
8 * This file is part of GPAC / IETF RTP/RTSP/SDP sub-project
9 *
10 * GPAC is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
13 * any later version.
14 *
15 * GPAC is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; see the file COPYING. If not, write to
22 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25
26
27 #include <gpac/internal/ietf_dev.h>
28
29 #ifndef GPAC_DISABLE_STREAMING
30
31 #include <gpac/token.h>
32
33 GF_EXPORT
gf_rtsp_command_new()34 GF_RTSPCommand *gf_rtsp_command_new()
35 {
36 GF_RTSPCommand *tmp;
37 GF_SAFEALLOC(tmp, GF_RTSPCommand);
38 if (!tmp) return NULL;
39 tmp->Xtensions = gf_list_new();
40 tmp->Transports = gf_list_new();
41 return tmp;
42 }
43
44
45 #define COM_FREE_CLEAN(hdr) if (com->hdr) gf_free(com->hdr); \
46 com->hdr = NULL;
47
48 GF_EXPORT
gf_rtsp_command_reset(GF_RTSPCommand * com)49 void gf_rtsp_command_reset(GF_RTSPCommand *com)
50 {
51 if (!com) return;
52
53 //free all headers
54 COM_FREE_CLEAN(Accept);
55 COM_FREE_CLEAN(Accept_Encoding);
56 COM_FREE_CLEAN(Accept_Language);
57 COM_FREE_CLEAN(Authorization);
58 COM_FREE_CLEAN(Cache_Control);
59 COM_FREE_CLEAN(Conference);
60 COM_FREE_CLEAN(Connection);
61 COM_FREE_CLEAN(From);
62 COM_FREE_CLEAN(Proxy_Authorization);
63 COM_FREE_CLEAN(Proxy_Require);
64 COM_FREE_CLEAN(Referer);
65 COM_FREE_CLEAN(Session);
66 COM_FREE_CLEAN(User_Agent);
67 COM_FREE_CLEAN(body);
68 COM_FREE_CLEAN(service_name);
69 COM_FREE_CLEAN(ControlString);
70 COM_FREE_CLEAN(method);
71
72 //this is for server only, set to OK by default
73 com->StatusCode = NC_RTSP_OK;
74
75
76 com->user_data = NULL;
77
78 com->Bandwidth = com->Blocksize = com->Content_Length = com->CSeq = 0;
79 com->Scale = com->Speed = 0.0;
80 if (com->Range) gf_free(com->Range);
81 com->Range = NULL;
82
83 while (gf_list_count(com->Transports)) {
84 GF_RTSPTransport *trans = (GF_RTSPTransport *) gf_list_get(com->Transports, 0);
85 gf_list_rem(com->Transports, 0);
86 gf_rtsp_transport_del(trans);
87 }
88 while (gf_list_count(com->Xtensions)) {
89 GF_X_Attribute *att = (GF_X_Attribute*)gf_list_get(com->Xtensions, 0);
90 gf_list_rem(com->Xtensions, 0);
91 gf_free(att->Name);
92 gf_free(att->Value);
93 gf_free(att);
94 }
95 }
96
97 GF_EXPORT
gf_rtsp_command_del(GF_RTSPCommand * com)98 void gf_rtsp_command_del(GF_RTSPCommand *com)
99 {
100 if (!com) return;
101 gf_rtsp_command_reset(com);
102 gf_list_del(com->Xtensions);
103 gf_list_del(com->Transports);
104 gf_free(com);
105 }
106
107
RTSP_WriteCommand(GF_RTSPSession * sess,GF_RTSPCommand * com,unsigned char * req_buffer,unsigned char ** out_buffer,u32 * out_size)108 GF_Err RTSP_WriteCommand(GF_RTSPSession *sess, GF_RTSPCommand *com, unsigned char *req_buffer,
109 unsigned char **out_buffer, u32 *out_size)
110 {
111 u32 i, cur_pos, size, count;
112 char *buffer;
113
114 *out_buffer = NULL;
115
116 size = RTSP_WRITE_STEPALLOC;
117 buffer = (char *) gf_malloc(size);
118 cur_pos = 0;
119
120 //request
121 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, req_buffer);
122
123 //then all headers
124 RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept", com->Accept);
125 RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept-Encoding", com->Accept_Encoding);
126 RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept-Language", com->Accept_Language);
127 RTSP_WRITE_HEADER(buffer, size, cur_pos, "Authorization", com->Authorization);
128 if (com->Bandwidth) {
129 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Bandwidth: ");
130 RTSP_WRITE_INT(buffer, size, cur_pos, com->Bandwidth, 0);
131 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
132 }
133 if (com->Blocksize) {
134 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Blocksize: ");
135 RTSP_WRITE_INT(buffer, size, cur_pos, com->Blocksize, 0);
136 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
137 }
138 RTSP_WRITE_HEADER(buffer, size, cur_pos, "Cache-Control", com->Cache_Control);
139 RTSP_WRITE_HEADER(buffer, size, cur_pos, "Conference", com->Conference);
140 RTSP_WRITE_HEADER(buffer, size, cur_pos, "Connection", com->Connection);
141 //if we have a body write the content length
142 if (com->body) {
143 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Content-Length: ");
144 RTSP_WRITE_INT(buffer, size, cur_pos, (u32) strlen(com->body), 0);
145 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
146 }
147 //write the CSeq - use the SESSION CSeq
148 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "CSeq: ");
149 RTSP_WRITE_INT(buffer, size, cur_pos, sess->CSeq, 0);
150 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
151
152 RTSP_WRITE_HEADER(buffer, size, cur_pos, "From", com->From);
153 RTSP_WRITE_HEADER(buffer, size, cur_pos, "Proxy-Authorization", com->Proxy_Authorization);
154 RTSP_WRITE_HEADER(buffer, size, cur_pos, "Proxy-Require", com->Proxy_Require);
155
156 //Range, only NPT
157 if (com->Range && !com->Range->UseSMPTE) {
158 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Range: npt=");
159 RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, com->Range->start);
160 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
161 if (com->Range->end > com->Range->start) {
162 RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, com->Range->end);
163 }
164 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
165 }
166
167 RTSP_WRITE_HEADER(buffer, size, cur_pos, "Referer", com->Referer);
168 if (com->Scale != 0.0) {
169 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Scale: ");
170 RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, com->Scale);
171 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
172 }
173 RTSP_WRITE_HEADER(buffer, size, cur_pos, "Session", com->Session);
174 if (com->Speed != 0.0) {
175 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Speed: ");
176 RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, com->Speed);
177 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
178 }
179
180 //transport info
181 count = gf_list_count(com->Transports);
182 if (count) {
183 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Transport: ");
184 for (i=0; i<count; i++) {
185 GF_RTSPTransport *trans;
186 //line separator for headers
187 if (i) RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n ,");
188 trans = (GF_RTSPTransport *) gf_list_get(com->Transports, i);
189
190 //then write the structure
191 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->Profile);
192 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, (trans->IsUnicast ? ";unicast" : ";multicast"));
193 if (trans->destination) {
194 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";destination=");
195 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->destination);
196 }
197 if (trans->source) {
198 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";source=");
199 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->source);
200 }
201 if (trans->IsRecord) {
202 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";mode=RECORD");
203 if (trans->Append) RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";append");
204 }
205 if (trans->IsInterleaved) {
206 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";interleaved=");
207 RTSP_WRITE_INT(buffer, size, cur_pos, trans->rtpID, 0);
208 if (trans->rtcpID != trans->rtpID) {
209 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
210 RTSP_WRITE_INT(buffer, size, cur_pos, trans->rtcpID, 0);
211 }
212 }
213 if (trans->port_first) {
214 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, (trans->IsUnicast ? ";server_port=" : ";port="));
215 RTSP_WRITE_INT(buffer, size, cur_pos, trans->port_first, 0);
216 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
217 RTSP_WRITE_INT(buffer, size, cur_pos, trans->port_last, 0);
218 }
219 if (/*trans->IsUnicast && */trans->client_port_first) {
220 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";client_port=");
221 RTSP_WRITE_INT(buffer, size, cur_pos, trans->client_port_first, 0);
222 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
223 RTSP_WRITE_INT(buffer, size, cur_pos, trans->client_port_last, 0);
224 }
225 //multicast specific
226 if (!trans->IsUnicast) {
227 if (trans->MulticastLayers) {
228 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";layers=");
229 RTSP_WRITE_INT(buffer, size, cur_pos, trans->MulticastLayers, 0);
230 }
231 if (trans->TTL) {
232 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";ttl=");
233 RTSP_WRITE_INT(buffer, size, cur_pos, trans->TTL, 0);
234 }
235 }
236 if (trans->SSRC) {
237 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";ssrc=");
238 RTSP_WRITE_HEX(buffer, size, cur_pos, trans->SSRC, 0);
239 }
240 }
241 //done with transport
242 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
243 }
244 RTSP_WRITE_HEADER(buffer, size, cur_pos, "User-Agent", com->User_Agent);
245
246 //eXtensions
247 count = gf_list_count(com->Xtensions);
248 for (i=0; i<count; i++) {
249 GF_X_Attribute *att = (GF_X_Attribute *) gf_list_get(com->Xtensions, i);
250 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "x-");
251 RTSP_WRITE_HEADER(buffer, size, cur_pos, att->Name, att->Value);
252 }
253
254 //the end of header
255 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
256 //then body
257 RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, com->body);
258 //the end of message ? to check, should not be needed...
259 // RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
260
261 *out_buffer = (unsigned char *)buffer;
262 *out_size = (u32) strlen(buffer);
263 return GF_OK;
264 }
265
266
267 //format a DESCRIBE, SETUP, PLAY or PAUSE on a session
268 //YOUR COMMAND MUST BE FORMATTED ACCORDINGLY
269 //sCtrl contains a control string if needed, formating the REQUEST as server_url/service_name/sCtrl
270 GF_EXPORT
gf_rtsp_send_command(GF_RTSPSession * sess,GF_RTSPCommand * com)271 GF_Err gf_rtsp_send_command(GF_RTSPSession *sess, GF_RTSPCommand *com)
272 {
273 GF_Err e;
274 char *sCtrl;
275 const char *rad;
276 u32 size;
277 char buffer[1024], *result, *body;
278
279 if (!com || !com->method) return GF_BAD_PARAM;
280
281 sCtrl = com->ControlString;
282
283 //NB: OPTIONS is not sent this way
284 if (strcmp(com->method, GF_RTSP_DESCRIBE)
285 && strcmp(com->method, GF_RTSP_ANNOUNCE)
286 && strcmp(com->method, GF_RTSP_GET_PARAMETER)
287 && strcmp(com->method, GF_RTSP_SET_PARAMETER)
288 && strcmp(com->method, GF_RTSP_SETUP)
289 && strcmp(com->method, GF_RTSP_PLAY)
290 && strcmp(com->method, GF_RTSP_PAUSE)
291 && strcmp(com->method, GF_RTSP_RECORD)
292 && strcmp(com->method, GF_RTSP_REDIRECT)
293 && strcmp(com->method, GF_RTSP_TEARDOWN)
294 && strcmp(com->method, GF_RTSP_OPTIONS)
295
296 ) return GF_BAD_PARAM;
297
298 //check the state machine
299 if (strcmp(com->method, GF_RTSP_PLAY)
300 && strcmp(com->method, GF_RTSP_PAUSE)
301 && strcmp(com->method, GF_RTSP_RECORD)
302 && sess->RTSP_State != GF_RTSP_STATE_INIT)
303 return GF_SERVICE_ERROR;
304
305 //aggregation is ONLY for the same request - unclear in RFC2326 ...
306 //it is often mentioned "queued requests" at the server, like 3 PLAYS
307 //and a PAUSE ....
308
309 /*
310 else if (sess->RTSP_State == GF_RTSP_STATE_WAIT_FOR_CONTROL
311 && strcmp(com->method, sess->RTSPLastRequest))
312 && strcmp(com->method, GF_RTSP_OPTIONS))
313
314 return GF_BAD_PARAM;
315 */
316
317 //OPTIONS must have a parameter string
318 if (!strcmp(com->method, GF_RTSP_OPTIONS) && !sCtrl) return GF_BAD_PARAM;
319
320
321 //update sequence number
322 sess->CSeq += 1;
323 sess->NbPending += 1;
324
325 if (!strcmp(com->method, GF_RTSP_OPTIONS)) {
326 sprintf(buffer, "OPTIONS %s %s\r\n", sCtrl, GF_RTSP_VERSION);
327 } else {
328 rad = (sess->ConnectionType == GF_SOCK_TYPE_TCP) ? "rtsp" : "rtspu";
329 if (sCtrl) {
330 //if both server and service names are included in the control, just
331 //use the control
332 if (strstr(sCtrl, sess->Server) && strstr(sCtrl, sess->Service)) {
333 sprintf(buffer, "%s %s %s\r\n", com->method, sCtrl, GF_RTSP_VERSION);
334 }
335 //if service is specified in ctrl, do not rewrite it
336 else if (strstr(sCtrl, sess->Service)) {
337 sprintf(buffer, "%s %s://%s:%d/%s %s\r\n", com->method, rad, sess->Server, sess->Port, sCtrl, GF_RTSP_VERSION);
338 }
339 else if (!strnicmp(sCtrl, "rtsp", 4)) {
340 sprintf(buffer, "%s %s %s\r\n", com->method, sCtrl, GF_RTSP_VERSION);
341 }
342 //otherwise rewrite full URL
343 else {
344 sprintf(buffer, "%s %s://%s/%s/%s %s\r\n", com->method, rad, sess->Server, sess->Service, sCtrl, GF_RTSP_VERSION);
345 // sprintf(buffer, "%s %s://%s:%d/%s/%s %s\r\n", com->method, rad, sess->Server, sess->Port, sess->Service, sCtrl, GF_RTSP_VERSION);
346 }
347 } else {
348 sprintf(buffer, "%s %s://%s:%d/%s %s\r\n", com->method, rad, sess->Server, sess->Port, sess->Service, GF_RTSP_VERSION);
349 }
350 }
351
352 //Body on ANNOUNCE, GET_PARAMETER, SET_PARAMETER ONLY
353 body = NULL;
354 if (strcmp(com->method, GF_RTSP_ANNOUNCE)
355 && strcmp(com->method, GF_RTSP_GET_PARAMETER)
356 && strcmp(com->method, GF_RTSP_SET_PARAMETER)
357 ) {
358 //this is an error, but don't say anything
359 if (com->body) {
360 body = com->body;
361 com->body = NULL;
362 }
363 }
364
365 result = NULL;
366 e = RTSP_WriteCommand(sess, com, (unsigned char *)buffer, (unsigned char **) &result, &size);
367 //restore body if needed
368 if (body) com->body = body;
369 if (e) goto exit;
370
371
372 GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSP] Sending Command:\n%s\n", result));
373
374 //send buffer
375 e = gf_rtsp_send_data(sess, result, size);
376 if (e) goto exit;
377
378
379 //update our state
380 if (!strcmp(com->method, GF_RTSP_RECORD)) sess->RTSP_State = GF_RTSP_STATE_WAIT_FOR_CONTROL;
381 else if (!strcmp(com->method, GF_RTSP_PLAY)) sess->RTSP_State = GF_RTSP_STATE_WAIT_FOR_CONTROL;
382 else if (!strcmp(com->method, GF_RTSP_PAUSE)) sess->RTSP_State = GF_RTSP_STATE_WAIT_FOR_CONTROL;
383 else sess->RTSP_State = GF_RTSP_STATE_WAITING;
384 //teardown invalidates the session most of the time, so we force the user to wait for the reply
385 //as the reply may indicate a connection-closed
386 strcpy(sess->RTSPLastRequest, com->method);
387
388 exit:
389 if (result) gf_free(result);
390 return e;
391 }
392
393
gf_rtsp_set_command_value(GF_RTSPCommand * com,char * Header,char * Value)394 void gf_rtsp_set_command_value(GF_RTSPCommand *com, char *Header, char *Value)
395 {
396 char LineBuffer[400];
397 s32 LinePos;
398 GF_RTSPTransport *trans;
399 GF_X_Attribute *x_Att;
400
401 if (!stricmp(Header, "Accept")) com->Accept = gf_strdup(Value);
402 else if (!stricmp(Header, "Accept-Encoding")) com->Accept_Encoding = gf_strdup(Value);
403 else if (!stricmp(Header, "Accept-Language")) com->Accept_Language = gf_strdup(Value);
404 else if (!stricmp(Header, "Authorization")) com->Authorization = gf_strdup(Value);
405 else if (!stricmp(Header, "Bandwidth")) sscanf(Value, "%u", &com->Bandwidth);
406 else if (!stricmp(Header, "Blocksize")) sscanf(Value, "%u", &com->Blocksize);
407 else if (!stricmp(Header, "Cache-Control")) com->Cache_Control = gf_strdup(Value);
408 else if (!stricmp(Header, "Conference")) com->Conference = gf_strdup(Value);
409 else if (!stricmp(Header, "Connection")) com->Connection = gf_strdup(Value);
410 else if (!stricmp(Header, "Content-Length")) sscanf(Value, "%u", &com->Content_Length);
411 else if (!stricmp(Header, "CSeq")) sscanf(Value, "%u", &com->CSeq);
412 else if (!stricmp(Header, "From")) com->From = gf_strdup(Value);
413 else if (!stricmp(Header, "Proxy_Authorization")) com->Proxy_Authorization = gf_strdup(Value);
414 else if (!stricmp(Header, "Proxy_Require")) com->Proxy_Require = gf_strdup(Value);
415 else if (!stricmp(Header, "Range")) com->Range = gf_rtsp_range_parse(Value);
416 else if (!stricmp(Header, "Referer")) com->Referer = gf_strdup(Value);
417 else if (!stricmp(Header, "Scale")) sscanf(Value, "%lf", &com->Scale);
418 else if (!stricmp(Header, "Session"))
419 com->Session = gf_strdup(Value);
420 else if (!stricmp(Header, "Speed")) sscanf(Value, "%lf", &com->Speed);
421 else if (!stricmp(Header, "User_Agent")) com->User_Agent = gf_strdup(Value);
422 //Transports
423 else if (!stricmp(Header, "Transport")) {
424 LinePos = 0;
425 while (1) {
426 LinePos = gf_token_get(Value, LinePos, "\r\n", LineBuffer, 400);
427 if (LinePos <= 0) return;
428 trans = gf_rtsp_transport_parse(Value);
429 if (trans) gf_list_add(com->Transports, trans);
430 }
431 }
432 //eXtensions attributes
433 else if (!strnicmp(Header, "x-", 2)) {
434 x_Att = (GF_X_Attribute*)gf_malloc(sizeof(GF_X_Attribute));
435 x_Att->Name = gf_strdup(Header+2);
436 x_Att->Value = NULL;
437 if (Value && strlen(Value)) x_Att->Value = gf_strdup(Value);
438 gf_list_add(com->Xtensions, x_Att);
439 }
440 //the rest is ignored
441 }
442
RTSP_ParseCommandHeader(GF_RTSPSession * sess,GF_RTSPCommand * com,u32 BodyStart)443 GF_Err RTSP_ParseCommandHeader(GF_RTSPSession *sess, GF_RTSPCommand *com, u32 BodyStart)
444 {
445 char LineBuffer[1024];
446 char ValBuf[1024];
447 char *buffer;
448 s32 Pos, ret;
449 u32 Size;
450
451 Size = sess->CurrentSize - sess->CurrentPos;
452 buffer = sess->tcp_buffer + sess->CurrentPos;
453
454 //by default the command is wrong ;)
455 com->StatusCode = NC_RTSP_Bad_Request;
456
457 //parse first line
458 ret = gf_token_get_line(buffer, 0, Size, LineBuffer, 1024);
459 if (ret < 0) return GF_REMOTE_SERVICE_ERROR;
460
461 //method
462 Pos = gf_token_get(LineBuffer, 0, " \t\r\n", ValBuf, 1024);
463 if (Pos <= 0) return GF_OK;
464 com->method = gf_strdup((const char *) ValBuf);
465
466 //URL
467 Pos = gf_token_get(LineBuffer, Pos, " \t\r\n", ValBuf, 1024);
468 if (Pos <= 0) return GF_OK;
469 com->service_name = gf_strdup(ValBuf);
470
471 //RTSP version
472 Pos = gf_token_get(LineBuffer, Pos, " \t\r\n", ValBuf, 1024);
473 if (Pos <= 0) return GF_OK;
474 if (strcmp(ValBuf, GF_RTSP_VERSION)) {
475 com->StatusCode = NC_RTSP_RTSP_Version_Not_Supported;
476 return GF_OK;
477 }
478
479 com->StatusCode = NC_RTSP_OK;
480
481 return gf_rtsp_parse_header(buffer + ret, Size - ret, BodyStart, com, NULL);
482 }
483
484 char *RTSP_DEFINED_METHODS[] =
485 {
486 GF_RTSP_DESCRIBE,
487 GF_RTSP_SETUP,
488 GF_RTSP_PLAY,
489 GF_RTSP_PAUSE,
490 GF_RTSP_RECORD,
491 GF_RTSP_TEARDOWN,
492 GF_RTSP_GET_PARAMETER,
493 GF_RTSP_SET_PARAMETER,
494 GF_RTSP_OPTIONS,
495 GF_RTSP_ANNOUNCE,
496 GF_RTSP_REDIRECT,
497 NULL
498 };
499
500 GF_EXPORT
gf_rtsp_get_command(GF_RTSPSession * sess,GF_RTSPCommand * com)501 GF_Err gf_rtsp_get_command(GF_RTSPSession *sess, GF_RTSPCommand *com)
502 {
503 GF_Err e;
504 u32 BodyStart, size;
505 if (!sess || !com) return GF_BAD_PARAM;
506
507 //reset the command
508 gf_rtsp_command_reset(com);
509 //if no connection, we have sent a "Connection: Close"
510 if (!sess->connection) return GF_IP_CONNECTION_CLOSED;
511
512 //lock
513 //fill TCP buffer
514 e = gf_rtsp_fill_buffer(sess);
515 if (e) goto exit;
516 if (sess->TCPChannels)
517 //this is upcoming, interleaved data
518 if (sess->interleaved) {
519 u32 i=0;
520 Bool sync = GF_FALSE;
521 while (RTSP_DEFINED_METHODS[i]) {
522 if (!strncmp(sess->tcp_buffer+sess->CurrentPos, RTSP_DEFINED_METHODS[i], strlen(RTSP_DEFINED_METHODS[i]) ) ) {
523 sync = GF_TRUE;
524 break;
525 }
526 }
527 if (!sync) {
528 e = GF_IP_NETWORK_EMPTY;
529 goto exit;
530 }
531 }
532 e = gf_rtsp_read_reply(sess);
533 if (e) goto exit;
534
535 GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSP] Got Command:\n%s\n", sess->tcp_buffer+sess->CurrentPos));
536
537 gf_rtsp_get_body_info(sess, &BodyStart, &size);
538 e = RTSP_ParseCommandHeader(sess, com, BodyStart);
539 //before returning an error we MUST reset the TCP buffer
540
541 //copy the body if any
542 if (!e && com->Content_Length) {
543 com->body = (char *) gf_malloc(sizeof(char) * (com->Content_Length));
544 memcpy(com->body, sess->tcp_buffer+sess->CurrentPos + BodyStart, com->Content_Length);
545 }
546 //reset TCP buffer
547 sess->CurrentPos += BodyStart + com->Content_Length;
548
549 if (!com->CSeq)
550 com->StatusCode = NC_RTSP_Bad_Request;
551
552 if (e || (com->StatusCode != NC_RTSP_OK)) goto exit;
553
554 //NB: there is no "session state" in our lib when acting at the server side, as it depends
555 //on the server implementation. We cannot block responses / announcement to be sent
556 //dynamically, nor reset the session ourselves as we don't know the details of the session
557 //(eg TEARDOWN may keep resources up or not, ...)
558
559 //we also have the same pb for CSeq, as nothing forbids a server to buffer commands (and it
560 //happens during aggregation of PLAY/PAUSE with overlapping ranges)
561
562 //however store the last CSeq in case for client checking
563 if (!sess->CSeq) {
564 sess->CSeq = com->CSeq;
565 }
566 //check we're in the right range
567 else {
568 if (sess->CSeq >= com->CSeq)
569 com->StatusCode = NC_RTSP_Header_Field_Not_Valid;
570 else
571 sess->CSeq = com->CSeq;
572 }
573
574 //
575 //if a connection closed is signal, check this is the good session
576 // and reset it (the client is no longer connected)
577 if (sess->last_session_id && com->Session && !strcmp(com->Session, sess->last_session_id)
578 && com->Connection && !stricmp(com->Connection, "Close")) {
579
580 gf_rtsp_session_reset(sess, GF_FALSE);
581 //destroy the socket
582 if (sess->connection) gf_sk_del(sess->connection);
583 sess->connection = NULL;
584
585 //destroy the http tunnel if any
586 if (sess->HasTunnel && sess->http) {
587 gf_sk_del(sess->http);
588 sess->http = NULL;
589 }
590 }
591
592 exit:
593 return e;
594 }
595
596 #endif /*GPAC_DISABLE_STREAMING*/
597