sdp: use vlc_memstream
authorRémi Denis-Courmont <remi@remlab.net>
Sat, 25 Feb 2017 11:14:01 +0000 (13:14 +0200)
committerRémi Denis-Courmont <remi@remlab.net>
Sat, 25 Feb 2017 11:14:01 +0000 (13:14 +0200)
include/vlc_sout.h
modules/misc/rtsp.c
modules/stream_out/rtp.c
modules/stream_out/standard.c
modules/stream_out/vod.c
src/stream_output/sdp.c

index acbb2cb..c710780 100644 (file)
@@ -280,10 +280,19 @@ VLC_API void sout_AnnounceUnRegister(vlc_object_t *,session_descriptor_t* );
 /** SDP */
 
 struct sockaddr;
-
-VLC_API char * vlc_sdp_Start( vlc_object_t *obj, const char *cfgpref, const struct sockaddr *src, size_t srclen, const struct sockaddr *addr, size_t addrlen ) VLC_USED;
-VLC_API char * sdp_AddMedia(char **sdp, const char *type, const char *protocol, int dport, unsigned pt, bool bw_indep, unsigned bw, const char *ptname, unsigned clockrate, unsigned channels, const char *fmtp);
-VLC_API char * sdp_AddAttribute(char **sdp, const char *name, const char *fmt, ...) VLC_FORMAT( 3, 4 );
+struct vlc_memstream;
+
+VLC_API int vlc_sdp_Start(struct vlc_memstream *, vlc_object_t *obj,
+                          const char *cfgpref,
+                          const struct sockaddr *src, size_t slen,
+                          const struct sockaddr *addr, size_t alen) VLC_USED;
+VLC_API void sdp_AddMedia(struct vlc_memstream *, const char *type,
+                          const char *protocol, int dport, unsigned pt,
+                          bool bw_indep, unsigned bw, const char *ptname,
+                          unsigned clockrate, unsigned channels,
+                          const char *fmtp);
+VLC_API void sdp_AddAttribute(struct vlc_memstream *, const char *name,
+                              const char *fmt, ...) VLC_FORMAT(3, 4);
 
 /** Description module */
 typedef struct sout_description_data_t
index cb0d218..d2a03c1 100644 (file)
@@ -44,6 +44,7 @@
 #include <vlc_charset.h>
 #include <vlc_strings.h>
 #include <vlc_rand.h>
+#include <vlc_memstream.h>
 
 #ifndef _WIN32
 # include <locale.h>
