]> git.sesse.net Git - vlc/blobdiff - modules/misc/sap.c
* modules/misc/dummy/decoder.c: use MAX_PATH instead of PATH_MAX.
[vlc] / modules / misc / sap.c
index e2712d10fe149c22946713aec12b9f0cdeeec519..6ac41dd4329deb985d87edf3cbab3801473c97da 100644 (file)
@@ -2,7 +2,7 @@
  * sap.c :  SAP interface module
  *****************************************************************************
  * Copyright (C) 2001 VideoLAN
- * $Id: sap.c,v 1.34 2003/11/12 08:10:21 zorglub Exp $
+ * $Id$
  *
  * Authors: Arnaud Schauly <gitan@via.ecp.fr>
  *          ClĂ©ment Stenac <zorglub@via.ecp.fr>
@@ -31,6 +31,7 @@
 
 #include <vlc/vlc.h>
 #include <vlc/intf.h>
+#include <vlc/input.h>
 
 #include <errno.h>                                                 /* ENOMEM */
 #include <ctype.h>
 
 #include "network.h"
 
+#ifdef HAVE_ZLIB_H
+#   include <zlib.h>
+#endif
+
 #define MAX_LINE_LENGTH 256
 
 /* SAP is always on that port */
 /*****************************************************************************
  * 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)")
+#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 the SAP module to listen to IPv4 announces" )
+#define SAP_IPV6_TEXT N_( "IPv6-SAP listening" )
+#define SAP_IPV6_LONGTEXT N_( \
+      "Set this if you want the SAP module to listen to IPv6 announces" )
+#define SAP_SCOPE_TEXT N_( "IPv6 SAP scope" )
+#define SAP_SCOPE_LONGTEXT N_( \
+       "Sets the scope for IPv6 announces (default is 8)" )
+#define SAP_TIMEOUT_TEXT N_( "SAP timeout (seconds)" )
+#define SAP_TIMEOUT_LONGTEXT N_( \
+       "Sets the time before SAP items get deleted if no new announce " \
+       "is received." )
+#define SAP_PARSE_TEXT N_( "Try to parse the SAP" )
+#define SAP_PARSE_LONGTEXT N_( \
+       "When SAP can it will try to parse the SAP. Normal behavior is " \
+       "to have livedotcom parse the announce." )
 
 static int  Open ( vlc_object_t * );
 static void Close( vlc_object_t * );
+static int  OpenSDP ( vlc_object_t * );
+static void CloseSDP( vlc_object_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);
-
-        add_bool( "sap-ipv6", 0 , NULL,
-                   SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE);
+    set_description( _("SAP interface") );
 
-        add_string( "sap-ipv6-scope", "8" , NULL,
-                    SAP_SCOPE_TEXT, SAP_SCOPE_LONGTEXT, 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 );
+    add_bool( "sap-ipv6", 0 , NULL,
+              SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE );
+    add_string( "sap-ipv6-scope", "8" , NULL,
+                SAP_SCOPE_TEXT, SAP_SCOPE_LONGTEXT, VLC_TRUE);
+    add_integer( "sap-timeout", 1800, NULL,
+                 SAP_TIMEOUT_TEXT, SAP_TIMEOUT_LONGTEXT, VLC_TRUE );
+    add_bool( "sap-parse", 1 , NULL,
+               SAP_PARSE_TEXT,SAP_PARSE_LONGTEXT, VLC_TRUE );
 
-    set_description( _("SAP interface") );
     set_capability( "interface", 0 );
     set_callbacks( Open, Close );
+    
+    add_submodule();
+        /* TODO FIXME
+         * This submodule is temporary and has a very high duplicate code rate
+         * This needs to be removed when SDP UDP parsing is fixed in liveMedia
+         */
+        set_description( _("SDP file parser (UDP only)") );
+        add_shortcut( "sdp" );
+        set_capability( "demux2", 51 );
+        set_callbacks( OpenSDP, CloseSDP );
+        
 vlc_module_end();
 
 /*****************************************************************************
@@ -118,24 +148,28 @@ vlc_module_end();
 static void Run    ( intf_thread_t *p_intf );
 static ssize_t NetRead( intf_thread_t *, int fd[2], uint8_t *, int );
 
+static int Demux( demux_t *p_demux );
+static int Control( demux_t *, int, va_list );
+
 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 sess_descr_t *  parse_sdp( vlc_object_t *, char * ) ;
 static void free_sd( sess_descr_t * );
 
 /* Detect multicast addresses */
 static int  ismult( char * );
 
