]> git.sesse.net Git - vlc/blobdiff - modules/misc/sap.c
* modules/misc/sap.c: fixed misplaced variable declaration.
[vlc] / modules / misc / sap.c
index e4d0fc38b96ae14caeae4d59918c50a6717aa696..d41f76d7e37b4cce3766f51c05ffe22443189884 100644 (file)
@@ -2,9 +2,12 @@
  * sap.c :  SAP interface module
  *****************************************************************************
  * Copyright (C) 2001 VideoLAN
- * $Id: sap.c,v 1.5 2002/12/10 00:02:29 gitan Exp $
+ * $Id: sap.c,v 1.35 2003/11/12 20:01:01 gbazin Exp $
  *
  * Authors: Arnaud Schauly <gitan@via.ecp.fr>
+ *          ClĂ©ment Stenac <zorglub@via.ecp.fr>
+ *          Damien Lucas <nitrox@videolan.org>
+ *          Laurent Aimar <fenrir@via.ecp.fr>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * Preamble
  *****************************************************************************/
 #include <stdlib.h>                                      /* malloc(), free() */
-#include <string.h>
-
-#include <errno.h>                                                 /* ENOMEM */
-#include <stdio.h>
-#include <ctype.h>
-#include <signal.h>
 
 #include <vlc/vlc.h>
 #include <vlc/intf.h>
-#include <vlc/vout.h>
+
+#include <errno.h>                                                 /* ENOMEM */
+#include <ctype.h>
 
 #ifdef HAVE_UNISTD_H
 #    include <unistd.h>
 #endif
-
 #ifdef HAVE_SYS_TIME_H
 #    include <sys/time.h>
 #endif
-#include <sys/types.h>
-
 
 #ifdef WIN32
 #   include <winsock2.h>
 #   endif
 #endif
 
+#ifdef UNDER_CE
+#   define close(a) CloseHandle(a)
+#elif defined( WIN32 )
+#   define close(a) closesocket(a)
+#endif
+
 #include "network.h"
 
 #define MAX_LINE_LENGTH 256
 #define HELLO_GROUP "224.2.127.254"
 #define ADD_SESSION 1
 
+#define IPV6_ADDR_1 "FF0"  /* Scope is inserted between them */
+#define IPV6_ADDR_2 "::2:7FFE"
+
+
 /*****************************************************************************
- * Local prototypes
+ * Module descriptor
  *****************************************************************************/
+#define SAP_ADDR_TEXT N_("SAP multicast address")
+#define SAP_ADDR_LONGTEXT N_("SAP multicast address")
+#define SAP_IPV4_TEXT N_("IPv4-SAP listening")
+#define SAP_IPV4_LONGTEXT N_("Set this if you want SAP to listen for IPv4 announces")
+#define SAP_IPV6_TEXT N_("IPv6-SAP listening")
+#define SAP_IPV6_LONGTEXT N_("Set this if you want SAP to listen for IPv6 announces")
+#define SAP_SCOPE_TEXT N_("IPv6 SAP scope")
+#define SAP_SCOPE_LONGTEXT N_("Sets the scope for IPv6 announces (default is 8)")
+
+static int  Open ( vlc_object_t * );
+static void Close( vlc_object_t * );
 
-typedef struct media_descr_t media_descr_t;
-typedef struct sess_descr_t sess_descr_t;
+vlc_module_begin();
+    add_category_hint( N_("SAP"), NULL, VLC_TRUE );
+        add_string( "sap-addr", NULL, NULL,
+                     SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, VLC_TRUE );
+
+        add_bool( "sap-ipv4", 1 , NULL,
+                     SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, VLC_TRUE);
 
-static int  Activate     ( vlc_object_t * );
-static void Run          ( intf_thread_t *p_intf );
-static int Kill          ( intf_thread_t * );
+        add_bool( "sap-ipv6", 0 , NULL,
+                   SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE);
 
-static ssize_t NetRead    ( intf_thread_t*, int , byte_t *, size_t );
+        add_string( "sap-ipv6-scope", "8" , NULL,
+                    SAP_SCOPE_TEXT, SAP_SCOPE_LONGTEXT, VLC_TRUE);
 
-/* playlist related functions */
-static int  sess_toitem( intf_thread_t *, sess_descr_t * );
+    set_description( _("SAP interface") );
+    set_capability( "interface", 0 );
+    set_callbacks( Open, Close );
+vlc_module_end();
 
-/* sap/sdp related functions */
-static int parse_sap ( char ** );
-static int packet_handle ( intf_thread_t *, char ** );
-static sess_descr_t *  parse_sdp( char *,intf_thread_t * ) ;
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
 
