]> git.sesse.net Git - vlc/blob - modules/access/rtsp/rtsp.c
Fix several memory leaks and do a bit of coding style cleanup.
[vlc] / modules / access / rtsp / rtsp.c
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 $
8  *
9  * Authors: Gildas Bazin <gbazin@videolan.org>
10  *          Adapted from xine which itself adapted it from joschkas real tools.
11  *
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.
16  *
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.
21  *
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  *****************************************************************************/
26 #include <stdio.h>
27 #include <stdlib.h>
28
29 #include <vlc/vlc.h>
30
31 #include "rtsp.h"
32
33 #define BUF_SIZE 4096
34 #define HEADER_SIZE 1024
35 #define MAX_FIELDS 256
36
37 struct rtsp_s {
38
39   int           s;
40
41   char         *host;
42   int           port;
43   char         *path;
44   char         *mrl;
45   char         *user_agent;
46
47   char         *server;
48   unsigned int  server_state;
49   uint32_t      server_caps;
50
51   unsigned int  cseq;
52   char         *session;
53
54   char        *answers[MAX_FIELDS];   /* data of last message */
55   char        *scheduled[MAX_FIELDS]; /* will be sent with next message */
56 };
57
58 /*
59  * constants
60  */
61
62 const char rtsp_protocol_version[]="RTSP/1.0";
63
64 /* server states */
65 #define RTSP_CONNECTED 1
66 #define RTSP_INIT      2
67 #define RTSP_READY     4
68 #define RTSP_PLAYING   8
69 #define RTSP_RECORDING 16
70
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
81
82 /*
83  * rtsp_get gets a line from stream
84  * and returns a null terminated string (must be freed).
85  */
86  
87 static char *rtsp_get( rtsp_client_t *rtsp )
88 {
89   char *psz_buffer = malloc( BUF_SIZE );
90   char *psz_string = NULL;
91
92   if( rtsp->pf_read_line( rtsp->p_userdata, psz_buffer, BUF_SIZE ) >= 0 )
93   {
94     //printf( "<< '%s'\n", psz_buffer );
95       psz_string = strdup( psz_buffer );
96   }
97
98   free( psz_buffer );
99   return psz_string;
100 }
101
102
103 /*
104  * rtsp_put puts a line on stream
105  */
106
107 static int rtsp_put( rtsp_client_t *rtsp, const char *psz_string )
108 {
109     int i_buffer = strlen( psz_string );
110     char *psz_buffer = malloc( i_buffer + 3 );
111     int i_ret;
112
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;
116
117     i_ret = rtsp->pf_write( rtsp->p_userdata, psz_buffer, i_buffer + 2 );
118
119     free( psz_buffer );
120     return i_ret;
121 }
122
123 /*
124  * extract server status code
125  */
126
127 static int rtsp_get_status_code( rtsp_client_t *rtsp, const char *psz_string )
128 {
129     char psz_buffer[4];
130     int i_code = 0;
131
132     if( !strncmp( psz_string, "RTSP/1.0", sizeof("RTSP/1.0") - 1 ) )
133     {
134         memcpy( psz_buffer, psz_string + sizeof("RTSP/1.0"), 3 );
135         psz_buffer[3] = 0;
136         i_code = atoi( psz_buffer );
137     }
138     else if( !strncmp( psz_string, "SET_PARAMETER", 8 ) )
139     {
140         return RTSP_STATUS_SET_PARAMETER;
141     }
142
143     if( i_code != 200 )
144     {
145         fprintf( stderr, "librtsp: server responds: '%s'\n", psz_string );
146     }
147
148     return i_code;
149 }
150
151 /*
152  * send a request
153  */
154
155 static int rtsp_send_request( rtsp_client_t *rtsp, const char *psz_type,
156                               const char *psz_what )
157 {
158     char **ppsz_payload = rtsp->p_private->scheduled;
159     char *psz_buffer;
160     int i_ret;
161
162     psz_buffer = malloc( strlen(psz_type) + strlen(psz_what) +
163                          sizeof("RTSP/1.0") + 2 );
164
165     sprintf( psz_buffer, "%s %s %s", psz_type, psz_what, "RTSP/1.0" );
166     i_ret = rtsp_put( rtsp, psz_buffer );
167     free( psz_buffer );
168
169     if( ppsz_payload )
170         while( *ppsz_payload )
171         {
172             rtsp_put( rtsp, *ppsz_payload );
173             ppsz_payload++;
174         }
175     rtsp_put( rtsp, "" );
176     rtsp_unschedule_all( rtsp );
177
178     return i_ret;
179 }
180
181 /*
182  * schedule standard fields
183  */
184
185 static void rtsp_schedule_standard( rtsp_client_t *rtsp )
186 {
187     char tmp[17];
188
189     sprintf( tmp, "Cseq: %u", rtsp->p_private->cseq);
190     rtsp_schedule_field( rtsp, tmp );
191
192     if( rtsp->p_private->session )
193     {
194         char *buf;
195         buf = malloc( strlen(rtsp->p_private->session) + 15 );
196         sprintf( buf, "Session: %s", rtsp->p_private->session );
197         rtsp_schedule_field( rtsp, buf );
198         free( buf );
199     }
200 }
201
202 /*
203  * get the answers, if server responses with something != 200, return NULL
204  */
205
206 static int rtsp_get_answers( rtsp_client_t *rtsp )
207 {
208     char *answer = NULL;
209     unsigned int answer_seq;
210     char **answer_ptr = rtsp->p_private->answers;
211     int code;
212     int ans_count = 0;
213
214     answer = rtsp_get( rtsp );
215     if( !answer ) return 0;
216     code = rtsp_get_status_code( rtsp, answer );
217     free( answer );
218
219     rtsp_free_answers( rtsp );
220
221     do { /* while we get answer lines */
222
223       answer = rtsp_get( rtsp );
224       if( !answer ) return 0;
225
226       if( !strncasecmp( answer, "Cseq:", 5 ) )
227       {
228           sscanf( answer, "%*s %u", &answer_seq );
229           if( rtsp->p_private->cseq != answer_seq )
230           {
231             //fprintf( stderr, "warning: Cseq mismatch. got %u, assumed %u",
232             //       answer_seq, rtsp->p_private->cseq );
233
234               rtsp->p_private->cseq = answer_seq;
235           }
236       }
237       if( !strncasecmp( answer, "Server:", 7 ) )
238       {
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 );
243           free( buf );
244       }
245       if( !strncasecmp( answer, "Session:", 8 ) )
246       {
247           char *buf = malloc( strlen(answer) );
248           sscanf( answer, "%*s %s", buf );
249           if( rtsp->p_private->session )
250           {
251               if( strcmp( buf, rtsp->p_private->session ) )
252               {
253                   fprintf( stderr, 
254                            "rtsp: warning: setting NEW session: %s\n", buf );
255                   free( rtsp->p_private->session );
256                   rtsp->p_private->session = strdup( buf );
257               }
258           }
259           else
260           {
261               fprintf( stderr, "setting session id to: %s\n", buf );
262               rtsp->p_private->session = strdup( buf );
263           }
264           free( buf );
265       }
266
267       *answer_ptr = answer;
268       answer_ptr++;
269     } while( (strlen(answer) != 0) && (++ans_count < MAX_FIELDS) );
270
271     rtsp->p_private->cseq++;
272
273     *answer_ptr = NULL;
274     rtsp_schedule_standard( rtsp );
275
276     return code;
277 }
278
279 /*
280  * send an ok message
281  */
282
283 int rtsp_send_ok( rtsp_client_t *rtsp )
284 {
285     char cseq[16];
286
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, "" );
291     return 0;
292 }
293
294 /*
295  * implementation of must-have rtsp requests; functions return
296  * server status code.
297  */
298
299 int rtsp_request_options( rtsp_client_t *rtsp, const char *what )
300 {
301     char *buf;
302
303     if( what ) buf = strdup(what);
304     else
305     {
306         buf = malloc( strlen(rtsp->p_private->host) + 16 );
307         sprintf( buf, "rtsp://%s:%i", rtsp->p_private->host,
308                  rtsp->p_private->port );
309     }
310     rtsp_send_request( rtsp, "OPTIONS", buf );
311     free( buf );
312
313     return rtsp_get_answers( rtsp );
314 }
315
316 int rtsp_request_describe( rtsp_client_t *rtsp, const char *what )
317 {
318     char *buf;
319
320     if( what )
321     {
322         buf = strdup(what);
323     }
324     else
325     {
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 );
330     }
331     rtsp_send_request( rtsp, "DESCRIBE", buf );
332     free( buf );
333
334     return rtsp_get_answers( rtsp );
335 }
336
337 int rtsp_request_setup( rtsp_client_t *rtsp, const char *what )
338 {
339     rtsp_send_request( rtsp, "SETUP", what );
340     return rtsp_get_answers( rtsp );
341 }
342
343 int rtsp_request_setparameter( rtsp_client_t *rtsp, const char *what )
344 {
345     char *buf;
346
347     if( what )
348     {
349         buf = strdup(what);
350     }
351     else
352     {
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 );
357     }
358
359     rtsp_send_request( rtsp, "SET_PARAMETER", buf );
360     free( buf );
361
362     return rtsp_get_answers( rtsp );
363 }
364
365 int rtsp_request_play( rtsp_client_t *rtsp, const char *what )
366 {
367     char *buf;
368
369     if( what )
370     {
371         buf = strdup( what );
372     }
373     else
374     {
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 );
379     }
380
381     rtsp_send_request( rtsp, "PLAY", buf );
382     free( buf );
383
384     return rtsp_get_answers( rtsp );
385 }
386
387 int rtsp_request_tearoff( rtsp_client_t *rtsp, const char *what )
388 {
389     rtsp_send_request( rtsp, "TEAROFF", what );
390     return rtsp_get_answers( rtsp );
391 }
392
393 /*
394  * read opaque data from stream
395  */
396
397 int rtsp_read_data( rtsp_client_t *rtsp, char *buffer, unsigned int size )
398 {
399     int i, seq;
400
401     if( size >= 4 )
402     {
403         i= rtsp->pf_read( rtsp->p_userdata, buffer, 4 );
404         if( i < 4 ) return i;
405
406         if( buffer[0]=='S' && buffer[1]=='E' && buffer[2]=='T' &&
407             buffer[3]=='_' )
408         {
409             char *rest = rtsp_get( rtsp );
410             if( !rest ) return -1;
411
412             seq = -1;
413             do
414             {
415                 free( rest );
416                 rest = rtsp_get( rtsp );
417                 if( !rest ) return -1;
418
419                 if( !strncasecmp( rest, "Cseq:", 5 ) )
420                     sscanf( rest, "%*s %u", &seq );
421             } while( strlen(rest) != 0 );
422
423             free( rest );
424             if( seq < 0 )
425             {
426                 fprintf(stderr, "warning: cseq not recognized!\n");
427                 seq=1;
428             }
429
430             /* lets make the server happy */
431             rtsp_put( rtsp, "RTSP/1.0 451 Parameter Not Understood" );
432             rest = malloc(17);
433             sprintf( rest,"CSeq: %u", seq );
434             rtsp_put( rtsp, rest );
435             rtsp_put( rtsp, "" );
436             rtsp->pf_read( rtsp->p_userdata, buffer, size );
437         }
438         else
439         {
440             i = rtsp->pf_read( rtsp->p_userdata, buffer + 4, size - 4 );
441             i += 4;
442         }
443     }
444     else i= rtsp->pf_read( rtsp->p_userdata, buffer, size );
445
446     //fprintf( stderr, "<< %d of %d bytes\n", i, size );
447
448     return i;
449 }
450
451 /*
452  * connect to a rtsp server
453  */
454
455 int rtsp_connect( rtsp_client_t *rtsp, const char *psz_mrl,
456                   const char *psz_user_agent )
457 {
458     rtsp_t *s;
459     char *mrl_ptr;
460     char *slash, *colon;
461     int hostend, pathbegin, i;
462
463     if( !psz_mrl ) return -1;
464     s = malloc( sizeof(rtsp_t) );
465     rtsp->p_private = s;
466
467     if( !strncmp( psz_mrl, "rtsp://", 7 ) ) psz_mrl += 7;
468     mrl_ptr = strdup( psz_mrl );
469
470     for( i=0; i<MAX_FIELDS; i++ )
471     {
472         s->answers[i]=NULL;
473         s->scheduled[i]=NULL;
474     }
475
476     s->host = NULL;
477     s->port = 554; /* rtsp standard port */
478     s->path = NULL;
479     s->mrl  = strdup(psz_mrl);
480
481     s->server = NULL;
482     s->server_state = 0;
483     s->server_caps = 0;
484
485     s->cseq = 0;
486     s->session = NULL;
487
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)" );
491
492     slash = strchr( mrl_ptr, '/' );
493     colon = strchr( mrl_ptr, ':' );
494
495     if( !slash ) slash = mrl_ptr + strlen(mrl_ptr) + 1;
496     if( !colon ) colon = slash; 
497     if( colon > slash ) colon = slash;
498
499     pathbegin = slash - mrl_ptr;
500     hostend = colon - mrl_ptr;
501
502     s->host = malloc(hostend+1);
503     strncpy( s->host, mrl_ptr, hostend );
504     s->host[hostend] = 0;
505
506     if( pathbegin < strlen(mrl_ptr) ) s->path = strdup(mrl_ptr+pathbegin+1);
507     if( colon != slash )
508     {
509         char buffer[pathbegin-hostend];
510
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;
515     }
516
517     fprintf( stderr, "got mrl: %s %i %s\n", s->host, s->port, s->path );
518
519     s->s = rtsp->pf_connect( rtsp->p_userdata, s->host, s->port );
520
521     if( s->s < 0 )
522     {
523         fprintf(stderr, "rtsp: failed to connect to '%s'\n", s->host);
524         rtsp_close( rtsp );
525         return -1;
526     }
527
528     s->server_state = RTSP_CONNECTED;
529
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 );
543
544     return 0;
545 }
546
547 /*
548  * closes an rtsp connection 
549  */
550
551 void rtsp_close( rtsp_client_t *rtsp )
552 {
553     if( rtsp->p_private->server_state )
554     {
555         /* TODO: send a TEAROFF */
556         rtsp->pf_disconnect( rtsp->p_userdata );
557     }
558
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 );
567 }
568
569 /*
570  * search in answers for tags. returns a pointer to the content
571  * after the first matched tag. returns NULL if no match found.
572  */
573
574 char *rtsp_search_answers( rtsp_client_t *rtsp, const char *tag )
575 {
576     char **answer;
577     char *ptr;
578
579     if( !rtsp->p_private->answers ) return NULL;
580     answer = rtsp->p_private->answers;
581
582     while(*answer)
583     {
584         if( !strncasecmp( *answer, tag, strlen(tag) ) )
585         {
586             ptr = strchr(*answer, ':');
587             ptr++;
588             while( *ptr == ' ' ) ptr++;
589             return ptr;
590         }
591         answer++;
592     }
593
594     return NULL;
595 }
596
597 /*
598  * session id management
599  */
600
601 void rtsp_set_session( rtsp_client_t *rtsp, const char *id )
602 {
603     if( rtsp->p_private->session ) free( rtsp->p_private->session );
604     rtsp->p_private->session = strdup(id);
605 }
606
607 char *rtsp_get_session( rtsp_client_t *rtsp )
608 {
609     return rtsp->p_private->session;
610 }
611
612 char *rtsp_get_mrl( rtsp_client_t *rtsp )
613 {
614     return rtsp->p_private->mrl;
615 }
616
617 /*
618  * schedules a field for transmission
619  */
620
621 void rtsp_schedule_field( rtsp_client_t *rtsp, const char *string )
622 {
623     int i = 0;
624
625     if( !string ) return;
626
627     while( rtsp->p_private->scheduled[i] ) i++;
628
629     rtsp->p_private->scheduled[i] = strdup(string);
630 }
631
632 /*
633  * removes the first scheduled field which prefix matches string. 
634  */
635
636 void rtsp_unschedule_field( rtsp_client_t *rtsp, const char *string )
637 {
638     char **ptr = rtsp->p_private->scheduled;
639
640     if( !string ) return;
641
642     while( *ptr )
643     {
644       if( !strncmp(*ptr, string, strlen(string)) ) break;
645     }
646     if( *ptr ) free( *ptr );
647     ptr++;
648     do
649     {
650         *(ptr-1) = *ptr;
651     } while( *ptr );
652 }
653
654 /*
655  * unschedule all fields
656  */
657
658 void rtsp_unschedule_all( rtsp_client_t *rtsp )
659 {
660     char **ptr;
661
662     if( !rtsp->p_private->scheduled ) return;
663     ptr = rtsp->p_private->scheduled;
664
665     while( *ptr )
666     {
667         free( *ptr );
668         *ptr = NULL;
669         ptr++;
670     }
671 }
672 /*
673  * free answers
674  */
675
676 void rtsp_free_answers( rtsp_client_t *rtsp )
677 {
678     char **answer;
679
680     if( !rtsp->p_private->answers ) return;
681     answer = rtsp->p_private->answers;
682
683     while( *answer )
684     {
685         free( *answer );
686         *answer = NULL;
687         answer++;
688     }
689 }