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.
34
#define closesocket close
35
#include <sys/socket.h>
36
#include <netinet/in.h>
48
#include <sys/types.h>
52
#include "../stream.h"
53
#include "../demuxer.h"
54
#include "rtsp_session.h"
55
#include "osdep/timer.h"
62
#define HEADER_SIZE 1024
63
#define MAX_FIELDS 256
65
extern int network_bandwidth;
78
unsigned int server_state;
84
char *answers[MAX_FIELDS]; /* data of last message */
85
char *scheduled[MAX_FIELDS]; /* will be sent with next message */
92
const char rtsp_protocol_version[]="RTSP/1.0";
95
#define RTSP_CONNECTED 1
98
#define RTSP_PLAYING 8
99
#define RTSP_RECORDING 16
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
116
static int host_connect_attempt(struct in_addr ia, int port) {
119
struct sockaddr_in sin;
121
s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
123
mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: socket(): %s\n", strerror(errno));
127
sin.sin_family = AF_INET;
129
sin.sin_port = htons(port);
131
if (connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1
132
#ifndef HAVE_WINSOCK2
133
&& errno != EINPROGRESS) {
135
&& WSAGetLastError() == WSAEINPROGRESS) {
137
mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: connect(): %s\n", strerror(errno));
145
static int host_connect(const char *host, int port) {
150
h = gethostbyname(host);
152
mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: unable to resolve '%s'.\n", host);
156
for (i = 0; h->h_addr_list[i]; i++) {
159
memcpy (&ia, h->h_addr_list[i], 4);
160
s = host_connect_attempt(ia, port);
164
mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: unable to connect to '%s'.\n", host);
168
static int write_stream(int s, const char *buf, int len) {
171
total = 0; timeout = 30;
175
n = send (s, &buf[total], len - total, 0);
180
#ifndef HAVE_WINSOCK2
181
if ((timeout>0) && ((errno == EAGAIN) || (errno == EINPROGRESS))) {
183
if ((timeout>0) && ((errno == EAGAIN) || (WSAGetLastError() == WSAEINPROGRESS))) {
185
usec_sleep (1000000); timeout--;
194
static ssize_t read_stream(int fd, void *buf, size_t count) {
200
while (total < count) {
202
ret=recv (fd, ((uint8_t*)buf)+total, count-total, 0);
205
if(errno == EAGAIN) {
207
struct timeval timeout;
215
if (select (fd+1, &rset, NULL, NULL, &timeout) <= 0) {
221
mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: read error.\n");
234
* debugging utilities
237
static void hexdump (char *buf, int length) {
241
mp_msg(MSGT_OPEN, MSGL_INFO, "rtsp: ascii>");
242
for (i = 0; i < length; i++) {
243
unsigned char c = buf[i];
245
if ((c >= 32) && (c <= 128))
246
mp_msg(MSGT_OPEN, MSGL_INFO, "%c", c);
248
mp_msg(MSGT_OPEN, MSGL_INFO, ".");
250
mp_msg(MSGT_OPEN, MSGL_INFO, "\n");
252
mp_msg(MSGT_OPEN, MSGL_INFO, "rtsp: hexdump> ");
253
for (i = 0; i < length; i++) {
254
unsigned char c = buf[i];
256
mp_msg(MSGT_OPEN, MSGL_INFO, "%02x", c);
259
mp_msg(MSGT_OPEN, MSGL_INFO, "\nrtsp: ");
262
mp_msg(MSGT_OPEN, MSGL_INFO, " ");
265
mp_msg(MSGT_OPEN, MSGL_INFO, "\n");
270
* rtsp_get gets a line from stream
271
* and returns a null terminated string.
274
static char *rtsp_get(rtsp_t *s) {
277
char *buffer = malloc(BUF_SIZE);
280
read_stream(s->s, buffer, 1);
282
read_stream(s->s, &(buffer[n]), 1);
283
if ((buffer[n-1]==0x0d)&&(buffer[n]==0x0a)) break;
288
mp_msg(MSGT_OPEN, MSGL_FATAL, "librtsp: buffer overflow in rtsp_get\n");
291
string=malloc(sizeof(char)*n);
292
memcpy(string,buffer,n-1);
296
mp_msg(MSGT_OPEN, MSGL_INFO, "librtsp: << '%s'\n", string);
305
* rtsp_put puts a line on stream
308
static void rtsp_put(rtsp_t *s, const char *string) {
310
int len=strlen(string);
311
char *buf=malloc(sizeof(char)*len+2);
314
mp_msg(MSGT_OPEN, MSGL_INFO, "librtsp: >> '%s'", string);
317
memcpy(buf,string,len);
321
write_stream(s->s, buf, len+2);
324
mp_msg(MSGT_OPEN, MSGL_INFO, " done.\n");
331
* extract server status code
334
static int rtsp_get_code(const char *string) {
339
if (!strncmp(string, rtsp_protocol_version, strlen(rtsp_protocol_version)))
341
memcpy(buf, string+strlen(rtsp_protocol_version)+1, 3);
344
} else if (!strncmp(string, "SET_PARAMETER",8))
346
return RTSP_STATUS_SET_PARAMETER;
349
if(code != 200) mp_msg(MSGT_OPEN, MSGL_INFO, "librtsp: server responds: '%s'\n",string);
358
static void rtsp_send_request(rtsp_t *s, const char *type, const char *what) {
360
char **payload=s->scheduled;
363
buf = malloc(strlen(type)+strlen(what)+strlen(rtsp_protocol_version)+3);
365
sprintf(buf,"%s %s %s",type, what, rtsp_protocol_version);
370
rtsp_put(s,*payload);
374
rtsp_unschedule_all(s);
378
* schedule standard fields
381
static void rtsp_schedule_standard(rtsp_t *s) {
385
snprintf(tmp, 16, "Cseq: %u", s->cseq);
386
rtsp_schedule_field(s, tmp);
390
buf = malloc(strlen(s->session)+15);
391
sprintf(buf, "Session: %s", s->session);
392
rtsp_schedule_field(s, buf);
397
* get the answers, if server responses with something != 200, return NULL
400
static int rtsp_get_answers(rtsp_t *s) {
403
unsigned int answer_seq;
404
char **answer_ptr=s->answers;
411
code=rtsp_get_code(answer);
414
rtsp_free_answers(s);
416
do { /* while we get answer lines */
422
if (!strncmp(answer,"Cseq:",5)) {
423
sscanf(answer,"Cseq: %u",&answer_seq);
424
if (s->cseq != answer_seq) {
426
mp_msg(MSGT_OPEN, MSGL_WARN, "librtsp: warning: Cseq mismatch. got %u, assumed %u", answer_seq, s->cseq);
431
if (!strncmp(answer,"Server:",7)) {
432
char *buf = malloc(strlen(answer));
433
sscanf(answer,"Server: %s",buf);
434
if (s->server) free(s->server);
435
s->server=strdup(buf);
438
if (!strncmp(answer,"Session:",8)) {
439
char *buf = calloc(1, strlen(answer));
440
sscanf(answer,"Session: %s",buf);
442
if (strcmp(buf, s->session)) {
443
mp_msg(MSGT_OPEN, MSGL_WARN, "rtsp: warning: setting NEW session: %s\n", buf);
445
s->session=strdup(buf);
450
mp_msg(MSGT_OPEN, MSGL_INFO, "rtsp: setting session id to: %s\n", buf);
452
s->session=strdup(buf);
458
} while ((strlen(answer)!=0) && (++ans_count < MAX_FIELDS));
463
rtsp_schedule_standard(s);
472
int rtsp_send_ok(rtsp_t *s) {
475
rtsp_put(s, "RTSP/1.0 200 OK");
476
sprintf(cseq,"CSeq: %u", s->cseq);
483
* implementation of must-have rtsp requests; functions return
484
* server status code.
487
int rtsp_request_options(rtsp_t *s, const char *what) {
495
buf=malloc(sizeof(char)*(strlen(s->host)+16));
496
sprintf(buf,"rtsp://%s:%i", s->host, s->port);
498
rtsp_send_request(s,"OPTIONS",buf);
501
return rtsp_get_answers(s);
504
int rtsp_request_describe(rtsp_t *s, const char *what) {
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);
515
rtsp_send_request(s,"DESCRIBE",buf);
518
return rtsp_get_answers(s);
521
int rtsp_request_setup(rtsp_t *s, const char *what) {
523
rtsp_send_request(s,"SETUP",what);
525
return rtsp_get_answers(s);
528
int rtsp_request_setparameter(rtsp_t *s, const char *what) {
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);
539
rtsp_send_request(s,"SET_PARAMETER",buf);
542
return rtsp_get_answers(s);
545
int rtsp_request_play(rtsp_t *s, const char *what) {
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);
556
rtsp_send_request(s,"PLAY",buf);
559
return rtsp_get_answers(s);
562
int rtsp_request_tearoff(rtsp_t *s, const char *what) {
564
rtsp_send_request(s,"TEAROFF",what);
566
return rtsp_get_answers(s);
570
* read opaque data from stream
573
int rtsp_read_data(rtsp_t *s, char *buffer, unsigned int size) {
578
i=read_stream(s->s, buffer, 4);
580
if (((buffer[0]=='S')&&(buffer[1]=='E')&&(buffer[2]=='T')&&(buffer[3]=='_')) ||
581
((buffer[0]=='O')&&(buffer[1]=='P')&&(buffer[2]=='T')&&(buffer[3]=='I'))) // OPTIONS
583
char *rest=rtsp_get(s);
593
if (!strncmp(rest,"Cseq:",5))
594
sscanf(rest,"Cseq: %u",&seq);
595
} while (strlen(rest)!=0);
599
mp_msg(MSGT_OPEN, MSGL_WARN, "rtsp: warning: cseq not recognized!\n");
603
/* let's make the server happy */
604
rtsp_put(s, "RTSP/1.0 451 Parameter Not Understood");
605
rest=malloc(sizeof(char)*16);
606
sprintf(rest,"CSeq: %u", seq);
609
i=read_stream(s->s, buffer, size);
612
i=read_stream(s->s, buffer+4, size-4);
616
i=read_stream(s->s, buffer, size);
618
mp_msg(MSGT_OPEN, MSGL_INFO, "librtsp: << %d of %d bytes\n", i, size);
625
* connect to a rtsp server
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) {
631
rtsp_t *s=malloc(sizeof(rtsp_t));
634
for (i=0; i<MAX_FIELDS; i++) {
636
s->scheduled[i]=NULL;
647
s->user_agent=strdup(user_agent);
649
s->user_agent=strdup("User-Agent: RealMedia Player Version 6.0.9.1235 (linux-2.0-libc6-i386-gcc2.95)");
651
s->mrl = strdup(mrl);
652
s->host = strdup(host);
654
s->path = strdup(path);
657
if ((s->param = strchr(s->path, '?')) != NULL)
659
//mp_msg(MSGT_OPEN, MSGL_INFO, "path=%s\n", s->path);
660
//mp_msg(MSGT_OPEN, MSGL_INFO, "param=%s\n", s->param ? s->param : "NULL");
664
mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: failed to connect to '%s'\n", s->host);
669
s->server_state=RTSP_CONNECTED;
671
/* now let's send an options request. */
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);
688
* closes an rtsp connection
691
void rtsp_close(rtsp_t *s) {
693
if (s->server_state) closesocket(s->s); /* TODO: send a TEAROFF */
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);
705
* search in answers for tags. returns a pointer to the content
706
* after the first matched tag. returns NULL if no match found.
709
char *rtsp_search_answers(rtsp_t *s, const char *tag) {
714
if (!s->answers) return NULL;
718
if (!strncasecmp(*answer,tag,strlen(tag))) {
719
ptr=strchr(*answer,':');
721
while(*ptr==' ') ptr++;
731
* session id management
734
void rtsp_set_session(rtsp_t *s, const char *id) {
736
if (s->session) free(s->session);
738
s->session=strdup(id);
742
char *rtsp_get_session(rtsp_t *s) {
748
char *rtsp_get_mrl(rtsp_t *s) {
754
char *rtsp_get_param(rtsp_t *s, char *p) {
760
return strdup(s->param);
763
while (param && *param) {
764
char *nparam = strchr(param, '&');
765
if (strncmp(param, p, len) == 0 && param[len] == '=') {
767
len = nparam ? nparam - param : strlen(param);
768
nparam = malloc(len + 1);
769
memcpy(nparam, param, len);
773
param = nparam ? nparam + 1 : NULL;
779
* schedules a field for transmission
782
void rtsp_schedule_field(rtsp_t *s, const char *string) {
788
while(s->scheduled[i]) {
791
s->scheduled[i]=strdup(string);
795
* removes the first scheduled field which prefix matches string.
798
void rtsp_unschedule_field(rtsp_t *s, const char *string) {
800
char **ptr=s->scheduled;
805
if (!strncmp(*ptr, string, strlen(string)))
808
if (*ptr) free(*ptr);
816
* unschedule all fields
819
void rtsp_unschedule_all(rtsp_t *s) {
823
if (!s->scheduled) return;
836
void rtsp_free_answers(rtsp_t *s) {
840
if (!s->answers) return;
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);
855
static int realrtsp_streaming_start( stream_t *stream ) {
857
rtsp_session_t *rtsp;
861
int redirected, temp;
862
if( stream==NULL ) return -1;
864
temp = 5; // counter so we don't get caught in infinite redirections (you never know)
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);
875
file = stream->streaming_ctrl->url->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);
880
rtsp = rtsp_session_start(fd,&mrl, file, stream->streaming_ctrl->url->hostname, port, &redirected, stream->streaming_ctrl->bandwidth);
882
if( redirected == 1) {
883
url_free(stream->streaming_ctrl->url);
884
stream->streaming_ctrl->url = url_new(mrl);
890
} while( (redirected != 0) && (temp > 0) );
895
stream->streaming_ctrl->data=rtsp;
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;
906
static int open_s(stream_t *stream,int mode, void* opts, int* file_format) {
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 )
914
stream->streaming_ctrl->bandwidth = network_bandwidth;
915
url = url_new(stream->url);
916
stream->streaming_ctrl->url = check4proxies(url);
920
if(realrtsp_streaming_start( stream ) < 0) {
921
streaming_ctrl_free(stream->streaming_ctrl);
922
stream->streaming_ctrl = NULL;
923
return STREAM_UNSUPORTED;
926
fixup_network_stream_cache(stream);
927
stream->type = STREAMTYPE_STREAM;
932
stream_info_t stream_info_rtsp = {
933
"RealNetworks rtsp streaming",
935
"Roberto Togni, xine team",
940
0 // Urls are an option string