-/* The struct that contains sdp informations */
+/* The struct that contains sdp information */
 struct  sess_descr_t
 {
     int  i_version;
     char *psz_sessionname;
     char *psz_connection;
+    char *psz_sdp;
 
     int           i_media;
     media_descr_t **pp_media;
@@ -143,7 +177,7 @@ struct  sess_descr_t
     attr_descr_t  **pp_attributes;
 };
 
-/* All this informations are not useful yet.  */
+/* All this information is not useful yet.  */
 struct media_descr_t
 {
     char *psz_medianame;
@@ -156,6 +190,14 @@ struct attr_descr_t
     char *psz_value;
 };
 
+struct sap_announce_t
+{
+    mtime_t i_last;
+    int i_id;
+    char *psz_name;
+    char *psz_uri;
+};
+
 struct intf_sys_t
 {
     /* IPV4 and IPV6 */
@@ -163,18 +205,70 @@ struct intf_sys_t
 
     /* playlist group */
     int i_group;
+
+    /* Table of announces */
+    int i_announces;
+    struct sap_announce_t **pp_announces;
+
+    int i_timeout;
 };
 
+#ifdef HAVE_ZLIB_H
+int do_decompress( unsigned char *src, unsigned char **_dst, int slen ) {
+    int result, dstsize, n;
+    unsigned char *dst;
+    z_stream d_stream;
+
+    d_stream.zalloc = (alloc_func)0;
+    d_stream.zfree = (free_func)0;
+    d_stream.opaque = (voidpf)0;
+    result = inflateInit(&d_stream);
+    if( result != Z_OK )
+    {
+        printf( "inflateInit() failed. Result: %d\n", result );
+        return( -1 );
+    }
+
+    d_stream.next_in = (Bytef *)src;
+    d_stream.avail_in = slen;
+    n = 0;
+    dst = NULL;
+    do
+    {
+        n++;
+        dst = (unsigned char *)realloc(dst, n * 1000);
+        d_stream.next_out = (Bytef *)&dst[(n - 1) * 1000];
+        d_stream.avail_out = 1000;
+        result = inflate(&d_stream, Z_NO_FLUSH);
+        if( ( result != Z_OK ) && ( result != Z_STREAM_END ) )
+        {
+            printf( "Zlib decompression failed. Result: %d\n", result );
+            return( -1 );
+        }
+    }
+    while( ( d_stream.avail_out == 0 ) && ( d_stream.avail_in != 0 ) &&
+           ( result != Z_STREAM_END ) );
+
+    dstsize = d_stream.total_out;
+    inflateEnd( &d_stream );
+
+    *_dst = (unsigned char *)realloc( dst, dstsize );
+
+    return dstsize;
+}
+#endif
+
 /*****************************************************************************
  * Open: initialize and create stuff
  *****************************************************************************/
 static int Open( vlc_object_t *p_this )
 {
-    intf_thread_t *p_intf = (intf_thread_t*)p_this;
+    intf_thread_t *p_intf = ( intf_thread_t* )p_this;
     intf_sys_t    *p_sys  = malloc( sizeof( intf_sys_t ) );
 
     playlist_t          *p_playlist;
 
+    p_sys->i_timeout = config_GetInt( p_intf,"sap-timeout" );
     p_sys->fd[0] = -1;
     p_sys->fd[1] = -1;
     if( config_GetInt( p_intf, "sap-ipv4" ) )
@@ -194,9 +288,9 @@ static int Open( vlc_object_t *p_this )
         sock.psz_server_addr   = "";
         sock.i_server_port     = 0;
         sock.i_ttl             = 0;
-        p_intf->p_private = (void*) &sock;
+        p_intf->p_private = ( void* ) &sock;
 
-        p_network = module_Need( p_intf, "network", "ipv4" );
+        p_network = module_Need( p_intf, "network", "ipv4", VLC_TRUE );
         if( p_network )
         {
             p_sys->fd[0] = sock.i_handle;
@@ -220,7 +314,8 @@ static int Open( vlc_object_t *p_this )
         {
             psz_scope = strdup( "8" );
         }
-        snprintf( psz_address, 100, "[%s%c%s]",IPV6_ADDR_1, psz_scope[0], IPV6_ADDR_2 );
+        snprintf( psz_address, 100, "[%s%c%s]",IPV6_ADDR_1,
+                  psz_scope[0], IPV6_ADDR_2 );
         free( psz_scope );
 
         sock.i_type            = NETWORK_UDP;
@@ -231,7 +326,7 @@ static int Open( vlc_object_t *p_this )
         sock.i_ttl             = 0;
         p_intf->p_private = (void*) &sock;
 
-        p_network = module_Need( p_intf, "network", "ipv6" );
+        p_network = module_Need( p_intf, "network", "ipv6", VLC_TRUE );
         if( p_network )
         {
             p_sys->fd[1] = sock.i_handle;
@@ -259,6 +354,9 @@ static int Open( vlc_object_t *p_this )
         vlc_object_release( p_playlist );
     }
 
+    p_sys->i_announces = 0;
+    p_sys->pp_announces = NULL;
+
     p_intf->pf_run = Run;
     p_intf->p_sys  = p_sys;
 
@@ -270,8 +368,9 @@ static int Open( vlc_object_t *p_this )
  *****************************************************************************/
 static void Close( vlc_object_t *p_this )
 {
-    intf_thread_t *p_intf = (intf_thread_t*)p_this;
+    intf_thread_t *p_intf = ( intf_thread_t* )p_this;
     intf_sys_t    *p_sys  = p_intf->p_sys;
+    int i;
 
     if( p_sys->fd[0] > 0 )
     {
@@ -282,6 +381,16 @@ static void Close( vlc_object_t *p_this )
         close( p_sys->fd[1] );
     }
 
+    for( i = 0 ; i< p_sys->i_announces ; i++ )
+    {
+        if( p_sys->pp_announces[i]->psz_name )
+           free( p_sys->pp_announces[i]->psz_name );
+        if( p_sys->pp_announces[i]->psz_uri )
+           free( p_sys->pp_announces[i]->psz_uri );
+        free( p_sys->pp_announces[i] );
+    }
+    free( p_sys->pp_announces );
+
     free( p_sys );
 }
 
@@ -290,18 +399,72 @@ static void Close( vlc_object_t *p_this )
  *****************************************************************************
  * Listens to SAP packets, and sends them to packet_handle
  *****************************************************************************/
-#define MAX_SAP_BUFFER 2000
+#define MAX_SAP_BUFFER 5000
 
 static void Run( intf_thread_t *p_intf )
 {
     intf_sys_t *p_sys  = p_intf->p_sys;
     uint8_t     buffer[MAX_SAP_BUFFER + 1];
+    uint8_t    *p_end;
+    
+    /* Dirty hack to slow down the startup of the sap interface */ 
+    msleep( 500000 );
 
     /* read SAP packets */
     while( !p_intf->b_die )
     {
+        playlist_t *p_playlist= NULL;
+        int i;
         int i_read = NetRead( p_intf, p_sys->fd, buffer, MAX_SAP_BUFFER );
         uint8_t *p_sdp;
+        int i_version;
+        int i_address_type;
+        int b_reserved;
+        int b_message_type;
+        int b_encrypted;
+        int b_compressed;
+        unsigned char *p_decompressed_buffer;
+        int i_decompressed_size;
+
+        /* Check for items that need deletion */
+        for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
+        {
+           struct sap_announce_t *p_announce;
+           mtime_t i_timeout = ( mtime_t ) 1000000*p_sys->i_timeout;
+           if( mdate() - p_intf->p_sys->pp_announces[i]->i_last > i_timeout )
+           {
+               msg_Dbg( p_intf,"Time out for %s, deleting (%i/%i)",
+                        p_intf->p_sys->pp_announces[i]->psz_name,
+                        i , p_intf->p_sys->i_announces );
+
+               /* Remove the playlist item */
+               p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
+                              FIND_ANYWHERE );
+               if( p_playlist )
+               {
+                   int i_pos = playlist_GetPositionById( p_playlist,
+                              p_intf->p_sys->pp_announces[i]->i_id );
+                   playlist_Delete( p_playlist, i_pos );
+               }
+
+               vlc_object_release( p_playlist );
+
+               /* Free the p_announce */
+               p_announce =  p_intf->p_sys->pp_announces[i];
+               if( p_announce->psz_name )
+                  free(  p_announce->psz_name );
+               if( p_announce->psz_uri )
+                  free(  p_announce->psz_uri );
+
+              /* Remove the sap_announce from the array */
+              REMOVE_ELEM( p_intf->p_sys->pp_announces,
+                           p_intf->p_sys->i_announces,
+                           i );
+
+              free( p_announce );
+
+           }
+        }
 
         /* Minimum length is > 6 */
         if( i_read <= 6 )
@@ -314,13 +477,58 @@ static void Run( intf_thread_t *p_intf )
         }
 
         buffer[i_read] = '\0';
+        p_end = &buffer[i_read];
 
         /* Parse the SAP header */
+        i_version = buffer[0] >> 5;
+        if( i_version != 1 )
+        {
+            msg_Dbg( p_intf, "strange sap version %d found", i_version );
+        }
+        i_address_type = buffer[0] & 0x10;
+        b_reserved = buffer[0] & 0x08;
+        if( b_reserved != 0 )
+        {
+            msg_Dbg( p_intf, "reserved bit incorrectly set" );
+        }
+        b_message_type = buffer[0] & 0x04;
+        if( b_message_type != 0 )
+        {
+            msg_Dbg( p_intf, "got session deletion packet" );
+        }
+        b_encrypted = buffer[0] & 0x02;
+        if( b_encrypted )
+        {
+            msg_Dbg( p_intf, "encrypted packet" );
+        }
+        b_compressed = buffer[0] & 0x01;
         p_sdp  = &buffer[4];
-        p_sdp += (buffer[0]&0x10) ? 16 : 4;
-        p_sdp += buffer[1];
-
-        while( p_sdp < &buffer[i_read-1] && *p_sdp != '\0' && p_sdp[0] != 'v' && p_sdp[1] != '=' )
+        if( i_address_type == 0 ) /* ipv4 source address */
+        {
+            p_sdp += 4;
+        }
+        else /* ipv6 source address */
+        {
+            p_sdp += 16;
+        }
+        if( b_compressed )
+        {
+#ifdef HAVE_ZLIB_H
+            i_decompressed_size = do_decompress( p_sdp, &p_decompressed_buffer,
+                                      i_read - ( p_sdp - buffer ) );
+            if( i_decompressed_size > 0 && i_decompressed_size < MAX_SAP_BUFFER )
+            {
+                memcpy( p_sdp, p_decompressed_buffer, i_decompressed_size );
+                p_sdp[i_decompressed_size] = '\0';
+                p_end = &p_sdp[i_decompressed_size];
+                free( p_decompressed_buffer );
+            }
+#else
+            msg_Warn( p_intf, "Ignoring compressed sap packet" );
+#endif
+        }
+        p_sdp += buffer[1]; /* size of signature */
+        while( p_sdp < p_end - 1 && *p_sdp != '\0' && p_sdp[0] != 'v' && p_sdp[1] != '=' )
         {
             p_sdp++;
         }
@@ -328,15 +536,22 @@ static void Run( intf_thread_t *p_intf )
         {
             p_sdp++;
         }
-        if( p_sdp < &buffer[i_read] )
+
+        if( p_sdp < p_end )
         {
-            sess_descr_t *p_sd = parse_sdp( p_intf, p_sdp );
+            sess_descr_t *p_sd = parse_sdp( (vlc_object_t *)p_intf, p_sdp );
             if( p_sd )
             {
                 sess_toitem ( p_intf, p_sd );
                 free_sd ( p_sd );
             }
         }
+        else
+        {
+            msg_Warn( p_intf, "ditching sap packet" );
+        }
+
+        memset( buffer, 0, MAX_SAP_BUFFER );
     }
 }
 
