[vlc-commits] sap: reorder to avoid forward declarations

Rémi Denis-Courmont git at videolan.org
Fri Apr 10 15:06:59 CEST 2020


vlc | branch: master | Rémi Denis-Courmont <remi at remlab.net> | Fri Apr 10 14:39:32 2020 +0300| [d13f204ccf592b2499998649fbcc0e04e903840b] | committer: Rémi Denis-Courmont

sap: reorder to avoid forward declarations

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=d13f204ccf592b2499998649fbcc0e04e903840b
---

 modules/services_discovery/sap.c | 2254 +++++++++++++++++++-------------------
 1 file changed, 1103 insertions(+), 1151 deletions(-)

diff --git a/modules/services_discovery/sap.c b/modules/services_discovery/sap.c
index bf90e58416..25e95dc799 100644
--- a/modules/services_discovery/sap.c
+++ b/modules/services_discovery/sap.c
@@ -75,14 +75,38 @@
 #define SAP_V4_LINK_ADDRESS     "224.0.0.255"
 #define ADD_SESSION 1
 
-/*****************************************************************************
- * Local structures
- *****************************************************************************/
+typedef struct attribute_t
+{
+    const char *value;
+    char name[];
+} attribute_t;
 
-typedef struct sdp_t sdp_t;
-typedef struct attribute_t attribute_t;
-typedef struct sap_announce_t sap_announce_t;
+static bool IsWellKnownPayload (int type)
+{
+    switch (type)
+    {   /* Should be in sync with modules/demux/rtp.c */
+        case  0: /* PCMU/8000 */
+        case  3:
+        case  8: /* PCMA/8000 */
+        case 10: /* L16/44100/2 */
+        case 11: /* L16/44100 */
+        case 12:
+        case 14: /* MPA/90000 */
+        case 32: /* MPV/90000 */
+        case 33: /* MP2/90000 */
+            return true;
+   }
+   return false;
+}
 
+static const char *GetAttribute (attribute_t **tab, unsigned n,
+                                 const char *name)
+{
+    for (unsigned i = 0; i < n; i++)
+        if (strcasecmp (tab[i]->name, name) == 0)
+            return tab[i]->value;
+    return NULL;
+}
 
 struct sdp_media_t
 {
@@ -95,9 +119,8 @@ struct sdp_media_t
     attribute_t  **pp_attributes;
 };
 
-
 /* The structure that contains sdp information */
-struct  sdp_t
+typedef struct sdp_t
 {
     const char *psz_sdp;
 
@@ -120,637 +143,828 @@ struct  sdp_t
     /* medias (well, we only support one atm) */
     unsigned            mediac;
     struct sdp_media_t *mediav;
-};
+} sdp_t;
 
-struct attribute_t
-{
-    const char *value;
-    char name[];
-};
-
-struct sap_announce_t
-{
-    vlc_tick_t i_last;
-    vlc_tick_t i_period;
-    uint8_t i_period_trust;
-
-    uint16_t    i_hash;
-    uint32_t    i_source[4];
-
-    /* SAP annnounces must only contain one SDP */
-    sdp_t       *p_sdp;
-
-    input_item_t * p_item;
-};
-
-typedef struct
+static const char *FindAttribute (const sdp_t *sdp, unsigned media,
+                                  const char *name)
 {
-    vlc_thread_t thread;
-
-    /* Socket descriptors */
-    int i_fd;
-    int *pi_fd;
-
-    /* Table of announces */
-    int i_announces;
-    struct sap_announce_t **pp_announces;
-
-    /* Modes */
-    bool  b_parse;
-
-    vlc_tick_t i_timeout;
-} services_discovery_sys_t;
+    /* Look for media attribute, and fallback to session */
+    const char *attr = GetAttribute (sdp->mediav[media].pp_attributes,
+                                     sdp->mediav[media].i_attributes, name);
+    if (attr == NULL)
+        attr = GetAttribute (sdp->pp_attributes, sdp->i_attributes, name);
+    return attr;
+}
 
-typedef struct
+/* Compute URI */
+static char *ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp )
 {
-    sdp_t *p_sdp;
-    /* "computed" URI */
     char *uri;
-} demux_sys_t;
-
-/*****************************************************************************
- * Local prototypes
- *****************************************************************************/
-
-
-/* Main functions */
-    static int Demux( demux_t *p_demux );
-    static int Control( demux_t *, int, va_list );
-    static void *Run  ( void *p_sd );
-
-/* Main parsing functions */
-static char *ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp );
-    static int ParseSAP( services_discovery_t *p_sd, const uint8_t *p_buffer, size_t i_read );
-    static sdp_t *ParseSDP (vlc_object_t *p_sd, const char *psz_sdp);
-static sap_announce_t *CreateAnnounce( services_discovery_t *, uint32_t *, uint16_t, sdp_t *, const char * );
-    static int RemoveAnnounce( services_discovery_t *p_sd, sap_announce_t *p_announce );
-
-/* Helper functions */
-    static inline attribute_t *MakeAttribute (const char *str);
-    static const char *GetAttribute (attribute_t **tab, unsigned n, const char *name);
-    static inline void FreeAttribute (attribute_t *a);
-    static const char *FindAttribute (const sdp_t *sdp, unsigned media,
-                                      const char *name);
-
-    static int InitSocket( services_discovery_t *p_sd, const char *psz_address, int i_port );
-    static int Decompress( const unsigned char *psz_src, unsigned char **_dst, int i_len );
-    static void FreeSDP( sdp_t *p_sdp );
-
-static bool IsWellKnownPayload (int type)
-{
-    switch (type)
-    {   /* Should be in sync with modules/demux/rtp.c */
-        case  0: /* PCMU/8000 */
-        case  3:
-        case  8: /* PCMA/8000 */
-        case 10: /* L16/44100/2 */
-        case 11: /* L16/44100 */
-        case 12:
-        case 14: /* MPA/90000 */
-        case 32: /* MPV/90000 */
-        case 33: /* MP2/90000 */
-            return true;
-   }
-   return false;
-}
 
