1
/*****************************************************************************
2
* sap.c : SAP interface module
3
*****************************************************************************
4
* Copyright (C) 2001 VideoLAN
5
* $Id: sap.c 7689 2004-05-16 18:08:41Z anil $
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
/* Dirty hack to slow down the startup of the sap interface*/
392
/* read SAP packets */
393
while( !p_intf->b_die )
395
playlist_t *p_playlist= NULL;
397
int i_read = NetRead( p_intf, p_sys->fd, buffer, MAX_SAP_BUFFER );
405
unsigned char *p_decompressed_buffer;
406
int i_decompressed_size;
408
/* Check for items that need deletion */
409
for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
411
struct sap_announce_t *p_announce;
412
mtime_t i_timeout = (mtime_t)1000000*p_sys->i_timeout;
413
if( mdate() - p_intf->p_sys->pp_announces[i]->i_last > i_timeout )
415
msg_Dbg(p_intf,"Time out for %s, deleting (%i/%i)",
416
p_intf->p_sys->pp_announces[i]->psz_name,
417
i , p_intf->p_sys->i_announces );
419
/* Remove the playlist item */
420
p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
424
int i_pos = playlist_GetPositionById( p_playlist,
425
p_intf->p_sys->pp_announces[i]->i_id );
426
playlist_Delete( p_playlist, i_pos );
429
vlc_object_release( p_playlist );
431
/* Free the p_announce */
432
p_announce = p_intf->p_sys->pp_announces[i];
433
if( p_announce->psz_name )
434
free( p_announce->psz_name );
435
if( p_announce->psz_uri )
436
free( p_announce->psz_uri );
438
/* Remove the sap_announce from the array */
439
REMOVE_ELEM( p_intf->p_sys->pp_announces,
440
p_intf->p_sys->i_announces,
448
/* Minimum length is > 6 */
453
msg_Warn( p_intf, "Cannot read in the socket" );
458
buffer[i_read] = '\0';
459
p_end = &buffer[i_read];
461
/* Parse the SAP header */
462
i_version = buffer[0] >> 5;
465
msg_Warn( p_intf, "strange sap version %d found", i_version );
467
i_address_type = buffer[0] & 0x10;
468
b_reserved = buffer[0] & 0x08;
469
if( b_reserved != 0 )
471
msg_Warn( p_intf, "reserved bit incorrectly set" );
473
b_message_type = buffer[0] & 0x04;
474
if( b_message_type != 0 )
476
msg_Warn( p_intf, "got session deletion packet" );
478
b_encrypted = buffer[0] & 0x02;
481
msg_Warn( p_intf, "encrypted packet" );
483
b_compressed = buffer[0] & 0x01;
485
if( i_address_type == 0 ) /* ipv4 source address */
489
else /* ipv6 source address */
496
i_decompressed_size = do_decompress( p_sdp, &p_decompressed_buffer, i_read - ( p_sdp - buffer ) );
497
if( i_decompressed_size > 0 && i_decompressed_size < MAX_SAP_BUFFER )
499
memcpy( p_sdp, p_decompressed_buffer, i_decompressed_size );
500
p_sdp[i_decompressed_size] = '\0';
501
p_end = &p_sdp[i_decompressed_size];
502
free( p_decompressed_buffer );
505
msg_Warn( p_intf, "Ignoring compressed sap packet" );
508
p_sdp += buffer[1]; /* size of signature */
509
while( p_sdp < p_end - 1 && *p_sdp != '\0' && p_sdp[0] != 'v' && p_sdp[1] != '=' )
520
sess_descr_t *p_sd = parse_sdp( p_intf, p_sdp );
523
sess_toitem ( p_intf, p_sd );
529
msg_Warn( p_intf, "ditching sap packet" );
532
memset( buffer, 0, MAX_SAP_BUFFER );
536
/**********************************************************************
538
*********************************************************************
539
* put into *ppsz_uri, the the uri in the cfield, psz_cfield.
540
*********************************************************************/
542
static void cfield_parse( char *psz_cfield, char **ppsz_uri )
548
psz_pos = psz_cfield;
550
while( *psz_pos != ' ' && *psz_pos !='\0' )
555
while( *psz_pos != ' ' && *psz_pos !='\0' )
561
while( *psz_pos != ' ' && *psz_pos !='/'
562
&& *psz_pos != '\0' )
578
/**********************************************************************
580
*********************************************************************
581
* put into *ppsz_proto, and *ppsz_port, the protocol and the port.
582
*********************************************************************/
585
static void mfield_parse( char *psz_mfield, char **ppsz_proto,
592
psz_pos = psz_mfield;
593
psz_media = psz_mfield;
594
while( *psz_pos != '\0' && *psz_pos != ' ' )
598
if( *psz_pos != '\0' )
601
if( strcmp( psz_media, "video" ) && strcmp( psz_media, "audio" ) )
609
*ppsz_port = psz_pos;
610
while( *psz_pos != '\0' && *psz_pos !=' ' && *psz_pos!='/' )
614
if( *psz_pos == '/' ) // FIXME does not support multi-port
618
while( *psz_pos != '\0' && *psz_pos !=' ' )
625
*ppsz_proto = psz_pos;
626
while( *psz_pos!='\0' && *psz_pos !=' ' &&
629
*psz_pos = tolower( *psz_pos );
643
/*******************************************************************
644
* sess_toitem : changes a sess_descr_t into a hurd of
645
* playlist_item_t, which are enqueued.
646
*******************************************************************
647
* Note : does not support sessions that take place on consecutive
648
* port or adresses yet.
649
*******************************************************************/
651
static void sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
653
struct sap_announce_t *p_announce;
654
char *psz_uri, *psz_proto, *psz_item_uri;
656
char *psz_uri_default;
657
int i_count, i, i_id = 0;
658
vlc_bool_t b_http = VLC_FALSE;
659
char *psz_http_path = NULL;
660
playlist_t *p_playlist = NULL;
661
playlist_item_t *p_item;
663
psz_uri_default = NULL;
664
if( p_sd->i_media > 1 || !config_GetInt( p_intf, "sap-parse" ) )
666
asprintf( &psz_uri, "sdp://%s", p_sd->psz_sdp );
667
/* Check if we have already added the item */
668
for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
670
if( !strcmp(p_intf->p_sys->pp_announces[i]->psz_uri,
673
p_intf->p_sys->pp_announces[i]->i_last = mdate();
678
/* Add it to the playlist */
679
p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
681
i_id = playlist_Add( p_playlist, psz_uri, p_sd->psz_sessionname ,
682
PLAYLIST_CHECK_INSERT, PLAYLIST_END );
685
playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_id );
686
playlist_ItemSetGroup( p_item, p_intf->p_sys->i_group );
690
p_announce = (struct sap_announce_t *)malloc(
691
sizeof(struct sap_announce_t) );
692
if( p_sd->psz_sessionname )
694
p_announce->psz_name = strdup( p_sd->psz_sessionname );
698
p_announce->psz_name = strdup( "" );
702
p_announce->psz_uri = strdup( psz_uri );
706
p_announce->psz_uri = strdup( "" );
708
p_announce->i_id = i_id;
709
p_announce->i_last = mdate();
711
INSERT_ELEM( p_intf->p_sys->pp_announces,
712
p_intf->p_sys->i_announces,
713
p_intf->p_sys->i_announces,
716
vlc_object_release( p_playlist );
721
cfield_parse( p_sd->psz_connection, &psz_uri_default );
723
for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ )
725
int i_group = p_intf->p_sys->i_group;
727
/* Build what we have to put in psz_item_uri, with the m and
730
if( !p_sd->pp_media[i_count] )
735
mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
736
& psz_proto, & psz_port );
738
if( !psz_proto || !psz_port )
743
if( p_sd->pp_media[i_count]->psz_mediaconnection )
745
cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
750
psz_uri = psz_uri_default;
753
if( psz_uri == NULL )
758
for( i = 0 ; i< p_sd->i_attributes ; i++ )
760
if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "type") &&
761
strstr( p_sd->pp_attributes[i]->psz_value, "http") )
765
if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "http-path"))
767
psz_http_path = strdup( p_sd->pp_attributes[i]->psz_value );
769
if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "plgroup"))
773
(playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
775
if( p_playlist == NULL )
780
i_group_id = playlist_GroupToId( p_playlist,
781
p_sd->pp_attributes[i]->psz_value);
782
if( i_group_id != 0 )
784
i_group = i_group_id;
788
playlist_group_t *p_group =
789
playlist_CreateGroup( p_playlist,
790
p_sd->pp_attributes[i]->psz_value);
791
i_group = p_group->i_id;
793
vlc_object_release( p_playlist );
797
/* Filling psz_uri */
798
if( b_http == VLC_FALSE )
800
psz_item_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
801
strlen( psz_port ) + 7 );
802
if( ismult( psz_uri ) )
804
sprintf( psz_item_uri, "%s://@%s:%s",
805
psz_proto, psz_uri, psz_port );
809
sprintf( psz_item_uri, "%s://%s:%s",
810
psz_proto, psz_uri, psz_port );
815
if( psz_http_path == NULL )
817
psz_http_path = strdup( "/" );
819
if( *psz_http_path == '/' )
821
asprintf( &psz_item_uri, "%s://%s:%s%s", psz_proto,
822
psz_uri, psz_port,psz_http_path );
826
asprintf( &psz_item_uri, "%s://%s:%s/%s", psz_proto, psz_uri,
827
psz_port, psz_http_path );
832
free( psz_http_path );
836
/* Check if we already know this item */
837
for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
839
if( !strcmp( p_intf->p_sys->pp_announces[i]->psz_uri,
842
p_intf->p_sys->pp_announces[i]->i_last = mdate();
844
/* Check if the name changed */
845
if( strcmp( p_intf->p_sys->pp_announces[i]->psz_name,
846
p_sd->psz_sessionname ) )
848
playlist_item_t *p_item;
849
p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
852
msg_Dbg(p_intf, "Name changed (%s -> %s) for %s",
853
p_intf->p_sys->pp_announces[i]->psz_name,
854
p_sd->psz_sessionname,
857
p_item = playlist_ItemGetById( p_playlist,
858
p_intf->p_sys->pp_announces[i]->i_id );
860
/* Change the name in the item */
861
if( p_item->input.psz_name )
862
free( p_item->input.psz_name );
863
p_item->input.psz_name = strdup( p_sd->psz_sessionname);
865
/* Update the stored name */
866
if( p_intf->p_sys->pp_announces[i]->psz_name )
867
free( p_intf->p_sys->pp_announces[i]->psz_name );
868
p_intf->p_sys->pp_announces[i]->psz_name =
869
strdup( p_sd->psz_sessionname );
871
vlc_object_release( p_playlist );
873
free( psz_item_uri );
878
/* Add the item in the playlist */
879
p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
881
i_id = playlist_Add ( p_playlist, psz_item_uri ,
882
p_sd->psz_sessionname,
883
PLAYLIST_CHECK_INSERT, PLAYLIST_END );
884
p_item = playlist_ItemGetById( p_playlist, i_id );
887
vlc_mutex_lock( &p_item->input.lock );
888
playlist_ItemSetGroup( p_item, i_group );
889
vlc_mutex_unlock( &p_item->input.lock );
892
/* Then remember it */
893
p_announce = (struct sap_announce_t *)malloc(
894
sizeof(struct sap_announce_t) );
895
if( p_sd->psz_sessionname )
897
p_announce->psz_name = strdup( p_sd->psz_sessionname );
901
p_announce->psz_name = strdup( "" );
905
p_announce->psz_uri = strdup( psz_item_uri );
909
p_announce->psz_uri = strdup( "" );
911
p_announce->i_id = i_id;
913
p_announce->i_last = mdate();
915
vlc_object_release( p_playlist );
917
INSERT_ELEM( p_intf->p_sys->pp_announces,
918
p_intf->p_sys->i_announces,
919
p_intf->p_sys->i_announces,
921
free( psz_item_uri );
925
/***********************************************************************
926
* parse_sdp : SDP parsing
927
* *********************************************************************
928
* Make a sess_descr_t with a psz
929
***********************************************************************/
931
static sess_descr_t * parse_sdp( intf_thread_t * p_intf, char *p_packet )
935
if( p_packet[0] != 'v' || p_packet[1] != '=' )
937
msg_Warn(p_intf, "bad SDP packet");
941
sd = malloc( sizeof( sess_descr_t ) );
942
sd->psz_sessionname = NULL;
943
sd->psz_connection = NULL;
944
sd->psz_sdp = strdup( p_packet );
948
sd->i_attributes = 0;
949
sd->pp_attributes = NULL;
951
while( *p_packet != '\0' )
955
/* Search begin of field */
956
while( *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' )
960
/* search end of line */
961
if( ( psz_end = strchr( p_packet, '\n' ) ) == NULL )
963
psz_end = p_packet + strlen( p_packet );
965
if( psz_end > p_packet && *(psz_end - 1 ) == '\r' )
970
if( psz_end <= p_packet )
976
if( p_packet[1] != '=' )
978
msg_Warn( p_intf, "invalid packet") ;
983
switch( p_packet[0] )
986
sd->i_version = atoi( &p_packet[2] );
989
sd->psz_sessionname = strdup( &p_packet[2] );
1001
char *psz_eof = strchr( &p_packet[2], ':' );
1003
if( psz_eof && psz_eof[1] != '\0' )
1005
attr_descr_t *attr = malloc( sizeof( attr_descr_t ) );
1009
attr->psz_field = strdup( &p_packet[2] );
1010
attr->psz_value = strdup( psz_eof );
1012
TAB_APPEND( sd->i_attributes, sd->pp_attributes, attr );
1019
media_descr_t *media = malloc( sizeof( media_descr_t ) );
1021
media->psz_medianame = strdup( &p_packet[2] );
1022
media->psz_mediaconnection = NULL;
1024
TAB_APPEND( sd->i_media, sd->pp_media, media );
1029
if( sd->i_media <= 0 )
1031
sd->psz_connection = strdup( &p_packet[2] );
1035
sd->pp_media[sd->i_media-1]->psz_mediaconnection = strdup( &p_packet[2] );
1050
if( p ) { free( p ); (p) = NULL; }
1051
static void free_sd( sess_descr_t * p_sd )
1055
FREE( p_sd->psz_sessionname );
1056
FREE( p_sd->psz_connection );
1057
FREE( p_sd->psz_sdp );
1059
for( i = 0; i < p_sd->i_media ; i++ )
1061
FREE( p_sd->pp_media[i]->psz_medianame );
1062
FREE( p_sd->pp_media[i]->psz_mediaconnection );
1063
FREE( p_sd->pp_media[i] );
1065
for( i = 0; i < p_sd->i_attributes ; i++ )
1067
FREE( p_sd->pp_attributes[i]->psz_field );
1068
FREE( p_sd->pp_attributes[i]->psz_value );
1069
FREE( p_sd->pp_attributes[i] );
1071
FREE( p_sd->pp_attributes );
1072
FREE( p_sd->pp_media );
1077
/***********************************************************************
1078
* ismult: returns true if we have a multicast address
1079
***********************************************************************/
1081
static int ismult( char *psz_uri )
1086
i_value = strtol( psz_uri, &psz_end, 0 );
1089
if( psz_uri[0] == '[')
1091
if( strncasecmp( &psz_uri[1], "FF0" , 3) ||
1092
strncasecmp( &psz_uri[2], "FF0" , 3))
1095
return( VLC_FALSE );
1098
if( *psz_end != '.' ) { return( VLC_FALSE ); }
1100
return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
1105
/*****************************************************************************
1106
* Read: read on a file descriptor, checking b_die periodically
1107
*****************************************************************************
1109
*****************************************************************************/
1110
static ssize_t NetRead( intf_thread_t *p_intf,
1111
int fd[2], uint8_t *p_buffer, int i_len )
1116
struct timeval timeout;
1119
int i_handle_max = __MAX( fd[0], fd[1] );
1121
/* Initialize file descriptor set */
1123
if( fd[0] > 0 ) FD_SET( fd[0], &fds );
1124
if( fd[1] > 0 ) FD_SET( fd[1], &fds );
1126
/* We'll wait 0.5 second if nothing happens */
1128
timeout.tv_usec = 500000;
1130
/* Find if some data is available */
1131
i_ret = select( i_handle_max + 1, &fds, NULL, NULL, &timeout );
1133
if( i_ret == -1 && errno != EINTR )
1135
msg_Err( p_intf, "network select error (%s)", strerror(errno) );
1137
else if( i_ret > 0 )
1139
if( fd[0] > 0 && FD_ISSET( fd[0], &fds ) )
1141
return recv( fd[0], p_buffer, i_len, 0 );
1143
else if( fd[1] > 0 && FD_ISSET( fd[1], &fds ) )
1145
return recv( fd[1], p_buffer, i_len, 0 );