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 56 : GF_Err gf_rtsp_read_reply(GF_RTSPSession *sess)
34 : {
35 : GF_Err e;
36 56 : u32 res, body_size = 0;
37 56 : u32 BodyStart = 0;
38 :
39 : //fetch more data on the socket if needed
40 : while (1) {
41 : //Locate header / body
42 56 : if (!BodyStart) gf_rtsp_get_body_info(sess, &BodyStart, &body_size);
43 :
44 56 : if (BodyStart) {
45 : //enough data
46 56 : res = sess->CurrentSize - sess->CurrentPos;
47 56 : if (!body_size || (res >= body_size + BodyStart)) {
48 : //done
49 : break;
50 : }
51 : }
52 : //this is the tricky part: if we do NOT have a body start -> we refill
53 0 : e = gf_rtsp_refill_buffer(sess);
54 0 : if (e) return e;
55 : }
56 : return GF_OK;
57 : }
58 :
59 112 : void gf_rtsp_get_body_info(GF_RTSPSession *sess, u32 *body_start, u32 *body_size)
60 : {
61 : u32 i;
62 : s32 start;
63 : char *buffer;
64 : char *cl_str;
65 :
66 112 : *body_start = *body_size = 0;
67 :
68 112 : buffer = sess->tcp_buffer + sess->CurrentPos;
69 112 : start = gf_token_find(buffer, 0, sess->CurrentSize - sess->CurrentPos, "\r\n\r\n");
70 112 : if (start<=0) {
71 : return;
72 : }
73 :
74 : //if found add the 2 "\r\n" and parse it
75 112 : *body_start = start + 4;
76 :
77 : //get the content length
78 112 : cl_str = strstr(buffer, "Content-Length: ");
79 112 : if (!cl_str) cl_str = strstr(buffer, "Content-length: ");
80 :
81 112 : if (cl_str) {
82 : char val[30];
83 10 : cl_str += 16;
84 : i = 0;
85 50 : while (cl_str[i] != '\r') {
86 30 : val[i] = cl_str[i];
87 30 : i += 1;
88 : }
89 10 : val[i] = 0;
90 10 : *body_size = atoi(val);
91 : } else {
92 102 : *body_size = 0;
93 : }
94 : }
95 :
96 :
97 1 : GF_Err gf_rtsp_refill_buffer(GF_RTSPSession *sess)
98 : {
99 : GF_Err e;
100 : u32 res;
101 : char *ptr;
102 :
103 1 : if (!sess) return GF_BAD_PARAM;
104 1 : if (!sess->connection) return GF_IP_NETWORK_EMPTY;
105 :
106 1 : res = sess->CurrentSize - sess->CurrentPos;
107 1 : if (!res) return gf_rtsp_fill_buffer(sess);
108 :
109 1 : ptr = (char *)gf_malloc(sizeof(char) * res);
110 1 : memcpy(ptr, sess->tcp_buffer + sess->CurrentPos, res);
111 1 : memcpy(sess->tcp_buffer, ptr, res);
112 1 : gf_free(ptr);
113 :
114 1 : sess->CurrentPos = 0;
115 1 : sess->CurrentSize = res;
116 :
117 : //now read from current pos
118 1 : e = gf_sk_receive(sess->connection, sess->tcp_buffer + sess->CurrentSize, sess->SockBufferSize - sess->CurrentSize, &res);
119 :
120 1 : if (!e) {
121 1 : sess->CurrentSize += res;
122 : }
123 : return e;
124 : }
125 :
126 :
127 2862 : GF_Err gf_rtsp_fill_buffer(GF_RTSPSession *sess)
128 : {
129 : GF_Err e = GF_OK;
130 :
131 2862 : if (!sess->connection) return GF_IP_NETWORK_EMPTY;
132 :
133 2837 : if (sess->CurrentSize == sess->CurrentPos) {
134 2835 : e = gf_sk_receive(sess->connection, sess->tcp_buffer, sess->SockBufferSize, &sess->CurrentSize);
135 2835 : sess->CurrentPos = 0;
136 2835 : sess->tcp_buffer[sess->CurrentSize] = 0;
137 2835 : if (e) sess->CurrentSize = 0;
138 2 : } else if (!sess->CurrentSize) e = GF_IP_NETWORK_EMPTY;
139 : return e;
140 : }
141 :
142 :
143 12 : GF_RTSPTransport *gf_rtsp_transport_parse(u8 *buffer)
144 : {
145 : Bool IsFirst;
146 : char buf[100], param_name[100], param_val[100];
147 : s32 pos, nPos;
148 : u32 v1, v2;
149 : GF_RTSPTransport *tmp;
150 12 : if (!buffer) return NULL;
151 : //only support for RTP/AVP for now
152 12 : if (strnicmp(buffer, "RTP/AVP", 7) && strnicmp(buffer, "RTP/SAVP", 8)) return NULL;
153 :
154 12 : GF_SAFEALLOC(tmp, GF_RTSPTransport);
155 12 : if (!tmp) return NULL;
156 :
157 : IsFirst = GF_TRUE;
158 : pos = 0;
159 : while (1) {
160 69 : pos = gf_token_get(buffer, pos, " ;", buf, 100);
161 69 : if (pos <= 0) break;
162 57 : if (strstr(buf, "=")) {
163 33 : nPos = gf_token_get(buf, 0, "=", param_name, 100);
164 33 : /*nPos = */gf_token_get(buf, nPos, "=", param_val, 100);
165 : } else {
166 : strcpy(param_name, buf);
167 : }
168 :
169 : //very first param is the profile
170 57 : if (IsFirst) {
171 12 : tmp->Profile = gf_strdup(param_name);
172 : IsFirst = GF_FALSE;
173 12 : continue;
174 : }
175 :
176 45 : if (!stricmp(param_name, "destination")) {
177 2 : if (tmp->destination) gf_free(tmp->destination);
178 2 : tmp->destination = gf_strdup(param_val);
179 : }
180 43 : else if (!stricmp(param_name, "source")) {
181 6 : if (tmp->source) gf_free(tmp->source);
182 6 : tmp->source = gf_strdup(param_val);
183 : }
184 37 : else if (!stricmp(param_name, "unicast")) tmp->IsUnicast = GF_TRUE;
185 27 : else if (!stricmp(param_name, "RECORD")) tmp->IsRecord = GF_TRUE;
186 27 : else if (!stricmp(param_name, "append")) tmp->Append = GF_TRUE;
187 27 : else if (!stricmp(param_name, "interleaved")) {
188 : u32 rID, rcID;
189 2 : tmp->IsInterleaved = GF_TRUE;
190 2 : if (sscanf(param_val, "%u-%u", &rID, &rcID) == 1) {
191 0 : sscanf(param_val, "%u", &rID);
192 0 : tmp->rtcpID = tmp->rtpID = (u8) rID;
193 : } else {
194 2 : tmp->rtpID = (u8) rID;
195 2 : tmp->rtcpID = (u8) rcID;
196 : }
197 : }
198 25 : else if (!stricmp(param_name, "layers")) sscanf(param_val, "%u", &tmp->MulticastLayers);
199 25 : else if (!stricmp(param_name, "ttl")) sscanf(param_val, "%c ", &tmp->TTL);
200 23 : else if (!stricmp(param_name, "port")) {
201 2 : sscanf(param_val, "%u-%u", &v1, &v2);
202 2 : tmp->port_first = (u16) v1;
203 2 : tmp->port_last = (u16) v2;
204 : }
205 : /*do not use %hud here, broken on Win32 (sscanf returns 1)*/
206 21 : else if (!stricmp(param_name, "server_port")) {
207 4 : sscanf(param_val, "%d-%d", &v1, &v2);
208 4 : tmp->port_first = (u16) v1;
209 4 : tmp->port_last = (u16) v2;
210 : }
211 : /*do not use %hud here, broken on Win32 (sscanf returns 1)*/
212 17 : else if (!stricmp(param_name, "client_port")) {
213 9 : sscanf(param_val, "%d-%d", &v1, &v2);
214 9 : tmp->client_port_first = (u16) v1;
215 9 : tmp->client_port_last = (u16) v2;
216 : }
217 8 : else if (!stricmp(param_name, "ssrc")) sscanf(param_val, "%X", &tmp->SSRC);
218 : }
219 : return tmp;
220 : }
221 :
222 :
223 :
224 56 : GF_Err gf_rtsp_parse_header(u8 *buffer, u32 BufferSize, u32 BodyStart, GF_RTSPCommand *com, GF_RTSPResponse *rsp)
225 : {
226 : char LineBuffer[1024];
227 : char HeaderBuf[100], ValBuf[1024], temp[400];
228 : s32 Pos, LinePos;
229 : u32 HeaderLine;
230 :
231 : //then parse the full header
232 : LinePos = 0;
233 : strcpy(HeaderBuf, "");
234 : while (1) {
235 307 : LinePos = gf_token_get_line(buffer, LinePos, BufferSize, LineBuffer, 1024);
236 307 : if (LinePos <= 0) return GF_REMOTE_SERVICE_ERROR;
237 :
238 : //extract field header and value. Warning: some params (transport, ..) may be on several lines
239 307 : Pos = gf_token_get(LineBuffer, 0, ":\r\n", temp, 400);
240 :
241 : //end of header
242 307 : if (Pos <= 0) {
243 : HeaderLine = 2;
244 : }
245 : //this is a header
246 251 : else if (LineBuffer[0] != ' ') {
247 : HeaderLine = 1;
248 : } else {
249 0 : Pos = gf_token_get(LineBuffer, 0, ", \r\n", temp, 400);
250 : //end of header - process any pending one
251 0 : if (Pos <= 0) {
252 : HeaderLine = 2;
253 : } else {
254 : //n-line value - append
255 : strcat(ValBuf, "\r\n");
256 : strcat(ValBuf, temp);
257 0 : continue;
258 : }
259 : }
260 : //process current value
261 307 : if (HeaderLine && strlen(HeaderBuf)) {
262 251 : if (rsp) {
263 126 : gf_rtsp_set_response_value(rsp, HeaderBuf, ValBuf);
264 : }
265 : else {
266 125 : gf_rtsp_set_command_value(com, HeaderBuf, ValBuf);
267 : }
268 : }
269 : //done with the header
270 307 : if ( (HeaderLine == 2) || ((u32) LinePos >= BodyStart) ) return GF_OK;
271 :
272 : //process current line
273 : strcpy(HeaderBuf, temp);
274 :
275 : //skip ':'
276 251 : Pos += 1;
277 : //a server should normally reply with a space, but check it
278 251 : if (LineBuffer[Pos] == ' ') Pos += 1;
279 : /*!! empty value !! - DSS may send these for CSeq if something goes wrong*/
280 251 : if (!strcmp(LineBuffer+Pos, "\r\n")) {
281 0 : HeaderBuf[0] = 0;
282 0 : continue;
283 : }
284 251 : Pos = gf_token_get(LineBuffer, Pos, "\r\n", ValBuf, 400);
285 251 : if (Pos <= 0) break;
286 :
287 : }
288 : //if we get here we haven't reached the BodyStart
289 : return GF_REMOTE_SERVICE_ERROR;
290 : }
291 :
292 :
293 : GF_EXPORT
294 112 : const char *gf_rtsp_nc_to_string(u32 ErrCode)
295 : {
296 112 : switch (ErrCode) {
297 : case NC_RTSP_Continue:
298 : return "Continue";
299 112 : case NC_RTSP_OK:
300 112 : return "OK";
301 0 : case NC_RTSP_Created:
302 0 : return "Created";
303 0 : case NC_RTSP_Low_on_Storage_Space:
304 0 : return "Low on Storage Space";
305 0 : case NC_RTSP_Multiple_Choice:
306 0 : return "Multiple Choice";
307 0 : case NC_RTSP_Moved_Permanently:
308 0 : return "Moved Permanently";
309 0 : case NC_RTSP_Moved_Temporarily:
310 0 : return "Moved Temporarily";
311 0 : case NC_RTSP_See_Other:
312 0 : return "See Other";
313 0 : case NC_RTSP_Use_Proxy:
314 0 : return "Use Proxy";
315 0 : case NC_RTSP_Bad_Request:
316 0 : return "Bad Request";
317 0 : case NC_RTSP_Unauthorized:
318 0 : return "Unauthorized";
319 0 : case NC_RTSP_Payment_Required:
320 0 : return "Payment Required";
321 0 : case NC_RTSP_Forbidden:
322 0 : return "Forbidden";
323 0 : case NC_RTSP_Not_Found:
324 0 : return "Not Found";
325 0 : case NC_RTSP_Method_Not_Allowed:
326 0 : return "Method Not Allowed";
327 0 : case NC_RTSP_Not_Acceptable:
328 0 : return "Not Acceptable";
329 0 : case NC_RTSP_Proxy_Authentication_Required:
330 0 : return "Proxy Authentication Required";
331 0 : case NC_RTSP_Request_Timeout:
332 0 : return "Request Timeout";
333 0 : case NC_RTSP_Gone:
334 0 : return "Gone";
335 0 : case NC_RTSP_Length_Required:
336 0 : return "Length Required";
337 0 : case NC_RTSP_Precondition_Failed:
338 0 : return "Precondition Failed";
339 0 : case NC_RTSP_Request_Entity_Too_Large:
340 0 : return "Request Entity Too Large";
341 0 : case NC_RTSP_Request_URI_Too_Long:
342 0 : return "Request URI Too Long";
343 0 : case NC_RTSP_Unsupported_Media_Type:
344 0 : return "Unsupported Media Type";
345 0 : case NC_RTSP_Invalid_parameter:
346 0 : return "Invalid parameter";
347 0 : case NC_RTSP_Illegal_Conference_Identifier:
348 0 : return "Illegal Conference Identifier";
349 0 : case NC_RTSP_Not_Enough_Bandwidth:
350 0 : return "Not Enough Bandwidth";
351 0 : case NC_RTSP_Session_Not_Found:
352 0 : return "Session Not Found";
353 0 : case NC_RTSP_Method_Not_Valid_In_This_State:
354 0 : return "Method Not Valid In This State";
355 0 : case NC_RTSP_Header_Field_Not_Valid:
356 0 : return "Header Field Not Valid";
357 0 : case NC_RTSP_Invalid_Range:
358 0 : return "Invalid Range";
359 0 : case NC_RTSP_Parameter_Is_ReadOnly:
360 0 : return "Parameter Is Read-Only";
361 0 : case NC_RTSP_Aggregate_Operation_Not_Allowed:
362 0 : return "Aggregate Operation Not Allowed";
363 0 : case NC_RTSP_Only_Aggregate_Operation_Allowed:
364 0 : return "Only Aggregate Operation Allowed";
365 0 : case NC_RTSP_Unsupported_Transport:
366 0 : return "Unsupported Transport";
367 0 : case NC_RTSP_Destination_Unreachable:
368 0 : return "Destination Unreachable";
369 0 : case NC_RTSP_Internal_Server_Error:
370 0 : return "Internal Server Error";
371 0 : case NC_RTSP_Bad_Gateway:
372 0 : return "Bad Gateway";
373 0 : case NC_RTSP_Service_Unavailable:
374 0 : return "Service Unavailable";
375 0 : case NC_RTSP_Gateway_Timeout:
376 0 : return "Gateway Timeout";
377 0 : case NC_RTSP_RTSP_Version_Not_Supported:
378 0 : return "RTSP Version Not Supported";
379 0 : case NC_RTSP_Option_not_support:
380 0 : return "Option not support";
381 :
382 0 : case NC_RTSP_Not_Implemented:
383 : default:
384 0 : return "Not Implemented";
385 : }
386 : }
387 :
388 : #endif /*GPAC_DISABLE_STREAMING*/
|