-/*****************************************************************************
- * Open: initialize and create stuff
- *****************************************************************************/
-static int Open( vlc_object_t *p_this )
-{
-    services_discovery_t *p_sd = ( services_discovery_t* )p_this;
-    services_discovery_sys_t *p_sys  = (services_discovery_sys_t *)
-                                malloc( sizeof( services_discovery_sys_t ) );
-    if( !p_sys )
-        return VLC_ENOMEM;
+    if (p_sdp->mediac == 0)
+    {
+        msg_Dbg (p_obj, "Ignoring SDP with no media");
+        return NULL;
+    }
 
-    p_sys->i_timeout = vlc_tick_from_sec(var_CreateGetInteger( p_sd, "sap-timeout" ));
+    for (unsigned i = 1; i < p_sdp->mediac; i++)
+    {
+        if ((p_sdp->mediav[i].n_addr != p_sdp->mediav->n_addr)
+         || (p_sdp->mediav[i].addrlen != p_sdp->mediav->addrlen)
+         || memcmp (&p_sdp->mediav[i].addr, &p_sdp->mediav->addr,
+                    p_sdp->mediav->addrlen))
+        {
+            msg_Dbg (p_obj, "Multiple media ports not supported -> live555");
+            return NULL;
+        }
+    }
 
-    p_sd->p_sys  = p_sys;
-    p_sd->description = _("Network streams (SAP)");
+    if (p_sdp->mediav->n_addr != 1)
+    {
+        msg_Dbg (p_obj, "Layered encoding not supported -> live555");
+        return NULL;
+    }
 
-    p_sys->pi_fd = NULL;
-    p_sys->i_fd = 0;
+    char psz_uri[1026];
+    const char *host;
+    int port;
 
-    p_sys->b_parse = var_CreateGetBool( p_sd, "sap-parse" );
+    psz_uri[0] = '[';
+    if (vlc_getnameinfo ((struct sockaddr *)&(p_sdp->mediav->addr),
+                         p_sdp->mediav->addrlen, psz_uri + 1,
+                         sizeof (psz_uri) - 2, &port, NI_NUMERICHOST))
+        return NULL;
 
-    p_sys->i_announces = 0;
-    p_sys->pp_announces = NULL;
-    /* TODO: create sockets here, and fix racy sockets table */
-    if (vlc_clone (&p_sys->thread, Run, p_sd, VLC_THREAD_PRIORITY_LOW))
+    if (strchr (psz_uri + 1, ':'))
     {
-        free (p_sys);
-        return VLC_EGENERIC;
+        host = psz_uri;
+        strcat (psz_uri, "]");
     }
+    else
+        host = psz_uri + 1;
 
-    return VLC_SUCCESS;
-}
-
-/*****************************************************************************
- * OpenDemux: initialize and create stuff
- *****************************************************************************/
-static int OpenDemux( vlc_object_t *p_this )
-{
-    demux_t *p_demux = (demux_t *)p_this;
-    const uint8_t *p_peek;
-    char *psz_sdp = NULL;
-    sdp_t *p_sdp = NULL;
-    int errval = VLC_EGENERIC;
-    size_t i_len;
+    /* Parse m= field */
+    char *sdp_proto = strdup (p_sdp->mediav[0].fmt);
+    if (sdp_proto == NULL)
+        return NULL;
 
-    if( !var_CreateGetBool( p_demux, "sap-parse" ) )
+    char *subtype = strchr (sdp_proto, ' ');
+    if (subtype == NULL)
     {
-        /* We want livedotcom module to parse this SDP file */
-        return VLC_EGENERIC;
+        msg_Dbg (p_obj, "missing SDP media subtype: %s", sdp_proto);
+        free (sdp_proto);
+        return NULL;
     }
 
-    assert( p_demux->s ); /* this is NOT an access_demux */
-
-    /* Probe for SDP */
-    if( vlc_stream_Peek( p_demux->s, &p_peek, 7 ) < 7 )
-        return VLC_EGENERIC;
+    *subtype++ = '\0';
+    /* FIXME: check for multiple payload types in RTP/AVP case.
+     * FIXME: check for "mpeg" subtype in raw udp case. */
+    if (strcasecmp(sdp_proto, "udp") != 0
+     && !IsWellKnownPayload(atoi(subtype)))
+    {
+        free(sdp_proto);
+        return NULL;
+    }
 
-    if( memcmp( p_peek, "v=0\r\no=", 7 ) && memcmp( p_peek, "v=0\no=", 6 ) )
-        return VLC_EGENERIC;
+    /* RTP protocol, nul, VLC shortcut, nul, flags byte as follow:
+     * 0x1: Connection-Oriented media. */
+    static const char proto_match[] =
+        "udp\0"             "udp\0\0"
+        "RTP/AVP\0"         "rtp\0\0"
+        "UDPLite/RTP/AVP\0" "udplite\0\0"
+        "DCCP/RTP/AVP\0"    "dccp\0\1"
+        "TCP/RTP/AVP\0"     "rtptcp\0\1"
+        "\0";
 
-    /* Gather the complete sdp file */
-    for( i_len = 0, psz_sdp = NULL; i_len < 65536; )
+    const char *vlc_proto = NULL;
+    uint8_t flags = 0;
+    for (const char *proto = proto_match; *proto;)
     {
-        const int i_read_max = 1024;
-        char *psz_sdp_new = realloc( psz_sdp, i_len + i_read_max + 1 );
-        size_t i_read;
-        if( psz_sdp_new == NULL )
-        {
-            errval = VLC_ENOMEM;
-            goto error;
-        }
-        psz_sdp = psz_sdp_new;
-
-        i_read = vlc_stream_Read( p_demux->s, &psz_sdp[i_len], i_read_max );
-        if( (int)i_read < 0 )
+        if (strcasecmp (proto, sdp_proto) == 0)
         {
-            msg_Err( p_demux, "cannot read SDP" );
-            goto error;
+            vlc_proto = proto + strlen (proto) + 1;
+            flags = vlc_proto[strlen (vlc_proto) + 1];
+            break;
         }
-        i_len += i_read;
-
-        psz_sdp[i_len] = '\0';
-
-        if( (int)i_read < i_read_max )
-            break; // EOF
+        proto += strlen (proto) + 1;
+        proto += strlen (proto) + 2;
     }
 
-    p_sdp = ParseSDP( VLC_OBJECT(p_demux), psz_sdp );
-
-    if( !p_sdp )
+    free (sdp_proto);
+    if (vlc_proto == NULL)
     {
-        msg_Warn( p_demux, "invalid SDP");
-        goto error;
+        msg_Dbg (p_obj, "unknown SDP media protocol: %s",
+                 p_sdp->mediav[0].fmt);
+        return NULL;
     }
 
-    char *uri = ParseConnection(VLC_OBJECT(p_demux), p_sdp);
-    if (uri == NULL)
-        goto error;
-
-    demux_sys_t *p_sys = malloc( sizeof(*p_sys) );
-    if( unlikely(p_sys == NULL) )
-        goto error;
-    p_sys->p_sdp = p_sdp;
-    p_sys->uri = uri;
-    p_demux->p_sys = p_sys;
-    p_demux->pf_control = Control;
-    p_demux->pf_demux = Demux;
-
-    FREENULL( psz_sdp );
-    return VLC_SUCCESS;
-
-error:
-    FREENULL( psz_sdp );
-    if( p_sdp ) FreeSDP( p_sdp );
-    return errval;
-}
-
-/*****************************************************************************
- * Close:
- *****************************************************************************/
-static void Close( vlc_object_t *p_this )
-{
-    services_discovery_t *p_sd = ( services_discovery_t* )p_this;
-    services_discovery_sys_t    *p_sys  = p_sd->p_sys;
-    int i;
-
-    vlc_cancel (p_sys->thread);
-    vlc_join (p_sys->thread, NULL);
-
-    for( i = p_sys->i_fd-1 ; i >= 0 ; i-- )
+    if (!strcmp (vlc_proto, "udp") || FindAttribute (p_sdp, 0, "rtcp-mux"))
+        p_sdp->rtcp_port = 0;
+    else
     {
-        net_Close( p_sys->pi_fd[i] );
+        const char *rtcp = FindAttribute (p_sdp, 0, "rtcp");
+        if (rtcp)
+            p_sdp->rtcp_port = atoi (rtcp);
+        else
+        if (port & 1) /* odd port -> RTCP; next even port -> RTP */
+            p_sdp->rtcp_port = port++;
+        else /* even port -> RTP; next odd port -> RTCP */
+            p_sdp->rtcp_port = port + 1;
     }
-    FREENULL( p_sys->pi_fd );
 
-    for( i = p_sys->i_announces  - 1;  i>= 0; i-- )
+    if (flags & 1)
     {
-        RemoveAnnounce( p_sd, p_sys->pp_announces[i] );
-    }
-    FREENULL( p_sys->pp_announces );
-
-    free( p_sys );
-}
-
-/*****************************************************************************
- * CloseDemux: Close the demuxer
- *****************************************************************************/
-static void CloseDemux( vlc_object_t *p_this )
-{
-    demux_t *p_demux = (demux_t *)p_this;
-    demux_sys_t *sys = p_demux->p_sys;
-
-    if( sys->p_sdp )
-        FreeSDP( sys->p_sdp );
-    free(sys->uri);
-    free( sys );
-}
-
-/*****************************************************************************
- * Run: main SAP thread
- *****************************************************************************
- * Listens to SAP packets, and sends them to packet_handle
- *****************************************************************************/
-#define MAX_SAP_BUFFER 5000
-
-static void *Run( void *data )
-{
-    services_discovery_t *p_sd = data;
-    services_discovery_sys_t *p_sys = p_sd->p_sys;
-    char *psz_addr;
-    int timeout = -1;
-    int canc = vlc_savecancel ();
-
-    /* Braindead Winsock DNS resolver will get stuck over 2 seconds per failed
-     * DNS queries, even if the DNS server returns an error with milliseconds.
-     * You don't want to know why the bug (as of XP SP2) wasn't fixed since
-     * Winsock 1.1 from Windows 95, if not Windows 3.1.
-     * Anyway, to avoid a 30 seconds delay for failed IPv6 socket creation,
-     * we have to open sockets in Run() rather than Open(). */
-    InitSocket( p_sd, SAP_V4_GLOBAL_ADDRESS, SAP_PORT );
-    InitSocket( p_sd, SAP_V4_ORG_ADDRESS, SAP_PORT );
-    InitSocket( p_sd, SAP_V4_LOCAL_ADDRESS, SAP_PORT );
-    InitSocket( p_sd, SAP_V4_LINK_ADDRESS, SAP_PORT );
+        /* Connection-oriented media */
+        const char *setup = FindAttribute (p_sdp, 0, "setup");
+        if (setup == NULL)
+            setup = "active"; /* default value */
 
-    char psz_address[NI_MAXNUMERICHOST] = "ff02::2:7ffe%";
-#ifndef _WIN32
-    struct if_nameindex *l = if_nameindex ();
-    if (l != NULL)
-    {
-        char *ptr = strchr (psz_address, '%') + 1;
-        for (unsigned i = 0; l[i].if_index; i++)
+        if (strcmp (setup, "actpass") && strcmp (setup, "passive"))
         {
-            strcpy (ptr, l[i].if_name);
-            InitSocket (p_sd, psz_address, SAP_PORT);
+            msg_Dbg (p_obj, "unsupported COMEDIA mode: %s", setup);
+            return NULL;
         }
-        if_freenameindex (l);
-    }
-#else
-        /* this is the Winsock2 equivalant of SIOCGIFCONF on BSD stacks,
-           which if_nameindex uses internally anyway */
 
-        // first create a dummy socket to pin down the protocol family
-        SOCKET s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
-        if( s != INVALID_SOCKET )
+        if (asprintf(&uri, "%s://%s:%d", vlc_proto, host, port) == -1)
+            return NULL;
+    }
+    else
+    {
+        /* Non-connected (normally multicast) media */
+        char psz_source[258] = "";
+        const char *sfilter = FindAttribute (p_sdp, 0, "source-filter");
+        if (sfilter != NULL)
         {
-            INTERFACE_INFO ifaces[10]; // Assume there will be no more than 10 IP interfaces
-            DWORD len = sizeof(ifaces);
+            char psz_source_ip[256];
+            unsigned ipv;
 
-            if( SOCKET_ERROR != WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0, &ifaces, len, &len, NULL, NULL) )
+            if (sscanf (sfilter, " incl IN IP%u %*s %255s ", &ipv,
+                        psz_source_ip) == 2)
             {
-                unsigned ifcount = len/sizeof(INTERFACE_INFO);
-                char *ptr = strchr (psz_address, '%') + 1;
-                for(unsigned i = 1; i<=ifcount; ++i )
+                /* According to RFC4570, FQDNs can be used for source-filters,
+                 * but -seriously- this is impractical */
+                switch (ipv)
                 {
-                    // append link-local zone identifier
-                    sprintf(ptr, "%d", i);
+#ifdef AF_INET6
+                    case 6:
+                    {
+                        struct in6_addr addr;
+                        if ((inet_pton (AF_INET6, psz_source_ip, &addr) > 0)
+                        && (inet_ntop (AF_INET6, &addr, psz_source + 1,
+                                        sizeof (psz_source) - 2) != NULL))
+                        {
+                            psz_source[0] = '[';
+                            psz_source[strlen (psz_source)] = ']';
+                        }
+                        break;
+                    }
+#endif
+                    case 4:
+                    {
+                        struct in_addr addr;
+                        if ((inet_pton (AF_INET, psz_source_ip, &addr) > 0)
+                        && (inet_ntop (AF_INET, &addr, psz_source,
+                                        sizeof (psz_source)) == NULL))
+                            *psz_source = '\0';
+                        break;
+                    }
                 }
             }
-            closesocket(s);
         }
-#endif
-    *strchr (psz_address, '%') = '\0';
 
-    static const char ipv6_scopes[] = "1456789ABCDE";
-    for (const char *c_scope = ipv6_scopes; *c_scope; c_scope++)
-    {
-        psz_address[3] = *c_scope;
-        InitSocket( p_sd, psz_address, SAP_PORT );
+        if (asprintf(&uri, "%s://%s@%s:%i", vlc_proto, psz_source,
+                     host, port) == -1)
+            return NULL;
     }
 
-    psz_addr = var_CreateGetString( p_sd, "sap-addr" );
-    if( psz_addr && *psz_addr )
-        InitSocket( p_sd, psz_addr, SAP_PORT );
-    free( psz_addr );
+    return uri;
+}
 