@@ -1499,7 +1500,8 @@ static int RtspCallbackES( httpd_callback_sys_t *p_args, httpd_client_t *cl,
  *****************************************************************************/
 static char *SDPGenerate( const vod_media_t *p_media, httpd_client_t *cl )
 {
-    char *psz_sdp, ip[NI_MAXNUMERICHOST];
+    struct vlc_memstream sdp;
+    char ip[NI_MAXNUMERICHOST];
     const char *psz_control;
     int port;
 
@@ -1521,15 +1523,14 @@ static char *SDPGenerate( const vod_media_t *p_media, httpd_client_t *cl )
     dst.ss_len = dstlen;
 #endif
 
-    psz_sdp = vlc_sdp_Start( VLC_OBJECT( p_media->p_vod ), "sout-rtp-",
-                             NULL, 0, (struct sockaddr *)&dst, dstlen );
-    if( psz_sdp == NULL )
+    if( vlc_sdp_Start( &sdp, VLC_OBJECT( p_media->p_vod ), "sout-rtp-",
+                       NULL, 0, (struct sockaddr *)&dst, dstlen ) )
         return NULL;
 
     if( p_media->i_length > 0 )
     {
         lldiv_t d = lldiv( p_media->i_length / 1000, 1000 );
-        sdp_AddAttribute( &psz_sdp, "range","npt=0-%lld.%03u", d.quot,
+        sdp_AddAttribute( &sdp, "range","npt=0-%lld.%03u", d.quot,
                           (unsigned)d.rem );
     }
 
@@ -1553,13 +1554,13 @@ static char *SDPGenerate( const vod_media_t *p_media, httpd_client_t *cl )
                 continue;
         }
 
-        sdp_AddMedia( &psz_sdp, mime_major, "RTP/AVP", 0 /* p_es->i_port */,
+        sdp_AddMedia( &sdp, mime_major, "RTP/AVP", 0 /* p_es->i_port */,
                       p_es->i_payload_type, false, 0,
                       p_es->psz_ptname, p_es->i_clock_rate, p_es->i_channels,
                       p_es->psz_fmtp );
 
-        sdp_AddAttribute( &psz_sdp, "control", psz_control, ip, port, i );
+        sdp_AddAttribute( &sdp, "control", psz_control, ip, port, i );
     }
 
-    return psz_sdp;
+    return vlc_memstream_close( &sdp ) ? NULL : sdp.ptr;
 }
index 3f85f2e..c4436a8 100644 (file)
@@ -41,6 +41,7 @@
 #include <vlc_network.h>
 #include <vlc_fs.h>
 #include <vlc_rand.h>
+#include <vlc_memstream.h>
 #ifdef HAVE_SRTP
 # include <srtp.h>
 # include <gcrypt.h>
@@ -780,8 +781,9 @@ out:
 char *SDPGenerate( sout_stream_t *p_stream, const char *rtsp_url )
 {
     sout_stream_sys_t *p_sys = p_stream->p_sys;
-    char *psz_sdp = NULL;
+    struct vlc_memstream sdp;
     struct sockaddr_storage dst;
+    char *psz_sdp = NULL;
     socklen_t dstlen;
     int i;
     /*
@@ -836,17 +838,16 @@ char *SDPGenerate( sout_stream_t *p_stream, const char *rtsp_url )
 #endif
     }
 
-    psz_sdp = vlc_sdp_Start( VLC_OBJECT( p_stream ), SOUT_CFG_PREFIX,
-                             NULL, 0, (struct sockaddr *)&dst, dstlen );
-    if( psz_sdp == NULL )
+    if( vlc_sdp_Start( &sdp, VLC_OBJECT( p_stream ), SOUT_CFG_PREFIX,
+                       NULL, 0, (struct sockaddr *)&dst, dstlen ) )
         goto out;
 
     /* TODO: a=source-filter */
     if( p_sys->rtcp_mux )
-        sdp_AddAttribute( &psz_sdp, "rtcp-mux", NULL );
+        sdp_AddAttribute( &sdp, "rtcp-mux", NULL );
 
     if( rtsp_url != NULL )
-        sdp_AddAttribute ( &psz_sdp, "control", "%s", rtsp_url );
+        sdp_AddAttribute ( &sdp, "control", "%s", rtsp_url );
 
     const char *proto = "RTP/AVP"; /* protocol */
     if( rtsp_url == NULL )
@@ -887,34 +888,36 @@ char *SDPGenerate( sout_stream_t *p_stream, const char *rtsp_url )
                 continue;
         }
 
-        sdp_AddMedia( &psz_sdp, mime_major, proto, inclport * id->i_port,
+        sdp_AddMedia( &sdp, mime_major, proto, inclport * id->i_port,
                       rtp_fmt->payload_type, false, rtp_fmt->bitrate,
                       rtp_fmt->ptname, rtp_fmt->clock_rate, rtp_fmt->channels,
                       rtp_fmt->fmtp);
 
         /* cf RFC4566 §5.14 */
         if( inclport && !p_sys->rtcp_mux && (id->i_port & 1) )
-            sdp_AddAttribute ( &psz_sdp, "rtcp", "%u", id->i_port + 1 );
+            sdp_AddAttribute( &sdp, "rtcp", "%u", id->i_port + 1 );
 
         if( rtsp_url != NULL )
         {
             char *track_url = RtspAppendTrackPath( id->rtsp_id, rtsp_url );
             if( track_url != NULL )
             {
-                sdp_AddAttribute ( &psz_sdp, "control", "%s", track_url );
+                sdp_AddAttribute( &sdp, "control", "%s", track_url );
                 free( track_url );
             }
         }
         else
         {
             if( id->listen.fd != NULL )
-                sdp_AddAttribute( &psz_sdp, "setup", "passive" );
+                sdp_AddAttribute( &sdp, "setup", "passive" );
             if( p_sys->proto == IPPROTO_DCCP )
-                sdp_AddAttribute( &psz_sdp, "dccp-service-code",
-                                  "SC:RTP%c",
+                sdp_AddAttribute( &sdp, "dccp-service-code", "SC:RTP%c",
                                   toupper( (unsigned char)mime_major[0] ) );
         }
     }
+
+    if( vlc_memstream_close( &sdp ) == 0 )
+        psz_sdp = sdp.ptr;
 out:
     vlc_mutex_unlock( &p_sys->lock_es );
     return psz_sdp;
index e8805ba..15c0f2b 100644 (file)
@@ -34,6 +34,7 @@
 
 #include <vlc_network.h>
 #include <vlc_url.h>
+#include <vlc_memstream.h>
 
 /*****************************************************************************
  * Module descriptor
@@ -179,29 +180,25 @@ static void create_SDP(sout_stream_t *p_stream, sout_access_out_t *p_access)
         freeaddrinfo (res);
     }
 
-    char *head = vlc_sdp_Start (VLC_OBJECT (p_stream), SOUT_CFG_PREFIX,
-            (struct sockaddr *)&src, srclen,
-            (struct sockaddr *)&dst, dstlen);
-    free (shost);
+    struct vlc_memstream sdp;
 
-    if (head != NULL)
+    if (vlc_sdp_Start(&sdp, VLC_OBJECT (p_stream), SOUT_CFG_PREFIX,
+                      (struct sockaddr *)&src, srclen,
+                      (struct sockaddr *)&dst, dstlen) == 0)
     {
-        char *psz_sdp = NULL;
-        if (asprintf (&psz_sdp, "%s"
-                    "m=video %d udp mpeg\r\n", head, dport) == -1)
-            psz_sdp = NULL;
-        free (head);
+        vlc_memstream_printf(&sdp, "m=video %d udp mpeg\r\n", dport);
 
         /* Register the SDP with the SAP thread */
-        if (psz_sdp)
+        if (vlc_memstream_close(&sdp))
         {
-            msg_Dbg (p_stream, "Generated SDP:\n%s", psz_sdp);
+            msg_Dbg(p_stream, "Generated SDP:\n%s", sdp.ptr);
             p_sys->p_session =
-                sout_AnnounceRegisterSDP (p_stream, psz_sdp, dhost);
-            free( psz_sdp );
+                sout_AnnounceRegisterSDP(p_stream, sdp.ptr, dhost);
+            free(sdp.ptr);
         }
     }
-    free (dhost);
+    free(shost);
+    free(dhost);
 }
 
 static const char *getMuxFromAlias( const char *psz_alias )
