blob: 666b8c259c4e5b71160235158087f06550358745 [file] [log] [blame]
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001/*
2 * Some SDP file parsing...
3 *
4 * (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
5 * (C) 2009-2014 by On-Waves
6 * All Rights Reserved
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
Philipp Maier8970c492017-10-11 13:33:42 +020023#include <osmocom/core/msgb.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020024#include <osmocom/mgcp/mgcp.h>
25#include <osmocom/mgcp/mgcp_internal.h>
26#include <osmocom/mgcp/mgcp_msg.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020027
28#include <errno.h>
29
30struct sdp_rtp_map {
31 /* the type */
32 int payload_type;
33 /* null, static or later dynamic codec name */
34 char *codec_name;
35 /* A pointer to the original line for later parsing */
36 char *map_line;
37
38 int rate;
39 int channels;
40};
41
Philipp Maier8970c492017-10-11 13:33:42 +020042/*! Set codec configuration depending on payload type and codec name.
43 * \param[in] ctx talloc context.
44 * \param[out] codec configuration (caller provided memory).
45 * \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
46 * \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
47 * \returns 0 on success, -1 on failure. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020048int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
49 int payload_type, const char *audio_name)
50{
51 int rate = codec->rate;
52 int channels = codec->channels;
53 char audio_codec[64];
54
55 talloc_free(codec->subtype_name);
56 codec->subtype_name = NULL;
57 talloc_free(codec->audio_name);
58 codec->audio_name = NULL;
59
60 if (payload_type != PTYPE_UNDEFINED)
61 codec->payload_type = payload_type;
62
63 if (!audio_name) {
64 switch (payload_type) {
Philipp Maierd8d7b4b2017-10-18 11:45:35 +020065 case 0:
66 audio_name = "PCMU/8000/1";
67 break;
68 case 3:
69 audio_name = "GSM/8000/1";
70 break;
71 case 8:
72 audio_name = "PCMA/8000/1";
73 break;
74 case 18:
75 audio_name = "G729/8000/1";
76 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020077 default:
Philipp Maierd8d7b4b2017-10-18 11:45:35 +020078 /* Payload type is unknown, don't change rate and
79 * channels. */
80 /* TODO: return value? */
81 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020082 }
83 }
84
85 if (sscanf(audio_name, "%63[^/]/%d/%d",
86 audio_codec, &rate, &channels) < 1)
87 return -EINVAL;
88
89 codec->rate = rate;
90 codec->channels = channels;
91 codec->subtype_name = talloc_strdup(ctx, audio_codec);
92 codec->audio_name = talloc_strdup(ctx, audio_name);
93
94 if (!strcmp(audio_codec, "G729")) {
95 codec->frame_duration_num = 10;
96 codec->frame_duration_den = 1000;
97 } else {
98 codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
99 codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
100 }
101
102 if (payload_type < 0) {
103 payload_type = 96;
104 if (rate == 8000 && channels == 1) {
105 if (!strcmp(audio_codec, "GSM"))
106 payload_type = 3;
107 else if (!strcmp(audio_codec, "PCMA"))
108 payload_type = 8;
109 else if (!strcmp(audio_codec, "PCMU"))
110 payload_type = 0;
111 else if (!strcmp(audio_codec, "G729"))
112 payload_type = 18;
113 }
114
115 codec->payload_type = payload_type;
116 }
117
118 if (channels != 1)
119 LOGP(DLMGCP, LOGL_NOTICE,
120 "Channels != 1 in SDP: '%s'\n", audio_name);
121
122 return 0;
123}
124
Philipp Maier8970c492017-10-11 13:33:42 +0200125static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200126{
127 int i;
128
129 for (i = 0; i < used; ++i) {
130 switch (codecs[i].payload_type) {
131 case 0:
132 codecs[i].codec_name = "PCMU";
133 codecs[i].rate = 8000;
134 codecs[i].channels = 1;
135 break;
136 case 3:
137 codecs[i].codec_name = "GSM";
138 codecs[i].rate = 8000;
139 codecs[i].channels = 1;
140 break;
141 case 8:
142 codecs[i].codec_name = "PCMA";
143 codecs[i].rate = 8000;
144 codecs[i].channels = 1;
145 break;
146 case 18:
147 codecs[i].codec_name = "G729";
148 codecs[i].rate = 8000;
149 codecs[i].channels = 1;
150 break;
151 }
152 }
153}
154
Philipp Maier8970c492017-10-11 13:33:42 +0200155static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
156 int payload, const char *audio_name)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200157{
158 int i;
159
160 for (i = 0; i < used; ++i) {
161 char audio_codec[64];
162 int rate = -1;
163 int channels = -1;
164 if (codecs[i].payload_type != payload)
165 continue;
166 if (sscanf(audio_name, "%63[^/]/%d/%d",
Philipp Maierd8d7b4b2017-10-18 11:45:35 +0200167 audio_codec, &rate, &channels) < 1) {
168 LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n",
169 audio_name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200170 continue;
171 }
172
173 codecs[i].map_line = talloc_strdup(ctx, audio_name);
174 codecs[i].codec_name = talloc_strdup(ctx, audio_codec);
175 codecs[i].rate = rate;
176 codecs[i].channels = channels;
177 return;
178 }
179
Philipp Maierd8d7b4b2017-10-18 11:45:35 +0200180 LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload,
181 audio_name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200182}
183
Philipp Maier8970c492017-10-11 13:33:42 +0200184/* Check if the codec matches what is set up in the trunk config */
185static int is_codec_compatible(const struct mgcp_endpoint *endp,
186 const struct sdp_rtp_map *codec)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200187{
Philipp Maiere472b4e2017-10-18 11:38:17 +0200188 char *codec_str;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200189 char audio_codec[64];
190
191 if (!codec->codec_name)
192 return 0;
193
Philipp Maier31c43052017-10-18 11:44:37 +0200194 /* GSM, GSM/8000 and GSM/8000/1 should all be compatible...
195 * let's go by name first. */
Philipp Maiere472b4e2017-10-18 11:38:17 +0200196 codec_str = endp->tcfg->audio_name;
197 if (sscanf(codec_str, "%63[^/]/%*d/%*d", audio_codec) < 1)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200198 return 0;
199
200 return strcasecmp(audio_codec, codec->codec_name) == 0;
201}
202
Philipp Maier8970c492017-10-11 13:33:42 +0200203/*! Analyze SDP input string.
204 * \param[in] endp trunk endpoint.
205 * \param[out] conn associated rtp connection.
206 * \param[out] caller provided memory to store the parsing results.
207 * \returns 0 on success, -1 on failure.
208 *
209 * Note: In conn (conn->end) the function returns the packet duration,
210 * the rtp port and the rtcp port */
211int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
212 struct mgcp_conn_rtp *conn,
213 struct mgcp_parse_data *p)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200214{
215 struct sdp_rtp_map codecs[10];
216 int codecs_used = 0;
217 char *line;
218 int maxptime = -1;
219 int i;
220 int codecs_assigned = 0;
221 void *tmp_ctx = talloc_new(NULL);
Philipp Maier8970c492017-10-11 13:33:42 +0200222 struct mgcp_rtp_end *rtp;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200223
Philipp Maierc7a228a2017-10-18 11:42:25 +0200224 int payload;
225 int ptime, ptime2 = 0;
226 char audio_name[64];
227 int port, rc;
228 char ipv4[16];
229
Philipp Maier8970c492017-10-11 13:33:42 +0200230 OSMO_ASSERT(endp);
231 OSMO_ASSERT(conn);
232 OSMO_ASSERT(p);
233
234 rtp = &conn->end;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200235 memset(&codecs, 0, sizeof(codecs));
236
237 for_each_line(line, p->save) {
238 switch (line[0]) {
239 case 'o':
240 case 's':
241 case 't':
242 case 'v':
243 /* skip these SDP attributes */
244 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200245 case 'a':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200246 if (sscanf(line, "a=rtpmap:%d %63s",
247 &payload, audio_name) == 2) {
Philipp Maierc7a228a2017-10-18 11:42:25 +0200248 codecs_update(tmp_ctx, codecs,
249 codecs_used, payload, audio_name);
250 } else
251 if (sscanf
252 (line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200253 if (ptime2 > 0 && ptime2 != ptime)
254 rtp->packet_duration_ms = 0;
255 else
256 rtp->packet_duration_ms = ptime;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200257 } else if (sscanf(line, "a=maxptime:%d", &ptime2)
258 == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200259 maxptime = ptime2;
260 }
261 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200262 case 'm':
263 rc = sscanf(line,
264 "m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d",
265 &port, &codecs[0].payload_type,
266 &codecs[1].payload_type,
267 &codecs[2].payload_type,
268 &codecs[3].payload_type,
269 &codecs[4].payload_type,
270 &codecs[5].payload_type,
271 &codecs[6].payload_type,
272 &codecs[7].payload_type,
273 &codecs[8].payload_type,
274 &codecs[9].payload_type);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200275 if (rc >= 2) {
276 rtp->rtp_port = htons(port);
277 rtp->rtcp_port = htons(port + 1);
278 codecs_used = rc - 1;
279 codecs_initialize(tmp_ctx, codecs, codecs_used);
280 }
281 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200282 case 'c':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200283
284 if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
285 inet_aton(ipv4, &rtp->addr);
286 }
287 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200288 default:
289 if (p->endp)
290 LOGP(DLMGCP, LOGL_NOTICE,
291 "Unhandled SDP option: '%c'/%d on 0x%x\n",
Philipp Maierc7a228a2017-10-18 11:42:25 +0200292 line[0], line[0],
293 ENDPOINT_NUMBER(p->endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200294 else
295 LOGP(DLMGCP, LOGL_NOTICE,
296 "Unhandled SDP option: '%c'/%d\n",
297 line[0], line[0]);
298 break;
299 }
300 }
301
302 /* Now select the primary and alt_codec */
303 for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) {
304 struct mgcp_rtp_codec *codec = codecs_assigned == 0 ?
Philipp Maierc7a228a2017-10-18 11:42:25 +0200305 &rtp->codec : &rtp->alt_codec;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200306
307 if (endp->tcfg->no_audio_transcoding &&
Philipp Maierc7a228a2017-10-18 11:42:25 +0200308 !is_codec_compatible(endp, &codecs[i])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200309 LOGP(DLMGCP, LOGL_NOTICE, "Skipping codec %s\n",
Philipp Maierc7a228a2017-10-18 11:42:25 +0200310 codecs[i].codec_name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200311 continue;
312 }
313
314 mgcp_set_audio_info(p->cfg, codec,
Philipp Maierc7a228a2017-10-18 11:42:25 +0200315 codecs[i].payload_type, codecs[i].map_line);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200316 codecs_assigned += 1;
317 }
318
319 if (codecs_assigned > 0) {
320 /* TODO/XXX: Store this per codec and derive it on use */
321 if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den >
Philipp Maierc7a228a2017-10-18 11:42:25 +0200322 rtp->codec.frame_duration_num * 1500) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200323 /* more than 1 frame */
324 rtp->packet_duration_ms = 0;
325 }
326
327 LOGP(DLMGCP, LOGL_NOTICE,
328 "Got media info via SDP: port %d, payload %d (%s), "
329 "duration %d, addr %s\n",
330 ntohs(rtp->rtp_port), rtp->codec.payload_type,
Philipp Maierc7a228a2017-10-18 11:42:25 +0200331 rtp->codec.subtype_name ? rtp->
332 codec.subtype_name : "unknown", rtp->packet_duration_ms,
333 inet_ntoa(rtp->addr));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200334 }
335
336 talloc_free(tmp_ctx);
337 return codecs_assigned > 0;
338}
339
Philipp Maier8970c492017-10-11 13:33:42 +0200340/*! Generate SDP response string.
341 * \param[in] endp trunk endpoint.
342 * \param[in] conn associated rtp connection.
343 * \param[out] sdp msg buffer to append resulting SDP string data.
344 * \param[in] addr IPV4 address string (e.g. 192.168.100.1).
345 * \returns 0 on success, -1 on failure. */
346int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
347 const struct mgcp_conn_rtp *conn, struct msgb *sdp,
348 const char *addr)
349{
350 const char *fmtp_extra;
351 const char *audio_name;
352 int payload_type;
353 int rc;
354
355 OSMO_ASSERT(endp);
356 OSMO_ASSERT(conn);
357 OSMO_ASSERT(sdp);
358 OSMO_ASSERT(addr);
359
360 /* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
361 endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
362 &payload_type, &audio_name,
363 &fmtp_extra,
364 (struct mgcp_conn_rtp *)conn);
365
366 rc = msgb_printf(sdp,
367 "v=0\r\n"
Philipp Maier01d24a32017-11-21 17:26:09 +0100368 "o=- %s 23 IN IP4 %s\r\n"
Philipp Maier8970c492017-10-11 13:33:42 +0200369 "s=-\r\n"
370 "c=IN IP4 %s\r\n"
371 "t=0 0\r\n", conn->conn->id, addr, addr);
372
373 if (rc < 0)
374 goto buffer_too_small;
375
376 if (payload_type >= 0) {
377 rc = msgb_printf(sdp, "m=audio %d RTP/AVP %d\r\n",
378 conn->end.local_port, payload_type);
379 if (rc < 0)
380 goto buffer_too_small;
381
382 if (audio_name && endp->tcfg->audio_send_name) {
383 rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n",
384 payload_type, audio_name);
385
386 if (rc < 0)
387 goto buffer_too_small;
388 }
389
390 if (fmtp_extra) {
391 rc = msgb_printf(sdp, "%s\r\n", fmtp_extra);
392
393 if (rc < 0)
394 goto buffer_too_small;
395 }
396 }
397 if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
398 rc = msgb_printf(sdp, "a=ptime:%u\r\n",
399 conn->end.packet_duration_ms);
400 if (rc < 0)
401 goto buffer_too_small;
402 }
403
404 return 0;
405
406buffer_too_small:
407 LOGP(DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
408 return -1;
409}