X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fstream_output%2Fsdp.c;h=d2c0610655df1ac88588dc4120ba48bdf1fcebca;hb=def14b82467447f3f7fd1717d237e99837d11de9;hp=5d6dadbe0064ee5e901f0e73b0ffcbd3be38e1e6;hpb=c3bc9e66d8722c5fbb13eb314d49b3070c6098a2;p=vlc diff --git a/src/stream_output/sdp.c b/src/stream_output/sdp.c index 5d6dadbe00..d2c0610655 100644 --- a/src/stream_output/sdp.c +++ b/src/stream_output/sdp.c @@ -4,99 +4,302 @@ * Copyright © 2007 Rémi Denis-Courmont * $Id$ * - * Authors: Rémi Denis-Courmont - * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ -# include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include -# include -# include -# include +#include +#include +#include +#include +#include +#include +#include -# define MAXSDPADDRESS 47 +#include "stream_output.h" + +#define MAXSDPADDRESS 47 static char *AddressToSDP (const struct sockaddr *addr, socklen_t addrlen, char *buf) { - const char *ttl = NULL; + if (addrlen < offsetof (struct sockaddr, sa_family) + + sizeof (addr->sa_family)) + return NULL; + strcpy (buf, "IN IP* "); + if (vlc_getnameinfo (addr, addrlen, buf + 7, MAXSDPADDRESS - 7, NULL, + NI_NUMERICHOST)) + return NULL; + switch (addr->sa_family) { case AF_INET: { if (net_SockAddrIsMulticast (addr, addrlen)) - ttl = "/255"; // obsolete in RFC4566, dummy value + strcat (buf, "/255"); // obsolete in RFC4566, dummy value buf[5] = '4'; break; } #ifdef AF_INET6 case AF_INET6: + { + char *ptr = strchr (buf, '%'); + if (ptr != NULL) + *ptr = '\0'; // remove scope ID buf[5] = '6'; break; + } #endif default: return NULL; } - if (vlc_getnameinfo (addr, addrlen, buf + 4, MAXSDPADDRESS - 4, NULL, - NI_NUMERICHOST)) - return NULL; + return buf; +} - if (ttl != NULL) - strcat (buf, ttl); - return buf; +static bool IsSDPString (const char *str) +{ + if (strchr (str, '\r') != NULL) + return false; + if (strchr (str, '\n') != NULL) + return false; + if (!IsUTF8 (str)) + return false; + return true; } -char *StartSDP (const char *name, - const struct sockaddr *orig, socklen_t origlen, - const struct sockaddr *addr, socklen_t addrlen) +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) { - uint64_t t = NTPtime64 (); - char *sdp, machine[MAXSDPADDRESS], conn[MAXSDPADDRESS]; + 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="; - if (strchr (name, '\r') || strchr (name, '\n') || !IsUTF8 (name) - || (AddressToSDP ((struct sockaddr *)&orig, origlen, machine) == NULL) - || (AddressToSDP ((struct sockaddr *)&addr, addrlen, conn) == NULL)) + 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; - if (asprintf (&sdp, "v=0\r\n" - "o=- "I64Fu" "I64Fu" %s\r\n" - "s=%s\r\n" - "i=N/A\r\n" // must be there even if useless - // no URL, email and phone here */ - "c=%s\r\n" + strcpy (sfilter, ""); + if (srclen > 0) + { + char machine[MAXSDPADDRESS]; + + if (AddressToSDP (src, srclen, machine) != NULL) + sprintf (sfilter, "\r\na=source-filter: incl IN IP%c * %s", + machine[5], machine + 7); + } + + 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 - "t= 0 0" // one dummy time span + "\r\nt=0 0" // one dummy time span // no repeating // no time zone adjustment (silly idea anyway) // no encryption key (deprecated) - "a=tool:"PACKAGE_STRING"\r\n" - "a=recvonly\r\n" - "a=type:broadcast\r\n" - "a=charset:UTF-8\r\n", - /* o= */ t, t, machine, + "\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, - /* c= */ conn) == -1) + /* i= */ description, + /* u= */ preurl, url, + /* e= */ premail, email, + /* p= */ prephone, phone, + /* c= */ connection, + /* source-filter */ sfilter) == -1) return NULL; return sdp; } + +static char * +vsdp_AddAttribute (char **sdp, const char *name, const char *fmt, va_list ap) +{ + size_t oldlen = strlen (*sdp); + size_t addlen = sizeof ("a=\r\n") + strlen (name); + + if (fmt != NULL) + { + va_list aq; + + va_copy (aq, ap); + addlen += 1 + vsnprintf (NULL, 0, fmt, aq); + va_end (aq); + } + + char *ret = realloc (*sdp, oldlen + addlen); + if (ret == NULL) + return NULL; + + oldlen += sprintf (ret + oldlen, "a=%s", name); + if (fmt != NULL) + { + ret[oldlen++] = ':'; + oldlen += vsprintf (ret + oldlen, fmt, ap); + } + + strcpy (ret + oldlen, "\r\n"); + return *sdp = ret; +} + + +char *sdp_AddAttribute (char **sdp, const char *name, const char *fmt, ...) +{ + char *ret; + va_list ap; + + va_start (ap, fmt); + ret = vsdp_AddAttribute (sdp, name, fmt, ap); + va_end (ap); + + return ret; +} + + +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; + + /* Some default values */ + if (type == NULL) + type = "video"; + if (protocol == NULL) + protocol = "RTP/AVP"; + assert (pt < 128u); + + 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); + + newsdp = realloc (*sdp, outlen + 1); + if (newsdp == NULL) + return NULL; + + *sdp = newsdp; + ptr = newsdp + inlen; + + 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"); + + /* RTP payload type map */ + if (ptname != 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); + } + /* Format parameters */ + if (fmtp != NULL) + sdp_AddAttribute (sdp, "fmtp", "%u %s", pt, fmtp); + + return newsdp; +} + + +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; + + 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; + + /* Totally non-standard */ + strcpy (subvar, "group"); + char *group = var_GetNonEmptyString (obj, varname); + if (group != NULL) + { + sdp_AddAttribute (&sdp, "x-plgroup", "%s", group); + free (group); + } + + return sdp; +}