index 9c75198..9db3d70 100644 (file)
@@ -40,6 +40,7 @@
 #include <vlc_vod.h>
 #include <vlc_url.h>
 #include <vlc_network.h>
+#include <vlc_memstream.h>
 
 #include <assert.h>
 
@@ -390,8 +391,6 @@ static void* CommandThread( void *obj )
  *****************************************************************************/
 char *SDPGenerateVoD( const vod_media_t *p_media, const char *rtsp_url )
 {
-    char *psz_sdp;
-
     assert(rtsp_url != NULL);
     /* Check against URL format rtsp://[<ipv6>]:<port>/<path> */
     bool ipv6 = strlen( rtsp_url ) > 7 && rtsp_url[7] == '[';
@@ -406,19 +405,20 @@ char *SDPGenerateVoD( const vod_media_t *p_media, const char *rtsp_url )
     dst.ss_len = dstlen;
 #endif
 
-    psz_sdp = vlc_sdp_Start( VLC_OBJECT( p_media->p_vod ), "sout-rtp-",
-                             NULL, 0, (struct sockaddr *)&dst, dstlen );
-    if( psz_sdp == NULL )
+    struct vlc_memstream sdp;
+
+    if( vlc_sdp_Start( &sdp, VLC_OBJECT( p_media->p_vod ), "sout-rtp-",
+                       NULL, 0, (struct sockaddr *)&dst, dstlen ) )
         return NULL;
 
     if( p_media->i_length > 0 )
     {
         lldiv_t d = lldiv( p_media->i_length / 1000, 1000 );
-        sdp_AddAttribute( &psz_sdp, "range"," npt=0-%lld.%03u", d.quot,
+        sdp_AddAttribute( &sdp, "range"," npt=0-%lld.%03u", d.quot,
                           (unsigned)d.rem );
     }
 
-    sdp_AddAttribute ( &psz_sdp, "control", "%s", rtsp_url );
+    sdp_AddAttribute( &sdp, "control", "%s", rtsp_url );
 
     /* No locking needed, the ES table can't be modified now */
     for( int i = 0; i < p_media->i_es; i++ )
@@ -442,7 +442,7 @@ char *SDPGenerateVoD( const vod_media_t *p_media, const char *rtsp_url )
                 continue;
         }
 
-        sdp_AddMedia( &psz_sdp, mime_major, "RTP/AVP", 0,
+        sdp_AddMedia( &sdp, mime_major, "RTP/AVP", 0,
                       rtp_fmt->payload_type, false, 0,
                       rtp_fmt->ptname, rtp_fmt->clock_rate, rtp_fmt->channels,
                       rtp_fmt->fmtp );
@@ -450,12 +450,12 @@ char *SDPGenerateVoD( const vod_media_t *p_media, const char *rtsp_url )
         char *track_url = RtspAppendTrackPath( p_es->rtsp_id, rtsp_url );
         if( track_url != NULL )
         {
-            sdp_AddAttribute ( &psz_sdp, "control", "%s", track_url );
+            sdp_AddAttribute( &sdp, "control", "%s", track_url );
             free( track_url );
         }
     }
 
-    return psz_sdp;
+    return vlc_memstream_close( &sdp ) ? NULL : sdp.ptr;
 }
 
 int vod_check_range(vod_media_t *p_media, const char *psz_session,
index 37c55b0..c2ba6b2 100644 (file)
@@ -32,6 +32,7 @@
 #include <assert.h>
 #include <vlc_network.h>
 #include <vlc_charset.h>
+#include <vlc_memstream.h>
 
 #include "stream_output.h"
 
@@ -90,217 +91,186 @@ static bool IsSDPString (const char *str)
     return true;
 }
 
