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 : #include <gpac/internal/ietf_dev.h>
27 :
28 : #ifndef GPAC_DISABLE_STREAMING
29 :
30 : #include <gpac/token.h>
31 :
32 :
33 : GF_EXPORT
34 32 : GF_RTSPResponse *gf_rtsp_response_new()
35 : {
36 : GF_RTSPResponse *tmp;
37 32 : GF_SAFEALLOC(tmp, GF_RTSPResponse);
38 32 : if (!tmp) return NULL;
39 32 : tmp->Transports = gf_list_new();
40 32 : tmp->RTP_Infos = gf_list_new();
41 32 : tmp->Xtensions = gf_list_new();
42 32 : return tmp;
43 : }
44 :
45 :
46 : #define RSP_FREE_CLEAN(hdr) if (rsp->hdr) gf_free(rsp->hdr); \
47 : rsp->hdr = NULL;
48 :
49 : GF_EXPORT
50 2067 : void gf_rtsp_response_reset(GF_RTSPResponse *rsp)
51 : {
52 2067 : if (!rsp) return;
53 :
54 : //free all headers
55 2067 : RSP_FREE_CLEAN(Accept);
56 2067 : RSP_FREE_CLEAN(Accept_Encoding);
57 2067 : RSP_FREE_CLEAN(Accept_Language);
58 2067 : RSP_FREE_CLEAN(Allow);
59 2067 : RSP_FREE_CLEAN(Authorization);
60 2067 : RSP_FREE_CLEAN(Cache_Control);
61 2067 : RSP_FREE_CLEAN(Conference);
62 2067 : RSP_FREE_CLEAN(Connection);
63 2067 : RSP_FREE_CLEAN(Content_Base);
64 2067 : RSP_FREE_CLEAN(Content_Encoding);
65 2067 : RSP_FREE_CLEAN(Content_Language);
66 2067 : RSP_FREE_CLEAN(Content_Location);
67 2067 : RSP_FREE_CLEAN(Content_Type);
68 2067 : RSP_FREE_CLEAN(Date);
69 2067 : RSP_FREE_CLEAN(Expires);
70 2067 : RSP_FREE_CLEAN(From);
71 2067 : RSP_FREE_CLEAN(Host);
72 2067 : RSP_FREE_CLEAN(If_Match);
73 2067 : RSP_FREE_CLEAN(If_Modified_Since);
74 2067 : RSP_FREE_CLEAN(Last_Modified);
75 2067 : RSP_FREE_CLEAN(Location);
76 2067 : RSP_FREE_CLEAN(Proxy_Authenticate);
77 2067 : RSP_FREE_CLEAN(Proxy_Require);
78 2067 : RSP_FREE_CLEAN(Public);
79 2067 : RSP_FREE_CLEAN(Referer);
80 2067 : RSP_FREE_CLEAN(Require);
81 2067 : RSP_FREE_CLEAN(Retry_After);
82 2067 : RSP_FREE_CLEAN(Server);
83 2067 : RSP_FREE_CLEAN(Session);
84 2067 : RSP_FREE_CLEAN(Timestamp);
85 2067 : RSP_FREE_CLEAN(Unsupported);
86 2067 : RSP_FREE_CLEAN(User_Agent);
87 2067 : RSP_FREE_CLEAN(Vary);
88 2067 : RSP_FREE_CLEAN(Via);
89 2067 : RSP_FREE_CLEAN(WWW_Authenticate);
90 :
91 : //this is for us
92 2067 : RSP_FREE_CLEAN(ResponseInfo);
93 2067 : RSP_FREE_CLEAN(body);
94 :
95 2067 : rsp->Bandwidth = rsp->Blocksize = rsp->ResponseCode = rsp->Content_Length = rsp->CSeq = 0;
96 2067 : rsp->Scale = rsp->Speed = 0.0;
97 2067 : if (rsp->Range) gf_free(rsp->Range);
98 2067 : rsp->Range = NULL;
99 :
100 2067 : rsp->SessionTimeOut = 0;
101 :
102 4140 : while (gf_list_count(rsp->Transports)) {
103 6 : GF_RTSPTransport *trans = (GF_RTSPTransport*) gf_list_get(rsp->Transports, 0);
104 6 : gf_list_rem(rsp->Transports, 0);
105 6 : gf_rtsp_transport_del(trans);
106 : }
107 :
108 2073 : while (gf_list_count(rsp->RTP_Infos)) {
109 6 : GF_RTPInfo *inf = (GF_RTPInfo*) gf_list_get(rsp->RTP_Infos, 0);
110 6 : gf_list_rem(rsp->RTP_Infos, 0);
111 6 : if (inf->url) gf_free(inf->url);
112 6 : gf_free(inf);
113 : }
114 2067 : while (gf_list_count(rsp->Xtensions)) {
115 0 : GF_X_Attribute *att = (GF_X_Attribute*)gf_list_get(rsp->Xtensions, 0);
116 0 : gf_list_rem(rsp->Xtensions, 0);
117 0 : gf_free(att->Name);
118 0 : gf_free(att->Value);
119 0 : gf_free(att);
120 : }
121 : }
122 :
123 : GF_EXPORT
124 32 : void gf_rtsp_response_del(GF_RTSPResponse *rsp)
125 : {
126 32 : if (!rsp) return;
127 :
128 32 : gf_rtsp_response_reset(rsp);
129 32 : gf_list_del(rsp->RTP_Infos);
130 32 : gf_list_del(rsp->Xtensions);
131 32 : gf_list_del(rsp->Transports);
132 32 : gf_free(rsp);
133 : }
134 :
135 :
136 :
137 : GF_EXPORT
138 23 : GF_RTSPRange *gf_rtsp_range_parse(char *range_buf)
139 : {
140 : GF_RTSPRange *rg;
141 :
142 23 : if (!strstr(range_buf, "npt")) return NULL;
143 :
144 23 : GF_SAFEALLOC(rg, GF_RTSPRange);
145 23 : if (!rg) return NULL;
146 23 : if (sscanf(range_buf, "npt=%lf-%lf", &rg->start, &rg->end) != 2) {
147 18 : rg->end = -1.0;
148 18 : sscanf(range_buf, "npt=%lf-", &rg->start);
149 : }
150 : return rg;
151 : }
152 :
153 : GF_EXPORT
154 18 : void gf_rtsp_transport_del(GF_RTSPTransport *transp)
155 : {
156 18 : if (!transp) return;
157 18 : if (transp->destination) gf_free(transp->destination);
158 18 : if (transp->Profile) gf_free(transp->Profile);
159 18 : if (transp->source) gf_free(transp->source);
160 18 : gf_free(transp);
161 : }
162 :
163 : GF_EXPORT
164 6 : GF_RTSPTransport *gf_rtsp_transport_clone(GF_RTSPTransport *original)
165 : {
166 : GF_RTSPTransport *tr;
167 :
168 6 : if (!original) return NULL;
169 :
170 6 : tr = (GF_RTSPTransport*) gf_malloc(sizeof(GF_RTSPTransport));
171 : memcpy(tr, original, sizeof(GF_RTSPTransport));
172 6 : tr->destination = tr->source = tr->Profile = NULL;
173 6 : if (original->destination) tr->destination = gf_strdup(original->destination);
174 6 : if (original->source) tr->source = gf_strdup(original->source);
175 6 : if (original->Profile) tr->Profile = gf_strdup(original->Profile);
176 : return tr;
177 : }
178 :
179 : GF_EXPORT
180 12 : GF_RTSPRange *gf_rtsp_range_new()
181 : {
182 : GF_RTSPRange *tmp;
183 12 : GF_SAFEALLOC(tmp, GF_RTSPRange);
184 12 : return tmp;
185 : }
186 :
187 : GF_EXPORT
188 5 : void gf_rtsp_range_del(GF_RTSPRange *range)
189 : {
190 5 : if (!range) return;
191 5 : gf_free(range);
192 : }
193 :
194 126 : void gf_rtsp_set_response_value(GF_RTSPResponse *rsp, char *Header, char *Value)
195 : {
196 : char LineBuffer[400], buf[1000], param_name[100], param_val[1000];
197 : s32 LinePos, Pos, nPos, s_val;
198 : GF_RTPInfo *info;
199 : GF_RTSPTransport *trans;
200 : GF_X_Attribute *x_Att;
201 :
202 126 : if (!stricmp(Header, "Accept")) rsp->Accept = gf_strdup(Value);
203 126 : else if (!stricmp(Header, "Accept-Encoding")) rsp->Accept_Encoding = gf_strdup(Value);
204 126 : else if (!stricmp(Header, "Accept-Language")) rsp->Accept_Language = gf_strdup(Value);
205 126 : else if (!stricmp(Header, "Allow")) rsp->Allow = gf_strdup(Value);
206 126 : else if (!stricmp(Header, "Authorization")) rsp->Authorization = gf_strdup(Value);
207 126 : else if (!stricmp(Header, "Bandwidth")) sscanf(Value, "%u", &rsp->Bandwidth);
208 126 : else if (!stricmp(Header, "Blocksize")) sscanf(Value, "%u", &rsp->Blocksize);
209 126 : else if (!stricmp(Header, "Cache-Control")) rsp->Cache_Control = gf_strdup(Value);
210 126 : else if (!stricmp(Header, "com.ses.streamID")) sscanf(Value, "%u", &rsp->StreamID);
211 126 : else if (!stricmp(Header, "Conference")) rsp->Conference = gf_strdup(Value);
212 126 : else if (!stricmp(Header, "Connection")) rsp->Connection = gf_strdup(Value);
213 102 : else if (!stricmp(Header, "Content-Base")) rsp->Content_Base = gf_strdup(Value);
214 102 : else if (!stricmp(Header, "Content-Encoding")) rsp->Content_Encoding = gf_strdup(Value);
215 102 : else if (!stricmp(Header, "Content-Length")) sscanf(Value, "%u", &rsp->Content_Length);
216 97 : else if (!stricmp(Header, "Content-Language")) rsp->Content_Language = gf_strdup(Value);
217 97 : else if (!stricmp(Header, "Content-Location")) rsp->Content_Location = gf_strdup(Value);
218 97 : else if (!stricmp(Header, "Content-Type")) rsp->Content_Type = gf_strdup(Value);
219 97 : else if (!stricmp(Header, "CSeq")) sscanf(Value, "%u", &rsp->CSeq);
220 69 : else if (!stricmp(Header, "Date")) rsp->Date = gf_strdup(Value);
221 69 : else if (!stricmp(Header, "Expires")) rsp->Expires = gf_strdup(Value);
222 69 : else if (!stricmp(Header, "From")) rsp->From = gf_strdup(Value);
223 69 : else if (!stricmp(Header, "Host")) rsp->Host = gf_strdup(Value);
224 69 : else if (!stricmp(Header, "If-Match")) rsp->If_Match = gf_strdup(Value);
225 69 : else if (!stricmp(Header, "If-Modified-Since")) rsp->If_Modified_Since = gf_strdup(Value);
226 69 : else if (!stricmp(Header, "Last-Modified")) rsp->Last_Modified = gf_strdup(Value);
227 69 : else if (!stricmp(Header, "Location")) rsp->Location = gf_strdup(Value);
228 69 : else if (!stricmp(Header, "Proxy-Authenticate")) rsp->Proxy_Authenticate = gf_strdup(Value);
229 69 : else if (!stricmp(Header, "Proxy-Require")) rsp->Proxy_Require = gf_strdup(Value);
230 69 : else if (!stricmp(Header, "Public")) rsp->Public = gf_strdup(Value);
231 69 : else if (!stricmp(Header, "Referer")) rsp->Referer = gf_strdup(Value);
232 69 : else if (!stricmp(Header, "Require")) rsp->Require = gf_strdup(Value);
233 69 : else if (!stricmp(Header, "Retry-After")) rsp->Retry_After = gf_strdup(Value);
234 69 : else if (!stricmp(Header, "Scale")) sscanf(Value, "%lf", &rsp->Scale);
235 69 : else if (!stricmp(Header, "Server")) rsp->Server = gf_strdup(Value);
236 69 : else if (!stricmp(Header, "Speed")) sscanf(Value, "%lf", &rsp->Speed);
237 69 : else if (!stricmp(Header, "Timestamp")) rsp->Timestamp = gf_strdup(Value);
238 69 : else if (!stricmp(Header, "Unsupported")) rsp->Unsupported = gf_strdup(Value);
239 69 : else if (!stricmp(Header, "User-Agent")) rsp->User_Agent = gf_strdup(Value);
240 41 : else if (!stricmp(Header, "Vary")) rsp->Vary = gf_strdup(Value);
241 41 : else if (!stricmp(Header, "Via")) rsp->Vary = gf_strdup(Value);
242 41 : else if (!stricmp(Header, "WWW_Authenticate")) rsp->Vary = gf_strdup(Value);
243 41 : else if (!stricmp(Header, "Transport")) {
244 : LinePos = 0;
245 : while (1) {
246 12 : LinePos = gf_token_get(Value, LinePos, "\r\n", LineBuffer, 400);
247 18 : if (LinePos <= 0) return;
248 6 : trans = gf_rtsp_transport_parse(Value);
249 6 : if (trans) gf_list_add(rsp->Transports, trans);
250 : }
251 : }
252 : //Session
253 35 : else if (!stricmp(Header, "Session")) {
254 23 : LinePos = gf_token_get(Value, 0, ";\r\n", LineBuffer, 400);
255 23 : rsp->Session = gf_strdup(LineBuffer);
256 : //get timeout if any
257 23 : if (Value[LinePos] == ';') {
258 0 : LinePos += 1;
259 0 : /*LinePos = */gf_token_get(Value, LinePos, ";\r\n", LineBuffer, 400);
260 0 : rsp->SessionTimeOut = 60; //default
261 0 : sscanf(LineBuffer, "timeout=%u", &rsp->SessionTimeOut);
262 : }
263 : }
264 :
265 : //Range
266 12 : else if (!stricmp(Header, "Range")) rsp->Range = gf_rtsp_range_parse(Value);
267 : //RTP-Info
268 6 : else if (!stricmp(Header, "RTP-Info")) {
269 : LinePos = 0;
270 : while (1) {
271 0 : LinePos = gf_token_get(Value, LinePos, ",\r\n", LineBuffer, 400);
272 0 : if (LinePos <= 0) return;
273 :
274 0 : GF_SAFEALLOC(info, GF_RTPInfo);
275 0 : if (!info) return;
276 :
277 : Pos = 0;
278 : while (1) {
279 0 : Pos = gf_token_get(LineBuffer, Pos, " ;", buf, 1000);
280 0 : if (Pos <= 0) break;
281 0 : if (strstr(buf, "=")) {
282 0 : nPos = gf_token_get(buf, 0, "=", param_name, 100);
283 0 : nPos += 1;
284 0 : /*nPos = */gf_token_get(buf, nPos, "", param_val, 1000);
285 : } else {
286 : strcpy(param_name, buf);
287 : }
288 0 : if (!stricmp(param_name, "url")) info->url = gf_strdup(param_val);
289 0 : else if (!stricmp(param_name, "seq")) sscanf(param_val, "%u", &info->seq);
290 0 : else if (!stricmp(param_name, "rtptime")) {
291 0 : sscanf(param_val, "%i", &s_val);
292 0 : info->rtp_time = (s_val>0) ? s_val : 0;
293 : }
294 0 : else if (!stricmp(param_name, "ssrc")) {
295 0 : sscanf(param_val, "%i", &s_val);
296 0 : info->ssrc = (s_val>0) ? s_val : 0;
297 : }
298 : }
299 0 : gf_list_add(rsp->RTP_Infos, info);
300 : }
301 : }
302 : //check for extended attributes
303 6 : else if (!strnicmp(Header, "x-", 2)) {
304 0 : x_Att = (GF_X_Attribute*)gf_malloc(sizeof(GF_X_Attribute));
305 0 : x_Att->Name = gf_strdup(Header+2);
306 0 : x_Att->Value = NULL;
307 0 : if (Value && strlen(Value)) x_Att->Value = gf_strdup(Value);
308 0 : gf_list_add(rsp->Xtensions, x_Att);
309 : }
310 : //unknown field - skip it
311 : }
312 :
313 :
314 :
315 : //parse all fields in the header
316 28 : GF_Err RTSP_ParseResponseHeader(GF_RTSPSession *sess, GF_RTSPResponse *rsp, u32 BodyStart)
317 : {
318 : char LineBuffer[1024];
319 : char ValBuf[400];
320 : char *buffer;
321 : s32 Pos, ret;
322 : u32 Size;
323 :
324 28 : Size = sess->CurrentSize - sess->CurrentPos;
325 28 : buffer = sess->tcp_buffer + sess->CurrentPos;
326 :
327 : //parse first line
328 28 : ret = gf_token_get_line(buffer, 0, Size, LineBuffer, 1024);
329 28 : if (ret < 0)
330 : return GF_REMOTE_SERVICE_ERROR;
331 : //RTSP/1.0
332 28 : Pos = gf_token_get(LineBuffer, 0, " \t\r\n", ValBuf, 400);
333 28 : if (Pos <= 0)
334 : return GF_REMOTE_SERVICE_ERROR;
335 28 : if (strcmp(ValBuf, GF_RTSP_VERSION))
336 : return GF_SERVICE_ERROR;
337 : //CODE
338 28 : Pos = gf_token_get(LineBuffer, Pos, " \t\r\n", ValBuf, 400);
339 28 : if (Pos <= 0)
340 : return GF_REMOTE_SERVICE_ERROR;
341 28 : rsp->ResponseCode = atoi(ValBuf);
342 : //string info
343 28 : Pos = gf_token_get(LineBuffer, Pos, "\t\r\n", ValBuf, 400);
344 28 : if (Pos > 0) rsp->ResponseInfo = gf_strdup(ValBuf);
345 :
346 28 : return gf_rtsp_parse_header(buffer + ret, Size - ret, BodyStart, NULL, rsp);
347 : }
348 :
349 :
350 :
351 28 : u32 IsRTSPMessage(char *buffer)
352 : {
353 28 : if (!buffer) return 0;
354 28 : if (buffer[0]=='$') return 0;
355 :
356 28 : if (!strncmp(buffer, "RTSP", 4)) return 1;
357 0 : if (!strncmp(buffer, "GET_PARAMETER", strlen("GET_PARAMETER"))) return 1;
358 0 : if (!strncmp(buffer, "ANNOUNCE", strlen("ANNOUNCE"))) return 1;
359 0 : if (!strncmp(buffer, "SET_PARAMETER", strlen("SET_PARAMETER"))) return 1;
360 0 : if (!strncmp(buffer, "REDIRECT", strlen("REDIRECT"))) return 1;
361 0 : if (!strncmp(buffer, "OPTIONS", strlen("OPTIONS"))) return 1;
362 0 : return 0;
363 : }
364 :
365 :
366 : GF_EXPORT
367 2007 : GF_Err gf_rtsp_get_response(GF_RTSPSession *sess, GF_RTSPResponse *rsp)
368 : {
369 : GF_Err e;
370 : Bool force_reset = GF_FALSE;
371 : u32 BodyStart, size;
372 :
373 2007 : if (!sess || !rsp) return GF_BAD_PARAM;
374 2007 : gf_rtsp_response_reset(rsp);
375 :
376 2007 : e = gf_rtsp_check_connection(sess);
377 2007 : if (e)
378 : goto exit;
379 :
380 : //push data in our queue
381 2007 : e = gf_rtsp_fill_buffer(sess);
382 2007 : if (e)
383 : goto exit;
384 :
385 : //this is interleaved data
386 28 : if (!IsRTSPMessage(sess->tcp_buffer+sess->CurrentPos) ) {
387 0 : gf_rtsp_session_read(sess);
388 : e = GF_IP_NETWORK_EMPTY;
389 0 : goto exit;
390 : }
391 28 : e = gf_rtsp_read_reply(sess);
392 28 : if (e)
393 : goto exit;
394 :
395 : //get the reply
396 28 : gf_rtsp_get_body_info(sess, &BodyStart, &size);
397 28 : e = RTSP_ParseResponseHeader(sess, rsp, BodyStart);
398 :
399 : //copy the body if any
400 28 : if (!e && rsp->Content_Length) {
401 5 : rsp->body = (char *)gf_malloc(sizeof(char) * (rsp->Content_Length));
402 5 : memcpy(rsp->body, sess->tcp_buffer+sess->CurrentPos + BodyStart, rsp->Content_Length);
403 : }
404 :
405 28 : GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSP] Got Response:\n%s\n", sess->tcp_buffer+sess->CurrentPos));
406 :
407 : //reset TCP buffer
408 28 : sess->CurrentPos += BodyStart + rsp->Content_Length;
409 :
410 28 : if (e) goto exit;
411 :
412 : //update RTSP aggreagation info
413 28 : if (sess->NbPending) sess->NbPending -= 1;
414 :
415 28 : if (sess->RTSP_State == GF_RTSP_STATE_WAITING) sess->RTSP_State = GF_RTSP_STATE_INIT;
416 : //control, and everything is received
417 12 : else if (sess->RTSP_State == GF_RTSP_STATE_WAIT_FOR_CONTROL) {
418 12 : if (!sess->NbPending) sess->RTSP_State = GF_RTSP_STATE_INIT;
419 : }
420 : //this is a late reply to an aggregated control - signal nothing
421 28 : if (!strcmp(sess->RTSPLastRequest, "RESET") && sess->CSeq > rsp->CSeq) {
422 : e = GF_IP_NETWORK_EMPTY;
423 : goto exit;
424 : }
425 :
426 : //reset last request
427 28 : if (sess->RTSP_State == GF_RTSP_STATE_INIT) strcpy(sess->RTSPLastRequest, "");
428 :
429 : //check the CSeq is in the right range. The server should ALWAYS reply in sequence
430 : //to an aggreagated sequence of requests
431 : //if we have reseted the connection (due to an APP error) return empty
432 28 : if (rsp->CSeq && sess->CSeq > rsp->CSeq + sess->NbPending) {
433 0 : return gf_rtsp_get_response(sess, rsp);
434 : }
435 :
436 28 : if (sess->CSeq != rsp->CSeq + sess->NbPending) {
437 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTSP] Invalid sequence number - got %d but expected %d\n", sess->CSeq, rsp->CSeq + sess->NbPending));
438 : e = GF_REMOTE_SERVICE_ERROR;
439 : goto exit;
440 : }
441 :
442 : /*check session ID*/
443 28 : if (rsp->Session && sess->last_session_id && strcmp(sess->last_session_id, rsp->Session)) {
444 : e = GF_REMOTE_SERVICE_ERROR;
445 : goto exit;
446 : }
447 :
448 : //destroy sessionID if needed - real doesn't close the connection when destroying
449 : //session
450 28 : if (!strcmp(sess->RTSPLastRequest, GF_RTSP_TEARDOWN)) {
451 0 : sess->last_session_id = NULL;
452 : }
453 :
454 4014 : exit:
455 2007 : if (rsp->Connection && !stricmp(rsp->Connection, "Close"))
456 : force_reset = GF_TRUE;
457 1983 : else if (e && (e != GF_IP_NETWORK_EMPTY))
458 : force_reset = GF_TRUE;
459 :
460 : if (force_reset) {
461 24 : gf_rtsp_session_reset(sess, GF_FALSE);
462 : //destroy the socket
463 24 : if (sess->connection) gf_sk_del(sess->connection);
464 24 : sess->connection = NULL;
465 :
466 : //destroy the http tunnel if any
467 24 : if (sess->HasTunnel && sess->http) {
468 0 : gf_sk_del(sess->http);
469 0 : sess->http = NULL;
470 : }
471 : }
472 : return e;
473 : }
474 :
475 28 : GF_Err RTSP_WriteResponse(GF_RTSPSession *sess, GF_RTSPResponse *rsp,
476 : unsigned char **out_buffer, u32 *out_size)
477 : {
478 : u32 i, cur_pos, size, count;
479 : char *buffer;
480 :
481 28 : *out_buffer = NULL;
482 :
483 : size = RTSP_WRITE_STEPALLOC;
484 28 : buffer = (char *) gf_malloc(size);
485 : cur_pos = 0;
486 :
487 : //RTSP line
488 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, GF_RTSP_VERSION);
489 28 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, " ");
490 84 : RTSP_WRITE_INT(buffer, size, cur_pos, rsp->ResponseCode, 0);
491 56 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, " ");
492 56 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, gf_rtsp_nc_to_string(rsp->ResponseCode));
493 56 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
494 :
495 : //all headers
496 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept", rsp->Accept);
497 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept-Encoding", rsp->Accept_Encoding);
498 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept-Language", rsp->Accept_Language);
499 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Allow", rsp->Allow);
500 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Authorization", rsp->Authorization);
501 28 : if (rsp->Bandwidth) {
502 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Bandwidth: ");
503 0 : RTSP_WRITE_INT(buffer, size, cur_pos, rsp->Bandwidth, 0);
504 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
505 : }
506 28 : if (rsp->Blocksize) {
507 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Blocksize: ");
508 0 : RTSP_WRITE_INT(buffer, size, cur_pos, rsp->Blocksize, 0);
509 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
510 : }
511 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Cache-Control", rsp->Cache_Control);
512 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Conference", rsp->Conference);
513 124 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Connection", rsp->Connection);
514 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Content-Base", rsp->Content_Base);
515 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Content-Encoding", rsp->Content_Encoding);
516 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Content-Language", rsp->Content_Language);
517 : //if we have a body write the content length
518 28 : if (rsp->body) {
519 10 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Content-Length: ");
520 15 : RTSP_WRITE_INT(buffer, size, cur_pos, (u32) strlen(rsp->body), 0);
521 10 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
522 : }
523 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Content-Location", rsp->Content_Location);
524 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Content-Type", rsp->Content_Type);
525 : //write the CSeq - use the RESPONSE CSeq
526 56 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "CSeq: ");
527 84 : RTSP_WRITE_INT(buffer, size, cur_pos, rsp->CSeq, 0);
528 56 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
529 :
530 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Date", rsp->Date);
531 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Expires", rsp->Expires);
532 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "From", rsp->From);
533 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Host", rsp->Host);
534 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "If-Match", rsp->If_Match);
535 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "If-Modified-Since", rsp->If_Modified_Since);
536 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Last-Modified", rsp->Last_Modified);
537 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Location", rsp->Location);
538 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Proxy-Authenticate", rsp->Proxy_Authenticate);
539 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Proxy-Require", rsp->Proxy_Require);
540 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Public", rsp->Public);
541 :
542 : //Range, only NPT
543 28 : if (rsp->Range && !rsp->Range->UseSMPTE) {
544 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Range: npt:");
545 18 : RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, rsp->Range->start);
546 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
547 6 : if (rsp->Range->end > rsp->Range->start) {
548 0 : RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, rsp->Range->end);
549 : }
550 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
551 : }
552 :
553 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Referer", rsp->Referer);
554 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Require", rsp->Require);
555 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Retry-After", rsp->Retry_After);
556 :
557 : //RTP Infos
558 28 : count = gf_list_count(rsp->RTP_Infos);
559 28 : if (count) {
560 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "RTPInfo: ");
561 :
562 12 : for (i=0; i<count; i++) {
563 : GF_RTPInfo *info;
564 : //line separator for headers
565 6 : if (i) RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n ,");
566 6 : info = (GF_RTPInfo*)gf_list_get(rsp->RTP_Infos, i);
567 :
568 6 : if (info->url) {
569 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "url=");
570 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, info->url);
571 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";");
572 : }
573 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "seq=");
574 18 : RTSP_WRITE_INT(buffer, size, cur_pos, info->seq, 0);
575 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";rtptime=");
576 18 : RTSP_WRITE_INT(buffer, size, cur_pos, info->rtp_time, 0);
577 : }
578 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
579 : }
580 :
581 28 : if (rsp->Scale != 0.0) {
582 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Scale: ");
583 0 : RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, rsp->Scale);
584 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
585 : }
586 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Server", rsp->Server);
587 120 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Session", rsp->Session);
588 28 : if (rsp->Speed != 0.0) {
589 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Scale: ");
590 0 : RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, rsp->Speed);
591 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
592 : }
593 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Timestamp", rsp->Timestamp);
594 :
595 : //transport info
596 28 : count = gf_list_count(rsp->Transports);
597 28 : if (count) {
598 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Transport: ");
599 12 : for (i=0; i<count; i++) {
600 : GF_RTSPTransport *trans;
601 : //line separator for headers
602 6 : if (i) RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n ,");
603 6 : trans = (GF_RTSPTransport*)gf_list_get(rsp->Transports, i);
604 :
605 : //then write the structure
606 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->Profile);
607 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, (trans->IsUnicast ? ";unicast" : ";multicast"));
608 6 : if (trans->destination) {
609 2 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";destination=");
610 2 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->destination);
611 : }
612 6 : if (trans->source) {
613 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";source=");
614 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->source);
615 : }
616 6 : if (trans->IsRecord) {
617 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";mode=RECORD");
618 0 : if (trans->Append) RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";append");
619 : }
620 6 : if (trans->IsInterleaved) {
621 2 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";interleaved=");
622 3 : RTSP_WRITE_INT(buffer, size, cur_pos, trans->rtpID, 0);
623 1 : if (trans->rtcpID != trans->rtpID) {
624 2 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
625 3 : RTSP_WRITE_INT(buffer, size, cur_pos, trans->rtcpID, 0);
626 : }
627 : }
628 : //multicast specific
629 6 : if (!trans->IsUnicast) {
630 1 : if (trans->MulticastLayers) {
631 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";layers=");
632 0 : RTSP_WRITE_INT(buffer, size, cur_pos, trans->MulticastLayers, 0);
633 : }
634 1 : if (trans->TTL) {
635 2 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";ttl=");
636 3 : RTSP_WRITE_INT(buffer, size, cur_pos, trans->TTL, 0);
637 : }
638 : }
639 6 : if (trans->port_first) {
640 10 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, (const char *) (trans->IsUnicast ? ";server_port=" : ";port="));
641 15 : RTSP_WRITE_INT(buffer, size, cur_pos, trans->port_first, 0);
642 10 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
643 15 : RTSP_WRITE_INT(buffer, size, cur_pos, trans->port_last, 0);
644 : }
645 6 : if (trans->IsUnicast && trans->client_port_first) {
646 8 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";client_port=");
647 12 : RTSP_WRITE_INT(buffer, size, cur_pos, trans->client_port_first, 0);
648 8 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
649 12 : RTSP_WRITE_INT(buffer, size, cur_pos, trans->client_port_last, 0);
650 : }
651 6 : if (trans->SSRC) {
652 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";ssrc=");
653 18 : RTSP_WRITE_HEX(buffer, size, cur_pos, trans->SSRC, 0);
654 : }
655 : }
656 : //done with transport
657 12 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
658 : }
659 :
660 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Unsupported", rsp->Unsupported);
661 140 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "User-Agent", rsp->User_Agent);
662 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Vary", rsp->Vary);
663 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "Via", rsp->Via);
664 28 : RTSP_WRITE_HEADER(buffer, size, cur_pos, "WWW-Authenticate", rsp->WWW_Authenticate);
665 :
666 : //eXtensions
667 28 : count = gf_list_count(rsp->Xtensions);
668 28 : for (i=0; i<count; i++) {
669 0 : GF_X_Attribute *att = (GF_X_Attribute*)gf_list_get(rsp->Xtensions, i);
670 0 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "x-");
671 0 : RTSP_WRITE_HEADER(buffer, size, cur_pos, att->Name, att->Value);
672 : }
673 : //end of header
674 56 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
675 : //then body
676 28 : RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, rsp->body);
677 :
678 28 : *out_buffer = (unsigned char *) buffer;
679 28 : *out_size = (u32) strlen(buffer);
680 28 : return GF_OK;
681 : }
682 :
683 :
684 : GF_EXPORT
685 28 : GF_Err gf_rtsp_send_response(GF_RTSPSession *sess, GF_RTSPResponse *rsp)
686 : {
687 : u32 size;
688 : char *buffer;
689 : GF_Err e;
690 :
691 28 : if (!sess || !rsp || !rsp->CSeq) return GF_BAD_PARAM;
692 :
693 : //check we're not sending something greater than the current CSeq
694 28 : if (rsp->CSeq > sess->CSeq) return GF_BAD_PARAM;
695 :
696 28 : e = RTSP_WriteResponse(sess, rsp, (unsigned char **) &buffer, &size);
697 28 : if (!e) {
698 : //send buffer
699 28 : e = gf_rtsp_send_data(sess, buffer, size);
700 : }
701 28 : if (buffer) gf_free(buffer);
702 : return e;
703 : }
704 :
705 : #endif /*GPAC_DISABLE_STREAMING*/
|