From eef45781024d6e17c7b473d15ec5d3d83515d0e2 Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Mon, 21 Oct 2019 03:24:04 +0200 Subject: [PATCH] add sdp_msg API: SDP parsing/composition Rationale: in order to add full SDP to the MNCC protocol (upcoming patch I8c3b2de53ffae4ec3a66b9dabf308c290a2c999f), we need to parse and compose SDP messages. Obviously, libosmo-mgcp-client already contains similar code, but that is unfortunately heavily glued to the actual MGCP implementation. The simplest solution is to create this separate implementation, copy-pasting from the existing libosmo-mgcp-client code as is convenient. This API is added here to probe whether it works well. When it does, the intention is to "move it up" to osmo-mgw and overhaul the SDP parsing in our MGCP client and MGCP server APIs using this same API. Change-Id: If3ce23cd5bab15e2ab4c52ef3e4c75979dffe931 --- configure.ac | 1 + include/osmocom/msc/Makefile.am | 1 + include/osmocom/msc/sdp_msg.h | 68 ++++ src/libmsc/Makefile.am | 1 + src/libmsc/sdp_msg.c | 576 +++++++++++++++++++++++++++++++ tests/Makefile.am | 1 + tests/sdp_msg/Makefile.am | 32 ++ tests/sdp_msg/sdp_msg_test.c | 569 ++++++++++++++++++++++++++++++ tests/sdp_msg/sdp_msg_test.err | 0 tests/sdp_msg/sdp_msg_test.ok | 590 ++++++++++++++++++++++++++++++++ tests/testsuite.at | 7 + 11 files changed, 1846 insertions(+) create mode 100644 include/osmocom/msc/sdp_msg.h create mode 100644 src/libmsc/sdp_msg.c create mode 100644 tests/sdp_msg/Makefile.am create mode 100644 tests/sdp_msg/sdp_msg_test.c create mode 100644 tests/sdp_msg/sdp_msg_test.err create mode 100644 tests/sdp_msg/sdp_msg_test.ok diff --git a/configure.ac b/configure.ac index 5aa652d9b..ee80900ff 100644 --- a/configure.ac +++ b/configure.ac @@ -254,6 +254,7 @@ AC_OUTPUT( tests/db_sms/Makefile tests/sms_queue/Makefile tests/msc_vlr/Makefile + tests/sdp_msg/Makefile doc/Makefile doc/examples/Makefile doc/manuals/Makefile diff --git a/include/osmocom/msc/Makefile.am b/include/osmocom/msc/Makefile.am index 0d7d45ce9..faf1048dc 100644 --- a/include/osmocom/msc/Makefile.am +++ b/include/osmocom/msc/Makefile.am @@ -39,6 +39,7 @@ noinst_HEADERS = \ rrlp.h \ rtp_stream.h \ sccp_ran.h \ + sdp_msg.h \ sgs_iface.h \ sgs_server.h \ sgs_vty.h \ diff --git a/include/osmocom/msc/sdp_msg.h b/include/osmocom/msc/sdp_msg.h new file mode 100644 index 000000000..7ac6036f6 --- /dev/null +++ b/include/osmocom/msc/sdp_msg.h @@ -0,0 +1,68 @@ +/* Minimalistic SDP parse/compose API, focused on GSM audio codecs */ +#pragma once + +#include +#include + +extern const struct value_string sdp_msg_payload_type_names[]; +static inline const char *sdp_msg_payload_type_name(unsigned int payload_type) +{ return get_value_string(sdp_msg_payload_type_names, payload_type); } +int sdp_subtype_name_to_payload_type(const char *subtype_name); + +struct sdp_audio_codec { + unsigned int payload_type; + /* Like "GSM", "AMR", "EFR", ... */ + char subtype_name[16]; + unsigned int rate; + char fmtp[64]; +}; + +struct sdp_audio_codecs { + unsigned int count; + struct sdp_audio_codec codec[16]; +}; + +struct sdp_msg { + struct osmo_sockaddr_str rtp; + unsigned int ptime; + struct sdp_audio_codecs audio_codecs; +}; + +#define foreach_sdp_audio_codec(/* struct sdp_audio_codec* */ CODEC, \ + /* struct sdp_audio_codecs* */ AC) \ + for (CODEC = (AC)->codec; \ + (CODEC - (AC)->codec) < OSMO_MIN((AC)->count, ARRAY_SIZE((AC)->codec)); \ + CODEC++) + +const char *sdp_msg_line_end(const char *src); + +int sdp_audio_codec_cmp(const struct sdp_audio_codec *a, const struct sdp_audio_codec *b); + +struct sdp_audio_codec *sdp_audio_codec_add(struct sdp_audio_codecs *ac, unsigned int payload_type, + const char *subtype_name, unsigned int rate, const char *fmtp); +struct sdp_audio_codec *sdp_audio_codec_add_copy(struct sdp_audio_codecs *ac, + const struct sdp_audio_codec *codec); +int sdp_audio_codec_remove(struct sdp_audio_codecs *ac, const struct sdp_audio_codec *codec); +struct sdp_audio_codec *sdp_audio_codec_by_payload_type(struct sdp_audio_codecs *ac, + unsigned int payload_type, bool create); +struct sdp_audio_codec *sdp_audio_codec_by_descr(struct sdp_audio_codecs *ac, + const struct sdp_audio_codec *codec); + +void sdp_audio_codecs_intersection(struct sdp_audio_codecs *ac_dest, const struct sdp_audio_codecs *ac_other, + bool translate_payload_type_numbers); +void sdp_audio_codecs_select(struct sdp_audio_codecs *ac, struct sdp_audio_codec *codec); + +int sdp_msg_to_str(char *dst, size_t dst_size, const struct sdp_msg *sdp); +int sdp_msg_from_str(struct sdp_msg *sdp, const char *src); + +int sdp_audio_codec_name_buf(char *buf, size_t buflen, const struct sdp_audio_codec *codec); +char *sdp_audio_codec_name_c(void *ctx, const struct sdp_audio_codec *codec); +const char *sdp_audio_codec_name(const struct sdp_audio_codec *codec); + +int sdp_audio_codecs_name_buf(char *buf, size_t buflen, const struct sdp_audio_codecs *ac); +char *sdp_audio_codecs_name_c(void *ctx, const struct sdp_audio_codecs *ac); +const char *sdp_audio_codecs_name(const struct sdp_audio_codecs *ac); + +int sdp_msg_name_buf(char *buf, size_t buflen, const struct sdp_msg *sdp); +char *sdp_msg_name_c(void *ctx, const struct sdp_msg *sdp); +const char *sdp_msg_name(const struct sdp_msg *sdp); diff --git a/src/libmsc/Makefile.am b/src/libmsc/Makefile.am index d83489680..e6a2dc164 100644 --- a/src/libmsc/Makefile.am +++ b/src/libmsc/Makefile.am @@ -64,6 +64,7 @@ libmsc_a_SOURCES = \ ran_peer.c \ rrlp.c \ rtp_stream.c \ + sdp_msg.c \ silent_call.c \ sms_queue.c \ transaction.c \ diff --git a/src/libmsc/sdp_msg.c b/src/libmsc/sdp_msg.c new file mode 100644 index 000000000..7880978a0 --- /dev/null +++ b/src/libmsc/sdp_msg.c @@ -0,0 +1,576 @@ +/* Minimalistic SDP parse/compose implementation, focused on GSM audio codecs */ +/* + * (C) 2019 by sysmocom - s.m.f.c. GmbH + * All Rights Reserved + * + * SPDX-License-Identifier: AGPL-3.0+ + * + * Author: Neels Hofmeyr + * + * 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 . + */ + +#include +#include + +#include +#include + +#include +#include + +/* Compare name, rate and fmtp, returning typical cmp result: 0 on match, and -1 / 1 on mismatch. + * Do *not* compare the payload_type number. + * The fmtp is only string-compared -- e.g. if AMR parameters appear in a different order, it amounts to a mismatch even + * though all parameters are the same. */ +int sdp_audio_codec_cmp(const struct sdp_audio_codec *a, const struct sdp_audio_codec *b) +{ + int rc; + if (a == b) + return 0; + if (!a) + return -1; + if (!b) + return 1; + rc = strncmp(a->subtype_name, b->subtype_name, sizeof(a->subtype_name)); + if (rc) + return rc; + + if (a->rate < b->rate) + return -1; + if (a->rate > b->rate) + return 1; + + rc = strncmp(a->fmtp, b->fmtp, sizeof(a->fmtp)); + if (rc) + return rc; + + return 0; +} + +/* Given a predefined fixed payload_type number, add an SDP audio codec entry, if not present yet. + * The payload_type must exist in sdp_msg_payload_type_names. + * Return the audio codec created or already existing for this payload type number. + */ +struct sdp_audio_codec *sdp_audio_codec_add(struct sdp_audio_codecs *ac, unsigned int payload_type, + const char *subtype_name, unsigned int rate, const char *fmtp) +{ + struct sdp_audio_codec *codec; + + /* Does an entry already exist? */ + codec = sdp_audio_codec_by_payload_type(ac, payload_type, false); + if (codec) { + /* Already exists, sanity check */ + if (!codec->subtype_name[0]) + OSMO_STRLCPY_ARRAY(codec->subtype_name, subtype_name); + else if (strcmp(codec->subtype_name, subtype_name)) { + /* There already is an entry with this payload_type number but a mismatching subtype_name. That is + * weird, rather abort. */ + return NULL; + } + if (codec->rate != rate + || (fmtp && strcmp(fmtp, codec->fmtp))) { + /* Mismatching details. Rather abort */ + return NULL; + } + return codec; + } + + /* None exists, create codec entry for this payload type number */ + codec = sdp_audio_codec_by_payload_type(ac, payload_type, true); + /* NULL means unable to add an entry */ + if (!codec) + return NULL; + + OSMO_STRLCPY_ARRAY(codec->subtype_name, subtype_name); + if (fmtp) + OSMO_STRLCPY_ARRAY(codec->fmtp, fmtp); + return codec; +} + +struct sdp_audio_codec *sdp_audio_codec_add_copy(struct sdp_audio_codecs *ac, const struct sdp_audio_codec *codec) +{ + return sdp_audio_codec_add(ac, codec->payload_type, codec->subtype_name, codec->rate, + codec->fmtp[0] ? codec->fmtp : NULL); +} + +struct sdp_audio_codec *sdp_audio_codec_by_payload_type(struct sdp_audio_codecs *ac, unsigned int payload_type, + bool create) +{ + struct sdp_audio_codec *codec; + foreach_sdp_audio_codec(codec, ac) { + if (codec->payload_type == payload_type) + return codec; + } + + if (!create) + return NULL; + + /* Not found; codec points after the last entry now. */ + if ((codec - ac->codec) >= ARRAY_SIZE(ac->codec)) + return NULL; + + *codec = (struct sdp_audio_codec){ + .payload_type = payload_type, + .rate = 8000, + }; + + ac->count = (codec - ac->codec) + 1; + return codec; +} + +/* Return a given sdp_msg's codec entry that matches the subtype_name, rate and fmtp of the given codec, or NULL if no + * match is found. Comparison is made by sdp_audio_codec_cmp(). */ +struct sdp_audio_codec *sdp_audio_codec_by_descr(struct sdp_audio_codecs *ac, const struct sdp_audio_codec *codec) +{ + struct sdp_audio_codec *i; + foreach_sdp_audio_codec(i, ac) { + if (!sdp_audio_codec_cmp(i, codec)) + return i; + } + return NULL; +} + +/* Remove the codec entry pointed at by 'codec'. 'codec' must point at an entry of 'sdp' (to use an external codec + * instance, use sdp_audio_codec_by_descr()). + * Return 0 on success, -ENOENT if codec does not point at the sdp->codec array. */ +int sdp_audio_codec_remove(struct sdp_audio_codecs *ac, const struct sdp_audio_codec *codec) +{ + struct sdp_audio_codec *i; + if ((codec < ac->codec) + || ((codec - ac->codec) >= OSMO_MIN(ac->count, ARRAY_SIZE(ac->codec)))) + return -ENOENT; + + /* Move all following entries one up */ + ac->count--; + foreach_sdp_audio_codec(i, ac) { + if (i < codec) + continue; + *i = *(i+1); + } + return 0; +} + +/* Convert struct sdp_msg to the actual SDP protocol representation */ +int sdp_msg_to_str(char *dst, size_t dst_size, const struct sdp_msg *sdp) +{ + const struct sdp_audio_codec *codec; + struct osmo_strbuf sb = { .buf = dst, .len = dst_size }; + const char *ip = sdp->rtp.ip[0] ? sdp->rtp.ip : "0.0.0.0"; + + OSMO_STRBUF_PRINTF(sb, + "v=0\r\n" + "o=OsmoMSC 0 0 IN IP4 %s\r\n" + "s=GSM Call\r\n" + "c=IN IP4 %s\r\n" + "t=0 0\r\n" + "m=audio %d RTP/AVP", + ip, ip, + sdp->rtp.port); + + /* Append all payload type numbers to 'm=audio RTP/AVP 3 4 112' line */ + foreach_sdp_audio_codec(codec, &sdp->audio_codecs) + OSMO_STRBUF_PRINTF(sb, " %d", codec->payload_type); + OSMO_STRBUF_PRINTF(sb, "\r\n"); + + /* Add details for all codecs */ + foreach_sdp_audio_codec(codec, &sdp->audio_codecs) { + if (codec->subtype_name[0]) { + OSMO_STRBUF_PRINTF(sb, "a=rtpmap:%d %s/%d\r\n", codec->payload_type, codec->subtype_name, + codec->rate > 0? codec->rate : 8000); + } + + if (codec->fmtp[0]) + OSMO_STRBUF_PRINTF(sb, "a=fmtp:%d %s\r\n", codec->payload_type, codec->fmtp); + } + + OSMO_STRBUF_PRINTF(sb, "a=ptime:%d\r\n", sdp->ptime > 0? sdp->ptime : 20); + + return sb.chars_needed; +} + +/* Return the first line ending (or the end of the string) at or after the given string position. */ +const char *sdp_msg_line_end(const char *src) +{ + const char *line_end = strchr(src, '\r'); + if (!line_end) + line_end = strchr(src, '\n'); + if (!line_end) + line_end = src + strlen(src); + return line_end; +} + +/* parse a line like 'a=rtpmap:0 PCMU/8000', 'a=fmtp:112 octet-align=1; mode-set=4', 'a=ptime:20'. + * The src should point at the character after 'a=', e.g. at the start of 'rtpmap', 'fmtp', 'ptime' + */ +int sdp_parse_attrib(struct sdp_msg *sdp, const char *src) +{ + unsigned int payload_type; + struct sdp_audio_codec *codec; +#define A_RTPMAP "rtpmap:" +#define A_FMTP "fmtp:" +#define A_PTIME "ptime:" +#define A_RTCP "rtcp:" +#define A_SENDRECV "sendrecv" +#define A_SENDONLY "sendonly" +#define A_RECVONLY "recvonly" + + if (osmo_str_startswith(src, A_RTPMAP)) { + char *audio_name; + unsigned int channels = 1; + if (sscanf(src, A_RTPMAP "%u", &payload_type) != 1) + return -EINVAL; + + audio_name = strchr(src, ' '); + if (!audio_name || audio_name >= sdp_msg_line_end(src)) + return -EINVAL; + + codec = sdp_audio_codec_by_payload_type(&sdp->audio_codecs, payload_type, true); + if (!codec) + return -ENOSPC; + + if (sscanf(audio_name, " %31[^/]/%u/%u", codec->subtype_name, &codec->rate, &channels) < 1) + return -EINVAL; + + if (channels != 1) + return -ENOTSUP; + } + + else if (osmo_str_startswith(src, A_FMTP)) { + char *fmtp_str; + const char *line_end = sdp_msg_line_end(src); + if (sscanf(src, A_FMTP "%u", &payload_type) != 1) + return -EINVAL; + + fmtp_str = strchr(src, ' '); + if (!fmtp_str) + return -EINVAL; + fmtp_str++; + if (fmtp_str >= line_end) + return -EINVAL; + + codec = sdp_audio_codec_by_payload_type(&sdp->audio_codecs, payload_type, true); + if (!codec) + return -ENOSPC; + + /* (+1 because osmo_strlcpy() interprets it as size including the '\0') */ + osmo_strlcpy(codec->fmtp, fmtp_str, line_end - fmtp_str + 1); + } + + else if (osmo_str_startswith(src, A_PTIME)) { + if (sscanf(src, A_PTIME "%u", &sdp->ptime) != 1) + return -EINVAL; + + } + + else if (osmo_str_startswith(src, A_RTCP)) { + /* TODO? */ + } + + else if (osmo_str_startswith(src, A_SENDRECV)) { + /* TODO? */ + } + + else if (osmo_str_startswith(src, A_SENDONLY)) { + /* TODO? */ + } + + else if (osmo_str_startswith(src, A_RECVONLY)) { + /* TODO? */ + } + + return 0; +} + +const struct value_string sdp_msg_payload_type_names[] = { + { 0, "PCMU" }, + { 3, "GSM" }, + { 8, "PCMA" }, + { 18, "G729" }, + { 110, "GSM-EFR" }, + { 111, "GSM-HR-08" }, + { 112, "AMR" }, + { 113, "AMR-WB" }, + {} +}; + +/* Return payload type number matching given string ("AMR", "GSM", ...) or negative if not found. */ +int sdp_subtype_name_to_payload_type(const char *subtype_name) +{ + return get_string_value(sdp_msg_payload_type_names, subtype_name); +} + +/* Parse a line like 'm=audio 16398 RTP/AVP 0 3 8 96 112', starting after the '=' */ +static int sdp_parse_media_description(struct sdp_msg *sdp, const char *src) +{ + unsigned int port; + int i; + const char *payload_type_str; + const char *line_end = sdp_msg_line_end(src); + if (sscanf(src, "audio %u RTP/AVP", &port) < 1) + return -ENOTSUP; + + if (port < 0 || port > 0xffff) + return -EINVAL; + + sdp->rtp.port = port; + + /* skip "audio 12345 RTP/AVP ", i.e. 3 spaces on */ + payload_type_str = src; + for (i = 0; i < 3; i++) { + payload_type_str = strchr(payload_type_str, ' '); + if (!payload_type_str) + return -EINVAL; + while (*payload_type_str == ' ') + payload_type_str++; + if (payload_type_str >= line_end) + return -EINVAL; + } + + /* Parse listing of payload type numbers after "RTP/AVP" */ + while (payload_type_str < line_end) { + unsigned int payload_type; + struct sdp_audio_codec *codec; + const char *subtype_name; + if (sscanf(payload_type_str, "%u", &payload_type) < 1) + return -EINVAL; + + codec = sdp_audio_codec_by_payload_type(&sdp->audio_codecs, payload_type, true); + if (!codec) + return -ENOSPC; + + /* Fill in subtype name for fixed payload types */ + subtype_name = get_value_string_or_null(sdp_msg_payload_type_names, codec->payload_type); + if (subtype_name) + OSMO_STRLCPY_ARRAY(codec->subtype_name, subtype_name); + + payload_type_str = strchr(payload_type_str, ' '); + if (!payload_type_str) + payload_type_str = line_end; + while (*payload_type_str == ' ') + payload_type_str++; + } + + return 0; +} + +/* parse a line like 'c=IN IP4 192.168.11.151' starting after the '=' */ +static int sdp_parse_connection_info(struct sdp_msg *sdp, const char *src) +{ + char ipv[10]; + char addr_str[INET6_ADDRSTRLEN]; + if (sscanf(src, "IN %s %s", ipv, addr_str) < 2) + return -EINVAL; + + /* supporting only IPv4 */ + if (strcmp(ipv, "IP4")) + return -ENOTSUP; + + osmo_sockaddr_str_from_str(&sdp->rtp, addr_str, sdp->rtp.port); + return 0; +} + +/* Parse SDP string into struct sdp_msg. Return 0 on success, negative on error. */ +int sdp_msg_from_str(struct sdp_msg *sdp, const char *src) +{ + const char *pos; + *sdp = (struct sdp_msg){}; + + for (pos = src; pos && *pos; pos++) { + char attrib; + int rc = 0; + + if (*pos == '\r' || *pos == '\n') + continue; + + /* Expecting only lines starting with 'X='. Not being too strict about it is probably alright. */ + if (pos[1] != '=') + goto next_line; + + attrib = *pos; + pos += 2; + switch (attrib) { + /* a=... */ + case 'a': + rc = sdp_parse_attrib(sdp, pos); + break; + case 'm': + rc = sdp_parse_media_description(sdp, pos); + break; + case 'c': + rc = sdp_parse_connection_info(sdp, pos); + break; + default: + /* ignore any other parameters */ + break; + } + + if (rc) { + size_t line_len; + const char *line_end = sdp_msg_line_end(pos); + pos -= 2; + line_len = line_end - pos; + switch (rc) { + case -EINVAL: + LOGP(DMNCC, LOGL_ERROR, + "Failed to parse SDP: invalid line: %s\n", osmo_quote_str(pos, line_len)); + break; + case -ENOSPC: + LOGP(DMNCC, LOGL_ERROR, + "Failed to parse SDP: no more space for: %s\n", osmo_quote_str(pos, line_len)); + break; + case -ENOTSUP: + LOGP(DMNCC, LOGL_ERROR, + "Failed to parse SDP: not supported: %s\n", osmo_quote_str(pos, line_len)); + break; + default: + LOGP(DMNCC, LOGL_ERROR, + "Failed to parse SDP: %s\n", osmo_quote_str(pos, line_len)); + break; + } + return rc; + } +next_line: + pos = strstr(pos, "\r\n"); + if (!pos) + break; + } + + return 0; +} + +/* Leave only those codecs in 'ac_dest' that are also present in 'ac_other'. + * The matching is made by sdp_audio_codec_cmp(), i.e. payload_type numbers are not compared and fmtp parameters are + * compared 1:1 as plain strings. + * If translate_payload_type_numbers has an effect if ac_dest and ac_other have mismatching payload_type numbers for the + * same SDP codec descriptions. If translate_payload_type_numbers is true, take the payload_type numbers from ac_other. + * If false, keep payload_type numbers in ac_dest unchanged. */ +void sdp_audio_codecs_intersection(struct sdp_audio_codecs *ac_dest, const struct sdp_audio_codecs *ac_other, + bool translate_payload_type_numbers) +{ + int i; + for (i = 0; i < ac_dest->count; i++) { + struct sdp_audio_codec *codec = &ac_dest->codec[i]; + struct sdp_audio_codec *other; + OSMO_ASSERT(i < ARRAY_SIZE(ac_dest->codec)); + + other = sdp_audio_codec_by_descr((struct sdp_audio_codecs*)ac_other, codec); + + if (!other) { + OSMO_ASSERT(sdp_audio_codec_remove(ac_dest, codec) == 0); + i--; + continue; + } + + /* Doing payload_type number translation of part of the intersection because it makes the algorithm + * simpler: we already know ac_dest is a subset of ac_other, and there is no need to resolve payload + * type number conflicts. */ + if (translate_payload_type_numbers) + codec->payload_type = other->payload_type; + } +} + +/* Make sure the given codec is listed as the first codec. 'codec' must be an actual codec entry of the given audio + * codecs list. */ +void sdp_audio_codecs_select(struct sdp_audio_codecs *ac, struct sdp_audio_codec *codec) +{ + struct sdp_audio_codec tmp; + struct sdp_audio_codec *pos; + OSMO_ASSERT((codec >= ac->codec) + && ((codec - ac->codec) < OSMO_MIN(ac->count, ARRAY_SIZE(ac->codec)))); + + /* Already the first? */ + if (codec == ac->codec) + return; + + tmp = *codec; + for (pos = codec - 1; pos >= ac->codec; pos--) + pos[1] = pos[0]; + + ac->codec[0] = tmp; + return; +} + +/* Short single-line representation of an SDP audio codec, convenient for logging. + * Like "AMR/8000:octet-align=1#122" */ +int sdp_audio_codec_name_buf(char *buf, size_t buflen, const struct sdp_audio_codec *codec) +{ + struct osmo_strbuf sb = { .buf = buf, .len = buflen }; + OSMO_STRBUF_PRINTF(sb, "%s", codec->subtype_name); + if (codec->fmtp[0]) + OSMO_STRBUF_PRINTF(sb, ":%s", codec->fmtp); + return sb.chars_needed; +} + +char *sdp_audio_codec_name_c(void *ctx, const struct sdp_audio_codec *codec) +{ + OSMO_NAME_C_IMPL(ctx, 32, "sdp_audio_codec_name_c-ERROR", sdp_audio_codec_name_buf, codec) +} + +const char *sdp_audio_codec_name(const struct sdp_audio_codec *codec) +{ + return sdp_audio_codec_name_c(OTC_SELECT, codec); +} + +/* Short single-line representation of a list of SDP audio codecs, convenient for logging */ +int sdp_audio_codecs_name_buf(char *buf, size_t buflen, const struct sdp_audio_codecs *ac) +{ + struct osmo_strbuf sb = { .buf = buf, .len = buflen }; + const struct sdp_audio_codec *codec; + if (!ac->count) + OSMO_STRBUF_PRINTF(sb, "(no-codecs)"); + foreach_sdp_audio_codec(codec, ac) { + bool first = (codec == ac->codec); + if (!first) + OSMO_STRBUF_PRINTF(sb, ","); + OSMO_STRBUF_APPEND(sb, sdp_audio_codec_name_buf, codec); + } + return sb.chars_needed; +} + +char *sdp_audio_codecs_name_c(void *ctx, const struct sdp_audio_codecs *ac) +{ + OSMO_NAME_C_IMPL(ctx, 128, "sdp_audio_codecs_name_c-ERROR", sdp_audio_codecs_name_buf, ac) +} + +const char *sdp_audio_codecs_name(const struct sdp_audio_codecs *ac) +{ + return sdp_audio_codecs_name_c(OTC_SELECT, ac); +} + +/* Short single-line representation of an SDP message, convenient for logging */ +int sdp_msg_name_buf(char *buf, size_t buflen, const struct sdp_msg *sdp) +{ + struct osmo_strbuf sb = { .buf = buf, .len = buflen }; + if (!sdp) { + OSMO_STRBUF_PRINTF(sb, "NULL"); + return sb.chars_needed; + } + + OSMO_STRBUF_PRINTF(sb, OSMO_SOCKADDR_STR_FMT, OSMO_SOCKADDR_STR_FMT_ARGS(&sdp->rtp)); + OSMO_STRBUF_PRINTF(sb, "{"); + OSMO_STRBUF_APPEND(sb, sdp_audio_codecs_name_buf, &sdp->audio_codecs); + OSMO_STRBUF_PRINTF(sb, "}"); + return sb.chars_needed; +} + +char *sdp_msg_name_c(void *ctx, const struct sdp_msg *sdp) +{ + OSMO_NAME_C_IMPL(ctx, 128, "sdp_msg_name_c-ERROR", sdp_msg_name_buf, sdp) +} + +const char *sdp_msg_name(const struct sdp_msg *sdp) +{ + return sdp_msg_name_c(OTC_SELECT, sdp); +} diff --git a/tests/Makefile.am b/tests/Makefile.am index 27c12051b..864ac7ccb 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -2,6 +2,7 @@ SUBDIRS = \ sms_queue \ msc_vlr \ db_sms \ + sdp_msg \ $(NULL) if BUILD_SMPP diff --git a/tests/sdp_msg/Makefile.am b/tests/sdp_msg/Makefile.am new file mode 100644 index 000000000..7d8def9b2 --- /dev/null +++ b/tests/sdp_msg/Makefile.am @@ -0,0 +1,32 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + -ggdb3 \ + $(LIBOSMOCORE_CFLAGS) \ + $(NULL) + +LDADD = \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(LIBOSMOCORE_LIBS) \ + $(NULL) + +EXTRA_DIST = \ + sdp_msg_test.ok \ + sdp_msg_test.err \ + $(NULL) + +noinst_PROGRAMS = \ + sdp_msg_test \ + $(NULL) + +sdp_msg_test_SOURCES = \ + sdp_msg_test.c \ + $(NULL) + +.PHONY: update_exp +update_exp: + $(builddir)/sdp_msg_test >$(srcdir)/sdp_msg_test.ok 2>$(srcdir)/sdp_msg_test.err diff --git a/tests/sdp_msg/sdp_msg_test.c b/tests/sdp_msg/sdp_msg_test.c new file mode 100644 index 000000000..b97127add --- /dev/null +++ b/tests/sdp_msg/sdp_msg_test.c @@ -0,0 +1,569 @@ +#include +#include +#include +#include + +struct sdp_test_data { + const char *sdp_input; + const char *expect_sdp_str; +}; + +static void dump_sdp(const char *str, const char *prefix) +{ + while (str && *str) { + const char *line_end = sdp_msg_line_end(str); + while (*line_end == '\r' || *line_end == '\n') + line_end++; + printf("%s%s\n", prefix, osmo_escape_str(str, line_end - str)); + str = line_end; + } +} + +struct sdp_test_data sdp_tests[] = { + { + "v=0\r\n" + "o=- 5628250 5628250 IN IP4 192.168.11.121\r\n" + "s=-\r\n" + "c=IN IP4 192.168.11.121\r\n" + "t=0 0\r\n" + "m=audio 10020 RTP/AVP 18 0 2 4 8 96 97 98 100 101\r\n" + "a=rtpmap:18 G729/8000\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:2 G726-32/8000\r\n" + "a=rtpmap:4 G723/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:96 G726-40/8000\r\n" + "a=rtpmap:97 G726-24/8000\r\n" + "a=rtpmap:98 G726-16/8000\r\n" + "a=rtpmap:100 NSE/8000\r\n" + "a=fmtp:100 192-193\r\n" + "a=rtpmap:101 telephone-event/8000\r\n" + "a=fmtp:101 0-15\r\n" + "a=ptime:20\r\n" + "a=sendrecv\r\n" + , + "v=0\r\n" + "o=OsmoMSC 0 0 IN IP4 192.168.11.121\r\n" + "s=GSM Call\r\n" + "c=IN IP4 192.168.11.121\r\n" + "t=0 0\r\n" + "m=audio 10020 RTP/AVP 18 0 2 4 8 96 97 98 100 101\r\n" + "a=rtpmap:18 G729/8000\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:2 G726-32/8000\r\n" + "a=rtpmap:4 G723/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:96 G726-40/8000\r\n" + "a=rtpmap:97 G726-24/8000\r\n" + "a=rtpmap:98 G726-16/8000\r\n" + "a=rtpmap:100 NSE/8000\r\n" + "a=fmtp:100 192-193\r\n" + "a=rtpmap:101 telephone-event/8000\r\n" + "a=fmtp:101 0-15\r\n" + "a=ptime:20\r\n" + , + }, + { + "v=0\r\n" + "o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n" + "s=FooBar\r\n" + "c=IN IP4 192.168.11.151\r\n" + "t=0 0\r\n" + "m=audio 16398 RTP/AVP 98\r\n" + "a=rtpmap:98 AMR/8000\r\n" + "a=fmtp:98 octet-align=1; mode-set=4\r\n" + "a=ptime:20\r\n" + "a=rtcp:16399 IN IP4 192.168.11.151\r\n" + , + "v=0\r\n" + "o=OsmoMSC 0 0 IN IP4 192.168.11.151\r\n" + "s=GSM Call\r\n" + "c=IN IP4 192.168.11.151\r\n" + "t=0 0\r\n" + "m=audio 16398 RTP/AVP 98\r\n" + "a=rtpmap:98 AMR/8000\r\n" + "a=fmtp:98 octet-align=1; mode-set=4\r\n" + "a=ptime:20\r\n" + , + }, + { + "v=0\r\n" + "o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n" + "s=FooBar\r\n" + "c=IN IP4 192.168.11.140\r\n" + "t=0 0\r\n" + "m=audio 30436 RTP/AVP 18 0 4 8 101\r\n" + "a=rtpmap:18 G729/8000\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:4 G723/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:101 telephone-event/8000\r\n" + "a=fmtp:101 0-15\r\n" + "a=sendrecv\r\n" + "a=rtcp:30437\r\n" + "a=ptime:20\r\n" + , + "v=0\r\n" + "o=OsmoMSC 0 0 IN IP4 192.168.11.140\r\n" /* <- NOTE: loses the 'o=' address, uses only 'c=' */ + "s=GSM Call\r\n" + "c=IN IP4 192.168.11.140\r\n" + "t=0 0\r\n" + "m=audio 30436 RTP/AVP 18 0 4 8 101\r\n" + "a=rtpmap:18 G729/8000\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:4 G723/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:101 telephone-event/8000\r\n" + "a=fmtp:101 0-15\r\n" + "a=ptime:20\r\n" + , + }, +}; + +void test_parse_and_compose() +{ + int i; + bool ok = true; + + printf("\n\n%s\n", __func__); + + for (i = 0; i < ARRAY_SIZE(sdp_tests); i++) { + struct sdp_test_data *t = &sdp_tests[i]; + struct sdp_msg sdp = {}; + char str[1024]; + printf("\n[%d]\n", i); + dump_sdp(t->sdp_input, "sdp input: "); + + sdp_msg_from_str(&sdp, t->sdp_input); + sdp_msg_to_str(str, sizeof(str), &sdp); + + dump_sdp(str, "sdp_msg_to_str: "); + if (strcmp(str, t->expect_sdp_str)) { + int j; + ok = false; + printf("ERROR:\n"); + dump_sdp(t->expect_sdp_str, "expect_sdp_str: "); + for (j = 0; t->expect_sdp_str[j]; j++) { + if (t->expect_sdp_str[j] != str[j]) { + printf("ERROR at position %d, at:\n", j); + dump_sdp(str + j, " mismatch: "); + break; + } + } + } else + printf("[%d] ok\n", i); + } + + OSMO_ASSERT(ok); +} + +struct sdp_intersect_test_data { + const char *descr; + const char *sdp_a; + const char *sdp_b; + const char *expect_intersection; +}; + +#define SDP_1 \ + "v=0\r\n" \ + "o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n" \ + "s=GSM Call\r\n" \ + "c=IN IP4 23.42.23.42\r\n" \ + "t=0 0\r\n" \ + "m=audio 30436 RTP/AVP 112 3 111 110\r\n" \ + "a=rtpmap:112 AMR/8000\r\n" \ + "a=fmtp:112 octet-align=1\r\n" \ + "a=rtpmap:3 GSM/8000\r\n" \ + "a=rtpmap:111 GSM-HR-08/8000\r\n" \ + "a=rtpmap:110 GSM-EFR/8000\r\n" \ + "a=ptime:20\r\n" + +#define SDP_2 \ + "v=0\r\n" \ + "o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n" \ + "s=GSM Call\r\n" \ + "c=IN IP4 23.42.23.42\r\n" \ + "t=0 0\r\n" \ + "m=audio 30436 RTP/AVP 112 110\r\n" \ + "a=rtpmap:112 AMR/8000\r\n" \ + "a=fmtp:112 octet-align=1\r\n" \ + "a=rtpmap:110 GSM-EFR/8000\r\n" \ + "a=ptime:20\r\n" + +#define SDP_3 \ + "v=0\r\n" \ + "o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n" \ + "s=GSM Call\r\n" \ + "c=IN IP4 23.42.23.42\r\n" \ + "t=0 0\r\n" \ + "m=audio 30436 RTP/AVP 3 111\r\n" \ + "a=rtpmap:3 GSM/8000\r\n" \ + "a=rtpmap:111 GSM-HR-08/8000\r\n" \ + "a=ptime:20\r\n" + + +struct sdp_intersect_test_data sdp_intersect_tests[] = { + { + "identical codecs lead to no change" + , + SDP_1 + , + "c=IN IP4 5.6.7.8\r\n" \ + "m=audio 12345 RTP/AVP 112 3 111 110\r\n" + "a=rtpmap:112 AMR/8000\r\n" + "a=fmtp:112 octet-align=1\r\n" + "a=rtpmap:3 GSM/8000\r\n" + "a=rtpmap:111 GSM-HR-08/8000\r\n" + "a=rtpmap:110 GSM-EFR/8000\r\n" + , + SDP_1 + }, + { + "identical codecs in different order also lead to no change" + , + SDP_1 + , + "c=IN IP4 5.6.7.8\r\n" \ + "m=audio 12345 RTP/AVP 3 110 111 112\r\n" + "a=rtpmap:3 GSM/8000\r\n" + "a=rtpmap:110 GSM-EFR/8000\r\n" + "a=rtpmap:111 GSM-HR-08/8000\r\n" + "a=rtpmap:112 AMR/8000\r\n" + "a=fmtp:112 octet-align=1\r\n" + , + SDP_1 + }, + { + "identical codecs with mismatching payload type numbers also lead to no change" + , + SDP_1 + , + "c=IN IP4 5.6.7.8\r\n" \ + "m=audio 12345 RTP/AVP 96 97 98 99\r\n" + "a=rtpmap:96 GSM/8000\r\n" + "a=rtpmap:97 GSM-EFR/8000\r\n" + "a=rtpmap:98 GSM-HR-08/8000\r\n" + "a=rtpmap:99 AMR/8000\r\n" + "a=fmtp:99 octet-align=1\r\n" + , + SDP_1 + }, + { + "identical codecs plus some extra codecs also lead to no change" + , + SDP_1 + , + "c=IN IP4 5.6.7.8\r\n" \ + "m=audio 12345 RTP/AVP 8 0 96 97 98 99\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:96 GSM/8000\r\n" + "a=rtpmap:97 GSM-EFR/8000\r\n" + "a=rtpmap:98 GSM-HR-08/8000\r\n" + "a=rtpmap:99 AMR/8000\r\n" + "a=fmtp:99 octet-align=1\r\n" + , + SDP_1 + }, + { + "some codecs removed", + SDP_1, + SDP_2, + SDP_2, + }, + { + "other codecs removed", + SDP_1, + SDP_3, + SDP_3, + }, + { + "all codecs removed", + SDP_1 + , + "s=empty" + , + "v=0\r\n" \ + "o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n" \ + "s=GSM Call\r\n" \ + "c=IN IP4 23.42.23.42\r\n" \ + "t=0 0\r\n" \ + "m=audio 30436 RTP/AVP\r\n" \ + "a=ptime:20\r\n" + }, + { + "some real world test case" + , + "v=0\r\n" + "o=OsmoMSC 0 0 IN IP4 0.0.0.0\r\n" + "s=GSM Call\r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "m=audio 0 RTP/AVP 112 113 110 3 111\r\n" + "a=rtpmap:112 AMR/8000\r\n" + "a=fmtp:112 octet-align=1;mode-set=0,1,2,3\r\n" + "a=rtpmap:113 AMR-WB/8000\r\n" + "a=fmtp:113 octet-align=1\r\n" + "a=rtpmap:110 GSM-EFR/8000\r\n" + "a=rtpmap:3 GSM/8000\r\n" + "a=rtpmap:111 GSM-HR-08/8000\r\n" + "a=ptime:20\r\n" + , + "v=0\r\n" + "o=OsmoMSC 0 0 IN IP4 0.0.0.0\r\n" + "s=GSM Call\r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "m=audio 0 RTP/AVP 112 113 110 3 111\r\n" + "a=rtpmap:112 AMR/8000\r\n" + "a=fmtp:112 octet-align=1;mode-set=0,1,2,3\r\n" + "a=rtpmap:113 AMR-WB/8000\r\n" + "a=fmtp:113 octet-align=1\r\n" + "a=rtpmap:110 GSM-EFR/8000\r\n" + "a=rtpmap:3 GSM/8000\r\n" + "a=rtpmap:111 GSM-HR-08/8000\r\n" + "a=ptime:20\r\n" + , + "v=0\r\n" + "o=OsmoMSC 0 0 IN IP4 0.0.0.0\r\n" + "s=GSM Call\r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "m=audio 0 RTP/AVP 112 113 110 3 111\r\n" + "a=rtpmap:112 AMR/8000\r\n" + "a=fmtp:112 octet-align=1;mode-set=0,1,2,3\r\n" + "a=rtpmap:113 AMR-WB/8000\r\n" + "a=fmtp:113 octet-align=1\r\n" + "a=rtpmap:110 GSM-EFR/8000\r\n" + "a=rtpmap:3 GSM/8000\r\n" + "a=rtpmap:111 GSM-HR-08/8000\r\n" + "a=ptime:20\r\n" + } +}; + +const char *sdp_msg_logstr(const struct sdp_msg *sdp) +{ + static char buf[1024]; + sdp_msg_to_str(buf, sizeof(buf), sdp); + return buf; +} + +static void test_intersect() +{ + int i; + bool ok = true; + int rc; + + printf("\n\n%s\n", __func__); + + for (i = 0; i < ARRAY_SIZE(sdp_intersect_tests); i++) { + struct sdp_intersect_test_data *t = &sdp_intersect_tests[i]; + struct sdp_msg sdp_a = {}; + struct sdp_msg sdp_b = {}; + char str[1024]; + printf("\n[%d] %s\n", i, t->descr); + dump_sdp(t->sdp_a, "SDP A: "); + dump_sdp(t->sdp_b, " SDP B: "); + + rc = sdp_msg_from_str(&sdp_a, t->sdp_a); + if (rc) { + printf("ERROR parsing SDP A: %d\n", rc); + break; + } + dump_sdp(sdp_msg_logstr(&sdp_a), "parsed SDP A: "); + rc = sdp_msg_from_str(&sdp_b, t->sdp_b); + if (rc) { + printf("ERROR parsing SDP A: %d\n", rc); + break; + } + dump_sdp(sdp_msg_logstr(&sdp_b), "parsed SDP B: "); + sdp_audio_codecs_intersection(&sdp_a.audio_codecs, &sdp_b.audio_codecs, false); + sdp_msg_to_str(str, sizeof(str), &sdp_a); + + dump_sdp(str, "sdp_msg_intersection(a,b): "); + if (strcmp(str, t->expect_intersection)) { + int j; + ok = false; + printf("ERROR:\n"); + dump_sdp(t->expect_intersection, "expect_intersection: "); + for (j = 0; t->expect_intersection[j]; j++) { + if (t->expect_intersection[j] != str[j]) { + printf("ERROR at position %d, at:\n", j); + dump_sdp(str + j, " mismatch: "); + break; + } + } + } else + printf("[%d] ok\n", i); + } + + OSMO_ASSERT(ok); +} + +struct sdp_select_test_data { + const char *sdp; + unsigned int select_payload_type; + const char *expect_sdp; +}; + +struct sdp_select_test_data sdp_select_tests[] = { + { + "v=0\r\n" + "o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n" + "s=GSM Call\r\n" + "c=IN IP4 23.42.23.42\r\n" + "t=0 0\r\n" + "m=audio 30436 RTP/AVP 112 3 111 110\r\n" + "a=rtpmap:112 AMR/8000\r\n" + "a=fmtp:112 octet-align=1\r\n" + "a=rtpmap:3 GSM/8000\r\n" + "a=rtpmap:111 GSM-HR-08/8000\r\n" + "a=rtpmap:110 GSM-EFR/8000\r\n" + "a=ptime:20\r\n" + , + 112, + NULL + }, + { + "v=0\r\n" + "o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n" + "s=GSM Call\r\n" + "c=IN IP4 23.42.23.42\r\n" + "t=0 0\r\n" + "m=audio 30436 RTP/AVP 112 3 111 110\r\n" + "a=rtpmap:112 AMR/8000\r\n" + "a=fmtp:112 octet-align=1\r\n" + "a=rtpmap:3 GSM/8000\r\n" + "a=rtpmap:111 GSM-HR-08/8000\r\n" + "a=rtpmap:110 GSM-EFR/8000\r\n" + "a=ptime:20\r\n" + , + 3, + "v=0\r\n" + "o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n" + "s=GSM Call\r\n" + "c=IN IP4 23.42.23.42\r\n" + "t=0 0\r\n" + "m=audio 30436 RTP/AVP 3 112 111 110\r\n" + "a=rtpmap:3 GSM/8000\r\n" + "a=rtpmap:112 AMR/8000\r\n" + "a=fmtp:112 octet-align=1\r\n" + "a=rtpmap:111 GSM-HR-08/8000\r\n" + "a=rtpmap:110 GSM-EFR/8000\r\n" + "a=ptime:20\r\n" + }, + { + "v=0\r\n" + "o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n" + "s=GSM Call\r\n" + "c=IN IP4 23.42.23.42\r\n" + "t=0 0\r\n" + "m=audio 30436 RTP/AVP 112 3 111 110\r\n" + "a=rtpmap:112 AMR/8000\r\n" + "a=fmtp:112 octet-align=1\r\n" + "a=rtpmap:3 GSM/8000\r\n" + "a=rtpmap:111 GSM-HR-08/8000\r\n" + "a=rtpmap:110 GSM-EFR/8000\r\n" + "a=ptime:20\r\n" + , + 111, + "v=0\r\n" + "o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n" + "s=GSM Call\r\n" + "c=IN IP4 23.42.23.42\r\n" + "t=0 0\r\n" + "m=audio 30436 RTP/AVP 111 112 3 110\r\n" + "a=rtpmap:111 GSM-HR-08/8000\r\n" + "a=rtpmap:112 AMR/8000\r\n" + "a=fmtp:112 octet-align=1\r\n" + "a=rtpmap:3 GSM/8000\r\n" + "a=rtpmap:110 GSM-EFR/8000\r\n" + "a=ptime:20\r\n" + }, + { + "v=0\r\n" + "o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n" + "s=GSM Call\r\n" + "c=IN IP4 23.42.23.42\r\n" + "t=0 0\r\n" + "m=audio 30436 RTP/AVP 112 3 111 110\r\n" + "a=rtpmap:112 AMR/8000\r\n" + "a=fmtp:112 octet-align=1\r\n" + "a=rtpmap:3 GSM/8000\r\n" + "a=rtpmap:111 GSM-HR-08/8000\r\n" + "a=rtpmap:110 GSM-EFR/8000\r\n" + "a=ptime:20\r\n" + , + 110, + "v=0\r\n" + "o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n" + "s=GSM Call\r\n" + "c=IN IP4 23.42.23.42\r\n" + "t=0 0\r\n" + "m=audio 30436 RTP/AVP 110 112 3 111\r\n" + "a=rtpmap:110 GSM-EFR/8000\r\n" + "a=rtpmap:112 AMR/8000\r\n" + "a=fmtp:112 octet-align=1\r\n" + "a=rtpmap:3 GSM/8000\r\n" + "a=rtpmap:111 GSM-HR-08/8000\r\n" + "a=ptime:20\r\n" + }, + +}; + +static void test_select() +{ + int i; + bool ok = true; + int rc; + + printf("\n\n%s\n", __func__); + + for (i = 0; i < ARRAY_SIZE(sdp_select_tests); i++) { + struct sdp_select_test_data *t = &sdp_select_tests[i]; + struct sdp_msg sdp = {}; + struct sdp_audio_codec *codec; + char buf[1024]; + printf("\n[%d]\n", i); + rc = sdp_msg_from_str(&sdp, t->sdp); + if (rc) { + printf("ERROR parsing SDP: %d\n", rc); + break; + } + printf("SDP: %s\n", sdp_audio_codecs_name(&sdp.audio_codecs)); + codec = sdp_audio_codec_by_payload_type(&sdp.audio_codecs, t->select_payload_type, false); + OSMO_ASSERT(codec); + printf("Select: %s\n", sdp_audio_codec_name(codec)); + + sdp_audio_codecs_select(&sdp.audio_codecs, codec); + + printf("SDP: %s\n", sdp_audio_codecs_name(&sdp.audio_codecs)); + sdp_msg_to_str(buf, sizeof(buf), &sdp); + + if (strcmp(buf, t->expect_sdp ? : t->sdp)) { + int j; + ok = false; + printf("ERROR:\n"); + dump_sdp(buf, "selection result: "); + dump_sdp(t->expect_sdp, "expect result: "); + for (j = 0; t->expect_sdp[j]; j++) { + if (t->expect_sdp[j] != buf[j]) { + printf("ERROR at position %d, at:\n", j); + dump_sdp(buf + j, " mismatch: "); + break; + } + } + } else + printf("[%d] ok\n", i); + } + + OSMO_ASSERT(ok); +} + +int main(void) +{ + test_parse_and_compose(); + test_intersect(); + test_select(); + return 0; +} diff --git a/tests/sdp_msg/sdp_msg_test.err b/tests/sdp_msg/sdp_msg_test.err new file mode 100644 index 000000000..e69de29bb diff --git a/tests/sdp_msg/sdp_msg_test.ok b/tests/sdp_msg/sdp_msg_test.ok new file mode 100644 index 000000000..a7a3d5156 --- /dev/null +++ b/tests/sdp_msg/sdp_msg_test.ok @@ -0,0 +1,590 @@ + + +test_parse_and_compose + +[0] +sdp input: v=0\r\n +sdp input: o=- 5628250 5628250 IN IP4 192.168.11.121\r\n +sdp input: s=-\r\n +sdp input: c=IN IP4 192.168.11.121\r\n +sdp input: t=0 0\r\n +sdp input: m=audio 10020 RTP/AVP 18 0 2 4 8 96 97 98 100 101\r\n +sdp input: a=rtpmap:18 G729/8000\r\n +sdp input: a=rtpmap:0 PCMU/8000\r\n +sdp input: a=rtpmap:2 G726-32/8000\r\n +sdp input: a=rtpmap:4 G723/8000\r\n +sdp input: a=rtpmap:8 PCMA/8000\r\n +sdp input: a=rtpmap:96 G726-40/8000\r\n +sdp input: a=rtpmap:97 G726-24/8000\r\n +sdp input: a=rtpmap:98 G726-16/8000\r\n +sdp input: a=rtpmap:100 NSE/8000\r\n +sdp input: a=fmtp:100 192-193\r\n +sdp input: a=rtpmap:101 telephone-event/8000\r\n +sdp input: a=fmtp:101 0-15\r\n +sdp input: a=ptime:20\r\n +sdp input: a=sendrecv\r\n +sdp_msg_to_str: v=0\r\n +sdp_msg_to_str: o=OsmoMSC 0 0 IN IP4 192.168.11.121\r\n +sdp_msg_to_str: s=GSM Call\r\n +sdp_msg_to_str: c=IN IP4 192.168.11.121\r\n +sdp_msg_to_str: t=0 0\r\n +sdp_msg_to_str: m=audio 10020 RTP/AVP 18 0 2 4 8 96 97 98 100 101\r\n +sdp_msg_to_str: a=rtpmap:18 G729/8000\r\n +sdp_msg_to_str: a=rtpmap:0 PCMU/8000\r\n +sdp_msg_to_str: a=rtpmap:2 G726-32/8000\r\n +sdp_msg_to_str: a=rtpmap:4 G723/8000\r\n +sdp_msg_to_str: a=rtpmap:8 PCMA/8000\r\n +sdp_msg_to_str: a=rtpmap:96 G726-40/8000\r\n +sdp_msg_to_str: a=rtpmap:97 G726-24/8000\r\n +sdp_msg_to_str: a=rtpmap:98 G726-16/8000\r\n +sdp_msg_to_str: a=rtpmap:100 NSE/8000\r\n +sdp_msg_to_str: a=fmtp:100 192-193\r\n +sdp_msg_to_str: a=rtpmap:101 telephone-event/8000\r\n +sdp_msg_to_str: a=fmtp:101 0-15\r\n +sdp_msg_to_str: a=ptime:20\r\n +[0] ok + +[1] +sdp input: v=0\r\n +sdp input: o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n +sdp input: s=FooBar\r\n +sdp input: c=IN IP4 192.168.11.151\r\n +sdp input: t=0 0\r\n +sdp input: m=audio 16398 RTP/AVP 98\r\n +sdp input: a=rtpmap:98 AMR/8000\r\n +sdp input: a=fmtp:98 octet-align=1; mode-set=4\r\n +sdp input: a=ptime:20\r\n +sdp input: a=rtcp:16399 IN IP4 192.168.11.151\r\n +sdp_msg_to_str: v=0\r\n +sdp_msg_to_str: o=OsmoMSC 0 0 IN IP4 192.168.11.151\r\n +sdp_msg_to_str: s=GSM Call\r\n +sdp_msg_to_str: c=IN IP4 192.168.11.151\r\n +sdp_msg_to_str: t=0 0\r\n +sdp_msg_to_str: m=audio 16398 RTP/AVP 98\r\n +sdp_msg_to_str: a=rtpmap:98 AMR/8000\r\n +sdp_msg_to_str: a=fmtp:98 octet-align=1; mode-set=4\r\n +sdp_msg_to_str: a=ptime:20\r\n +[1] ok + +[2] +sdp input: v=0\r\n +sdp input: o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n +sdp input: s=FooBar\r\n +sdp input: c=IN IP4 192.168.11.140\r\n +sdp input: t=0 0\r\n +sdp input: m=audio 30436 RTP/AVP 18 0 4 8 101\r\n +sdp input: a=rtpmap:18 G729/8000\r\n +sdp input: a=rtpmap:0 PCMU/8000\r\n +sdp input: a=rtpmap:4 G723/8000\r\n +sdp input: a=rtpmap:8 PCMA/8000\r\n +sdp input: a=rtpmap:101 telephone-event/8000\r\n +sdp input: a=fmtp:101 0-15\r\n +sdp input: a=sendrecv\r\n +sdp input: a=rtcp:30437\r\n +sdp input: a=ptime:20\r\n +sdp_msg_to_str: v=0\r\n +sdp_msg_to_str: o=OsmoMSC 0 0 IN IP4 192.168.11.140\r\n +sdp_msg_to_str: s=GSM Call\r\n +sdp_msg_to_str: c=IN IP4 192.168.11.140\r\n +sdp_msg_to_str: t=0 0\r\n +sdp_msg_to_str: m=audio 30436 RTP/AVP 18 0 4 8 101\r\n +sdp_msg_to_str: a=rtpmap:18 G729/8000\r\n +sdp_msg_to_str: a=rtpmap:0 PCMU/8000\r\n +sdp_msg_to_str: a=rtpmap:4 G723/8000\r\n +sdp_msg_to_str: a=rtpmap:8 PCMA/8000\r\n +sdp_msg_to_str: a=rtpmap:101 telephone-event/8000\r\n +sdp_msg_to_str: a=fmtp:101 0-15\r\n +sdp_msg_to_str: a=ptime:20\r\n +[2] ok + + +test_intersect + +[0] identical codecs lead to no change +SDP A: v=0\r\n +SDP A: o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +SDP A: s=GSM Call\r\n +SDP A: c=IN IP4 23.42.23.42\r\n +SDP A: t=0 0\r\n +SDP A: m=audio 30436 RTP/AVP 112 3 111 110\r\n +SDP A: a=rtpmap:112 AMR/8000\r\n +SDP A: a=fmtp:112 octet-align=1\r\n +SDP A: a=rtpmap:3 GSM/8000\r\n +SDP A: a=rtpmap:111 GSM-HR-08/8000\r\n +SDP A: a=rtpmap:110 GSM-EFR/8000\r\n +SDP A: a=ptime:20\r\n + SDP B: c=IN IP4 5.6.7.8\r\n + SDP B: m=audio 12345 RTP/AVP 112 3 111 110\r\n + SDP B: a=rtpmap:112 AMR/8000\r\n + SDP B: a=fmtp:112 octet-align=1\r\n + SDP B: a=rtpmap:3 GSM/8000\r\n + SDP B: a=rtpmap:111 GSM-HR-08/8000\r\n + SDP B: a=rtpmap:110 GSM-EFR/8000\r\n +parsed SDP A: v=0\r\n +parsed SDP A: o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +parsed SDP A: s=GSM Call\r\n +parsed SDP A: c=IN IP4 23.42.23.42\r\n +parsed SDP A: t=0 0\r\n +parsed SDP A: m=audio 30436 RTP/AVP 112 3 111 110\r\n +parsed SDP A: a=rtpmap:112 AMR/8000\r\n +parsed SDP A: a=fmtp:112 octet-align=1\r\n +parsed SDP A: a=rtpmap:3 GSM/8000\r\n +parsed SDP A: a=rtpmap:111 GSM-HR-08/8000\r\n +parsed SDP A: a=rtpmap:110 GSM-EFR/8000\r\n +parsed SDP A: a=ptime:20\r\n +parsed SDP B: v=0\r\n +parsed SDP B: o=OsmoMSC 0 0 IN IP4 5.6.7.8\r\n +parsed SDP B: s=GSM Call\r\n +parsed SDP B: c=IN IP4 5.6.7.8\r\n +parsed SDP B: t=0 0\r\n +parsed SDP B: m=audio 12345 RTP/AVP 112 3 111 110\r\n +parsed SDP B: a=rtpmap:112 AMR/8000\r\n +parsed SDP B: a=fmtp:112 octet-align=1\r\n +parsed SDP B: a=rtpmap:3 GSM/8000\r\n +parsed SDP B: a=rtpmap:111 GSM-HR-08/8000\r\n +parsed SDP B: a=rtpmap:110 GSM-EFR/8000\r\n +parsed SDP B: a=ptime:20\r\n +sdp_msg_intersection(a,b): v=0\r\n +sdp_msg_intersection(a,b): o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +sdp_msg_intersection(a,b): s=GSM Call\r\n +sdp_msg_intersection(a,b): c=IN IP4 23.42.23.42\r\n +sdp_msg_intersection(a,b): t=0 0\r\n +sdp_msg_intersection(a,b): m=audio 30436 RTP/AVP 112 3 111 110\r\n +sdp_msg_intersection(a,b): a=rtpmap:112 AMR/8000\r\n +sdp_msg_intersection(a,b): a=fmtp:112 octet-align=1\r\n +sdp_msg_intersection(a,b): a=rtpmap:3 GSM/8000\r\n +sdp_msg_intersection(a,b): a=rtpmap:111 GSM-HR-08/8000\r\n +sdp_msg_intersection(a,b): a=rtpmap:110 GSM-EFR/8000\r\n +sdp_msg_intersection(a,b): a=ptime:20\r\n +[0] ok + +[1] identical codecs in different order also lead to no change +SDP A: v=0\r\n +SDP A: o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +SDP A: s=GSM Call\r\n +SDP A: c=IN IP4 23.42.23.42\r\n +SDP A: t=0 0\r\n +SDP A: m=audio 30436 RTP/AVP 112 3 111 110\r\n +SDP A: a=rtpmap:112 AMR/8000\r\n +SDP A: a=fmtp:112 octet-align=1\r\n +SDP A: a=rtpmap:3 GSM/8000\r\n +SDP A: a=rtpmap:111 GSM-HR-08/8000\r\n +SDP A: a=rtpmap:110 GSM-EFR/8000\r\n +SDP A: a=ptime:20\r\n + SDP B: c=IN IP4 5.6.7.8\r\n + SDP B: m=audio 12345 RTP/AVP 3 110 111 112\r\n + SDP B: a=rtpmap:3 GSM/8000\r\n + SDP B: a=rtpmap:110 GSM-EFR/8000\r\n + SDP B: a=rtpmap:111 GSM-HR-08/8000\r\n + SDP B: a=rtpmap:112 AMR/8000\r\n + SDP B: a=fmtp:112 octet-align=1\r\n +parsed SDP A: v=0\r\n +parsed SDP A: o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +parsed SDP A: s=GSM Call\r\n +parsed SDP A: c=IN IP4 23.42.23.42\r\n +parsed SDP A: t=0 0\r\n +parsed SDP A: m=audio 30436 RTP/AVP 112 3 111 110\r\n +parsed SDP A: a=rtpmap:112 AMR/8000\r\n +parsed SDP A: a=fmtp:112 octet-align=1\r\n +parsed SDP A: a=rtpmap:3 GSM/8000\r\n +parsed SDP A: a=rtpmap:111 GSM-HR-08/8000\r\n +parsed SDP A: a=rtpmap:110 GSM-EFR/8000\r\n +parsed SDP A: a=ptime:20\r\n +parsed SDP B: v=0\r\n +parsed SDP B: o=OsmoMSC 0 0 IN IP4 5.6.7.8\r\n +parsed SDP B: s=GSM Call\r\n +parsed SDP B: c=IN IP4 5.6.7.8\r\n +parsed SDP B: t=0 0\r\n +parsed SDP B: m=audio 12345 RTP/AVP 3 110 111 112\r\n +parsed SDP B: a=rtpmap:3 GSM/8000\r\n +parsed SDP B: a=rtpmap:110 GSM-EFR/8000\r\n +parsed SDP B: a=rtpmap:111 GSM-HR-08/8000\r\n +parsed SDP B: a=rtpmap:112 AMR/8000\r\n +parsed SDP B: a=fmtp:112 octet-align=1\r\n +parsed SDP B: a=ptime:20\r\n +sdp_msg_intersection(a,b): v=0\r\n +sdp_msg_intersection(a,b): o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +sdp_msg_intersection(a,b): s=GSM Call\r\n +sdp_msg_intersection(a,b): c=IN IP4 23.42.23.42\r\n +sdp_msg_intersection(a,b): t=0 0\r\n +sdp_msg_intersection(a,b): m=audio 30436 RTP/AVP 112 3 111 110\r\n +sdp_msg_intersection(a,b): a=rtpmap:112 AMR/8000\r\n +sdp_msg_intersection(a,b): a=fmtp:112 octet-align=1\r\n +sdp_msg_intersection(a,b): a=rtpmap:3 GSM/8000\r\n +sdp_msg_intersection(a,b): a=rtpmap:111 GSM-HR-08/8000\r\n +sdp_msg_intersection(a,b): a=rtpmap:110 GSM-EFR/8000\r\n +sdp_msg_intersection(a,b): a=ptime:20\r\n +[1] ok + +[2] identical codecs with mismatching payload type numbers also lead to no change +SDP A: v=0\r\n +SDP A: o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +SDP A: s=GSM Call\r\n +SDP A: c=IN IP4 23.42.23.42\r\n +SDP A: t=0 0\r\n +SDP A: m=audio 30436 RTP/AVP 112 3 111 110\r\n +SDP A: a=rtpmap:112 AMR/8000\r\n +SDP A: a=fmtp:112 octet-align=1\r\n +SDP A: a=rtpmap:3 GSM/8000\r\n +SDP A: a=rtpmap:111 GSM-HR-08/8000\r\n +SDP A: a=rtpmap:110 GSM-EFR/8000\r\n +SDP A: a=ptime:20\r\n + SDP B: c=IN IP4 5.6.7.8\r\n + SDP B: m=audio 12345 RTP/AVP 96 97 98 99\r\n + SDP B: a=rtpmap:96 GSM/8000\r\n + SDP B: a=rtpmap:97 GSM-EFR/8000\r\n + SDP B: a=rtpmap:98 GSM-HR-08/8000\r\n + SDP B: a=rtpmap:99 AMR/8000\r\n + SDP B: a=fmtp:99 octet-align=1\r\n +parsed SDP A: v=0\r\n +parsed SDP A: o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +parsed SDP A: s=GSM Call\r\n +parsed SDP A: c=IN IP4 23.42.23.42\r\n +parsed SDP A: t=0 0\r\n +parsed SDP A: m=audio 30436 RTP/AVP 112 3 111 110\r\n +parsed SDP A: a=rtpmap:112 AMR/8000\r\n +parsed SDP A: a=fmtp:112 octet-align=1\r\n +parsed SDP A: a=rtpmap:3 GSM/8000\r\n +parsed SDP A: a=rtpmap:111 GSM-HR-08/8000\r\n +parsed SDP A: a=rtpmap:110 GSM-EFR/8000\r\n +parsed SDP A: a=ptime:20\r\n +parsed SDP B: v=0\r\n +parsed SDP B: o=OsmoMSC 0 0 IN IP4 5.6.7.8\r\n +parsed SDP B: s=GSM Call\r\n +parsed SDP B: c=IN IP4 5.6.7.8\r\n +parsed SDP B: t=0 0\r\n +parsed SDP B: m=audio 12345 RTP/AVP 96 97 98 99\r\n +parsed SDP B: a=rtpmap:96 GSM/8000\r\n +parsed SDP B: a=rtpmap:97 GSM-EFR/8000\r\n +parsed SDP B: a=rtpmap:98 GSM-HR-08/8000\r\n +parsed SDP B: a=rtpmap:99 AMR/8000\r\n +parsed SDP B: a=fmtp:99 octet-align=1\r\n +parsed SDP B: a=ptime:20\r\n +sdp_msg_intersection(a,b): v=0\r\n +sdp_msg_intersection(a,b): o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +sdp_msg_intersection(a,b): s=GSM Call\r\n +sdp_msg_intersection(a,b): c=IN IP4 23.42.23.42\r\n +sdp_msg_intersection(a,b): t=0 0\r\n +sdp_msg_intersection(a,b): m=audio 30436 RTP/AVP 112 3 111 110\r\n +sdp_msg_intersection(a,b): a=rtpmap:112 AMR/8000\r\n +sdp_msg_intersection(a,b): a=fmtp:112 octet-align=1\r\n +sdp_msg_intersection(a,b): a=rtpmap:3 GSM/8000\r\n +sdp_msg_intersection(a,b): a=rtpmap:111 GSM-HR-08/8000\r\n +sdp_msg_intersection(a,b): a=rtpmap:110 GSM-EFR/8000\r\n +sdp_msg_intersection(a,b): a=ptime:20\r\n +[2] ok + +[3] identical codecs plus some extra codecs also lead to no change +SDP A: v=0\r\n +SDP A: o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +SDP A: s=GSM Call\r\n +SDP A: c=IN IP4 23.42.23.42\r\n +SDP A: t=0 0\r\n +SDP A: m=audio 30436 RTP/AVP 112 3 111 110\r\n +SDP A: a=rtpmap:112 AMR/8000\r\n +SDP A: a=fmtp:112 octet-align=1\r\n +SDP A: a=rtpmap:3 GSM/8000\r\n +SDP A: a=rtpmap:111 GSM-HR-08/8000\r\n +SDP A: a=rtpmap:110 GSM-EFR/8000\r\n +SDP A: a=ptime:20\r\n + SDP B: c=IN IP4 5.6.7.8\r\n + SDP B: m=audio 12345 RTP/AVP 8 0 96 97 98 99\r\n + SDP B: a=rtpmap:8 PCMA/8000\r\n + SDP B: a=rtpmap:0 PCMU/8000\r\n + SDP B: a=rtpmap:96 GSM/8000\r\n + SDP B: a=rtpmap:97 GSM-EFR/8000\r\n + SDP B: a=rtpmap:98 GSM-HR-08/8000\r\n + SDP B: a=rtpmap:99 AMR/8000\r\n + SDP B: a=fmtp:99 octet-align=1\r\n +parsed SDP A: v=0\r\n +parsed SDP A: o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +parsed SDP A: s=GSM Call\r\n +parsed SDP A: c=IN IP4 23.42.23.42\r\n +parsed SDP A: t=0 0\r\n +parsed SDP A: m=audio 30436 RTP/AVP 112 3 111 110\r\n +parsed SDP A: a=rtpmap:112 AMR/8000\r\n +parsed SDP A: a=fmtp:112 octet-align=1\r\n +parsed SDP A: a=rtpmap:3 GSM/8000\r\n +parsed SDP A: a=rtpmap:111 GSM-HR-08/8000\r\n +parsed SDP A: a=rtpmap:110 GSM-EFR/8000\r\n +parsed SDP A: a=ptime:20\r\n +parsed SDP B: v=0\r\n +parsed SDP B: o=OsmoMSC 0 0 IN IP4 5.6.7.8\r\n +parsed SDP B: s=GSM Call\r\n +parsed SDP B: c=IN IP4 5.6.7.8\r\n +parsed SDP B: t=0 0\r\n +parsed SDP B: m=audio 12345 RTP/AVP 8 0 96 97 98 99\r\n +parsed SDP B: a=rtpmap:8 PCMA/8000\r\n +parsed SDP B: a=rtpmap:0 PCMU/8000\r\n +parsed SDP B: a=rtpmap:96 GSM/8000\r\n +parsed SDP B: a=rtpmap:97 GSM-EFR/8000\r\n +parsed SDP B: a=rtpmap:98 GSM-HR-08/8000\r\n +parsed SDP B: a=rtpmap:99 AMR/8000\r\n +parsed SDP B: a=fmtp:99 octet-align=1\r\n +parsed SDP B: a=ptime:20\r\n +sdp_msg_intersection(a,b): v=0\r\n +sdp_msg_intersection(a,b): o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +sdp_msg_intersection(a,b): s=GSM Call\r\n +sdp_msg_intersection(a,b): c=IN IP4 23.42.23.42\r\n +sdp_msg_intersection(a,b): t=0 0\r\n +sdp_msg_intersection(a,b): m=audio 30436 RTP/AVP 112 3 111 110\r\n +sdp_msg_intersection(a,b): a=rtpmap:112 AMR/8000\r\n +sdp_msg_intersection(a,b): a=fmtp:112 octet-align=1\r\n +sdp_msg_intersection(a,b): a=rtpmap:3 GSM/8000\r\n +sdp_msg_intersection(a,b): a=rtpmap:111 GSM-HR-08/8000\r\n +sdp_msg_intersection(a,b): a=rtpmap:110 GSM-EFR/8000\r\n +sdp_msg_intersection(a,b): a=ptime:20\r\n +[3] ok + +[4] some codecs removed +SDP A: v=0\r\n +SDP A: o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +SDP A: s=GSM Call\r\n +SDP A: c=IN IP4 23.42.23.42\r\n +SDP A: t=0 0\r\n +SDP A: m=audio 30436 RTP/AVP 112 3 111 110\r\n +SDP A: a=rtpmap:112 AMR/8000\r\n +SDP A: a=fmtp:112 octet-align=1\r\n +SDP A: a=rtpmap:3 GSM/8000\r\n +SDP A: a=rtpmap:111 GSM-HR-08/8000\r\n +SDP A: a=rtpmap:110 GSM-EFR/8000\r\n +SDP A: a=ptime:20\r\n + SDP B: v=0\r\n + SDP B: o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n + SDP B: s=GSM Call\r\n + SDP B: c=IN IP4 23.42.23.42\r\n + SDP B: t=0 0\r\n + SDP B: m=audio 30436 RTP/AVP 112 110\r\n + SDP B: a=rtpmap:112 AMR/8000\r\n + SDP B: a=fmtp:112 octet-align=1\r\n + SDP B: a=rtpmap:110 GSM-EFR/8000\r\n + SDP B: a=ptime:20\r\n +parsed SDP A: v=0\r\n +parsed SDP A: o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +parsed SDP A: s=GSM Call\r\n +parsed SDP A: c=IN IP4 23.42.23.42\r\n +parsed SDP A: t=0 0\r\n +parsed SDP A: m=audio 30436 RTP/AVP 112 3 111 110\r\n +parsed SDP A: a=rtpmap:112 AMR/8000\r\n +parsed SDP A: a=fmtp:112 octet-align=1\r\n +parsed SDP A: a=rtpmap:3 GSM/8000\r\n +parsed SDP A: a=rtpmap:111 GSM-HR-08/8000\r\n +parsed SDP A: a=rtpmap:110 GSM-EFR/8000\r\n +parsed SDP A: a=ptime:20\r\n +parsed SDP B: v=0\r\n +parsed SDP B: o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +parsed SDP B: s=GSM Call\r\n +parsed SDP B: c=IN IP4 23.42.23.42\r\n +parsed SDP B: t=0 0\r\n +parsed SDP B: m=audio 30436 RTP/AVP 112 110\r\n +parsed SDP B: a=rtpmap:112 AMR/8000\r\n +parsed SDP B: a=fmtp:112 octet-align=1\r\n +parsed SDP B: a=rtpmap:110 GSM-EFR/8000\r\n +parsed SDP B: a=ptime:20\r\n +sdp_msg_intersection(a,b): v=0\r\n +sdp_msg_intersection(a,b): o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +sdp_msg_intersection(a,b): s=GSM Call\r\n +sdp_msg_intersection(a,b): c=IN IP4 23.42.23.42\r\n +sdp_msg_intersection(a,b): t=0 0\r\n +sdp_msg_intersection(a,b): m=audio 30436 RTP/AVP 112 110\r\n +sdp_msg_intersection(a,b): a=rtpmap:112 AMR/8000\r\n +sdp_msg_intersection(a,b): a=fmtp:112 octet-align=1\r\n +sdp_msg_intersection(a,b): a=rtpmap:110 GSM-EFR/8000\r\n +sdp_msg_intersection(a,b): a=ptime:20\r\n +[4] ok + +[5] other codecs removed +SDP A: v=0\r\n +SDP A: o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +SDP A: s=GSM Call\r\n +SDP A: c=IN IP4 23.42.23.42\r\n +SDP A: t=0 0\r\n +SDP A: m=audio 30436 RTP/AVP 112 3 111 110\r\n +SDP A: a=rtpmap:112 AMR/8000\r\n +SDP A: a=fmtp:112 octet-align=1\r\n +SDP A: a=rtpmap:3 GSM/8000\r\n +SDP A: a=rtpmap:111 GSM-HR-08/8000\r\n +SDP A: a=rtpmap:110 GSM-EFR/8000\r\n +SDP A: a=ptime:20\r\n + SDP B: v=0\r\n + SDP B: o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n + SDP B: s=GSM Call\r\n + SDP B: c=IN IP4 23.42.23.42\r\n + SDP B: t=0 0\r\n + SDP B: m=audio 30436 RTP/AVP 3 111\r\n + SDP B: a=rtpmap:3 GSM/8000\r\n + SDP B: a=rtpmap:111 GSM-HR-08/8000\r\n + SDP B: a=ptime:20\r\n +parsed SDP A: v=0\r\n +parsed SDP A: o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +parsed SDP A: s=GSM Call\r\n +parsed SDP A: c=IN IP4 23.42.23.42\r\n +parsed SDP A: t=0 0\r\n +parsed SDP A: m=audio 30436 RTP/AVP 112 3 111 110\r\n +parsed SDP A: a=rtpmap:112 AMR/8000\r\n +parsed SDP A: a=fmtp:112 octet-align=1\r\n +parsed SDP A: a=rtpmap:3 GSM/8000\r\n +parsed SDP A: a=rtpmap:111 GSM-HR-08/8000\r\n +parsed SDP A: a=rtpmap:110 GSM-EFR/8000\r\n +parsed SDP A: a=ptime:20\r\n +parsed SDP B: v=0\r\n +parsed SDP B: o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +parsed SDP B: s=GSM Call\r\n +parsed SDP B: c=IN IP4 23.42.23.42\r\n +parsed SDP B: t=0 0\r\n +parsed SDP B: m=audio 30436 RTP/AVP 3 111\r\n +parsed SDP B: a=rtpmap:3 GSM/8000\r\n +parsed SDP B: a=rtpmap:111 GSM-HR-08/8000\r\n +parsed SDP B: a=ptime:20\r\n +sdp_msg_intersection(a,b): v=0\r\n +sdp_msg_intersection(a,b): o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +sdp_msg_intersection(a,b): s=GSM Call\r\n +sdp_msg_intersection(a,b): c=IN IP4 23.42.23.42\r\n +sdp_msg_intersection(a,b): t=0 0\r\n +sdp_msg_intersection(a,b): m=audio 30436 RTP/AVP 3 111\r\n +sdp_msg_intersection(a,b): a=rtpmap:3 GSM/8000\r\n +sdp_msg_intersection(a,b): a=rtpmap:111 GSM-HR-08/8000\r\n +sdp_msg_intersection(a,b): a=ptime:20\r\n +[5] ok + +[6] all codecs removed +SDP A: v=0\r\n +SDP A: o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +SDP A: s=GSM Call\r\n +SDP A: c=IN IP4 23.42.23.42\r\n +SDP A: t=0 0\r\n +SDP A: m=audio 30436 RTP/AVP 112 3 111 110\r\n +SDP A: a=rtpmap:112 AMR/8000\r\n +SDP A: a=fmtp:112 octet-align=1\r\n +SDP A: a=rtpmap:3 GSM/8000\r\n +SDP A: a=rtpmap:111 GSM-HR-08/8000\r\n +SDP A: a=rtpmap:110 GSM-EFR/8000\r\n +SDP A: a=ptime:20\r\n + SDP B: s=empty +parsed SDP A: v=0\r\n +parsed SDP A: o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +parsed SDP A: s=GSM Call\r\n +parsed SDP A: c=IN IP4 23.42.23.42\r\n +parsed SDP A: t=0 0\r\n +parsed SDP A: m=audio 30436 RTP/AVP 112 3 111 110\r\n +parsed SDP A: a=rtpmap:112 AMR/8000\r\n +parsed SDP A: a=fmtp:112 octet-align=1\r\n +parsed SDP A: a=rtpmap:3 GSM/8000\r\n +parsed SDP A: a=rtpmap:111 GSM-HR-08/8000\r\n +parsed SDP A: a=rtpmap:110 GSM-EFR/8000\r\n +parsed SDP A: a=ptime:20\r\n +parsed SDP B: v=0\r\n +parsed SDP B: o=OsmoMSC 0 0 IN IP4 0.0.0.0\r\n +parsed SDP B: s=GSM Call\r\n +parsed SDP B: c=IN IP4 0.0.0.0\r\n +parsed SDP B: t=0 0\r\n +parsed SDP B: m=audio 0 RTP/AVP\r\n +parsed SDP B: a=ptime:20\r\n +sdp_msg_intersection(a,b): v=0\r\n +sdp_msg_intersection(a,b): o=OsmoMSC 0 0 IN IP4 23.42.23.42\r\n +sdp_msg_intersection(a,b): s=GSM Call\r\n +sdp_msg_intersection(a,b): c=IN IP4 23.42.23.42\r\n +sdp_msg_intersection(a,b): t=0 0\r\n +sdp_msg_intersection(a,b): m=audio 30436 RTP/AVP\r\n +sdp_msg_intersection(a,b): a=ptime:20\r\n +[6] ok + +[7] some real world test case +SDP A: v=0\r\n +SDP A: o=OsmoMSC 0 0 IN IP4 0.0.0.0\r\n +SDP A: s=GSM Call\r\n +SDP A: c=IN IP4 0.0.0.0\r\n +SDP A: t=0 0\r\n +SDP A: m=audio 0 RTP/AVP 112 113 110 3 111\r\n +SDP A: a=rtpmap:112 AMR/8000\r\n +SDP A: a=fmtp:112 octet-align=1;mode-set=0,1,2,3\r\n +SDP A: a=rtpmap:113 AMR-WB/8000\r\n +SDP A: a=fmtp:113 octet-align=1\r\n +SDP A: a=rtpmap:110 GSM-EFR/8000\r\n +SDP A: a=rtpmap:3 GSM/8000\r\n +SDP A: a=rtpmap:111 GSM-HR-08/8000\r\n +SDP A: a=ptime:20\r\n + SDP B: v=0\r\n + SDP B: o=OsmoMSC 0 0 IN IP4 0.0.0.0\r\n + SDP B: s=GSM Call\r\n + SDP B: c=IN IP4 0.0.0.0\r\n + SDP B: t=0 0\r\n + SDP B: m=audio 0 RTP/AVP 112 113 110 3 111\r\n + SDP B: a=rtpmap:112 AMR/8000\r\n + SDP B: a=fmtp:112 octet-align=1;mode-set=0,1,2,3\r\n + SDP B: a=rtpmap:113 AMR-WB/8000\r\n + SDP B: a=fmtp:113 octet-align=1\r\n + SDP B: a=rtpmap:110 GSM-EFR/8000\r\n + SDP B: a=rtpmap:3 GSM/8000\r\n + SDP B: a=rtpmap:111 GSM-HR-08/8000\r\n + SDP B: a=ptime:20\r\n +parsed SDP A: v=0\r\n +parsed SDP A: o=OsmoMSC 0 0 IN IP4 0.0.0.0\r\n +parsed SDP A: s=GSM Call\r\n +parsed SDP A: c=IN IP4 0.0.0.0\r\n +parsed SDP A: t=0 0\r\n +parsed SDP A: m=audio 0 RTP/AVP 112 113 110 3 111\r\n +parsed SDP A: a=rtpmap:112 AMR/8000\r\n +parsed SDP A: a=fmtp:112 octet-align=1;mode-set=0,1,2,3\r\n +parsed SDP A: a=rtpmap:113 AMR-WB/8000\r\n +parsed SDP A: a=fmtp:113 octet-align=1\r\n +parsed SDP A: a=rtpmap:110 GSM-EFR/8000\r\n +parsed SDP A: a=rtpmap:3 GSM/8000\r\n +parsed SDP A: a=rtpmap:111 GSM-HR-08/8000\r\n +parsed SDP A: a=ptime:20\r\n +parsed SDP B: v=0\r\n +parsed SDP B: o=OsmoMSC 0 0 IN IP4 0.0.0.0\r\n +parsed SDP B: s=GSM Call\r\n +parsed SDP B: c=IN IP4 0.0.0.0\r\n +parsed SDP B: t=0 0\r\n +parsed SDP B: m=audio 0 RTP/AVP 112 113 110 3 111\r\n +parsed SDP B: a=rtpmap:112 AMR/8000\r\n +parsed SDP B: a=fmtp:112 octet-align=1;mode-set=0,1,2,3\r\n +parsed SDP B: a=rtpmap:113 AMR-WB/8000\r\n +parsed SDP B: a=fmtp:113 octet-align=1\r\n +parsed SDP B: a=rtpmap:110 GSM-EFR/8000\r\n +parsed SDP B: a=rtpmap:3 GSM/8000\r\n +parsed SDP B: a=rtpmap:111 GSM-HR-08/8000\r\n +parsed SDP B: a=ptime:20\r\n +sdp_msg_intersection(a,b): v=0\r\n +sdp_msg_intersection(a,b): o=OsmoMSC 0 0 IN IP4 0.0.0.0\r\n +sdp_msg_intersection(a,b): s=GSM Call\r\n +sdp_msg_intersection(a,b): c=IN IP4 0.0.0.0\r\n +sdp_msg_intersection(a,b): t=0 0\r\n +sdp_msg_intersection(a,b): m=audio 0 RTP/AVP 112 113 110 3 111\r\n +sdp_msg_intersection(a,b): a=rtpmap:112 AMR/8000\r\n +sdp_msg_intersection(a,b): a=fmtp:112 octet-align=1;mode-set=0,1,2,3\r\n +sdp_msg_intersection(a,b): a=rtpmap:113 AMR-WB/8000\r\n +sdp_msg_intersection(a,b): a=fmtp:113 octet-align=1\r\n +sdp_msg_intersection(a,b): a=rtpmap:110 GSM-EFR/8000\r\n +sdp_msg_intersection(a,b): a=rtpmap:3 GSM/8000\r\n +sdp_msg_intersection(a,b): a=rtpmap:111 GSM-HR-08/8000\r\n +sdp_msg_intersection(a,b): a=ptime:20\r\n +[7] ok + + +test_select + +[0] +SDP: AMR:octet-align=1,GSM,GSM-HR-08,GSM-EFR +Select: AMR:octet-align=1 +SDP: AMR:octet-align=1,GSM,GSM-HR-08,GSM-EFR +[0] ok + +[1] +SDP: AMR:octet-align=1,GSM,GSM-HR-08,GSM-EFR +Select: GSM +SDP: GSM,AMR:octet-align=1,GSM-HR-08,GSM-EFR +[1] ok + +[2] +SDP: AMR:octet-align=1,GSM,GSM-HR-08,GSM-EFR +Select: GSM-HR-08 +SDP: GSM-HR-08,AMR:octet-align=1,GSM,GSM-EFR +[2] ok + +[3] +SDP: AMR:octet-align=1,GSM,GSM-HR-08,GSM-EFR +Select: GSM-EFR +SDP: GSM-EFR,AMR:octet-align=1,GSM,GSM-HR-08 +[3] ok diff --git a/tests/testsuite.at b/tests/testsuite.at index c0788b9ab..946d0db11 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -107,3 +107,10 @@ cat $abs_srcdir/msc_vlr/msc_vlr_test_ss.ok > expout cat $abs_srcdir/msc_vlr/msc_vlr_test_ss.err > experr AT_CHECK([$abs_top_builddir/tests/msc_vlr/msc_vlr_test_ss], [], [expout], [experr]) AT_CLEANUP + +AT_SETUP([sdp_msg_test]) +AT_KEYWORDS([sdp_msg_test]) +cat $abs_srcdir/sdp_msg/sdp_msg_test.ok > expout +cat $abs_srcdir/sdp_msg/sdp_msg_test.err > experr +AT_CHECK([$abs_top_builddir/tests/sdp_msg/sdp_msg_test], [], [expout], [experr]) +AT_CLEANUP