-
-static
-char *sdp_Start (const char *name, const char *description, const char *url,
-                 const char *email, const char *phone,
-                 const struct sockaddr *src, size_t srclen,
-                 const struct sockaddr *addr, size_t addrlen)
+static void vsdp_AddAttribute(struct vlc_memstream *restrict stream,
+                              const char *name, const char *fmt, va_list ap)
 {
-    uint64_t now = NTPtime64 ();
-    char *sdp;
-    char connection[MAXSDPADDRESS], hostname[256],
-         sfilter[MAXSDPADDRESS + sizeof ("\r\na=source-filter: incl * ")];
-    const char *preurl = "\r\nu=", *premail = "\r\ne=", *prephone = "\r\np=";
-
-    gethostname (hostname, sizeof (hostname));
-
-    if (name == NULL)
-        name = "Unnamed";
-    if (description == NULL)
-        description = "N/A";
-    if (url == NULL)
-        preurl = url = "";
-    if (email == NULL)
-        premail = email = "";
-    if (phone == NULL)
-        prephone = phone = "";
-
-    if (!IsSDPString (name) || !IsSDPString (description)
-     || !IsSDPString (url) || !IsSDPString (email) || !IsSDPString (phone)
-     || (AddressToSDP (addr, addrlen, connection) == NULL))
-        return NULL;
-
-    strcpy (sfilter, "");
-    if (srclen > 0)
-    {
-        char machine[MAXSDPADDRESS];
+    vlc_memstream_printf(stream, "a=%s:", name);
+    vlc_memstream_vprintf(stream, fmt, ap);
+    vlc_memstream_puts(stream, "\r\n");
+}
 
-        if (AddressToSDP (src, srclen, machine) != NULL)
-            sprintf (sfilter, "\r\na=source-filter: incl IN IP%c * %s",
-                     machine[5], machine + 7);
-    }
+void sdp_AddAttribute(struct vlc_memstream *restrict stream, const char *name,
+                      const char *fmt, ...)
+{
+    va_list ap;
 
-    if (asprintf (&sdp, "v=0"
-                    "\r\no=- %"PRIu64" %"PRIu64" IN IP%c %s"
-                    "\r\ns=%s"
-                    "\r\ni=%s"
-                    "%s%s" // optional URL
-                    "%s%s" // optional email
-                    "%s%s" // optional phone number
-                    "\r\nc=%s"
-                        // bandwidth not specified
-                    "\r\nt=0 0" // one dummy time span
-                        // no repeating
-                        // no time zone adjustment (silly idea anyway)
-                        // no encryption key (deprecated)
-                    "\r\na=tool:"PACKAGE_STRING
-                    "\r\na=recvonly"
-                    "\r\na=type:broadcast"
-                    "\r\na=charset:UTF-8"
-                    "%s" // optional source filter
-                    "\r\n",
-               /* o= */ now, now, connection[5], hostname,
-               /* s= */ name,
-               /* i= */ description,
-               /* u= */ preurl, url,
-               /* e= */ premail, email,
-               /* p= */ prephone, phone,
-               /* c= */ connection,
-    /* source-filter */ sfilter) == -1)
-        return NULL;
-    return sdp;
+    va_start(ap, fmt);
+    vsdp_AddAttribute(stream, name, fmt, ap);
+    va_end(ap);
 }
 