@@ -348,7 +563,6 @@ static void Run( intf_thread_t *p_intf )
 
 static void cfield_parse( char *psz_cfield, char **ppsz_uri )
 {
-
     char *psz_pos;
     if( psz_cfield )
     {
@@ -379,7 +593,6 @@ static void cfield_parse( char *psz_cfield, char **ppsz_uri )
     }
 
     return;
-
 }
 
 /**********************************************************************
@@ -457,38 +670,83 @@ static void mfield_parse( char *psz_mfield, char **ppsz_proto,
 
 static void sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
 {
-    playlist_item_t * p_item;
-    char *psz_uri, *psz_proto;
+    struct sap_announce_t *p_announce;
+    char *psz_uri, *psz_proto, *psz_item_uri;
     char *psz_port;
     char *psz_uri_default;
-    int i_count , i;
+    int i_count, i, i_id = 0;
     vlc_bool_t b_http = VLC_FALSE;
     char *psz_http_path = NULL;
     playlist_t *p_playlist = NULL;
+    playlist_item_t *p_item;
 
     psz_uri_default = NULL;
+    if( p_sd->i_media > 1 || !config_GetInt( p_intf, "sap-parse" ) )
+    {
+        asprintf( &psz_uri, "sdp://%s", p_sd->psz_sdp );
+        /* Check if we have already added the item */
+        for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
+        {
+            if( !strcmp( p_intf->p_sys->pp_announces[i]->psz_uri,
+                         psz_uri ) )
+            {
+                p_intf->p_sys->pp_announces[i]->i_last = mdate();
+                free( psz_uri );
+                return;
+            }
+        }
+        /* Add it to the playlist */
+        p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
+                                      FIND_ANYWHERE );
+        i_id = playlist_Add( p_playlist, psz_uri, p_sd->psz_sessionname ,
+                      PLAYLIST_CHECK_INSERT, PLAYLIST_END );
+        if( i_id != -1 )
+        {
+            playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_id );
+            playlist_ItemSetGroup( p_item, p_intf->p_sys->i_group );
+        }
+
+        /* Remember it */
+        p_announce = ( struct sap_announce_t * )malloc(
+                      sizeof( struct sap_announce_t ) );
+        if( p_sd->psz_sessionname )
+        {
+            p_announce->psz_name = strdup(  p_sd->psz_sessionname );
+        }
+        else
+        {
+            p_announce->psz_name = strdup( "" );
+        }
+        if( psz_uri )
+        {
+            p_announce->psz_uri  = strdup( psz_uri );
+        }
+        else
+        {
+            p_announce->psz_uri = strdup( "" );
+        }
+        p_announce->i_id = i_id;
+        p_announce->i_last = mdate();
+
+        INSERT_ELEM( p_intf->p_sys->pp_announces,
+                     p_intf->p_sys->i_announces,
+                     p_intf->p_sys->i_announces,
+                     p_announce );
+
+        vlc_object_release( p_playlist );
+        free( psz_uri );
+        return;
+    }
+
     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( "" );