-/* specific sdp fields parsing */
+static void Run    ( intf_thread_t *p_intf );
+static ssize_t NetRead( intf_thread_t *, int fd[2], uint8_t *, int );
 
-static void cfield_parse( char *, char ** );
-static void mfield_parse( char *psz_mfield, char **ppsz_proto,
-               char **ppsz_port );
+typedef struct media_descr_t media_descr_t;
+typedef struct sess_descr_t sess_descr_t;
+typedef struct attr_descr_t attr_descr_t;
+
+static void sess_toitem( intf_thread_t *, sess_descr_t * );
 
+static sess_descr_t *  parse_sdp( intf_thread_t *, char * ) ;
 static void free_sd( sess_descr_t * );
+
+/* Detect multicast addresses */
 static int  ismult( char * );
 
 /* The struct that contains sdp informations */
-struct  sess_descr_t {
-    char *psz_version;
-    char *psz_origin;
+struct  sess_descr_t
+{
+    int  i_version;
     char *psz_sessionname;
-    char *psz_information;
-    char *psz_uri;
-    char *psz_emails;
-    char *psz_phone;
-    char *psz_time;
-    char *psz_repeat;
-    char *psz_attribute;
     char *psz_connection;
-    int  i_media;
-    media_descr_t ** pp_media;
+
+    int           i_media;
+    media_descr_t **pp_media;
+    int           i_attributes;
+    attr_descr_t  **pp_attributes;
 };
 
 /* All this informations are not useful yet.  */
-struct media_descr_t {
+struct media_descr_t
+{
     char *psz_medianame;
     char *psz_mediaconnection;
 };
 
-/*****************************************************************************
- * Module descriptor
- *****************************************************************************/
-vlc_module_begin();
-    add_category_hint( N_("SAP"), NULL );
-        add_string( "sap-addr", NULL, NULL,
-                     "SAP multicast address", "SAP multicast address" );
-    set_description( _("SAP interface module") );
-    set_capability( "interface", 0 );
-    set_callbacks( Activate, NULL);
-vlc_module_end();
-
-/*****************************************************************************
- * Activate: initialize and create stuff
- *****************************************************************************/
-static int Activate( vlc_object_t *p_this )
+struct attr_descr_t
 {
-    intf_thread_t *p_intf = (intf_thread_t*)p_this;
+    char *psz_field;
+    char *psz_value;
+};
 
-    p_intf->pf_run = Run;
+struct intf_sys_t
+{
+    /* IPV4 and IPV6 */
+    int fd[2];
 
-    return VLC_SUCCESS;
-}
+    /* playlist group */
+    int i_group;
+};
 
 /*****************************************************************************
- * Run: sap thread
- *****************************************************************************
- * Listens to SAP packets, and sends them to packet_handle
+ * Open: initialize and create stuff
  *****************************************************************************/
-
-static void Run( intf_thread_t *p_intf )
+static int Open( vlc_object_t *p_this )
 {
-    char *psz_addr;
-    char *psz_network = NULL;
-    struct sockaddr_in addr;
-    int fd,addrlen;
-    char *psz_buf;
-
-    module_t            *p_network;
-    network_socket_t    socket_desc;
+    intf_thread_t *p_intf = (intf_thread_t*)p_this;
+    intf_sys_t    *p_sys  = malloc( sizeof( intf_sys_t ) );
 
-    psz_buf = NULL;
+    playlist_t          *p_playlist;
 
-    if( !(psz_addr = config_GetPsz( p_intf, "sap-addr" ) ) )
+    p_sys->fd[0] = -1;
+    p_sys->fd[1] = -1;
+    if( config_GetInt( p_intf, "sap-ipv4" ) )
     {
-        psz_addr = strdup( HELLO_GROUP );
-    }
-
-    /* Prepare the network_socket_t structure */
-    socket_desc.i_type = NETWORK_UDP;
-    socket_desc.psz_bind_addr = psz_addr;
-    socket_desc.i_bind_port   = HELLO_PORT;
-    socket_desc.psz_server_addr   = "";
-    socket_desc.i_server_port     = 0;
-    p_intf->p_private = (void*) &socket_desc;
-
-    psz_network = "ipv4"; // FIXME
-
-    /* Create, Bind the socket, ... with the appropriate module  */
+        char *psz_address = config_GetPsz( p_intf, "sap-addr" );
+        network_socket_t    sock;
+        module_t            *p_network;
+        if( psz_address == NULL || *psz_address == '\0' )
+        {
+            psz_address = strdup( HELLO_GROUP );
+        }
 
-    if( !( p_network = module_Need( p_intf, "network", psz_network ) ) )
-    {
-        msg_Err( p_intf, "failed to open a connection (udp)" );
-        return;
+        /* Prepare the network_socket_t structure */
+        sock.i_type            = NETWORK_UDP;
+        sock.psz_bind_addr     = psz_address;
+        sock.i_bind_port       = HELLO_PORT;
+        sock.psz_server_addr   = "";
+        sock.i_server_port     = 0;
+        sock.i_ttl             = 0;
+        p_intf->p_private = (void*) &sock;
+
+        p_network = module_Need( p_intf, "network", "ipv4" );
+        if( p_network )
+        {
+            p_sys->fd[0] = sock.i_handle;
+            module_Unneed( p_intf, p_network );
+        }
+        else
+        {
+            msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT );
+        }
+        free( psz_address );
     }
-    module_Unneed( p_intf, p_network );
-
-    fd = socket_desc.i_handle;
-
-
-    /* read SAP packets */
-
-    psz_buf = malloc( 2000 );
 
-    while( !p_intf->b_die )
+    if( config_GetInt( p_intf, "sap-ipv6" ) )
     {
-        int i_read;
-        addrlen=sizeof(addr);
-
-
-        memset(psz_buf, 0, 2000);
+        char psz_address[100];
+        char *psz_scope = config_GetPsz( p_intf, "sap-ipv6-scope" );
+        network_socket_t    sock;
+        module_t            *p_network;
 
-        i_read = NetRead( p_intf, fd, psz_buf, 2000 );
-
-        if( i_read < 0 )
+        if( psz_scope == NULL || *psz_scope == '\0' )
         {
-            msg_Err( p_intf, "Cannot read in the socket" );
+            psz_scope = strdup( "8" );
         }
-        if( i_read == 0 )
+        snprintf( psz_address, 100, "[%s%c%s]",IPV6_ADDR_1, psz_scope[0], IPV6_ADDR_2 );
+        free( psz_scope );
+
+        sock.i_type            = NETWORK_UDP;
+        sock.psz_bind_addr     = psz_address;
+        sock.i_bind_port       = HELLO_PORT;
+        sock.psz_server_addr   = "";
+        sock.i_server_port     = 0;
+        sock.i_ttl             = 0;
+        p_intf->p_private = (void*) &sock;
+
+        p_network = module_Need( p_intf, "network", "ipv6" );
+        if( p_network )
         {
-            continue;
+            p_sys->fd[1] = sock.i_handle;
+            module_Unneed( p_intf, p_network );
+        }
+        else
+        {
+            msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT );
         }
-
-
-        packet_handle( p_intf,  &psz_buf );
-
     }
