Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

rtsp.c

00001 /*****************************************************************************
00002  * rtsp.c: minimalistic implementation of rtsp protocol.
00003  *         Not RFC 2326 compilant yet and only handle REAL RTSP.
00004  *****************************************************************************
00005  * Copyright (C) 2002-2004 the xine project
00006  * Copyright (C) 2005 VideoLAN
00007  * $Id$
00008  *
00009  * Authors: Gildas Bazin <[email protected]>
00010  *          Adapted from xine which itself adapted it from joschkas real tools.
00011  *
00012  * This program is free software; you can redistribute it and/or modify
00013  * it under the terms of the GNU General Public License as published by
00014  * the Free Software Foundation; either version 2 of the License, or
00015  * (at your option) any later version.
00016  *
00017  * This program is distributed in the hope that it will be useful,
00018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020  * GNU General Public License for more details.
00021  *
00022  * You should have received a copy of the GNU General Public License
00023  * along with this program; if not, write to the Free Software
00024  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00025  *****************************************************************************/
00026 #include <stdio.h>
00027 #include <stdlib.h>
00028 
00029 #include <vlc/vlc.h>
00030 
00031 #include "rtsp.h"
00032 
00033 #define BUF_SIZE 4096
00034 #define HEADER_SIZE 1024
00035 #define MAX_FIELDS 256
00036 
00037 struct rtsp_s {
00038 
00039   int           s;
00040 
00041   char         *host;
00042   int           port;
00043   char         *path;
00044   char         *mrl;
00045   char         *user_agent;
00046 
00047   char         *server;
00048   unsigned int  server_state;
00049   uint32_t      server_caps;
00050 
00051   unsigned int  cseq;
00052   char         *session;
00053 
00054   char        *answers[MAX_FIELDS];   /* data of last message */
00055   char        *scheduled[MAX_FIELDS]; /* will be sent with next message */
00056 };
00057 
00058 /*
00059  * constants
00060  */
00061 
00062 const char rtsp_protocol_version[]="RTSP/1.0";
00063 
00064 /* server states */
00065 #define RTSP_CONNECTED 1
00066 #define RTSP_INIT      2
00067 #define RTSP_READY     4
00068 #define RTSP_PLAYING   8
00069 #define RTSP_RECORDING 16
00070 
00071 /* server capabilities */
00072 #define RTSP_OPTIONS       0x001
00073 #define RTSP_DESCRIBE      0x002
00074 #define RTSP_ANNOUNCE      0x004
00075 #define RTSP_SETUP         0x008
00076 #define RTSP_GET_PARAMETER 0x010
00077 #define RTSP_SET_PARAMETER 0x020
00078 #define RTSP_TEARDOWN      0x040
00079 #define RTSP_PLAY          0x080
00080 #define RTSP_RECORD        0x100
00081 
00082 /*
00083  * rtsp_get gets a line from stream
00084  * and returns a null terminated string (must be freed).
00085  */
00086  
00087 static char *rtsp_get( rtsp_client_t *rtsp )
00088 {
00089   char *psz_buffer = malloc( BUF_SIZE );
00090   char *psz_string = NULL;
00091 
00092   if( rtsp->pf_read_line( rtsp->p_userdata, psz_buffer, BUF_SIZE ) >= 0 )
00093   {
00094     //printf( "<< '%s'\n", psz_buffer );
00095       psz_string = strdup( psz_buffer );
00096   }
00097 
00098   free( psz_buffer );
00099   return psz_string;
00100 }
00101 
00102 
00103 /*
00104  * rtsp_put puts a line on stream
00105  */
00106 
00107 static int rtsp_put( rtsp_client_t *rtsp, const char *psz_string )
00108 {
00109     int i_buffer = strlen( psz_string );
00110     char *psz_buffer = malloc( i_buffer + 3 );
00111     int i_ret;
00112 
00113     strcpy( psz_buffer, psz_string );
00114     psz_buffer[i_buffer] = '\r'; psz_buffer[i_buffer+1] = '\n';
00115     psz_buffer[i_buffer+2] = 0;
00116 
00117     i_ret = rtsp->pf_write( rtsp->p_userdata, psz_buffer, i_buffer + 2 );
00118 
00119     free( psz_buffer );
00120     return i_ret;
00121 }
00122 
00123 /*
00124  * extract server status code
00125  */
00126 
00127 static int rtsp_get_status_code( rtsp_client_t *rtsp, const char *psz_string )
00128 {
00129     char psz_buffer[4];
00130     int i_code = 0;
00131 
00132     if( !strncmp( psz_string, "RTSP/1.0", sizeof("RTSP/1.0") - 1 ) )
00133     {
00134         memcpy( psz_buffer, psz_string + sizeof("RTSP/1.0"), 3 );
00135         psz_buffer[3] = 0;
00136         i_code = atoi( psz_buffer );
00137     }
00138     else if( !strncmp( psz_string, "SET_PARAMETER", 8 ) )
00139     {
00140         return RTSP_STATUS_SET_PARAMETER;
00141     }
00142 
00143     if( i_code != 200 )
00144     {
00145         fprintf( stderr, "librtsp: server responds: '%s'\n", psz_string );
00146     }
00147 
00148     return i_code;
00149 }
00150 
00151 /*
00152  * send a request
00153  */
00154 
00155 static int rtsp_send_request( rtsp_client_t *rtsp, const char *psz_type,
00156                               const char *psz_what )
00157 {
00158     char **ppsz_payload = rtsp->p_private->scheduled;
00159     char *psz_buffer;
00160     int i_ret;
00161 
00162     psz_buffer = malloc( strlen(psz_type) + strlen(psz_what) +
00163                          sizeof("RTSP/1.0") + 2 );
00164 
00165     sprintf( psz_buffer, "%s %s %s", psz_type, psz_what, "RTSP/1.0" );
00166     i_ret = rtsp_put( rtsp, psz_buffer );
00167     free( psz_buffer );
00168 
00169     if( ppsz_payload )
00170         while( *ppsz_payload )
00171         {
00172             rtsp_put( rtsp, *ppsz_payload );
00173             ppsz_payload++;
00174         }
00175     rtsp_put( rtsp, "" );
00176     rtsp_unschedule_all( rtsp );
00177 
00178     return i_ret;
00179 }
00180 
00181 /*
00182  * schedule standard fields
00183  */
00184 
00185 static void rtsp_schedule_standard( rtsp_client_t *rtsp )
00186 {
00187     char tmp[17];
00188 
00189     sprintf( tmp, "Cseq: %u", rtsp->p_private->cseq);
00190     rtsp_schedule_field( rtsp, tmp );
00191 
00192     if( rtsp->p_private->session )
00193     {
00194         char *buf;
00195         buf = malloc( strlen(rtsp->p_private->session) + 15 );
00196         sprintf( buf, "Session: %s", rtsp->p_private->session );
00197         rtsp_schedule_field( rtsp, buf );
00198         free( buf );
00199     }
00200 }
00201 
00202 /*
00203  * get the answers, if server responses with something != 200, return NULL
00204  */
00205 
00206 static int rtsp_get_answers( rtsp_client_t *rtsp )
00207 {
00208     char *answer = NULL;
00209     unsigned int answer_seq;
00210     char **answer_ptr = rtsp->p_private->answers;
00211     int code;
00212     int ans_count = 0;
00213 
00214     answer = rtsp_get( rtsp );
00215     if( !answer ) return 0;
00216     code = rtsp_get_status_code( rtsp, answer );
00217     free( answer );
00218 
00219     rtsp_free_answers( rtsp );
00220 
00221     do { /* while we get answer lines */
00222 
00223       answer = rtsp_get( rtsp );
00224       if( !answer ) return 0;
00225 
00226       if( !strncasecmp( answer, "Cseq:", 5 ) )
00227       {
00228           sscanf( answer, "%*s %u", &answer_seq );
00229           if( rtsp->p_private->cseq != answer_seq )
00230           {
00231             //fprintf( stderr, "warning: Cseq mismatch. got %u, assumed %u",
00232             //       answer_seq, rtsp->p_private->cseq );
00233 
00234               rtsp->p_private->cseq = answer_seq;
00235           }
00236       }
00237       if( !strncasecmp( answer, "Server:", 7 ) )
00238       {
00239           char *buf = malloc( strlen(answer) );
00240           sscanf( answer, "%*s %s", buf );
00241           if( rtsp->p_private->server ) free( rtsp->p_private->server );
00242           rtsp->p_private->server = buf;
00243       }
00244       if( !strncasecmp( answer, "Session:", 8 ) )
00245       {
00246           char *buf = malloc( strlen(answer) );
00247           sscanf( answer, "%*s %s", buf );
00248           if( rtsp->p_private->session )
00249           {
00250               if( strcmp( buf, rtsp->p_private->session ) )
00251               {
00252                   fprintf( stderr, 
00253                            "rtsp: warning: setting NEW session: %s\n", buf );
00254                   free( rtsp->p_private->session );
00255                   rtsp->p_private->session = strdup( buf );
00256               }
00257           }
00258           else
00259           {
00260               fprintf( stderr, "setting session id to: %s\n", buf );
00261               rtsp->p_private->session = strdup( buf );
00262           }
00263           free( buf );
00264       }
00265 
00266       *answer_ptr = answer;
00267       answer_ptr++;
00268     } while( (strlen(answer) != 0) && (++ans_count < MAX_FIELDS) );
00269 
00270     rtsp->p_private->cseq++;
00271 
00272     *answer_ptr = NULL;
00273     rtsp_schedule_standard( rtsp );
00274 
00275     return code;
00276 }
00277 
00278 /*
00279  * send an ok message
00280  */
00281 
00282 int rtsp_send_ok( rtsp_client_t *rtsp )
00283 {
00284     char cseq[16];
00285 
00286     rtsp_put( rtsp, "RTSP/1.0 200 OK" );
00287     sprintf( cseq, "CSeq: %u", rtsp->p_private->cseq );
00288     rtsp_put( rtsp, cseq );
00289     rtsp_put( rtsp, "" );
00290     return 0;
00291 }
00292 
00293 /*
00294  * implementation of must-have rtsp requests; functions return
00295  * server status code.
00296  */
00297 
00298 int rtsp_request_options( rtsp_client_t *rtsp, const char *what )
00299 {
00300     char *buf;
00301 
00302     if( what ) buf = strdup(what);
00303     else
00304     {
00305         buf = malloc( strlen(rtsp->p_private->host) + 16 );
00306         sprintf( buf, "rtsp://%s:%i", rtsp->p_private->host,
00307                  rtsp->p_private->port );
00308     }
00309     rtsp_send_request( rtsp, "OPTIONS", buf );
00310     free( buf );
00311 
00312     return rtsp_get_answers( rtsp );
00313 }
00314 
00315 int rtsp_request_describe( rtsp_client_t *rtsp, const char *what )
00316 {
00317     char *buf;
00318 
00319     if( what )
00320     {
00321         buf = strdup(what);
00322     }
00323     else
00324     {
00325         buf = malloc( strlen(rtsp->p_private->host) +
00326                       strlen(rtsp->p_private->path) + 16 );
00327         sprintf( buf, "rtsp://%s:%i/%s", rtsp->p_private->host,
00328                  rtsp->p_private->port, rtsp->p_private->path );
00329     }
00330     rtsp_send_request( rtsp, "DESCRIBE", buf );
00331     free( buf );
00332 
00333     return rtsp_get_answers( rtsp );
00334 }
00335 
00336 int rtsp_request_setup( rtsp_client_t *rtsp, const char *what )
00337 {
00338     rtsp_send_request( rtsp, "SETUP", what );
00339     return rtsp_get_answers( rtsp );
00340 }
00341 
00342 int rtsp_request_setparameter( rtsp_client_t *rtsp, const char *what )
00343 {
00344     char *buf;
00345 
00346     if( what )
00347     {
00348         buf = strdup(what);
00349     }
00350     else
00351     {
00352         buf = malloc( strlen(rtsp->p_private->host) +
00353                       strlen(rtsp->p_private->path) + 16 );
00354         sprintf( buf, "rtsp://%s:%i/%s", rtsp->p_private->host,
00355                  rtsp->p_private->port, rtsp->p_private->path );
00356     }
00357 
00358     rtsp_send_request( rtsp, "SET_PARAMETER", buf );
00359     free( buf );
00360 
00361     return rtsp_get_answers( rtsp );
00362 }
00363 
00364 int rtsp_request_play( rtsp_client_t *rtsp, const char *what )
00365 {
00366     char *buf;
00367 
00368     if( what )
00369     {
00370         buf = strdup( what );
00371     }
00372     else
00373     {
00374         buf = malloc( strlen(rtsp->p_private->host) +
00375                       strlen(rtsp->p_private->path) + 16 );
00376         sprintf( buf, "rtsp://%s:%i/%s", rtsp->p_private->host,
00377                  rtsp->p_private->port, rtsp->p_private->path );
00378     }
00379 
00380     rtsp_send_request( rtsp, "PLAY", buf );
00381     free( buf );
00382 
00383     return rtsp_get_answers( rtsp );
00384 }
00385 
00386 int rtsp_request_tearoff( rtsp_client_t *rtsp, const char *what )
00387 {
00388     rtsp_send_request( rtsp, "TEAROFF", what );
00389     return rtsp_get_answers( rtsp );
00390 }
00391 
00392 /*
00393  * read opaque data from stream
00394  */
00395 
00396 int rtsp_read_data( rtsp_client_t *rtsp, char *buffer, unsigned int size )
00397 {
00398     int i, seq;
00399 
00400     if( size >= 4 )
00401     {
00402         i = rtsp->pf_read( rtsp->p_userdata, buffer, 4 );
00403         if( i < 4 ) return i;
00404 
00405         if( buffer[0]=='S' && buffer[1]=='E' && buffer[2]=='T' &&
00406             buffer[3]=='_' )
00407         {
00408             char *rest = rtsp_get( rtsp );
00409             if( !rest ) return -1;
00410 
00411             seq = -1;
00412             do
00413             {
00414                 free( rest );
00415                 rest = rtsp_get( rtsp );
00416                 if( !rest ) return -1;
00417 
00418                 if( !strncasecmp( rest, "Cseq:", 5 ) )
00419                     sscanf( rest, "%*s %u", &seq );
00420             } while( *rest );
00421             free( rest );
00422 
00423             if( seq < 0 )
00424             {
00425                 fprintf(stderr, "warning: cseq not recognized!\n");
00426                 seq = 1;
00427             }
00428 
00429             /* lets make the server happy */
00430             rtsp_put( rtsp, "RTSP/1.0 451 Parameter Not Understood" );
00431             rest = malloc(17);
00432             sprintf( rest,"CSeq: %u", seq );
00433             rtsp_put( rtsp, rest );
00434             rtsp_put( rtsp, "" );
00435             free( rest );
00436             i = rtsp->pf_read( rtsp->p_userdata, buffer, size );
00437         }
00438         else
00439         {
00440             i = rtsp->pf_read( rtsp->p_userdata, buffer + 4, size - 4 );
00441             i += 4;
00442         }
00443     }
00444     else i = rtsp->pf_read( rtsp->p_userdata, buffer, size );
00445 
00446     //fprintf( stderr, "<< %d of %d bytes\n", i, size );
00447 
00448     return i;
00449 }
00450 
00451 /*
00452  * connect to a rtsp server
00453  */
00454 
00455 int rtsp_connect( rtsp_client_t *rtsp, const char *psz_mrl,
00456                   const char *psz_user_agent )
00457 {
00458     rtsp_t *s;
00459     char *mrl_ptr;
00460     char *slash, *colon;
00461     unsigned int hostend, pathbegin, i;
00462 
00463     if( !psz_mrl ) return -1;
00464     s = malloc( sizeof(rtsp_t) );
00465     rtsp->p_private = s;
00466 
00467     if( !strncmp( psz_mrl, "rtsp://", 7 ) ) psz_mrl += 7;
00468     mrl_ptr = strdup( psz_mrl );
00469 
00470     for( i=0; i<MAX_FIELDS; i++ )
00471     {
00472         s->answers[i]=NULL;
00473         s->scheduled[i]=NULL;
00474     }
00475 
00476     s->host = NULL;
00477     s->port = 554; /* rtsp standard port */
00478     s->path = NULL;
00479     s->mrl  = strdup(psz_mrl);
00480 
00481     s->server = NULL;
00482     s->server_state = 0;
00483     s->server_caps = 0;
00484 
00485     s->cseq = 0;
00486     s->session = NULL;
00487 
00488     if( psz_user_agent ) s->user_agent = strdup( psz_user_agent );
00489     else s->user_agent = strdup( "User-Agent: RealMedia Player Version "
00490                                  "6.0.9.1235 (linux-2.0-libc6-i386-gcc2.95)" );
00491 
00492     slash = strchr( mrl_ptr, '/' );
00493     colon = strchr( mrl_ptr, ':' );
00494 
00495     if( !slash ) slash = mrl_ptr + strlen(mrl_ptr) + 1;
00496     if( !colon ) colon = slash; 
00497     if( colon > slash ) colon = slash;
00498 
00499     pathbegin = slash - mrl_ptr;
00500     hostend = colon - mrl_ptr;
00501 
00502     s->host = malloc(hostend+1);
00503     strncpy( s->host, mrl_ptr, hostend );
00504     s->host[hostend] = 0;
00505 
00506     if( pathbegin < strlen(mrl_ptr) ) s->path = strdup(mrl_ptr+pathbegin+1);
00507     if( colon != slash )
00508     {
00509         char buffer[pathbegin-hostend];
00510 
00511         strncpy( buffer, mrl_ptr+hostend+1, pathbegin-hostend-1 );
00512         buffer[pathbegin-hostend-1] = 0;
00513         s->port = atoi(buffer);
00514         if( s->port < 0 || s->port > 65535 ) s->port = 554;
00515     }
00516 
00517     free( mrl_ptr );
00518     fprintf( stderr, "got mrl: %s %i %s\n", s->host, s->port, s->path );
00519 
00520     s->s = rtsp->pf_connect( rtsp->p_userdata, s->host, s->port );
00521 
00522     if( s->s < 0 )
00523     {
00524         fprintf(stderr, "rtsp: failed to connect to '%s'\n", s->host);
00525         rtsp_close( rtsp );
00526         return -1;
00527     }
00528 
00529     s->server_state = RTSP_CONNECTED;
00530 
00531     /* now lets send an options request. */
00532     rtsp_schedule_field( rtsp, "CSeq: 1");
00533     rtsp_schedule_field( rtsp, s->user_agent);
00534     rtsp_schedule_field( rtsp, "ClientChallenge: "
00535                                "9e26d33f2984236010ef6253fb1887f7");
00536     rtsp_schedule_field( rtsp, "PlayerStarttime: [28/03/2003:22:50:23 00:00]");
00537     rtsp_schedule_field( rtsp, "CompanyID: KnKV4M4I/B2FjJ1TToLycw==" );
00538     rtsp_schedule_field( rtsp, "GUID: 00000000-0000-0000-0000-000000000000" );
00539     rtsp_schedule_field( rtsp, "RegionData: 0" );
00540     rtsp_schedule_field( rtsp, "ClientID: "
00541                                "Linux_2.4_6.0.9.1235_play32_RN01_EN_586" );
00542     /*rtsp_schedule_field( rtsp, "Pragma: initiate-session" );*/
00543     rtsp_request_options( rtsp, NULL );
00544 
00545     return 0;
00546 }
00547 
00548 /*
00549  * closes an rtsp connection 
00550  */
00551 
00552 void rtsp_close( rtsp_client_t *rtsp )
00553 {
00554     if( rtsp->p_private->server_state )
00555     {
00556         /* TODO: send a TEAROFF */
00557         rtsp->pf_disconnect( rtsp->p_userdata );
00558     }
00559 
00560     if( rtsp->p_private->path ) free( rtsp->p_private->path );
00561     if( rtsp->p_private->host ) free( rtsp->p_private->host );
00562     if( rtsp->p_private->mrl ) free( rtsp->p_private->mrl );
00563     if( rtsp->p_private->session ) free( rtsp->p_private->session );
00564     if( rtsp->p_private->user_agent ) free( rtsp->p_private->user_agent );
00565     if( rtsp->p_private->server ) free( rtsp->p_private->server );
00566     rtsp_free_answers( rtsp );
00567     rtsp_unschedule_all( rtsp );
00568     free( rtsp->p_private );
00569 }
00570 
00571 /*
00572  * search in answers for tags. returns a pointer to the content
00573  * after the first matched tag. returns NULL if no match found.
00574  */
00575 
00576 char *rtsp_search_answers( rtsp_client_t *rtsp, const char *tag )
00577 {
00578     char **answer;
00579     char *ptr;
00580 
00581     if( !rtsp->p_private->answers ) return NULL;
00582     answer = rtsp->p_private->answers;
00583 
00584     while(*answer)
00585     {
00586         if( !strncasecmp( *answer, tag, strlen(tag) ) )
00587         {
00588             ptr = strchr(*answer, ':');
00589             ptr++;
00590             while( *ptr == ' ' ) ptr++;
00591             return ptr;
00592         }
00593         answer++;
00594     }
00595 
00596     return NULL;
00597 }
00598 
00599 /*
00600  * session id management
00601  */
00602 
00603 void rtsp_set_session( rtsp_client_t *rtsp, const char *id )
00604 {
00605     if( rtsp->p_private->session ) free( rtsp->p_private->session );
00606     rtsp->p_private->session = strdup(id);
00607 }
00608 
00609 char *rtsp_get_session( rtsp_client_t *rtsp )
00610 {
00611     return rtsp->p_private->session;
00612 }
00613 
00614 char *rtsp_get_mrl( rtsp_client_t *rtsp )
00615 {
00616     return rtsp->p_private->mrl;
00617 }
00618 
00619 /*
00620  * schedules a field for transmission
00621  */
00622 
00623 void rtsp_schedule_field( rtsp_client_t *rtsp, const char *string )
00624 {
00625     int i = 0;
00626 
00627     if( !string ) return;
00628 
00629     while( rtsp->p_private->scheduled[i] ) i++;
00630 
00631     rtsp->p_private->scheduled[i] = strdup(string);
00632 }
00633 
00634 /*
00635  * removes the first scheduled field which prefix matches string. 
00636  */
00637 
00638 void rtsp_unschedule_field( rtsp_client_t *rtsp, const char *string )
00639 {
00640     char **ptr = rtsp->p_private->scheduled;
00641 
00642     if( !string ) return;
00643 
00644     while( *ptr )
00645     {
00646       if( !strncmp(*ptr, string, strlen(string)) ) break;
00647     }
00648     if( *ptr ) free( *ptr );
00649     ptr++;
00650     do
00651     {
00652         *(ptr-1) = *ptr;
00653     } while( *ptr );
00654 }
00655 
00656 /*
00657  * unschedule all fields
00658  */
00659 
00660 void rtsp_unschedule_all( rtsp_client_t *rtsp )
00661 {
00662     char **ptr;
00663 
00664     if( !rtsp->p_private->scheduled ) return;
00665     ptr = rtsp->p_private->scheduled;
00666 
00667     while( *ptr )
00668     {
00669         free( *ptr );
00670         *ptr = NULL;
00671         ptr++;
00672     }
00673 }
00674 /*
00675  * free answers
00676  */
00677 
00678 void rtsp_free_answers( rtsp_client_t *rtsp )
00679 {
00680     char **answer;
00681 
00682     if( !rtsp->p_private->answers ) return;
00683     answer = rtsp->p_private->answers;
00684 
00685     while( *answer )
00686     {
00687         free( *answer );
00688         *answer = NULL;
00689         answer++;
00690     }
00691 }

Generated on Tue Dec 20 10:14:26 2005 for vlc-0.8.4a by  doxygen 1.4.2