+        int i_group = p_intf->p_sys->i_group;
+        int i_packetsize = config_GetInt( p_intf, "mtu" );
 
-        psz_uri = NULL;
-
-        /* Build what we have to put in p_item->psz_uri, with the m and
-         *  c fields  */
+        /* Build what we have to put in psz_item_uri, with the m and
+         * c fields  */
 
         if( !p_sd->pp_media[i_count] )
         {
@@ -520,17 +778,19 @@ static void sess_toitem( intf_thread_t * p_intf, sess_descr_t * 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") )
+            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"))
+            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"))
+            if( ( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "plgroup" ) ) ||
+                ( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "x-plgroup" ) ) )
             {
+                int i_group_id;
                 p_playlist =
                 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                                         FIND_ANYWHERE );
@@ -539,37 +799,38 @@ static void sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
                     return;
                 }
 
-                int i_id=playlist_GroupToId( p_playlist,
-                                      p_sd->pp_attributes[i]->psz_value);
-                if( i_id != 0 )
+                i_group_id = playlist_GroupToId( p_playlist,
+                                 p_sd->pp_attributes[i]->psz_value );
+                if( i_group_id != 0 )
                 {
-                    p_item->i_group = i_id;
+                    i_group = i_group_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;
+                                       p_sd->pp_attributes[i]->psz_value );
+                    i_group = p_group->i_id;
                 }
                 vlc_object_release( p_playlist );
             }
