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>
48 #include <sys/socket.h>
52 #include "rtsp_session.h"
53 #include "osdep/timer.h"
54 #include "stream/network.h"
64 static int write_stream(int s
, const char *buf
, int len
) {
67 total
= 0; timeout
= 30;
71 n
= send (s
, &buf
[total
], len
- total
, DEFAULT_SEND_FLAGS
);
77 if ((timeout
>0) && ((errno
== EAGAIN
) || (errno
== EINPROGRESS
))) {
79 if ((timeout
>0) && ((errno
== EAGAIN
) || (WSAGetLastError() == WSAEINPROGRESS
))) {
81 usec_sleep (1000000); timeout
--;
90 static ssize_t
read_stream(int fd
, void *buf
, size_t count
) {
96 while (total
< count
) {
98 ret
=recv (fd
, ((uint8_t*)buf
)+total
, count
-total
, 0);
101 if(errno
== EAGAIN
) {
103 struct timeval timeout
;
111 if (select (fd
+1, &rset
, NULL
, NULL
, &timeout
) <= 0) {
117 mp_msg(MSGT_OPEN
, MSGL_ERR
, "rtsp: read error.\n");
130 * rtsp_get gets a line from stream
131 * and returns a null terminated string.
134 static char *rtsp_get(rtsp_t
*s
) {
137 char *buffer
= malloc(BUF_SIZE
);
140 read_stream(s
->s
, buffer
, 1);
142 read_stream(s
->s
, &(buffer
[n
]), 1);
143 if ((buffer
[n
-1]==0x0d)&&(buffer
[n
]==0x0a)) break;
148 mp_msg(MSGT_OPEN
, MSGL_FATAL
, "librtsp: buffer overflow in rtsp_get\n");
152 memcpy(string
,buffer
,n
-1);
156 mp_msg(MSGT_OPEN
, MSGL_INFO
, "librtsp: << '%s'\n", string
);
165 * rtsp_put puts a line on stream
168 static void rtsp_put(rtsp_t
*s
, const char *string
) {
170 int len
=strlen(string
);
171 char *buf
=malloc(len
+2);
174 mp_msg(MSGT_OPEN
, MSGL_INFO
, "librtsp: >> '%s'", string
);
177 memcpy(buf
,string
,len
);
181 write_stream(s
->s
, buf
, len
+2);
184 mp_msg(MSGT_OPEN
, MSGL_INFO
, " done.\n");
191 * extract server status code
194 static int rtsp_get_code(const char *string
) {
199 if (!strncmp(string
, RTSP_PROTOCOL_VERSION
, strlen(RTSP_PROTOCOL_VERSION
)))
201 memcpy(buf
, string
+strlen(RTSP_PROTOCOL_VERSION
)+1, 3);
204 } else if (!strncmp(string
, RTSP_METHOD_SET_PARAMETER
,8))
206 return RTSP_STATUS_SET_PARAMETER
;
209 if(code
!= RTSP_STATUS_OK
) mp_msg(MSGT_OPEN
, MSGL_INFO
, "librtsp: server responds: '%s'\n",string
);
218 static void rtsp_send_request(rtsp_t
*s
, const char *type
, const char *what
) {
220 char **payload
=s
->scheduled
;
223 buf
= malloc(strlen(type
)+strlen(what
)+strlen(RTSP_PROTOCOL_VERSION
)+3);
225 sprintf(buf
,"%s %s %s",type
, what
, RTSP_PROTOCOL_VERSION
);
230 rtsp_put(s
,*payload
);
234 rtsp_unschedule_all(s
);
238 * schedule standard fields
241 static void rtsp_schedule_standard(rtsp_t
*s
) {
245 snprintf(tmp
, 17, "CSeq: %u", s
->cseq
);
246 rtsp_schedule_field(s
, tmp
);
250 buf
= malloc(strlen(s
->session
)+15);
251 sprintf(buf
, "Session: %s", s
->session
);
252 rtsp_schedule_field(s
, buf
);
257 * get the answers, if server responses with something != 200, return NULL
260 static int rtsp_get_answers(rtsp_t
*s
) {
263 unsigned int answer_seq
;
264 char **answer_ptr
=s
->answers
;
271 code
=rtsp_get_code(answer
);
274 rtsp_free_answers(s
);
276 do { /* while we get answer lines */
282 if (!strncasecmp(answer
,"CSeq:",5)) {
283 sscanf(answer
,"%*s %u",&answer_seq
);
284 if (s
->cseq
!= answer_seq
) {
286 mp_msg(MSGT_OPEN
, MSGL_WARN
, "librtsp: warning: CSeq mismatch. got %u, assumed %u", answer_seq
, s
->cseq
);
291 if (!strncasecmp(answer
,"Server:",7)) {
292 char *buf
= malloc(strlen(answer
));
293 sscanf(answer
,"%*s %s",buf
);
295 s
->server
=strdup(buf
);
298 if (!strncasecmp(answer
,"Session:",8)) {
299 char *buf
= calloc(1, strlen(answer
));
300 sscanf(answer
,"%*s %s",buf
);
302 if (strcmp(buf
, s
->session
)) {
303 mp_msg(MSGT_OPEN
, MSGL_WARN
, "rtsp: warning: setting NEW session: %s\n", buf
);
305 s
->session
=strdup(buf
);
310 mp_msg(MSGT_OPEN
, MSGL_INFO
, "rtsp: setting session id to: %s\n", buf
);
312 s
->session
=strdup(buf
);
318 } while ((strlen(answer
)!=0) && (++ans_count
< MAX_FIELDS
));
323 rtsp_schedule_standard(s
);
332 int rtsp_send_ok(rtsp_t
*s
) {
335 rtsp_put(s
, "RTSP/1.0 200 OK");
336 sprintf(cseq
,"CSeq: %u", s
->cseq
);
343 * implementation of must-have rtsp requests; functions return
344 * server status code.
347 int rtsp_request_options(rtsp_t
*s
, const char *what
) {
355 buf
=malloc(strlen(s
->host
)+16);
356 sprintf(buf
,"rtsp://%s:%i", s
->host
, s
->port
);
358 rtsp_send_request(s
,RTSP_METHOD_OPTIONS
,buf
);
361 return rtsp_get_answers(s
);
364 int rtsp_request_describe(rtsp_t
*s
, const char *what
) {
372 buf
=malloc(strlen(s
->host
)+strlen(s
->path
)+16);
373 sprintf(buf
,"rtsp://%s:%i/%s", s
->host
, s
->port
, s
->path
);
375 rtsp_send_request(s
,RTSP_METHOD_DESCRIBE
,buf
);
378 return rtsp_get_answers(s
);
381 int rtsp_request_setup(rtsp_t
*s
, const char *what
, char *control
) {
389 int len
= strlen (s
->host
) + strlen (s
->path
) + 16;
391 len
+= strlen (control
) + 1;
394 sprintf (buf
, "rtsp://%s:%i/%s%s%s", s
->host
, s
->port
, s
->path
,
395 control
? "/" : "", control
? control
: "");
398 rtsp_send_request (s
, RTSP_METHOD_SETUP
, buf
);
400 return rtsp_get_answers (s
);
403 int rtsp_request_setparameter(rtsp_t
*s
, const char *what
) {
411 buf
=malloc(strlen(s
->host
)+strlen(s
->path
)+16);
412 sprintf(buf
,"rtsp://%s:%i/%s", s
->host
, s
->port
, s
->path
);
414 rtsp_send_request(s
,RTSP_METHOD_SET_PARAMETER
,buf
);
417 return rtsp_get_answers(s
);
420 int rtsp_request_play(rtsp_t
*s
, const char *what
) {
429 buf
=malloc(strlen(s
->host
)+strlen(s
->path
)+16);
430 sprintf(buf
,"rtsp://%s:%i/%s", s
->host
, s
->port
, s
->path
);
432 rtsp_send_request(s
,RTSP_METHOD_PLAY
,buf
);
435 ret
= rtsp_get_answers (s
);
436 if (ret
== RTSP_STATUS_OK
)
437 s
->server_state
= RTSP_PLAYING
;
442 int rtsp_request_teardown(rtsp_t
*s
, const char *what
) {
451 malloc (strlen (s
->host
) + strlen (s
->path
) + 16);
452 sprintf (buf
, "rtsp://%s:%i/%s", s
->host
, s
->port
, s
->path
);
454 rtsp_send_request (s
, RTSP_METHOD_TEARDOWN
, buf
);
457 /* after teardown we're done with RTSP streaming, no need to get answer as
458 reading more will only result to garbage and buffer overflow */
459 return RTSP_STATUS_OK
;
463 * read opaque data from stream
466 int rtsp_read_data(rtsp_t
*s
, char *buffer
, unsigned int size
) {
471 i
=read_stream(s
->s
, buffer
, 4);
473 if (((buffer
[0]=='S')&&(buffer
[1]=='E')&&(buffer
[2]=='T')&&(buffer
[3]=='_')) ||
474 ((buffer
[0]=='O')&&(buffer
[1]=='P')&&(buffer
[2]=='T')&&(buffer
[3]=='I'))) // OPTIONS
476 char *rest
=rtsp_get(s
);
486 if (!strncasecmp(rest
,"CSeq:",5))
487 sscanf(rest
,"%*s %u",&seq
);
488 } while (strlen(rest
)!=0);
492 mp_msg(MSGT_OPEN
, MSGL_WARN
, "rtsp: warning: CSeq not recognized!\n");
496 /* let's make the server happy */
497 rtsp_put(s
, "RTSP/1.0 451 Parameter Not Understood");
499 sprintf(rest
,"CSeq: %u", seq
);
503 i
=read_stream(s
->s
, buffer
, size
);
506 i
=read_stream(s
->s
, buffer
+4, size
-4);
510 i
=read_stream(s
->s
, buffer
, size
);
512 mp_msg(MSGT_OPEN
, MSGL_INFO
, "librtsp: << %d of %d bytes\n", i
, size
);
519 * connect to a rtsp server
522 //rtsp_t *rtsp_connect(const char *mrl, const char *user_agent) {
523 rtsp_t
*rtsp_connect(int fd
, char* mrl
, char *path
, char *host
, int port
, char *user_agent
) {
529 mp_msg(MSGT_OPEN
, MSGL_ERR
, "rtsp: failed to connect to '%s'\n", host
);
533 s
= malloc(sizeof(rtsp_t
));
535 for (i
=0; i
<MAX_FIELDS
; i
++) {
537 s
->scheduled
[i
]=NULL
;
549 s
->user_agent
=strdup(user_agent
);
551 s
->user_agent
=strdup("User-Agent: RealMedia Player Version 6.0.9.1235 (linux-2.0-libc6-i386-gcc2.95)");
553 s
->mrl
= strdup(mrl
);
554 s
->host
= strdup(host
);
556 s
->path
= strdup(path
);
559 if ((s
->param
= strchr(s
->path
, '?')) != NULL
)
561 //mp_msg(MSGT_OPEN, MSGL_INFO, "path=%s\n", s->path);
562 //mp_msg(MSGT_OPEN, MSGL_INFO, "param=%s\n", s->param ? s->param : "NULL");
564 s
->server_state
=RTSP_CONNECTED
;
566 /* now let's send an options request. */
567 rtsp_schedule_field(s
, "CSeq: 1");
568 rtsp_schedule_field(s
, s
->user_agent
);
569 rtsp_schedule_field(s
, "ClientChallenge: 9e26d33f2984236010ef6253fb1887f7");
570 rtsp_schedule_field(s
, "PlayerStarttime: [28/03/2003:22:50:23 00:00]");
571 rtsp_schedule_field(s
, "CompanyID: KnKV4M4I/B2FjJ1TToLycw==");
572 rtsp_schedule_field(s
, "GUID: 00000000-0000-0000-0000-000000000000");
573 rtsp_schedule_field(s
, "RegionData: 0");
574 rtsp_schedule_field(s
, "ClientID: Linux_2.4_6.0.9.1235_play32_RN01_EN_586");
575 /*rtsp_schedule_field(s, "Pragma: initiate-session");*/
576 rtsp_request_options(s
, NULL
);
583 * search in answers for tags. returns a pointer to the content
584 * after the first matched tag. returns NULL if no match found.
587 char *rtsp_search_answers(rtsp_t
*s
, const char *tag
) {
592 if (!s
->answers
) return NULL
;
596 if (!strncasecmp(*answer
,tag
,strlen(tag
))) {
597 ptr
=strchr(*answer
,':');
598 if (!ptr
) return NULL
;
600 while(*ptr
==' ') ptr
++;
610 * session id management
613 void rtsp_set_session(rtsp_t
*s
, const char *id
) {
617 s
->session
=strdup(id
);
621 char *rtsp_get_session(rtsp_t
*s
) {
627 char *rtsp_get_mrl(rtsp_t
*s
) {
633 char *rtsp_get_param(rtsp_t
*s
, const char *p
) {
639 return strdup(s
->param
);
642 while (param
&& *param
) {
643 char *nparam
= strchr(param
, '&');
644 if (strncmp(param
, p
, len
) == 0 && param
[len
] == '=') {
646 len
= nparam
? nparam
- param
: strlen(param
);
647 nparam
= malloc(len
+ 1);
648 memcpy(nparam
, param
, len
);
652 param
= nparam
? nparam
+ 1 : NULL
;
658 * schedules a field for transmission
661 void rtsp_schedule_field(rtsp_t
*s
, const char *string
) {
667 while(s
->scheduled
[i
]) {
670 s
->scheduled
[i
]=strdup(string
);
674 * removes the first scheduled field which prefix matches string.
677 void rtsp_unschedule_field(rtsp_t
*s
, const char *string
) {
679 char **ptr
=s
->scheduled
;
684 if (!strncmp(*ptr
, string
, strlen(string
)))
697 * unschedule all fields
700 void rtsp_unschedule_all(rtsp_t
*s
) {
704 if (!s
->scheduled
) return;
717 void rtsp_free_answers(rtsp_t
*s
) {
721 if (!s
->answers
) return;