]> git.sesse.net Git - vlc/blob - modules/access/rtsp/rtsp.c
67b5b6eb8a97197d0a4d41c7b5a86965b32ce371
[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$
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
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, (uint8_t*)psz_buffer, (unsigned int)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     unsigned 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, (uint8_t*)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 = buf;
245       }
246       if( !strncasecmp( answer, "Session:", 8 ) )
247       {
248           char *buf = malloc( strlen(answer) );
249           sscanf( answer, "%*s %s", buf );
250           if( rtsp->p_private->session )
251           {
252               if( strcmp( buf, rtsp->p_private->session ) )
253               {
254                   //fprintf( stderr,
255                   //         "rtsp: warning: setting NEW session: %s\n", buf );
256                   free( rtsp->p_private->session );
257                   rtsp->p_private->session = strdup( buf );
258               }
259           }
260           else
261           {
262               //fprintf( stderr, "setting session id to: %s\n", buf );
263               rtsp->p_private->session = strdup( buf );
264           }
265           free( buf );
266       }
267
268       *answer_ptr = answer;
269       answer_ptr++;
270     } while( (strlen(answer) != 0) && (++ans_count < MAX_FIELDS) );
271
272     rtsp->p_private->cseq++;
273
274     *answer_ptr = NULL;
275     rtsp_schedule_standard( rtsp );
276
277     return code;
278 }
279
280 /*
281  * send an ok message
282  */
283
284 int rtsp_send_ok( rtsp_client_t *rtsp )
285 {
286     char cseq[16];
287
288     rtsp_put( rtsp, "RTSP/1.0 200 OK" );
289     sprintf( cseq, "CSeq: %u", rtsp->p_private->cseq );
290     rtsp_put( rtsp, cseq );
291     rtsp_put( rtsp, "" );
292     return 0;
293 }
294
295 /*
296  * implementation of must-have rtsp requests; functions return
297  * server status code.
298  */
299
300 int rtsp_request_options( rtsp_client_t *rtsp, const char *what )
301 {
302     char *buf;
303
304     if( what ) buf = strdup(what);
305     else
306     {
307         buf = malloc( strlen(rtsp->p_private->host) + 16 );
308         sprintf( buf, "rtsp://%s:%i", rtsp->p_private->host,
309                  rtsp->p_private->port );
310     }
311     rtsp_send_request( rtsp, "OPTIONS", buf );
312     free( buf );
313
314     return rtsp_get_answers( rtsp );
315 }
316
317 int rtsp_request_describe( rtsp_client_t *rtsp, const char *what )
318 {
319     char *buf;
320
321     if( what )
322     {
323         buf = strdup(what);
324     }
325     else
326     {
327         buf = malloc( strlen(rtsp->p_private->host) +
328                       strlen(rtsp->p_private->path) + 16 );
329         sprintf( buf, "rtsp://%s:%i/%s", rtsp->p_private->host,
330                  rtsp->p_private->port, rtsp->p_private->path );
331     }
332     rtsp_send_request( rtsp, "DESCRIBE", buf );
333     free( buf );
334
335     return rtsp_get_answers( rtsp );
336 }
337
338 int rtsp_request_setup( rtsp_client_t *rtsp, const char *what )
339 {
340     rtsp_send_request( rtsp, "SETUP", what );
341     return rtsp_get_answers( rtsp );
342 }
343
344 int rtsp_request_setparameter( rtsp_client_t *rtsp, const char *what )
345 {
346     char *buf;
347
348     if( what )
349     {
350         buf = strdup(what);
351     }
352     else
353     {
354         buf = malloc( strlen(rtsp->p_private->host) +
355                       strlen(rtsp->p_private->path) + 16 );
356         sprintf( buf, "rtsp://%s:%i/%s", rtsp->p_private->host,
357                  rtsp->p_private->port, rtsp->p_private->path );
358     }
359
360     rtsp_send_request( rtsp, "SET_PARAMETER", buf );
361     free( buf );
362
363     return rtsp_get_answers( rtsp );
364 }
365
366 int rtsp_request_play( rtsp_client_t *rtsp, const char *what )
367 {
368     char *buf;
369
370     if( what )
371     {
372         buf = strdup( what );
373     }
374     else
375     {
376         buf = malloc( strlen(rtsp->p_private->host) +
377                       strlen(rtsp->p_private->path) + 16 );
378         sprintf( buf, "rtsp://%s:%i/%s", rtsp->p_private->host,
379                  rtsp->p_private->port, rtsp->p_private->path );
380     }
381
382     rtsp_send_request( rtsp, "PLAY", buf );
383     free( buf );
384
385     return rtsp_get_answers( rtsp );
386 }
387
388 int rtsp_request_tearoff( rtsp_client_t *rtsp, const char *what )
389 {
390     rtsp_send_request( rtsp, "TEAROFF", what );
391     return rtsp_get_answers( rtsp );
392 }
393
394 /*
395  * read opaque data from stream
396  */
397
398 int rtsp_read_data( rtsp_client_t *rtsp, uint8_t *buffer, unsigned int size )
399 {
400     int i, seq;
401
402     if( size >= 4 )
403     {
404         i = rtsp->pf_read( rtsp->p_userdata, (uint8_t*)buffer, (unsigned int) 4 );
405         if( i < 4 ) return i;
406
407         if( buffer[0]=='S' && buffer[1]=='E' && buffer[2]=='T' &&
408             buffer[3]=='_' )
409         {
410             char *rest = rtsp_get( rtsp );
411             if( !rest ) return -1;
412
413             seq = -1;
414             do
415             {
416                 free( rest );
417                 rest = rtsp_get( rtsp );
418                 if( !rest ) return -1;
419
420                 if( !strncasecmp( rest, "Cseq:", 5 ) )
421                     sscanf( rest, "%*s %u", &seq );
422             } while( *rest );
423             free( rest );
424
425             if( seq < 0 )
426             {
427                 //fprintf(stderr, "warning: cseq not recognized!\n");
428                 seq = 1;
429             }
430
431             /* lets make the server happy */
432             rtsp_put( rtsp, "RTSP/1.0 451 Parameter Not Understood" );
433             rest = malloc(17);
434             sprintf( rest,"CSeq: %u", seq );
435             rtsp_put( rtsp, rest );
436             rtsp_put( rtsp, "" );
437             free( rest );
438             i = rtsp->pf_read( rtsp->p_userdata, (unsigned char*)buffer, size );
439         }
440         else
441         {
442             i = rtsp->pf_read( rtsp->p_userdata, (unsigned char*)buffer + 4, size - 4 );
443             i += 4;
444         }
445     }
446     else i = rtsp->pf_read( rtsp->p_userdata, (unsigned char*)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     unsigned 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     free( mrl_ptr );
520     //fprintf( stderr, "got mrl: %s %i %s\n", s->host, s->port, s->path );
521
522     s->s = rtsp->pf_connect( rtsp->p_userdata, s->host, s->port );
523
524     if( s->s < 0 )
525     {
526         //fprintf(stderr, "rtsp: failed to connect to '%s'\n", s->host);
527         rtsp_close( rtsp );
528         return -1;
529     }
530
531     s->server_state = RTSP_CONNECTED;
532
533     /* now lets send an options request. */
534     rtsp_schedule_field( rtsp, "CSeq: 1");
535     rtsp_schedule_field( rtsp, s->user_agent);
536     rtsp_schedule_field( rtsp, "ClientChallenge: "
537                                "9e26d33f2984236010ef6253fb1887f7");
538     rtsp_schedule_field( rtsp, "PlayerStarttime: [28/03/2003:22:50:23 00:00]");
539     rtsp_schedule_field( rtsp, "CompanyID: KnKV4M4I/B2FjJ1TToLycw==" );
540     rtsp_schedule_field( rtsp, "GUID: 00000000-0000-0000-0000-000000000000" );
541     rtsp_schedule_field( rtsp, "RegionData: 0" );
542     rtsp_schedule_field( rtsp, "ClientID: "
543                                "Linux_2.4_6.0.9.1235_play32_RN01_EN_586" );
544     /*rtsp_schedule_field( rtsp, "Pragma: initiate-session" );*/
545     rtsp_request_options( rtsp, NULL );
546
547     return 0;
548 }
549
550 /*
551  * closes an rtsp connection
552  */
553
554 void rtsp_close( rtsp_client_t *rtsp )
555 {
556     if( rtsp->p_private->server_state )
557     {
558         /* TODO: send a TEAROFF */
559         rtsp->pf_disconnect( rtsp->p_userdata );
560     }
561
562     if( rtsp->p_private->path ) free( rtsp->p_private->path );
563     if( rtsp->p_private->host ) free( rtsp->p_private->host );
564     if( rtsp->p_private->mrl ) free( rtsp->p_private->mrl );
565     if( rtsp->p_private->session ) free( rtsp->p_private->session );
566     if( rtsp->p_private->user_agent ) free( rtsp->p_private->user_agent );
567     if( rtsp->p_private->server ) free( rtsp->p_private->server );
568     rtsp_free_answers( rtsp );
569     rtsp_unschedule_all( rtsp );
570     free( rtsp->p_private );
571 }
572
573 /*
574  * search in answers for tags. returns a pointer to the content
575  * after the first matched tag. returns NULL if no match found.
576  */
577
578 char *rtsp_search_answers( rtsp_client_t *rtsp, const char *tag )
579 {
580     char **answer;
581     char *ptr;
582
583     if( !rtsp->p_private->answers ) return NULL;
584     answer = rtsp->p_private->answers;
585
586     while(*answer)
587     {
588         if( !strncasecmp( *answer, tag, strlen(tag) ) )
589         {
590             ptr = strchr(*answer, ':');
591             ptr++;
592             while( *ptr == ' ' ) ptr++;
593             return ptr;
594         }
595         answer++;
596     }
597
598     return NULL;
599 }
600
601 /*
602  * session id management
603  */
604
605 void rtsp_set_session( rtsp_client_t *rtsp, const char *id )
606 {
607     if( rtsp->p_private->session ) free( rtsp->p_private->session );
608     rtsp->p_private->session = strdup(id);
609 }
610
611 char *rtsp_get_session( rtsp_client_t *rtsp )
612 {
613     return rtsp->p_private->session;
614 }
615
616 char *rtsp_get_mrl( rtsp_client_t *rtsp )
617 {
618     return rtsp->p_private->mrl;
619 }
620
621 /*
622  * schedules a field for transmission
623  */
624
625 void rtsp_schedule_field( rtsp_client_t *rtsp, const char *string )
626 {
627     int i = 0;
628
629     if( !string ) return;
630
631     while( rtsp->p_private->scheduled[i] ) i++;
632
633     rtsp->p_private->scheduled[i] = strdup(string);
634 }
635
636 /*
637  * removes the first scheduled field which prefix matches string.
638  */
639
640 void rtsp_unschedule_field( rtsp_client_t *rtsp, const char *string )
641 {
642     char **ptr = rtsp->p_private->scheduled;
643
644     if( !string ) return;
645
646     while( *ptr )
647     {
648       if( !strncmp(*ptr, string, strlen(string)) ) break;
649     }
650     if( *ptr ) free( *ptr );
651     ptr++;
652     do
653     {
654         *(ptr-1) = *ptr;
655     } while( *ptr );
656 }
657
658 /*
659  * unschedule all fields
660  */
661
662 void rtsp_unschedule_all( rtsp_client_t *rtsp )
663 {
664     char **ptr;
665
666     if( !rtsp->p_private->scheduled ) return;
667     ptr = rtsp->p_private->scheduled;
668
669     while( *ptr )
670     {
671         free( *ptr );
672         *ptr = NULL;
673         ptr++;
674     }
675 }
676 /*
677  * free answers
678  */
679
680 void rtsp_free_answers( rtsp_client_t *rtsp )
681 {
682     char **answer;
683
684     if( !rtsp->p_private->answers ) return;
685     answer = rtsp->p_private->answers;
686
687     while( *answer )
688     {
689         free( *answer );
690         *answer = NULL;
691         answer++;
692     }
693 }