-    free( psz_buf );
+    if( p_sys->fd[0] <= 0 && p_sys->fd[1] <= 0 )
+    {
+        msg_Warn( p_intf, "IPV4 and IPV6 failed" );
+        free( p_sys );
+        return VLC_EGENERIC;
+    }
 
-    /* Closing socket */
+    /* Create our playlist group */
+    p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
+                                                FIND_ANYWHERE );
+    if( p_playlist )
+    {
+        playlist_group_t *p_group = playlist_CreateGroup( p_playlist , "SAP" );
+        p_sys->i_group = p_group->i_id;
+        vlc_object_release( p_playlist );
+    }
 
-#ifdef UNDER_CE
-    CloseHandle( socket_desc.i_handle );
-#elif defined( WIN32 )
-    closesocket( socket_desc.i_handle );
-#else
-    close( socket_desc.i_handle );
-#endif
+    p_intf->pf_run = Run;
+    p_intf->p_sys  = p_sys;
 
+    return VLC_SUCCESS;
 }
 
-/********************************************************************
- * Kill
- *******************************************************************
- * Kills the SAP interface.
- ********************************************************************/
-static int Kill( intf_thread_t *p_intf )
+/*****************************************************************************
+ * Close:
+ *****************************************************************************/
+static void Close( vlc_object_t *p_this )
 {
+    intf_thread_t *p_intf = (intf_thread_t*)p_this;
+    intf_sys_t    *p_sys  = p_intf->p_sys;
 
-    p_intf->b_die = VLC_TRUE;
+    if( p_sys->fd[0] > 0 )
+    {
+        close( p_sys->fd[0] );
+    }
+    if( p_sys->fd[1] > 0 )
+    {
+        close( p_sys->fd[1] );
+    }
 
-    return VLC_SUCCESS;
+    free( p_sys );
 }
 
-/*******************************************************************
- * sess_toitem : changes a sess_descr_t into a hurd of
- * playlist_item_t, which are enqueued.
- *******************************************************************
- * Note : does not support sessions that take place on consecutive
- * port or adresses yet.
- *******************************************************************/
+/*****************************************************************************
+ * Run: sap thread
+ *****************************************************************************
+ * Listens to SAP packets, and sends them to packet_handle
+ *****************************************************************************/
+#define MAX_SAP_BUFFER 2000
 
