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