+            if( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "packetsize" ) )
+            {
+                i_packetsize = strtol( p_sd->pp_attributes[i]->psz_value, NULL, 10 );
+            }
         }
 
-
-        /* Filling p_item->psz_uri */
+        /* Filling 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",
+                asprintf( &psz_item_uri, "%s://@%s:%s",
                          psz_proto, psz_uri, psz_port );
             }
             else
             {
-                sprintf( p_item->psz_uri, "%s://%s:%s",
+                asprintf( &psz_item_uri, "%s://%s:%s",
                          psz_proto, psz_uri, psz_port );
             }
         }
@@ -579,11 +840,16 @@ static void sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
             {
                 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 == '/' )
+            {
+                asprintf( &psz_item_uri, "%s://%s:%s%s", psz_proto,
+                         psz_uri, psz_port,psz_http_path );
+            }
+            else
+            {
+                asprintf( &psz_item_uri, "%s://%s:%s/%s", psz_proto, psz_uri,
+                          psz_port, psz_http_path );
+            }
 
             if( psz_http_path )
             {
@@ -591,10 +857,102 @@ static void sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
             }
         }
 
-        /* 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 );
+        /* Check if we already know this item */
+         for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
+         {
+            if( !strcmp( p_intf->p_sys->pp_announces[i]->psz_uri,
+                         psz_item_uri ) )
+            {
+                p_intf->p_sys->pp_announces[i]->i_last = mdate();
+
+                /* Check if the name changed */
+                if( strcmp( p_intf->p_sys->pp_announces[i]->psz_name,
+                             p_sd->psz_sessionname ) )
+                {
+                    playlist_item_t *p_item;
+                    p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
+                                                  FIND_ANYWHERE );
+
+                    msg_Dbg(p_intf, "Name changed (%s -> %s) for %s",
+                            p_intf->p_sys->pp_announces[i]->psz_name,
+                            p_sd->psz_sessionname,
+                            psz_item_uri );
+
+                    p_item = playlist_ItemGetById( p_playlist,
+                                    p_intf->p_sys->pp_announces[i]->i_id );
+
+                    /* Change the name in the item */
+                    if( p_item )
+                    {
+                        vlc_mutex_lock( &p_item->input.lock );
+                        if( p_item->input.psz_name )
+                            free( p_item->input.psz_name );
+                        p_item->input.psz_name = strdup( p_sd->psz_sessionname );
+                        vlc_mutex_unlock( &p_item->input.lock );
+                    }
+
+                    /* Update the stored name */
+                    if( p_intf->p_sys->pp_announces[i]->psz_name )
+                        free( p_intf->p_sys->pp_announces[i]->psz_name );
+                    p_intf->p_sys->pp_announces[i]->psz_name =
+                                   strdup( p_sd->psz_sessionname );
+
+                    vlc_object_release( p_playlist );
+                }
+                free( psz_item_uri );
+                return;
+            }
+        }
+
+        /* Add the item in the playlist */
+        p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
+                                      FIND_ANYWHERE );
+
+        p_item = playlist_ItemNew( p_intf, psz_item_uri, p_sd->psz_sessionname );
+
+        if( p_item )
+        {
+            playlist_ItemSetGroup( p_item, i_group );
+            if( i_packetsize > config_GetInt( p_intf, "mtu" ) )
+            {
+                char *psz_packetsize_option;
+                asprintf( &psz_packetsize_option, "mtu=%i", i_packetsize );
+                playlist_ItemAddOption( p_item, psz_packetsize_option );
+                free( psz_packetsize_option );
+            }
+            playlist_AddItem( p_playlist , p_item , PLAYLIST_CHECK_INSERT, PLAYLIST_END );
+        }
+
+        /* Then remember it */
+        p_announce = (struct sap_announce_t *)malloc(
+                      sizeof( struct sap_announce_t ) );
+        if(  p_sd->psz_sessionname )
+        {
+            p_announce->psz_name = strdup(  p_sd->psz_sessionname );
+        }
+        else
+        {
+            p_announce->psz_name = strdup( "" );
+        }
+        if( psz_item_uri )
+        {
+            p_announce->psz_uri  = strdup( psz_item_uri );
+        }
+        else
+        {
+            p_announce->psz_uri = strdup( "" );
+        }
+        p_announce->i_id = i_id;
+
+        p_announce->i_last = mdate();
+
         vlc_object_release( p_playlist );
