]> git.sesse.net Git - vlc/blob - src/stream_output/sdp.c
Add helper for SDP media description formatting
[vlc] / src / stream_output / sdp.c
1 /*****************************************************************************
2  * sdp.c : SDP creation helpers
3  *****************************************************************************
4  * Copyright © 2007 Rémi Denis-Courmont
5  * $Id$
6  *
7  * Authors: Rémi Denis-Courmont
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #include <vlc/vlc.h>
25
26 #include <string.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <assert.h>
30 #include <vlc_network.h>
31 #include <vlc_charset.h>
32
33 #include "stream_output.h"
34
35 #define MAXSDPADDRESS 47
36
37 static
38 char *AddressToSDP (const struct sockaddr *addr, socklen_t addrlen, char *buf)
39 {
40     if (addrlen < offsetof (struct sockaddr, sa_family)
41                  + sizeof (addr->sa_family))
42         return NULL;
43
44     strcpy (buf, "IN IP* ");
45
46     if (vlc_getnameinfo (addr, addrlen, buf + 7, MAXSDPADDRESS - 7, NULL,
47                          NI_NUMERICHOST))
48         return NULL;
49
50     switch (addr->sa_family)
51     {
52         case AF_INET:
53         {
54             if (net_SockAddrIsMulticast (addr, addrlen))
55                 strcat (buf, "/255"); // obsolete in RFC4566, dummy value
56             buf[5] = '4';
57             break;
58         }
59
60 #ifdef AF_INET6
61         case AF_INET6:
62         {
63             char *ptr = strchr (buf, '%');
64             if (ptr != NULL)
65                 *ptr = '\0'; // remove scope ID
66             buf[5] = '6';
67             break;
68         }
69 #endif
70
71         default:
72             return NULL;
73     }
74
75     return buf;
76 }
77
78
79 static vlc_bool_t IsSDPString (const char *str)
80 {
81     if (strchr (str, '\r') != NULL)
82         return VLC_FALSE;
83     if (strchr (str, '\n') != NULL)
84         return VLC_FALSE;
85     if (!IsUTF8 (str))
86         return VLC_FALSE;
87     return VLC_TRUE;
88 }
89
90
91 char *StartSDP (const char *name, const char *description, const char *url,
92                 const char *email, const char *phone, vlc_bool_t ssm,
93                 const struct sockaddr *orig, socklen_t origlen,
94                 const struct sockaddr *addr, socklen_t addrlen)
95 {
96     uint64_t t = NTPtime64 ();
97     char *sdp, machine[MAXSDPADDRESS], conn[MAXSDPADDRESS],
98          sfilter[MAXSDPADDRESS + sizeof ("\r\na=source-filter: incl * ")];
99     const char *preurl = "\r\nu=", *premail = "\r\ne=", *prephone = "\r\np=";
100
101     if (name == NULL)
102         name = "Unnamed";
103     if (description == NULL)
104         description = "N/A";
105     if (url == NULL)
106         preurl = url = "";
107     if (email == NULL)
108         premail = email = "";
109     if (phone == NULL)
110         prephone = phone = "";
111
112     if (!IsSDPString (name) || !IsSDPString (description)
113      || !IsSDPString (url) || !IsSDPString (email) || !IsSDPString (phone)
114      || (AddressToSDP (orig, origlen, machine) == NULL)
115      || (AddressToSDP (addr, addrlen, conn) == NULL))
116         return NULL;
117
118     if (ssm)
119         sprintf (sfilter, "\r\na=source-filter: incl IN IP%c * %s",
120                  machine[5], machine + 7);
121     else
122         *sfilter = '\0';
123
124     if (asprintf (&sdp, "v=0"
125                     "\r\no=- "I64Fu" "I64Fu" %s"
126                     "\r\ns=%s"
127                     "\r\ni=%s"
128                     "%s%s" // optional URL
129                     "%s%s" // optional email
130                     "%s%s" // optional phone number
131                     "\r\nc=%s"
132                         // bandwidth not specified
133                     "\r\nt=0 0" // one dummy time span
134                         // no repeating
135                         // no time zone adjustment (silly idea anyway)
136                         // no encryption key (deprecated)
137                     "\r\na=tool:"PACKAGE_STRING
138                     "\r\na=recvonly"
139                     "\r\na=type:broadcast"
140                     "\r\na=charset:UTF-8"
141                     "%s" // optional source filter
142                     "\r\n",
143                /* o= */ t, t, machine,
144                /* s= */ name,
145                /* i= */ description,
146                /* u= */ preurl, url,
147                /* e= */ premail, email,
148                /* p= */ prephone, phone,
149                /* c= */ conn,
150     /* source-filter */ sfilter) == -1)
151         return NULL;
152     return sdp;
153 }
154
155
156 char *MakeSDPMedia (const char *type, int dport, const char *protocol,
157                     unsigned pt, const char *rtpmap, const char *fmtpfmt, ...)
158 {
159     char *sdp_media = NULL;
160
161     /* Some default values */
162     if (type == NULL)
163         type = "video";
164     if (dport == 0)
165         dport = 9;
166     if (protocol == NULL)
167         protocol = "RTP/AVP";
168     assert (pt < 128u);
169
170     /* RTP payload type map */
171     char sdp_rtpmap[rtpmap ? (sizeof ("a=rtpmap:123 *\r\n") + strlen (rtpmap)) : 1];
172     if (rtpmap != NULL)
173         sprintf (sdp_rtpmap, "a=rtpmap:%u %s\r\n", pt, rtpmap);
174     else
175         *sdp_rtpmap = '\0';
176
177     /* Format parameters */
178     char *fmtp = NULL;
179     if (fmtpfmt != NULL)
180     {
181         va_list ap;
182
183         va_start (ap, fmtpfmt);
184         if (vasprintf (&fmtp, fmtpfmt, ap) == -1)
185             fmtpfmt = NULL;
186         va_end (ap);
187
188         if (fmtp == NULL)
189             return NULL;
190     }
191
192     char sdp_fmtp[fmtp ? (sizeof ("a=fmtp:123 *\r\n") + strlen (fmtp)) : 1];
193     if (fmtp != NULL)
194     {
195         sprintf (sdp_fmtp, "a=fmtp:%u %s\r\n", pt, fmtp);
196         free (fmtp);
197     }
198     else
199         *sdp_fmtp = '\0';
200
201     if (asprintf (&sdp_media, "m=%s %u %s %d\r\n" "%s" "%s",
202                   type, dport, protocol, pt,
203                   sdp_rtpmap, sdp_fmtp) == -1)
204         return NULL;
205
206     return sdp_media;
207 }