-    if( p_sys->i_fd == 0 )
-    {
-        msg_Err( p_sd, "unable to listen on any address" );
-        return NULL;
-    }
+static int ParseSDPConnection (const char *str, struct sockaddr_storage *addr,
+                               socklen_t *addrlen, unsigned *number)
+{
+    char host[60];
+    unsigned fam, n1, n2;
 
-    /* read SAP packets */
-    for (;;)
+    int res = sscanf (str, "IN IP%u %59[^/]/%u/%u", &fam, host, &n1, &n2);
+    if (res < 2)
+        return -1;
+
+    switch (fam)
     {
-        vlc_restorecancel (canc);
-        unsigned n = p_sys->i_fd;
-        struct pollfd ufd[n];
+#ifdef AF_INET6
+        case 6:
+            addr->ss_family = AF_INET6;
+# ifdef HAVE_SA_LEN
+            addr->ss_len =
+# endif
+           *addrlen = sizeof (struct sockaddr_in6);
 
-        for (unsigned i = 0; i < n; i++)
-        {
-            ufd[i].fd = p_sys->pi_fd[i];
-            ufd[i].events = POLLIN;
-            ufd[i].revents = 0;
-        }
+            if (inet_pton (AF_INET6, host,
+                           &((struct sockaddr_in6 *)addr)->sin6_addr) <= 0)
+                return -1;
 
-        int val = poll (ufd, n, timeout);
-        canc = vlc_savecancel ();
-        if (val > 0)
-        {
-            for (unsigned i = 0; i < n; i++)
-            {
-                if (ufd[i].revents)
-                {
-                    uint8_t p_buffer[MAX_SAP_BUFFER+1];
-                    ssize_t i_read;
+            *number = (res >= 3) ? n1 : 1;
+            break;
+#endif
 
-                    i_read = recv (ufd[i].fd, p_buffer, MAX_SAP_BUFFER, 0);
-                    if (i_read < 0)
-                        msg_Warn (p_sd, "receive error: %s",
-                                  vlc_strerror_c(errno));
-                    if (i_read > 6)
-                    {
-                        /* Parse the packet */
-                        p_buffer[i_read] = '\0';
-                        ParseSAP (p_sd, p_buffer, i_read);
-                    }
-                }
-            }
-        }
+        case 4:
+            addr->ss_family = AF_INET;
+# ifdef HAVE_SA_LEN
+            addr->ss_len =
+# endif
+           *addrlen = sizeof (struct sockaddr_in);
 
-        vlc_tick_t now = vlc_tick_now();
+            if (inet_pton (AF_INET, host,
+                           &((struct sockaddr_in *)addr)->sin_addr) <= 0)
+                return -1;
 
-        /* A 1 hour timeout correspond to the RFC Implicit timeout.
-         * This timeout is tuned in the following loop. */
-        timeout = 1000 * 60 * 60;
-
-        /* Check for items that need deletion */
-        for( int i = 0; i < p_sys->i_announces; i++ )
-        {
-            sap_announce_t * p_announce = p_sys->pp_announces[i];
-            vlc_tick_t i_last_period = now - p_announce->i_last;
-
-            /* Remove the announcement, if the last announcement was 1 hour ago
-             * or if the last packet emitted was 10 times the average time
-             * between two packets */
-            if( ( p_announce->i_period_trust > 5 && i_last_period > 10 * p_announce->i_period ) ||
-                i_last_period > p_sys->i_timeout )
-            {
-                RemoveAnnounce( p_sd, p_announce );
-            }
-            else
-            {
-                /* Compute next timeout */
-                if( p_announce->i_period_trust > 5 )
-                    timeout = __MIN(MS_FROM_VLC_TICK(10 * p_announce->i_period - i_last_period), timeout);
-                timeout = __MIN(MS_FROM_VLC_TICK(p_sys->i_timeout - i_last_period), timeout);
-            }
-        }
+            *number = (res >= 4) ? n2 : 1;
+            break;
 
-        if( !p_sys->i_announces )
-            timeout = -1; /* We can safely poll indefinitely. */
-        else if( timeout < 200 )
-            timeout = 200; /* Don't wakeup too fast. */
+        default:
+            return -1;
     }
-    vlc_assert_unreachable ();
+    return 0;
 }
 
-/**********************************************************************
- * Demux: reads and demuxes data packets
- * Return -1 if error, 0 if EOF, 1 else
- **********************************************************************/
-static int Demux( demux_t *p_demux )
+static void net_SetPort(struct sockaddr *addr, uint16_t port)
 {
-    demux_sys_t *p_sys = p_demux->p_sys;
-    sdp_t *p_sdp = p_sys->p_sdp;
-    input_item_t *p_parent_input = p_demux->p_input_item;
-
-    if( !p_parent_input )
-    {
-        msg_Err( p_demux, "parent input could not be found" );
-        return VLC_EGENERIC;
-    }
-
-    input_item_SetURI(p_parent_input, p_sys->uri);
-    input_item_SetName( p_parent_input, p_sdp->psz_sessionname );
-    if( p_sdp->rtcp_port )
+    switch (addr->sa_family)
     {
-        char *rtcp;
-        if( asprintf( &rtcp, ":rtcp-port=%u", p_sdp->rtcp_port ) != -1 )
-        {
-            input_item_AddOption( p_parent_input, rtcp, VLC_INPUT_OPTION_TRUSTED );
-            free( rtcp );
-        }
+#ifdef AF_INET6
+        case AF_INET6:
+            ((struct sockaddr_in6 *)addr)->sin6_port = port;
+        break;
+#endif
+        case AF_INET:
+            ((struct sockaddr_in *)addr)->sin_port = port;
+        break;
     }
-
-    vlc_mutex_lock( &p_parent_input->lock );
-
-    p_parent_input->i_type = ITEM_TYPE_STREAM;
-    p_parent_input->b_net = true;
-
-    vlc_mutex_unlock( &p_parent_input->lock );
-    return VLC_SUCCESS;
 }
 
-static int Control( demux_t *p_demux, int i_query, va_list args )
+static inline void FreeAttribute (attribute_t *a)
 {
-    VLC_UNUSED(p_demux); VLC_UNUSED(i_query); VLC_UNUSED(args);
-    return VLC_EGENERIC;
+    free (a);
 }
 
-/**************************************************************
- * Local functions
- **************************************************************/
-
-/* i_read is at least > 6 */
-static int ParseSAP( services_discovery_t *p_sd, const uint8_t *buf,
-                     size_t len )
+static void FreeSDP( sdp_t *p_sdp )
 {
-    services_discovery_sys_t *p_sys = p_sd->p_sys;
-    const char          *psz_sdp;
-    const uint8_t *end = buf + len;
-    sdp_t               *p_sdp;
-    uint32_t            i_source[4];
-
-    assert (buf[len] == '\0');
-
-    if (len < 4)
-        return VLC_EGENERIC;
-
-    uint8_t flags = buf[0];
-    uint8_t auth_len = buf[1];
-
-    /* First, check the sap announce is correct */
-    if ((flags >> 5) != 1)
-        return VLC_EGENERIC;
-
-    bool b_ipv6 = (flags & 0x10) != 0;
-    bool b_need_delete = (flags & 0x04) != 0;
+    free( p_sdp->psz_sessionname );
+    free( p_sdp->psz_sessioninfo );
 
-    if (flags & 0x02)
+    for (unsigned j = 0; j < p_sdp->mediac; j++)
     {
-        msg_Dbg( p_sd, "encrypted packet, unsupported" );
-        return VLC_EGENERIC;
+        free (p_sdp->mediav[j].fmt);
+        for (int i = 0; i < p_sdp->mediav[j].i_attributes; i++)
+            FreeAttribute (p_sdp->mediav[j].pp_attributes[i]);
+        free (p_sdp->mediav[j].pp_attributes);
     }
+    free (p_sdp->mediav);
 
-    bool b_compressed = (flags & 0x01) != 0;
+    for (int i = 0; i < p_sdp->i_attributes; i++)
+        FreeAttribute (p_sdp->pp_attributes[i]);
 
-    uint16_t i_hash = U16_AT (buf + 2);
+    free (p_sdp->pp_attributes);
+    free (p_sdp);
+}
 
-    if( i_hash == 0 )
-        return VLC_EGENERIC;
+static inline attribute_t *MakeAttribute (const char *str)
+{
+    attribute_t *a = malloc (sizeof (*a) + strlen (str) + 1);
+    if (a == NULL)
+        return NULL;
 
-    buf += 4;
-    if( b_ipv6 )
+    strcpy (a->name, str);
+    EnsureUTF8 (a->name);
+    char *value = strchr (a->name, ':');
+    if (value != NULL)
     {
-        for( int i = 0; i < 4; i++,buf+=4)
-            i_source[i] = U32_AT(buf);
+        *value++ = '\0';
+        a->value = value;
     }
     else
-    {
-        memset(i_source, 0, sizeof(i_source));
-        i_source[3] = U32_AT(buf);
-        buf+=4;
-    }
-    // Skips auth data
-    buf += auth_len;
-    if (buf > end)
-        return VLC_EGENERIC;
+        a->value = "";
+    return a;
+}
 
