1 /*****************************************************************************
2 * sdp.c: SDP parser and builtin UDP/RTP/RTSP
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: sdp.c,v 1.3 2003/08/04 00:48:11 fenrir Exp $
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
27 #include <stdlib.h> /* malloc(), free() */
30 #include <vlc/input.h>
34 /*****************************************************************************
36 *****************************************************************************/
37 static int Open ( vlc_object_t * );
38 static void Close( vlc_object_t * );
41 set_description( _("SDP demuxer + UDP/RTP/RTSP") );
42 set_capability( "demux", 100 );
43 add_category_hint( "Stream", NULL, VLC_FALSE );
44 add_integer( "sdp-session", 0, NULL,
45 "Session", "Session", VLC_TRUE );
47 set_callbacks( Open, Close );
48 add_shortcut( "sdp" );
52 /*****************************************************************************
54 *****************************************************************************/
55 static int Demux ( input_thread_t * );
57 #define FREE( p ) if( p ) { free( p ) ; (p) = NULL; }
72 char *psz_description;
78 sdp_attribute_t *attribute;
86 char *psz_description;
96 sdp_attribute_t *attribute;
106 sdp_session_t *session;
111 static sdp_t *sdp_Parse ( char * );
112 static void sdp_Dump ( input_thread_t *, sdp_t * );
113 static void sdp_Release ( sdp_t * );
115 #define RTP_PAYLOAD_MAX 10
118 int i_cat; /* AUDIO_ES, VIDEO_ES */
120 char *psz_control; /* If any, eg rtsp://blabla/... */
122 int i_port; /* base port */
123 int i_port_count; /* for hierachical stream */
125 int i_address_count;/* for hierachical stream */
129 char *psz_transport; /* RTP/AVP, udp, ... */
137 } payload[RTP_PAYLOAD_MAX];
141 static sdp_track_t *sdp_TrackCreate ( sdp_t *, int , int , char * );
142 static void sdp_TrackRelease( sdp_track_t * );
158 /*****************************************************************************
160 *****************************************************************************/
161 static int Open( vlc_object_t * p_this )
163 input_thread_t *p_input = (input_thread_t *)p_this;
174 sdp_session_t *p_session;
176 /* See if it looks like a SDP
177 v, o, s fields are mandatory and in this order */
178 if( input_Peek( p_input, &p_peek, 7 ) < 7 )
180 msg_Err( p_input, "cannot peek" );
183 if( strncmp( p_peek, "v=0\r\no=", 7 ) &&
184 strncmp( p_peek, "v=0\no=", 6 ) )
186 msg_Err( p_input, "SDP module discarded" );
190 /* Set input_thread_t fields */
191 p_input->pf_demux = Demux;
192 p_input->p_demux_data = p_sys = malloc( sizeof( demux_sys_t ) );
194 /* Init private data */
197 if( ( p_sys->s = stream_OpenInput( p_input ) ) == NULL )
199 msg_Err( p_input, "cannot create stream" );
203 /* Read the complete SDP file */
206 psz_sdp = malloc( i_sdp_max );
211 i_read = stream_Read( p_sys->s, &psz_sdp[i_sdp], i_sdp_max - i_sdp -1 );
212 if( i_read < i_sdp_max - i_sdp -1 )
222 psz_sdp = realloc( psz_sdp, i_sdp_max );
224 psz_sdp[i_sdp] = '\0';
226 if( strlen( psz_sdp ) <= 0 )
228 msg_Err( p_input, "cannot read SDP file" );
234 if( ( p_sys->p_sdp = sdp_Parse( psz_sdp ) ) == NULL )
236 msg_Err( p_input, "cannot parse SDP" );
239 sdp_Dump( p_input, p_sys->p_sdp );
241 /* Get the selected session */
242 var_Create( p_input, "sdp-session", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
243 var_Get( p_input, "sdp-session", &val );
244 p_sys->i_session = val.i_int;
245 if( p_sys->i_session >= p_sys->p_sdp->i_session )
247 p_sys->i_session = 0;
249 p_session = &p_sys->p_sdp->session[p_sys->i_session];
251 /* Now create a track for each media */
252 for( i = 0; i < p_session->i_media; i++ )
257 tk = sdp_TrackCreate( p_sys->p_sdp, p_sys->i_session, i, p_input->psz_source );
260 msg_Warn( p_input, "media[%d] invalid", i );
264 msg_Dbg( p_input, "media[%d] :", i );
265 msg_Dbg( p_input, " - cat : %s",
266 tk->i_cat == AUDIO_ES ? "audio" :
267 ( tk->i_cat == VIDEO_ES ? "video":"unknown") );
268 msg_Dbg( p_input, " - control : %s", tk->psz_control );
269 msg_Dbg( p_input, " - address : %s ttl : %d count : %d",
270 tk->psz_address, tk->i_address_ttl, tk->i_address_count );
271 msg_Dbg( p_input, " - port : %d count : %d", tk->i_port, tk->i_port_count );
272 msg_Dbg( p_input, " - transport : %s", tk->psz_transport );
273 for( j = 0; j < tk->i_payload_count; j++ )
275 msg_Dbg( p_input, " - payload[%d] : type : %d rtpmap : %s fmtp : %s",
276 j, tk->payload[j].i_type,
277 tk->payload[j].psz_rtpmap,
278 tk->payload[j].psz_fmtp );
281 sdp_TrackRelease( tk );
288 stream_Release( p_sys->s );
294 /*****************************************************************************
295 * Close: frees unused data
296 *****************************************************************************/
297 static void Close( vlc_object_t *p_this )
299 input_thread_t *p_input = (input_thread_t *)p_this;
300 demux_sys_t *p_sys = p_input->p_demux_data;
302 sdp_Release( p_sys->p_sdp );
306 /*****************************************************************************
307 * Demux: reads and demuxes data packets
308 *****************************************************************************
309 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
310 *****************************************************************************/
311 static int Demux ( input_thread_t *p_input )
317 /*****************************************************************************
319 *****************************************************************************/
321 /*****************************************************************************
323 *****************************************************************************/
324 static int sdp_GetLine( char **ppsz_sdp, char *p_com, char **pp_arg )
330 if( p[0] < 'a' || p[0] > 'z' || p[1] != '=' )
337 if( ( p_end = strstr( p, "\n" ) ) == NULL )
339 p_end = p + strlen( p );
343 while( *p_end == '\n' || *p_end == '\r' )
350 i_size = p_end - &p[2];
351 *pp_arg = malloc( i_size + 1 );
352 memcpy( *pp_arg, &p[2], i_size );
353 (*pp_arg)[i_size] = '\0';
355 while( *p_end == '\r' || *p_end == '\n' )
363 static sdp_t *sdp_Parse ( char *psz_sdp )
365 sdp_t *sdp = malloc( sizeof( sdp_t ) );
372 #define p_session (&sdp->session[sdp->i_session])
373 #define p_media (&p_session->media[p_session->i_media])
376 if( sdp_GetLine( &psz_sdp, &com, &psz ) )
380 fprintf( stderr, "com=%c arg=%s\n", com, psz );
381 if( sdp->i_session < 0 && ( com !='v' || strcmp( psz, "0" ) ) )
388 fprintf( stderr, "New session added\n" );
389 if( sdp->i_session != -1 )
391 p_session->i_media++;
393 /* Add a new session */
396 realloc( sdp->session,
397 (sdp->i_session + 1)*sizeof(sdp_session_t) );
398 p_session->psz_origin = NULL;
399 p_session->psz_session = NULL;
400 p_session->psz_description= NULL;
401 p_session->psz_uri = NULL;
402 p_session->psz_email = NULL;
403 p_session->psz_phone = NULL;
404 p_session->psz_connection = NULL;
405 p_session->psz_bandwith = NULL;
406 p_session->psz_key = NULL;
407 p_session->psz_timezone = NULL;
408 p_session->i_media = -1;
409 p_session->media = NULL;
410 p_session->i_attribute = 0;
411 p_session->attribute = 0;
415 fprintf( stderr, "New media added\n" );
416 p_session->i_media++;
418 realloc( p_session->media,
419 (p_session->i_media + 1)*sizeof( sdp_media_t ) );
420 p_media->psz_media = strdup( psz );
421 p_media->psz_description= NULL;
422 p_media->psz_connection = NULL;
423 p_media->psz_bandwith = NULL;
424 p_media->psz_key = NULL;
425 p_media->i_attribute = 0;
426 p_media->attribute = 0;
429 p_session->psz_origin = strdup( psz );
432 p_session->psz_session = strdup( psz );
435 if( p_session->i_media != -1 )
437 p_media->psz_description = strdup( psz );
441 p_session->psz_description = strdup( psz );
445 p_session->psz_uri = strdup( psz );
448 p_session->psz_email = strdup( psz );
451 p_session->psz_phone = strdup( psz );
454 if( p_session->i_media != -1 )
456 p_media->psz_connection = strdup( psz );
460 /* FIXME could be multiple address FIXME */
462 c=IN IP4 224.2.1.1/127
463 c=IN IP4 224.2.1.2/127
464 c=IN IP4 224.2.1.3/127
466 p_session->psz_connection = strdup( psz );
470 if( p_session->i_media != -1 )
472 p_media->psz_bandwith = strdup( psz );
476 p_session->psz_bandwith = strdup( psz );
480 if( p_session->i_media != -1 )
482 p_media->psz_key = strdup( psz );
486 p_session->psz_key = strdup( psz );
490 p_session->psz_timezone = strdup( psz );
494 char *p = strchr( psz, ':' );
503 name = strdup( psz );
505 if( p_session->i_media != -1 )
508 = realloc( p_media->attribute,
509 ( p_media->i_attribute + 1 ) *
510 sizeof( sdp_attribute_t ) );
511 p_media->attribute[p_media->i_attribute].psz_name = name;
512 p_media->attribute[p_media->i_attribute].psz_value = value;
513 p_media->i_attribute++;
517 p_session->psz_key = strdup( psz );
519 = realloc( p_session->attribute,
520 ( p_session->i_attribute + 1 ) *
521 sizeof( sdp_attribute_t ) );
522 p_session->attribute[p_session->i_attribute].psz_name = name;
523 p_session->attribute[p_session->i_attribute].psz_value = value;
524 p_session->i_attribute++;
530 fprintf( stderr, "unhandled com=%c\n", com );
538 if( sdp->i_session < 0 )
543 sdp->session[sdp->i_session].i_media++;
548 static void sdp_Release( sdp_t *p_sdp )
551 for( i = 0; i < p_sdp->i_session; i++ )
553 FREE( p_sdp->session[i].psz_origin );
554 FREE( p_sdp->session[i].psz_session );
555 FREE( p_sdp->session[i].psz_description );
556 FREE( p_sdp->session[i].psz_uri );
557 FREE( p_sdp->session[i].psz_email );
558 FREE( p_sdp->session[i].psz_phone );
559 FREE( p_sdp->session[i].psz_connection );
560 FREE( p_sdp->session[i].psz_bandwith );
561 FREE( p_sdp->session[i].psz_key );
562 FREE( p_sdp->session[i].psz_timezone );
563 for( i_attr = 0; i_attr < p_sdp->session[i].i_attribute; i_attr++ )
565 FREE( p_sdp->session[i].attribute[i].psz_name );
566 FREE( p_sdp->session[i].attribute[i].psz_value );
568 FREE( p_sdp->session[i].attribute );
570 for( j = 0; j < p_sdp->session[i].i_media; j++ )
572 FREE( p_sdp->session[i].media[j].psz_media );
573 FREE( p_sdp->session[i].media[j].psz_description );
574 FREE( p_sdp->session[i].media[j].psz_connection );
575 FREE( p_sdp->session[i].media[j].psz_bandwith );
576 FREE( p_sdp->session[i].media[j].psz_key );
577 for( i_attr = 0; i_attr < p_sdp->session[i].i_attribute; i_attr++ )
579 FREE( p_sdp->session[i].media[j].attribute[i_attr].psz_name );
580 FREE( p_sdp->session[i].media[j].attribute[i_attr].psz_value );
582 FREE( p_sdp->session[i].media[j].attribute );
584 FREE( p_sdp->session[i].media);
586 FREE( p_sdp->session );
590 static void sdp_Dump ( input_thread_t *p_input, sdp_t *p_sdp )
593 #define PRINTS( var, fmt ) \
594 if( var ) { msg_Dbg( p_input, " - " fmt " : %s", var ); }
595 #define PRINTM( var, fmt ) \
596 if( var ) { msg_Dbg( p_input, " - " fmt " : %s", var ); }
598 for( i = 0; i < p_sdp->i_session; i++ )
600 msg_Dbg( p_input, "session[%d]", i );
601 PRINTS( p_sdp->session[i].psz_origin, "Origin" );
602 PRINTS( p_sdp->session[i].psz_session, "Session" );
603 PRINTS( p_sdp->session[i].psz_description, "Description" );
604 PRINTS( p_sdp->session[i].psz_uri, "URI" );
605 PRINTS( p_sdp->session[i].psz_email, "e-mail" );
606 PRINTS( p_sdp->session[i].psz_phone, "Phone" );
607 PRINTS( p_sdp->session[i].psz_connection, "Connection" );
608 PRINTS( p_sdp->session[i].psz_bandwith, "Bandwith" );
609 PRINTS( p_sdp->session[i].psz_key, "Key" );
610 PRINTS( p_sdp->session[i].psz_timezone, "TimeZone" );
611 for( i_attr = 0; i_attr < p_sdp->session[i].i_attribute; i_attr++ )
613 msg_Dbg( p_input, " - attribute[%d] name:'%s' value:'%s'",
615 p_sdp->session[i].attribute[i_attr].psz_name,
616 p_sdp->session[i].attribute[i_attr].psz_value );
619 for( j = 0; j < p_sdp->session[i].i_media; j++ )
621 msg_Dbg( p_input, " - media[%d]", j );
622 PRINTM( p_sdp->session[i].media[j].psz_media, "Name" );
623 PRINTM( p_sdp->session[i].media[j].psz_description, "Description" );
624 PRINTM( p_sdp->session[i].media[j].psz_connection, "Connection" );
625 PRINTM( p_sdp->session[i].media[j].psz_bandwith, "Bandwith" );
626 PRINTM( p_sdp->session[i].media[j].psz_key, "Key" );
628 for( i_attr = 0; i_attr < p_sdp->session[i].media[j].i_attribute; i_attr++ )
630 msg_Dbg( p_input, " - attribute[%d] name:'%s' value:'%s'",
632 p_sdp->session[i].media[j].attribute[i_attr].psz_name,
633 p_sdp->session[i].media[j].attribute[i_attr].psz_value );
640 static char * sdp_AttributeValue( int i_attribute, sdp_attribute_t *attribute,
645 for( i = 0; i < i_attribute; i++ )
647 if( !strcmp( attribute[i].psz_name, name ) )
649 return attribute[i].psz_value;
656 * Create a track from an SDP session/media
658 static sdp_track_t *sdp_TrackCreate( sdp_t *p_sdp, int i_session, int i_media,
661 sdp_track_t *tk = malloc( sizeof( sdp_track_t ) );
662 sdp_session_t *p_session = &p_sdp->session[i_session];
663 sdp_media_t *p_media = &p_session->media[i_media];
669 if( !strncmp( p_media->psz_media, "audio", 5 ) )
671 tk->i_cat = AUDIO_ES;
673 else if( !strncmp( p_media->psz_media, "video", 5 ) )
675 tk->i_cat = VIDEO_ES;
682 p = &p_media->psz_media[5];
684 /* Get track port base and count */
685 tk->i_port = strtol( p, &p, 0 );
690 tk->i_port_count = strtol( p, &p, 0 );
694 tk->i_port_count = 0;
703 tk->psz_transport = strdup( p );
704 if( ( psz = strchr( tk->psz_transport, ' ' ) ) )
708 while( *p && *p != ' ' )
713 /* Get payload type+fmt */
714 tk->i_payload_count = 0;
719 tk->payload[tk->i_payload_count].i_type = strtol( p, &p, 0 );
720 tk->payload[tk->i_payload_count].psz_rtpmap = NULL;
721 tk->payload[tk->i_payload_count].psz_fmtp = NULL;
723 for( i = 0; i < p_media->i_attribute; i++ )
725 if( !strcmp( p_media->attribute[i].psz_name, "rtpmap" ) &&
726 p_media->attribute[i].psz_value )
728 char *p = p_media->attribute[i].psz_value;
729 int i_type = strtol( p, &p, 0 );
731 if( i_type == tk->payload[tk->i_payload_count].i_type )
733 tk->payload[tk->i_payload_count].psz_rtpmap = strdup( p );
736 else if( !strcmp( p_media->attribute[i].psz_name, "fmtp" ) &&
737 p_media->attribute[i].psz_value )
739 char *p = p_media->attribute[i].psz_value;
740 int i_type = strtol( p, &p, 0 );
742 if( i_type == tk->payload[tk->i_payload_count].i_type )
744 tk->payload[tk->i_payload_count].psz_fmtp = strdup( p );
749 tk->i_payload_count++;
750 if( *p == '\0' || tk->i_payload_count >= RTP_PAYLOAD_MAX )
757 psz = sdp_AttributeValue( p_media->i_attribute, p_media->attribute, "control" );
758 if( !psz || *psz == '\0' )
760 psz = sdp_AttributeValue( p_session->i_attribute, p_session->attribute,
766 if( strstr( psz, "://" ) || psz_url_base == NULL )
768 tk->psz_control = strdup( psz );
772 tk->psz_control = malloc( strlen( psz_url_base ) + strlen( psz ) + 1 );
773 strcpy( tk->psz_control, psz_url_base );
774 strcat( tk->psz_control, psz );
779 tk->psz_control = NULL;
783 psz = p_media->psz_connection;
786 psz = p_session->psz_connection;
790 tk->i_address_count = 0;
791 tk->i_address_ttl = 0;
792 tk->psz_address = NULL;
794 if( ( p = strstr( psz, "IP4" ) ) == NULL )
796 p = strstr( psz, "IP6" ); /* FIXME No idea if it exists ... */
806 tk->psz_address = p = strdup( p );
807 p = strchr( p, '/' );
812 tk->i_address_ttl= strtol( p, &p, 0 );
815 tk->i_address_count = strtol( p, &p, 0 );
822 tk->i_address_count = 0;
823 tk->i_address_ttl = 0;
824 tk->psz_address = NULL;
831 static void sdp_TrackRelease( sdp_track_t *tk )
835 FREE( tk->psz_control );
836 FREE( tk->psz_address );
837 FREE( tk->psz_transport );
838 for( i = 0; i < tk->i_payload_count; i++ )
840 FREE( tk->payload[i].psz_rtpmap );
841 FREE( tk->payload[i].psz_fmtp );