1 /*****************************************************************************
2 * sap.c : SAP interface module
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
7 * Authors: Arnaud Schauly <gitan@via.ecp.fr>
8 * Clément Stenac <zorglub@via.ecp.fr>
9 * Damien Lucas <nitrox@videolan.org>
10 * Laurent Aimar <fenrir@via.ecp.fr>
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.
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.
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 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
30 #include <stdlib.h> /* malloc(), free() */
35 #include <errno.h> /* ENOMEM */
41 #ifdef HAVE_SYS_TIME_H
42 # include <sys/time.h>
46 # include <winsock2.h>
47 # include <ws2tcpip.h>
49 # define IN_MULTICAST(a) IN_CLASSD(a)
52 # include <sys/socket.h>
53 # include <netinet/in.h>
55 # include <arpa/inet.h>
56 # elif defined( SYS_BEOS )
57 # include <net/netdb.h>
62 # define close(a) CloseHandle(a)
63 #elif defined( WIN32 )
64 # define close(a) closesocket(a)
73 #define MAX_LINE_LENGTH 256
75 /* SAP is always on that port */
76 #define HELLO_PORT 9875
77 #define HELLO_GROUP "224.2.127.254"
80 #define IPV6_ADDR_1 "FF0" /* Scope is inserted between them */
81 #define IPV6_ADDR_2 "::2:7FFE"
84 /*****************************************************************************
86 *****************************************************************************/
87 #define SAP_ADDR_TEXT N_( "SAP multicast address" )
88 #define SAP_ADDR_LONGTEXT N_( "SAP multicast address" )
89 #define SAP_IPV4_TEXT N_( "IPv4-SAP listening" )
90 #define SAP_IPV4_LONGTEXT N_( \
91 "Set this if you want the SAP module to listen to IPv4 announces" )
92 #define SAP_IPV6_TEXT N_( "IPv6-SAP listening" )
93 #define SAP_IPV6_LONGTEXT N_( \
94 "Set this if you want the SAP module to listen to IPv6 announces" )
95 #define SAP_SCOPE_TEXT N_( "IPv6 SAP scope" )
96 #define SAP_SCOPE_LONGTEXT N_( \
97 "Sets the scope for IPv6 announces (default is 8)" )
98 #define SAP_TIMEOUT_TEXT N_( "SAP timeout (seconds)" )
99 #define SAP_TIMEOUT_LONGTEXT N_( \
100 "Sets the time before SAP items get deleted if no new announce " \
102 #define SAP_PARSE_TEXT N_( "Try to parse the SAP" )
103 #define SAP_PARSE_LONGTEXT N_( \
104 "When SAP can it will try to parse the SAP. Normal behavior is " \
105 "to have livedotcom parse the announce." )
107 static int Open ( vlc_object_t * );
108 static void Close( vlc_object_t * );
111 set_description( _("SAP interface") );
113 add_string( "sap-addr", NULL, NULL,
114 SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, VLC_TRUE );
115 add_bool( "sap-ipv4", 1 , NULL,
116 SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, VLC_TRUE );
117 add_bool( "sap-ipv6", 0 , NULL,
118 SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE );
119 add_string( "sap-ipv6-scope", "8" , NULL,
120 SAP_SCOPE_TEXT, SAP_SCOPE_LONGTEXT, VLC_TRUE);
121 add_integer( "sap-timeout", 1800, NULL,
122 SAP_TIMEOUT_TEXT, SAP_TIMEOUT_LONGTEXT, VLC_TRUE );
123 add_bool( "sap-parse", 1 , NULL,
124 SAP_PARSE_TEXT,SAP_PARSE_LONGTEXT, VLC_TRUE );
126 set_capability( "interface", 0 );
127 set_callbacks( Open, Close );
130 /*****************************************************************************
132 *****************************************************************************/
134 static void Run ( intf_thread_t *p_intf );
135 static ssize_t NetRead( intf_thread_t *, int fd[2], uint8_t *, int );
137 typedef struct media_descr_t media_descr_t;
138 typedef struct sess_descr_t sess_descr_t;
139 typedef struct attr_descr_t attr_descr_t;
141 static void sess_toitem( intf_thread_t *, sess_descr_t * );
143 static sess_descr_t * parse_sdp( intf_thread_t *, char * ) ;
144 static void free_sd( sess_descr_t * );
146 /* Detect multicast addresses */
147 static int ismult( char * );
149 /* The struct that contains sdp information */
153 char *psz_sessionname;
154 char *psz_connection;
158 media_descr_t **pp_media;
160 attr_descr_t **pp_attributes;
163 /* All this information is not useful yet. */
167 char *psz_mediaconnection;
176 struct sap_announce_t
192 /* Table of announces */
194 struct sap_announce_t **pp_announces;
200 int do_decompress( unsigned char *src, unsigned char **_dst, int slen ) {
201 int result, dstsize, n;
205 d_stream.zalloc = (alloc_func)0;
206 d_stream.zfree = (free_func)0;
207 d_stream.opaque = (voidpf)0;
208 result = inflateInit(&d_stream);
211 printf( "inflateInit() failed. Result: %d\n", result );
215 d_stream.next_in = (Bytef *)src;
216 d_stream.avail_in = slen;
222 dst = (unsigned char *)realloc(dst, n * 1000);
223 d_stream.next_out = (Bytef *)&dst[(n - 1) * 1000];
224 d_stream.avail_out = 1000;
225 result = inflate(&d_stream, Z_NO_FLUSH);
226 if( ( result != Z_OK ) && ( result != Z_STREAM_END ) )
228 printf( "Zlib decompression failed. Result: %d\n", result );
232 while( ( d_stream.avail_out == 0 ) && ( d_stream.avail_in != 0 ) &&
233 ( result != Z_STREAM_END ) );
235 dstsize = d_stream.total_out;
236 inflateEnd( &d_stream );
238 *_dst = (unsigned char *)realloc( dst, dstsize );
244 /*****************************************************************************
245 * Open: initialize and create stuff
246 *****************************************************************************/
247 static int Open( vlc_object_t *p_this )
249 intf_thread_t *p_intf = ( intf_thread_t* )p_this;
250 intf_sys_t *p_sys = malloc( sizeof( intf_sys_t ) );
252 playlist_t *p_playlist;
254 p_sys->i_timeout = config_GetInt( p_intf,"sap-timeout" );
257 if( config_GetInt( p_intf, "sap-ipv4" ) )
259 char *psz_address = config_GetPsz( p_intf, "sap-addr" );
260 network_socket_t sock;
262 if( psz_address == NULL || *psz_address == '\0' )
264 psz_address = strdup( HELLO_GROUP );
267 /* Prepare the network_socket_t structure */
268 sock.i_type = NETWORK_UDP;
269 sock.psz_bind_addr = psz_address;
270 sock.i_bind_port = HELLO_PORT;
271 sock.psz_server_addr = "";
272 sock.i_server_port = 0;
274 p_intf->p_private = ( void* ) &sock;
276 p_network = module_Need( p_intf, "network", "ipv4", VLC_TRUE );
279 p_sys->fd[0] = sock.i_handle;
280 module_Unneed( p_intf, p_network );
284 msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT );
289 if( config_GetInt( p_intf, "sap-ipv6" ) )
291 char psz_address[100];
292 char *psz_scope = config_GetPsz( p_intf, "sap-ipv6-scope" );
293 network_socket_t sock;
296 if( psz_scope == NULL || *psz_scope == '\0' )
298 psz_scope = strdup( "8" );
300 snprintf( psz_address, 100, "[%s%c%s]",IPV6_ADDR_1,
301 psz_scope[0], IPV6_ADDR_2 );
304 sock.i_type = NETWORK_UDP;
305 sock.psz_bind_addr = psz_address;
306 sock.i_bind_port = HELLO_PORT;
307 sock.psz_server_addr = "";
308 sock.i_server_port = 0;
310 p_intf->p_private = (void*) &sock;
312 p_network = module_Need( p_intf, "network", "ipv6", VLC_TRUE );
315 p_sys->fd[1] = sock.i_handle;
316 module_Unneed( p_intf, p_network );
320 msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT );
323 if( p_sys->fd[0] <= 0 && p_sys->fd[1] <= 0 )
325 msg_Warn( p_intf, "IPV4 and IPV6 failed" );
330 /* Create our playlist group */
331 p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
335 playlist_group_t *p_group = playlist_CreateGroup( p_playlist , "SAP" );
336 p_sys->i_group = p_group->i_id;
337 vlc_object_release( p_playlist );
340 p_sys->i_announces = 0;
341 p_sys->pp_announces = NULL;
343 p_intf->pf_run = Run;
344 p_intf->p_sys = p_sys;
349 /*****************************************************************************
351 *****************************************************************************/
352 static void Close( vlc_object_t *p_this )
354 intf_thread_t *p_intf = ( intf_thread_t* )p_this;
355 intf_sys_t *p_sys = p_intf->p_sys;
358 if( p_sys->fd[0] > 0 )
360 close( p_sys->fd[0] );
362 if( p_sys->fd[1] > 0 )
364 close( p_sys->fd[1] );
367 for( i = 0 ; i< p_sys->i_announces ; i++ )
369 if( p_sys->pp_announces[i]->psz_name )
370 free( p_sys->pp_announces[i]->psz_name );
371 if( p_sys->pp_announces[i]->psz_uri )
372 free( p_sys->pp_announces[i]->psz_uri );
373 free( p_sys->pp_announces[i] );
375 free( p_sys->pp_announces );
380 /*****************************************************************************
382 *****************************************************************************
383 * Listens to SAP packets, and sends them to packet_handle
384 *****************************************************************************/
385 #define MAX_SAP_BUFFER 5000
387 static void Run( intf_thread_t *p_intf )
389 intf_sys_t *p_sys = p_intf->p_sys;
390 uint8_t buffer[MAX_SAP_BUFFER + 1];
393 /* Dirty hack to slow down the startup of the sap interface */
396 /* read SAP packets */
397 while( !p_intf->b_die )
399 playlist_t *p_playlist= NULL;
401 int i_read = NetRead( p_intf, p_sys->fd, buffer, MAX_SAP_BUFFER );
409 unsigned char *p_decompressed_buffer;
410 int i_decompressed_size;
412 /* Check for items that need deletion */
413 for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
415 struct sap_announce_t *p_announce;
416 mtime_t i_timeout = ( mtime_t ) 1000000*p_sys->i_timeout;
417 if( mdate() - p_intf->p_sys->pp_announces[i]->i_last > i_timeout )
419 msg_Dbg( p_intf,"Time out for %s, deleting (%i/%i)",
420 p_intf->p_sys->pp_announces[i]->psz_name,
421 i , p_intf->p_sys->i_announces );
423 /* Remove the playlist item */
424 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
428 int i_pos = playlist_GetPositionById( p_playlist,
429 p_intf->p_sys->pp_announces[i]->i_id );
430 playlist_Delete( p_playlist, i_pos );
433 vlc_object_release( p_playlist );
435 /* Free the p_announce */
436 p_announce = p_intf->p_sys->pp_announces[i];
437 if( p_announce->psz_name )
438 free( p_announce->psz_name );
439 if( p_announce->psz_uri )
440 free( p_announce->psz_uri );
442 /* Remove the sap_announce from the array */
443 REMOVE_ELEM( p_intf->p_sys->pp_announces,
444 p_intf->p_sys->i_announces,
452 /* Minimum length is > 6 */
457 msg_Warn( p_intf, "Cannot read in the socket" );
462 buffer[i_read] = '\0';
463 p_end = &buffer[i_read];
465 /* Parse the SAP header */
466 i_version = buffer[0] >> 5;
469 msg_Dbg( p_intf, "strange sap version %d found", i_version );
471 i_address_type = buffer[0] & 0x10;
472 b_reserved = buffer[0] & 0x08;
473 if( b_reserved != 0 )
475 msg_Dbg( p_intf, "reserved bit incorrectly set" );
477 b_message_type = buffer[0] & 0x04;
478 if( b_message_type != 0 )
480 msg_Dbg( p_intf, "got session deletion packet" );
482 b_encrypted = buffer[0] & 0x02;
485 msg_Dbg( p_intf, "encrypted packet" );
487 b_compressed = buffer[0] & 0x01;
489 if( i_address_type == 0 ) /* ipv4 source address */
493 else /* ipv6 source address */
500 i_decompressed_size = do_decompress( p_sdp, &p_decompressed_buffer,
501 i_read - ( p_sdp - buffer ) );
502 if( i_decompressed_size > 0 && i_decompressed_size < MAX_SAP_BUFFER )
504 memcpy( p_sdp, p_decompressed_buffer, i_decompressed_size );
505 p_sdp[i_decompressed_size] = '\0';
506 p_end = &p_sdp[i_decompressed_size];
507 free( p_decompressed_buffer );
510 msg_Warn( p_intf, "Ignoring compressed sap packet" );
513 p_sdp += buffer[1]; /* size of signature */
514 while( p_sdp < p_end - 1 && *p_sdp != '\0' && p_sdp[0] != 'v' && p_sdp[1] != '=' )
525 sess_descr_t *p_sd = parse_sdp( p_intf, p_sdp );
528 sess_toitem ( p_intf, p_sd );
534 msg_Warn( p_intf, "ditching sap packet" );
537 memset( buffer, 0, MAX_SAP_BUFFER );
541 /**********************************************************************
543 *********************************************************************
544 * put into *ppsz_uri, the the uri in the cfield, psz_cfield.
545 *********************************************************************/
547 static void cfield_parse( char *psz_cfield, char **ppsz_uri )
552 psz_pos = psz_cfield;
554 while( *psz_pos != ' ' && *psz_pos !='\0' )
559 while( *psz_pos != ' ' && *psz_pos !='\0' )
565 while( *psz_pos != ' ' && *psz_pos !='/'
566 && *psz_pos != '\0' )
581 /**********************************************************************
583 *********************************************************************
584 * put into *ppsz_proto, and *ppsz_port, the protocol and the port.
585 *********************************************************************/
588 static void mfield_parse( char *psz_mfield, char **ppsz_proto,
595 psz_pos = psz_mfield;
596 psz_media = psz_mfield;
597 while( *psz_pos != '\0' && *psz_pos != ' ' )
601 if( *psz_pos != '\0' )
604 if( strcmp( psz_media, "video" ) && strcmp( psz_media, "audio" ) )
612 *ppsz_port = psz_pos;
613 while( *psz_pos != '\0' && *psz_pos !=' ' && *psz_pos!='/' )
617 if( *psz_pos == '/' ) // FIXME does not support multi-port
621 while( *psz_pos != '\0' && *psz_pos !=' ' )
628 *ppsz_proto = psz_pos;
629 while( *psz_pos!='\0' && *psz_pos !=' ' &&
632 *psz_pos = tolower( *psz_pos );
646 /*******************************************************************
647 * sess_toitem : changes a sess_descr_t into a hurd of
648 * playlist_item_t, which are enqueued.
649 *******************************************************************
650 * Note : does not support sessions that take place on consecutive
651 * port or adresses yet.
652 *******************************************************************/
654 static void sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
656 struct sap_announce_t *p_announce;
657 char *psz_uri, *psz_proto, *psz_item_uri;
659 char *psz_uri_default;
660 int i_count, i, i_id = 0;
661 vlc_bool_t b_http = VLC_FALSE;
662 char *psz_http_path = NULL;
663 playlist_t *p_playlist = NULL;
664 playlist_item_t *p_item;
666 psz_uri_default = NULL;
667 if( p_sd->i_media > 1 || !config_GetInt( p_intf, "sap-parse" ) )
669 asprintf( &psz_uri, "sdp://%s", p_sd->psz_sdp );
670 /* Check if we have already added the item */
671 for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
673 if( !strcmp( p_intf->p_sys->pp_announces[i]->psz_uri,
676 p_intf->p_sys->pp_announces[i]->i_last = mdate();
681 /* Add it to the playlist */
682 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
684 i_id = playlist_Add( p_playlist, psz_uri, p_sd->psz_sessionname ,
685 PLAYLIST_CHECK_INSERT, PLAYLIST_END );
688 playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_id );
689 playlist_ItemSetGroup( p_item, p_intf->p_sys->i_group );
693 p_announce = ( struct sap_announce_t * )malloc(
694 sizeof( struct sap_announce_t ) );
695 if( p_sd->psz_sessionname )
697 p_announce->psz_name = strdup( p_sd->psz_sessionname );
701 p_announce->psz_name = strdup( "" );
705 p_announce->psz_uri = strdup( psz_uri );
709 p_announce->psz_uri = strdup( "" );
711 p_announce->i_id = i_id;
712 p_announce->i_last = mdate();
714 INSERT_ELEM( p_intf->p_sys->pp_announces,
715 p_intf->p_sys->i_announces,
716 p_intf->p_sys->i_announces,
719 vlc_object_release( p_playlist );
724 cfield_parse( p_sd->psz_connection, &psz_uri_default );
726 for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ )
728 int i_group = p_intf->p_sys->i_group;
729 int i_packetsize = config_GetInt( p_intf, "mtu" );
731 /* Build what we have to put in psz_item_uri, with the m and
734 if( !p_sd->pp_media[i_count] )
739 mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
740 & psz_proto, & psz_port );
742 if( !psz_proto || !psz_port )
747 if( p_sd->pp_media[i_count]->psz_mediaconnection )
749 cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
754 psz_uri = psz_uri_default;
757 if( psz_uri == NULL )
762 for( i = 0 ; i< p_sd->i_attributes ; i++ )
764 if( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "type" ) &&
765 strstr( p_sd->pp_attributes[i]->psz_value, "http" ) )
769 if( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "http-path" ) )
771 psz_http_path = strdup( p_sd->pp_attributes[i]->psz_value );
773 if( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "plgroup" ) ||
774 !strcasecmp( p_sd->pp_attributes[i]->psz_field , "x-plgroup" ) )
778 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
780 if( p_playlist == NULL )
785 i_group_id = playlist_GroupToId( p_playlist,
786 p_sd->pp_attributes[i]->psz_value );
787 if( i_group_id != 0 )
789 i_group = i_group_id;
793 playlist_group_t *p_group =
794 playlist_CreateGroup( p_playlist,
795 p_sd->pp_attributes[i]->psz_value );
796 i_group = p_group->i_id;
798 vlc_object_release( p_playlist );
800 if( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "packetsize" ) )
802 i_packetsize = strtol( p_sd->pp_attributes[i]->psz_value, NULL, 10 );
806 /* Filling psz_uri */
807 if( b_http == VLC_FALSE )
809 if( ismult( psz_uri ) )
811 asprintf( &psz_item_uri, "%s://@%s:%s",
812 psz_proto, psz_uri, psz_port );
816 asprintf( &psz_item_uri, "%s://%s:%s",
817 psz_proto, psz_uri, psz_port );
822 if( psz_http_path == NULL )
824 psz_http_path = strdup( "/" );
826 if( *psz_http_path == '/' )
828 asprintf( &psz_item_uri, "%s://%s:%s%s", psz_proto,
829 psz_uri, psz_port,psz_http_path );
833 asprintf( &psz_item_uri, "%s://%s:%s/%s", psz_proto, psz_uri,
834 psz_port, psz_http_path );
839 free( psz_http_path );
843 /* Check if we already know this item */
844 for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
846 if( !strcmp( p_intf->p_sys->pp_announces[i]->psz_uri,
849 p_intf->p_sys->pp_announces[i]->i_last = mdate();
851 /* Check if the name changed */
852 if( strcmp( p_intf->p_sys->pp_announces[i]->psz_name,
853 p_sd->psz_sessionname ) )
855 playlist_item_t *p_item;
856 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
859 msg_Dbg(p_intf, "Name changed (%s -> %s) for %s",
860 p_intf->p_sys->pp_announces[i]->psz_name,
861 p_sd->psz_sessionname,
864 p_item = playlist_ItemGetById( p_playlist,
865 p_intf->p_sys->pp_announces[i]->i_id );
867 /* Change the name in the item */
870 vlc_mutex_lock( &p_item->input.lock );
871 if( p_item->input.psz_name )
872 free( p_item->input.psz_name );
873 p_item->input.psz_name = strdup( p_sd->psz_sessionname );
874 vlc_mutex_unlock( &p_item->input.lock );
877 /* Update the stored name */
878 if( p_intf->p_sys->pp_announces[i]->psz_name )
879 free( p_intf->p_sys->pp_announces[i]->psz_name );
880 p_intf->p_sys->pp_announces[i]->psz_name =
881 strdup( p_sd->psz_sessionname );
883 vlc_object_release( p_playlist );
885 free( psz_item_uri );
890 /* Add the item in the playlist */
891 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
894 p_item = playlist_ItemNew( p_intf, psz_item_uri, p_sd->psz_sessionname );
898 playlist_ItemSetGroup( p_item, i_group );
899 if( i_packetsize > config_GetInt( p_intf, "mtu" ) )
901 char *psz_packetsize_option;
902 asprintf( &psz_packetsize_option, "mtu=%i", i_packetsize );
903 playlist_ItemAddOption( p_item, psz_packetsize_option );
904 free( psz_packetsize_option );
906 playlist_AddItem( p_playlist , p_item , PLAYLIST_CHECK_INSERT, PLAYLIST_END );
909 /* Then remember it */
910 p_announce = (struct sap_announce_t *)malloc(
911 sizeof( struct sap_announce_t ) );
912 if( p_sd->psz_sessionname )
914 p_announce->psz_name = strdup( p_sd->psz_sessionname );
918 p_announce->psz_name = strdup( "" );
922 p_announce->psz_uri = strdup( psz_item_uri );
926 p_announce->psz_uri = strdup( "" );
928 p_announce->i_id = i_id;
930 p_announce->i_last = mdate();
932 vlc_object_release( p_playlist );
934 INSERT_ELEM( p_intf->p_sys->pp_announces,
935 p_intf->p_sys->i_announces,
936 p_intf->p_sys->i_announces,
938 free( psz_item_uri );
942 /***********************************************************************
943 * parse_sdp : SDP parsing
944 * *********************************************************************
945 * Make a sess_descr_t with a psz
946 ***********************************************************************/
948 static sess_descr_t * parse_sdp( intf_thread_t * p_intf, char *p_packet )
952 if( p_packet[0] != 'v' || p_packet[1] != '=' )
954 msg_Warn( p_intf, "bad SDP packet" );
958 sd = malloc( sizeof( sess_descr_t ) );
959 sd->psz_sessionname = NULL;
960 sd->psz_connection = NULL;
961 sd->psz_sdp = strdup( p_packet );
965 sd->i_attributes = 0;
966 sd->pp_attributes = NULL;
968 while( *p_packet != '\0' )
972 /* Search begin of field */
973 while( *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' )
977 /* search end of line */
978 if( ( psz_end = strchr( p_packet, '\n' ) ) == NULL )
980 psz_end = p_packet + strlen( p_packet );
982 if( psz_end > p_packet && *( psz_end - 1 ) == '\r' )
987 if( psz_end <= p_packet )
993 if( p_packet[1] != '=' )
995 msg_Warn( p_intf, "invalid packet" ) ;
1000 switch( p_packet[0] )
1003 sd->i_version = atoi( &p_packet[2] );
1006 sd->psz_sessionname = strdup( &p_packet[2] );
1018 char *psz_eof = strchr( &p_packet[2], ':' );
1020 if( psz_eof && psz_eof[1] != '\0' )
1022 attr_descr_t *attr = malloc( sizeof( attr_descr_t ) );
1026 attr->psz_field = strdup( &p_packet[2] );
1027 attr->psz_value = strdup( psz_eof );
1029 TAB_APPEND( sd->i_attributes, sd->pp_attributes, attr );
1036 media_descr_t *media = malloc( sizeof( media_descr_t ) );
1038 media->psz_medianame = strdup( &p_packet[2] );
1039 media->psz_mediaconnection = NULL;
1041 TAB_APPEND( sd->i_media, sd->pp_media, media );
1046 if( sd->i_media <= 0 )
1048 sd->psz_connection = strdup( &p_packet[2] );
1052 sd->pp_media[sd->i_media-1]->psz_mediaconnection = strdup( &p_packet[2] );
1067 if( p ) { free( p ); (p) = NULL; }
1068 static void free_sd( sess_descr_t * p_sd )
1072 FREE( p_sd->psz_sessionname );
1073 FREE( p_sd->psz_connection );
1074 FREE( p_sd->psz_sdp );
1076 for( i = 0; i < p_sd->i_media ; i++ )
1078 FREE( p_sd->pp_media[i]->psz_medianame );
1079 FREE( p_sd->pp_media[i]->psz_mediaconnection );
1080 FREE( p_sd->pp_media[i] );
1082 for( i = 0; i < p_sd->i_attributes ; i++ )
1084 FREE( p_sd->pp_attributes[i]->psz_field );
1085 FREE( p_sd->pp_attributes[i]->psz_value );
1086 FREE( p_sd->pp_attributes[i] );
1088 FREE( p_sd->pp_attributes );
1089 FREE( p_sd->pp_media );
1094 /***********************************************************************
1095 * ismult: returns true if we have a multicast address
1096 ***********************************************************************/
1098 static int ismult( char *psz_uri )
1103 i_value = strtol( psz_uri, &psz_end, 0 );
1106 if( psz_uri[0] == '[')
1108 if( strncasecmp( &psz_uri[1], "FF0" , 3) ||
1109 strncasecmp( &psz_uri[2], "FF0" , 3))
1112 return( VLC_FALSE );
1115 if( *psz_end != '.' ) { return( VLC_FALSE ); }
1117 return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
1120 /*****************************************************************************
1121 * NetRead: read on a file descriptor, checking b_die periodically
1122 *****************************************************************************
1124 * Code duplication because of select(). We need a net_Select() but that's
1126 *****************************************************************************/
1127 static ssize_t NetRead( intf_thread_t *p_intf,
1128 int fd[2], uint8_t *p_buffer, int i_len )
1133 struct timeval timeout;
1136 int i_handle_max = __MAX( fd[0], fd[1] );
1138 /* Initialize file descriptor set */
1141 if( fd[0] > 0 ) FD_SET( fd[0], &fds );
1142 if( fd[1] > 0 ) FD_SET( fd[1], &fds );
1144 /* We'll wait 0.5 second if nothing happens */
1146 timeout.tv_usec = 500000;
1148 /* Find if some data is available */
1149 i_ret = select( i_handle_max + 1, &fds, NULL, NULL, &timeout );
1151 if( i_ret == -1 && errno != EINTR )
1153 msg_Err( p_intf, "network select error (%s)", strerror(errno) );
1155 else if( i_ret > 0 )
1157 if( fd[0] > 0 && FD_ISSET( fd[0], &fds ) )
1159 return recv( fd[0], p_buffer, i_len, 0 );
1161 else if( fd[1] > 0 && FD_ISSET( fd[1], &fds ) )
1163 return recv( fd[1], p_buffer, i_len, 0 );