-    uint8_t *decomp = NULL;
-    if( b_compressed )
-    {
-        int newsize = Decompress (buf, &decomp, end - buf);
-        if (newsize < 0)
-        {
-            msg_Dbg( p_sd, "decompression of SAP packet failed" );
-            return VLC_EGENERIC;
-        }
+/***********************************************************************
+ * ParseSDP : SDP parsing
+ * *********************************************************************
+ * Validate SDP and parse all fields
+ ***********************************************************************/
+static sdp_t *ParseSDP (vlc_object_t *p_obj, const char *psz_sdp)
+{
+    if( psz_sdp == NULL )
+        return NULL;
 
-        decomp = xrealloc (decomp, newsize + 1);
-        decomp[newsize] = '\0';
-        psz_sdp = (const char *)decomp;
-        len = newsize;
-    }
-    else
-    {
-        psz_sdp = (const char *)buf;
-        len = end - buf;
-    }
+    sdp_t *p_sdp = calloc (1, sizeof (*p_sdp));
+    if (p_sdp == NULL)
+        return NULL;
 
-    /* len is a strlen here here. both buf and decomp are len+1 where the 1 should be a \0 */
-    assert( psz_sdp[len] == '\0');
+    char expect = 'V';
+    struct sockaddr_storage glob_addr;
+    memset (&glob_addr, 0, sizeof (glob_addr));
+    socklen_t glob_len = 0;
+    unsigned glob_count = 1;
+    int port = 0;
 
-    /* Skip payload type */
-    /* SAPv1 has implicit "application/sdp" payload type: first line is v=0 */
-    if (strncmp (psz_sdp, "v=0", 3))
+    /* TODO: use iconv and charset attribute instead of EnsureUTF8 */
+    while (*psz_sdp)
     {
-        size_t clen = strlen (psz_sdp) + 1;
-
-        if (strcmp (psz_sdp, "application/sdp"))
-        {
-            msg_Dbg (p_sd, "unsupported content type: %s", psz_sdp);
-            goto error;
-        }
-
-        // skips content type
-        if (len <= clen)
+        /* Extract one line */
+        size_t linelen = strcspn(psz_sdp, "\n");
+        if (psz_sdp[linelen] == '\0')
             goto error;
 
-        len -= clen;
-        psz_sdp += clen;
-    }
-
-    /* Parse SDP info */
-    p_sdp = ParseSDP( VLC_OBJECT(p_sd), psz_sdp );
+        char line[linelen + 1];
+        memcpy (line, psz_sdp, linelen);
+        line[linelen] = '\0';
 
-    if( p_sdp == NULL )
-        goto error;
+        psz_sdp += linelen + 1;
 
-    p_sdp->psz_sdp = psz_sdp;
+        /* Remove carriage return if present */
+        char *eol = strchr (line, '\r');
+        if (eol != NULL)
+        {
+            linelen = eol - line;
+            line[linelen] = '\0';
+        }
 
-    /* Decide whether we should add a playlist item for this SDP */
-    /* Parse connection information (c= & m= ) */
-    char *uri = ParseConnection( VLC_OBJECT(p_sd), p_sdp );
+        /* Validate line */
+        char cat = line[0], *data = line + 2;
+        if (!cat || (strchr ("vosiuepcbtrzkam", cat) == NULL))
+        {
+            /* MUST ignore SDP with unknown line type */
+            msg_Dbg (p_obj, "unknown SDP line type: 0x%02x", (int)cat);
+            goto error;
+        }
+        if (line[1] != '=')
+        {
+            msg_Dbg (p_obj, "invalid SDP line: %s", line);
+            goto error;
+        }
 
-    /* Multi-media or no-parse -> pass to LIVE.COM */
-    if( !p_sys->b_parse )
-    {
-        free(uri);
-        if (asprintf(&uri, "sdp://%s", p_sdp->psz_sdp) == -1)
-            uri = NULL;
-    }
+        assert (linelen >= 2);
 
-    if (uri == NULL)
+        /* SDP parsing state machine
+         * We INTERNALLY use uppercase for session, lowercase for media
+         */
+        switch (expect)
+        {
+            /* Session description */
+            case 'V':
+                expect = 'O';
+                if (cat != 'v')
+                {
+                    msg_Dbg (p_obj, "missing SDP version");
+                    goto error;
+                }
+                if (strcmp (data, "0"))
+                {
+                    msg_Dbg (p_obj, "unknown SDP version: %s", data);
+                    goto error;
+                }
+                break;
+
+            case 'O':
+                expect = 'S';
+                if (cat != 'o')
+                {
+                    msg_Dbg (p_obj, "missing SDP originator");
+                    goto error;
+                }
+
+                if (sscanf(data, "%63s %*u %*u IN %*s %*s%n",
+                           p_sdp->username, &(int){ 0 }) != 2)
+                {
+                    msg_Dbg (p_obj, "SDP origin not supported: %s", data);
+                    /* Or maybe out-of-range, but this looks suspicious */
+                    goto error;
+                }
+                break;
+
+            case 'S':
+                expect = 'I';
+                if ((cat != 's') || !*data)
+                {
+                    /* MUST be present AND non-empty */
+                    msg_Dbg (p_obj, "missing SDP session name");
+                    goto error;
+                }
+                assert (p_sdp->psz_sessionname == NULL); // no memleak here
+                p_sdp->psz_sessionname = strdup (data);
+                if (p_sdp->psz_sessionname == NULL)
+                    goto error;
+                EnsureUTF8 (p_sdp->psz_sessionname);
+                break;
+
+            case 'I':
+                expect = 'U';
+                /* optional (and may be empty) */
+                if (cat == 'i')
+                {
+                    assert (p_sdp->psz_sessioninfo == NULL);
+                    p_sdp->psz_sessioninfo = strdup (data);
+                    if (p_sdp->psz_sessioninfo == NULL)
+                        goto error;
+                    EnsureUTF8 (p_sdp->psz_sessioninfo);
+                    break;
+                }
+                /* fall through */
+
+            case 'U':
+                expect = 'E';
+                if (cat == 'u')
+                    break;
+                /* fall through */
+            case 'E':
+                expect = 'E';
+                if (cat == 'e')
+                    break;
+                /* fall through */
+            case 'P':
+                expect = 'P';
+                if (cat == 'p')
+                    break;
+                /* fall through */
+            case 'C':
+                expect = 'B';
+                if (cat == 'c')
+                {
+                    if (ParseSDPConnection (data, &glob_addr, &glob_len,
+                                            &glob_count))
+                    {
+                        msg_Dbg (p_obj, "SDP connection infos not supported: "
+                                 "%s", data);
+                        goto error;
+                    }
+                    break;
+                }
+                /* fall through */
+            case 'B':
+                assert (expect == 'B');
+                if (cat == 'b')
+                    break;
+                /* fall through */
+            case 'T':
+                expect = 'R';
+                if (cat != 't')
+                {
+                    msg_Dbg (p_obj, "missing SDP time description");
+                    goto error;
+                }
+                break;
+
+            case 'R':
+                if ((cat == 't') || (cat == 'r'))
+                    break;
+                /* fall through */
+            case 'Z':
+                expect = 'K';
+                if (cat == 'z')
+                    break;
+                /* fall through */
+            case 'K':
+                expect = 'A';
+                if (cat == 'k')
+                    break;
+                /* fall through */
+            case 'A':
+                //expect = 'A';
+                if (cat == 'a')
+                {
+                    attribute_t *p_attr = MakeAttribute (data);
+                    TAB_APPEND( p_sdp->i_attributes, p_sdp->pp_attributes, p_attr );
+                    break;
+                }
+                /* fall through */
+
+            /* Media description */
+            case 'm':
+            media:
+            {
+                expect = 'i';
+                if (cat != 'm')
+                {
+                    msg_Dbg (p_obj, "missing SDP media description");
+                    goto error;
+                }
+                struct sdp_media_t *m;
+                m = realloc (p_sdp->mediav, (p_sdp->mediac + 1) * sizeof (*m));
+                if (m == NULL)
+                    goto error;
+
+                p_sdp->mediav = m;
+                m += p_sdp->mediac;
+                p_sdp->mediac++;
+
+                memset (m, 0, sizeof (*m));
+                memcpy (&m->addr, &glob_addr, m->addrlen = glob_len);
+                m->n_addr = glob_count;
+
+                /* TODO: remember media type (if we need multiple medias) */
+                data = strchr (data, ' ');
+                if (data == NULL)
+                {
+                    msg_Dbg (p_obj, "missing SDP media port");
+                    goto error;
+                }
+                port = atoi (++data);
+                if (port <= 0 || port >= 65536)
+                {
+                    msg_Dbg (p_obj, "invalid transport port %d", port);
+                    goto error;
+                }
+                net_SetPort ((struct sockaddr *)&m->addr, htons (port));
+
+                data = strchr (data, ' ');
+                if (data == NULL)
+                {
+                    msg_Dbg (p_obj, "missing SDP media format");
+                    goto error;
+                }
+                m->fmt = strdup (++data);
+                if (m->fmt == NULL)
+                    goto error;
+
+                break;
+            }
+
+            case 'i':
+                expect = 'c';
+                if (cat == 'i')
+                    break;
+                /* fall through */
+            case 'c':
+                expect = 'b';
+                if (cat == 'c')
+                {
+                    struct sdp_media_t *m = p_sdp->mediav + p_sdp->mediac - 1;
+                    if (ParseSDPConnection (data, &m->addr, &m->addrlen,
+                                            &m->n_addr))
+                    {
+                        msg_Dbg (p_obj, "SDP connection infos not supported: "
+                                 "%s", data);
+                        goto error;
+                    }
+                    net_SetPort ((struct sockaddr *)&m->addr, htons (port));
+                    break;
+                }
+                /* fall through */
+            case 'b':
+                expect = 'b';
+                if (cat == 'b')
+                    break;
+                /* fall through */
+            case 'k':
+                expect = 'a';
+                if (cat == 'k')
+                    break;
+                /* fall through */
+            case 'a':
+                assert (expect == 'a');
+                if (cat == 'a')
+                {
+                    attribute_t *p_attr = MakeAttribute (data);
+                    if (p_attr == NULL)
+                        goto error;
+
+                    TAB_APPEND (p_sdp->mediav[p_sdp->mediac - 1].i_attributes,
+                                p_sdp->mediav[p_sdp->mediac - 1].pp_attributes, p_attr);
+                    break;
+                }
+
+                if (cat == 'm')
+                    goto media;
+
+                msg_Dbg (p_obj, "unexpected SDP line: 0x%02x", (int)cat);
+                goto error;
+
+            default:
+                msg_Err (p_obj, "*** BUG in SDP parser! ***");
+                goto error;
+        }
+    }
+
+    return p_sdp;
+
+error:
+    FreeSDP (p_sdp);
+    return NULL;
+}
+
+static int Decompress( const unsigned char *psz_src, unsigned char **_dst, int i_len )
+{
+#ifdef HAVE_ZLIB_H
+    int i_result, i_dstsize, n = 0;
+    unsigned char *psz_dst = NULL;
+    z_stream d_stream;
+
+    memset (&d_stream, 0, sizeof (d_stream));
+
+    i_result = inflateInit(&d_stream);
+    if( i_result != Z_OK )
+        return( -1 );
+
+    d_stream.next_in = (Bytef *)psz_src;
+    d_stream.avail_in = i_len;
+
+    do
     {
-        FreeSDP( p_sdp );
-        goto error;
+        n++;
+        psz_dst = xrealloc( psz_dst, n * 1000 );
+        d_stream.next_out = (Bytef *)&psz_dst[(n - 1) * 1000];
+        d_stream.avail_out = 1000;
+
+        i_result = inflate(&d_stream, Z_NO_FLUSH);
+        if( ( i_result != Z_OK ) && ( i_result != Z_STREAM_END ) )
+        {
+            inflateEnd( &d_stream );
+            free( psz_dst );
+            return( -1 );
+        }
     }
+    while( ( d_stream.avail_out == 0 ) && ( d_stream.avail_in != 0 ) &&
+           ( i_result != Z_STREAM_END ) );
 
-    for( int i = 0 ; i < p_sys->i_announces ; i++ )
+    i_dstsize = d_stream.total_out;
+    inflateEnd( &d_stream );
+
+    *_dst = xrealloc( psz_dst, i_dstsize );
+
+    return i_dstsize;
+#else
+    (void)psz_src;
+    (void)_dst;
+    (void)i_len;
+    return -1;
+#endif
+}
+
+typedef struct
+{
+    sdp_t *p_sdp;
+    /* "computed" URI */
+    char *uri;
+} demux_sys_t;
+
+/**********************************************************************
+ * Demux: reads and demuxes data packets
+ * Return -1 if error, 0 if EOF, 1 else
+ **********************************************************************/
+static int Demux( demux_t *p_demux )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    sdp_t *p_sdp = p_sys->p_sdp;
+    input_item_t *p_parent_input = p_demux->p_input_item;
+
+    if( !p_parent_input )
     {
-        sap_announce_t * p_announce = p_sys->pp_announces[i];
-        /* FIXME: slow */
-        if (p_announce->i_hash == i_hash
-         && memcmp(p_announce->i_source, i_source, sizeof (i_source)) == 0)
+        msg_Err( p_demux, "parent input could not be found" );
+        return VLC_EGENERIC;
+    }
+
+    input_item_SetURI(p_parent_input, p_sys->uri);
+    input_item_SetName( p_parent_input, p_sdp->psz_sessionname );
+    if( p_sdp->rtcp_port )
+    {
+        char *rtcp;
+        if( asprintf( &rtcp, ":rtcp-port=%u", p_sdp->rtcp_port ) != -1 )
         {
-            /* We don't support delete announcement as they can easily
-             * Be used to highjack an announcement by a third party.
-             * Instead we cleverly implement Implicit Announcement removal.
-             *
-             * if( b_need_delete )
-             *    RemoveAnnounce( p_sd, p_sys->pp_announces[i]);
-             * else
-             */
+            input_item_AddOption( p_parent_input, rtcp, VLC_INPUT_OPTION_TRUSTED );
+            free( rtcp );
+        }
+    }
 
