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 informations */
153 char *psz_sessionname;
154 char *psz_connection;
158 media_descr_t **pp_media;
160 attr_descr_t **pp_attributes;
163 /* All this informations are 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);
209 if (result != Z_OK) {
210 printf("inflateInit() failed. Result: %d\n", result);
214 d_stream.next_in = (Bytef *)src;
215 d_stream.avail_in = slen;
220 dst = (unsigned char *)realloc(dst, n * 1000);
221 d_stream.next_out = (Bytef *)&dst[(n - 1) * 1000];
222 d_stream.avail_out = 1000;
223 result = inflate(&d_stream, Z_NO_FLUSH);
224 if ((result != Z_OK) && (result != Z_STREAM_END)) {
225 printf("Zlib decompression failed. Result: %d\n", result);
228 } while ((d_stream.avail_out == 0) && (d_stream.avail_in != 0) &&
229 (result != Z_STREAM_END));
231 dstsize = d_stream.total_out;
232 inflateEnd(&d_stream);
234 *_dst = (unsigned char *)realloc(dst, dstsize);
240 /*****************************************************************************
241 * Open: initialize and create stuff
242 *****************************************************************************/
243 static int Open( vlc_object_t *p_this )
245 intf_thread_t *p_intf = (intf_thread_t*)p_this;
246 intf_sys_t *p_sys = malloc( sizeof( intf_sys_t ) );
248 playlist_t *p_playlist;
250 p_sys->i_timeout = config_GetInt(p_intf,"sap-timeout");
253 if( config_GetInt( p_intf, "sap-ipv4" ) )
255 char *psz_address = config_GetPsz( p_intf, "sap-addr" );
256 network_socket_t sock;
258 if( psz_address == NULL || *psz_address == '\0' )
260 psz_address = strdup( HELLO_GROUP );
263 /* Prepare the network_socket_t structure */
264 sock.i_type = NETWORK_UDP;
265 sock.psz_bind_addr = psz_address;
266 sock.i_bind_port = HELLO_PORT;
267 sock.psz_server_addr = "";
268 sock.i_server_port = 0;
270 p_intf->p_private = (void*) &sock;
272 p_network = module_Need( p_intf, "network", "ipv4", VLC_TRUE );
275 p_sys->fd[0] = sock.i_handle;
276 module_Unneed( p_intf, p_network );
280 msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT );
285 if( config_GetInt( p_intf, "sap-ipv6" ) )
287 char psz_address[100];
288 char *psz_scope = config_GetPsz( p_intf, "sap-ipv6-scope" );
289 network_socket_t sock;
292 if( psz_scope == NULL || *psz_scope == '\0' )
294 psz_scope = strdup( "8" );
296 snprintf( psz_address, 100, "[%s%c%s]",IPV6_ADDR_1,
297 psz_scope[0], IPV6_ADDR_2 );
300 sock.i_type = NETWORK_UDP;
301 sock.psz_bind_addr = psz_address;
302 sock.i_bind_port = HELLO_PORT;
303 sock.psz_server_addr = "";
304 sock.i_server_port = 0;
306 p_intf->p_private = (void*) &sock;
308 p_network = module_Need( p_intf, "network", "ipv6", VLC_TRUE );
311 p_sys->fd[1] = sock.i_handle;
312 module_Unneed( p_intf, p_network );
316 msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT );
319 if( p_sys->fd[0] <= 0 && p_sys->fd[1] <= 0 )
321 msg_Warn( p_intf, "IPV4 and IPV6 failed" );
326 /* Create our playlist group */
327 p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
331 playlist_group_t *p_group = playlist_CreateGroup( p_playlist , "SAP" );
332 p_sys->i_group = p_group->i_id;
333 vlc_object_release( p_playlist );
336 p_sys->i_announces = 0;
337 p_sys->pp_announces = NULL;
339 p_intf->pf_run = Run;
340 p_intf->p_sys = p_sys;
345 /*****************************************************************************
347 *****************************************************************************/
348 static void Close( vlc_object_t *p_this )
350 intf_thread_t *p_intf = (intf_thread_t*)p_this;
351 intf_sys_t *p_sys = p_intf->p_sys;
354 if( p_sys->fd[0] > 0 )
356 close( p_sys->fd[0] );
358 if( p_sys->fd[1] > 0 )
360 close( p_sys->fd[1] );
363 for( i = 0 ; i< p_sys->i_announces ; i++ )
365 if( p_sys->pp_announces[i]->psz_name )
366 free( p_sys->pp_announces[i]->psz_name );
367 if( p_sys->pp_announces[i]->psz_uri )
368 free( p_sys->pp_announces[i]->psz_uri );
369 free( p_sys->pp_announces[i] );
371 free( p_sys->pp_announces );
376 /*****************************************************************************
378 *****************************************************************************
379 * Listens to SAP packets, and sends them to packet_handle
380 *****************************************************************************/
381 #define MAX_SAP_BUFFER 5000
383 static void Run( intf_thread_t *p_intf )
385 intf_sys_t *p_sys = p_intf->p_sys;
386 uint8_t buffer[MAX_SAP_BUFFER + 1];
389 /* read SAP packets */
390 while( !p_intf->b_die )
392 playlist_t *p_playlist= NULL;
394 int i_read = NetRead( p_intf, p_sys->fd, buffer, MAX_SAP_BUFFER );
402 unsigned char *p_decompressed_buffer;
403 int i_decompressed_size;
405 /* Check for items that need deletion */
406 for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
408 struct sap_announce_t *p_announce;
409 mtime_t i_timeout = (mtime_t)1000000*p_sys->i_timeout;
410 if( mdate() - p_intf->p_sys->pp_announces[i]->i_last > i_timeout )
412 msg_Dbg(p_intf,"Time out for %s, deleting (%i/%i)",
413 p_intf->p_sys->pp_announces[i]->psz_name,
414 i , p_intf->p_sys->i_announces );
416 /* Remove the playlist item */
417 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
421 int i_pos = playlist_GetPositionById( p_playlist,
422 p_intf->p_sys->pp_announces[i]->i_id );
423 playlist_Delete( p_playlist, i_pos );
426 vlc_object_release( p_playlist );
428 /* Free the p_announce */
429 p_announce = p_intf->p_sys->pp_announces[i];
430 if( p_announce->psz_name )
431 free( p_announce->psz_name );
432 if( p_announce->psz_uri )
433 free( p_announce->psz_uri );
435 /* Remove the sap_announce from the array */
436 REMOVE_ELEM( p_intf->p_sys->pp_announces,
437 p_intf->p_sys->i_announces,
445 /* Minimum length is > 6 */
450 msg_Warn( p_intf, "Cannot read in the socket" );
455 buffer[i_read] = '\0';
456 p_end = &buffer[i_read];
458 /* Parse the SAP header */
459 i_version = buffer[0] >> 5;
462 msg_Warn( p_intf, "strange sap version %d found", i_version );
464 i_address_type = buffer[0] & 0x10;
465 b_reserved = buffer[0] & 0x08;
466 if( b_reserved != 0 )
468 msg_Warn( p_intf, "reserved bit incorrectly set" );
470 b_message_type = buffer[0] & 0x04;
471 if( b_message_type != 0 )
473 msg_Warn( p_intf, "got session deletion packet" );
475 b_encrypted = buffer[0] & 0x02;
478 msg_Warn( p_intf, "encrypted packet" );
480 b_compressed = buffer[0] & 0x01;
482 if( i_address_type == 0 ) /* ipv4 source address */
486 else /* ipv6 source address */
493 i_decompressed_size = do_decompress( p_sdp, &p_decompressed_buffer, i_read - ( p_sdp - buffer ) );
494 if( i_decompressed_size > 0 && i_decompressed_size < MAX_SAP_BUFFER )
496 memcpy( p_sdp, p_decompressed_buffer, i_decompressed_size );
497 p_sdp[i_decompressed_size] = '\0';
498 p_end = &p_sdp[i_decompressed_size];
499 free( p_decompressed_buffer );
502 msg_Warn( p_intf, "Ignoring compressed sap packet" );
505 p_sdp += buffer[1]; /* size of signature */
506 while( p_sdp < p_end - 1 && *p_sdp != '\0' && p_sdp[0] != 'v' && p_sdp[1] != '=' )
517 sess_descr_t *p_sd = parse_sdp( p_intf, p_sdp );
520 sess_toitem ( p_intf, p_sd );
526 msg_Warn( p_intf, "ditching sap packet" );
529 memset( buffer, 0, MAX_SAP_BUFFER );
533 /**********************************************************************
535 *********************************************************************
536 * put into *ppsz_uri, the the uri in the cfield, psz_cfield.
537 *********************************************************************/
539 static void cfield_parse( char *psz_cfield, char **ppsz_uri )
545 psz_pos = psz_cfield;
547 while( *psz_pos != ' ' && *psz_pos !='\0' )
552 while( *psz_pos != ' ' && *psz_pos !='\0' )
558 while( *psz_pos != ' ' && *psz_pos !='/'
559 && *psz_pos != '\0' )
575 /**********************************************************************
577 *********************************************************************
578 * put into *ppsz_proto, and *ppsz_port, the protocol and the port.
579 *********************************************************************/
582 static void mfield_parse( char *psz_mfield, char **ppsz_proto,
589 psz_pos = psz_mfield;
590 psz_media = psz_mfield;
591 while( *psz_pos != '\0' && *psz_pos != ' ' )
595 if( *psz_pos != '\0' )
598 if( strcmp( psz_media, "video" ) && strcmp( psz_media, "audio" ) )
606 *ppsz_port = psz_pos;
607 while( *psz_pos != '\0' && *psz_pos !=' ' && *psz_pos!='/' )
611 if( *psz_pos == '/' ) // FIXME does not support multi-port
615 while( *psz_pos != '\0' && *psz_pos !=' ' )
622 *ppsz_proto = psz_pos;
623 while( *psz_pos!='\0' && *psz_pos !=' ' &&
626 *psz_pos = tolower( *psz_pos );
640 /*******************************************************************
641 * sess_toitem : changes a sess_descr_t into a hurd of
642 * playlist_item_t, which are enqueued.
643 *******************************************************************
644 * Note : does not support sessions that take place on consecutive
645 * port or adresses yet.
646 *******************************************************************/
648 static void sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
650 struct sap_announce_t *p_announce;
651 char *psz_uri, *psz_proto, *psz_item_uri;
653 char *psz_uri_default;
654 int i_count, i, i_id = 0;
655 vlc_bool_t b_http = VLC_FALSE;
656 char *psz_http_path = NULL;
657 playlist_t *p_playlist = NULL;
658 playlist_item_t *p_item;
660 psz_uri_default = NULL;
661 if( p_sd->i_media > 1 || !config_GetInt( p_intf, "sap-parse" ) )
663 asprintf( &psz_uri, "sdp://%s", p_sd->psz_sdp );
664 /* Check if we have already added the item */
665 for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
667 if( !strcmp(p_intf->p_sys->pp_announces[i]->psz_uri,
670 p_intf->p_sys->pp_announces[i]->i_last = mdate();
675 /* Add it to the playlist */
676 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
678 i_id = playlist_Add( p_playlist, psz_uri, p_sd->psz_sessionname ,
679 PLAYLIST_CHECK_INSERT, PLAYLIST_END );
682 playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_id );
683 playlist_ItemSetGroup( p_item, p_intf->p_sys->i_group );
687 p_announce = (struct sap_announce_t *)malloc(
688 sizeof(struct sap_announce_t) );
689 if( p_sd->psz_sessionname )
691 p_announce->psz_name = strdup( p_sd->psz_sessionname );
695 p_announce->psz_name = strdup( "" );
699 p_announce->psz_uri = strdup( psz_uri );
703 p_announce->psz_uri = strdup( "" );
705 p_announce->i_id = i_id;
706 p_announce->i_last = mdate();
708 INSERT_ELEM( p_intf->p_sys->pp_announces,
709 p_intf->p_sys->i_announces,
710 p_intf->p_sys->i_announces,
713 vlc_object_release( p_playlist );
718 cfield_parse( p_sd->psz_connection, &psz_uri_default );
720 for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ )
722 int i_group = p_intf->p_sys->i_group;
724 /* Build what we have to put in psz_item_uri, with the m and
727 if( !p_sd->pp_media[i_count] )
732 mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
733 & psz_proto, & psz_port );
735 if( !psz_proto || !psz_port )
740 if( p_sd->pp_media[i_count]->psz_mediaconnection )
742 cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
747 psz_uri = psz_uri_default;
750 if( psz_uri == NULL )
755 for( i = 0 ; i< p_sd->i_attributes ; i++ )
757 if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "type") &&
758 strstr( p_sd->pp_attributes[i]->psz_value, "http") )
762 if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "http-path"))
764 psz_http_path = strdup( p_sd->pp_attributes[i]->psz_value );
766 if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "plgroup"))
770 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
772 if( p_playlist == NULL )
777 i_group_id = playlist_GroupToId( p_playlist,
778 p_sd->pp_attributes[i]->psz_value);
779 if( i_group_id != 0 )
781 i_group = i_group_id;
785 playlist_group_t *p_group =
786 playlist_CreateGroup( p_playlist,
787 p_sd->pp_attributes[i]->psz_value);
788 i_group = p_group->i_id;
790 vlc_object_release( p_playlist );
794 /* Filling psz_uri */
795 if( b_http == VLC_FALSE )
797 psz_item_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
798 strlen( psz_port ) + 7 );
799 if( ismult( psz_uri ) )
801 sprintf( psz_item_uri, "%s://@%s:%s",
802 psz_proto, psz_uri, psz_port );
806 sprintf( psz_item_uri, "%s://%s:%s",
807 psz_proto, psz_uri, psz_port );
812 if( psz_http_path == NULL )
814 psz_http_path = strdup( "/" );
816 if( *psz_http_path == '/' )
818 asprintf( &psz_item_uri, "%s://%s:%s%s", psz_proto,
819 psz_uri, psz_port,psz_http_path );
823 asprintf( &psz_item_uri, "%s://%s:%s/%s", psz_proto, psz_uri,
824 psz_port, psz_http_path );
829 free( psz_http_path );
833 /* Check if we already know this item */
834 for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
836 if( !strcmp( p_intf->p_sys->pp_announces[i]->psz_uri,
839 p_intf->p_sys->pp_announces[i]->i_last = mdate();
841 /* Check if the name changed */
842 if( strcmp( p_intf->p_sys->pp_announces[i]->psz_name,
843 p_sd->psz_sessionname ) )
845 playlist_item_t *p_item;
846 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
849 msg_Dbg(p_intf, "Name changed (%s -> %s) for %s",
850 p_intf->p_sys->pp_announces[i]->psz_name,
851 p_sd->psz_sessionname,
854 p_item = playlist_ItemGetById( p_playlist,
855 p_intf->p_sys->pp_announces[i]->i_id );
857 /* Change the name in the item */
858 if( p_item->input.psz_name )
859 free( p_item->input.psz_name );
860 p_item->input.psz_name = strdup( p_sd->psz_sessionname);
862 /* Update the stored name */
863 if( p_intf->p_sys->pp_announces[i]->psz_name )
864 free( p_intf->p_sys->pp_announces[i]->psz_name );
865 p_intf->p_sys->pp_announces[i]->psz_name =
866 strdup( p_sd->psz_sessionname );
868 vlc_object_release( p_playlist );
870 free( psz_item_uri );
875 /* Add the item in the playlist */
876 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
878 i_id = playlist_Add ( p_playlist, psz_item_uri ,
879 p_sd->psz_sessionname,
880 PLAYLIST_CHECK_INSERT, PLAYLIST_END );
881 p_item = playlist_ItemGetById( p_playlist, i_id );
884 vlc_mutex_lock( &p_item->input.lock );
885 playlist_ItemSetGroup( p_item, i_group );
886 vlc_mutex_unlock( &p_item->input.lock );
889 /* Then remember it */
890 p_announce = (struct sap_announce_t *)malloc(
891 sizeof(struct sap_announce_t) );
892 if( p_sd->psz_sessionname )
894 p_announce->psz_name = strdup( p_sd->psz_sessionname );
898 p_announce->psz_name = strdup( "" );
902 p_announce->psz_uri = strdup( psz_item_uri );
906 p_announce->psz_uri = strdup( "" );
908 p_announce->i_id = i_id;
910 p_announce->i_last = mdate();
912 vlc_object_release( p_playlist );
914 INSERT_ELEM( p_intf->p_sys->pp_announces,
915 p_intf->p_sys->i_announces,
916 p_intf->p_sys->i_announces,
918 free( psz_item_uri );
922 /***********************************************************************
923 * parse_sdp : SDP parsing
924 * *********************************************************************
925 * Make a sess_descr_t with a psz
926 ***********************************************************************/
928 static sess_descr_t * parse_sdp( intf_thread_t * p_intf, char *p_packet )
932 if( p_packet[0] != 'v' || p_packet[1] != '=' )
934 msg_Warn(p_intf, "bad SDP packet");
938 sd = malloc( sizeof( sess_descr_t ) );
939 sd->psz_sessionname = NULL;
940 sd->psz_connection = NULL;
941 sd->psz_sdp = strdup( p_packet );
945 sd->i_attributes = 0;
946 sd->pp_attributes = NULL;
948 while( *p_packet != '\0' )
952 /* Search begin of field */
953 while( *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' )
957 /* search end of line */
958 if( ( psz_end = strchr( p_packet, '\n' ) ) == NULL )
960 psz_end = p_packet + strlen( p_packet );
962 if( psz_end > p_packet && *(psz_end - 1 ) == '\r' )
967 if( psz_end <= p_packet )
973 if( p_packet[1] != '=' )
975 msg_Warn( p_intf, "invalid packet") ;
980 switch( p_packet[0] )
983 sd->i_version = atoi( &p_packet[2] );
986 sd->psz_sessionname = strdup( &p_packet[2] );
998 char *psz_eof = strchr( &p_packet[2], ':' );
1000 if( psz_eof && psz_eof[1] != '\0' )
1002 attr_descr_t *attr = malloc( sizeof( attr_descr_t ) );
1006 attr->psz_field = strdup( &p_packet[2] );
1007 attr->psz_value = strdup( psz_eof );
1009 TAB_APPEND( sd->i_attributes, sd->pp_attributes, attr );
1016 media_descr_t *media = malloc( sizeof( media_descr_t ) );
1018 media->psz_medianame = strdup( &p_packet[2] );
1019 media->psz_mediaconnection = NULL;
1021 TAB_APPEND( sd->i_media, sd->pp_media, media );
1026 if( sd->i_media <= 0 )
1028 sd->psz_connection = strdup( &p_packet[2] );
1032 sd->pp_media[sd->i_media-1]->psz_mediaconnection = strdup( &p_packet[2] );
1047 if( p ) { free( p ); (p) = NULL; }
1048 static void free_sd( sess_descr_t * p_sd )
1052 FREE( p_sd->psz_sessionname );
1053 FREE( p_sd->psz_connection );
1054 FREE( p_sd->psz_sdp );
1056 for( i = 0; i < p_sd->i_media ; i++ )
1058 FREE( p_sd->pp_media[i]->psz_medianame );
1059 FREE( p_sd->pp_media[i]->psz_mediaconnection );
1060 FREE( p_sd->pp_media[i] );
1062 for( i = 0; i < p_sd->i_attributes ; i++ )
1064 FREE( p_sd->pp_attributes[i]->psz_field );
1065 FREE( p_sd->pp_attributes[i]->psz_value );
1066 FREE( p_sd->pp_attributes[i] );
1068 FREE( p_sd->pp_attributes );
1069 FREE( p_sd->pp_media );
1074 /***********************************************************************
1075 * ismult: returns true if we have a multicast address
1076 ***********************************************************************/
1078 static int ismult( char *psz_uri )
1083 i_value = strtol( psz_uri, &psz_end, 0 );
1086 if( psz_uri[0] == '[')
1088 if( strncasecmp( &psz_uri[1], "FF0" , 3) ||
1089 strncasecmp( &psz_uri[2], "FF0" , 3))
1092 return( VLC_FALSE );
1095 if( *psz_end != '.' ) { return( VLC_FALSE ); }
1097 return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
1102 /*****************************************************************************
1103 * Read: read on a file descriptor, checking b_die periodically
1104 *****************************************************************************
1106 *****************************************************************************/
1107 static ssize_t NetRead( intf_thread_t *p_intf,
1108 int fd[2], uint8_t *p_buffer, int i_len )
1113 struct timeval timeout;
1116 int i_handle_max = __MAX( fd[0], fd[1] );
1118 /* Initialize file descriptor set */
1120 if( fd[0] > 0 ) FD_SET( fd[0], &fds );
1121 if( fd[1] > 0 ) FD_SET( fd[1], &fds );
1123 /* We'll wait 0.5 second if nothing happens */
1125 timeout.tv_usec = 500000;
1127 /* Find if some data is available */
1128 i_ret = select( i_handle_max + 1, &fds, NULL, NULL, &timeout );
1130 if( i_ret == -1 && errno != EINTR )
1132 msg_Err( p_intf, "network select error (%s)", strerror(errno) );
1134 else if( i_ret > 0 )
1136 if( fd[0] > 0 && FD_ISSET( fd[0], &fds ) )
1138 return recv( fd[0], p_buffer, i_len, 0 );
1140 else if( fd[1] > 0 && FD_ISSET( fd[1], &fds ) )
1142 return recv( fd[1], p_buffer, i_len, 0 );