-static int sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
+static void Run( intf_thread_t *p_intf )
 {
-    playlist_item_t * p_item;
-    char *psz_uri, *psz_proto;
-    char *psz_port;
-    char *psz_uri_default;
-    int i_multicast;
-    int i_count;
-    playlist_t *p_playlist;
-
-    psz_uri_default = NULL;
-    cfield_parse( p_sd->psz_connection, &psz_uri_default );
+    intf_sys_t *p_sys  = p_intf->p_sys;
+    uint8_t     buffer[MAX_SAP_BUFFER + 1];
 
-    for( i_count=0 ; i_count <= p_sd->i_media ; i_count ++ )
+    /* read SAP packets */
+    while( !p_intf->b_die )
     {
+        int i_read = NetRead( p_intf, p_sys->fd, buffer, MAX_SAP_BUFFER );
+        uint8_t *p_sdp;
 
-        p_item = malloc( sizeof( playlist_item_t ) );
-        p_item->psz_name = strdup( p_sd->psz_sessionname );
-        p_item->i_type = 0;
-        p_item->i_status = 0;
-        p_item->b_autodeletion = VLC_FALSE;
-        p_item->psz_uri = NULL;
-
-        psz_uri = NULL;
-
-        /* Build what we have to put in p_item->psz_uri, with the m and
-         *  c fields  */
-
-        if( !p_sd->pp_media[i_count] )
-        {
-            return 0;
-        }
-
-        mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
-                        & psz_proto, & psz_port );
-
-        if( !psz_proto || !psz_port )
+        /* Minimum length is > 6 */
+        if( i_read <= 6 )
         {
-            return 0;
-        }
-
-        if( p_sd->pp_media[i_count]->psz_mediaconnection )
-        {
-            cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
-                            & psz_uri );
-        }
-        else
-        {
-            psz_uri = NULL;
-        }
-
-        if( psz_uri == NULL )
-        {
-            if( psz_uri_default )
+            if( i_read < 0 )
             {
-                psz_uri = psz_uri_default;
-            }
-            else
-            {
-                return 0;
+                msg_Warn( p_intf, "Cannot read in the socket" );
             }
+            continue;
         }
 
+        buffer[i_read] = '\0';
 
-        /* Filling p_item->psz_uri */
-        i_multicast = ismult( psz_uri );
+        /* Parse the SAP header */
+        p_sdp  = &buffer[4];
+        p_sdp += (buffer[0]&0x10) ? 16 : 4;
+        p_sdp += buffer[1];
 
-        p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
-                        strlen( psz_port ) + 5 +i_multicast );
-        if( p_item->psz_uri == NULL )
+        while( p_sdp < &buffer[i_read-1] && *p_sdp != '\0' && p_sdp[0] != 'v' && p_sdp[1] != '=' )
         {
-            msg_Err( p_intf, "Not enough memory");
-            return 0;
+            p_sdp++;
         }
-
-        if( i_multicast == 1)
+        if( *p_sdp == '\0' )
         {
-            sprintf( p_item->psz_uri, "%s://@%s:%s", psz_proto,
-                            psz_uri, psz_port );
+            p_sdp++;
         }
-        else
+        if( p_sdp < &buffer[i_read] )
         {
-            sprintf( p_item->psz_uri, "%s://%s:%s", psz_proto,
-                            psz_uri, psz_port );
-        }
-
-        /* Enqueueing p_item in the playlist */
-
-        if( p_item )
-        {
-            p_playlist = vlc_object_find( p_intf,
-            VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
-
-            playlist_AddItem ( p_playlist, p_item,
-            PLAYLIST_CHECK_INSERT, PLAYLIST_END);
-            vlc_object_release( p_playlist );
+            sess_descr_t *p_sd = parse_sdp( p_intf, p_sdp );
+            if( p_sd )
+            {
+                sess_toitem ( p_intf, p_sd );
+                free_sd ( p_sd );
+            }
         }
-
-
     }