+
+        INSERT_ELEM( p_intf->p_sys->pp_announces,
+                     p_intf->p_sys->i_announces,
+                     p_intf->p_sys->i_announces,
+                     p_announce );
+        free( psz_item_uri );
     }
 }
 
@@ -604,19 +962,21 @@ static void sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
  * Make a sess_descr_t with a psz
  ***********************************************************************/
 
-static sess_descr_t *  parse_sdp( intf_thread_t * p_intf, char *p_packet )
+static sess_descr_t *  parse_sdp( vlc_object_t * p_parent, char *p_packet )
 {
     sess_descr_t *  sd;
 
     if( p_packet[0] != 'v' || p_packet[1] != '=' )
     {
-        msg_Warn(p_intf, "bad SDP packet");
+        msg_Warn( p_parent, "bad SDP packet" );
         return NULL;
     }
 
     sd = malloc( sizeof( sess_descr_t ) );
     sd->psz_sessionname = NULL;
     sd->psz_connection  = NULL;
+    sd->psz_sdp         = strdup( p_packet );
+
     sd->i_media         = 0;
     sd->pp_media        = NULL;
     sd->i_attributes    = 0;
@@ -636,7 +996,7 @@ static sess_descr_t *  parse_sdp( intf_thread_t * p_intf, char *p_packet )
         {
             psz_end = p_packet + strlen( p_packet );
         }
-        if( psz_end > p_packet && *(psz_end - 1 ) == '\r' )
+        if( psz_end > p_packet && *( psz_end - 1 ) == '\r' )
         {
             psz_end--;
         }
@@ -649,7 +1009,7 @@ static sess_descr_t *  parse_sdp( intf_thread_t * p_intf, char *p_packet )
 
         if( p_packet[1] != '=' )
         {
-            msg_Warn( p_intf, "packet invalid" );
+            msg_Warn( p_parent, "invalid packet" ) ;
             free_sd( sd );
             return NULL;
         }
@@ -728,16 +1088,19 @@ static void free_sd( sess_descr_t * p_sd )
 
     FREE( p_sd->psz_sessionname );
     FREE( p_sd->psz_connection );
+    FREE( p_sd->psz_sdp );
 
     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[i] );
     }
     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 );
