blob: 5cc34ea47f3864a9d53d5451c6bc0b0d33957b5e [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>
Philipp Maier37d11c82018-02-01 14:38:12 +010027#include <osmocom/mgcp/mgcp_endp.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020028
29#include <errno.h>
30
31struct sdp_rtp_map {
32 /* the type */
33 int payload_type;
34 /* null, static or later dynamic codec name */
35 char *codec_name;
36 /* A pointer to the original line for later parsing */
37 char *map_line;
38
39 int rate;
40 int channels;
41};
42
Philipp Maier8970c492017-10-11 13:33:42 +020043/*! Set codec configuration depending on payload type and codec name.
44 * \param[in] ctx talloc context.
45 * \param[out] codec configuration (caller provided memory).
46 * \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
47 * \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
48 * \returns 0 on success, -1 on failure. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020049int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
50 int payload_type, const char *audio_name)
51{
52 int rate = codec->rate;
53 int channels = codec->channels;
54 char audio_codec[64];
55
56 talloc_free(codec->subtype_name);
57 codec->subtype_name = NULL;
58 talloc_free(codec->audio_name);
59 codec->audio_name = NULL;
60
61 if (payload_type != PTYPE_UNDEFINED)
62 codec->payload_type = payload_type;
63
64 if (!audio_name) {
65 switch (payload_type) {
Philipp Maierd8d7b4b2017-10-18 11:45:35 +020066 case 0:
67 audio_name = "PCMU/8000/1";
68 break;
69 case 3:
70 audio_name = "GSM/8000/1";
71 break;
72 case 8:
73 audio_name = "PCMA/8000/1";
74 break;
75 case 18:
76 audio_name = "G729/8000/1";
77 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020078 default:
Philipp Maierd8d7b4b2017-10-18 11:45:35 +020079 /* Payload type is unknown, don't change rate and
80 * channels. */
81 /* TODO: return value? */
82 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020083 }
84 }
85
86 if (sscanf(audio_name, "%63[^/]/%d/%d",
87 audio_codec, &rate, &channels) < 1)
88 return -EINVAL;
89
90 codec->rate = rate;
91 codec->channels = channels;
92 codec->subtype_name = talloc_strdup(ctx, audio_codec);
93 codec->audio_name = talloc_strdup(ctx, audio_name);
94
95 if (!strcmp(audio_codec, "G729")) {
96 codec->frame_duration_num = 10;
97 codec->frame_duration_den = 1000;
98 } else {
99 codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
100 codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
101 }
102
103 if (payload_type < 0) {
104 payload_type = 96;
105 if (rate == 8000 && channels == 1) {
106 if (!strcmp(audio_codec, "GSM"))
107 payload_type = 3;
108 else if (!strcmp(audio_codec, "PCMA"))
109 payload_type = 8;
110 else if (!strcmp(audio_codec, "PCMU"))
111 payload_type = 0;
112 else if (!strcmp(audio_codec, "G729"))
113 payload_type = 18;
114 }
115
116 codec->payload_type = payload_type;
117 }
118
119 if (channels != 1)
120 LOGP(DLMGCP, LOGL_NOTICE,
121 "Channels != 1 in SDP: '%s'\n", audio_name);
122
123 return 0;
124}
125
Philipp Maier8970c492017-10-11 13:33:42 +0200126static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200127{
128 int i;
129
130 for (i = 0; i < used; ++i) {
131 switch (codecs[i].payload_type) {
132 case 0:
133 codecs[i].codec_name = "PCMU";
134 codecs[i].rate = 8000;
135 codecs[i].channels = 1;
136 break;
137 case 3:
138 codecs[i].codec_name = "GSM";
139 codecs[i].rate = 8000;
140 codecs[i].channels = 1;
141 break;
142 case 8:
143 codecs[i].codec_name = "PCMA";
144 codecs[i].rate = 8000;
145 codecs[i].channels = 1;
146 break;
147 case 18:
148 codecs[i].codec_name = "G729";
149 codecs[i].rate = 8000;
150 codecs[i].channels = 1;
151 break;
152 }
153 }
154}
155
Philipp Maier8970c492017-10-11 13:33:42 +0200156static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
157 int payload, const char *audio_name)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200158{
159 int i;
160
161 for (i = 0; i < used; ++i) {
162 char audio_codec[64];
163 int rate = -1;
164 int channels = -1;
165 if (codecs[i].payload_type != payload)
166 continue;
167 if (sscanf(audio_name, "%63[^/]/%d/%d",
Philipp Maierd8d7b4b2017-10-18 11:45:35 +0200168 audio_codec, &rate, &channels) < 1) {
169 LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n",
170 audio_name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200171 continue;
172 }
173
174 codecs[i].map_line = talloc_strdup(ctx, audio_name);
175 codecs[i].codec_name = talloc_strdup(ctx, audio_codec);
176 codecs[i].rate = rate;
177 codecs[i].channels = channels;
178 return;
179 }
180
Philipp Maierd8d7b4b2017-10-18 11:45:35 +0200181 LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload,
182 audio_name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200183}
184
Philipp Maier8970c492017-10-11 13:33:42 +0200185/* Check if the codec matches what is set up in the trunk config */
186static int is_codec_compatible(const struct mgcp_endpoint *endp,
187 const struct sdp_rtp_map *codec)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200188{
Philipp Maiere472b4e2017-10-18 11:38:17 +0200189 char *codec_str;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200190 char audio_codec[64];
191
192 if (!codec->codec_name)
193 return 0;
194
Philipp Maier31c43052017-10-18 11:44:37 +0200195 /* GSM, GSM/8000 and GSM/8000/1 should all be compatible...
196 * let's go by name first. */
Philipp Maiere472b4e2017-10-18 11:38:17 +0200197 codec_str = endp->tcfg->audio_name;
198 if (sscanf(codec_str, "%63[^/]/%*d/%*d", audio_codec) < 1)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200199 return 0;
200
201 return strcasecmp(audio_codec, codec->codec_name) == 0;
202}
203
Philipp Maier8970c492017-10-11 13:33:42 +0200204/*! Analyze SDP input string.
205 * \param[in] endp trunk endpoint.
206 * \param[out] conn associated rtp connection.
207 * \param[out] caller provided memory to store the parsing results.
Philipp Maierdbd70c72018-05-25 11:07:31 +0200208 * \returns 1 when a codec is assigned, 0 when no codec is assigned
Philipp Maier8970c492017-10-11 13:33:42 +0200209 *
210 * Note: In conn (conn->end) the function returns the packet duration,
Philipp Maierdbd70c72018-05-25 11:07:31 +0200211 * rtp port, rtcp port and the assigned codec. */
Philipp Maier8970c492017-10-11 13:33:42 +0200212int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
213 struct mgcp_conn_rtp *conn,
214 struct mgcp_parse_data *p)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200215{
216 struct sdp_rtp_map codecs[10];
217 int codecs_used = 0;
218 char *line;
219 int maxptime = -1;
220 int i;
Philipp Maierdbd70c72018-05-25 11:07:31 +0200221 bool codec_assigned = false;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200222 void *tmp_ctx = talloc_new(NULL);
Philipp Maier8970c492017-10-11 13:33:42 +0200223 struct mgcp_rtp_end *rtp;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200224
Philipp Maierc7a228a2017-10-18 11:42:25 +0200225 int payload;
226 int ptime, ptime2 = 0;
227 char audio_name[64];
228 int port, rc;
229 char ipv4[16];
230
Philipp Maier8970c492017-10-11 13:33:42 +0200231 OSMO_ASSERT(endp);
232 OSMO_ASSERT(conn);
233 OSMO_ASSERT(p);
234
235 rtp = &conn->end;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200236 memset(&codecs, 0, sizeof(codecs));
237
238 for_each_line(line, p->save) {
239 switch (line[0]) {
240 case 'o':
241 case 's':
242 case 't':
243 case 'v':
244 /* skip these SDP attributes */
245 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200246 case 'a':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200247 if (sscanf(line, "a=rtpmap:%d %63s",
248 &payload, audio_name) == 2) {
Philipp Maierc7a228a2017-10-18 11:42:25 +0200249 codecs_update(tmp_ctx, codecs,
250 codecs_used, payload, audio_name);
251 } else
252 if (sscanf
253 (line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200254 if (ptime2 > 0 && ptime2 != ptime)
255 rtp->packet_duration_ms = 0;
256 else
257 rtp->packet_duration_ms = ptime;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200258 } else if (sscanf(line, "a=maxptime:%d", &ptime2)
259 == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200260 maxptime = ptime2;
261 }
262 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200263 case 'm':
264 rc = sscanf(line,
265 "m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d",
266 &port, &codecs[0].payload_type,
267 &codecs[1].payload_type,
268 &codecs[2].payload_type,
269 &codecs[3].payload_type,
270 &codecs[4].payload_type,
271 &codecs[5].payload_type,
272 &codecs[6].payload_type,
273 &codecs[7].payload_type,
274 &codecs[8].payload_type,
275 &codecs[9].payload_type);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200276 if (rc >= 2) {
277 rtp->rtp_port = htons(port);
278 rtp->rtcp_port = htons(port + 1);
279 codecs_used = rc - 1;
Philipp Maierdbd70c72018-05-25 11:07:31 +0200280
281 /* So far we have only set the payload type in
282 * the codec struct. Now we fill up the
283 * remaining fields of the codec description */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200284 codecs_initialize(tmp_ctx, codecs, codecs_used);
285 }
286 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200287 case 'c':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200288
289 if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
290 inet_aton(ipv4, &rtp->addr);
291 }
292 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200293 default:
294 if (p->endp)
295 LOGP(DLMGCP, LOGL_NOTICE,
296 "Unhandled SDP option: '%c'/%d on 0x%x\n",
Philipp Maierc7a228a2017-10-18 11:42:25 +0200297 line[0], line[0],
298 ENDPOINT_NUMBER(p->endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200299 else
300 LOGP(DLMGCP, LOGL_NOTICE,
301 "Unhandled SDP option: '%c'/%d\n",
302 line[0], line[0]);
303 break;
304 }
305 }
306
Philipp Maierdbd70c72018-05-25 11:07:31 +0200307 /* Now select a suitable codec */
308 for (i = 0; i < codecs_used; i++) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200309
Philipp Maierdbd70c72018-05-25 11:07:31 +0200310 /* When no transcoding is available, avoid codecs that would
311 * require transcoding. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200312 if (endp->tcfg->no_audio_transcoding &&
Philipp Maierc7a228a2017-10-18 11:42:25 +0200313 !is_codec_compatible(endp, &codecs[i])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200314 LOGP(DLMGCP, LOGL_NOTICE, "Skipping codec %s\n",
Philipp Maierc7a228a2017-10-18 11:42:25 +0200315 codecs[i].codec_name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200316 continue;
317 }
318
Philipp Maierdbd70c72018-05-25 11:07:31 +0200319 mgcp_set_audio_info(p->cfg, &rtp->codec,
Philipp Maierc7a228a2017-10-18 11:42:25 +0200320 codecs[i].payload_type, codecs[i].map_line);
Philipp Maierdbd70c72018-05-25 11:07:31 +0200321
322 codec_assigned = true;
323 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200324 }
325
Philipp Maierdbd70c72018-05-25 11:07:31 +0200326 if (codec_assigned) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200327 /* TODO/XXX: Store this per codec and derive it on use */
328 if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den >
Philipp Maierc7a228a2017-10-18 11:42:25 +0200329 rtp->codec.frame_duration_num * 1500) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200330 /* more than 1 frame */
331 rtp->packet_duration_ms = 0;
332 }
333
334 LOGP(DLMGCP, LOGL_NOTICE,
335 "Got media info via SDP: port %d, payload %d (%s), "
336 "duration %d, addr %s\n",
337 ntohs(rtp->rtp_port), rtp->codec.payload_type,
Philipp Maierc7a228a2017-10-18 11:42:25 +0200338 rtp->codec.subtype_name ? rtp->
339 codec.subtype_name : "unknown", rtp->packet_duration_ms,
340 inet_ntoa(rtp->addr));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200341 }
342
343 talloc_free(tmp_ctx);
Philipp Maierdbd70c72018-05-25 11:07:31 +0200344
345 if (codec_assigned)
346 return 1;
347 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200348}
349
Philipp Maier8970c492017-10-11 13:33:42 +0200350/*! Generate SDP response string.
351 * \param[in] endp trunk endpoint.
352 * \param[in] conn associated rtp connection.
353 * \param[out] sdp msg buffer to append resulting SDP string data.
354 * \param[in] addr IPV4 address string (e.g. 192.168.100.1).
355 * \returns 0 on success, -1 on failure. */
356int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
357 const struct mgcp_conn_rtp *conn, struct msgb *sdp,
358 const char *addr)
359{
360 const char *fmtp_extra;
361 const char *audio_name;
362 int payload_type;
363 int rc;
364
365 OSMO_ASSERT(endp);
366 OSMO_ASSERT(conn);
367 OSMO_ASSERT(sdp);
368 OSMO_ASSERT(addr);
369
370 /* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
371 endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
372 &payload_type, &audio_name,
373 &fmtp_extra,
374 (struct mgcp_conn_rtp *)conn);
375
376 rc = msgb_printf(sdp,
377 "v=0\r\n"
Philipp Maier01d24a32017-11-21 17:26:09 +0100378 "o=- %s 23 IN IP4 %s\r\n"
Philipp Maier8970c492017-10-11 13:33:42 +0200379 "s=-\r\n"
380 "c=IN IP4 %s\r\n"
381 "t=0 0\r\n", conn->conn->id, addr, addr);
382
383 if (rc < 0)
384 goto buffer_too_small;
385
386 if (payload_type >= 0) {
387 rc = msgb_printf(sdp, "m=audio %d RTP/AVP %d\r\n",
388 conn->end.local_port, payload_type);
389 if (rc < 0)
390 goto buffer_too_small;
391
392 if (audio_name && endp->tcfg->audio_send_name) {
393 rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n",
394 payload_type, audio_name);
395
396 if (rc < 0)
397 goto buffer_too_small;
398 }
399
400 if (fmtp_extra) {
401 rc = msgb_printf(sdp, "%s\r\n", fmtp_extra);
402
403 if (rc < 0)
404 goto buffer_too_small;
405 }
406 }
407 if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
408 rc = msgb_printf(sdp, "a=ptime:%u\r\n",
409 conn->end.packet_duration_ms);
410 if (rc < 0)
411 goto buffer_too_small;
412 }
413
414 return 0;
415
416buffer_too_small:
417 LOGP(DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
418 return -1;
419}