Mercurial > mplayer.hg
annotate libmpdemux/realrtsp/rtsp.c @ 17440:de9a36b1082c
MPLAYER_VERBOSE, new enviorment variable to control verbosity before init
author | ods15 |
---|---|
date | Fri, 20 Jan 2006 20:38:46 +0000 |
parents | 88adbc28f60b |
children | 492f442c11af |
rev | line source |
---|---|
9922 | 1 /* |
2 * This file was ported to MPlayer from xine CVS rtsp.c,v 1.9 2003/04/10 02:30:48 | |
3 */ | |
4 | |
5 /* | |
6 * Copyright (C) 2000-2002 the xine project | |
7 * | |
8 * This file is part of xine, a free video player. | |
9 * | |
10 * xine is free software; you can redistribute it and/or modify | |
11 * it under the terms of the GNU General Public License as published by | |
12 * the Free Software Foundation; either version 2 of the License, or | |
13 * (at your option) any later version. | |
14 * | |
15 * xine 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 General Public License for more details. | |
19 * | |
20 * You should have received a copy of the GNU General Public License | |
21 * along with this program; if not, write to the Free Software | |
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | |
23 * | |
24 * | |
25 * a minimalistic implementation of rtsp protocol, | |
26 * *not* RFC 2326 compilant yet. | |
27 */ | |
28 | |
29 #include <unistd.h> | |
30 #include <stdio.h> | |
31 #include <assert.h> | |
10281 | 32 #include "config.h" |
33 #ifndef HAVE_WINSOCK2 | |
34 #define closesocket close | |
9922 | 35 #include <sys/socket.h> |
36 #include <netinet/in.h> | |
37 #include <netdb.h> | |
10281 | 38 #else |
39 #include <winsock2.h> | |
40 #endif | |
9922 | 41 #include <string.h> |
42 #include <sys/stat.h> | |
43 #include <fcntl.h> | |
44 #include <errno.h> | |
45 #include <stdlib.h> | |
46 #include <time.h> | |
47 #include <sys/time.h> | |
48 #include <sys/types.h> | |
9939
6b88f67564ce
Fix compilation for *BSD, Mac OS X and maybe others (info by Steven M. Schultz and Dan Christiansen)
rtognimp
parents:
9922
diff
changeset
|
49 #include <inttypes.h> |
9922 | 50 |
51 #include "rtsp.h" | |
15585 | 52 #include "../stream.h" |
53 #include "../demuxer.h" | |
54 #include "rtsp_session.h" | |
17092 | 55 #include "osdep/timer.h" |
9922 | 56 |
57 /* | |
58 #define LOG | |
59 */ | |
60 | |
61 #define BUF_SIZE 4096 | |
62 #define HEADER_SIZE 1024 | |
63 #define MAX_FIELDS 256 | |
64 | |
15585 | 65 extern int network_bandwidth; |
9922 | 66 struct rtsp_s { |
67 | |
68 int s; | |
69 | |
70 char *host; | |
71 int port; | |
72 char *path; | |
11506
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
73 char *param; |
9922 | 74 char *mrl; |
75 char *user_agent; | |
76 | |
77 char *server; | |
78 unsigned int server_state; | |
79 uint32_t server_caps; | |
80 | |
81 unsigned int cseq; | |
82 char *session; | |
83 | |
84 char *answers[MAX_FIELDS]; /* data of last message */ | |
85 char *scheduled[MAX_FIELDS]; /* will be sent with next message */ | |
86 }; | |
87 | |
88 /* | |
89 * constants | |
90 */ | |
91 | |
92 const char rtsp_protocol_version[]="RTSP/1.0"; | |
93 | |
94 /* server states */ | |
95 #define RTSP_CONNECTED 1 | |
96 #define RTSP_INIT 2 | |
97 #define RTSP_READY 4 | |
98 #define RTSP_PLAYING 8 | |
99 #define RTSP_RECORDING 16 | |
100 | |
101 /* server capabilities */ | |
102 #define RTSP_OPTIONS 0x001 | |
103 #define RTSP_DESCRIBE 0x002 | |
104 #define RTSP_ANNOUNCE 0x004 | |
105 #define RTSP_SETUP 0x008 | |
106 #define RTSP_GET_PARAMETER 0x010 | |
107 #define RTSP_SET_PARAMETER 0x020 | |
108 #define RTSP_TEARDOWN 0x040 | |
109 #define RTSP_PLAY 0x080 | |
110 #define RTSP_RECORD 0x100 | |
111 | |
112 /* | |
113 * network utilities | |
114 */ | |
115 | |
116 static int host_connect_attempt(struct in_addr ia, int port) { | |
117 | |
118 int s; | |
119 struct sockaddr_in sin; | |
120 | |
121 s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); | |
122 if (s == -1) { | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
123 mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: socket(): %s\n", strerror(errno)); |
9922 | 124 return -1; |
125 } | |
126 | |
127 sin.sin_family = AF_INET; | |
128 sin.sin_addr = ia; | |
129 sin.sin_port = htons(port); | |
130 | |
131 if (connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1 | |
10281 | 132 #ifndef HAVE_WINSOCK2 |
9922 | 133 && errno != EINPROGRESS) { |
10281 | 134 #else |
135 && WSAGetLastError() == WSAEINPROGRESS) { | |
136 #endif | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
137 mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: connect(): %s\n", strerror(errno)); |
10281 | 138 closesocket(s); |
9922 | 139 return -1; |
140 } | |
141 | |
142 return s; | |
143 } | |
144 | |
145 static int host_connect(const char *host, int port) { | |
146 | |
147 struct hostent *h; | |
148 int i, s; | |
149 | |
150 h = gethostbyname(host); | |
151 if (h == NULL) { | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
152 mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: unable to resolve '%s'.\n", host); |
9922 | 153 return -1; |
154 } | |
155 | |
156 for (i = 0; h->h_addr_list[i]; i++) { | |
157 struct in_addr ia; | |
158 | |
159 memcpy (&ia, h->h_addr_list[i], 4); | |
160 s = host_connect_attempt(ia, port); | |
161 if(s != -1) | |
162 return s; | |
163 } | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
164 mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: unable to connect to '%s'.\n", host); |
9922 | 165 return -1; |
166 } | |
167 | |
168 static int write_stream(int s, const char *buf, int len) { | |
169 int total, timeout; | |
170 | |
171 total = 0; timeout = 30; | |
172 while (total < len){ | |
173 int n; | |
174 | |
10206
35e306346e59
Using recv/send instead read/write for proper MinGW support (it's a 4.2BSD standard). Patch by FloDt <flodt8@yahoo.de>
alex
parents:
9939
diff
changeset
|
175 n = send (s, &buf[total], len - total, 0); |
9922 | 176 |
177 if (n > 0) | |
178 total += n; | |
179 else if (n < 0) { | |
10281 | 180 #ifndef HAVE_WINSOCK2 |
9922 | 181 if ((timeout>0) && ((errno == EAGAIN) || (errno == EINPROGRESS))) { |
10281 | 182 #else |
183 if ((timeout>0) && ((errno == EAGAIN) || (WSAGetLastError() == WSAEINPROGRESS))) { | |
184 #endif | |
16372
b313a38c69cb
replace sleep with usec_sleep, required for recent mingw versions, patch by Robert Swain <robert.swain at gmail.com>
faust3
parents:
15797
diff
changeset
|
185 usec_sleep (1000000); timeout--; |
9922 | 186 } else |
187 return -1; | |
188 } | |
189 } | |
190 | |
191 return total; | |
192 } | |
193 | |
194 static ssize_t read_stream(int fd, void *buf, size_t count) { | |
195 | |
196 ssize_t ret, total; | |
197 | |
198 total = 0; | |
199 | |
200 while (total < count) { | |
201 | |
10206
35e306346e59
Using recv/send instead read/write for proper MinGW support (it's a 4.2BSD standard). Patch by FloDt <flodt8@yahoo.de>
alex
parents:
9939
diff
changeset
|
202 ret=recv (fd, ((uint8_t*)buf)+total, count-total, 0); |
9922 | 203 |
204 if (ret<0) { | |
205 if(errno == EAGAIN) { | |
206 fd_set rset; | |
207 struct timeval timeout; | |
208 | |
209 FD_ZERO (&rset); | |
210 FD_SET (fd, &rset); | |
211 | |
212 timeout.tv_sec = 30; | |
213 timeout.tv_usec = 0; | |
214 | |
215 if (select (fd+1, &rset, NULL, NULL, &timeout) <= 0) { | |
216 return -1; | |
217 } | |
218 continue; | |
219 } | |
220 | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
221 mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: read error.\n"); |
9922 | 222 return ret; |
223 } else | |
224 total += ret; | |
225 | |
226 /* end of stream */ | |
227 if (!ret) break; | |
228 } | |
229 | |
230 return total; | |
231 } | |
232 | |
233 /* | |
234 * debugging utilities | |
235 */ | |
236 #if 0 | |
237 static void hexdump (char *buf, int length) { | |
238 | |
239 int i; | |
240 | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
241 mp_msg(MSGT_OPEN, MSGL_INFO, "rtsp: ascii>"); |
9922 | 242 for (i = 0; i < length; i++) { |
243 unsigned char c = buf[i]; | |
244 | |
245 if ((c >= 32) && (c <= 128)) | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
246 mp_msg(MSGT_OPEN, MSGL_INFO, "%c", c); |
9922 | 247 else |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
248 mp_msg(MSGT_OPEN, MSGL_INFO, "."); |
9922 | 249 } |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
250 mp_msg(MSGT_OPEN, MSGL_INFO, "\n"); |
9922 | 251 |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
252 mp_msg(MSGT_OPEN, MSGL_INFO, "rtsp: hexdump> "); |
9922 | 253 for (i = 0; i < length; i++) { |
254 unsigned char c = buf[i]; | |
255 | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
256 mp_msg(MSGT_OPEN, MSGL_INFO, "%02x", c); |
9922 | 257 |
258 if ((i % 16) == 15) | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
259 mp_msg(MSGT_OPEN, MSGL_INFO, "\nrtsp: "); |
9922 | 260 |
261 if ((i % 2) == 1) | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
262 mp_msg(MSGT_OPEN, MSGL_INFO, " "); |
9922 | 263 |
264 } | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
265 mp_msg(MSGT_OPEN, MSGL_INFO, "\n"); |
9922 | 266 } |
267 #endif | |
268 | |
269 /* | |
270 * rtsp_get gets a line from stream | |
271 * and returns a null terminated string. | |
272 */ | |
273 | |
274 static char *rtsp_get(rtsp_t *s) { | |
275 | |
15797 | 276 int n=1; |
12271
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
277 char *buffer = malloc(BUF_SIZE); |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
278 char *string = NULL; |
9922 | 279 |
15797 | 280 read_stream(s->s, buffer, 1); |
9922 | 281 while (n<BUF_SIZE) { |
12271
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
282 read_stream(s->s, &(buffer[n]), 1); |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
283 if ((buffer[n-1]==0x0d)&&(buffer[n]==0x0a)) break; |
9922 | 284 n++; |
285 } | |
286 | |
287 if (n>=BUF_SIZE) { | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
288 mp_msg(MSGT_OPEN, MSGL_FATAL, "librtsp: buffer overflow in rtsp_get\n"); |
9922 | 289 exit(1); |
290 } | |
291 string=malloc(sizeof(char)*n); | |
12271
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
292 memcpy(string,buffer,n-1); |
9922 | 293 string[n-1]=0; |
294 | |
295 #ifdef LOG | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
296 mp_msg(MSGT_OPEN, MSGL_INFO, "librtsp: << '%s'\n", string); |
9922 | 297 #endif |
298 | |
299 | |
12271
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
300 free(buffer); |
9922 | 301 return string; |
302 } | |
303 | |
304 /* | |
305 * rtsp_put puts a line on stream | |
306 */ | |
307 | |
308 static void rtsp_put(rtsp_t *s, const char *string) { | |
309 | |
310 int len=strlen(string); | |
311 char *buf=malloc(sizeof(char)*len+2); | |
312 | |
313 #ifdef LOG | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
314 mp_msg(MSGT_OPEN, MSGL_INFO, "librtsp: >> '%s'", string); |
9922 | 315 #endif |
316 | |
317 memcpy(buf,string,len); | |
318 buf[len]=0x0d; | |
319 buf[len+1]=0x0a; | |
320 | |
321 write_stream(s->s, buf, len+2); | |
322 | |
323 #ifdef LOG | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
324 mp_msg(MSGT_OPEN, MSGL_INFO, " done.\n"); |
9922 | 325 #endif |
326 | |
327 free(buf); | |
328 } | |
329 | |
330 /* | |
331 * extract server status code | |
332 */ | |
333 | |
334 static int rtsp_get_code(const char *string) { | |
335 | |
336 char buf[4]; | |
337 int code=0; | |
338 | |
339 if (!strncmp(string, rtsp_protocol_version, strlen(rtsp_protocol_version))) | |
340 { | |
341 memcpy(buf, string+strlen(rtsp_protocol_version)+1, 3); | |
342 buf[3]=0; | |
343 code=atoi(buf); | |
344 } else if (!strncmp(string, "SET_PARAMETER",8)) | |
345 { | |
346 return RTSP_STATUS_SET_PARAMETER; | |
347 } | |
348 | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
349 if(code != 200) mp_msg(MSGT_OPEN, MSGL_INFO, "librtsp: server responds: '%s'\n",string); |
9922 | 350 |
351 return code; | |
352 } | |
353 | |
354 /* | |
355 * send a request | |
356 */ | |
357 | |
358 static void rtsp_send_request(rtsp_t *s, const char *type, const char *what) { | |
359 | |
360 char **payload=s->scheduled; | |
12271
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
361 char *buf; |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
362 |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
363 buf = malloc(strlen(type)+strlen(what)+strlen(rtsp_protocol_version)+3); |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
364 |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
365 sprintf(buf,"%s %s %s",type, what, rtsp_protocol_version); |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
366 rtsp_put(s,buf); |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
367 free(buf); |
9922 | 368 if (payload) |
369 while (*payload) { | |
370 rtsp_put(s,*payload); | |
371 payload++; | |
372 } | |
373 rtsp_put(s,""); | |
374 rtsp_unschedule_all(s); | |
375 } | |
376 | |
377 /* | |
378 * schedule standard fields | |
379 */ | |
380 | |
381 static void rtsp_schedule_standard(rtsp_t *s) { | |
382 | |
12271
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
383 char tmp[16]; |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
384 |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
385 snprintf(tmp, 16, "Cseq: %u", s->cseq); |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
386 rtsp_schedule_field(s, tmp); |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
387 |
9922 | 388 if (s->session) { |
12271
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
389 char *buf; |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
390 buf = malloc(strlen(s->session)+15); |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
391 sprintf(buf, "Session: %s", s->session); |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
392 rtsp_schedule_field(s, buf); |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
393 free(buf); |
9922 | 394 } |
395 } | |
396 /* | |
397 * get the answers, if server responses with something != 200, return NULL | |
398 */ | |
399 | |
400 static int rtsp_get_answers(rtsp_t *s) { | |
401 | |
402 char *answer=NULL; | |
403 unsigned int answer_seq; | |
404 char **answer_ptr=s->answers; | |
405 int code; | |
15172
56541efe420b
Fix potential buffer overflow if server answers with too many lines
rtognimp
parents:
13677
diff
changeset
|
406 int ans_count = 0; |
9922 | 407 |
408 answer=rtsp_get(s); | |
12271
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
409 if (!answer) |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
410 return 0; |
9922 | 411 code=rtsp_get_code(answer); |
412 free(answer); | |
413 | |
414 rtsp_free_answers(s); | |
415 | |
416 do { /* while we get answer lines */ | |
417 | |
418 answer=rtsp_get(s); | |
12271
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
419 if (!answer) |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
420 return 0; |
9922 | 421 |
422 if (!strncmp(answer,"Cseq:",5)) { | |
423 sscanf(answer,"Cseq: %u",&answer_seq); | |
424 if (s->cseq != answer_seq) { | |
425 #ifdef LOG | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
426 mp_msg(MSGT_OPEN, MSGL_WARN, "librtsp: warning: Cseq mismatch. got %u, assumed %u", answer_seq, s->cseq); |
9922 | 427 #endif |
428 s->cseq=answer_seq; | |
429 } | |
430 } | |
431 if (!strncmp(answer,"Server:",7)) { | |
12271
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
432 char *buf = malloc(strlen(answer)); |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
433 sscanf(answer,"Server: %s",buf); |
9922 | 434 if (s->server) free(s->server); |
12271
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
435 s->server=strdup(buf); |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
436 free(buf); |
9922 | 437 } |
438 if (!strncmp(answer,"Session:",8)) { | |
12271
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
439 char *buf = calloc(1, strlen(answer)); |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
440 sscanf(answer,"Session: %s",buf); |
9922 | 441 if (s->session) { |
12271
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
442 if (strcmp(buf, s->session)) { |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
443 mp_msg(MSGT_OPEN, MSGL_WARN, "rtsp: warning: setting NEW session: %s\n", buf); |
9922 | 444 free(s->session); |
12271
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
445 s->session=strdup(buf); |
9922 | 446 } |
447 } else | |
448 { | |
449 #ifdef LOG | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
450 mp_msg(MSGT_OPEN, MSGL_INFO, "rtsp: setting session id to: %s\n", buf); |
9922 | 451 #endif |
12271
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
452 s->session=strdup(buf); |
9922 | 453 } |
12271
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
454 free(buf); |
9922 | 455 } |
456 *answer_ptr=answer; | |
457 answer_ptr++; | |
15172
56541efe420b
Fix potential buffer overflow if server answers with too many lines
rtognimp
parents:
13677
diff
changeset
|
458 } while ((strlen(answer)!=0) && (++ans_count < MAX_FIELDS)); |
9922 | 459 |
460 s->cseq++; | |
461 | |
462 *answer_ptr=NULL; | |
463 rtsp_schedule_standard(s); | |
464 | |
465 return code; | |
466 } | |
467 | |
468 /* | |
469 * send an ok message | |
470 */ | |
471 | |
472 int rtsp_send_ok(rtsp_t *s) { | |
473 char cseq[16]; | |
474 | |
475 rtsp_put(s, "RTSP/1.0 200 OK"); | |
476 sprintf(cseq,"CSeq: %u", s->cseq); | |
477 rtsp_put(s, cseq); | |
478 rtsp_put(s, ""); | |
479 return 0; | |
480 } | |
481 | |
482 /* | |
483 * implementation of must-have rtsp requests; functions return | |
484 * server status code. | |
485 */ | |
486 | |
487 int rtsp_request_options(rtsp_t *s, const char *what) { | |
488 | |
489 char *buf; | |
490 | |
491 if (what) { | |
492 buf=strdup(what); | |
493 } else | |
494 { | |
495 buf=malloc(sizeof(char)*(strlen(s->host)+16)); | |
496 sprintf(buf,"rtsp://%s:%i", s->host, s->port); | |
497 } | |
498 rtsp_send_request(s,"OPTIONS",buf); | |
499 free(buf); | |
500 | |
501 return rtsp_get_answers(s); | |
502 } | |
503 | |
504 int rtsp_request_describe(rtsp_t *s, const char *what) { | |
505 | |
506 char *buf; | |
507 | |
508 if (what) { | |
509 buf=strdup(what); | |
510 } else | |
511 { | |
512 buf=malloc(sizeof(char)*(strlen(s->host)+strlen(s->path)+16)); | |
513 sprintf(buf,"rtsp://%s:%i/%s", s->host, s->port, s->path); | |
514 } | |
515 rtsp_send_request(s,"DESCRIBE",buf); | |
516 free(buf); | |
517 | |
518 return rtsp_get_answers(s); | |
519 } | |
520 | |
521 int rtsp_request_setup(rtsp_t *s, const char *what) { | |
522 | |
523 rtsp_send_request(s,"SETUP",what); | |
524 | |
525 return rtsp_get_answers(s); | |
526 } | |
527 | |
528 int rtsp_request_setparameter(rtsp_t *s, const char *what) { | |
529 | |
530 char *buf; | |
531 | |
532 if (what) { | |
533 buf=strdup(what); | |
534 } else | |
535 { | |
536 buf=malloc(sizeof(char)*(strlen(s->host)+strlen(s->path)+16)); | |
537 sprintf(buf,"rtsp://%s:%i/%s", s->host, s->port, s->path); | |
538 } | |
539 rtsp_send_request(s,"SET_PARAMETER",buf); | |
540 free(buf); | |
541 | |
542 return rtsp_get_answers(s); | |
543 } | |
544 | |
545 int rtsp_request_play(rtsp_t *s, const char *what) { | |
546 | |
547 char *buf; | |
548 | |
549 if (what) { | |
550 buf=strdup(what); | |
551 } else | |
552 { | |
553 buf=malloc(sizeof(char)*(strlen(s->host)+strlen(s->path)+16)); | |
554 sprintf(buf,"rtsp://%s:%i/%s", s->host, s->port, s->path); | |
555 } | |
556 rtsp_send_request(s,"PLAY",buf); | |
557 free(buf); | |
558 | |
559 return rtsp_get_answers(s); | |
560 } | |
561 | |
562 int rtsp_request_tearoff(rtsp_t *s, const char *what) { | |
563 | |
564 rtsp_send_request(s,"TEAROFF",what); | |
565 | |
566 return rtsp_get_answers(s); | |
567 } | |
568 | |
569 /* | |
570 * read opaque data from stream | |
571 */ | |
572 | |
573 int rtsp_read_data(rtsp_t *s, char *buffer, unsigned int size) { | |
574 | |
575 int i,seq; | |
576 | |
577 if (size>=4) { | |
578 i=read_stream(s->s, buffer, 4); | |
579 if (i<4) return i; | |
17032
7d0773bb1e42
Ignore OPTIONS rtsp command during playback. Fixes
rtognimp
parents:
16372
diff
changeset
|
580 if (((buffer[0]=='S')&&(buffer[1]=='E')&&(buffer[2]=='T')&&(buffer[3]=='_')) || |
7d0773bb1e42
Ignore OPTIONS rtsp command during playback. Fixes
rtognimp
parents:
16372
diff
changeset
|
581 ((buffer[0]=='O')&&(buffer[1]=='P')&&(buffer[2]=='T')&&(buffer[3]=='I'))) // OPTIONS |
9922 | 582 { |
583 char *rest=rtsp_get(s); | |
12271
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
584 if (!rest) |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
585 return -1; |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
586 |
9922 | 587 seq=-1; |
588 do { | |
589 free(rest); | |
590 rest=rtsp_get(s); | |
12271
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
591 if (!rest) |
4adb4a3b52a2
More bounds checking fixes (thnaks to Miguel Freitas)
rtognimp
parents:
11663
diff
changeset
|
592 return -1; |
9922 | 593 if (!strncmp(rest,"Cseq:",5)) |
594 sscanf(rest,"Cseq: %u",&seq); | |
595 } while (strlen(rest)!=0); | |
596 free(rest); | |
597 if (seq<0) { | |
598 #ifdef LOG | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
599 mp_msg(MSGT_OPEN, MSGL_WARN, "rtsp: warning: cseq not recognized!\n"); |
9922 | 600 #endif |
601 seq=1; | |
602 } | |
11000 | 603 /* let's make the server happy */ |
9922 | 604 rtsp_put(s, "RTSP/1.0 451 Parameter Not Understood"); |
605 rest=malloc(sizeof(char)*16); | |
606 sprintf(rest,"CSeq: %u", seq); | |
607 rtsp_put(s, rest); | |
608 rtsp_put(s, ""); | |
609 i=read_stream(s->s, buffer, size); | |
610 } else | |
611 { | |
612 i=read_stream(s->s, buffer+4, size-4); | |
613 i+=4; | |
614 } | |
615 } else | |
616 i=read_stream(s->s, buffer, size); | |
617 #ifdef LOG | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
618 mp_msg(MSGT_OPEN, MSGL_INFO, "librtsp: << %d of %d bytes\n", i, size); |
9922 | 619 #endif |
620 | |
621 return i; | |
622 } | |
623 | |
624 /* | |
625 * connect to a rtsp server | |
626 */ | |
627 | |
628 //rtsp_t *rtsp_connect(const char *mrl, const char *user_agent) { | |
629 rtsp_t *rtsp_connect(int fd, char* mrl, char *path, char *host, int port, char *user_agent) { | |
630 | |
631 rtsp_t *s=malloc(sizeof(rtsp_t)); | |
632 int i; | |
633 | |
634 for (i=0; i<MAX_FIELDS; i++) { | |
635 s->answers[i]=NULL; | |
636 s->scheduled[i]=NULL; | |
637 } | |
638 | |
639 s->server=NULL; | |
640 s->server_state=0; | |
641 s->server_caps=0; | |
642 | |
12678
9cdff452833b
cseq starts from 1 according to the standard, patch by Yoshinori Sato
alex
parents:
12271
diff
changeset
|
643 s->cseq=1; |
9922 | 644 s->session=NULL; |
645 | |
646 if (user_agent) | |
647 s->user_agent=strdup(user_agent); | |
648 else | |
649 s->user_agent=strdup("User-Agent: RealMedia Player Version 6.0.9.1235 (linux-2.0-libc6-i386-gcc2.95)"); | |
650 | |
651 s->mrl = strdup(mrl); | |
652 s->host = strdup(host); | |
653 s->port = port; | |
11663
d700a93ab34c
10l let path behave like before the start/stop patch
rtognimp
parents:
11506
diff
changeset
|
654 s->path = strdup(path); |
11506
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
655 while (*path == '/') |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
656 path++; |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
657 if ((s->param = strchr(s->path, '?')) != NULL) |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
658 s->param++; |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
659 //mp_msg(MSGT_OPEN, MSGL_INFO, "path=%s\n", s->path); |
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
660 //mp_msg(MSGT_OPEN, MSGL_INFO, "param=%s\n", s->param ? s->param : "NULL"); |
9922 | 661 s->s = fd; |
662 | |
663 if (s->s < 0) { | |
15626
941b1a71351f
printf converted to mp_msg; made static many unnecessarily global symbols
nicodvb
parents:
15585
diff
changeset
|
664 mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: failed to connect to '%s'\n", s->host); |
9922 | 665 rtsp_close(s); |
666 return NULL; | |
667 } | |
668 | |
669 s->server_state=RTSP_CONNECTED; | |
670 | |
11000 | 671 /* now let's send an options request. */ |
9922 | 672 rtsp_schedule_field(s, "CSeq: 1"); |
673 rtsp_schedule_field(s, s->user_agent); | |
674 rtsp_schedule_field(s, "ClientChallenge: 9e26d33f2984236010ef6253fb1887f7"); | |
675 rtsp_schedule_field(s, "PlayerStarttime: [28/03/2003:22:50:23 00:00]"); | |
676 rtsp_schedule_field(s, "CompanyID: KnKV4M4I/B2FjJ1TToLycw=="); | |
677 rtsp_schedule_field(s, "GUID: 00000000-0000-0000-0000-000000000000"); | |
678 rtsp_schedule_field(s, "RegionData: 0"); | |
679 rtsp_schedule_field(s, "ClientID: Linux_2.4_6.0.9.1235_play32_RN01_EN_586"); | |
680 /*rtsp_schedule_field(s, "Pragma: initiate-session");*/ | |
681 rtsp_request_options(s, NULL); | |
682 | |
683 return s; | |
684 } | |
685 | |
686 | |
687 /* | |
688 * closes an rtsp connection | |
689 */ | |
690 | |
691 void rtsp_close(rtsp_t *s) { | |
692 | |
10281 | 693 if (s->server_state) closesocket(s->s); /* TODO: send a TEAROFF */ |
9922 | 694 if (s->path) free(s->path); |
695 if (s->host) free(s->host); | |
696 if (s->mrl) free(s->mrl); | |
697 if (s->session) free(s->session); | |
698 if (s->user_agent) free(s->user_agent); | |
699 rtsp_free_answers(s); | |
700 rtsp_unschedule_all(s); | |
701 free(s); | |
702 } | |
703 | |
704 /* | |
705 * search in answers for tags. returns a pointer to the content | |
706 * after the first matched tag. returns NULL if no match found. | |
707 */ | |
708 | |
709 char *rtsp_search_answers(rtsp_t *s, const char *tag) { | |
710 | |
711 char **answer; | |
712 char *ptr; | |
713 | |
714 if (!s->answers) return NULL; | |
715 answer=s->answers; | |
716 | |
717 while (*answer) { | |
718 if (!strncasecmp(*answer,tag,strlen(tag))) { | |
719 ptr=strchr(*answer,':'); | |
720 ptr++; | |
721 while(*ptr==' ') ptr++; | |
722 return ptr; | |
723 } | |
724 answer++; | |
725 } | |
726 | |
727 return NULL; | |
728 } | |
729 | |
730 /* | |
731 * session id management | |
732 */ | |
733 | |
734 void rtsp_set_session(rtsp_t *s, const char *id) { | |
735 | |
736 if (s->session) free(s->session); | |
737 | |
738 s->session=strdup(id); | |
739 | |
740 } | |
741 | |
742 char *rtsp_get_session(rtsp_t *s) { | |
743 | |
744 return s->session; | |
745 | |
746 } | |
747 | |
748 char *rtsp_get_mrl(rtsp_t *s) { | |
749 | |
750 return s->mrl; | |
751 | |
752 } | |
753 | |
11506
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
754 char *rtsp_get_param(rtsp_t *s, char *p) { |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
755 int len; |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
756 char *param; |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
757 if (!s->param) |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
758 return NULL; |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
759 if (!p) |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
760 return strdup(s->param); |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
761 len = strlen(p); |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
762 param = s->param; |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
763 while (param && *param) { |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
764 char *nparam = strchr(param, '&'); |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
765 if (strncmp(param, p, len) == 0 && param[len] == '=') { |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
766 param += len + 1; |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
767 len = nparam ? nparam - param : strlen(param); |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
768 nparam = malloc(len + 1); |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
769 memcpy(nparam, param, len); |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
770 nparam[len] = 0; |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
771 return nparam; |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
772 } |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
773 param = nparam ? nparam + 1 : NULL; |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
774 } |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
775 return NULL; |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
776 } |
fff1b6f1a9cc
Real rtsp Range parameter (Start and End) support.
rtognimp
parents:
11000
diff
changeset
|
777 |
9922 | 778 /* |
779 * schedules a field for transmission | |
780 */ | |
781 | |
782 void rtsp_schedule_field(rtsp_t *s, const char *string) { | |
783 | |
784 int i=0; | |
785 | |
786 if (!string) return; | |
787 | |
788 while(s->scheduled[i]) { | |
789 i++; | |
790 } | |
791 s->scheduled[i]=strdup(string); | |
792 } | |
793 | |
794 /* | |
795 * removes the first scheduled field which prefix matches string. | |
796 */ | |
797 | |
798 void rtsp_unschedule_field(rtsp_t *s, const char *string) { | |
799 | |
800 char **ptr=s->scheduled; | |
801 | |
802 if (!string) return; | |
803 | |
804 while(*ptr) { | |
805 if (!strncmp(*ptr, string, strlen(string))) | |
806 break; | |
807 } | |
808 if (*ptr) free(*ptr); | |
809 ptr++; | |
810 do { | |
811 *(ptr-1)=*ptr; | |
812 } while(*ptr); | |
813 } | |
814 | |
815 /* | |
816 * unschedule all fields | |
817 */ | |
818 | |
819 void rtsp_unschedule_all(rtsp_t *s) { | |
820 | |
821 char **ptr; | |
822 | |
823 if (!s->scheduled) return; | |
824 ptr=s->scheduled; | |
825 | |
826 while (*ptr) { | |
827 free(*ptr); | |
828 *ptr=NULL; | |
829 ptr++; | |
830 } | |
831 } | |
832 /* | |
833 * free answers | |
834 */ | |
835 | |
836 void rtsp_free_answers(rtsp_t *s) { | |
837 | |
838 char **answer; | |
839 | |
840 if (!s->answers) return; | |
841 answer=s->answers; | |
842 | |
843 while (*answer) { | |
844 free(*answer); | |
845 *answer=NULL; | |
846 answer++; | |
847 } | |
848 } | |
15585 | 849 |
850 static int realrtsp_streaming_read( int fd, char *buffer, int size, streaming_ctrl_t *stream_ctrl ) { | |
851 return rtsp_session_read(stream_ctrl->data, buffer, size); | |
852 } | |
853 | |
854 | |
855 static int realrtsp_streaming_start( stream_t *stream ) { | |
856 int fd; | |
857 rtsp_session_t *rtsp; | |
858 char *mrl; | |
859 char *file; | |
860 int port; | |
861 int redirected, temp; | |
862 if( stream==NULL ) return -1; | |
863 | |
864 temp = 5; // counter so we don't get caught in infinite redirections (you never know) | |
865 | |
866 do { | |
867 redirected = 0; | |
868 | |
869 fd = connect2Server( stream->streaming_ctrl->url->hostname, | |
870 port = (stream->streaming_ctrl->url->port ? stream->streaming_ctrl->url->port : 554),1 ); | |
871 if(fd<0 && !stream->streaming_ctrl->url->port) | |
872 fd = connect2Server(stream->streaming_ctrl->url->hostname, port = 7070, 1); | |
873 if(fd<0) return -1; | |
874 | |
875 file = stream->streaming_ctrl->url->file; | |
876 if (file[0] == '/') | |
877 file++; | |
878 mrl = malloc(sizeof(char)*(strlen(stream->streaming_ctrl->url->hostname)+strlen(file)+16)); | |
879 sprintf(mrl,"rtsp://%s:%i/%s",stream->streaming_ctrl->url->hostname,port,file); | |
17332
88adbc28f60b
This patch makes real rtsp tell the server to deliver data at specified
rtognimp
parents:
17092
diff
changeset
|
880 rtsp = rtsp_session_start(fd,&mrl, file, stream->streaming_ctrl->url->hostname, port, &redirected, stream->streaming_ctrl->bandwidth); |
15585 | 881 |
882 if( redirected == 1) { | |
883 url_free(stream->streaming_ctrl->url); | |
884 stream->streaming_ctrl->url = url_new(mrl); | |
885 closesocket(fd); | |
886 } | |
887 | |
888 free(mrl); | |
889 temp--; | |
890 } while( (redirected != 0) && (temp > 0) ); | |
891 | |
892 if(!rtsp) return -1; | |
893 | |
894 stream->fd=fd; | |
895 stream->streaming_ctrl->data=rtsp; | |
896 | |
897 stream->streaming_ctrl->streaming_read = realrtsp_streaming_read; | |
898 //stream->streaming_ctrl->streaming_seek = nop_streaming_seek; | |
899 stream->streaming_ctrl->prebuffer_size = 128*1024; // 8 KBytes | |
900 stream->streaming_ctrl->buffering = 1; | |
901 stream->streaming_ctrl->status = streaming_playing_e; | |
902 return 0; | |
903 } | |
904 | |
905 | |
906 static int open_s(stream_t *stream,int mode, void* opts, int* file_format) { | |
907 URL_t *url; | |
908 | |
909 mp_msg(MSGT_OPEN, MSGL_INFO, "STREAM_RTSP, URL: %s\n", stream->url); | |
910 stream->streaming_ctrl = streaming_ctrl_new(); | |
911 if( stream->streaming_ctrl==NULL ) | |
912 return STREAM_ERROR; | |
913 | |
914 stream->streaming_ctrl->bandwidth = network_bandwidth; | |
915 url = url_new(stream->url); | |
916 stream->streaming_ctrl->url = check4proxies(url); | |
917 //url_free(url); | |
918 | |
919 stream->fd = -1; | |
920 if(realrtsp_streaming_start( stream ) < 0) { | |
921 streaming_ctrl_free(stream->streaming_ctrl); | |
922 stream->streaming_ctrl = NULL; | |
923 return STREAM_UNSUPORTED; | |
924 } | |
925 | |
926 fixup_network_stream_cache(stream); | |
927 stream->type = STREAMTYPE_STREAM; | |
928 return STREAM_OK; | |
929 } | |
930 | |
931 | |
932 stream_info_t stream_info_rtsp = { | |
933 "RealNetworks rtsp streaming", | |
934 "realrtsp", | |
935 "Roberto Togni, xine team", | |
936 "ported from xine", | |
937 open_s, | |
938 {"rtsp", NULL}, | |
939 NULL, | |
940 0 // Urls are an option string | |
941 }; |