+        FREE( p_sd->pp_attributes[i] );
     }
     FREE( p_sd->pp_attributes );
     FREE( p_sd->pp_media );
@@ -748,7 +1111,6 @@ static void free_sd( sess_descr_t * p_sd )
 /***********************************************************************
  * ismult: returns true if we have a multicast address
  ***********************************************************************/
-
 static int ismult( char *psz_uri )
 {
     char *psz_end;
@@ -771,12 +1133,12 @@ static int ismult( char *psz_uri )
     return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
 }
 
-
-
 /*****************************************************************************
- * Read: read on a file descriptor, checking b_die periodically
+ * NetRead: read on a file descriptor, checking b_die periodically
  *****************************************************************************
- * Taken from udp.c
+ * Taken from net.c
+ * Code duplication because of select(). We need a net_Select() but that's 
+ * quite difficult.
  *****************************************************************************/
 static ssize_t NetRead( intf_thread_t *p_intf,
                         int fd[2], uint8_t *p_buffer, int i_len )
@@ -790,6 +1152,7 @@ static ssize_t NetRead( intf_thread_t *p_intf,
     int             i_handle_max = __MAX( fd[0], fd[1] );
 
     /* Initialize file descriptor set */
+
     FD_ZERO( &fds );
     if( fd[0] > 0 ) FD_SET( fd[0], &fds );
     if( fd[1] > 0 ) FD_SET( fd[1], &fds );
@@ -819,3 +1182,256 @@ static ssize_t NetRead( intf_thread_t *p_intf,
     return 0;
 #endif
 }
+
+/*
+ * This submodule is temporary and has a very high duplicate code rate
+ * This needs to be removed when SDP UDP parsing is fixed in liveMedia
+ */
+static int OpenSDP( vlc_object_t * p_this )
+{
+    demux_t *p_demux = (demux_t *)p_this;
+    uint8_t *p_peek;
+
+    if( p_demux->s )
+    {
+        /* See if it looks like a SDP
+           v, o, s fields are mandatory and in this order */
+        if( stream_Peek( p_demux->s, &p_peek, 7 ) < 7 )
+        {
+            msg_Err( p_demux, "cannot peek" );
+            return VLC_EGENERIC;
+        }
+        if( strncmp( (char*)p_peek, "v=0\r\n", 5 ) && strncmp( (char*)p_peek, "v=0\n", 4 ) &&
+            ( p_peek[0] < 'a' || p_peek[0] > 'z' || p_peek[1] != '=' ) )
+        {
+            msg_Warn( p_demux, "SDP (UDP) module discarded" );
+            return VLC_EGENERIC;
+        }
+    }
+    
+    p_demux->pf_control = Control;
+    p_demux->pf_demux = Demux;
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Demux: reads and demuxes data packets
+ * This submodule is temporary and has a very high duplicate code rate
+ * This needs to be removed when SDP UDP parsing is fixed in liveMedia
+ *****************************************************************************
+ * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
+ *****************************************************************************/
+static int Demux( demux_t *p_demux )
+{
+    sess_descr_t *p_sd = NULL;
+    playlist_t *p_playlist = NULL;
+    int i_sdp = 0;
+    int i_sdp_max = 1000;
+    int i_position = -1;
+    char *p_sdp = (uint8_t*)malloc( i_sdp_max );
+    
+    p_playlist = vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST,
+                                          FIND_ANYWHERE );
+    
+    if( !p_playlist )
+    {
+        msg_Err( p_demux, "can't find playlist" );
+        return -1;
+    }
+    
+    p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE;
+    i_position = p_playlist->i_index + 1;
+    
+    /* Gather the complete sdp file */
+    for( ;; )
+    {
+        int i_read = stream_Read( p_demux->s, &p_sdp[i_sdp], i_sdp_max - i_sdp - 1 );
+
+        if( i_read < 0 )
+        {
+            msg_Err( p_demux, "failed to read SDP" );
+            return VLC_EGENERIC;
+        }
+
+        i_sdp += i_read;
+
+        if( i_read < i_sdp_max - i_sdp - 1 )
+        {
+            p_sdp[i_sdp] = '\0';
+            break;
+        }
+
+        i_sdp_max += 1000;
+        p_sdp = (uint8_t*)realloc( p_sdp, i_sdp_max );
+    }
+
+    msg_Dbg( p_demux, "sdp=%s\n", p_sdp );
+    
+    p_sd = parse_sdp( (vlc_object_t *)p_demux, p_sdp );
+    if( p_sd )
+    {
+        char *psz_uri, *psz_proto, *psz_item_uri;
+        char *psz_port;
+        char *psz_uri_default;
+        int i_count, i, i_id = 0;
+        vlc_bool_t b_http = VLC_FALSE;
+        char *psz_http_path = NULL;
+        playlist_item_t *p_item;
+        psz_uri_default = NULL;
+        
+        if( p_sd->i_media > 1 || !config_GetInt( p_demux, "sap-parse" ) )
+        {
+            /* Let another module try this */
+            asprintf( &psz_uri, "sdp://%s", p_sd->psz_sdp );
+            i_id = playlist_Add( p_playlist, psz_uri, p_sd->psz_sessionname ,
+                          PLAYLIST_CHECK_INSERT, i_position );
+            free( psz_uri );
+            return 0;
+        }
+        
+        /* We try to parse it ourselves */
+        cfield_parse( p_sd->psz_connection, &psz_uri_default );
+
+        for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ )
+        {
+            int i_group = 0;
+            int i_packetsize = config_GetInt( p_demux, "mtu" );
+
+            /* Build what we have to put in psz_item_uri, with the m and
+             * c fields  */
+
+            if( !p_sd->pp_media[i_count] )
+            {
+                return -1;
+            }
+
+            mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
+                            &psz_proto, &psz_port );
+
+            if( !psz_proto || !psz_port )
+            {
+                return -1;
+            }
+
+            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 -1;
+            }
+
+            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" ) ) ||
+                    ( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "x-plgroup" ) ) )
+                {
+                    int i_group_id;
+
+                    i_group_id = playlist_GroupToId( p_playlist,
+                                     p_sd->pp_attributes[i]->psz_value );
+                    if( i_group_id != 0 )
+                    {
+                        i_group = i_group_id;
+                    }
+                    else
+                    {
+                        playlist_group_t *p_group =
+                                playlist_CreateGroup( p_playlist,
+                                           p_sd->pp_attributes[i]->psz_value );
+                        i_group = p_group->i_id;
+                    }
+                }
+                if( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "packetsize" ) )
+                {
+                    i_packetsize = strtol( p_sd->pp_attributes[i]->psz_value, NULL, 10 );
+                }
+            }
+
+            /* Filling psz_uri */
+            if( b_http == VLC_FALSE )
+            {
+                if( ismult( psz_uri ) )
+                {
+                    asprintf( &psz_item_uri, "%s://@%s:%s",
+                             psz_proto, psz_uri, psz_port );
+                }
+                else
+                {
+                    asprintf( &psz_item_uri, "%s://%s:%s",
+                             psz_proto, psz_uri, psz_port );
+                }
+            }
+            else
+            {
+                if( psz_http_path == NULL )
+                {
+                    psz_http_path = strdup( "/" );
+                }
+                if( *psz_http_path == '/' )
+                {
+                    asprintf( &psz_item_uri, "%s://%s:%s%s", psz_proto,
+                             psz_uri, psz_port,psz_http_path );
+                }
+                else
+                {
+                    asprintf( &psz_item_uri, "%s://%s:%s/%s", psz_proto, psz_uri,
+                              psz_port, psz_http_path );
+                }
+
+                if( psz_http_path )
+                {
+                    free( psz_http_path );
+                }
+            }
+
+            /* Add the item in the playlist */
+            p_item = playlist_ItemNew( p_demux, psz_item_uri, p_sd->psz_sessionname );
+
+            if( p_item )
+            {
+                playlist_ItemSetGroup( p_item, i_group );
+                if( i_packetsize > config_GetInt( p_demux, "mtu" ) )
+                {
+                    char *psz_packetsize_option;
+                    asprintf( &psz_packetsize_option, "mtu=%i", i_packetsize );
+                    playlist_ItemAddOption( p_item, psz_packetsize_option );
+                    free( psz_packetsize_option );
+                }
+                playlist_AddItem( p_playlist , p_item , PLAYLIST_CHECK_INSERT, i_position );
+            }
+
+            free( psz_item_uri );
+        }
+
+        free_sd ( p_sd );
+    }
+    vlc_object_release( p_playlist );
+    return 0;
+}    
+
+static int Control( demux_t *p_demux, int i_query, va_list args )
+{
+    return VLC_EGENERIC;
+}
+
+static void CloseSDP( vlc_object_t *p_this )
+{
+}