Fix realaudio linking on Linux without the Win32 loader
[vlc.git] / src / stream_output / sdp.c
1 /*****************************************************************************
2  * sdp.c : SDP creation helpers
3  *****************************************************************************
4  * Copyright © 2007 Rémi Denis-Courmont
5  * $Id$
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation; either version 2.1 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
20  *****************************************************************************/
21
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25
26 #include <vlc/vlc.h>
27
28 #include <string.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <assert.h>
32 #include <vlc_network.h>
33 #include <vlc_charset.h>
34
35 #include "stream_output.h"
36
37 #define MAXSDPADDRESS 47
38
39 static
40 char *AddressToSDP (const struct sockaddr *addr, socklen_t addrlen, char *buf)
41 {
42     if (addrlen < offsetof (struct sockaddr, sa_family)
43                  + sizeof (addr->sa_family))
44         return NULL;
45
46     strcpy (buf, "IN IP* ");
47
48     if (vlc_getnameinfo (addr, addrlen, buf + 7, MAXSDPADDRESS - 7, NULL,
49                          NI_NUMERICHOST))
50         return NULL;
51
52     switch (addr->sa_family)
53     {
54         case AF_INET:
55         {
56             if (net_SockAddrIsMulticast (addr, addrlen))
57                 strcat (buf, "/255"); // obsolete in RFC4566, dummy value
58             buf[5] = '4';
59             break;
60         }
61
62 #ifdef AF_INET6
63         case AF_INET6:
64         {
65             char *ptr = strchr (buf, '%');
66             if (ptr != NULL)
67                 *ptr = '\0'; // remove scope ID
68             buf[5] = '6';
69             break;
70         }
71 #endif
72
73         default:
74             return NULL;
75     }
76
77     return buf;
78 }
79
80
81 static vlc_bool_t IsSDPString (const char *str)
82 {
83     if (strchr (str, '\r') != NULL)
84         return VLC_FALSE;
85     if (strchr (str, '\n') != NULL)
86         return VLC_FALSE;
87     if (!IsUTF8 (str))
88         return VLC_FALSE;
89     return VLC_TRUE;
90 }
91
92
93 static
94 char *sdp_Start (const char *name, const char *description, const char *url,
95                  const char *email, const char *phone,
96                  const struct sockaddr *src, size_t srclen,
97                  const struct sockaddr *addr, size_t addrlen)
98 {
99     uint64_t now = NTPtime64 ();
100     char *sdp;
101     char connection[MAXSDPADDRESS], hostname[256],
102          sfilter[MAXSDPADDRESS + sizeof ("\r\na=source-filter: incl * ")];
103     const char *preurl = "\r\nu=", *premail = "\r\ne=", *prephone = "\r\np=";
104
105     gethostname (hostname, sizeof (hostname));
106
107     if (name == NULL)
108         name = "Unnamed";
109     if (description == NULL)
110         description = "N/A";
111     if (url == NULL)
112         preurl = url = "";
113     if (email == NULL)
114         premail = email = "";
115     if (phone == NULL)
116         prephone = phone = "";
117
118     if (!IsSDPString (name) || !IsSDPString (description)
119      || !IsSDPString (url) || !IsSDPString (email) || !IsSDPString (phone)
120      || (AddressToSDP (addr, addrlen, connection) == NULL))
121         return NULL;
122
123     strcpy (sfilter, "");
124     if (srclen > 0)
125     {
126         char machine[MAXSDPADDRESS];
127
128         if (AddressToSDP (src, srclen, machine) != NULL)
129             sprintf (sfilter, "\r\na=source-filter: incl IN IP%c * %s",
130                      machine[5], machine + 7);
131     }
132
133     if (asprintf (&sdp, "v=0"
134                     "\r\no=- "I64Fu" "I64Fu" IN IP%c %s"
135                     "\r\ns=%s"
136                     "\r\ni=%s"
137                     "%s%s" // optional URL
138                     "%s%s" // optional email
139                     "%s%s" // optional phone number
140                     "\r\nc=%s"
141                         // bandwidth not specified
142                     "\r\nt=0 0" // one dummy time span
143                         // no repeating
144                         // no time zone adjustment (silly idea anyway)
145                         // no encryption key (deprecated)
146                     "\r\na=tool:"PACKAGE_STRING
147                     "\r\na=recvonly"
148                     "\r\na=type:broadcast"
149                     "\r\na=charset:UTF-8"
150                     "%s" // optional source filter
151                     "\r\n",
152                /* o= */ now, now, connection[5], hostname,
153                /* s= */ name,
154                /* i= */ description,
155                /* u= */ preurl, url,
156                /* e= */ premail, email,
157                /* p= */ prephone, phone,
158                /* c= */ connection,
159     /* source-filter */ sfilter) == -1)
160         return NULL;
161     return sdp;
162 }
163
164
165 static char *
166 vsdp_AddAttribute (char **sdp, const char *name, const char *fmt, va_list ap)
167 {
168     size_t oldlen = strlen (*sdp);
169     size_t addlen = sizeof ("a=\r\n") + strlen (name);
170
171     if (fmt != NULL)
172     {
173         va_list aq;
174
175         va_copy (aq, ap);
176         addlen += 1 + vsnprintf (NULL, 0, fmt, aq);
177         va_end (aq);
178     }
179
180     char *ret = realloc (*sdp, oldlen + addlen);
181     if (ret == NULL)
182         return NULL;
183
184     oldlen += sprintf (ret + oldlen, "a=%s", name);
185     if (fmt != NULL)
186     {
187         ret[oldlen++] = ':';
188         oldlen += vsprintf (ret + oldlen, fmt, ap);
189     }
190
191     strcpy (ret + oldlen, "\r\n");
192     return *sdp = ret;
193 }
194
195
196 char *sdp_AddAttribute (char **sdp, const char *name, const char *fmt, ...)
197 {
198     char *ret;
199     va_list ap;
200
201     va_start (ap, fmt);
202     ret = vsdp_AddAttribute (sdp, name, fmt, ap);
203     va_end (ap);
204
205     return ret;
206 }
207
208
209 char *sdp_AddMedia (char **sdp,
210                     const char *type, const char *protocol, int dport,
211                     unsigned pt, vlc_bool_t bw_indep, unsigned bw,
212                     const char *ptname, unsigned clock, unsigned chans,
213                     const char *fmtp)
214 {
215     char *newsdp, *ptr;
216     size_t inlen = strlen (*sdp), outlen = inlen;
217
218     /* Some default values */
219     if (type == NULL)
220         type = "video";
221     if (protocol == NULL)
222         protocol = "RTP/AVP";
223     assert (pt < 128u);
224
225     outlen += snprintf (NULL, 0,
226                         "m=%s %u %s %d\r\n"
227                         "b=TIAS:%u\r\n"
228                         "b=RR:0\r\n",
229                         type, dport, protocol, pt, bw);
230
231     newsdp = realloc (*sdp, outlen + 1);
232     if (newsdp == NULL)
233         return NULL;
234
235     *sdp = newsdp;
236     ptr = newsdp + inlen;
237
238     ptr += sprintf (ptr, "m=%s %u %s %u\r\n",
239                          type, dport, protocol, pt);
240     if (bw > 0)
241         ptr += sprintf (ptr, "b=%s:%u\r\n", bw_indep ? "TIAS" : "AS", bw);
242     ptr += sprintf (ptr, "b=RR:0\r\n");
243
244     /* RTP payload type map */
245     if (ptname != NULL)
246     {
247         if ((strcmp (type, "audio") == 0) && (chans != 1))
248             sdp_AddAttribute (sdp, "rtpmap", "%u %s/%u/%u", pt, ptname, clock,
249                               chans);
250         else
251             sdp_AddAttribute (sdp, "rtpmap", "%u %s/%u", pt, ptname, clock);
252     }
253     /* Format parameters */
254     if (fmtp != NULL)
255         sdp_AddAttribute (sdp, "fmtp", "%u %s", pt, fmtp);
256
257     return newsdp;
258 }
259
260
261 char *vlc_sdp_Start (vlc_object_t *obj, const char *cfgpref,
262                      const struct sockaddr *src, size_t srclen,
263                      const struct sockaddr *addr, size_t addrlen)
264 {
265     size_t cfglen = strlen (cfgpref);
266     if (cfglen > 100)
267         return NULL;
268
269     char varname[cfglen + sizeof ("description")], *subvar = varname + cfglen;
270     strcpy (varname, cfgpref);
271
272     session_descriptor_t *p_session = calloc (1, sizeof (*p_session));
273     if (p_session == NULL)
274         return NULL;
275
276     strcpy (subvar, "name");
277     char *name = var_GetNonEmptyString (obj, varname);
278     strcpy (subvar, "description");
279     char *description = var_GetNonEmptyString (obj, varname);
280     strcpy (subvar, "url");
281     char *url = var_GetNonEmptyString (obj, varname);
282     strcpy (subvar, "email");
283     char *email = var_GetNonEmptyString (obj, varname);
284     strcpy (subvar, "phone");
285     char *phone = var_GetNonEmptyString (obj, varname);
286
287     char *sdp = sdp_Start (name, description, url, email, phone,
288                            src, srclen, addr, addrlen);
289     free (name);
290     free (description);
291     free (url);
292     free (email);
293     free (phone);
294
295     if (sdp == NULL)
296         return NULL;
297
298     /* Totally non-standard */
299     strcpy (subvar, "group");
300     char *group = var_GetNonEmptyString (obj, varname);
301     if (group != NULL)
302     {
303         sdp_AddAttribute (&sdp, "x-plgroup", "%s", group);
304         free (group);
305     }
306
307     return sdp;
308 }