comparison stream/librtsp/rtsp.c @ 19271:64d82a45a05d

introduce new 'stream' directory for all stream layer related components and split them from libmpdemux
author ben
date Mon, 31 Jul 2006 17:39:17 +0000
parents libmpdemux/librtsp/rtsp.c@5e767cabf4cd
children a3f6dc43b585
comparison
equal deleted inserted replaced
19270:7d39b911f0bd 19271:64d82a45a05d
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 * 2006, Benjamin Zores and Vincent Mussard
29 * fixed a lot of RFC compliance issues.
30 */
31
32 #include <unistd.h>
33 #include <stdio.h>
34 #include <assert.h>
35 #include "config.h"
36 #ifndef HAVE_WINSOCK2
37 #define closesocket close
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <netdb.h>
41 #else
42 #include <winsock2.h>
43 #endif
44 #include <string.h>
45 #include <sys/stat.h>
46 #include <fcntl.h>
47 #include <errno.h>
48 #include <stdlib.h>
49 #include <time.h>
50 #include <sys/time.h>
51 #include <sys/types.h>
52 #include <inttypes.h>
53
54 #include "mp_msg.h"
55 #include "rtsp.h"
56 #include "rtsp_session.h"
57 #include "osdep/timer.h"
58
59 /*
60 #define LOG
61 */
62
63 #define BUF_SIZE 4096
64 #define HEADER_SIZE 1024
65 #define MAX_FIELDS 256
66
67 struct rtsp_s {
68
69 int s;
70
71 char *host;
72 int port;
73 char *path;
74 char *param;
75 char *mrl;
76 char *user_agent;
77
78 char *server;
79 unsigned int server_state;
80 uint32_t server_caps;
81
82 unsigned int cseq;
83 char *session;
84
85 char *answers[MAX_FIELDS]; /* data of last message */
86 char *scheduled[MAX_FIELDS]; /* will be sent with next message */
87 };
88
89 /*
90 * constants
91 */
92
93 #define RTSP_PROTOCOL_VERSION "RTSP/1.0"
94
95 /* server states */
96 #define RTSP_CONNECTED 1
97 #define RTSP_INIT 2
98 #define RTSP_READY 4
99 #define RTSP_PLAYING 8
100 #define RTSP_RECORDING 16
101
102 /* server capabilities */
103 #define RTSP_OPTIONS 0x001
104 #define RTSP_DESCRIBE 0x002
105 #define RTSP_ANNOUNCE 0x004
106 #define RTSP_SETUP 0x008
107 #define RTSP_GET_PARAMETER 0x010
108 #define RTSP_SET_PARAMETER 0x020
109 #define RTSP_TEARDOWN 0x040
110 #define RTSP_PLAY 0x080
111 #define RTSP_RECORD 0x100
112
113 /*
114 * network utilities
115 */
116
117 static int write_stream(int s, const char *buf, int len) {
118 int total, timeout;
119
120 total = 0; timeout = 30;
121 while (total < len){
122 int n;
123
124 n = send (s, &buf[total], len - total, 0);
125
126 if (n > 0)
127 total += n;
128 else if (n < 0) {
129 #ifndef HAVE_WINSOCK2
130 if ((timeout>0) && ((errno == EAGAIN) || (errno == EINPROGRESS))) {
131 #else
132 if ((timeout>0) && ((errno == EAGAIN) || (WSAGetLastError() == WSAEINPROGRESS))) {
133 #endif
134 usec_sleep (1000000); timeout--;
135 } else
136 return -1;
137 }
138 }
139
140 return total;
141 }
142
143 static ssize_t read_stream(int fd, void *buf, size_t count) {
144
145 ssize_t ret, total;
146
147 total = 0;
148
149 while (total < count) {
150
151 ret=recv (fd, ((uint8_t*)buf)+total, count-total, 0);
152
153 if (ret<0) {
154 if(errno == EAGAIN) {
155 fd_set rset;
156 struct timeval timeout;
157
158 FD_ZERO (&rset);
159 FD_SET (fd, &rset);
160
161 timeout.tv_sec = 30;
162 timeout.tv_usec = 0;
163
164 if (select (fd+1, &rset, NULL, NULL, &timeout) <= 0) {
165 return -1;
166 }
167 continue;
168 }
169
170 mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: read error.\n");
171 return ret;
172 } else
173 total += ret;
174
175 /* end of stream */
176 if (!ret) break;
177 }
178
179 return total;
180 }
181
182 /*
183 * rtsp_get gets a line from stream
184 * and returns a null terminated string.
185 */
186
187 static char *rtsp_get(rtsp_t *s) {
188
189 int n=1;
190 char *buffer = malloc(BUF_SIZE);
191 char *string = NULL;
192
193 read_stream(s->s, buffer, 1);
194 while (n<BUF_SIZE) {
195 read_stream(s->s, &(buffer[n]), 1);
196 if ((buffer[n-1]==0x0d)&&(buffer[n]==0x0a)) break;
197 n++;
198 }
199
200 if (n>=BUF_SIZE) {
201 mp_msg(MSGT_OPEN, MSGL_FATAL, "librtsp: buffer overflow in rtsp_get\n");
202 exit(1);
203 }
204 string=malloc(n);
205 memcpy(string,buffer,n-1);
206 string[n-1]=0;
207
208 #ifdef LOG
209 mp_msg(MSGT_OPEN, MSGL_INFO, "librtsp: << '%s'\n", string);
210 #endif
211
212
213 free(buffer);
214 return string;
215 }
216
217 /*
218 * rtsp_put puts a line on stream
219 */
220
221 static void rtsp_put(rtsp_t *s, const char *string) {
222
223 int len=strlen(string);
224 char *buf=malloc(len+2);
225
226 #ifdef LOG
227 mp_msg(MSGT_OPEN, MSGL_INFO, "librtsp: >> '%s'", string);
228 #endif
229
230 memcpy(buf,string,len);
231 buf[len]=0x0d;
232 buf[len+1]=0x0a;
233
234 write_stream(s->s, buf, len+2);
235
236 #ifdef LOG
237 mp_msg(MSGT_OPEN, MSGL_INFO, " done.\n");
238 #endif
239
240 free(buf);
241 }
242
243 /*
244 * extract server status code
245 */
246
247 static int rtsp_get_code(const char *string) {
248
249 char buf[4];
250 int code=0;
251
252 if (!strncmp(string, RTSP_PROTOCOL_VERSION, strlen(RTSP_PROTOCOL_VERSION)))
253 {
254 memcpy(buf, string+strlen(RTSP_PROTOCOL_VERSION)+1, 3);
255 buf[3]=0;
256 code=atoi(buf);
257 } else if (!strncmp(string, RTSP_METHOD_SET_PARAMETER,8))
258 {
259 return RTSP_STATUS_SET_PARAMETER;
260 }
261
262 if(code != RTSP_STATUS_OK) mp_msg(MSGT_OPEN, MSGL_INFO, "librtsp: server responds: '%s'\n",string);
263
264 return code;
265 }
266
267 /*
268 * send a request
269 */
270
271 static void rtsp_send_request(rtsp_t *s, const char *type, const char *what) {
272
273 char **payload=s->scheduled;
274 char *buf;
275
276 buf = malloc(strlen(type)+strlen(what)+strlen(RTSP_PROTOCOL_VERSION)+3);
277
278 sprintf(buf,"%s %s %s",type, what, RTSP_PROTOCOL_VERSION);
279 rtsp_put(s,buf);
280 free(buf);
281 if (payload)
282 while (*payload) {
283 rtsp_put(s,*payload);
284 payload++;
285 }
286 rtsp_put(s,"");
287 rtsp_unschedule_all(s);
288 }
289
290 /*
291 * schedule standard fields
292 */
293
294 static void rtsp_schedule_standard(rtsp_t *s) {
295
296 char tmp[17];
297
298 snprintf(tmp, 17, "CSeq: %u", s->cseq);
299 rtsp_schedule_field(s, tmp);
300
301 if (s->session) {
302 char *buf;
303 buf = malloc(strlen(s->session)+15);
304 sprintf(buf, "Session: %s", s->session);
305 rtsp_schedule_field(s, buf);
306 free(buf);
307 }
308 }
309 /*
310 * get the answers, if server responses with something != 200, return NULL
311 */
312
313 static int rtsp_get_answers(rtsp_t *s) {
314
315 char *answer=NULL;
316 unsigned int answer_seq;
317 char **answer_ptr=s->answers;
318 int code;
319 int ans_count = 0;
320
321 answer=rtsp_get(s);
322 if (!answer)
323 return 0;
324 code=rtsp_get_code(answer);
325 free(answer);
326
327 rtsp_free_answers(s);
328
329 do { /* while we get answer lines */
330
331 answer=rtsp_get(s);
332 if (!answer)
333 return 0;
334
335 if (!strncasecmp(answer,"CSeq:",5)) {
336 sscanf(answer,"%*s %u",&answer_seq);
337 if (s->cseq != answer_seq) {
338 #ifdef LOG
339 mp_msg(MSGT_OPEN, MSGL_WARN, "librtsp: warning: CSeq mismatch. got %u, assumed %u", answer_seq, s->cseq);
340 #endif
341 s->cseq=answer_seq;
342 }
343 }
344 if (!strncasecmp(answer,"Server:",7)) {
345 char *buf = malloc(strlen(answer));
346 sscanf(answer,"%*s %s",buf);
347 if (s->server) free(s->server);
348 s->server=strdup(buf);
349 free(buf);
350 }
351 if (!strncasecmp(answer,"Session:",8)) {
352 char *buf = calloc(1, strlen(answer));
353 sscanf(answer,"%*s %s",buf);
354 if (s->session) {
355 if (strcmp(buf, s->session)) {
356 mp_msg(MSGT_OPEN, MSGL_WARN, "rtsp: warning: setting NEW session: %s\n", buf);
357 free(s->session);
358 s->session=strdup(buf);
359 }
360 } else
361 {
362 #ifdef LOG
363 mp_msg(MSGT_OPEN, MSGL_INFO, "rtsp: setting session id to: %s\n", buf);
364 #endif
365 s->session=strdup(buf);
366 }
367 free(buf);
368 }
369 *answer_ptr=answer;
370 answer_ptr++;
371 } while ((strlen(answer)!=0) && (++ans_count < MAX_FIELDS));
372
373 s->cseq++;
374
375 *answer_ptr=NULL;
376 rtsp_schedule_standard(s);
377
378 return code;
379 }
380
381 /*
382 * send an ok message
383 */
384
385 int rtsp_send_ok(rtsp_t *s) {
386 char cseq[16];
387
388 rtsp_put(s, "RTSP/1.0 200 OK");
389 sprintf(cseq,"CSeq: %u", s->cseq);
390 rtsp_put(s, cseq);
391 rtsp_put(s, "");
392 return 0;
393 }
394
395 /*
396 * implementation of must-have rtsp requests; functions return
397 * server status code.
398 */
399
400 int rtsp_request_options(rtsp_t *s, const char *what) {
401
402 char *buf;
403
404 if (what) {
405 buf=strdup(what);
406 } else
407 {
408 buf=malloc(strlen(s->host)+16);
409 sprintf(buf,"rtsp://%s:%i", s->host, s->port);
410 }
411 rtsp_send_request(s,RTSP_METHOD_OPTIONS,buf);
412 free(buf);
413
414 return rtsp_get_answers(s);
415 }
416
417 int rtsp_request_describe(rtsp_t *s, const char *what) {
418
419 char *buf;
420
421 if (what) {
422 buf=strdup(what);
423 } else
424 {
425 buf=malloc(strlen(s->host)+strlen(s->path)+16);
426 sprintf(buf,"rtsp://%s:%i/%s", s->host, s->port, s->path);
427 }
428 rtsp_send_request(s,RTSP_METHOD_DESCRIBE,buf);
429 free(buf);
430
431 return rtsp_get_answers(s);
432 }
433
434 int rtsp_request_setup(rtsp_t *s, const char *what, char *control) {
435
436 char *buf = NULL;
437
438 if (what)
439 buf = strdup (what);
440 else
441 {
442 int len = strlen (s->host) + strlen (s->path) + 16;
443 if (control)
444 len += strlen (control) + 1;
445
446 buf = malloc (len);
447 sprintf (buf, "rtsp://%s:%i/%s%s%s", s->host, s->port, s->path,
448 control ? "/" : "", control ? control : "");
449 }
450
451 rtsp_send_request (s, RTSP_METHOD_SETUP, buf);
452 free (buf);
453 return rtsp_get_answers (s);
454 }
455
456 int rtsp_request_setparameter(rtsp_t *s, const char *what) {
457
458 char *buf;
459
460 if (what) {
461 buf=strdup(what);
462 } else
463 {
464 buf=malloc(strlen(s->host)+strlen(s->path)+16);
465 sprintf(buf,"rtsp://%s:%i/%s", s->host, s->port, s->path);
466 }
467 rtsp_send_request(s,RTSP_METHOD_SET_PARAMETER,buf);
468 free(buf);
469
470 return rtsp_get_answers(s);
471 }
472
473 int rtsp_request_play(rtsp_t *s, const char *what) {
474
475 char *buf;
476 int ret;
477
478 if (what) {
479 buf=strdup(what);
480 } else
481 {
482 buf=malloc(strlen(s->host)+strlen(s->path)+16);
483 sprintf(buf,"rtsp://%s:%i/%s", s->host, s->port, s->path);
484 }
485 rtsp_send_request(s,RTSP_METHOD_PLAY,buf);
486 free(buf);
487
488 ret = rtsp_get_answers (s);
489 if (ret == RTSP_STATUS_OK)
490 s->server_state = RTSP_PLAYING;
491
492 return ret;
493 }
494
495 int rtsp_request_teardown(rtsp_t *s, const char *what) {
496
497 char *buf;
498
499 if (what)
500 buf = strdup (what);
501 else
502 {
503 buf =
504 malloc (strlen (s->host) + strlen (s->path) + 16);
505 sprintf (buf, "rtsp://%s:%i/%s", s->host, s->port, s->path);
506 }
507 rtsp_send_request (s, RTSP_METHOD_TEARDOWN, buf);
508 free (buf);
509
510 /* after teardown we're done with RTSP streaming, no need to get answer as
511 reading more will only result to garbage and buffer overflow */
512 return RTSP_STATUS_OK;
513 }
514
515 /*
516 * read opaque data from stream
517 */
518
519 int rtsp_read_data(rtsp_t *s, char *buffer, unsigned int size) {
520
521 int i,seq;
522
523 if (size>=4) {
524 i=read_stream(s->s, buffer, 4);
525 if (i<4) return i;
526 if (((buffer[0]=='S')&&(buffer[1]=='E')&&(buffer[2]=='T')&&(buffer[3]=='_')) ||
527 ((buffer[0]=='O')&&(buffer[1]=='P')&&(buffer[2]=='T')&&(buffer[3]=='I'))) // OPTIONS
528 {
529 char *rest=rtsp_get(s);
530 if (!rest)
531 return -1;
532
533 seq=-1;
534 do {
535 free(rest);
536 rest=rtsp_get(s);
537 if (!rest)
538 return -1;
539 if (!strncasecmp(rest,"CSeq:",5))
540 sscanf(rest,"%*s %u",&seq);
541 } while (strlen(rest)!=0);
542 free(rest);
543 if (seq<0) {
544 #ifdef LOG
545 mp_msg(MSGT_OPEN, MSGL_WARN, "rtsp: warning: CSeq not recognized!\n");
546 #endif
547 seq=1;
548 }
549 /* let's make the server happy */
550 rtsp_put(s, "RTSP/1.0 451 Parameter Not Understood");
551 rest=malloc(17);
552 sprintf(rest,"CSeq: %u", seq);
553 rtsp_put(s, rest);
554 free(rest);
555 rtsp_put(s, "");
556 i=read_stream(s->s, buffer, size);
557 } else
558 {
559 i=read_stream(s->s, buffer+4, size-4);
560 i+=4;
561 }
562 } else
563 i=read_stream(s->s, buffer, size);
564 #ifdef LOG
565 mp_msg(MSGT_OPEN, MSGL_INFO, "librtsp: << %d of %d bytes\n", i, size);
566 #endif
567
568 return i;
569 }
570
571 /*
572 * connect to a rtsp server
573 */
574
575 //rtsp_t *rtsp_connect(const char *mrl, const char *user_agent) {
576 rtsp_t *rtsp_connect(int fd, char* mrl, char *path, char *host, int port, char *user_agent) {
577
578 rtsp_t *s=malloc(sizeof(rtsp_t));
579 int i;
580
581 for (i=0; i<MAX_FIELDS; i++) {
582 s->answers[i]=NULL;
583 s->scheduled[i]=NULL;
584 }
585
586 s->server=NULL;
587 s->server_state=0;
588 s->server_caps=0;
589
590 s->cseq=0;
591 s->session=NULL;
592
593 if (user_agent)
594 s->user_agent=strdup(user_agent);
595 else
596 s->user_agent=strdup("User-Agent: RealMedia Player Version 6.0.9.1235 (linux-2.0-libc6-i386-gcc2.95)");
597
598 s->mrl = strdup(mrl);
599 s->host = strdup(host);
600 s->port = port;
601 s->path = strdup(path);
602 while (*path == '/')
603 path++;
604 if ((s->param = strchr(s->path, '?')) != NULL)
605 s->param++;
606 //mp_msg(MSGT_OPEN, MSGL_INFO, "path=%s\n", s->path);
607 //mp_msg(MSGT_OPEN, MSGL_INFO, "param=%s\n", s->param ? s->param : "NULL");
608 s->s = fd;
609
610 if (s->s < 0) {
611 mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: failed to connect to '%s'\n", s->host);
612 rtsp_close(s);
613 return NULL;
614 }
615
616 s->server_state=RTSP_CONNECTED;
617
618 /* now let's send an options request. */
619 rtsp_schedule_field(s, "CSeq: 1");
620 rtsp_schedule_field(s, s->user_agent);
621 rtsp_schedule_field(s, "ClientChallenge: 9e26d33f2984236010ef6253fb1887f7");
622 rtsp_schedule_field(s, "PlayerStarttime: [28/03/2003:22:50:23 00:00]");
623 rtsp_schedule_field(s, "CompanyID: KnKV4M4I/B2FjJ1TToLycw==");
624 rtsp_schedule_field(s, "GUID: 00000000-0000-0000-0000-000000000000");
625 rtsp_schedule_field(s, "RegionData: 0");
626 rtsp_schedule_field(s, "ClientID: Linux_2.4_6.0.9.1235_play32_RN01_EN_586");
627 /*rtsp_schedule_field(s, "Pragma: initiate-session");*/
628 rtsp_request_options(s, NULL);
629
630 return s;
631 }
632
633
634 /*
635 * closes an rtsp connection
636 */
637
638 void rtsp_close(rtsp_t *s) {
639
640 if (s->server_state)
641 {
642 if (s->server_state == RTSP_PLAYING)
643 rtsp_request_teardown (s, NULL);
644 closesocket (s->s);
645 }
646
647 if (s->path) free(s->path);
648 if (s->host) free(s->host);
649 if (s->mrl) free(s->mrl);
650 if (s->session) free(s->session);
651 if (s->user_agent) free(s->user_agent);
652 rtsp_free_answers(s);
653 rtsp_unschedule_all(s);
654 free(s);
655 }
656
657 /*
658 * search in answers for tags. returns a pointer to the content
659 * after the first matched tag. returns NULL if no match found.
660 */
661
662 char *rtsp_search_answers(rtsp_t *s, const char *tag) {
663
664 char **answer;
665 char *ptr;
666
667 if (!s->answers) return NULL;
668 answer=s->answers;
669
670 while (*answer) {
671 if (!strncasecmp(*answer,tag,strlen(tag))) {
672 ptr=strchr(*answer,':');
673 if (!ptr) return NULL;
674 ptr++;
675 while(*ptr==' ') ptr++;
676 return ptr;
677 }
678 answer++;
679 }
680
681 return NULL;
682 }
683
684 /*
685 * session id management
686 */
687
688 void rtsp_set_session(rtsp_t *s, const char *id) {
689
690 if (s->session) free(s->session);
691
692 s->session=strdup(id);
693
694 }
695
696 char *rtsp_get_session(rtsp_t *s) {
697
698 return s->session;
699
700 }
701
702 char *rtsp_get_mrl(rtsp_t *s) {
703
704 return s->mrl;
705
706 }
707
708 char *rtsp_get_param(rtsp_t *s, const char *p) {
709 int len;
710 char *param;
711 if (!s->param)
712 return NULL;
713 if (!p)
714 return strdup(s->param);
715 len = strlen(p);
716 param = s->param;
717 while (param && *param) {
718 char *nparam = strchr(param, '&');
719 if (strncmp(param, p, len) == 0 && param[len] == '=') {
720 param += len + 1;
721 len = nparam ? nparam - param : strlen(param);
722 nparam = malloc(len + 1);
723 memcpy(nparam, param, len);
724 nparam[len] = 0;
725 return nparam;
726 }
727 param = nparam ? nparam + 1 : NULL;
728 }
729 return NULL;
730 }
731
732 /*
733 * schedules a field for transmission
734 */
735
736 void rtsp_schedule_field(rtsp_t *s, const char *string) {
737
738 int i=0;
739
740 if (!string) return;
741
742 while(s->scheduled[i]) {
743 i++;
744 }
745 s->scheduled[i]=strdup(string);
746 }
747
748 /*
749 * removes the first scheduled field which prefix matches string.
750 */
751
752 void rtsp_unschedule_field(rtsp_t *s, const char *string) {
753
754 char **ptr=s->scheduled;
755
756 if (!string) return;
757
758 while(*ptr) {
759 if (!strncmp(*ptr, string, strlen(string)))
760 break;
761 else
762 ptr++;
763 }
764 if (*ptr) free(*ptr);
765 ptr++;
766 do {
767 *(ptr-1)=*ptr;
768 } while(*ptr);
769 }
770
771 /*
772 * unschedule all fields
773 */
774
775 void rtsp_unschedule_all(rtsp_t *s) {
776
777 char **ptr;
778
779 if (!s->scheduled) return;
780 ptr=s->scheduled;
781
782 while (*ptr) {
783 free(*ptr);
784 *ptr=NULL;
785 ptr++;
786 }
787 }
788 /*
789 * free answers
790 */
791
792 void rtsp_free_answers(rtsp_t *s) {
793
794 char **answer;
795
796 if (!s->answers) return;
797 answer=s->answers;
798
799 while (*answer) {
800 free(*answer);
801 *answer=NULL;
802 answer++;
803 }
804 }