-
-
-    return 1;
-
 }
 
 /**********************************************************************
@@ -415,16 +393,28 @@ static void mfield_parse( char *psz_mfield, char **ppsz_proto,
                char **ppsz_port )
 {
     char *psz_pos;
+    char *psz_media;
     if( psz_mfield )
     {
         psz_pos = psz_mfield;
+        psz_media = psz_mfield;
         while( *psz_pos != '\0' && *psz_pos != ' ' )
         {
             psz_pos++;
         }
+        if( *psz_pos != '\0' )
+        {
+            *psz_pos = '\0';
+            if( strcmp( psz_media, "video" ) && strcmp( psz_media, "audio" ) )
+            {
+                *ppsz_proto = NULL;
+                *ppsz_port = NULL;
+                return;
+            }
+        }
         psz_pos++;
         *ppsz_port = psz_pos;
-        while( *psz_pos != '\0' && *psz_pos && *psz_pos !=' ' && *psz_pos!='/' )
+        while( *psz_pos != '\0' && *psz_pos !=' ' && *psz_pos!='/' )
         {
             psz_pos++;
         }
@@ -440,7 +430,7 @@ static void mfield_parse( char *psz_mfield, char **ppsz_proto,
         *psz_pos = '\0';
         psz_pos++;
         *ppsz_proto = psz_pos;
-        while( *psz_pos!='\0' && *psz_pos !=' ' && 
+        while( *psz_pos!='\0' && *psz_pos !=' ' &&
                         *psz_pos!='/' )
         {
             *psz_pos = tolower( *psz_pos );
@@ -456,43 +446,158 @@ static void mfield_parse( char *psz_mfield, char **ppsz_proto,
     return;
 }
 
-/***********************************************************************
- * parse_sap : Takes care of the SAP headers
- ***********************************************************************
- * checks if the packet has the true headers ;
- ***********************************************************************/
 
-static int parse_sap( char **  ppsz_sa_packet ) {  /* Dummy Parser : does nothing !*/
-   if( *ppsz_sa_packet )  return ADD_SESSION; //Add this packet
-   return 0; /* FIXME */
-}
+/*******************************************************************
+ * sess_toitem : changes a sess_descr_t into a hurd of
+ * playlist_item_t, which are enqueued.
+ *******************************************************************
+ * Note : does not support sessions that take place on consecutive
+ * port or adresses yet.
+ *******************************************************************/
+
+static void sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
+{
+    playlist_item_t * p_item;
+    char *psz_uri, *psz_proto;
+    char *psz_port;
+    char *psz_uri_default;
+    int i_count , i;
+    vlc_bool_t b_http = VLC_FALSE;
+    char *psz_http_path = NULL;
+    playlist_t *p_playlist = NULL;
+
+    psz_uri_default = NULL;
+    cfield_parse( p_sd->psz_connection, &psz_uri_default );
+
+    for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ )
+    {
+        p_item = malloc( sizeof( playlist_item_t ) );
+        p_item->psz_name    = strdup( p_sd->psz_sessionname );
+        p_item->psz_uri     = NULL;
+        p_item->i_duration  = -1;
+        p_item->ppsz_options= NULL;
+        p_item->i_options   = 0;
+
+        p_item->i_type      = 0;
+        p_item->i_status    = 0;
+        p_item->b_autodeletion = VLC_FALSE;
+        p_item->b_enabled   = VLC_TRUE;
+        p_item->i_group     = p_intf->p_sys->i_group;
+        p_item->psz_author  = strdup( "" );
+
+        psz_uri = NULL;
 
-/*************************************************************************
- * packet_handle : handle the received packet and enques the
- * the understated session
- *************************************************************************/
+        /* Build what we have to put in p_item->psz_uri, with the m and
+         *  c fields  */
 
-static int packet_handle( intf_thread_t * p_intf, char **  ppsz_packet )  {
-    int j=0;
-    sess_descr_t * p_sd;
+        if( !p_sd->pp_media[i_count] )
+        {
+            return;
+        }
 
-    j=parse_sap( ppsz_packet );
+        mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
+                        & psz_proto, & psz_port );
 
-    if(j != 0) {
+        if( !psz_proto || !psz_port )
+        {
+            return;
+        }
 
-        p_sd = parse_sdp( *ppsz_packet, p_intf );
+        if( p_sd->pp_media[i_count]->psz_mediaconnection )
+        {
+            cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
+                            & psz_uri );
+        }
+        else
+        {
+            psz_uri = psz_uri_default;
+        }
 
+        if( psz_uri == NULL )
+        {
+            return;
+        }
 
-        sess_toitem ( p_intf, p_sd );
+        for( i = 0 ; i< p_sd->i_attributes ; i++ )
+        {
+            if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "type") &&
+                strstr( p_sd->pp_attributes[i]->psz_value, "http") )
+            {
+                b_http = VLC_TRUE;
+            }
+            if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "http-path"))
+            {
+                psz_http_path = strdup(  p_sd->pp_attributes[i]->psz_value );
+            }
+            if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "plgroup"))
+            {
+                int i_id;
+                p_playlist =
+                (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
+                                                        FIND_ANYWHERE );
+                if( p_playlist == NULL )
+                {
+                    return;
+                }
 
-        free_sd ( p_sd );
-        return 1;
-    }
-    return 0; // Invalid Packet
-}
+                i_id = playlist_GroupToId( p_playlist,
+                                           p_sd->pp_attributes[i]->psz_value);
+                if( i_id != 0 )
+                {
+                    p_item->i_group = i_id;
+                }
+                else
+                {
+                    playlist_group_t *p_group =
+                            playlist_CreateGroup( p_playlist,
+                                       p_sd->pp_attributes[i]->psz_value);
+                    p_item->i_group = p_group->i_id;
+                }
+                vlc_object_release( p_playlist );
+            }
+        }
+
+
+        /* Filling p_item->psz_uri */
+        if( b_http == VLC_FALSE )
+        {
+            p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
+                                      strlen( psz_port ) + 7 );
+            if( ismult( psz_uri ) )
+            {
+                sprintf( p_item->psz_uri, "%s://@%s:%s",
+                         psz_proto, psz_uri, psz_port );
+            }
+            else
+            {
+                sprintf( p_item->psz_uri, "%s://%s:%s",
+                         psz_proto, psz_uri, psz_port );
+            }
+        }
+        else
+        {
+            if( psz_http_path == NULL )
+            {
+                psz_http_path = strdup( "/" );
+            }
 
+            p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
+                                      strlen( psz_port ) + strlen(psz_http_path) + 5 );
+            sprintf( p_item->psz_uri, "%s://%s:%s%s", psz_proto,
+                            psz_uri, psz_port,psz_http_path );
 
