Line data Source code
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
34 55 : GF_RTSPCommand *gf_rtsp_command_new()
35 : {
36 : GF_RTSPCommand *tmp;
37 55 : GF_SAFEALLOC(tmp, GF_RTSPCommand);
38 55 : if (!tmp) return NULL;
39 55 : tmp->Xtensions = gf_list_new();
40 55 : tmp->Transports = gf_list_new();
41 55 : 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
49 473 : void gf_rtsp_command_reset(GF_RTSPCommand *com)
50 : {
51 473 : if (!com) return;
52 :
53 : //free all headers
54 473 : COM_FREE_CLEAN(Accept);
55 473 : COM_FREE_CLEAN(Accept_Encoding);
56 473 : COM_FREE_CLEAN(Accept_Language);
57 473 : COM_FREE_CLEAN(Authorization);
58 473 : COM_FREE_CLEAN(Cache_Control);
59 473 : COM_FREE_CLEAN(Conference);
60 473 : COM_FREE_CLEAN(Connection);
61 473 : COM_FREE_CLEAN(From);
62 473 : COM_FREE_CLEAN(Proxy_Authorization);
63 473 : COM_FREE_CLEAN(Proxy_Require);
64 473 : COM_FREE_CLEAN(Referer);
65 473 : COM_FREE_CLEAN(Session);
66 473 : COM_FREE_CLEAN(User_Agent);
67 473 : COM_FREE_CLEAN(body);
68 473 : COM_FREE_CLEAN(service_name);
69 473 : COM_FREE_CLEAN(ControlString);
70 473 : COM_FREE_CLEAN(method);
71 :
72 : //this is for server only, set to OK by default
73 473 : com->StatusCode = NC_RTSP_OK;
74 :
75 :
76 473 : com->user_data = NULL;
77 :
78 473 : com->Bandwidth = com->Blocksize = com->Content_Length = com->CSeq = 0;
79 473 : com->Scale = com->Speed = 0.0;
80 473 : if (com->Range) gf_free(com->Range);
81 473 : com->Range = NULL;
82 :
83 958 : while (gf_list_count(com->Transports)) {
84 12 : GF_RTSPTransport *trans = (GF_RTSPTransport *) gf_list_get(com->Transports, 0);
85 12 : gf_list_rem(com->Transports, 0);
86 12 : gf_rtsp_transport_del(trans);
87 : }
88 473 : while (gf_list_count(com->Xtensions)) {
89 0 : GF_X_Attribute *att = (GF_X_Attribute*)gf_list_get(com->Xtensions, 0);
90 0 : gf_list_rem(com->Xtensions, 0);
91 0 : gf_free(att->Name);
92 0 : gf_free(att->Value);
93 0 : gf_free(att);
94 : }
95 : }
96 :
97 : GF_EXPORT
98 77 : void gf_rtsp_command_del(GF_RTSPCommand *com)
99 : {
100 77 : if (!com) return;
101 55 : gf_rtsp_command_reset(com);
102 55 : gf_list_del(com->Xtensions);
103 55 : gf_list_del(com->Transports);
104 55 : gf_free(com);
105 : }
106 :
107 :
108 28 : 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 28 : *out_buffer = NULL;
115 :
116 : size = RTSP_WRITE_STEPALLOC;
117 28 : buffer = (char *) gf_malloc(size);
118 : cur_pos = 0;
119 :
120 : //request
121 56 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, req_buffer);
122 :
123 : //then all headers
124 48 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept", com->Accept);
125 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept-Encoding", com->Accept_Encoding);
126 140 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept-Language", com->Accept_Language);
127 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Authorization", com->Authorization);
128 28 : if (com->Bandwidth) {
129 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Bandwidth: ");
130 0 : RTSP_WRITE_INT(buffer, size, cur_pos, com->Bandwidth, 0);
131 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
132 : }
133 28 : if (com->Blocksize) {
134 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Blocksize: ");
135 0 : RTSP_WRITE_INT(buffer, size, cur_pos, com->Blocksize, 0);
136 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
137 : }
138 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Cache-Control", com->Cache_Control);
139 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Conference", com->Conference);
140 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Connection", com->Connection);
141 : //if we have a body write the content length
142 28 : if (com->body) {
143 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Content-Length: ");
144 0 : RTSP_WRITE_INT(buffer, size, cur_pos, (u32) strlen(com->body), 0);
145 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
146 : }
147 : //write the CSeq - use the SESSION CSeq
148 56 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "CSeq: ");
149 84 : RTSP_WRITE_INT(buffer, size, cur_pos, sess->CSeq, 0);
150 56 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
151 :
152 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "From", com->From);
153 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Proxy-Authorization", com->Proxy_Authorization);
154 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Proxy-Require", com->Proxy_Require);
155 :
156 : //Range, only NPT
157 28 : if (com->Range && !com->Range->UseSMPTE) {
158 24 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Range: npt=");
159 36 : RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, com->Range->start);
160 24 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
161 12 : if (com->Range->end > com->Range->start) {
162 0 : RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, com->Range->end);
163 : }
164 24 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
165 : }
166 :
167 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Referer", com->Referer);
168 28 : if (com->Scale != 0.0) {
169 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Scale: ");
170 0 : RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, com->Scale);
171 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
172 : }
173 100 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Session", com->Session);
174 28 : if (com->Speed != 0.0) {
175 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Speed: ");
176 0 : RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, com->Speed);
177 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
178 : }
179 :
180 : //transport info
181 28 : count = gf_list_count(com->Transports);
182 28 : if (count) {
183 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Transport: ");
184 12 : for (i=0; i<count; i++) {
185 : GF_RTSPTransport *trans;
186 : //line separator for headers
187 6 : if (i) RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n ,");
188 6 : trans = (GF_RTSPTransport *) gf_list_get(com->Transports, i);
189 :
190 : //then write the structure
191 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->Profile);
192 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, (trans->IsUnicast ? ";unicast" : ";multicast"));
193 6 : if (trans->destination) {
194 2 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";destination=");
195 2 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->destination);
196 : }
197 6 : if (trans->source) {
198 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";source=");
199 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->source);
200 : }
201 6 : if (trans->IsRecord) {
202 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";mode=RECORD");
203 0 : if (trans->Append) RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";append");
204 : }
205 6 : if (trans->IsInterleaved) {
206 2 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";interleaved=");
207 3 : RTSP_WRITE_INT(buffer, size, cur_pos, trans->rtpID, 0);
208 1 : if (trans->rtcpID != trans->rtpID) {
209 2 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
210 3 : RTSP_WRITE_INT(buffer, size, cur_pos, trans->rtcpID, 0);
211 : }
212 : }
213 6 : if (trans->port_first) {
214 2 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, (trans->IsUnicast ? ";server_port=" : ";port="));
215 3 : RTSP_WRITE_INT(buffer, size, cur_pos, trans->port_first, 0);
216 2 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
217 3 : RTSP_WRITE_INT(buffer, size, cur_pos, trans->port_last, 0);
218 : }
219 6 : if (/*trans->IsUnicast && */trans->client_port_first) {
220 10 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";client_port=");
221 15 : RTSP_WRITE_INT(buffer, size, cur_pos, trans->client_port_first, 0);
222 10 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
223 15 : RTSP_WRITE_INT(buffer, size, cur_pos, trans->client_port_last, 0);
224 : }
225 : //multicast specific
226 6 : if (!trans->IsUnicast) {
227 1 : if (trans->MulticastLayers) {
228 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";layers=");
229 0 : RTSP_WRITE_INT(buffer, size, cur_pos, trans->MulticastLayers, 0);
230 : }
231 1 : if (trans->TTL) {
232 2 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";ttl=");
233 3 : RTSP_WRITE_INT(buffer, size, cur_pos, trans->TTL, 0);
234 : }
235 : }
236 6 : if (trans->SSRC) {
237 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";ssrc=");
238 0 : RTSP_WRITE_HEX(buffer, size, cur_pos, trans->SSRC, 0);
239 : }
240 : }
241 : //done with transport
242 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
243 : }
244 140 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "User-Agent", com->User_Agent);
245 :
246 : //eXtensions
247 28 : count = gf_list_count(com->Xtensions);
248 28 : for (i=0; i<count; i++) {
249 0 : GF_X_Attribute *att = (GF_X_Attribute *) gf_list_get(com->Xtensions, i);
250 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "x-");
251 0 : RTSP_WRITE_HEADER(buffer, size, cur_pos, att->Name, att->Value);
252 : }
253 :
254 : //the end of header
255 56 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
256 : //then body
257 28 : 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 28 : *out_buffer = (unsigned char *)buffer;
262 28 : *out_size = (u32) strlen(buffer);
263 28 : 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, formatting the REQUEST as server_url/service_name/sCtrl
270 : GF_EXPORT
271 28 : 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 28 : if (!com || !com->method) return GF_BAD_PARAM;
280 :
281 28 : sCtrl = com->ControlString;
282 :
283 : //NB: OPTIONS is not sent this way
284 28 : if (strcmp(com->method, GF_RTSP_DESCRIBE)
285 23 : && strcmp(com->method, GF_RTSP_ANNOUNCE)
286 23 : && strcmp(com->method, GF_RTSP_GET_PARAMETER)
287 23 : && strcmp(com->method, GF_RTSP_SET_PARAMETER)
288 23 : && strcmp(com->method, GF_RTSP_SETUP)
289 17 : && strcmp(com->method, GF_RTSP_PLAY)
290 11 : && strcmp(com->method, GF_RTSP_PAUSE)
291 5 : && strcmp(com->method, GF_RTSP_RECORD)
292 5 : && strcmp(com->method, GF_RTSP_REDIRECT)
293 5 : && strcmp(com->method, GF_RTSP_TEARDOWN)
294 0 : && strcmp(com->method, GF_RTSP_OPTIONS)
295 :
296 : ) return GF_BAD_PARAM;
297 :
298 : //check the state machine
299 28 : if (strcmp(com->method, GF_RTSP_PLAY)
300 22 : && strcmp(com->method, GF_RTSP_PAUSE)
301 16 : && strcmp(com->method, GF_RTSP_RECORD)
302 16 : && 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 28 : if (!strcmp(com->method, GF_RTSP_OPTIONS) && !sCtrl) return GF_BAD_PARAM;
319 :
320 :
321 : //update sequence number
322 28 : sess->CSeq += 1;
323 28 : sess->NbPending += 1;
324 :
325 28 : if (!strcmp(com->method, GF_RTSP_OPTIONS)) {
326 : sprintf(buffer, "OPTIONS %s %s\r\n", sCtrl, GF_RTSP_VERSION);
327 : } else {
328 28 : rad = (sess->ConnectionType == GF_SOCK_TYPE_TCP) ? "rtsp" : "rtspu";
329 28 : if (sCtrl) {
330 : //if both server and service names are included in the control, just
331 : //use the control
332 12 : 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 12 : else if (strstr(sCtrl, sess->Service)) {
337 0 : sprintf(buffer, "%s %s://%s:%d/%s %s\r\n", com->method, rad, sess->Server, sess->Port, sCtrl, GF_RTSP_VERSION);
338 : }
339 12 : 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 16 : 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 28 : if (strcmp(com->method, GF_RTSP_ANNOUNCE)
355 28 : && strcmp(com->method, GF_RTSP_GET_PARAMETER)
356 28 : && strcmp(com->method, GF_RTSP_SET_PARAMETER)
357 : ) {
358 : //this is an error, but don't say anything
359 28 : if (com->body) {
360 : body = com->body;
361 0 : com->body = NULL;
362 : }
363 : }
364 :
365 28 : result = NULL;
366 28 : e = RTSP_WriteCommand(sess, com, (unsigned char *)buffer, (unsigned char **) &result, &size);
367 : //restore body if needed
368 28 : if (body) com->body = body;
369 28 : if (e) goto exit;
370 :
371 :
372 28 : GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSP] Sending Command:\n%s\n", result));
373 :
374 : //send buffer
375 28 : e = gf_rtsp_send_data(sess, result, size);
376 28 : if (e) goto exit;
377 :
378 :
379 : //update our state
380 28 : if (!strcmp(com->method, GF_RTSP_RECORD)) sess->RTSP_State = GF_RTSP_STATE_WAIT_FOR_CONTROL;
381 28 : else if (!strcmp(com->method, GF_RTSP_PLAY)) sess->RTSP_State = GF_RTSP_STATE_WAIT_FOR_CONTROL;
382 22 : else if (!strcmp(com->method, GF_RTSP_PAUSE)) sess->RTSP_State = GF_RTSP_STATE_WAIT_FOR_CONTROL;
383 16 : 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 28 : strcpy(sess->RTSPLastRequest, com->method);
387 :
388 28 : exit:
389 28 : if (result) gf_free(result);
390 : return e;
391 : }
392 :
393 :
394 125 : 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 125 : if (!stricmp(Header, "Accept")) com->Accept = gf_strdup(Value);
402 120 : else if (!stricmp(Header, "Accept-Encoding")) com->Accept_Encoding = gf_strdup(Value);
403 120 : else if (!stricmp(Header, "Accept-Language")) com->Accept_Language = gf_strdup(Value);
404 92 : else if (!stricmp(Header, "Authorization")) com->Authorization = gf_strdup(Value);
405 92 : else if (!stricmp(Header, "Bandwidth")) sscanf(Value, "%u", &com->Bandwidth);
406 92 : else if (!stricmp(Header, "Blocksize")) sscanf(Value, "%u", &com->Blocksize);
407 92 : else if (!stricmp(Header, "Cache-Control")) com->Cache_Control = gf_strdup(Value);
408 92 : else if (!stricmp(Header, "Conference")) com->Conference = gf_strdup(Value);
409 92 : else if (!stricmp(Header, "Connection")) com->Connection = gf_strdup(Value);
410 92 : else if (!stricmp(Header, "Content-Length")) sscanf(Value, "%u", &com->Content_Length);
411 92 : else if (!stricmp(Header, "CSeq")) sscanf(Value, "%u", &com->CSeq);
412 64 : else if (!stricmp(Header, "From")) com->From = gf_strdup(Value);
413 64 : else if (!stricmp(Header, "Proxy_Authorization")) com->Proxy_Authorization = gf_strdup(Value);
414 64 : else if (!stricmp(Header, "Proxy_Require")) com->Proxy_Require = gf_strdup(Value);
415 64 : else if (!stricmp(Header, "Range")) com->Range = gf_rtsp_range_parse(Value);
416 52 : else if (!stricmp(Header, "Referer")) com->Referer = gf_strdup(Value);
417 52 : else if (!stricmp(Header, "Scale")) sscanf(Value, "%lf", &com->Scale);
418 52 : else if (!stricmp(Header, "Session"))
419 18 : com->Session = gf_strdup(Value);
420 34 : else if (!stricmp(Header, "Speed")) sscanf(Value, "%lf", &com->Speed);
421 34 : else if (!stricmp(Header, "User_Agent")) com->User_Agent = gf_strdup(Value);
422 : //Transports
423 34 : else if (!stricmp(Header, "Transport")) {
424 : LinePos = 0;
425 : while (1) {
426 12 : LinePos = gf_token_get(Value, LinePos, "\r\n", LineBuffer, 400);
427 18 : if (LinePos <= 0) return;
428 6 : trans = gf_rtsp_transport_parse(Value);
429 6 : if (trans) gf_list_add(com->Transports, trans);
430 : }
431 : }
432 : //eXtensions attributes
433 28 : else if (!strnicmp(Header, "x-", 2)) {
434 0 : x_Att = (GF_X_Attribute*)gf_malloc(sizeof(GF_X_Attribute));
435 0 : x_Att->Name = gf_strdup(Header+2);
436 0 : x_Att->Value = NULL;
437 0 : if (Value && strlen(Value)) x_Att->Value = gf_strdup(Value);
438 0 : gf_list_add(com->Xtensions, x_Att);
439 : }
440 : //the rest is ignored
441 : }
442 :
443 28 : 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 28 : Size = sess->CurrentSize - sess->CurrentPos;
452 28 : buffer = sess->tcp_buffer + sess->CurrentPos;
453 :
454 : //by default the command is wrong ;)
455 28 : com->StatusCode = NC_RTSP_Bad_Request;
456 :
457 : //parse first line
458 28 : ret = gf_token_get_line(buffer, 0, Size, LineBuffer, 1024);
459 28 : if (ret < 0) return GF_REMOTE_SERVICE_ERROR;
460 :
461 : //method
462 28 : Pos = gf_token_get(LineBuffer, 0, " \t\r\n", ValBuf, 1024);
463 28 : if (Pos <= 0) return GF_OK;
464 28 : com->method = gf_strdup((const char *) ValBuf);
465 :
466 : //URL
467 28 : Pos = gf_token_get(LineBuffer, Pos, " \t\r\n", ValBuf, 1024);
468 28 : if (Pos <= 0) return GF_OK;
469 28 : com->service_name = gf_strdup(ValBuf);
470 :
471 : //RTSP version
472 28 : Pos = gf_token_get(LineBuffer, Pos, " \t\r\n", ValBuf, 1024);
473 28 : if (Pos <= 0) return GF_OK;
474 28 : if (strcmp(ValBuf, GF_RTSP_VERSION)) {
475 0 : com->StatusCode = NC_RTSP_RTSP_Version_Not_Supported;
476 0 : return GF_OK;
477 : }
478 :
479 28 : com->StatusCode = NC_RTSP_OK;
480 :
481 28 : 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
501 418 : GF_Err gf_rtsp_get_command(GF_RTSPSession *sess, GF_RTSPCommand *com)
502 : {
503 : GF_Err e;
504 : u32 BodyStart, size;
505 418 : if (!sess || !com) return GF_BAD_PARAM;
506 :
507 : //reset the command
508 418 : gf_rtsp_command_reset(com);
509 : //if no connection, we have sent a "Connection: Close"
510 418 : if (!sess->connection) return GF_IP_CONNECTION_CLOSED;
511 :
512 : //lock
513 : //fill TCP buffer
514 418 : e = gf_rtsp_fill_buffer(sess);
515 418 : if (e) goto exit;
516 28 : if (sess->TCPChannels)
517 : //this is upcoming, interleaved data
518 28 : if (sess->interleaved) {
519 : u32 i=0;
520 : Bool sync = GF_FALSE;
521 0 : while (RTSP_DEFINED_METHODS[i]) {
522 0 : 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 0 : if (!sync) {
528 : e = GF_IP_NETWORK_EMPTY;
529 : goto exit;
530 : }
531 : }
532 28 : e = gf_rtsp_read_reply(sess);
533 28 : if (e) goto exit;
534 :
535 28 : GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSP] Got Command:\n%s\n", sess->tcp_buffer+sess->CurrentPos));
536 :
537 28 : gf_rtsp_get_body_info(sess, &BodyStart, &size);
538 28 : 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 28 : if (!e && com->Content_Length) {
543 0 : com->body = (char *) gf_malloc(sizeof(char) * (com->Content_Length));
544 0 : memcpy(com->body, sess->tcp_buffer+sess->CurrentPos + BodyStart, com->Content_Length);
545 : }
546 : //reset TCP buffer
547 28 : sess->CurrentPos += BodyStart + com->Content_Length;
548 :
549 28 : if (!com->CSeq)
550 0 : com->StatusCode = NC_RTSP_Bad_Request;
551 :
552 56 : 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 28 : if (!sess->CSeq) {
564 25 : sess->CSeq = com->CSeq;
565 : }
566 : //check we're in the right range
567 : else {
568 3 : if (sess->CSeq >= com->CSeq)
569 0 : com->StatusCode = NC_RTSP_Header_Field_Not_Valid;
570 : else
571 3 : 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 28 : if (sess->last_session_id && com->Session && !strcmp(com->Session, sess->last_session_id)
578 0 : && com->Connection && !stricmp(com->Connection, "Close")) {
579 :
580 0 : gf_rtsp_session_reset(sess, GF_FALSE);
581 : //destroy the socket
582 0 : if (sess->connection) gf_sk_del(sess->connection);
583 0 : sess->connection = NULL;
584 :
585 : //destroy the http tunnel if any
586 0 : if (sess->HasTunnel && sess->http) {
587 0 : gf_sk_del(sess->http);
588 0 : sess->http = NULL;
589 : }
590 : }
591 :
592 418 : exit:
593 : return e;
594 : }
595 :
596 : #endif /*GPAC_DISABLE_STREAMING*/
|