2 * This file was ported to MPlayer from xine CVS rtsp.c,v 1.9 2003/04/10 02:30:48
6 * Copyright (C) 2000-2002 the xine project
8 * This file is part of xine, a free video player.
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.
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.
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
25 * a minimalistic implementation of rtsp protocol,
26 * *not* RFC 2326 compilant yet.
28 * 2006, Benjamin Zores and Vincent Mussard
29 * fixed a lot of RFC compliance issues.
43 #include <sys/types.h>
45 #ifdef HAVE_WINSOCK2_H
48 #include <sys/socket.h>
52 #include "rtsp_session.h"
53 #include "osdep/timer.h"
63 static int write_stream(int s
, const char *buf
, int len
) {
66 total
= 0; timeout
= 30;
70 n
= send (s
, &buf
[total
], len
- total
, 0);
75 #ifndef HAVE_WINSOCK2_H
76 if ((timeout
>0) && ((errno
== EAGAIN
) || (errno
== EINPROGRESS
))) {
78 if ((timeout
>0) && ((errno
== EAGAIN
) || (WSAGetLastError() == WSAEINPROGRESS
))) {
80 usec_sleep (1000000); timeout
--;
89 static ssize_t
read_stream(int fd
, void *buf
, size_t count
) {
95 while (total
< count
) {
97 ret
=recv (fd
, ((uint8_t*)buf
)+total
, count
-total
, 0);
100 if(errno
== EAGAIN
) {
102 struct timeval timeout
;
110 if (select (fd
+1, &rset
, NULL
, NULL
, &timeout
) <= 0) {
116 mp_msg(MSGT_OPEN
, MSGL_ERR
, "rtsp: read error.\n");
129 * rtsp_get gets a line from stream
130 * and returns a null terminated string.
133 static char *rtsp_get(rtsp_t
*s
) {
136 char *buffer
= malloc(BUF_SIZE
);
139 read_stream(s
->s
, buffer
, 1);
141 read_stream(s
->s
, &(buffer
[n
]), 1);
142 if ((buffer
[n
-1]==0x0d)&&(buffer
[n
]==0x0a)) break;
147 mp_msg(MSGT_OPEN
, MSGL_FATAL
, "librtsp: buffer overflow in rtsp_get\n");
151 memcpy(string
,buffer
,n
-1);
155 mp_msg(MSGT_OPEN
, MSGL_INFO
, "librtsp: << '%s'\n", string
);
164 * rtsp_put puts a line on stream
167 static void rtsp_put(rtsp_t
*s
, const char *string
) {
169 int len
=strlen(string
);
170 char *buf
=malloc(len
+2);
173 mp_msg(MSGT_OPEN
, MSGL_INFO
, "librtsp: >> '%s'", string
);
176 memcpy(buf
,string
,len
);
180 write_stream(s
->s
, buf
, len
+2);
183 mp_msg(MSGT_OPEN
, MSGL_INFO
, " done.\n");
190 * extract server status code
193 static int rtsp_get_code(const char *string
) {
198 if (!strncmp(string
, RTSP_PROTOCOL_VERSION
, strlen(RTSP_PROTOCOL_VERSION
)))
200 memcpy(buf
, string
+strlen(RTSP_PROTOCOL_VERSION
)+1, 3);
203 } else if (!strncmp(string
, RTSP_METHOD_SET_PARAMETER
,8))
205 return RTSP_STATUS_SET_PARAMETER
;
208 if(code
!= RTSP_STATUS_OK
) mp_msg(MSGT_OPEN
, MSGL_INFO
, "librtsp: server responds: '%s'\n",string
);
217 static void rtsp_send_request(rtsp_t
*s
, const char *type
, const char *what
) {
219 char **payload
=s
->scheduled
;
222 buf
= malloc(strlen(type
)+strlen(what
)+strlen(RTSP_PROTOCOL_VERSION
)+3);
224 sprintf(buf
,"%s %s %s",type
, what
, RTSP_PROTOCOL_VERSION
);
229 rtsp_put(s
,*payload
);
233 rtsp_unschedule_all(s
);
237 * schedule standard fields
240 static void rtsp_schedule_standard(rtsp_t
*s
) {
244 snprintf(tmp
, 17, "CSeq: %u", s
->cseq
);
245 rtsp_schedule_field(s
, tmp
);
249 buf
= malloc(strlen(s
->session
)+15);
250 sprintf(buf
, "Session: %s", s
->session
);
251 rtsp_schedule_field(s
, buf
);
256 * get the answers, if server responses with something != 200, return NULL
259 static int rtsp_get_answers(rtsp_t
*s
) {
262 unsigned int answer_seq
;
263 char **answer_ptr
=s
->answers
;
270 code
=rtsp_get_code(answer
);
273 rtsp_free_answers(s
);
275 do { /* while we get answer lines */
281 if (!strncasecmp(answer
,"CSeq:",5)) {
282 sscanf(answer
,"%*s %u",&answer_seq
);
283 if (s
->cseq
!= answer_seq
) {
285 mp_msg(MSGT_OPEN
, MSGL_WARN
, "librtsp: warning: CSeq mismatch. got %u, assumed %u", answer_seq
, s
->cseq
);
290 if (!strncasecmp(answer
,"Server:",7)) {
291 char *buf
= malloc(strlen(answer
));
292 sscanf(answer
,"%*s %s",buf
);
293 if (s
->server
) free(s
->server
);
294 s
->server
=strdup(buf
);
297 if (!strncasecmp(answer
,"Session:",8)) {
298 char *buf
= calloc(1, strlen(answer
));
299 sscanf(answer
,"%*s %s",buf
);
301 if (strcmp(buf
, s
->session
)) {
302 mp_msg(MSGT_OPEN
, MSGL_WARN
, "rtsp: warning: setting NEW session: %s\n", buf
);
304 s
->session
=strdup(buf
);
309 mp_msg(MSGT_OPEN
, MSGL_INFO
, "rtsp: setting session id to: %s\n", buf
);
311 s
->session
=strdup(buf
);
317 } while ((strlen(answer
)!=0) && (++ans_count
< MAX_FIELDS
));
322 rtsp_schedule_standard(s
);
331 int rtsp_send_ok(rtsp_t
*s
) {
334 rtsp_put(s
, "RTSP/1.0 200 OK");
335 sprintf(cseq
,"CSeq: %u", s
->cseq
);
342 * implementation of must-have rtsp requests; functions return
343 * server status code.
346 int rtsp_request_options(rtsp_t
*s
, const char *what
) {
354 buf
=malloc(strlen(s
->host
)+16);
355 sprintf(buf
,"rtsp://%s:%i", s
->host
, s
->port
);
357 rtsp_send_request(s
,RTSP_METHOD_OPTIONS
,buf
);
360 return rtsp_get_answers(s
);
363 int rtsp_request_describe(rtsp_t
*s
, const char *what
) {
371 buf
=malloc(strlen(s
->host
)+strlen(s
->path
)+16);
372 sprintf(buf
,"rtsp://%s:%i/%s", s
->host
, s
->port
, s
->path
);
374 rtsp_send_request(s
,RTSP_METHOD_DESCRIBE
,buf
);
377 return rtsp_get_answers(s
);
380 int rtsp_request_setup(rtsp_t
*s
, const char *what
, char *control
) {
388 int len
= strlen (s
->host
) + strlen (s
->path
) + 16;
390 len
+= strlen (control
) + 1;
393 sprintf (buf
, "rtsp://%s:%i/%s%s%s", s
->host
, s
->port
, s
->path
,
394 control
? "/" : "", control
? control
: "");
397 rtsp_send_request (s
, RTSP_METHOD_SETUP
, buf
);
399 return rtsp_get_answers (s
);
402 int rtsp_request_setparameter(rtsp_t
*s
, const char *what
) {
410 buf
=malloc(strlen(s
->host
)+strlen(s
->path
)+16);
411 sprintf(buf
,"rtsp://%s:%i/%s", s
->host
, s
->port
, s
->path
);
413 rtsp_send_request(s
,RTSP_METHOD_SET_PARAMETER
,buf
);
416 return rtsp_get_answers(s
);
419 int rtsp_request_play(rtsp_t
*s
, const char *what
) {
428 buf
=malloc(strlen(s
->host
)+strlen(s
->path
)+16);
429 sprintf(buf
,"rtsp://%s:%i/%s", s
->host
, s
->port
, s
->path
);
431 rtsp_send_request(s
,RTSP_METHOD_PLAY
,buf
);
434 ret
= rtsp_get_answers (s
);
435 if (ret
== RTSP_STATUS_OK
)
436 s
->server_state
= RTSP_PLAYING
;
441 int rtsp_request_teardown(rtsp_t
*s
, const char *what
) {
450 malloc (strlen (s
->host
) + strlen (s
->path
) + 16);
451 sprintf (buf
, "rtsp://%s:%i/%s", s
->host
, s
->port
, s
->path
);
453 rtsp_send_request (s
, RTSP_METHOD_TEARDOWN
, buf
);
456 /* after teardown we're done with RTSP streaming, no need to get answer as
457 reading more will only result to garbage and buffer overflow */
458 return RTSP_STATUS_OK
;
462 * read opaque data from stream
465 int rtsp_read_data(rtsp_t
*s
, char *buffer
, unsigned int size
) {
470 i
=read_stream(s
->s
, buffer
, 4);
472 if (((buffer
[0]=='S')&&(buffer
[1]=='E')&&(buffer
[2]=='T')&&(buffer
[3]=='_')) ||
473 ((buffer
[0]=='O')&&(buffer
[1]=='P')&&(buffer
[2]=='T')&&(buffer
[3]=='I'))) // OPTIONS
475 char *rest
=rtsp_get(s
);
485 if (!strncasecmp(rest
,"CSeq:",5))
486 sscanf(rest
,"%*s %u",&seq
);
487 } while (strlen(rest
)!=0);
491 mp_msg(MSGT_OPEN
, MSGL_WARN
, "rtsp: warning: CSeq not recognized!\n");
495 /* let's make the server happy */
496 rtsp_put(s
, "RTSP/1.0 451 Parameter Not Understood");
498 sprintf(rest
,"CSeq: %u", seq
);
502 i
=read_stream(s
->s
, buffer
, size
);
505 i
=read_stream(s
->s
, buffer
+4, size
-4);
509 i
=read_stream(s
->s
, buffer
, size
);
511 mp_msg(MSGT_OPEN
, MSGL_INFO
, "librtsp: << %d of %d bytes\n", i
, size
);
518 * connect to a rtsp server
521 //rtsp_t *rtsp_connect(const char *mrl, const char *user_agent) {
522 rtsp_t
*rtsp_connect(int fd
, char* mrl
, char *path
, char *host
, int port
, char *user_agent
) {
528 mp_msg(MSGT_OPEN
, MSGL_ERR
, "rtsp: failed to connect to '%s'\n", host
);
532 s
= malloc(sizeof(rtsp_t
));
534 for (i
=0; i
<MAX_FIELDS
; i
++) {
536 s
->scheduled
[i
]=NULL
;
548 s
->user_agent
=strdup(user_agent
);
550 s
->user_agent
=strdup("User-Agent: RealMedia Player Version 6.0.9.1235 (linux-2.0-libc6-i386-gcc2.95)");
552 s
->mrl
= strdup(mrl
);
553 s
->host
= strdup(host
);
555 s
->path
= strdup(path
);
558 if ((s
->param
= strchr(s
->path
, '?')) != NULL
)
560 //mp_msg(MSGT_OPEN, MSGL_INFO, "path=%s\n", s->path);
561 //mp_msg(MSGT_OPEN, MSGL_INFO, "param=%s\n", s->param ? s->param : "NULL");
563 s
->server_state
=RTSP_CONNECTED
;
565 /* now let's send an options request. */
566 rtsp_schedule_field(s
, "CSeq: 1");
567 rtsp_schedule_field(s
, s
->user_agent
);
568 rtsp_schedule_field(s
, "ClientChallenge: 9e26d33f2984236010ef6253fb1887f7");
569 rtsp_schedule_field(s
, "PlayerStarttime: [28/03/2003:22:50:23 00:00]");
570 rtsp_schedule_field(s
, "CompanyID: KnKV4M4I/B2FjJ1TToLycw==");
571 rtsp_schedule_field(s
, "GUID: 00000000-0000-0000-0000-000000000000");
572 rtsp_schedule_field(s
, "RegionData: 0");
573 rtsp_schedule_field(s
, "ClientID: Linux_2.4_6.0.9.1235_play32_RN01_EN_586");
574 /*rtsp_schedule_field(s, "Pragma: initiate-session");*/
575 rtsp_request_options(s
, NULL
);
582 * search in answers for tags. returns a pointer to the content
583 * after the first matched tag. returns NULL if no match found.
586 char *rtsp_search_answers(rtsp_t
*s
, const char *tag
) {
591 if (!s
->answers
) return NULL
;
595 if (!strncasecmp(*answer
,tag
,strlen(tag
))) {
596 ptr
=strchr(*answer
,':');
597 if (!ptr
) return NULL
;
599 while(*ptr
==' ') ptr
++;
609 * session id management
612 void rtsp_set_session(rtsp_t
*s
, const char *id
) {
614 if (s
->session
) free(s
->session
);
616 s
->session
=strdup(id
);
620 char *rtsp_get_session(rtsp_t
*s
) {
626 char *rtsp_get_mrl(rtsp_t
*s
) {
632 char *rtsp_get_param(rtsp_t
*s
, const char *p
) {
638 return strdup(s
->param
);
641 while (param
&& *param
) {
642 char *nparam
= strchr(param
, '&');
643 if (strncmp(param
, p
, len
) == 0 && param
[len
] == '=') {
645 len
= nparam
? nparam
- param
: strlen(param
);
646 nparam
= malloc(len
+ 1);
647 memcpy(nparam
, param
, len
);
651 param
= nparam
? nparam
+ 1 : NULL
;
657 * schedules a field for transmission
660 void rtsp_schedule_field(rtsp_t
*s
, const char *string
) {
666 while(s
->scheduled
[i
]) {
669 s
->scheduled
[i
]=strdup(string
);
673 * removes the first scheduled field which prefix matches string.
676 void rtsp_unschedule_field(rtsp_t
*s
, const char *string
) {
678 char **ptr
=s
->scheduled
;
683 if (!strncmp(*ptr
, string
, strlen(string
)))
688 if (*ptr
) free(*ptr
);
696 * unschedule all fields
699 void rtsp_unschedule_all(rtsp_t
*s
) {
703 if (!s
->scheduled
) return;
716 void rtsp_free_answers(rtsp_t
*s
) {
720 if (!s
->answers
) return;