+            if( psz_http_path )
+            {
+                free( psz_http_path );
+            }
+        }
 
+        /* Enqueueing p_item in the playlist */
+        p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
+        playlist_AddItem ( p_playlist, p_item, PLAYLIST_CHECK_INSERT, PLAYLIST_END );
+        vlc_object_release( p_playlist );
+    }
+}
 
 /***********************************************************************
  * parse_sdp : SDP parsing
@@ -500,136 +605,118 @@ static int packet_handle( intf_thread_t * p_intf, char **  ppsz_packet )  {
  * Make a sess_descr_t with a psz
  ***********************************************************************/
 
-static sess_descr_t *  parse_sdp( char *  psz_pct, intf_thread_t * p_intf )
+static sess_descr_t *  parse_sdp( intf_thread_t * p_intf, char *p_packet )
 {
-    int j,k;
-    char **  ppsz_fill=NULL;
     sess_descr_t *  sd;
 
-    sd = malloc( sizeof(sess_descr_t) );
-
-
-    if( sd == NULL )
+    if( p_packet[0] != 'v' || p_packet[1] != '=' )
     {
-        msg_Err( p_intf, "out of memory" );
+        msg_Warn(p_intf, "bad SDP packet");
+        return NULL;
     }
-    else
+
+    sd = malloc( sizeof( sess_descr_t ) );
+    sd->psz_sessionname = NULL;
+    sd->psz_connection  = NULL;
+    sd->i_media         = 0;
+    sd->pp_media        = NULL;
+    sd->i_attributes    = 0;
+    sd->pp_attributes   = NULL;
+
+    while( *p_packet != '\0'  )
     {
-        sd->pp_media = NULL;
-        sd->psz_origin = NULL;
-        sd->psz_sessionname = NULL;
-        sd->psz_information = NULL;
-        sd->psz_uri = NULL;
-        sd->psz_emails = NULL;
-        sd->psz_phone = NULL;
-        sd->psz_time = NULL;
-        sd->psz_repeat = NULL;
-        sd->psz_attribute = NULL;
-        sd->psz_connection = NULL;
-
-        sd->i_media=-1;
-        j=0;
-        while( psz_pct[j]!=EOF && psz_pct[j] != '\0' )
-        {
-           j++;
-           if (psz_pct[j] == '=')
-           {
-               switch(psz_pct[(j-1)]) {
-               case ('v') : {
-                     ppsz_fill = & sd->psz_version;
-                     break;
-                }
-                case ('o') : {
-                   ppsz_fill = & sd->psz_origin;
-                   break;
-                }
-                case ('s') : {
-                   ppsz_fill = & sd->psz_sessionname;
-                   break;
-                }
-                case ('i') : {
-                   ppsz_fill = & sd->psz_information;
-                   break;
-                }
-                case ('u') : {
-                   ppsz_fill = & sd->psz_uri;
-                   break;
-                }
-                case ('e') : {
-                   ppsz_fill = & sd->psz_emails;
-                   break;
-                }
-                case ('p') : {
-                   ppsz_fill = & sd->psz_phone;
-                   break;
-                }
-                case ('t') : {
-                   ppsz_fill = & sd->psz_time;
-                   break;
-                }
-                case ('r') : {
-                   ppsz_fill = & sd->psz_repeat;
-                   break;
-                }
-                case ('a') : {
-                   ppsz_fill = & sd->psz_attribute;
-                   break;
-                }
-                case ('m') : {
-                   sd->i_media++;
-                   if( sd->pp_media ) {
-                       sd->pp_media = realloc( sd->pp_media,
-                                ( sizeof( void * )  * (sd->i_media + 1)) );
-                   }
-                   else
-                   {
-                       sd->pp_media = malloc( sizeof ( void * ) );
-                   }
-                   sd->pp_media[sd->i_media] =
-                           malloc( sizeof( media_descr_t ) );
-
-                   sd->pp_media[sd->i_media]->psz_medianame = NULL;
-                   sd->pp_media[sd->i_media]->psz_mediaconnection = NULL;
-
-                   ppsz_fill = & sd->pp_media[sd->i_media]->psz_medianame;
-                   break;
-                }
-                case ('c') : {
-                   if( sd->i_media == -1 )
-                   {
-                       ppsz_fill = & sd->psz_connection;
-                   }
-                   else
-                   {
-                       ppsz_fill = & sd->pp_media[sd->i_media]->
-                               psz_mediaconnection;
-                   }
-                   break;
-                }
+        char *psz_end;
+
+        /* Search begin of field */
+        while( *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' )
+        {
+            p_packet++;
+        }
+        /* search end of line */
+        if( ( psz_end = strchr( p_packet, '\n' ) ) == NULL )
+        {
+            psz_end = p_packet + strlen( p_packet );
+        }
+        if( psz_end > p_packet && *(psz_end - 1 ) == '\r' )
+        {
+            psz_end--;
+        }
+
+        if( psz_end <= p_packet )
+        {
+            break;
+        }
+        *psz_end++ = '\0';
+
+        if( p_packet[1] != '=' )
+        {
+            msg_Warn( p_intf, "packet invalid" );
+            free_sd( sd );
+            return NULL;
+        }
+
+        switch( p_packet[0] )
+        {
+            case( 'v' ):
+                sd->i_version = atoi( &p_packet[2] );
+                break;
+            case( 's' ):
+                sd->psz_sessionname = strdup( &p_packet[2] );
+                break;
+            case ( 'o' ):
+            case( 'i' ):
+            case( 'u' ):
+            case( 'e' ):
+            case( 'p' ):
+            case( 't' ):
+            case( 'r' ):
+                break;
+            case( 'a' ):
+            {
+                char *psz_eof = strchr( &p_packet[2], ':' );
+
+                if( psz_eof && psz_eof[1] != '\0' )
+                {
+                    attr_descr_t *attr = malloc( sizeof( attr_descr_t ) );
 
-                default : {
-                   ppsz_fill = NULL;
+                    *psz_eof++ = '\0';
+
+                    attr->psz_field = strdup( &p_packet[2] );
+                    attr->psz_value = strdup( psz_eof );
+
+                    TAB_APPEND( sd->i_attributes, sd->pp_attributes, attr );
                 }
+                break;
+            }
 
+            case( 'm' ):
+            {
+                media_descr_t *media = malloc( sizeof( media_descr_t ) );
 
-             }
-          k=0;j++;
+                media->psz_medianame = strdup( &p_packet[2] );
+                media->psz_mediaconnection = NULL;
 
-          while (psz_pct[j] != '\n'&& psz_pct[j] != EOF) {
-             k++; j++;
-          }
-          j--;
+                TAB_APPEND( sd->i_media, sd->pp_media, media );
+                break;
+            }
 
-          if( ppsz_fill != NULL )
-          {
-              *ppsz_fill= malloc( sizeof(char) * (k+1) );
-             memcpy(*ppsz_fill, &(psz_pct[j-k+1]), k );
-             (*ppsz_fill)[k]='\0';
-          }
-          ppsz_fill = NULL;
-          } // if
-       } //for
-    } //if
+            case( 'c' ):
+                if( sd->i_media <= 0 )
+                {
+                    sd->psz_connection = strdup( &p_packet[2] );
+                }
+                else
+                {
+                    sd->pp_media[sd->i_media-1]->psz_mediaconnection = strdup( &p_packet[2] );
+                }
+               break;
+
+            default:
+               break;
+        }
+
+        p_packet = psz_end;
+    }
 
     return sd;
 }
@@ -639,94 +726,81 @@ static sess_descr_t *  parse_sdp( char *  psz_pct, intf_thread_t * p_intf )
 static void free_sd( sess_descr_t * p_sd )
 {
     int i;
-    if( p_sd )
-    {
 
-    FREE( p_sd->psz_origin );
     FREE( p_sd->psz_sessionname );
-    FREE( p_sd->psz_information );
-    FREE( p_sd->psz_uri );
-    FREE( p_sd->psz_emails );
-    FREE( p_sd->psz_phone );
-    FREE( p_sd->psz_time );
-    FREE( p_sd->psz_repeat );
-    FREE( p_sd->psz_attribute );
     FREE( p_sd->psz_connection );
 
-    if( p_sd->i_media >= 0 && p_sd->pp_media )
+    for( i = 0; i < p_sd->i_media ; i++ )
     {
-        for( i=0; i <= p_sd->i_media ; i++ )
-        {
-            FREE( p_sd->pp_media[i]->psz_medianame );
-            FREE( p_sd->pp_media[i]->psz_mediaconnection );
-        }
-        FREE( p_sd->pp_media );
+        FREE( p_sd->pp_media[i]->psz_medianame );
+        FREE( p_sd->pp_media[i]->psz_mediaconnection );
     }
-    free( p_sd );
-    }
-    else
+    for( i = 0; i < p_sd->i_attributes ; i++ )
     {
-        ;
+        FREE( p_sd->pp_attributes[i]->psz_field );
+        FREE( p_sd->pp_attributes[i]->psz_value );
     }
-    return;
+    FREE( p_sd->pp_attributes );
+    FREE( p_sd->pp_media );
+
+    free( p_sd );
 }
 
 /***********************************************************************
- * ismult
+ * ismult: returns true if we have a multicast address
  ***********************************************************************/
 
 static int ismult( char *psz_uri )
 {
-    char *psz_c;
-    int i;
+    char *psz_end;
+    int  i_value;
 
-    psz_c = malloc( 3 );
+    i_value = strtol( psz_uri, &psz_end, 0 );
 
-    memcpy( psz_c, psz_uri, 3 );
-    if( psz_c[2] == '.' || psz_c[1] == '.' )
+    /* IPv6 */
+    if( psz_uri[0] == '[')
     {
-        free( psz_c );
-        return 0;
+      if( strncasecmp( &psz_uri[1], "FF0" , 3) ||
+          strncasecmp( &psz_uri[2], "FF0" , 3))
+            return( VLC_TRUE );
+        else
+            return( VLC_FALSE );
     }
 
-    i = atoi( psz_c );
-    if( i < 224 )
-    {
-        free( psz_c );
-        return 0;
-    }
+    if( *psz_end != '.' ) { return( VLC_FALSE ); }
 
-    free( psz_c );
-    return 1;
+    return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
 }
 
+
+
 /*****************************************************************************
  * Read: read on a file descriptor, checking b_die periodically
  *****************************************************************************
  * Taken from udp.c
- ******************************************************************************/
+ *****************************************************************************/
 static ssize_t NetRead( intf_thread_t *p_intf,
-                        int i_handle, byte_t *p_buffer, size_t i_len)
+                        int fd[2], uint8_t *p_buffer, int i_len )
 {
 #ifdef UNDER_CE
     return -1;
-
 #else
     struct timeval  timeout;
     fd_set          fds;
     int             i_ret;
+    int             i_handle_max = __MAX( fd[0], fd[1] );
 
     /* Initialize file descriptor set */
     FD_ZERO( &fds );
-    FD_SET( i_handle, &fds );
+    if( fd[0] > 0 ) FD_SET( fd[0], &fds );
+    if( fd[1] > 0 ) FD_SET( fd[1], &fds );
 
     /* We'll wait 0.5 second if nothing happens */
     timeout.tv_sec = 0;
     timeout.tv_usec = 500000;
 
     /* Find if some data is available */
-    i_ret = select( i_handle + 1, &fds,
-    NULL, NULL, &timeout );
+    i_ret = select( i_handle_max + 1, &fds, NULL, NULL, &timeout );
 
     if( i_ret == -1 && errno != EINTR )
     {
@@ -734,18 +808,15 @@ static ssize_t NetRead( intf_thread_t *p_intf,
     }
     else if( i_ret > 0 )
     {
-        ssize_t i_recv = recv( i_handle, p_buffer, i_len, 0 );
-
-        if( i_recv < 0 )
+        if( fd[0] > 0 && FD_ISSET( fd[0], &fds ) )
         {
-           msg_Err( p_intf, "recv failed (%s)", strerror(errno) );
+             return recv( fd[0], p_buffer, i_len, 0 );
+        }
+        else if( fd[1] > 0 && FD_ISSET( fd[1], &fds ) )
+        {
+             return recv( fd[1], p_buffer, i_len, 0 );
         }
-
-        return i_recv;
     }
-
     return 0;
-
 #endif
 }
-