-            if( !b_need_delete )
-            {
-                /* No need to go after six, as we start to trust the
-                 * average period at six */
-                if( p_announce->i_period_trust <= 5 )
-                    p_announce->i_period_trust++;
+    vlc_mutex_lock( &p_parent_input->lock );
 
-                /* Compute the average period */
-                vlc_tick_t now = vlc_tick_now();
-                p_announce->i_period = ( p_announce->i_period * (p_announce->i_period_trust-1) + (now - p_announce->i_last) ) / p_announce->i_period_trust;
-                p_announce->i_last = now;
-            }
-            FreeSDP( p_sdp );
-            free(uri);
-            free (decomp);
-            return VLC_SUCCESS;
+    p_parent_input->i_type = ITEM_TYPE_STREAM;
+    p_parent_input->b_net = true;
+
+    vlc_mutex_unlock( &p_parent_input->lock );
+    return VLC_SUCCESS;
+}
+
+static int Control( demux_t *p_demux, int i_query, va_list args )
+{
+    VLC_UNUSED(p_demux); VLC_UNUSED(i_query); VLC_UNUSED(args);
+    return VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * OpenDemux: initialize and create stuff
+ *****************************************************************************/
+static int OpenDemux( vlc_object_t *p_this )
+{
+    demux_t *p_demux = (demux_t *)p_this;
+    const uint8_t *p_peek;
+    char *psz_sdp = NULL;
+    sdp_t *p_sdp = NULL;
+    int errval = VLC_EGENERIC;
+    size_t i_len;
+
+    if( !var_CreateGetBool( p_demux, "sap-parse" ) )
+    {
+        /* We want livedotcom module to parse this SDP file */
+        return VLC_EGENERIC;
+    }
+
+    assert( p_demux->s ); /* this is NOT an access_demux */
+
+    /* Probe for SDP */
+    if( vlc_stream_Peek( p_demux->s, &p_peek, 7 ) < 7 )
+        return VLC_EGENERIC;
+
+    if( memcmp( p_peek, "v=0\r\no=", 7 ) && memcmp( p_peek, "v=0\no=", 6 ) )
+        return VLC_EGENERIC;
+
+    /* Gather the complete sdp file */
+    for( i_len = 0, psz_sdp = NULL; i_len < 65536; )
+    {
+        const int i_read_max = 1024;
+        char *psz_sdp_new = realloc( psz_sdp, i_len + i_read_max + 1 );
+        size_t i_read;
+        if( psz_sdp_new == NULL )
+        {
+            errval = VLC_ENOMEM;
+            goto error;
+        }
+        psz_sdp = psz_sdp_new;
+
+        i_read = vlc_stream_Read( p_demux->s, &psz_sdp[i_len], i_read_max );
+        if( (int)i_read < 0 )
+        {
+            msg_Err( p_demux, "cannot read SDP" );
+            goto error;
         }
+        i_len += i_read;
+
+        psz_sdp[i_len] = '\0';
+
+        if( (int)i_read < i_read_max )
+            break; // EOF
+    }
+
+    p_sdp = ParseSDP( VLC_OBJECT(p_demux), psz_sdp );
+
+    if( !p_sdp )
+    {
+        msg_Warn( p_demux, "invalid SDP");
+        goto error;
     }
 
-    sap_announce_t *sap = CreateAnnounce(p_sd, i_source, i_hash, p_sdp, uri);
+    char *uri = ParseConnection(VLC_OBJECT(p_demux), p_sdp);
+    if (uri == NULL)
+        goto error;
 
-    if (sap != NULL)
-        TAB_APPEND(p_sys->i_announces, p_sys->pp_announces, sap);
+    demux_sys_t *p_sys = malloc( sizeof(*p_sys) );
+    if( unlikely(p_sys == NULL) )
+        goto error;
+    p_sys->p_sdp = p_sdp;
+    p_sys->uri = uri;
+    p_demux->p_sys = p_sys;
+    p_demux->pf_control = Control;
+    p_demux->pf_demux = Demux;
 
-    free(uri);
-    free (decomp);
+    FREENULL( psz_sdp );
     return VLC_SUCCESS;
+
 error:
-    free (decomp);
-    return VLC_EGENERIC;
+    FREENULL( psz_sdp );
+    if( p_sdp ) FreeSDP( p_sdp );
+    return errval;
+}
+
+/*****************************************************************************
+ * CloseDemux: Close the demuxer
+ *****************************************************************************/
+static void CloseDemux( vlc_object_t *p_this )
+{
+    demux_t *p_demux = (demux_t *)p_this;
+    demux_sys_t *sys = p_demux->p_sys;
+
+    if( sys->p_sdp )
+        FreeSDP( sys->p_sdp );
+    free(sys->uri);
+    free( sys );
 }
 
+typedef struct sap_announce_t
+{
+    vlc_tick_t i_last;
+    vlc_tick_t i_period;
+    uint8_t i_period_trust;
+
+    uint16_t    i_hash;
+    uint32_t    i_source[4];
+
+    /* SAP annnounces must only contain one SDP */
+    sdp_t       *p_sdp;
+
+    input_item_t * p_item;
+} sap_announce_t;
+
 sap_announce_t *CreateAnnounce( services_discovery_t *p_sd, uint32_t *i_source, uint16_t i_hash,
                                 sdp_t *p_sdp, const char *uri )
 {
@@ -831,712 +1045,450 @@ sap_announce_t *CreateAnnounce( services_discovery_t *p_sd, uint32_t *i_source,
     return p_sap;
 }
 
-
-static const char *FindAttribute (const sdp_t *sdp, unsigned media,
-                                  const char *name)
-{
-    /* Look for media attribute, and fallback to session */
-    const char *attr = GetAttribute (sdp->mediav[media].pp_attributes,
-                                     sdp->mediav[media].i_attributes, name);
-    if (attr == NULL)
-        attr = GetAttribute (sdp->pp_attributes, sdp->i_attributes, name);
-    return attr;
-}
-
-
-/* Compute URI */
-static char *ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp )
+typedef struct
 {
-    char *uri;
-
-    if (p_sdp->mediac == 0)
-    {
-        msg_Dbg (p_obj, "Ignoring SDP with no media");
-        return NULL;
-    }
-
-    for (unsigned i = 1; i < p_sdp->mediac; i++)
-    {
-        if ((p_sdp->mediav[i].n_addr != p_sdp->mediav->n_addr)
-         || (p_sdp->mediav[i].addrlen != p_sdp->mediav->addrlen)
-         || memcmp (&p_sdp->mediav[i].addr, &p_sdp->mediav->addr,
-                    p_sdp->mediav->addrlen))
-        {
-            msg_Dbg (p_obj, "Multiple media ports not supported -> live555");
-            return NULL;
-        }
-    }
-
-    if (p_sdp->mediav->n_addr != 1)
-    {
-        msg_Dbg (p_obj, "Layered encoding not supported -> live555");
-        return NULL;
-    }
-
-    char psz_uri[1026];
-    const char *host;
-    int port;
-
-    psz_uri[0] = '[';
-    if (vlc_getnameinfo ((struct sockaddr *)&(p_sdp->mediav->addr),
-                         p_sdp->mediav->addrlen, psz_uri + 1,
-                         sizeof (psz_uri) - 2, &port, NI_NUMERICHOST))
-        return NULL;
-
-    if (strchr (psz_uri + 1, ':'))
-    {
-        host = psz_uri;
-        strcat (psz_uri, "]");
-    }
-    else
-        host = psz_uri + 1;
-
-    /* Parse m= field */
-    char *sdp_proto = strdup (p_sdp->mediav[0].fmt);
-    if (sdp_proto == NULL)
-        return NULL;
-
-    char *subtype = strchr (sdp_proto, ' ');
-    if (subtype == NULL)
-    {
-        msg_Dbg (p_obj, "missing SDP media subtype: %s", sdp_proto);
-        free (sdp_proto);
-        return NULL;
-    }
+    vlc_thread_t thread;
 
-    *subtype++ = '\0';
-    /* FIXME: check for multiple payload types in RTP/AVP case.
-     * FIXME: check for "mpeg" subtype in raw udp case. */
-    if (strcasecmp(sdp_proto, "udp") != 0
-     && !IsWellKnownPayload(atoi(subtype)))
-    {
-        free(sdp_proto);
-        return NULL;
-    }
+    /* Socket descriptors */
+    int i_fd;
+    int *pi_fd;
 
-    /* RTP protocol, nul, VLC shortcut, nul, flags byte as follow:
-     * 0x1: Connection-Oriented media. */
-    static const char proto_match[] =
-        "udp\0"             "udp\0\0"
-        "RTP/AVP\0"         "rtp\0\0"
-        "UDPLite/RTP/AVP\0" "udplite\0\0"
-        "DCCP/RTP/AVP\0"    "dccp\0\1"
-        "TCP/RTP/AVP\0"     "rtptcp\0\1"
-        "\0";
+    /* Table of announces */
+    int i_announces;
+    struct sap_announce_t **pp_announces;
 
-    const char *vlc_proto = NULL;
-    uint8_t flags = 0;
-    for (const char *proto = proto_match; *proto;)
-    {
-        if (strcasecmp (proto, sdp_proto) == 0)
-        {
-            vlc_proto = proto + strlen (proto) + 1;
-            flags = vlc_proto[strlen (vlc_proto) + 1];
-            break;
-        }
-        proto += strlen (proto) + 1;
-        proto += strlen (proto) + 2;
-    }
+    /* Modes */
+    bool  b_parse;
 
-    free (sdp_proto);
-    if (vlc_proto == NULL)
-    {
-        msg_Dbg (p_obj, "unknown SDP media protocol: %s",
-                 p_sdp->mediav[0].fmt);
-        return NULL;
-    }
+    vlc_tick_t i_timeout;
+} services_discovery_sys_t;
 
-    if (!strcmp (vlc_proto, "udp") || FindAttribute (p_sdp, 0, "rtcp-mux"))
-        p_sdp->rtcp_port = 0;
-    else
+static int RemoveAnnounce( services_discovery_t *p_sd,
+                           sap_announce_t *p_announce )
+{
+    if( p_announce->p_sdp )
     {
-        const char *rtcp = FindAttribute (p_sdp, 0, "rtcp");
-        if (rtcp)
-            p_sdp->rtcp_port = atoi (rtcp);
-        else
-        if (port & 1) /* odd port -> RTCP; next even port -> RTP */
-            p_sdp->rtcp_port = port++;
-        else /* even port -> RTP; next odd port -> RTCP */
-            p_sdp->rtcp_port = port + 1;
+        FreeSDP( p_announce->p_sdp );
+        p_announce->p_sdp = NULL;
     }
 
-    if (flags & 1)
-    {
-        /* Connection-oriented media */
-        const char *setup = FindAttribute (p_sdp, 0, "setup");
-        if (setup == NULL)
-            setup = "active"; /* default value */
-
-        if (strcmp (setup, "actpass") && strcmp (setup, "passive"))
-        {
-            msg_Dbg (p_obj, "unsupported COMEDIA mode: %s", setup);
-            return NULL;
-        }
-
-        if (asprintf(&uri, "%s://%s:%d", vlc_proto, host, port) == -1)
-            return NULL;
-    }
-    else
+    if( p_announce->p_item )
     {
-        /* Non-connected (normally multicast) media */
-        char psz_source[258] = "";
-        const char *sfilter = FindAttribute (p_sdp, 0, "source-filter");
-        if (sfilter != NULL)
-        {
-            char psz_source_ip[256];
-            unsigned ipv;
-
-            if (sscanf (sfilter, " incl IN IP%u %*s %255s ", &ipv,
-                        psz_source_ip) == 2)
-            {
-                /* According to RFC4570, FQDNs can be used for source-filters,
-                 * but -seriously- this is impractical */
-                switch (ipv)
-                {
-#ifdef AF_INET6
-                    case 6:
-                    {
-                        struct in6_addr addr;
-                        if ((inet_pton (AF_INET6, psz_source_ip, &addr) > 0)
-                        && (inet_ntop (AF_INET6, &addr, psz_source + 1,
-                                        sizeof (psz_source) - 2) != NULL))
-                        {
-                            psz_source[0] = '[';
-                            psz_source[strlen (psz_source)] = ']';
-                        }
-                        break;
-                    }
-#endif
-                    case 4:
-                    {
-                        struct in_addr addr;
-                        if ((inet_pton (AF_INET, psz_source_ip, &addr) > 0)
-                        && (inet_ntop (AF_INET, &addr, psz_source,
-                                        sizeof (psz_source)) == NULL))
-                            *psz_source = '\0';
-                        break;
-                    }
-                }
-            }
-        }
-
-        if (asprintf(&uri, "%s://%s@%s:%i", vlc_proto, psz_source,
-                     host, port) == -1)
-            return NULL;
+        services_discovery_RemoveItem( p_sd, p_announce->p_item );
+        input_item_Release( p_announce->p_item );
+        p_announce->p_item = NULL;
     }
 
-    return uri;
-}
+    services_discovery_sys_t *p_sys = p_sd->p_sys;
+    TAB_REMOVE(p_sys->i_announces, p_sys->pp_announces, p_announce);
+    free( p_announce );
 
+    return VLC_SUCCESS;
+}
 
