Add copyright notices to all relevant files. (based on svn log)
[profile/ivi/pulseaudio.git] / src / modules / rtp / sdp.c
1 /* $Id$ */
2
3 /***
4   This file is part of PulseAudio.
5
6   Copyright 2006 Lennart Poettering
7  
8   PulseAudio is free software; you can redistribute it and/or modify
9   it under the terms of the GNU Lesser General Public License as published
10   by the Free Software Foundation; either version 2 of the License,
11   or (at your option) any later version.
12  
13   PulseAudio is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   General Public License for more details.
17  
18   You should have received a copy of the GNU Lesser General Public License
19   along with PulseAudio; if not, write to the Free Software
20   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21   USA.
22 ***/
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <assert.h>
29 #include <time.h>
30 #include <stdlib.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <string.h>
36
37 #include <pulse/xmalloc.h>
38
39 #include <pulsecore/core-util.h>
40 #include <pulsecore/log.h>
41
42 #include "sdp.h"
43 #include "rtp.h"
44
45
46 char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, uint16_t port, uint8_t payload, const pa_sample_spec *ss) {
47     uint32_t ntp;
48     char buf_src[64], buf_dst[64];
49     const char *u, *f, *a;
50
51     assert(src);
52     assert(dst);
53     assert(af == AF_INET || af == AF_INET6);
54
55     f = pa_rtp_format_to_string(ss->format);
56     assert(f);
57     
58     if (!(u = getenv("USER")))
59         if (!(u = getenv("USERNAME")))
60             u = "-";
61     
62     ntp = time(NULL) + 2208988800U;
63
64     a = inet_ntop(af, src, buf_src, sizeof(buf_src));
65     assert(a);
66     a = inet_ntop(af, dst, buf_dst, sizeof(buf_dst));
67     assert(a);
68     
69     return pa_sprintf_malloc(
70             PA_SDP_HEADER
71             "o=%s %lu 0 IN %s %s\n"
72             "s=%s\n"
73             "c=IN %s %s\n"
74             "t=%lu 0\n"
75             "a=recvonly\n"
76             "m=audio %u RTP/AVP %i\n"
77             "a=rtpmap:%i %s/%u/%u\n"
78             "a=type:broadcast\n",
79             u, (unsigned long) ntp, af == AF_INET ? "IP4" : "IP6", buf_src,
80             name,
81             af == AF_INET ? "IP4" : "IP6", buf_dst,
82             (unsigned long) ntp,
83             port, payload,
84             payload, f, ss->rate, ss->channels);
85 }
86
87 static pa_sample_spec *parse_sdp_sample_spec(pa_sample_spec *ss, char *c) {
88     unsigned rate, channels;
89     assert(ss);
90     assert(c);
91
92     if (pa_startswith(c, "L16/")) {
93         ss->format = PA_SAMPLE_S16BE;
94         c += 4;
95     } else if (pa_startswith(c, "L8/")) {
96         ss->format = PA_SAMPLE_U8;
97         c += 3;
98     } else if (pa_startswith(c, "PCMA/")) {
99         ss->format = PA_SAMPLE_ALAW;
100         c += 5;
101     } else if (pa_startswith(c, "PCMU/")) {
102         ss->format = PA_SAMPLE_ULAW;
103         c += 5;
104     } else
105         return NULL;
106
107     if (sscanf(c, "%u/%u", &rate, &channels) == 2) {
108         ss->rate = rate;
109         ss->channels = channels;
110     } else if (sscanf(c, "%u", &rate) == 2) {
111         ss->rate = rate;
112         ss->channels = 1;
113     } else
114         return NULL;
115
116     if (!pa_sample_spec_valid(ss))
117         return NULL;
118
119     return ss;
120 }
121
122 pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) {
123     uint16_t port = 0;
124     int ss_valid = 0;
125
126     assert(t);
127     assert(i);
128     
129     i->origin = i->session_name = NULL;
130     i->salen = 0;
131     i->payload = 255;
132     
133     if (!pa_startswith(t, PA_SDP_HEADER)) {
134         pa_log("Failed to parse SDP data: invalid header.");
135         goto fail;
136     }
137
138     t += sizeof(PA_SDP_HEADER)-1;
139
140     while (*t) {
141         size_t l;
142
143         l = strcspn(t, "\n");
144
145         if (l <= 2) {
146             pa_log("Failed to parse SDP data: line too short: >%s<.", t);
147             goto fail;
148         }
149
150         if (pa_startswith(t, "o="))
151             i->origin = pa_xstrndup(t+2, l-2);
152         else if (pa_startswith(t, "s="))
153             i->session_name = pa_xstrndup(t+2, l-2);
154         else if (pa_startswith(t, "c=IN IP4 ")) {
155             char a[64];
156             size_t k;
157
158             k = l-8 > sizeof(a) ? sizeof(a) : l-8;
159             
160             pa_strlcpy(a, t+9, k);
161             a[strcspn(a, "/")] = 0;
162
163             if (inet_pton(AF_INET, a, &((struct sockaddr_in*) &i->sa)->sin_addr) <= 0) {
164                 pa_log("Failed to parse SDP data: bad address: >%s<.", a);
165                 goto fail;
166             }
167
168             ((struct sockaddr_in*) &i->sa)->sin_family = AF_INET;
169             ((struct sockaddr_in*) &i->sa)->sin_port = 0;
170             i->salen = sizeof(struct sockaddr_in);
171         } else if (pa_startswith(t, "c=IN IP6 ")) {
172             char a[64];
173             size_t k;
174
175             k = l-8 > sizeof(a) ? sizeof(a) : l-8;
176             
177             pa_strlcpy(a, t+9, k);
178             a[strcspn(a, "/")] = 0;
179
180             if (inet_pton(AF_INET6, a, &((struct sockaddr_in6*) &i->sa)->sin6_addr) <= 0) {
181                 pa_log("Failed to parse SDP data: bad address: >%s<.", a);
182                 goto fail;
183             }
184
185             ((struct sockaddr_in6*) &i->sa)->sin6_family = AF_INET6;
186             ((struct sockaddr_in6*) &i->sa)->sin6_port = 0;
187             i->salen = sizeof(struct sockaddr_in6);
188         } else if (pa_startswith(t, "m=audio ")) {
189
190             if (i->payload > 127) {
191                 int _port, _payload;
192                 
193                 if (sscanf(t+8, "%i RTP/AVP %i", &_port, &_payload) == 2) {
194
195                     if (_port <= 0 || _port > 0xFFFF) {
196                         pa_log("Failed to parse SDP data: invalid port %i.", _port);
197                         goto fail;
198                     }
199
200                     if (_payload < 0 || _payload > 127) {
201                         pa_log("Failed to parse SDP data: invalid payload %i.", _payload);
202                         goto fail;
203                     }
204
205                     port = (uint16_t) _port;
206                     i->payload = (uint8_t) _payload;
207
208                     if (pa_rtp_sample_spec_from_payload(i->payload, &i->sample_spec))
209                         ss_valid = 1;
210                 }
211             }
212         } else if (pa_startswith(t, "a=rtpmap:")) {
213
214             if (i->payload <= 127) {
215                 char c[64];
216                 int _payload;
217
218                 if (sscanf(t+9, "%i %64c", &_payload, c) == 2) {
219
220                     if (_payload < 0 || _payload > 127) {
221                         pa_log("Failed to parse SDP data: invalid payload %i.", _payload);
222                         goto fail;
223                     }
224                     if (_payload == i->payload) {
225
226                         c[strcspn(c, "\n")] = 0;
227                         
228                         if (parse_sdp_sample_spec(&i->sample_spec, c))
229                             ss_valid = 1;
230                     }
231                 }
232             }
233         }
234         
235         t += l;
236         
237         if (*t == '\n')
238             t++;
239     }
240
241     if (!i->origin || (!is_goodbye && (!i->salen || i->payload > 127 || !ss_valid || port == 0))) {
242         pa_log("Failed to parse SDP data: missing data.");
243         goto fail;
244     }
245
246     if (((struct sockaddr*) &i->sa)->sa_family == AF_INET)
247         ((struct sockaddr_in*) &i->sa)->sin_port = htons(port);
248     else
249         ((struct sockaddr_in6*) &i->sa)->sin6_port = htons(port);
250     
251     return i;
252
253 fail:
254     pa_xfree(i->origin);
255     pa_xfree(i->session_name);
256
257     return NULL;
258 }
259
260 void pa_sdp_info_destroy(pa_sdp_info *i) {
261     assert(i);
262
263     pa_xfree(i->origin);
264     pa_xfree(i->session_name);
265 }