demux/playlist: xspf: only use text-elements inside current tag
[vlc.git] / modules / access / rtsp / rtsp.c
blobe6e4c749b686116bd9d5c4ef0d10756262550ddd
1 /*****************************************************************************
2 * rtsp.c: minimalistic implementation of rtsp protocol.
3 * Not RFC 2326 compilant yet and only handle REAL RTSP.
4 *****************************************************************************
5 * Copyright (C) 2002-2004 the xine project
6 * Copyright (C) 2005 VideoLAN
7 * $Id$
9 * Authors: Gildas Bazin <gbazin@videolan.org>
10 * Adapted from xine which itself adapted it from joschkas real tools.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <vlc_common.h>
32 #include <vlc_access.h>
33 #include <vlc_messages.h>
35 #include "rtsp.h"
37 #define BUF_SIZE 4096
38 #define HEADER_SIZE 1024
39 #define MAX_FIELDS 256
41 struct rtsp_s {
43 int s;
45 char *host;
46 int port;
47 char *path;
48 char *mrl;
49 char *user_agent;
51 char *server;
52 unsigned int server_state;
53 uint32_t server_caps;
55 unsigned int cseq;
56 char *session;
58 char *answers[MAX_FIELDS]; /* data of last message */
59 char *scheduled[MAX_FIELDS]; /* will be sent with next message */
63 * constants
66 const char rtsp_protocol_version[]="RTSP/1.0";
68 /* server states */
69 #define RTSP_CONNECTED 1
70 #define RTSP_INIT 2
71 #define RTSP_READY 4
72 #define RTSP_PLAYING 8
73 #define RTSP_RECORDING 16
75 /* server capabilities */
76 #define RTSP_OPTIONS 0x001
77 #define RTSP_DESCRIBE 0x002
78 #define RTSP_ANNOUNCE 0x004
79 #define RTSP_SETUP 0x008
80 #define RTSP_GET_PARAMETER 0x010
81 #define RTSP_SET_PARAMETER 0x020
82 #define RTSP_TEARDOWN 0x040
83 #define RTSP_PLAY 0x080
84 #define RTSP_RECORD 0x100
87 * rtsp_get gets a line from stream
88 * and returns a null terminated string (must be freed).
91 static char *rtsp_get( rtsp_client_t *rtsp )
93 char *psz_buffer = xmalloc( BUF_SIZE );
94 char *psz_string = NULL;
96 if( rtsp->pf_read_line( rtsp->p_userdata, (uint8_t*)psz_buffer, (unsigned int)BUF_SIZE ) >= 0 )
98 //printf( "<< '%s'\n", psz_buffer );
99 psz_string = strdup( psz_buffer );
102 free( psz_buffer );
103 return psz_string;
108 * rtsp_put puts a line on stream
111 static int rtsp_put( rtsp_client_t *rtsp, const char *psz_string )
113 unsigned int i_buffer = strlen( psz_string );
114 uint8_t *psz_buffer = xmalloc( i_buffer + 2 );
115 int i_ret;
117 memcpy( psz_buffer, psz_string, i_buffer );
118 psz_buffer[i_buffer] = '\r'; psz_buffer[i_buffer+1] = '\n';
120 i_ret = rtsp->pf_write( rtsp->p_userdata, psz_buffer, i_buffer + 2 );
122 free( psz_buffer );
123 return i_ret;
127 * extract server status code
130 static int rtsp_get_status_code( rtsp_client_t *rtsp, const char *psz_string )
132 VLC_UNUSED(rtsp);
133 char psz_buffer[4] = {0,0,0,0};
134 int i_code = 0;
136 if( !strncmp( psz_string, "RTSP/1.0", sizeof("RTSP/1.0") - 1 ) )
138 strncpy(psz_buffer, psz_string + sizeof("RTSP/1.0"), 3);
139 i_code = atoi( psz_buffer );
141 else if( !strncmp( psz_string, "SET_PARAMETER", sizeof("SET_PARAMETER") - 1 ) )
143 return RTSP_STATUS_SET_PARAMETER;
146 if( i_code != 200 )
148 //fprintf( stderr, "librtsp: server responds: '%s'\n", psz_string );
151 return i_code;
155 * send a request
158 static int rtsp_send_request( rtsp_client_t *rtsp, const char *psz_type,
159 const char *psz_what )
161 char **ppsz_payload;
162 char *psz_buffer;
163 int i_ret, i;
165 if (rtsp->p_private == NULL)
166 return -1;
168 ppsz_payload = rtsp->p_private->scheduled;
170 psz_buffer = xmalloc( strlen(psz_type) + strlen(psz_what) +
171 sizeof("RTSP/1.0") + 2 );
173 sprintf( psz_buffer, "%s %s %s", psz_type, psz_what, "RTSP/1.0" );
174 i_ret = rtsp_put( rtsp, psz_buffer );
175 free( psz_buffer );
177 for (i = 0; i < MAX_FIELDS; ++i) {
178 if (!ppsz_payload[i])
179 break;
181 rtsp_put (rtsp, ppsz_payload[i]);
184 rtsp_put( rtsp, "" );
185 rtsp_unschedule_all( rtsp );
187 return i_ret;
191 * schedule standard fields
194 static void rtsp_schedule_standard( rtsp_client_t *rtsp )
196 char tmp[sizeof("CSeq: ") + 3 * sizeof(int)];
198 sprintf( tmp, "CSeq: %u", rtsp->p_private->cseq);
199 rtsp_schedule_field( rtsp, tmp );
201 if( rtsp->p_private->session )
203 char *buf;
204 buf = xmalloc( strlen(rtsp->p_private->session) + 15 );
205 sprintf( buf, "Session: %s", rtsp->p_private->session );
206 rtsp_schedule_field( rtsp, buf );
207 free( buf );
212 * get the answers, if server responses with something != 200, return NULL
215 static int rtsp_get_answers( rtsp_client_t *rtsp )
217 access_t *p_access = (access_t*)rtsp->p_userdata;
218 char *answer = NULL;
219 unsigned int answer_seq;
220 char **answer_ptr = rtsp->p_private->answers;
221 int code;
222 int ans_count = 0;
224 answer = rtsp_get( rtsp );
225 if( !answer ) return 0;
226 code = rtsp_get_status_code( rtsp, answer );
227 free( answer );
229 rtsp_free_answers( rtsp );
231 do { /* while we get answer lines */
233 answer = rtsp_get( rtsp );
234 if( !answer ) return 0;
236 if( !strncasecmp( answer, "CSeq:", 5 ) )
238 if (sscanf( answer, "%*s %u", &answer_seq ) == 1) {
239 if( rtsp->p_private->cseq != answer_seq )
241 msg_Warn (p_access, "Cseq mismatch, got %u, assumed %u", answer_seq, rtsp->p_private->cseq);
242 rtsp->p_private->cseq = answer_seq;
244 } else {
245 msg_Warn (p_access, "remote server sent CSeq without payload, ignoring.");
248 if( !strncasecmp( answer, "Server:", 7 ) )
250 char *buf = xmalloc( strlen(answer) );
251 if (sscanf( answer, "%*s %s", buf ) == 1) {
252 free( rtsp->p_private->server );
253 rtsp->p_private->server = buf;
254 } else {
255 msg_Warn(p_access, "remote server sent Server without payload, ignoring.");
258 if( !strncasecmp( answer, "Session:", 8 ) )
260 char *buf = xmalloc( strlen(answer) );
261 if (sscanf( answer, "%*s %s", buf ) == 1) { // TODO: ignore attributes "Session: ${session-id};${attribute=value...}"
262 if( rtsp->p_private->session )
264 if( strcmp( buf, rtsp->p_private->session ) )
266 msg_Warn (p_access, "setting NEW session: %s", buf);
267 free( rtsp->p_private->session );
268 rtsp->p_private->session = strdup( buf );
271 else
273 msg_Dbg (p_access, "session id: '%s'", buf);
274 rtsp->p_private->session = strdup( buf );
276 } else {
277 msg_Warn(p_access, "remote server sent Session without payload, ignoring.");
279 free( buf );
282 *answer_ptr = answer;
283 answer_ptr++;
284 } while( (strlen(answer) != 0) && (++ans_count < MAX_FIELDS) );
286 rtsp->p_private->cseq++;
288 if (ans_count != MAX_FIELDS) {
289 *answer_ptr = NULL;
292 rtsp_schedule_standard( rtsp );
294 return code;
298 * send an ok message
301 int rtsp_send_ok( rtsp_client_t *rtsp )
303 char cseq[sizeof("CSeq: ") + 3 * sizeof(int)];
305 rtsp_put( rtsp, "RTSP/1.0 200 OK" );
306 sprintf( cseq, "CSeq: %u", rtsp->p_private->cseq );
307 rtsp_put( rtsp, cseq );
308 rtsp_put( rtsp, "" );
309 return 0;
313 * implementation of must-have rtsp requests; functions return
314 * server status code.
317 int rtsp_request_options( rtsp_client_t *rtsp, const char *what )
319 char *buf;
321 if( what ) buf = strdup(what);
322 else
324 buf = xmalloc( strlen(rtsp->p_private->host) + 16 );
325 sprintf( buf, "rtsp://%s:%i", rtsp->p_private->host,
326 rtsp->p_private->port );
328 rtsp_send_request( rtsp, "OPTIONS", buf );
329 free( buf );
331 return rtsp_get_answers( rtsp );
334 int rtsp_request_describe( rtsp_client_t *rtsp, const char *what )
336 char *buf;
338 if( what )
340 buf = strdup(what);
342 else
344 buf = xmalloc( strlen(rtsp->p_private->host) +
345 strlen(rtsp->p_private->path) + 16 );
346 sprintf( buf, "rtsp://%s:%i/%s", rtsp->p_private->host,
347 rtsp->p_private->port, rtsp->p_private->path );
349 rtsp_send_request( rtsp, "DESCRIBE", buf );
350 free( buf );
352 return rtsp_get_answers( rtsp );
355 int rtsp_request_setup( rtsp_client_t *rtsp, const char *what )
357 rtsp_send_request( rtsp, "SETUP", what );
358 return rtsp_get_answers( rtsp );
361 int rtsp_request_setparameter( rtsp_client_t *rtsp, const char *what )
363 char *buf;
365 if( what )
367 buf = strdup(what);
369 else
371 buf = xmalloc( strlen(rtsp->p_private->host) +
372 strlen(rtsp->p_private->path) + 16 );
373 sprintf( buf, "rtsp://%s:%i/%s", rtsp->p_private->host,
374 rtsp->p_private->port, rtsp->p_private->path );
377 rtsp_send_request( rtsp, "SET_PARAMETER", buf );
378 free( buf );
380 return rtsp_get_answers( rtsp );
383 int rtsp_request_play( rtsp_client_t *rtsp, const char *what )
385 char *buf;
387 if( what )
389 buf = strdup( what );
391 else
393 buf = xmalloc( strlen(rtsp->p_private->host) +
394 strlen(rtsp->p_private->path) + 16 );
395 sprintf( buf, "rtsp://%s:%i/%s", rtsp->p_private->host,
396 rtsp->p_private->port, rtsp->p_private->path );
399 rtsp_send_request( rtsp, "PLAY", buf );
400 free( buf );
402 return rtsp_get_answers( rtsp );
405 int rtsp_request_tearoff( rtsp_client_t *rtsp, const char *what )
407 rtsp_send_request( rtsp, "TEAROFF", what );
408 return rtsp_get_answers( rtsp );
412 * read opaque data from stream
415 int rtsp_read_data( rtsp_client_t *rtsp, uint8_t *buffer, unsigned int size )
417 int i, seq;
419 if( size >= 4 )
421 i = rtsp->pf_read( rtsp->p_userdata, (uint8_t*)buffer, (unsigned int) 4 );
422 if( i < 4 ) return i;
424 if( buffer[0]=='S' && buffer[1]=='E' && buffer[2]=='T' &&
425 buffer[3]=='_' )
427 char *rest = rtsp_get( rtsp );
428 if( !rest ) return -1;
430 seq = -1;
433 free( rest );
434 rest = rtsp_get( rtsp );
435 if( !rest ) return -1;
437 if( !strncasecmp( rest, "CSeq:", 5 ) )
438 sscanf( rest, "%*s %u", &seq );
439 } while( *rest );
440 free( rest );
442 if( seq < 0 )
444 //fprintf(stderr, "warning: cseq not recognized!\n");
445 seq = 1;
448 /* lets make the server happy */
449 rtsp_put( rtsp, "RTSP/1.0 451 Parameter Not Understood" );
450 rest = xmalloc(sizeof("Cseq: ") + 3 * sizeof(int));
451 sprintf( rest,"CSeq: %u", seq );
452 rtsp_put( rtsp, rest );
453 rtsp_put( rtsp, "" );
454 free( rest );
455 i = rtsp->pf_read( rtsp->p_userdata, (unsigned char*)buffer, size );
457 else
459 i = rtsp->pf_read( rtsp->p_userdata, (unsigned char*)buffer + 4, size - 4 );
460 i += 4;
463 else i = rtsp->pf_read( rtsp->p_userdata, (unsigned char*)buffer, size );
465 //fprintf( stderr, "<< %d of %d bytes\n", i, size );
467 return i;
471 * connect to a rtsp server
474 int rtsp_connect( rtsp_client_t *rtsp, const char *psz_mrl,
475 const char *psz_user_agent )
477 rtsp_t *s;
478 char *mrl_ptr;
479 char *slash, *colon;
480 unsigned int hostend, pathbegin, i;
482 if( !psz_mrl ) return -1;
483 s = xmalloc( sizeof(rtsp_t) );
484 rtsp->p_private = s;
486 if( !strncmp( psz_mrl, "rtsp://", 7 ) ) psz_mrl += 7;
487 mrl_ptr = strdup( psz_mrl );
489 for( i=0; i<MAX_FIELDS; i++ )
491 s->answers[i]=NULL;
492 s->scheduled[i]=NULL;
495 s->host = NULL;
496 s->port = 554; /* rtsp standard port */
497 s->path = NULL;
498 s->mrl = strdup(psz_mrl);
500 s->server = NULL;
501 s->server_state = 0;
502 s->server_caps = 0;
504 s->cseq = 0;
505 s->session = NULL;
507 if( psz_user_agent ) s->user_agent = strdup( psz_user_agent );
508 else s->user_agent = strdup( "User-Agent: RealMedia Player Version "
509 "6.0.9.1235 (linux-2.0-libc6-i386-gcc2.95)" );
511 slash = strchr( mrl_ptr, '/' );
512 colon = strchr( mrl_ptr, ':' );
514 if( !slash ) slash = mrl_ptr + strlen(mrl_ptr) + 1;
515 if( !colon ) colon = slash;
516 if( colon > slash ) colon = slash;
518 pathbegin = slash - mrl_ptr;
519 hostend = colon - mrl_ptr;
521 s->host = xmalloc(hostend+1);
522 strncpy( s->host, mrl_ptr, hostend );
523 s->host[hostend] = 0;
525 if( pathbegin < strlen(mrl_ptr) ) s->path = strdup(mrl_ptr+pathbegin+1);
526 if( colon != slash )
528 char buffer[pathbegin-hostend];
530 strncpy( buffer, mrl_ptr+hostend+1, pathbegin-hostend-1 );
531 buffer[pathbegin-hostend-1] = 0;
532 s->port = atoi(buffer);
533 if( s->port < 0 || s->port > 65535 ) s->port = 554;
536 free( mrl_ptr );
537 //fprintf( stderr, "got mrl: %s %i %s\n", s->host, s->port, s->path );
539 s->s = rtsp->pf_connect( rtsp->p_userdata, s->host, s->port );
541 if( s->s < 0 )
543 //fprintf(stderr, "rtsp: failed to connect to '%s'\n", s->host);
544 rtsp_close( rtsp );
545 return -1;
548 s->server_state = RTSP_CONNECTED;
550 /* now lets send an options request. */
551 rtsp_schedule_field( rtsp, "CSeq: 1");
552 rtsp_schedule_field( rtsp, s->user_agent);
553 rtsp_schedule_field( rtsp, "ClientChallenge: "
554 "9e26d33f2984236010ef6253fb1887f7");
555 rtsp_schedule_field( rtsp, "PlayerStarttime: [28/03/2003:22:50:23 00:00]");
556 rtsp_schedule_field( rtsp, "CompanyID: KnKV4M4I/B2FjJ1TToLycw==" );
557 rtsp_schedule_field( rtsp, "GUID: 00000000-0000-0000-0000-000000000000" );
558 rtsp_schedule_field( rtsp, "RegionData: 0" );
559 rtsp_schedule_field( rtsp, "ClientID: "
560 "Linux_2.4_6.0.9.1235_play32_RN01_EN_586" );
561 /*rtsp_schedule_field( rtsp, "Pragma: initiate-session" );*/
562 rtsp_request_options( rtsp, NULL );
564 return 0;
568 * closes an rtsp connection
571 void rtsp_close( rtsp_client_t *rtsp )
573 if( rtsp->p_private->server_state )
575 /* TODO: send a TEAROFF */
576 rtsp->pf_disconnect( rtsp->p_userdata );
579 free( rtsp->p_private->path );
580 free( rtsp->p_private->host );
581 free( rtsp->p_private->mrl );
582 free( rtsp->p_private->session );
583 free( rtsp->p_private->user_agent );
584 free( rtsp->p_private->server );
585 rtsp_free_answers( rtsp );
586 rtsp_unschedule_all( rtsp );
587 free( rtsp->p_private );
591 * search in answers for tags. returns a pointer to the content
592 * after the first matched tag. returns NULL if no match found.
595 char *rtsp_search_answers( rtsp_client_t *rtsp, const char *tag )
597 char **answers;
598 char *ptr;
599 int i;
601 if(rtsp->p_private->answers == NULL || tag == NULL)
602 return NULL;
604 answers = rtsp->p_private->answers;
606 for (i = 0; i < MAX_FIELDS; ++i) {
607 if (answers[i] == NULL)
608 break;
610 if (!strncasecmp(answers[i], tag, strlen(tag))){
611 ptr = strchr(answers[i], ':');
613 if (ptr == NULL)
614 return answers[i] + strlen(answers[i]); /* no payload => empty string */
616 for (++ptr; *ptr == ' '; ++ptr)
619 return ptr;
623 return NULL;
627 * session id management
630 void rtsp_set_session( rtsp_client_t *rtsp, const char *id )
632 free( rtsp->p_private->session );
633 rtsp->p_private->session = strdup(id);
636 char *rtsp_get_session( rtsp_client_t *rtsp )
638 return rtsp->p_private->session;
641 char *rtsp_get_mrl( rtsp_client_t *rtsp )
643 return rtsp->p_private->mrl;
647 * schedules a field for transmission
650 void rtsp_schedule_field( rtsp_client_t *rtsp, const char *data )
652 access_t * p_access = (access_t*)rtsp->p_userdata;
653 char **pptr;
654 int i = 0;
656 if( rtsp->p_private == NULL || data == NULL)
657 return;
659 pptr = rtsp->p_private->scheduled;
661 for (i = 0; i < MAX_FIELDS; ++i) {
662 if (pptr[i] == NULL) {
663 pptr[i] = strdup(data);
664 break;
668 if (i == MAX_FIELDS) {
669 msg_Warn (p_access, "Unable to schedule '%s': the buffer is full!", data);
674 * removes the first scheduled field which prefix matches string.
677 void rtsp_unschedule_field( rtsp_client_t *rtsp, const char *needle )
679 char **pptr;
680 int i;
682 if (rtsp->p_private == NULL || needle == NULL)
683 return;
685 pptr = rtsp->p_private->scheduled;
687 for (i = 0; i < MAX_FIELDS; ++i) {
688 if (pptr[i] == NULL)
689 break;
691 if (!strncmp (pptr[i], needle, strlen(needle))) {
692 free (pptr[i]);
693 pptr[i] = NULL;
694 break;
698 for (i++; i < MAX_FIELDS && pptr[i]; ++i) {
699 pptr[i-1] = pptr[i];
702 if (i < MAX_FIELDS) {
703 pptr[i] = NULL;
707 static void pp_free_helper_ (char **pptr, int max_length) {
708 int i;
710 for (i = 0; i < max_length; ++i) {
711 if (pptr[i] == NULL)
712 break;
714 free (pptr[i]);
715 pptr[i] = NULL;
720 * unschedule all fields
723 void rtsp_unschedule_all( rtsp_client_t *rtsp )
725 if (rtsp->p_private == NULL)
726 return;
728 pp_free_helper_ (rtsp->p_private->scheduled, MAX_FIELDS);
731 * free answers
734 void rtsp_free_answers( rtsp_client_t *rtsp )
736 if (rtsp->p_private == NULL)
737 return;
739 pp_free_helper_ (rtsp->p_private->answers, MAX_FIELDS);