-static int ParseSDPConnection (const char *str, struct sockaddr_storage *addr,
-                               socklen_t *addrlen, unsigned *number)
+static int InitSocket( services_discovery_t *p_sd, const char *psz_address,
+                       int i_port )
 {
-    char host[60];
-    unsigned fam, n1, n2;
-
-    int res = sscanf (str, "IN IP%u %59[^/]/%u/%u", &fam, host, &n1, &n2);
-    if (res < 2)
-        return -1;
+    int i_fd = net_ListenUDP1 ((vlc_object_t *)p_sd, psz_address, i_port);
+    if (i_fd == -1)
+        return VLC_EGENERIC;
 
-    switch (fam)
-    {
-#ifdef AF_INET6
-        case 6:
-            addr->ss_family = AF_INET6;
-# ifdef HAVE_SA_LEN
-            addr->ss_len =
-# endif
-           *addrlen = sizeof (struct sockaddr_in6);
+    shutdown( i_fd, SHUT_WR );
+    services_discovery_sys_t *p_sys = p_sd->p_sys;
+    TAB_APPEND(p_sys->i_fd, p_sys->pi_fd, i_fd);
+    return VLC_SUCCESS;
+}
 
-            if (inet_pton (AF_INET6, host,
-                           &((struct sockaddr_in6 *)addr)->sin6_addr) <= 0)
-                return -1;
+/* i_read is at least > 6 */
+static int ParseSAP( services_discovery_t *p_sd, const uint8_t *buf,
+                     size_t len )
+{
+    services_discovery_sys_t *p_sys = p_sd->p_sys;
+    const char          *psz_sdp;
+    const uint8_t *end = buf + len;
+    sdp_t               *p_sdp;
+    uint32_t            i_source[4];
 
-            *number = (res >= 3) ? n1 : 1;
-            break;
-#endif
+    assert (buf[len] == '\0');
 
-        case 4:
-            addr->ss_family = AF_INET;
-# ifdef HAVE_SA_LEN
-            addr->ss_len =
-# endif
-           *addrlen = sizeof (struct sockaddr_in);
+    if (len < 4)
+        return VLC_EGENERIC;
 
-            if (inet_pton (AF_INET, host,
-                           &((struct sockaddr_in *)addr)->sin_addr) <= 0)
-                return -1;
+    uint8_t flags = buf[0];
+    uint8_t auth_len = buf[1];
 
-            *number = (res >= 4) ? n2 : 1;
-            break;
+    /* First, check the sap announce is correct */
+    if ((flags >> 5) != 1)
+        return VLC_EGENERIC;
 
-        default:
-            return -1;
-    }
-    return 0;
-}
+    bool b_ipv6 = (flags & 0x10) != 0;
+    bool b_need_delete = (flags & 0x04) != 0;
 
-static void net_SetPort(struct sockaddr *addr, uint16_t port)
-{
-    switch (addr->sa_family)
+    if (flags & 0x02)
     {
-#ifdef AF_INET6
-        case AF_INET6:
-            ((struct sockaddr_in6 *)addr)->sin6_port = port;
-        break;
-#endif
-        case AF_INET:
-            ((struct sockaddr_in *)addr)->sin_port = port;
-        break;
+        msg_Dbg( p_sd, "encrypted packet, unsupported" );
+        return VLC_EGENERIC;
     }
-}
 