-
-static char *
-vsdp_AddAttribute (char **sdp, const char *name, const char *fmt, va_list ap)
+void sdp_AddMedia(struct vlc_memstream *restrict stream,
+                  const char *type, const char *proto, int dport,
+                  unsigned pt, bool bw_indep, unsigned bw,
+                  const char *ptname, unsigned clock, unsigned chans,
+                  const char *fmtp)
 {
-    size_t oldlen = strlen (*sdp);
-    size_t addlen = sizeof ("a=\r\n") + strlen (name);
-
-    if (fmt != NULL)
-    {
-        va_list aq;
+    /* Some default values */
+    if (type == NULL)
+        type = "video";
+    if (proto == NULL)
+        proto = "RTP/AVP";
+    assert (pt < 128u);
 
-        va_copy (aq, ap);
-        addlen += 1 + vsnprintf (NULL, 0, fmt, aq);
-        va_end (aq);
-    }
+    vlc_memstream_printf(stream, "m=%s %u %s %u\r\n", type, dport, proto, pt);
 
-    char *ret = realloc (*sdp, oldlen + addlen);
-    if (ret == NULL)
-        return NULL;
+    if (bw > 0)
+        vlc_memstream_printf(stream, "b=%s:%u\r\n",
+                             bw_indep ? "TIAS" : "AS", bw);
+    vlc_memstream_printf(stream, "b=%s:%u\r\n", "RR", 0);
 
-    oldlen += sprintf (ret + oldlen, "a=%s", name);
-    if (fmt != NULL)
+    /* RTP payload type map */
+    if (ptname != NULL)
     {
-        ret[oldlen++] = ':';
-        oldlen += vsprintf (ret + oldlen, fmt, ap);
+        vlc_memstream_printf(stream, "a=rtpmap:%u %s/%u", pt, ptname, clock);
+        if ((strcmp(type, "audio") == 0) && (chans != 1))
+            vlc_memstream_printf(stream, "/%u", chans);
+        vlc_memstream_puts(stream, "\r\n");
     }
 
-    strcpy (ret + oldlen, "\r\n");
-    return *sdp = ret;
+    /* Format parameters */
+    if (fmtp != NULL)
+        vlc_memstream_printf(stream, "a=fmtp:%u %s\r\n", pt, fmtp);
 }
 
