contrib: theora: do not run autoreconf
[vlc/gmpfix.git] / modules / access / rtsp / rtsp.c
blob7b1e606df0d1d9363bd9757e616027f269d7c2fe
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>
33 #include "rtsp.h"
35 #define BUF_SIZE 4096
36 #define HEADER_SIZE 1024
37 #define MAX_FIELDS 256
39 struct rtsp_s {
41 int s;
43 char *host;
44 int port;
45 char *path;
46 char *mrl;
47 char *user_agent;
49 char *server;
50 unsigned int server_state;
51 uint32_t server_caps;
53 unsigned int cseq;
54 char *session;
56 char *answers[MAX_FIELDS]; /* data of last message */
57 char *scheduled[MAX_FIELDS]; /* will be sent with next message */
61 * constants
64 const char rtsp_protocol_version[]="RTSP/1.0";
66 /* server states */
67 #define RTSP_CONNECTED 1
68 #define RTSP_INIT 2
69 #define RTSP_READY 4
70 #define RTSP_PLAYING 8
71 #define RTSP_RECORDING 16
73 /* server capabilities */
74 #define RTSP_OPTIONS 0x001
75 #define RTSP_DESCRIBE 0x002
76 #define RTSP_ANNOUNCE 0x004
77 #define RTSP_SETUP 0x008
78 #define RTSP_GET_PARAMETER 0x010
79 #define RTSP_SET_PARAMETER 0x020
80 #define RTSP_TEARDOWN 0x040
81 #define RTSP_PLAY 0x080
82 #define RTSP_RECORD 0x100
85 * rtsp_get gets a line from stream
86 * and returns a null terminated string (must be freed).
89 static char *rtsp_get( rtsp_client_t *rtsp )
91 char *psz_buffer = malloc( BUF_SIZE );
92 char *psz_string = NULL;
94 if( rtsp->pf_read_line( rtsp->p_userdata, (uint8_t*)psz_buffer, (unsigned int)BUF_SIZE ) >= 0 )
96 //printf( "<< '%s'\n", psz_buffer );
97 psz_string = strdup( psz_buffer );
100 free( psz_buffer );
101 return psz_string;
106 * rtsp_put puts a line on stream
109 static int rtsp_put( rtsp_client_t *rtsp, const char *psz_string )
111 unsigned int i_buffer = strlen( psz_string );
112 char *psz_buffer = malloc( i_buffer + 3 );
113 int i_ret;
115 strcpy( psz_buffer, psz_string );
116 psz_buffer[i_buffer] = '\r'; psz_buffer[i_buffer+1] = '\n';
117 psz_buffer[i_buffer+2] = 0;
119 i_ret = rtsp->pf_write( rtsp->p_userdata, (uint8_t*)psz_buffer, i_buffer + 2 );
121 free( psz_buffer );
122 return i_ret;
126 * extract server status code
129 static int rtsp_get_status_code( rtsp_client_t *rtsp, const char *psz_string )
131 VLC_UNUSED(rtsp);
132 char psz_buffer[4];
133 int i_code = 0;
135 if( !strncmp( psz_string, "RTSP/1.0", sizeof("RTSP/1.0") - 1 ) )
137 memcpy( psz_buffer, psz_string + sizeof("RTSP/1.0"), 3 );
138 psz_buffer[3] = 0;
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 = rtsp->p_private->scheduled;
162 char *psz_buffer;
163 int i_ret;
165 psz_buffer = malloc( strlen(psz_type) + strlen(psz_what) +
166 sizeof("RTSP/1.0") + 2 );
168 sprintf( psz_buffer, "%s %s %s", psz_type, psz_what, "RTSP/1.0" );
169 i_ret = rtsp_put( rtsp, psz_buffer );
170 free( psz_buffer );
172 if( ppsz_payload )
173 while( *ppsz_payload )
175 rtsp_put( rtsp, *ppsz_payload );
176 ppsz_payload++;
178 rtsp_put( rtsp, "" );
179 rtsp_unschedule_all( rtsp );
181 return i_ret;
185 * schedule standard fields
188 static void rtsp_schedule_standard( rtsp_client_t *rtsp )
190 char tmp[17];
192 sprintf( tmp, "Cseq: %u", rtsp->p_private->cseq);
193 rtsp_schedule_field( rtsp, tmp );
195 if( rtsp->p_private->session )
197 char *buf;
198 buf = malloc( strlen(rtsp->p_private->session) + 15 );
199 sprintf( buf, "Session: %s", rtsp->p_private->session );
200 rtsp_schedule_field( rtsp, buf );
201 free( buf );
206 * get the answers, if server responses with something != 200, return NULL
209 static int rtsp_get_answers( rtsp_client_t *rtsp )
211 char *answer = NULL;
212 unsigned int answer_seq;
213 char **answer_ptr = rtsp->p_private->answers;
214 int code;
215 int ans_count = 0;
217 answer = rtsp_get( rtsp );
218 if( !answer ) return 0;
219 code = rtsp_get_status_code( rtsp, answer );
220 free( answer );
222 rtsp_free_answers( rtsp );
224 do { /* while we get answer lines */
226 answer = rtsp_get( rtsp );
227 if( !answer ) return 0;
229 if( !strncasecmp( answer, "Cseq:", 5 ) )
231 sscanf( answer, "%*s %u", &answer_seq );
232 if( rtsp->p_private->cseq != answer_seq )
234 //fprintf( stderr, "warning: Cseq mismatch. got %u, assumed %u",
235 // answer_seq, rtsp->p_private->cseq );
237 rtsp->p_private->cseq = answer_seq;
240 if( !strncasecmp( answer, "Server:", 7 ) )
242 char *buf = malloc( strlen(answer) );
243 sscanf( answer, "%*s %s", buf );
244 free( rtsp->p_private->server );
245 rtsp->p_private->server = buf;
247 if( !strncasecmp( answer, "Session:", 8 ) )
249 char *buf = malloc( strlen(answer) );
250 sscanf( answer, "%*s %s", buf );
251 if( rtsp->p_private->session )
253 if( strcmp( buf, rtsp->p_private->session ) )
255 //fprintf( stderr,
256 // "rtsp: warning: setting NEW session: %s\n", buf );
257 free( rtsp->p_private->session );
258 rtsp->p_private->session = strdup( buf );
261 else
263 //fprintf( stderr, "setting session id to: %s\n", buf );
264 rtsp->p_private->session = strdup( buf );
266 free( buf );
269 *answer_ptr = answer;
270 answer_ptr++;
271 } while( (strlen(answer) != 0) && (++ans_count < MAX_FIELDS) );
273 rtsp->p_private->cseq++;
275 *answer_ptr = NULL;
276 rtsp_schedule_standard( rtsp );
278 return code;
282 * send an ok message
285 int rtsp_send_ok( rtsp_client_t *rtsp )
287 char cseq[16];
289 rtsp_put( rtsp, "RTSP/1.0 200 OK" );
290 sprintf( cseq, "CSeq: %u", rtsp->p_private->cseq );
291 rtsp_put( rtsp, cseq );
292 rtsp_put( rtsp, "" );
293 return 0;
297 * implementation of must-have rtsp requests; functions return
298 * server status code.
301 int rtsp_request_options( rtsp_client_t *rtsp, const char *what )
303 char *buf;
305 if( what ) buf = strdup(what);
306 else
308 buf = malloc( strlen(rtsp->p_private->host) + 16 );
309 sprintf( buf, "rtsp://%s:%i", rtsp->p_private->host,
310 rtsp->p_private->port );
312 rtsp_send_request( rtsp, "OPTIONS", buf );
313 free( buf );
315 return rtsp_get_answers( rtsp );
318 int rtsp_request_describe( rtsp_client_t *rtsp, const char *what )
320 char *buf;
322 if( what )
324 buf = strdup(what);
326 else
328 buf = malloc( strlen(rtsp->p_private->host) +
329 strlen(rtsp->p_private->path) + 16 );
330 sprintf( buf, "rtsp://%s:%i/%s", rtsp->p_private->host,
331 rtsp->p_private->port, rtsp->p_private->path );
333 rtsp_send_request( rtsp, "DESCRIBE", buf );
334 free( buf );
336 return rtsp_get_answers( rtsp );
339 int rtsp_request_setup( rtsp_client_t *rtsp, const char *what )
341 rtsp_send_request( rtsp, "SETUP", what );
342 return rtsp_get_answers( rtsp );
345 int rtsp_request_setparameter( rtsp_client_t *rtsp, const char *what )
347 char *buf;
349 if( what )
351 buf = strdup(what);
353 else
355 buf = malloc( strlen(rtsp->p_private->host) +
356 strlen(rtsp->p_private->path) + 16 );
357 sprintf( buf, "rtsp://%s:%i/%s", rtsp->p_private->host,
358 rtsp->p_private->port, rtsp->p_private->path );
361 rtsp_send_request( rtsp, "SET_PARAMETER", buf );
362 free( buf );
364 return rtsp_get_answers( rtsp );
367 int rtsp_request_play( rtsp_client_t *rtsp, const char *what )
369 char *buf;
371 if( what )
373 buf = strdup( what );
375 else
377 buf = malloc( strlen(rtsp->p_private->host) +
378 strlen(rtsp->p_private->path) + 16 );
379 sprintf( buf, "rtsp://%s:%i/%s", rtsp->p_private->host,
380 rtsp->p_private->port, rtsp->p_private->path );
383 rtsp_send_request( rtsp, "PLAY", buf );
384 free( buf );
386 return rtsp_get_answers( rtsp );
389 int rtsp_request_tearoff( rtsp_client_t *rtsp, const char *what )
391 rtsp_send_request( rtsp, "TEAROFF", what );
392 return rtsp_get_answers( rtsp );
396 * read opaque data from stream
399 int rtsp_read_data( rtsp_client_t *rtsp, uint8_t *buffer, unsigned int size )
401 int i, seq;
403 if( size >= 4 )
405 i = rtsp->pf_read( rtsp->p_userdata, (uint8_t*)buffer, (unsigned int) 4 );
406 if( i < 4 ) return i;
408 if( buffer[0]=='S' && buffer[1]=='E' && buffer[2]=='T' &&
409 buffer[3]=='_' )
411 char *rest = rtsp_get( rtsp );
412 if( !rest ) return -1;
414 seq = -1;
417 free( rest );
418 rest = rtsp_get( rtsp );
419 if( !rest ) return -1;
421 if( !strncasecmp( rest, "Cseq:", 5 ) )
422 sscanf( rest, "%*s %u", &seq );
423 } while( *rest );
424 free( rest );
426 if( seq < 0 )
428 //fprintf(stderr, "warning: cseq not recognized!\n");
429 seq = 1;
432 /* lets make the server happy */
433 rtsp_put( rtsp, "RTSP/1.0 451 Parameter Not Understood" );
434 rest = malloc(17);
435 sprintf( rest,"CSeq: %u", seq );
436 rtsp_put( rtsp, rest );
437 rtsp_put( rtsp, "" );
438 free( rest );
439 i = rtsp->pf_read( rtsp->p_userdata, (unsigned char*)buffer, size );
441 else
443 i = rtsp->pf_read( rtsp->p_userdata, (unsigned char*)buffer + 4, size - 4 );
444 i += 4;
447 else i = rtsp->pf_read( rtsp->p_userdata, (unsigned char*)buffer, size );
449 //fprintf( stderr, "<< %d of %d bytes\n", i, size );
451 return i;
455 * connect to a rtsp server
458 int rtsp_connect( rtsp_client_t *rtsp, const char *psz_mrl,
459 const char *psz_user_agent )
461 rtsp_t *s;
462 char *mrl_ptr;
463 char *slash, *colon;
464 unsigned int hostend, pathbegin, i;
466 if( !psz_mrl ) return -1;
467 s = malloc( sizeof(rtsp_t) );
468 rtsp->p_private = s;
470 if( !strncmp( psz_mrl, "rtsp://", 7 ) ) psz_mrl += 7;
471 mrl_ptr = strdup( psz_mrl );
473 for( i=0; i<MAX_FIELDS; i++ )
475 s->answers[i]=NULL;
476 s->scheduled[i]=NULL;
479 s->host = NULL;
480 s->port = 554; /* rtsp standard port */
481 s->path = NULL;
482 s->mrl = strdup(psz_mrl);
484 s->server = NULL;
485 s->server_state = 0;
486 s->server_caps = 0;
488 s->cseq = 0;
489 s->session = NULL;
491 if( psz_user_agent ) s->user_agent = strdup( psz_user_agent );
492 else s->user_agent = strdup( "User-Agent: RealMedia Player Version "
493 "6.0.9.1235 (linux-2.0-libc6-i386-gcc2.95)" );
495 slash = strchr( mrl_ptr, '/' );
496 colon = strchr( mrl_ptr, ':' );
498 if( !slash ) slash = mrl_ptr + strlen(mrl_ptr) + 1;
499 if( !colon ) colon = slash;
500 if( colon > slash ) colon = slash;
502 pathbegin = slash - mrl_ptr;
503 hostend = colon - mrl_ptr;
505 s->host = malloc(hostend+1);
506 strncpy( s->host, mrl_ptr, hostend );
507 s->host[hostend] = 0;
509 if( pathbegin < strlen(mrl_ptr) ) s->path = strdup(mrl_ptr+pathbegin+1);
510 if( colon != slash )
512 char buffer[pathbegin-hostend];
514 strncpy( buffer, mrl_ptr+hostend+1, pathbegin-hostend-1 );
515 buffer[pathbegin-hostend-1] = 0;
516 s->port = atoi(buffer);
517 if( s->port < 0 || s->port > 65535 ) s->port = 554;
520 free( mrl_ptr );
521 //fprintf( stderr, "got mrl: %s %i %s\n", s->host, s->port, s->path );
523 s->s = rtsp->pf_connect( rtsp->p_userdata, s->host, s->port );
525 if( s->s < 0 )
527 //fprintf(stderr, "rtsp: failed to connect to '%s'\n", s->host);
528 rtsp_close( rtsp );
529 return -1;
532 s->server_state = RTSP_CONNECTED;
534 /* now lets send an options request. */
535 rtsp_schedule_field( rtsp, "CSeq: 1");
536 rtsp_schedule_field( rtsp, s->user_agent);
537 rtsp_schedule_field( rtsp, "ClientChallenge: "
538 "9e26d33f2984236010ef6253fb1887f7");
539 rtsp_schedule_field( rtsp, "PlayerStarttime: [28/03/2003:22:50:23 00:00]");
540 rtsp_schedule_field( rtsp, "CompanyID: KnKV4M4I/B2FjJ1TToLycw==" );
541 rtsp_schedule_field( rtsp, "GUID: 00000000-0000-0000-0000-000000000000" );
542 rtsp_schedule_field( rtsp, "RegionData: 0" );
543 rtsp_schedule_field( rtsp, "ClientID: "
544 "Linux_2.4_6.0.9.1235_play32_RN01_EN_586" );
545 /*rtsp_schedule_field( rtsp, "Pragma: initiate-session" );*/
546 rtsp_request_options( rtsp, NULL );
548 return 0;
552 * closes an rtsp connection
555 void rtsp_close( rtsp_client_t *rtsp )
557 if( rtsp->p_private->server_state )
559 /* TODO: send a TEAROFF */
560 rtsp->pf_disconnect( rtsp->p_userdata );
563 free( rtsp->p_private->path );
564 free( rtsp->p_private->host );
565 free( rtsp->p_private->mrl );
566 free( rtsp->p_private->session );
567 free( rtsp->p_private->user_agent );
568 free( rtsp->p_private->server );
569 rtsp_free_answers( rtsp );
570 rtsp_unschedule_all( rtsp );
571 free( rtsp->p_private );
575 * search in answers for tags. returns a pointer to the content
576 * after the first matched tag. returns NULL if no match found.
579 char *rtsp_search_answers( rtsp_client_t *rtsp, const char *tag )
581 char **answer;
582 char *ptr;
584 if( !rtsp->p_private->answers ) return NULL;
585 answer = rtsp->p_private->answers;
587 while(*answer)
589 if( !strncasecmp( *answer, tag, strlen(tag) ) )
591 ptr = strchr(*answer, ':');
592 ptr++;
593 while( *ptr == ' ' ) ptr++;
594 return ptr;
596 answer++;
599 return NULL;
603 * session id management
606 void rtsp_set_session( rtsp_client_t *rtsp, const char *id )
608 free( rtsp->p_private->session );
609 rtsp->p_private->session = strdup(id);
612 char *rtsp_get_session( rtsp_client_t *rtsp )
614 return rtsp->p_private->session;
617 char *rtsp_get_mrl( rtsp_client_t *rtsp )
619 return rtsp->p_private->mrl;
623 * schedules a field for transmission
626 void rtsp_schedule_field( rtsp_client_t *rtsp, const char *string )
628 int i = 0;
630 if( !string ) return;
632 while( rtsp->p_private->scheduled[i] ) i++;
634 rtsp->p_private->scheduled[i] = strdup(string);
638 * removes the first scheduled field which prefix matches string.
641 void rtsp_unschedule_field( rtsp_client_t *rtsp, const char *string )
643 char **ptr = rtsp->p_private->scheduled;
645 if( !string ) return;
647 while( *ptr )
649 if( !strncmp(*ptr, string, strlen(string)) ) break;
651 free( *ptr );
652 ptr++;
655 *(ptr-1) = *ptr;
656 } while( *ptr );
660 * unschedule all fields
663 void rtsp_unschedule_all( rtsp_client_t *rtsp )
665 char **ptr;
667 if( !rtsp->p_private->scheduled ) return;
668 ptr = rtsp->p_private->scheduled;
670 while( *ptr )
672 free( *ptr );
673 *ptr = NULL;
674 ptr++;
678 * free answers
681 void rtsp_free_answers( rtsp_client_t *rtsp )
683 char **answer;
685 if( !rtsp->p_private->answers ) return;
686 answer = rtsp->p_private->answers;
688 while( *answer )
690 free( *answer );
691 *answer = NULL;
692 answer++;