mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-10-23 08:12:01 +00:00
The function mgcp_write_response_sdp() is responsible to write the audio port and the list with the supported payload type numbers to the sdp response. At the moment it can only write exactly one payload type number to the response, but in the future we may want to write several payload type numbers to the response. Lets add a function for that so that now. - add add_audio() helper function to add multiple payload type numbers, but keep the functionality as it is for now Change-Id: I662c725f697b2ffb1e3ad4671a445f943cd79b63 Related: OS#3442
403 lines
10 KiB
C
403 lines
10 KiB
C
/*
|
|
* Some SDP file parsing...
|
|
*
|
|
* (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
|
|
* (C) 2009-2014 by On-Waves
|
|
* All Rights Reserved
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include <osmocom/core/msgb.h>
|
|
#include <osmocom/mgcp/mgcp.h>
|
|
#include <osmocom/mgcp/mgcp_internal.h>
|
|
#include <osmocom/mgcp/mgcp_msg.h>
|
|
#include <osmocom/mgcp/mgcp_endp.h>
|
|
#include <osmocom/mgcp/mgcp_codec.h>
|
|
|
|
#include <errno.h>
|
|
|
|
/* A struct to store intermediate parsing results. The function
|
|
* mgcp_parse_sdp_data() is using it as temporary storage for parsing the SDP
|
|
* codec information. */
|
|
struct sdp_rtp_map {
|
|
/* the type */
|
|
int payload_type;
|
|
/* null, static or later dynamic codec name */
|
|
char *codec_name;
|
|
/* A pointer to the original line for later parsing */
|
|
char *map_line;
|
|
|
|
int rate;
|
|
int channels;
|
|
};
|
|
|
|
/* Helper function to extrapolate missing codec parameters in a codec mao from
|
|
* an already filled in payload_type, called from: mgcp_parse_sdp_data() */
|
|
static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < used; ++i) {
|
|
switch (codecs[i].payload_type) {
|
|
case 0:
|
|
codecs[i].codec_name = "PCMU";
|
|
codecs[i].rate = 8000;
|
|
codecs[i].channels = 1;
|
|
break;
|
|
case 3:
|
|
codecs[i].codec_name = "GSM";
|
|
codecs[i].rate = 8000;
|
|
codecs[i].channels = 1;
|
|
break;
|
|
case 8:
|
|
codecs[i].codec_name = "PCMA";
|
|
codecs[i].rate = 8000;
|
|
codecs[i].channels = 1;
|
|
break;
|
|
case 18:
|
|
codecs[i].codec_name = "G729";
|
|
codecs[i].rate = 8000;
|
|
codecs[i].channels = 1;
|
|
break;
|
|
default:
|
|
codecs[i].codec_name = NULL;
|
|
codecs[i].rate = 0;
|
|
codecs[i].channels = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Helper function to update codec map information with additional data from
|
|
* SDP, called from: mgcp_parse_sdp_data() */
|
|
static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
|
|
int payload, const char *audio_name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < used; ++i) {
|
|
char audio_codec[64];
|
|
int rate = -1;
|
|
int channels = -1;
|
|
|
|
/* Note: We can only update payload codecs that already exist
|
|
* in our codec list. If we get an unexpected payload type,
|
|
* we just drop it */
|
|
if (codecs[i].payload_type != payload)
|
|
continue;
|
|
|
|
if (sscanf(audio_name, "%63[^/]/%d/%d",
|
|
audio_codec, &rate, &channels) < 1) {
|
|
LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n",
|
|
audio_name);
|
|
continue;
|
|
}
|
|
|
|
codecs[i].map_line = talloc_strdup(ctx, audio_name);
|
|
codecs[i].codec_name = talloc_strdup(ctx, audio_codec);
|
|
codecs[i].rate = rate;
|
|
codecs[i].channels = channels;
|
|
return;
|
|
}
|
|
|
|
LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload,
|
|
audio_name);
|
|
}
|
|
|
|
/* Extract payload types from SDP, also check for duplicates */
|
|
static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
|
|
unsigned int codecs_len, char *sdp)
|
|
{
|
|
char *str;
|
|
char *str_ptr;
|
|
char *pt_str;
|
|
unsigned int pt;
|
|
unsigned int count = 0;
|
|
unsigned int i;
|
|
|
|
str = talloc_zero_size(ctx, strlen(sdp) + 1);
|
|
str_ptr = str;
|
|
strcpy(str_ptr, sdp);
|
|
|
|
str_ptr = strstr(str_ptr, "RTP/AVP ");
|
|
if (!str_ptr)
|
|
goto exit;
|
|
|
|
pt_str = strtok(str_ptr, " ");
|
|
if (!pt_str)
|
|
goto exit;
|
|
|
|
while (1) {
|
|
/* Do not allow excessive payload types */
|
|
if (count > codecs_len)
|
|
goto error;
|
|
|
|
pt_str = strtok(NULL, " ");
|
|
if (!pt_str)
|
|
break;
|
|
|
|
pt = atoi(pt_str);
|
|
|
|
/* Do not allow duplicate payload types */
|
|
for (i = 0; i < count; i++)
|
|
if (codecs[i].payload_type == pt)
|
|
goto error;
|
|
|
|
codecs[count].payload_type = pt;
|
|
count++;
|
|
}
|
|
|
|
exit:
|
|
talloc_free(str);
|
|
return count;
|
|
error:
|
|
talloc_free(str);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*! Analyze SDP input string.
|
|
* \param[in] endp trunk endpoint.
|
|
* \param[out] conn associated rtp connection.
|
|
* \param[out] caller provided memory to store the parsing results.
|
|
*
|
|
* Note: In conn (conn->end) the function returns the packet duration,
|
|
* rtp port, rtcp port and the codec information.
|
|
* \returns 0 on success, -1 on failure. */
|
|
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
|
struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p)
|
|
{
|
|
struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
|
|
unsigned int codecs_used = 0;
|
|
char *line;
|
|
unsigned int i;
|
|
void *tmp_ctx = talloc_new(NULL);
|
|
struct mgcp_rtp_end *rtp;
|
|
|
|
int payload;
|
|
int ptime, ptime2 = 0;
|
|
char audio_name[64];
|
|
int port, rc;
|
|
char ipv4[16];
|
|
|
|
OSMO_ASSERT(endp);
|
|
OSMO_ASSERT(conn);
|
|
OSMO_ASSERT(p);
|
|
|
|
rtp = &conn->end;
|
|
memset(&codecs, 0, sizeof(codecs));
|
|
|
|
for_each_line(line, p->save) {
|
|
switch (line[0]) {
|
|
case 'o':
|
|
case 's':
|
|
case 't':
|
|
case 'v':
|
|
/* skip these SDP attributes */
|
|
break;
|
|
case 'a':
|
|
if (sscanf(line, "a=rtpmap:%d %63s",
|
|
&payload, audio_name) == 2) {
|
|
codecs_update(tmp_ctx, codecs,
|
|
codecs_used, payload, audio_name);
|
|
} else
|
|
if (sscanf
|
|
(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
|
|
if (ptime2 > 0 && ptime2 != ptime)
|
|
rtp->packet_duration_ms = 0;
|
|
else
|
|
rtp->packet_duration_ms = ptime;
|
|
} else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
|
|
rtp->maximum_packet_time = ptime2;
|
|
}
|
|
break;
|
|
case 'm':
|
|
rc = sscanf(line, "m=audio %d RTP/AVP", &port);
|
|
if (rc == 1) {
|
|
rtp->rtp_port = htons(port);
|
|
rtp->rtcp_port = htons(port + 1);
|
|
}
|
|
|
|
rc = pt_from_sdp(conn->conn, codecs,
|
|
ARRAY_SIZE(codecs), line);
|
|
if (rc > 0)
|
|
codecs_used = rc;
|
|
break;
|
|
case 'c':
|
|
|
|
if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
|
|
inet_aton(ipv4, &rtp->addr);
|
|
}
|
|
break;
|
|
default:
|
|
if (p->endp)
|
|
LOGP(DLMGCP, LOGL_NOTICE,
|
|
"Unhandled SDP option: '%c'/%d on 0x%x\n",
|
|
line[0], line[0],
|
|
ENDPOINT_NUMBER(p->endp));
|
|
else
|
|
LOGP(DLMGCP, LOGL_NOTICE,
|
|
"Unhandled SDP option: '%c'/%d\n",
|
|
line[0], line[0]);
|
|
break;
|
|
}
|
|
}
|
|
OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS);
|
|
|
|
/* So far we have only set the payload type in the codec struct. Now we
|
|
* fill up the remaining fields of the codec description with some default
|
|
* information */
|
|
codecs_initialize(tmp_ctx, codecs, codecs_used);
|
|
|
|
/* Store parsed codec information */
|
|
for (i = 0; i < codecs_used; i++) {
|
|
rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line);
|
|
if (rc < 0)
|
|
LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
|
|
}
|
|
|
|
talloc_free(tmp_ctx);
|
|
|
|
LOGP(DLMGCP, LOGL_NOTICE,
|
|
"Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
|
|
ntohs(rtp->rtp_port), inet_ntoa(rtp->addr),
|
|
rtp->packet_duration_ms);
|
|
if (codecs_used == 0)
|
|
LOGPC(DLMGCP, LOGL_NOTICE, "none");
|
|
for (i = 0; i < codecs_used; i++) {
|
|
LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
|
|
rtp->codecs[i].payload_type,
|
|
rtp->codecs[i].subtype_name ? rtp-> codecs[i].subtype_name : "unknown");
|
|
LOGPC(DLMGCP, LOGL_NOTICE, " ");
|
|
}
|
|
LOGPC(DLMGCP, LOGL_NOTICE, "\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Add rtpmap string to the sdp payload, but only when the payload type falls
|
|
* into the dynamic payload type range */
|
|
static int add_rtpmap(struct msgb *sdp, int payload_type, const char *audio_name)
|
|
{
|
|
int rc;
|
|
|
|
if (payload_type >= 96 && payload_type <= 127) {
|
|
if (!audio_name)
|
|
return -EINVAL;
|
|
rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n", payload_type, audio_name);
|
|
if (rc < 0)
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Add audio string to sdp payload */
|
|
static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_types_len, int local_port)
|
|
{
|
|
int rc;
|
|
unsigned int i;
|
|
|
|
if (payload_types_len < 0)
|
|
return -EINVAL;
|
|
|
|
rc = msgb_printf(sdp, "m=audio %d RTP/AVP", local_port);
|
|
if (rc < 0)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < payload_types_len; i++) {
|
|
rc = msgb_printf(sdp, " %d", payload_types[i]);
|
|
if (rc < 0)
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = msgb_printf(sdp, "\r\n");
|
|
if (rc < 0)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*! Generate SDP response string.
|
|
* \param[in] endp trunk endpoint.
|
|
* \param[in] conn associated rtp connection.
|
|
* \param[out] sdp msg buffer to append resulting SDP string data.
|
|
* \param[in] addr IPV4 address string (e.g. 192.168.100.1).
|
|
* \returns 0 on success, -1 on failure. */
|
|
int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
|
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
|
|
const char *addr)
|
|
{
|
|
const char *fmtp_extra;
|
|
const char *audio_name;
|
|
int payload_type;
|
|
int rc;
|
|
int payload_types[1];
|
|
|
|
OSMO_ASSERT(endp);
|
|
OSMO_ASSERT(conn);
|
|
OSMO_ASSERT(sdp);
|
|
OSMO_ASSERT(addr);
|
|
|
|
/* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
|
|
endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
|
|
&payload_type, &audio_name,
|
|
&fmtp_extra,
|
|
(struct mgcp_conn_rtp *)conn);
|
|
|
|
rc = msgb_printf(sdp,
|
|
"v=0\r\n"
|
|
"o=- %s 23 IN IP4 %s\r\n"
|
|
"s=-\r\n"
|
|
"c=IN IP4 %s\r\n"
|
|
"t=0 0\r\n", conn->conn->id, addr, addr);
|
|
|
|
if (rc < 0)
|
|
goto buffer_too_small;
|
|
|
|
if (payload_type >= 0) {
|
|
|
|
payload_types[0] = payload_type;
|
|
rc = add_audio(sdp, payload_types, 1, conn->end.local_port);
|
|
if (rc < 0)
|
|
goto buffer_too_small;
|
|
|
|
if (endp->tcfg->audio_send_name) {
|
|
rc = add_rtpmap(sdp, payload_type, audio_name);
|
|
if (rc < 0)
|
|
goto buffer_too_small;
|
|
}
|
|
|
|
if (fmtp_extra) {
|
|
rc = msgb_printf(sdp, "%s\r\n", fmtp_extra);
|
|
|
|
if (rc < 0)
|
|
goto buffer_too_small;
|
|
}
|
|
}
|
|
if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
|
|
rc = msgb_printf(sdp, "a=ptime:%u\r\n",
|
|
conn->end.packet_duration_ms);
|
|
if (rc < 0)
|
|
goto buffer_too_small;
|
|
}
|
|
|
|
return 0;
|
|
|
|
buffer_too_small:
|
|
LOGP(DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
|
|
return -1;
|
|
}
|