-/***********************************************************************
- * ParseSDP : SDP parsing
- * *********************************************************************
- * Validate SDP and parse all fields
- ***********************************************************************/
-static sdp_t *ParseSDP (vlc_object_t *p_obj, const char *psz_sdp)
-{
-    if( psz_sdp == NULL )
-        return NULL;
+    bool b_compressed = (flags & 0x01) != 0;
 
-    sdp_t *p_sdp = calloc (1, sizeof (*p_sdp));
-    if (p_sdp == NULL)
-        return NULL;
+    uint16_t i_hash = U16_AT (buf + 2);
 
-    char expect = 'V';
-    struct sockaddr_storage glob_addr;
-    memset (&glob_addr, 0, sizeof (glob_addr));
-    socklen_t glob_len = 0;
-    unsigned glob_count = 1;
-    int port = 0;
+    if( i_hash == 0 )
+        return VLC_EGENERIC;
 
-    /* TODO: use iconv and charset attribute instead of EnsureUTF8 */
-    while (*psz_sdp)
+    buf += 4;
+    if( b_ipv6 )
     {
-        /* Extract one line */
-        size_t linelen = strcspn(psz_sdp, "\n");
-        if (psz_sdp[linelen] == '\0')
-            goto error;
-
-        char line[linelen + 1];
-        memcpy (line, psz_sdp, linelen);
-        line[linelen] = '\0';
-
-        psz_sdp += linelen + 1;
+        for( int i = 0; i < 4; i++,buf+=4)
+            i_source[i] = U32_AT(buf);
+    }
+    else
+    {
+        memset(i_source, 0, sizeof(i_source));
+        i_source[3] = U32_AT(buf);
+        buf+=4;
+    }
+    // Skips auth data
+    buf += auth_len;
+    if (buf > end)
+        return VLC_EGENERIC;
 
-        /* Remove carriage return if present */
-        char *eol = strchr (line, '\r');
-        if (eol != NULL)
+    uint8_t *decomp = NULL;
+    if( b_compressed )
+    {
+        int newsize = Decompress (buf, &decomp, end - buf);
+        if (newsize < 0)
         {
-            linelen = eol - line;
-            line[linelen] = '\0';
+            msg_Dbg( p_sd, "decompression of SAP packet failed" );
+            return VLC_EGENERIC;
         }
 
-        /* Validate line */
-        char cat = line[0], *data = line + 2;
-        if (!cat || (strchr ("vosiuepcbtrzkam", cat) == NULL))
-        {
-            /* MUST ignore SDP with unknown line type */
-            msg_Dbg (p_obj, "unknown SDP line type: 0x%02x", (int)cat);
-            goto error;
-        }
-        if (line[1] != '=')
-        {
-            msg_Dbg (p_obj, "invalid SDP line: %s", line);
-            goto error;
-        }
+        decomp = xrealloc (decomp, newsize + 1);
+        decomp[newsize] = '\0';
+        psz_sdp = (const char *)decomp;
+        len = newsize;
+    }
+    else
+    {
+        psz_sdp = (const char *)buf;
+        len = end - buf;
+    }
 
-        assert (linelen >= 2);
+    /* len is a strlen here here. both buf and decomp are len+1 where the 1 should be a \0 */
+    assert( psz_sdp[len] == '\0');
 
-        /* SDP parsing state machine
-         * We INTERNALLY use uppercase for session, lowercase for media
-         */
-        switch (expect)
+    /* Skip payload type */
+    /* SAPv1 has implicit "application/sdp" payload type: first line is v=0 */
+    if (strncmp (psz_sdp, "v=0", 3))
+    {
+        size_t clen = strlen (psz_sdp) + 1;
+
+        if (strcmp (psz_sdp, "application/sdp"))
         {
-            /* Session description */
-            case 'V':
-                expect = 'O';
-                if (cat != 'v')
-                {
-                    msg_Dbg (p_obj, "missing SDP version");
-                    goto error;
-                }
-                if (strcmp (data, "0"))
-                {
-                    msg_Dbg (p_obj, "unknown SDP version: %s", data);
-                    goto error;
-                }
-                break;
+            msg_Dbg (p_sd, "unsupported content type: %s", psz_sdp);
+            goto error;
+        }
 
-            case 'O':
-                expect = 'S';
-                if (cat != 'o')
-                {
-                    msg_Dbg (p_obj, "missing SDP originator");
-                    goto error;
-                }
+        // skips content type
+        if (len <= clen)
+            goto error;
 
-                if (sscanf(data, "%63s %*u %*u IN %*s %*s%n",
-                           p_sdp->username, &(int){ 0 }) != 2)
-                {
-                    msg_Dbg (p_obj, "SDP origin not supported: %s", data);
-                    /* Or maybe out-of-range, but this looks suspicious */
-                    goto error;
-                }
-                break;
+        len -= clen;
+        psz_sdp += clen;
+    }
 
-            case 'S':
-                expect = 'I';
-                if ((cat != 's') || !*data)
-                {
-                    /* MUST be present AND non-empty */
-                    msg_Dbg (p_obj, "missing SDP session name");
-                    goto error;
-                }
-                assert (p_sdp->psz_sessionname == NULL); // no memleak here
-                p_sdp->psz_sessionname = strdup (data);
-                if (p_sdp->psz_sessionname == NULL)
-                    goto error;
-                EnsureUTF8 (p_sdp->psz_sessionname);
-                break;
+    /* Parse SDP info */
+    p_sdp = ParseSDP( VLC_OBJECT(p_sd), psz_sdp );
 
-            case 'I':
-                expect = 'U';
-                /* optional (and may be empty) */
-                if (cat == 'i')
-                {
-                    assert (p_sdp->psz_sessioninfo == NULL);
-                    p_sdp->psz_sessioninfo = strdup (data);
-                    if (p_sdp->psz_sessioninfo == NULL)
-                        goto error;
-                    EnsureUTF8 (p_sdp->psz_sessioninfo);
-                    break;
-                }
-                /* fall through */
+    if( p_sdp == NULL )
+        goto error;
 
-            case 'U':
-                expect = 'E';
-                if (cat == 'u')
-                    break;
-                /* fall through */
-            case 'E':
-                expect = 'E';
-                if (cat == 'e')
-                    break;
-                /* fall through */
-            case 'P':
-                expect = 'P';
-                if (cat == 'p')
-                    break;
-                /* fall through */
-            case 'C':
-                expect = 'B';
-                if (cat == 'c')
-                {
-                    if (ParseSDPConnection (data, &glob_addr, &glob_len,
-                                            &glob_count))
-                    {
-                        msg_Dbg (p_obj, "SDP connection infos not supported: "
-                                 "%s", data);
-                        goto error;
-                    }
-                    break;
-                }
-                /* fall through */
-            case 'B':
-                assert (expect == 'B');
-                if (cat == 'b')
-                    break;
-                /* fall through */
-            case 'T':
-                expect = 'R';
-                if (cat != 't')
-                {
-                    msg_Dbg (p_obj, "missing SDP time description");
-                    goto error;
-                }
-                break;
+    p_sdp->psz_sdp = psz_sdp;
 
-            case 'R':
-                if ((cat == 't') || (cat == 'r'))
-                    break;
-                /* fall through */
-            case 'Z':
-                expect = 'K';
-                if (cat == 'z')
-                    break;
-                /* fall through */
-            case 'K':
-                expect = 'A';
-                if (cat == 'k')
-                    break;
-                /* fall through */
-            case 'A':
-                //expect = 'A';
-                if (cat == 'a')
-                {
-                    attribute_t *p_attr = MakeAttribute (data);
-                    TAB_APPEND( p_sdp->i_attributes, p_sdp->pp_attributes, p_attr );
-                    break;
-                }
-                /* fall through */
+    /* Decide whether we should add a playlist item for this SDP */
+    /* Parse connection information (c= & m= ) */
+    char *uri = ParseConnection( VLC_OBJECT(p_sd), p_sdp );
 
-            /* Media description */
-            case 'm':
-            media:
+    /* Multi-media or no-parse -> pass to LIVE.COM */
+    if( !p_sys->b_parse )
+    {
+        free(uri);
+        if (asprintf(&uri, "sdp://%s", p_sdp->psz_sdp) == -1)
+            uri = NULL;
+    }
+
+    if (uri == NULL)
+    {
+        FreeSDP( p_sdp );
+        goto error;
+    }
+
+    for( int i = 0 ; i < p_sys->i_announces ; i++ )
+    {
+        sap_announce_t * p_announce = p_sys->pp_announces[i];
+        /* FIXME: slow */
+        if (p_announce->i_hash == i_hash
+         && memcmp(p_announce->i_source, i_source, sizeof (i_source)) == 0)
+        {
+            /* We don't support delete announcement as they can easily
+             * Be used to highjack an announcement by a third party.
+             * Instead we cleverly implement Implicit Announcement removal.
+             *
+             * if( b_need_delete )
+             *    RemoveAnnounce( p_sd, p_sys->pp_announces[i]);
+             * else
+             */
+
+            if( !b_need_delete )
             {
-                expect = 'i';
-                if (cat != 'm')
-                {
-                    msg_Dbg (p_obj, "missing SDP media description");
-                    goto error;
-                }
-                struct sdp_media_t *m;
-                m = realloc (p_sdp->mediav, (p_sdp->mediac + 1) * sizeof (*m));
-                if (m == NULL)
-                    goto error;
+                /* No need to go after six, as we start to trust the
+                 * average period at six */
+                if( p_announce->i_period_trust <= 5 )
+                    p_announce->i_period_trust++;
 
-                p_sdp->mediav = m;
-                m += p_sdp->mediac;
-                p_sdp->mediac++;
+                /* Compute the average period */
+                vlc_tick_t now = vlc_tick_now();
+                p_announce->i_period = ( p_announce->i_period * (p_announce->i_period_trust-1) + (now - p_announce->i_last) ) / p_announce->i_period_trust;
+                p_announce->i_last = now;
+            }
+            FreeSDP( p_sdp );
+            free(uri);
+            free (decomp);
+            return VLC_SUCCESS;
+        }
+    }
 
-                memset (m, 0, sizeof (*m));
-                memcpy (&m->addr, &glob_addr, m->addrlen = glob_len);
-                m->n_addr = glob_count;
+    sap_announce_t *sap = CreateAnnounce(p_sd, i_source, i_hash, p_sdp, uri);
 
-                /* TODO: remember media type (if we need multiple medias) */
-                data = strchr (data, ' ');
-                if (data == NULL)
-                {
-                    msg_Dbg (p_obj, "missing SDP media port");
-                    goto error;
-                }
-                port = atoi (++data);
-                if (port <= 0 || port >= 65536)
-                {
-                    msg_Dbg (p_obj, "invalid transport port %d", port);
-                    goto error;
-                }
-                net_SetPort ((struct sockaddr *)&m->addr, htons (port));
+    if (sap != NULL)
+        TAB_APPEND(p_sys->i_announces, p_sys->pp_announces, sap);
 
-                data = strchr (data, ' ');
-                if (data == NULL)
-                {
-                    msg_Dbg (p_obj, "missing SDP media format");
-                    goto error;
-                }
-                m->fmt = strdup (++data);
-                if (m->fmt == NULL)
-                    goto error;
+    free(uri);
+    free (decomp);
+    return VLC_SUCCESS;
+error:
+    free (decomp);
+    return VLC_EGENERIC;
+}
 
-                break;
-            }
+/*****************************************************************************
+ * Run: main SAP thread
+ *****************************************************************************
+ * Listens to SAP packets, and sends them to packet_handle
+ *****************************************************************************/
+#define MAX_SAP_BUFFER 5000
 
-            case 'i':
-                expect = 'c';
-                if (cat == 'i')
-                    break;
-                /* fall through */
-            case 'c':
-                expect = 'b';
-                if (cat == 'c')
-                {
-                    struct sdp_media_t *m = p_sdp->mediav + p_sdp->mediac - 1;
-                    if (ParseSDPConnection (data, &m->addr, &m->addrlen,
-                                            &m->n_addr))
-                    {
-                        msg_Dbg (p_obj, "SDP connection infos not supported: "
-                                 "%s", data);
-                        goto error;
-                    }
-                    net_SetPort ((struct sockaddr *)&m->addr, htons (port));
-                    break;
-                }
-                /* fall through */
-            case 'b':
-                expect = 'b';
-                if (cat == 'b')
-                    break;
-                /* fall through */
-            case 'k':
-                expect = 'a';
-                if (cat == 'k')
-                    break;
-                /* fall through */
-            case 'a':
-                assert (expect == 'a');
-                if (cat == 'a')
-                {
-                    attribute_t *p_attr = MakeAttribute (data);
-                    if (p_attr == NULL)
-                        goto error;
+static void *Run( void *data )
+{
+    services_discovery_t *p_sd = data;
+    services_discovery_sys_t *p_sys = p_sd->p_sys;
+    char *psz_addr;
+    int timeout = -1;
+    int canc = vlc_savecancel ();
 
-                    TAB_APPEND (p_sdp->mediav[p_sdp->mediac - 1].i_attributes,
-                                p_sdp->mediav[p_sdp->mediac - 1].pp_attributes, p_attr);
-                    break;
-                }
+    /* Braindead Winsock DNS resolver will get stuck over 2 seconds per failed
+     * DNS queries, even if the DNS server returns an error with milliseconds.
+     * You don't want to know why the bug (as of XP SP2) wasn't fixed since
+     * Winsock 1.1 from Windows 95, if not Windows 3.1.
+     * Anyway, to avoid a 30 seconds delay for failed IPv6 socket creation,
+     * we have to open sockets in Run() rather than Open(). */
+    InitSocket( p_sd, SAP_V4_GLOBAL_ADDRESS, SAP_PORT );
+    InitSocket( p_sd, SAP_V4_ORG_ADDRESS, SAP_PORT );
+    InitSocket( p_sd, SAP_V4_LOCAL_ADDRESS, SAP_PORT );
+    InitSocket( p_sd, SAP_V4_LINK_ADDRESS, SAP_PORT );
 
-                if (cat == 'm')
-                    goto media;
+    char psz_address[NI_MAXNUMERICHOST] = "ff02::2:7ffe%";
+#ifndef _WIN32
+    struct if_nameindex *l = if_nameindex ();
+    if (l != NULL)
+    {
+        char *ptr = strchr (psz_address, '%') + 1;
+        for (unsigned i = 0; l[i].if_index; i++)
+        {
+            strcpy (ptr, l[i].if_name);
+            InitSocket (p_sd, psz_address, SAP_PORT);
+        }
+        if_freenameindex (l);
+    }
+#else
+        /* this is the Winsock2 equivalant of SIOCGIFCONF on BSD stacks,
+           which if_nameindex uses internally anyway */
 
-                msg_Dbg (p_obj, "unexpected SDP line: 0x%02x", (int)cat);
-                goto error;
+        // first create a dummy socket to pin down the protocol family
+        SOCKET s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+        if( s != INVALID_SOCKET )
+        {
+            INTERFACE_INFO ifaces[10]; // Assume there will be no more than 10 IP interfaces
+            DWORD len = sizeof(ifaces);
 
-            default:
-                msg_Err (p_obj, "*** BUG in SDP parser! ***");
-                goto error;
+            if( SOCKET_ERROR != WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0, &ifaces, len, &len, NULL, NULL) )
+            {
+                unsigned ifcount = len/sizeof(INTERFACE_INFO);
+                char *ptr = strchr (psz_address, '%') + 1;
+                for(unsigned i = 1; i<=ifcount; ++i )
+                {
+                    // append link-local zone identifier
+                    sprintf(ptr, "%d", i);
+                }
+            }
+            closesocket(s);
         }