-
-char *sdp_AddAttribute (char **sdp, const char *name, const char *fmt, ...)
+int vlc_sdp_Start(struct vlc_memstream *restrict stream,
+                  vlc_object_t *obj, const char *cfgpref,
+                  const struct sockaddr *src, size_t srclen,
+                  const struct sockaddr *addr, size_t addrlen)
 {
-    char *ret;
-    va_list ap;
+    char connection[MAXSDPADDRESS];
+    char *str = NULL;
 
-    va_start (ap, fmt);
-    ret = vsdp_AddAttribute (sdp, name, fmt, ap);
-    va_end (ap);
+    size_t cfglen = strlen(cfgpref);
+    if (cfglen >= 128)
+        return -1;
 
-    return ret;
-}
+    char varname[cfglen + sizeof ("description")];
+    char *subvar = varname + cfglen;
 
+    strcpy(varname, cfgpref);
 
-char *sdp_AddMedia (char **sdp,
-                    const char *type, const char *protocol, int dport,
-                    unsigned pt, bool bw_indep, unsigned bw,
-                    const char *ptname, unsigned clock, unsigned chans,
-                    const char *fmtp)
-{
-    char *newsdp, *ptr;
-    size_t inlen = strlen (*sdp), outlen = inlen;
+    vlc_memstream_open(stream);
+    vlc_memstream_puts(stream, "v=0\r\n");
 
-    /* Some default values */
-    if (type == NULL)
-        type = "video";
-    if (protocol == NULL)
-        protocol = "RTP/AVP";
-    assert (pt < 128u);
+    if (AddressToSDP(addr, addrlen, connection) == NULL)
+        goto error;
+    {
+        const uint_fast64_t now = NTPtime64();
+        char hostname[256];
 
-    outlen += snprintf (NULL, 0,
-                        "m=%s %u %s %d\r\n"
-                        "b=TIAS:%u\r\n"
-                        "b=RR:0\r\n",
-                        type, dport, protocol, pt, bw);
+        gethostname(hostname, sizeof (hostname));
 
-    newsdp = realloc (*sdp, outlen + 1);
-    if (newsdp == NULL)
-        return NULL;
+        vlc_memstream_printf(stream, "o=- %"PRIu64" %"PRIu64" IN IP%c %s\r\n",
+                             now, now, connection[5], hostname);
+    }
 
-    *sdp = newsdp;
-    ptr = newsdp + inlen;
+    strcpy(subvar, "name");
+    str = var_GetNonEmptyString(obj, varname);
+    if (str != NULL)
+    {
+        if (!IsSDPString(str))
+            goto error;
 
-    ptr += sprintf (ptr, "m=%s %u %s %u\r\n",
-                         type, dport, protocol, pt);
-    if (bw > 0)
-        ptr += sprintf (ptr, "b=%s:%u\r\n", bw_indep ? "TIAS" : "AS", bw);
-    ptr += sprintf (ptr, "b=RR:0\r\n");
+        vlc_memstream_printf(stream, "s=%s\r\n", str);
+        free(str);
+    }
+    else
+        vlc_memstream_printf(stream, "s=%s\r\n", "Unnamed");
 
-    /* RTP payload type map */
-    if (ptname != NULL)
+    strcpy(subvar, "description");
+    str = var_GetNonEmptyString(obj, varname);
+    if (str != NULL)
     {
-        if ((strcmp (type, "audio") == 0) && (chans != 1))
-            sdp_AddAttribute (sdp, "rtpmap", "%u %s/%u/%u", pt, ptname, clock,
-                              chans);
-        else
-            sdp_AddAttribute (sdp, "rtpmap", "%u %s/%u", pt, ptname, clock);
+        if (!IsSDPString(str))
+            goto error;
+
+        vlc_memstream_printf(stream, "i=%s\r\n", str);
+        free(str);
     }
-    /* Format parameters */
-    if (fmtp != NULL)
-        sdp_AddAttribute (sdp, "fmtp", "%u %s", pt, fmtp);
+    else
+        vlc_memstream_printf(stream, "i=%s\r\n", "N/A");
 
-    return newsdp;
-}
+    strcpy(subvar, "url");
+    str = var_GetNonEmptyString(obj, varname);
+    if (str != NULL)
+    {
+        if (!IsSDPString(str))
+            goto error;
 
+        vlc_memstream_printf(stream, "u=%s\r\n", str);
+        free(str);
+    }
 
