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
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 *****************************************************************************/
33 #define HEADER_SIZE 1024
34 #define MAX_FIELDS 256
47 unsigned int server_state;
53 char *answers[MAX_FIELDS]; /* data of last message */
54 char *scheduled[MAX_FIELDS]; /* will be sent with next message */
61 const char rtsp_protocol_version[]="RTSP/1.0";
64 #define RTSP_CONNECTED 1
67 #define RTSP_PLAYING 8
68 #define RTSP_RECORDING 16
70 /* server capabilities */
71 #define RTSP_OPTIONS 0x001
72 #define RTSP_DESCRIBE 0x002
73 #define RTSP_ANNOUNCE 0x004
74 #define RTSP_SETUP 0x008
75 #define RTSP_GET_PARAMETER 0x010
76 #define RTSP_SET_PARAMETER 0x020
77 #define RTSP_TEARDOWN 0x040
78 #define RTSP_PLAY 0x080
79 #define RTSP_RECORD 0x100
82 * rtsp_get gets a line from stream
83 * and returns a null terminated string (must be freed).
86 static char *rtsp_get( rtsp_client_t *rtsp )
88 char *psz_buffer = malloc( BUF_SIZE );
89 char *psz_string = NULL;
91 if( rtsp->pf_read_line( rtsp->p_userdata, (uint8_t*)psz_buffer, (unsigned int)BUF_SIZE ) >= 0 )
93 //printf( "<< '%s'\n", psz_buffer );
94 psz_string = strdup( psz_buffer );
103 * rtsp_put puts a line on stream
106 static int rtsp_put( rtsp_client_t *rtsp, const char *psz_string )
108 unsigned int i_buffer = strlen( psz_string );
109 char *psz_buffer = malloc( i_buffer + 3 );
112 strcpy( psz_buffer, psz_string );
113 psz_buffer[i_buffer] = '\r'; psz_buffer[i_buffer+1] = '\n';
114 psz_buffer[i_buffer+2] = 0;
116 i_ret = rtsp->pf_write( rtsp->p_userdata, (uint8_t*)psz_buffer, i_buffer + 2 );
123 * extract server status code
126 static int rtsp_get_status_code( rtsp_client_t *rtsp, const char *psz_string )
131 if( !strncmp( psz_string, "RTSP/1.0", sizeof("RTSP/1.0") - 1 ) )
133 memcpy( psz_buffer, psz_string + sizeof("RTSP/1.0"), 3 );
135 i_code = atoi( psz_buffer );
137 else if( !strncmp( psz_string, "SET_PARAMETER", 8 ) )
139 return RTSP_STATUS_SET_PARAMETER;
144 //fprintf( stderr, "librtsp: server responds: '%s'\n", psz_string );
154 static int rtsp_send_request( rtsp_client_t *rtsp, const char *psz_type,
155 const char *psz_what )
157 char **ppsz_payload = rtsp->p_private->scheduled;
161 psz_buffer = malloc( strlen(psz_type) + strlen(psz_what) +
162 sizeof("RTSP/1.0") + 2 );
164 sprintf( psz_buffer, "%s %s %s", psz_type, psz_what, "RTSP/1.0" );
165 i_ret = rtsp_put( rtsp, psz_buffer );
169 while( *ppsz_payload )
171 rtsp_put( rtsp, *ppsz_payload );
174 rtsp_put( rtsp, "" );
175 rtsp_unschedule_all( rtsp );
181 * schedule standard fields
184 static void rtsp_schedule_standard( rtsp_client_t *rtsp )
188 sprintf( tmp, "Cseq: %u", rtsp->p_private->cseq);
189 rtsp_schedule_field( rtsp, tmp );
191 if( rtsp->p_private->session )
194 buf = malloc( strlen(rtsp->p_private->session) + 15 );
195 sprintf( buf, "Session: %s", rtsp->p_private->session );
196 rtsp_schedule_field( rtsp, buf );
202 * get the answers, if server responses with something != 200, return NULL
205 static int rtsp_get_answers( rtsp_client_t *rtsp )
208 unsigned int answer_seq;
209 char **answer_ptr = rtsp->p_private->answers;
213 answer = rtsp_get( rtsp );
214 if( !answer ) return 0;
215 code = rtsp_get_status_code( rtsp, answer );
218 rtsp_free_answers( rtsp );
220 do { /* while we get answer lines */
222 answer = rtsp_get( rtsp );
223 if( !answer ) return 0;
225 if( !strncasecmp( answer, "Cseq:", 5 ) )
227 sscanf( answer, "%*s %u", &answer_seq );
228 if( rtsp->p_private->cseq != answer_seq )
230 //fprintf( stderr, "warning: Cseq mismatch. got %u, assumed %u",
231 // answer_seq, rtsp->p_private->cseq );
233 rtsp->p_private->cseq = answer_seq;
236 if( !strncasecmp( answer, "Server:", 7 ) )
238 char *buf = malloc( strlen(answer) );
239 sscanf( answer, "%*s %s", buf );
240 if( rtsp->p_private->server ) free( rtsp->p_private->server );
241 rtsp->p_private->server = buf;
243 if( !strncasecmp( answer, "Session:", 8 ) )
245 char *buf = malloc( strlen(answer) );
246 sscanf( answer, "%*s %s", buf );
247 if( rtsp->p_private->session )
249 if( strcmp( buf, rtsp->p_private->session ) )
252 // "rtsp: warning: setting NEW session: %s\n", buf );
253 free( rtsp->p_private->session );
254 rtsp->p_private->session = strdup( buf );
259 //fprintf( stderr, "setting session id to: %s\n", buf );
260 rtsp->p_private->session = strdup( buf );
265 *answer_ptr = answer;
267 } while( (strlen(answer) != 0) && (++ans_count < MAX_FIELDS) );
269 rtsp->p_private->cseq++;
272 rtsp_schedule_standard( rtsp );
281 int rtsp_send_ok( rtsp_client_t *rtsp )
285 rtsp_put( rtsp, "RTSP/1.0 200 OK" );
286 sprintf( cseq, "CSeq: %u", rtsp->p_private->cseq );
287 rtsp_put( rtsp, cseq );
288 rtsp_put( rtsp, "" );
293 * implementation of must-have rtsp requests; functions return
294 * server status code.
297 int rtsp_request_options( rtsp_client_t *rtsp, const char *what )
301 if( what ) buf = strdup(what);
304 buf = malloc( strlen(rtsp->p_private->host) + 16 );
305 sprintf( buf, "rtsp://%s:%i", rtsp->p_private->host,
306 rtsp->p_private->port );
308 rtsp_send_request( rtsp, "OPTIONS", buf );
311 return rtsp_get_answers( rtsp );
314 int rtsp_request_describe( rtsp_client_t *rtsp, const char *what )
324 buf = malloc( strlen(rtsp->p_private->host) +
325 strlen(rtsp->p_private->path) + 16 );
326 sprintf( buf, "rtsp://%s:%i/%s", rtsp->p_private->host,
327 rtsp->p_private->port, rtsp->p_private->path );
329 rtsp_send_request( rtsp, "DESCRIBE", buf );
332 return rtsp_get_answers( rtsp );
335 int rtsp_request_setup( rtsp_client_t *rtsp, const char *what )
337 rtsp_send_request( rtsp, "SETUP", what );
338 return rtsp_get_answers( rtsp );
341 int rtsp_request_setparameter( rtsp_client_t *rtsp, const char *what )
351 buf = malloc( strlen(rtsp->p_private->host) +
352 strlen(rtsp->p_private->path) + 16 );
353 sprintf( buf, "rtsp://%s:%i/%s", rtsp->p_private->host,
354 rtsp->p_private->port, rtsp->p_private->path );
357 rtsp_send_request( rtsp, "SET_PARAMETER", buf );
360 return rtsp_get_answers( rtsp );
363 int rtsp_request_play( rtsp_client_t *rtsp, const char *what )
369 buf = strdup( what );
373 buf = malloc( strlen(rtsp->p_private->host) +
374 strlen(rtsp->p_private->path) + 16 );
375 sprintf( buf, "rtsp://%s:%i/%s", rtsp->p_private->host,
376 rtsp->p_private->port, rtsp->p_private->path );
379 rtsp_send_request( rtsp, "PLAY", buf );
382 return rtsp_get_answers( rtsp );
385 int rtsp_request_tearoff( rtsp_client_t *rtsp, const char *what )
387 rtsp_send_request( rtsp, "TEAROFF", what );
388 return rtsp_get_answers( rtsp );
392 * read opaque data from stream
395 int rtsp_read_data( rtsp_client_t *rtsp, uint8_t *buffer, unsigned int size )
401 i = rtsp->pf_read( rtsp->p_userdata, (uint8_t*)buffer, (unsigned int) 4 );
402 if( i < 4 ) return i;
404 if( buffer[0]=='S' && buffer[1]=='E' && buffer[2]=='T' &&
407 char *rest = rtsp_get( rtsp );
408 if( !rest ) return -1;
414 rest = rtsp_get( rtsp );
415 if( !rest ) return -1;
417 if( !strncasecmp( rest, "Cseq:", 5 ) )
418 sscanf( rest, "%*s %u", &seq );
424 //fprintf(stderr, "warning: cseq not recognized!\n");
428 /* lets make the server happy */
429 rtsp_put( rtsp, "RTSP/1.0 451 Parameter Not Understood" );
431 sprintf( rest,"CSeq: %u", seq );
432 rtsp_put( rtsp, rest );
433 rtsp_put( rtsp, "" );
435 i = rtsp->pf_read( rtsp->p_userdata, (unsigned char*)buffer, size );
439 i = rtsp->pf_read( rtsp->p_userdata, (unsigned char*)buffer + 4, size - 4 );
443 else i = rtsp->pf_read( rtsp->p_userdata, (unsigned char*)buffer, size );
445 //fprintf( stderr, "<< %d of %d bytes\n", i, size );
451 * connect to a rtsp server
454 int rtsp_connect( rtsp_client_t *rtsp, const char *psz_mrl,
455 const char *psz_user_agent )
460 unsigned int hostend, pathbegin, i;
462 if( !psz_mrl ) return -1;
463 s = malloc( sizeof(rtsp_t) );
466 if( !strncmp( psz_mrl, "rtsp://", 7 ) ) psz_mrl += 7;
467 mrl_ptr = strdup( psz_mrl );
469 for( i=0; i<MAX_FIELDS; i++ )
472 s->scheduled[i]=NULL;
476 s->port = 554; /* rtsp standard port */
478 s->mrl = strdup(psz_mrl);
487 if( psz_user_agent ) s->user_agent = strdup( psz_user_agent );
488 else s->user_agent = strdup( "User-Agent: RealMedia Player Version "
489 "6.0.9.1235 (linux-2.0-libc6-i386-gcc2.95)" );
491 slash = strchr( mrl_ptr, '/' );
492 colon = strchr( mrl_ptr, ':' );
494 if( !slash ) slash = mrl_ptr + strlen(mrl_ptr) + 1;
495 if( !colon ) colon = slash;
496 if( colon > slash ) colon = slash;
498 pathbegin = slash - mrl_ptr;
499 hostend = colon - mrl_ptr;
501 s->host = malloc(hostend+1);
502 strncpy( s->host, mrl_ptr, hostend );
503 s->host[hostend] = 0;
505 if( pathbegin < strlen(mrl_ptr) ) s->path = strdup(mrl_ptr+pathbegin+1);
508 char buffer[pathbegin-hostend];
510 strncpy( buffer, mrl_ptr+hostend+1, pathbegin-hostend-1 );
511 buffer[pathbegin-hostend-1] = 0;
512 s->port = atoi(buffer);
513 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 if( rtsp->p_private->server ) free( rtsp->p_private->server );
565 rtsp_free_answers( rtsp );
566 rtsp_unschedule_all( rtsp );
567 free( rtsp->p_private );
571 * search in answers for tags. returns a pointer to the content
572 * after the first matched tag. returns NULL if no match found.
575 char *rtsp_search_answers( rtsp_client_t *rtsp, const char *tag )
580 if( !rtsp->p_private->answers ) return NULL;
581 answer = rtsp->p_private->answers;
585 if( !strncasecmp( *answer, tag, strlen(tag) ) )
587 ptr = strchr(*answer, ':');
589 while( *ptr == ' ' ) ptr++;
599 * session id management
602 void rtsp_set_session( rtsp_client_t *rtsp, const char *id )
604 if( rtsp->p_private->session ) free( rtsp->p_private->session );
605 rtsp->p_private->session = strdup(id);
608 char *rtsp_get_session( rtsp_client_t *rtsp )
610 return rtsp->p_private->session;
613 char *rtsp_get_mrl( rtsp_client_t *rtsp )
615 return rtsp->p_private->mrl;
619 * schedules a field for transmission
622 void rtsp_schedule_field( rtsp_client_t *rtsp, const char *string )
626 if( !string ) return;
628 while( rtsp->p_private->scheduled[i] ) i++;
630 rtsp->p_private->scheduled[i] = strdup(string);
634 * removes the first scheduled field which prefix matches string.
637 void rtsp_unschedule_field( rtsp_client_t *rtsp, const char *string )
639 char **ptr = rtsp->p_private->scheduled;
641 if( !string ) return;
645 if( !strncmp(*ptr, string, strlen(string)) ) break;
647 if( *ptr ) free( *ptr );
656 * unschedule all fields
659 void rtsp_unschedule_all( rtsp_client_t *rtsp )
663 if( !rtsp->p_private->scheduled ) return;
664 ptr = rtsp->p_private->scheduled;
677 void rtsp_free_answers( rtsp_client_t *rtsp )
681 if( !rtsp->p_private->answers ) return;
682 answer = rtsp->p_private->answers;