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: file.c 10310 2005-03-11 22:36:40Z anil $
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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
25 *****************************************************************************/
34 #define HEADER_SIZE 1024
35 #define MAX_FIELDS 256
48 unsigned int server_state;
54 char *answers[MAX_FIELDS]; /* data of last message */
55 char *scheduled[MAX_FIELDS]; /* will be sent with next message */
62 const char rtsp_protocol_version[]="RTSP/1.0";
65 #define RTSP_CONNECTED 1
68 #define RTSP_PLAYING 8
69 #define RTSP_RECORDING 16
71 /* server capabilities */
72 #define RTSP_OPTIONS 0x001
73 #define RTSP_DESCRIBE 0x002
74 #define RTSP_ANNOUNCE 0x004
75 #define RTSP_SETUP 0x008
76 #define RTSP_GET_PARAMETER 0x010
77 #define RTSP_SET_PARAMETER 0x020
78 #define RTSP_TEARDOWN 0x040
79 #define RTSP_PLAY 0x080
80 #define RTSP_RECORD 0x100
83 * rtsp_get gets a line from stream
84 * and returns a null terminated string (must be freed).
87 static char *rtsp_get( rtsp_client_t *rtsp )
89 char *psz_buffer = malloc( BUF_SIZE );
90 char *psz_string = NULL;
92 if( rtsp->pf_read_line( rtsp->p_userdata, psz_buffer, BUF_SIZE ) >= 0 )
94 //printf( "<< '%s'\n", psz_buffer );
95 psz_string = strdup( psz_buffer );
104 * rtsp_put puts a line on stream
107 static int rtsp_put( rtsp_client_t *rtsp, const char *psz_string )
109 int i_buffer = strlen( psz_string );
110 char *psz_buffer = malloc( i_buffer + 3 );
113 strcpy( psz_buffer, psz_string );
114 psz_buffer[i_buffer] = '\r'; psz_buffer[i_buffer+1] = '\n';
115 psz_buffer[i_buffer+2] = 0;
117 i_ret = rtsp->pf_write( rtsp->p_userdata, psz_buffer, i_buffer + 2 );
124 * extract server status code
127 static int rtsp_get_status_code( rtsp_client_t *rtsp, const char *psz_string )
132 if( !strncmp( psz_string, "RTSP/1.0", sizeof("RTSP/1.0") - 1 ) )
134 memcpy( psz_buffer, psz_string + sizeof("RTSP/1.0"), 3 );
136 i_code = atoi( psz_buffer );
138 else if( !strncmp( psz_string, "SET_PARAMETER", 8 ) )
140 return RTSP_STATUS_SET_PARAMETER;
145 fprintf( stderr, "librtsp: server responds: '%s'\n", psz_string );
155 static int rtsp_send_request( rtsp_client_t *rtsp, const char *psz_type,
156 const char *psz_what )
158 char **ppsz_payload = rtsp->p_private->scheduled;
162 psz_buffer = malloc( strlen(psz_type) + strlen(psz_what) +
163 sizeof("RTSP/1.0") + 2 );
165 sprintf( psz_buffer, "%s %s %s", psz_type, psz_what, "RTSP/1.0" );
166 i_ret = rtsp_put( rtsp, psz_buffer );
170 while( *ppsz_payload )
172 rtsp_put( rtsp, *ppsz_payload );
175 rtsp_put( rtsp, "" );
176 rtsp_unschedule_all( rtsp );
182 * schedule standard fields
185 static void rtsp_schedule_standard( rtsp_client_t *rtsp )
189 sprintf( tmp, "Cseq: %u", rtsp->p_private->cseq);
190 rtsp_schedule_field( rtsp, tmp );
192 if( rtsp->p_private->session )
195 buf = malloc( strlen(rtsp->p_private->session) + 15 );
196 sprintf( buf, "Session: %s", rtsp->p_private->session );
197 rtsp_schedule_field( rtsp, buf );
203 * get the answers, if server responses with something != 200, return NULL
206 static int rtsp_get_answers( rtsp_client_t *rtsp )
209 unsigned int answer_seq;
210 char **answer_ptr = rtsp->p_private->answers;
214 answer = rtsp_get( rtsp );
215 if( !answer ) return 0;
216 code = rtsp_get_status_code( rtsp, answer );
219 rtsp_free_answers( rtsp );
221 do { /* while we get answer lines */
223 answer = rtsp_get( rtsp );
224 if( !answer ) return 0;
226 if( !strncasecmp( answer, "Cseq:", 5 ) )
228 sscanf( answer, "%*s %u", &answer_seq );
229 if( rtsp->p_private->cseq != answer_seq )
231 //fprintf( stderr, "warning: Cseq mismatch. got %u, assumed %u",
232 // answer_seq, rtsp->p_private->cseq );
234 rtsp->p_private->cseq = answer_seq;
237 if( !strncasecmp( answer, "Server:", 7 ) )
239 char *buf = malloc( strlen(answer) );
240 sscanf( answer, "%*s %s", buf );
241 if( rtsp->p_private->server ) free( rtsp->p_private->server );
242 rtsp->p_private->server = strdup( buf );
245 if( !strncasecmp( answer, "Session:", 8 ) )
247 char *buf = malloc( strlen(answer) );
248 sscanf( answer, "%*s %s", buf );
249 if( rtsp->p_private->session )
251 if( strcmp( buf, rtsp->p_private->session ) )
254 "rtsp: warning: setting NEW session: %s\n", buf );
255 free( rtsp->p_private->session );
256 rtsp->p_private->session = strdup( buf );
261 fprintf( stderr, "setting session id to: %s\n", buf );
262 rtsp->p_private->session = strdup( buf );
267 *answer_ptr = answer;
269 } while( (strlen(answer) != 0) && (++ans_count < MAX_FIELDS) );
271 rtsp->p_private->cseq++;
274 rtsp_schedule_standard( rtsp );
283 int rtsp_send_ok( rtsp_client_t *rtsp )
287 rtsp_put( rtsp, "RTSP/1.0 200 OK" );
288 sprintf( cseq, "CSeq: %u", rtsp->p_private->cseq );
289 rtsp_put( rtsp, cseq );
290 rtsp_put( rtsp, "" );
295 * implementation of must-have rtsp requests; functions return
296 * server status code.
299 int rtsp_request_options( rtsp_client_t *rtsp, const char *what )
303 if( what ) buf = strdup(what);
306 buf = malloc( strlen(rtsp->p_private->host) + 16 );
307 sprintf( buf, "rtsp://%s:%i", rtsp->p_private->host,
308 rtsp->p_private->port );
310 rtsp_send_request( rtsp, "OPTIONS", buf );
313 return rtsp_get_answers( rtsp );
316 int rtsp_request_describe( rtsp_client_t *rtsp, const char *what )
326 buf = malloc( strlen(rtsp->p_private->host) +
327 strlen(rtsp->p_private->path) + 16 );
328 sprintf( buf, "rtsp://%s:%i/%s", rtsp->p_private->host,
329 rtsp->p_private->port, rtsp->p_private->path );
331 rtsp_send_request( rtsp, "DESCRIBE", buf );
334 return rtsp_get_answers( rtsp );
337 int rtsp_request_setup( rtsp_client_t *rtsp, const char *what )
339 rtsp_send_request( rtsp, "SETUP", what );
340 return rtsp_get_answers( rtsp );
343 int rtsp_request_setparameter( rtsp_client_t *rtsp, const char *what )
353 buf = malloc( strlen(rtsp->p_private->host) +
354 strlen(rtsp->p_private->path) + 16 );
355 sprintf( buf, "rtsp://%s:%i/%s", rtsp->p_private->host,
356 rtsp->p_private->port, rtsp->p_private->path );
359 rtsp_send_request( rtsp, "SET_PARAMETER", buf );
362 return rtsp_get_answers( rtsp );
365 int rtsp_request_play( rtsp_client_t *rtsp, const char *what )
371 buf = strdup( what );
375 buf = malloc( strlen(rtsp->p_private->host) +
376 strlen(rtsp->p_private->path) + 16 );
377 sprintf( buf, "rtsp://%s:%i/%s", rtsp->p_private->host,
378 rtsp->p_private->port, rtsp->p_private->path );
381 rtsp_send_request( rtsp, "PLAY", buf );
384 return rtsp_get_answers( rtsp );
387 int rtsp_request_tearoff( rtsp_client_t *rtsp, const char *what )
389 rtsp_send_request( rtsp, "TEAROFF", what );
390 return rtsp_get_answers( rtsp );
394 * read opaque data from stream
397 int rtsp_read_data( rtsp_client_t *rtsp, char *buffer, unsigned int size )
403 i= rtsp->pf_read( rtsp->p_userdata, buffer, 4 );
404 if( i < 4 ) return i;
406 if( buffer[0]=='S' && buffer[1]=='E' && buffer[2]=='T' &&
409 char *rest = rtsp_get( rtsp );
410 if( !rest ) return -1;
416 rest = rtsp_get( rtsp );
417 if( !rest ) return -1;
419 if( !strncasecmp( rest, "Cseq:", 5 ) )
420 sscanf( rest, "%*s %u", &seq );
421 } while( strlen(rest) != 0 );
426 fprintf(stderr, "warning: cseq not recognized!\n");
430 /* lets make the server happy */
431 rtsp_put( rtsp, "RTSP/1.0 451 Parameter Not Understood" );
433 sprintf( rest,"CSeq: %u", seq );
434 rtsp_put( rtsp, rest );
435 rtsp_put( rtsp, "" );
436 rtsp->pf_read( rtsp->p_userdata, buffer, size );
440 i = rtsp->pf_read( rtsp->p_userdata, buffer + 4, size - 4 );
444 else i= rtsp->pf_read( rtsp->p_userdata, buffer, size );
446 //fprintf( stderr, "<< %d of %d bytes\n", i, size );
452 * connect to a rtsp server
455 int rtsp_connect( rtsp_client_t *rtsp, const char *psz_mrl,
456 const char *psz_user_agent )
461 int hostend, pathbegin, i;
463 if( !psz_mrl ) return -1;
464 s = malloc( sizeof(rtsp_t) );
467 if( !strncmp( psz_mrl, "rtsp://", 7 ) ) psz_mrl += 7;
468 mrl_ptr = strdup( psz_mrl );
470 for( i=0; i<MAX_FIELDS; i++ )
473 s->scheduled[i]=NULL;
477 s->port = 554; /* rtsp standard port */
479 s->mrl = strdup(psz_mrl);
488 if( psz_user_agent ) s->user_agent = strdup( psz_user_agent );
489 else s->user_agent = strdup( "User-Agent: RealMedia Player Version "
490 "6.0.9.1235 (linux-2.0-libc6-i386-gcc2.95)" );
492 slash = strchr( mrl_ptr, '/' );
493 colon = strchr( mrl_ptr, ':' );
495 if( !slash ) slash = mrl_ptr + strlen(mrl_ptr) + 1;
496 if( !colon ) colon = slash;
497 if( colon > slash ) colon = slash;
499 pathbegin = slash - mrl_ptr;
500 hostend = colon - mrl_ptr;
502 s->host = malloc(hostend+1);
503 strncpy( s->host, mrl_ptr, hostend );
504 s->host[hostend] = 0;
506 if( pathbegin < strlen(mrl_ptr) ) s->path = strdup(mrl_ptr+pathbegin+1);
509 char buffer[pathbegin-hostend];
511 strncpy( buffer, mrl_ptr+hostend+1, pathbegin-hostend-1 );
512 buffer[pathbegin-hostend-1] = 0;
513 s->port = atoi(buffer);
514 if( s->port < 0 || s->port > 65535 ) s->port = 554;
517 fprintf( stderr, "got mrl: %s %i %s\n", s->host, s->port, s->path );
519 s->s = rtsp->pf_connect( rtsp->p_userdata, s->host, s->port );
523 fprintf(stderr, "rtsp: failed to connect to '%s'\n", s->host);
528 s->server_state = RTSP_CONNECTED;
530 /* now lets send an options request. */
531 rtsp_schedule_field( rtsp, "CSeq: 1");
532 rtsp_schedule_field( rtsp, s->user_agent);
533 rtsp_schedule_field( rtsp, "ClientChallenge: "
534 "9e26d33f2984236010ef6253fb1887f7");
535 rtsp_schedule_field( rtsp, "PlayerStarttime: [28/03/2003:22:50:23 00:00]");
536 rtsp_schedule_field( rtsp, "CompanyID: KnKV4M4I/B2FjJ1TToLycw==" );
537 rtsp_schedule_field( rtsp, "GUID: 00000000-0000-0000-0000-000000000000" );
538 rtsp_schedule_field( rtsp, "RegionData: 0" );
539 rtsp_schedule_field( rtsp, "ClientID: "
540 "Linux_2.4_6.0.9.1235_play32_RN01_EN_586" );
541 /*rtsp_schedule_field( rtsp, "Pragma: initiate-session" );*/
542 rtsp_request_options( rtsp, NULL );
548 * closes an rtsp connection
551 void rtsp_close( rtsp_client_t *rtsp )
553 if( rtsp->p_private->server_state )
555 /* TODO: send a TEAROFF */
556 rtsp->pf_disconnect( rtsp->p_userdata );
559 if( rtsp->p_private->path ) free( rtsp->p_private->path );
560 if( rtsp->p_private->host ) free( rtsp->p_private->host );
561 if( rtsp->p_private->mrl ) free( rtsp->p_private->mrl );
562 if( rtsp->p_private->session ) free( rtsp->p_private->session );
563 if( rtsp->p_private->user_agent ) free( rtsp->p_private->user_agent );
564 rtsp_free_answers( rtsp );
565 rtsp_unschedule_all( rtsp );
566 free( rtsp->p_private );
570 * search in answers for tags. returns a pointer to the content
571 * after the first matched tag. returns NULL if no match found.
574 char *rtsp_search_answers( rtsp_client_t *rtsp, const char *tag )
579 if( !rtsp->p_private->answers ) return NULL;
580 answer = rtsp->p_private->answers;
584 if( !strncasecmp( *answer, tag, strlen(tag) ) )
586 ptr = strchr(*answer, ':');
588 while( *ptr == ' ' ) ptr++;
598 * session id management
601 void rtsp_set_session( rtsp_client_t *rtsp, const char *id )
603 if( rtsp->p_private->session ) free( rtsp->p_private->session );
604 rtsp->p_private->session = strdup(id);
607 char *rtsp_get_session( rtsp_client_t *rtsp )
609 return rtsp->p_private->session;
612 char *rtsp_get_mrl( rtsp_client_t *rtsp )
614 return rtsp->p_private->mrl;
618 * schedules a field for transmission
621 void rtsp_schedule_field( rtsp_client_t *rtsp, const char *string )
625 if( !string ) return;
627 while( rtsp->p_private->scheduled[i] ) i++;
629 rtsp->p_private->scheduled[i] = strdup(string);
633 * removes the first scheduled field which prefix matches string.
636 void rtsp_unschedule_field( rtsp_client_t *rtsp, const char *string )
638 char **ptr = rtsp->p_private->scheduled;
640 if( !string ) return;
644 if( !strncmp(*ptr, string, strlen(string)) ) break;
646 if( *ptr ) free( *ptr );
655 * unschedule all fields
658 void rtsp_unschedule_all( rtsp_client_t *rtsp )
662 if( !rtsp->p_private->scheduled ) return;
663 ptr = rtsp->p_private->scheduled;
676 void rtsp_free_answers( rtsp_client_t *rtsp )
680 if( !rtsp->p_private->answers ) return;
681 answer = rtsp->p_private->answers;