-    }
+#endif
+    *strchr (psz_address, '%') = '\0';
 
-    return p_sdp;
+    static const char ipv6_scopes[] = "1456789ABCDE";
+    for (const char *c_scope = ipv6_scopes; *c_scope; c_scope++)
+    {
+        psz_address[3] = *c_scope;
+        InitSocket( p_sd, psz_address, SAP_PORT );
+    }
 
-error:
-    FreeSDP (p_sdp);
-    return NULL;
-}
+    psz_addr = var_CreateGetString( p_sd, "sap-addr" );
+    if( psz_addr && *psz_addr )
+        InitSocket( p_sd, psz_addr, SAP_PORT );
+    free( psz_addr );
 
-static int InitSocket( services_discovery_t *p_sd, const char *psz_address,
-                       int i_port )
-{
-    int i_fd = net_ListenUDP1 ((vlc_object_t *)p_sd, psz_address, i_port);
-    if (i_fd == -1)
-        return VLC_EGENERIC;
+    if( p_sys->i_fd == 0 )
+    {
+        msg_Err( p_sd, "unable to listen on any address" );
+        return NULL;
+    }
 
-    shutdown( i_fd, SHUT_WR );
-    services_discovery_sys_t *p_sys = p_sd->p_sys;
-    TAB_APPEND(p_sys->i_fd, p_sys->pi_fd, i_fd);
-    return VLC_SUCCESS;
-}
+    /* read SAP packets */
+    for (;;)
+    {
+        vlc_restorecancel (canc);
+        unsigned n = p_sys->i_fd;
+        struct pollfd ufd[n];
 
-static int Decompress( const unsigned char *psz_src, unsigned char **_dst, int i_len )
-{
-#ifdef HAVE_ZLIB_H
-    int i_result, i_dstsize, n = 0;
-    unsigned char *psz_dst = NULL;
-    z_stream d_stream;
+        for (unsigned i = 0; i < n; i++)
+        {
+            ufd[i].fd = p_sys->pi_fd[i];
+            ufd[i].events = POLLIN;
+            ufd[i].revents = 0;
+        }
 
-    memset (&d_stream, 0, sizeof (d_stream));
+        int val = poll (ufd, n, timeout);
+        canc = vlc_savecancel ();
+        if (val > 0)
+        {
+            for (unsigned i = 0; i < n; i++)
+            {
+                if (ufd[i].revents)
+                {
+                    uint8_t p_buffer[MAX_SAP_BUFFER+1];
+                    ssize_t i_read;
 
-    i_result = inflateInit(&d_stream);
-    if( i_result != Z_OK )
-        return( -1 );
+                    i_read = recv (ufd[i].fd, p_buffer, MAX_SAP_BUFFER, 0);
+                    if (i_read < 0)
+                        msg_Warn (p_sd, "receive error: %s",
+                                  vlc_strerror_c(errno));
+                    if (i_read > 6)
+                    {
+                        /* Parse the packet */
+                        p_buffer[i_read] = '\0';
+                        ParseSAP (p_sd, p_buffer, i_read);
+                    }
+                }
+            }
+        }
 
-    d_stream.next_in = (Bytef *)psz_src;
-    d_stream.avail_in = i_len;
+        vlc_tick_t now = vlc_tick_now();
 
-    do
-    {
-        n++;
-        psz_dst = xrealloc( psz_dst, n * 1000 );
-        d_stream.next_out = (Bytef *)&psz_dst[(n - 1) * 1000];
-        d_stream.avail_out = 1000;
+        /* A 1 hour timeout correspond to the RFC Implicit timeout.
+         * This timeout is tuned in the following loop. */
+        timeout = 1000 * 60 * 60;
 
-        i_result = inflate(&d_stream, Z_NO_FLUSH);
-        if( ( i_result != Z_OK ) && ( i_result != Z_STREAM_END ) )
+        /* Check for items that need deletion */
+        for( int i = 0; i < p_sys->i_announces; i++ )
         {
-            inflateEnd( &d_stream );
-            free( psz_dst );
-            return( -1 );
-        }
-    }
-    while( ( d_stream.avail_out == 0 ) && ( d_stream.avail_in != 0 ) &&
-           ( i_result != Z_STREAM_END ) );
-
-    i_dstsize = d_stream.total_out;
-    inflateEnd( &d_stream );
+            sap_announce_t * p_announce = p_sys->pp_announces[i];
+            vlc_tick_t i_last_period = now - p_announce->i_last;
 
-    *_dst = xrealloc( psz_dst, i_dstsize );
+            /* Remove the announcement, if the last announcement was 1 hour ago
+             * or if the last packet emitted was 10 times the average time
+             * between two packets */
+            if( ( p_announce->i_period_trust > 5 && i_last_period > 10 * p_announce->i_period ) ||
+                i_last_period > p_sys->i_timeout )
+            {
+                RemoveAnnounce( p_sd, p_announce );
+            }
+            else
+            {
+                /* Compute next timeout */
+                if( p_announce->i_period_trust > 5 )
+                    timeout = __MIN(MS_FROM_VLC_TICK(10 * p_announce->i_period - i_last_period), timeout);
+                timeout = __MIN(MS_FROM_VLC_TICK(p_sys->i_timeout - i_last_period), timeout);
+            }
+        }
 
-    return i_dstsize;
-#else
-    (void)psz_src;
-    (void)_dst;
-    (void)i_len;
-    return -1;
-#endif
+        if( !p_sys->i_announces )
+            timeout = -1; /* We can safely poll indefinitely. */
+        else if( timeout < 200 )
+            timeout = 200; /* Don't wakeup too fast. */
+    }
+    vlc_assert_unreachable ();
 }
 
-
-static void FreeSDP( sdp_t *p_sdp )
+/*****************************************************************************
+ * Open: initialize and create stuff
+ *****************************************************************************/
+static int Open( vlc_object_t *p_this )
 {
-    free( p_sdp->psz_sessionname );
-    free( p_sdp->psz_sessioninfo );
+    services_discovery_t *p_sd = ( services_discovery_t* )p_this;
+    services_discovery_sys_t *p_sys  = (services_discovery_sys_t *)
+                                malloc( sizeof( services_discovery_sys_t ) );
+    if( !p_sys )
+        return VLC_ENOMEM;
 
-    for (unsigned j = 0; j < p_sdp->mediac; j++)
-    {
-        free (p_sdp->mediav[j].fmt);
-        for (int i = 0; i < p_sdp->mediav[j].i_attributes; i++)
-            FreeAttribute (p_sdp->mediav[j].pp_attributes[i]);
-        free (p_sdp->mediav[j].pp_attributes);
-    }
-    free (p_sdp->mediav);
+    p_sys->i_timeout = vlc_tick_from_sec(var_CreateGetInteger( p_sd, "sap-timeout" ));
 
-    for (int i = 0; i < p_sdp->i_attributes; i++)
-        FreeAttribute (p_sdp->pp_attributes[i]);
+    p_sd->p_sys  = p_sys;
+    p_sd->description = _("Network streams (SAP)");
 
-    free (p_sdp->pp_attributes);
-    free (p_sdp);
-}
+    p_sys->pi_fd = NULL;
+    p_sys->i_fd = 0;
 
-static int RemoveAnnounce( services_discovery_t *p_sd,
-                           sap_announce_t *p_announce )
-{
-    if( p_announce->p_sdp )
-    {
-        FreeSDP( p_announce->p_sdp );
-        p_announce->p_sdp = NULL;
-    }
+    p_sys->b_parse = var_CreateGetBool( p_sd, "sap-parse" );
 
-    if( p_announce->p_item )
+    p_sys->i_announces = 0;
+    p_sys->pp_announces = NULL;
+    /* TODO: create sockets here, and fix racy sockets table */
+    if (vlc_clone (&p_sys->thread, Run, p_sd, VLC_THREAD_PRIORITY_LOW))
     {
-        services_discovery_RemoveItem( p_sd, p_announce->p_item );
-        input_item_Release( p_announce->p_item );
-        p_announce->p_item = NULL;
+        free (p_sys);
+        return VLC_EGENERIC;
     }
 
-    services_discovery_sys_t *p_sys = p_sd->p_sys;
-    TAB_REMOVE(p_sys->i_announces, p_sys->pp_announces, p_announce);
-    free( p_announce );
-
     return VLC_SUCCESS;
 }
 
-static inline attribute_t *MakeAttribute (const char *str)
+/*****************************************************************************
+ * Close:
+ *****************************************************************************/
+static void Close( vlc_object_t *p_this )
 {
-    attribute_t *a = malloc (sizeof (*a) + strlen (str) + 1);
-    if (a == NULL)
-        return NULL;
+    services_discovery_t *p_sd = ( services_discovery_t* )p_this;
+    services_discovery_sys_t    *p_sys  = p_sd->p_sys;
+    int i;
 
-    strcpy (a->name, str);
-    EnsureUTF8 (a->name);
-    char *value = strchr (a->name, ':');
-    if (value != NULL)
+    vlc_cancel (p_sys->thread);
+    vlc_join (p_sys->thread, NULL);
+
+    for( i = p_sys->i_fd-1 ; i >= 0 ; i-- )
     {
-        *value++ = '\0';
-        a->value = value;
+        net_Close( p_sys->pi_fd[i] );
     }
-    else
-        a->value = "";
-    return a;
-}
-
-
-static const char *GetAttribute (attribute_t **tab, unsigned n,
-                                 const char *name)
-{
-    for (unsigned i = 0; i < n; i++)
-        if (strcasecmp (tab[i]->name, name) == 0)
-            return tab[i]->value;
-    return NULL;
-}
+    FREENULL( p_sys->pi_fd );
 
+    for( i = p_sys->i_announces  - 1;  i>= 0; i-- )
+    {
+        RemoveAnnounce( p_sd, p_sys->pp_announces[i] );
+    }
+    FREENULL( p_sys->pp_announces );
 
-static inline void FreeAttribute (attribute_t *a)
-{
-    free (a);
+    free( p_sys );
 }
 
 /*****************************************************************************



More information about the vlc-commits mailing list