-char *vlc_sdp_Start (vlc_object_t *obj, const char *cfgpref,
-                     const struct sockaddr *src, size_t srclen,
-                     const struct sockaddr *addr, size_t addrlen)
-{
-    size_t cfglen = strlen (cfgpref);
-    if (cfglen > 100)
-        return NULL;
+    strcpy(subvar, "email");
+    str = var_GetNonEmptyString(obj, varname);
+    if (str != NULL)
+    {
+        if (!IsSDPString(str))
+            goto error;
 
-    char varname[cfglen + sizeof ("description")], *subvar = varname + cfglen;
-    strcpy (varname, cfgpref);
-
-    strcpy (subvar, "name");
-    char *name = var_GetNonEmptyString (obj, varname);
-    strcpy (subvar, "description");
-    char *description = var_GetNonEmptyString (obj, varname);
-    strcpy (subvar, "url");
-    char *url = var_GetNonEmptyString (obj, varname);
-    strcpy (subvar, "email");
-    char *email = var_GetNonEmptyString (obj, varname);
-    strcpy (subvar, "phone");
-    char *phone = var_GetNonEmptyString (obj, varname);
-
-    char *sdp = sdp_Start (name, description, url, email, phone,
-                           src, srclen, addr, addrlen);
-    free (name);
-    free (description);
-    free (url);
-    free (email);
-    free (phone);
-
-    if (sdp == NULL)
-        return NULL;
+        vlc_memstream_printf(stream, "e=%s\r\n", str);
+        free(str);
+    }
+
+    strcpy(subvar, "phone");
+    str = var_GetNonEmptyString(obj, varname);
+    if (str != NULL)
+    {
+        if (!IsSDPString(str))
+            goto error;
+
+        vlc_memstream_printf(stream, "p=%s\r\n", str);
+        free(str);
+    }
 
-    strcpy (subvar, "cat");
-    char *cat = var_GetNonEmptyString (obj, varname);
-    if (cat != NULL)
+    vlc_memstream_printf(stream, "c=%s\r\n", connection);
+    // bandwidth not specified
+    vlc_memstream_puts(stream, "t=0 0\r\n"); // one dummy time span
+    // no repeating
+    // no time zone adjustment (silly idea anyway)
+    // no encryption key (deprecated)
+
+    vlc_memstream_printf(stream, "a=tool:%s\r\n", PACKAGE_STRING);
+    vlc_memstream_puts(stream, "a=recvonly\r\n");
+    vlc_memstream_puts(stream, "a=type:broadcast\r\n");
+    vlc_memstream_puts(stream, "a=charset:UTF-8\r\n");
+
+    if (srclen > 0)
     {
-        sdp_AddAttribute (&sdp, "cat", "%s", cat);
-        /* Totally non-standard */
-        sdp_AddAttribute (&sdp, "x-plgroup", "%s", cat);
-        free (cat);
+        char machine[MAXSDPADDRESS];
+
+        if (AddressToSDP(src, srclen, machine) != NULL)
+            vlc_memstream_printf(stream,
+                                 "a=source-filter: incl IN IP%c * %s\r\n",
+                                 machine[5], machine + 7);
     }
 
-    return sdp;
+    strcpy(subvar, "cat");
+    str = var_GetNonEmptyString(obj, varname);
+    if (str != NULL)
+    {
+        if (IsSDPString(str))
+            goto error;
+
+        vlc_memstream_printf(stream, "a=cat:%s\r\n", str);
+        vlc_memstream_printf(stream, "a=x-plgroup:%s\r\n", str);
+        free(str);
+    }
+    return 0;
+error:
+    free(str);
+    if (vlc_memstream_close(stream) == 0)
+        free(stream->ptr);
+    return -1;
 }