diff --git a/include/Makefile.am b/include/Makefile.am index 45e60b575..ec024cce9 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -9,6 +9,7 @@ nobase_include_HEADERS = \ osmocom/mgcp_client/mgcp_client_fsm.h \ osmocom/mgcp_client/mgcp_client_pool.h \ osmocom/sdp/fmtp.h \ + osmocom/sdp/sdp_codec.h \ osmocom/sdp/sdp_strings.h \ $(NULL) diff --git a/include/osmocom/sdp/Makefile.am b/include/osmocom/sdp/Makefile.am index e69de29bb..d52794222 100644 --- a/include/osmocom/sdp/Makefile.am +++ b/include/osmocom/sdp/Makefile.am @@ -0,0 +1,3 @@ +noinst_HEADERS = \ + sdp_internal.h \ + $(NULL) diff --git a/include/osmocom/sdp/sdp_codec.h b/include/osmocom/sdp/sdp_codec.h new file mode 100644 index 000000000..2a0cf8a98 --- /dev/null +++ b/include/osmocom/sdp/sdp_codec.h @@ -0,0 +1,123 @@ +/* Public API for codec management in SDP messages. */ +/* + * (C) 2024 by sysmocom - s.f.m.c. GmbH + * All Rights Reserved. + * + * Author: Neels Janosch Hofmeyr + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#pragma once + +#include +#include + +/* RFC-8866 5.14 and 6.6. + * + * Represent the items describing an SDP codec entry, as in: + * + * m=audio 1234 RTP/AVP + * a=rtpmap: / + * a=fmtp: + * + * For example: + * + * m=audio 1234 RTP/AVP 98 + * a=rtpmap:98 AMR/8000 + * a=fmtp:98 octet-align=1;mode-set=0,2,4,7 + */ +struct osmo_sdp_codec { + /* Payload type number ("payload-type"), like 3 for GSM-FR. Limited to 0..127. */ + int8_t payload_type; + + /* Encoding name like "GSM", "AMR", "GSM-EFR". + * + * RFC-8866 defines no length limit on the encoding name. This API leaves it up to the caller to provide + * sufficient space, via the SDP_SIZES_* definitions. + * + * encoding-name = token + * token = 1*(token-char) + * token-char = ALPHA / DIGIT + * / "!" / "#" / "$" / "%" / "&" + * / "'" ; (single quote) + * / "*" / "+" / "-" / "." / "^" / "_" + * / "`" ; (Grave accent) + */ + char *encoding_name; + + /* Samplerate ("clock-rate"), usually 8000 for GSM. */ + unsigned int rate; + + /* Codec parameters as supplied in SDP line 'a=fmtp: '. This holds only + * the 'format-specific-params' bytestring. For example, for SDP line 'a=fmtp:123 param1=val1;param2=val2', this + * holds only the , "param1=val1;param2=val2" part. For the buffer size, see fmtp_size. */ + char *fmtp; + + /* For future extension, always set to false. */ + bool v2; +}; + +struct osmo_sdp_codec *osmo_sdp_codec_alloc(void *ctx); + +int osmo_sdp_codec_set(struct osmo_sdp_codec *c, + int8_t payload_type, const char *encoding_name, unsigned int rate, const char *fmtp); +int osmo_sdp_codec_set_encoding_name(struct osmo_sdp_codec *c, const char *encoding_name); +int osmo_sdp_codec_set_fmtp(struct osmo_sdp_codec *c, const char *fmtp); + +bool osmo_sdp_codec_is_set(const struct osmo_sdp_codec *a); + +int osmo_sdp_codec_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec *codec); +char *osmo_sdp_codec_to_str_c(void *ctx, const struct osmo_sdp_codec *codec); + +int osmo_sdp_codec_from_str(struct osmo_sdp_codec *dst, const char *str, int str_len); + +enum osmo_sdp_cmp { + OSMO_SDP_CMP_IGNORE = 0, + OSMO_SDP_CMP_EQUIVALENT, + OSMO_SDP_CMP_EXACT, +}; + +/*! Indicate how to match SDP codecs to various osmo_sdp_*() functions. + * Callers may define own flags, or use predefined instances: + * osmo_sdp_codec_cmp_exact, osmo_sdp_codec_cmp_equivalent, ... + * + * For example, to trigger some action if any item has changed, set all items to true / OSMO_SDP_CMP_EXACT (see + * osmo_sdp_codec_cmp_exact). + * To find codecs that are the same between two SDP sessions, set payload_type=false and fmtp=OSMO_SDP_CMP_EQUIVALENT + * (see osmo_sdp_codec_cmp_equivalent). + * To just list all contained "AMR" codecs, set only encoding_name=true (see osmo_sdp_codec_cmp_name). + */ +struct osmo_sdp_codec_cmp_flags { + /*! true = compare payload type numbers 1:1; false = ignore. */ + bool payload_type; + /*! true = compare encoding_name 1:1; false = ignore. */ + bool encoding_name; + /*! true = compare rate 1:1; false = ignore. */ + bool rate; + /*! OSMO_SDP_CMP_IGNORE = ignore fmtp; + * OSMO_SDP_CMP_EQUIVALENT = use osmo_sdp_fmtp_amr_match() for AMR, otherwise compare 1:1; + * OSMO_SDP_CMP_EXACT = compare 1:1. + */ + enum osmo_sdp_cmp fmtp; +}; + +extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_exact; +extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_equivalent; +extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_name; + +int osmo_sdp_codec_cmp(const struct osmo_sdp_codec *a, const struct osmo_sdp_codec *b, + const struct osmo_sdp_codec_cmp_flags *cmp); diff --git a/include/osmocom/sdp/sdp_internal.h b/include/osmocom/sdp/sdp_internal.h new file mode 100644 index 000000000..2a87b7b8a --- /dev/null +++ b/include/osmocom/sdp/sdp_internal.h @@ -0,0 +1,11 @@ +/* Internal header for non-public API shared across .c files */ +#pragma once + +struct token { + const char *start; + const char *end; +}; + +/* Copy a string from [start,end[, return as talloc allocated under ctx in *dst. + * If *dst is non-NULL, talloc_free(*dst) first. */ +void token_copy(void *ctx, char **dst, const struct token *t); diff --git a/src/libosmo-sdp/Makefile.am b/src/libosmo-sdp/Makefile.am index 126bfe5d0..6e5de1b84 100644 --- a/src/libosmo-sdp/Makefile.am +++ b/src/libosmo-sdp/Makefile.am @@ -19,6 +19,8 @@ lib_LTLIBRARIES = \ $(NULL) libosmo_sdp_la_SOURCES = \ + sdp_codec.c \ + sdp_internal.c \ fmtp.c \ $(NULL) diff --git a/src/libosmo-sdp/sdp_codec.c b/src/libosmo-sdp/sdp_codec.c new file mode 100644 index 000000000..c8bba6e2a --- /dev/null +++ b/src/libosmo-sdp/sdp_codec.c @@ -0,0 +1,251 @@ +/* Codec management in SDP messages. */ +/* + * (C) 2024 by sysmocom - s.f.m.c. GmbH + * All Rights Reserved. + * + * Author: Neels Janosch Hofmeyr + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include + +#include + +#include +#include +#include + +struct osmo_sdp_codec *osmo_sdp_codec_alloc(void *ctx) +{ + return talloc_zero(ctx, struct osmo_sdp_codec); +} + +int osmo_sdp_codec_set(struct osmo_sdp_codec *c, + int8_t payload_type, const char *encoding_name, unsigned int rate, const char *fmtp) +{ + c->rate = rate; + osmo_sdp_codec_set_encoding_name(c, encoding_name); + osmo_sdp_codec_set_fmtp(c, fmtp); + c->payload_type = payload_type; + return 0; +} + +int osmo_sdp_codec_set_encoding_name(struct osmo_sdp_codec *c, const char *encoding_name) +{ + osmo_talloc_replace_string(c, &c->encoding_name, encoding_name); + return 0; +} + +int osmo_sdp_codec_set_fmtp(struct osmo_sdp_codec *c, const char *fmtp) +{ + osmo_talloc_replace_string(c, &c->fmtp, fmtp); + return 0; +} + +bool osmo_sdp_codec_is_set(const struct osmo_sdp_codec *a) +{ + return a && a->encoding_name && a->encoding_name[0]; +} + +int osmo_sdp_codec_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec *codec) +{ + struct osmo_strbuf sb = { .buf = buf, .len = buflen }; + if (!codec) { + OSMO_STRBUF_PRINTF(sb, "NULL"); + return sb.chars_needed; + } + if (codec->encoding_name && codec->encoding_name[0]) + OSMO_STRBUF_PRINTF(sb, "%s", codec->encoding_name); + if (codec->rate != 8000) + OSMO_STRBUF_PRINTF(sb, "/%u", codec->rate); + if (codec->fmtp && codec->fmtp[0]) + OSMO_STRBUF_PRINTF(sb, ":%s", codec->fmtp); + OSMO_STRBUF_PRINTF(sb, "#%d", codec->payload_type); + return sb.chars_needed; +} + +char *osmo_sdp_codec_to_str_c(void *ctx, const struct osmo_sdp_codec *codec) +{ + OSMO_NAME_C_IMPL(ctx, 32, "osmo_sdp_codec_to_str_c-ERROR", osmo_sdp_codec_to_str_buf, codec) +} + +/*! Parse a codec string as from osmo_sdp_codec_to_str_buf() back to an osmo_sdp_codec struct. + * Write the parsed result to *dst, using ctx as talloc parent. + * The input string is like [:][#] + * for example: + * "FOO:my-fmtp=1;my-other-fmtp=2#42" + * Note that ';' are separators only within the fmtp string. This function does not separate those. In above example, + * the fmtp string part is "my-fmtp=val;my-other-fmtp=val2" and ends up in dst->ftmp as-is. + * Parse at most str_len characters, or the entire string when str_len < 0 or str_len > strlen(str). + * Return 0 on success, negative on failure. */ +int osmo_sdp_codec_from_str(struct osmo_sdp_codec *dst, const char *str, int str_len) +{ + const char *pos = str; + const char *str_end = str + (str_len >= 0 ? str_len : strlen(str)); + const char *p2; + + struct token token_encoding_name = {}; + struct token token_rate = {}; + struct token token_fmtp = {}; + struct token token_payload_type = {}; + + struct token *new_t = NULL; + /* start with the encoding name */ + struct token *t = &token_encoding_name; + t->start = pos; + + for (; pos < str_end; pos++) { + new_t = NULL; + switch (*pos) { + case '/': + new_t = &token_rate; + break; + case ':': + new_t = &token_fmtp; + break; + case '#': + /* count this '#' only if there is no other one following. It might be part of a fmtp. */ + for (p2 = pos + 1; p2 < str_end; p2++) + if (*p2 == '#') + break; + if (p2 < str_end && *p2 == '#') + break; + /* This is the last '#' in the string. Count it only when a digit follows. */ + if (!isdigit(pos[1])) + break; + new_t = &token_payload_type; + break; + default: + break; + } + if (!new_t) + continue; + /* If we already have a token for a start character, don't start it again. These may be part of a fmtp + * string. */ + if (new_t == t) + continue; + t->end = pos; + t = new_t; + t->start = pos + 1; + } + t->end = pos; + + token_copy(dst, &dst->encoding_name, &token_encoding_name); + if (token_rate.start) + dst->rate = atoi(token_rate.start); + else + dst->rate = 8000; + token_copy(dst, &dst->fmtp, &token_fmtp); + if (token_payload_type.start) + dst->payload_type = atoi(token_payload_type.start); + return 0; +} + +/* Compare both payload type number and fmtp string 1:1 */ +const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_exact = { + .payload_type = true, + .encoding_name = true, + .rate = true, + .fmtp = OSMO_SDP_CMP_EXACT, +}; + +/* Ignore payload type number; compare fmtp string by meaning when possible, else 1:1 */ +const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_equivalent = { + .payload_type = false, + .encoding_name = true, + .rate = true, + .fmtp = OSMO_SDP_CMP_EQUIVALENT, +}; + +/* Compare only encoding name */ +const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_name = { + .payload_type = false, + .encoding_name = true, + .rate = false, + .fmtp = OSMO_SDP_CMP_IGNORE, +}; + +extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_equivalent; +static inline int strcmp_safe(const char *a, const char *b) +{ + return strcmp(a ? : "", b ? : ""); +} + +/*! Compare encoding name, rate and fmtp, returning cmp result: -1 if a < b, 0 if a == b, 1 if a > b. + * Compare as defined in 'cmp': + * If cmpf->payload_type is false, ignore payload_type numbers. + * If cmpf->rate is false, ignore rate. + * If cmpf->fmtp is OSMO_SDP_CMP_IGNORE, ignore fmtp strings. + * If cmpf->fmtp is OSMO_SDP_CMP_EXACT, use strcmp() to match fmtp 1:1. + * If cmpf->fmtp is OSMO_SDP_CMP_EQUIVALENT, use specific fmtp knowledge to match equivalent entries; + * - AMR fmtp matching is done by osmo_sdp_fmtp_amr_match(). + * - for all others, still compare fmtp 1:1. + */ +int osmo_sdp_codec_cmp(const struct osmo_sdp_codec *a, const struct osmo_sdp_codec *b, + const struct osmo_sdp_codec_cmp_flags *cmpf) +{ + int cmp; + if (a == b) + return 0; + if (!a) + return -1; + if (!b) + return 1; + + if (!cmpf) + cmpf = &osmo_sdp_codec_cmp_exact; + + if (cmpf->encoding_name) { + cmp = strcmp_safe(a->encoding_name, b->encoding_name); + if (cmp) + return cmp; + } + + if (cmpf->rate) { + cmp = OSMO_CMP(a->rate, b->rate); + if (cmp) + return cmp; + } + + switch (cmpf->fmtp) { + default: + case OSMO_SDP_CMP_EXACT: + cmp = strcmp_safe(a->fmtp, b->fmtp); + break; + + case OSMO_SDP_CMP_EQUIVALENT: + /* In case of AMR, allow logical matching; we only need to do that if the strings differ. */ + cmp = strcmp_safe(a->fmtp, b->fmtp); + if (cmp + && !strcmp_safe("AMR", a->encoding_name) + && osmo_sdp_fmtp_amr_match(a->fmtp, b->fmtp)) + cmp = 0; + break; + + case OSMO_SDP_CMP_IGNORE: + cmp = 0; + break; + } + if (cmp) + return cmp; + + if (cmpf->payload_type) + cmp = OSMO_CMP(a->payload_type, b->payload_type); + + return cmp; +} diff --git a/src/libosmo-sdp/sdp_internal.c b/src/libosmo-sdp/sdp_internal.c new file mode 100644 index 000000000..12c55e34b --- /dev/null +++ b/src/libosmo-sdp/sdp_internal.c @@ -0,0 +1,47 @@ +/* + * (C) 2024 by sysmocom - s.f.m.c. GmbH + * All Rights Reserved. + * + * Author: Neels Janosch Hofmeyr + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include + +#include +#include + +#include + +/* Copy a string from t->start to t->end, return as talloc allocated under ctx in *dst. + * If *dst is non-NULL, talloc_free(*dst) first. */ +void token_copy(void *ctx, char **dst, const struct token *t) +{ + size_t len; + if (*dst) + talloc_free(*dst); + if (!t->start || !(t->end > t->start)) { + *dst = NULL; + return; + } + + len = t->end - t->start; + *dst = talloc_size(ctx, len + 1); + osmo_strlcpy(*dst, t->start, len + 1); + talloc_set_name_const(*dst, *dst); +} diff --git a/tests/sdp/Makefile.am b/tests/sdp/Makefile.am index 7a55fd1e1..6ca2fac31 100644 --- a/tests/sdp/Makefile.am +++ b/tests/sdp/Makefile.am @@ -21,10 +21,13 @@ AM_LDFLAGS = \ EXTRA_DIST = \ sdp_fmtp_test.ok \ sdp_fmtp_test.err \ + sdp_codec_test.ok \ + sdp_codec_test.err \ $(NULL) check_PROGRAMS = \ sdp_fmtp_test \ + sdp_codec_test \ $(NULL) sdp_fmtp_test_SOURCES = \ @@ -35,5 +38,15 @@ sdp_fmtp_test_LDADD = \ $(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \ $(NULL) +sdp_codec_test_SOURCES = \ + sdp_codec_test.c \ + $(NULL) + +sdp_codec_test_LDADD = \ + $(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \ + $(LIBOSMOCORE_LIBS) \ + $(NULL) + update_exp: $(builddir)/sdp_fmtp_test >$(srcdir)/sdp_fmtp_test.ok 2>$(srcdir)/sdp_fmtp_test.err + $(builddir)/sdp_codec_test >$(srcdir)/sdp_codec_test.ok 2>$(srcdir)/sdp_codec_test.err diff --git a/tests/sdp/sdp_codec_test.c b/tests/sdp/sdp_codec_test.c new file mode 100644 index 000000000..46e9372a9 --- /dev/null +++ b/tests/sdp/sdp_codec_test.c @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +void *test_ctx = NULL; + +static void report_callback(const void *ptr, int depth, int max_depth, int is_ref, void *priv) +{ + const char *name = talloc_get_name(ptr); + printf(" |%*s%3zu %s\n", depth, "", talloc_total_blocks(ptr), name); +} + +/* Print a talloc report that is reproducible for test output verification. It contains no pointer addresses. */ +#define report(CTX) _report(CTX, #CTX) +static void _report(void *ctx, const char *label) +{ + fflush(stdout); + fflush(stderr); + printf("%s\n", label); + talloc_report_depth_cb(ctx, 0, 100, report_callback, NULL); + fflush(stdout); +} + +struct codec_test { + struct osmo_sdp_codec set; + int expect_rc; + const char *expect_str; + bool expect_is_set; +}; + +struct codec_test codec_tests[] = { + { + .set = { 23, "encoding-name", 8000, NULL }, + .expect_str = "encoding-name#23", + .expect_is_set = true, + }, + { + .set = { 112, "AMR", 8000, "octet-align=1;mode-set=0,2,4" }, + .expect_str = "AMR:octet-align=1;mode-set=0,2,4#112", + .expect_is_set = true, + }, + { + .set = { 96, "AMR", 8000, "mode-set=0,2,4;octet-align=1" }, + .expect_str = "AMR:mode-set=0,2,4;octet-align=1#96", + .expect_is_set = true, + }, + { + .set = { 114, "AMR", 8000, "mode-set=0,2,4" }, + .expect_str = "AMR:mode-set=0,2,4#114", + .expect_is_set = true, + }, + { + .set = { 97, "AMR", 8000, "mode-set=0,2,4;octet-align=0" }, + .expect_str = "AMR:mode-set=0,2,4;octet-align=0#97", + .expect_is_set = true, + }, + { + .set = { 98, "AMR", 8000, "octet-align=1" }, + .expect_str = "AMR:octet-align=1#98", + .expect_is_set = true, + }, + { + .set = { 96, "AMR-WB", 16000 }, + .expect_str = "AMR-WB/16000#96", + .expect_is_set = true, + }, + { + .set = { 3, "GSM", 8000 }, + .expect_str = "GSM#3", + .expect_is_set = true, + }, + { + .set = { }, + .expect_str = "/0#0", + .expect_is_set = false, + }, + { + .set = { 112, NULL, 8000, "octet-align=1" }, + .expect_str = ":octet-align=1#112", + .expect_is_set = false, + }, + { + .set = { 112, "", 8000, "octet-align=1" }, + .expect_str = ":octet-align=1#112", + .expect_is_set = false, + }, +}; + +void test_codec(void) +{ + void *ctx = talloc_named_const(test_ctx, 0, __func__); + + struct codec_test *t; + struct codec_test *t2; + + printf("\n\n--- %s()\n", __func__); + printf("- osmo_sdp_codec_set():\n"); + for (t = codec_tests; (t - codec_tests) < ARRAY_SIZE(codec_tests); t++) { + struct osmo_sdp_codec *codec = osmo_sdp_codec_alloc(ctx); + char *str; + bool is_set; + + osmo_sdp_codec_set(codec, t->set.payload_type, t->set.encoding_name, t->set.rate, t->set.fmtp); + + str = osmo_sdp_codec_to_str_c(ctx, codec); + printf("osmo_sdp_codec_set [%d] '%s'\n", (int)(t - codec_tests), str); + if (strcmp(str, t->expect_str)) + printf(" *** ERROR: expected '%s'\n", t->expect_str); + + if (!osmo_sdp_codec_cmp(codec, &t->set, &osmo_sdp_codec_cmp_exact)) + printf(" osmo_sdp_codec_cmp() ok\n"); + else + printf(" osmo_sdp_codec_cmp() *** ERROR: mismatches original values\n"); + + is_set = osmo_sdp_codec_is_set(codec); + printf(" osmo_sdp_codec_is_set() = %s\n", is_set ? "true" : "false"); + if (is_set != t->expect_is_set) + printf(" *** ERROR: expected is_set = %s\n", t->expect_is_set ? "true" : "false"); + + if (is_set != osmo_sdp_codec_is_set(&t->set)) + printf(" *** ERROR: is_set(copy) != is_set(orig)\n"); + + talloc_free(str); + talloc_free(codec); + if (talloc_total_blocks(ctx) != 1) + printf(" *** ERROR: ctx has %zu items, should be 1\n", talloc_total_blocks(ctx)); + } + + printf("\n- osmo_sdp_codec_cmp(equivalent):\n"); + for (t = codec_tests; (t - codec_tests) < ARRAY_SIZE(codec_tests); t++) { + for (t2 = codec_tests; (t2 - codec_tests) < ARRAY_SIZE(codec_tests); t2++) { + int cmp = osmo_sdp_codec_cmp(&t->set, &t2->set, &osmo_sdp_codec_cmp_equivalent); + int reverse_cmp = osmo_sdp_codec_cmp(&t2->set, &t->set, &osmo_sdp_codec_cmp_equivalent); + printf(" %s %s %s %s %s\n", + osmo_sdp_codec_to_str_c(ctx, &t->set), + (cmp == 0) ? "==" + : ((cmp < 0) ? "<" : ">"), + osmo_sdp_codec_to_str_c(ctx, &t2->set), + (reverse_cmp == 0) ? "==" + : ((reverse_cmp < 0) ? "<" : ">"), + osmo_sdp_codec_to_str_c(ctx, &t->set)); + + if (reverse_cmp != -cmp) + printf(" *** ERROR: osmo_sdp_codec_cmp(reverse args) == %d, expected %d\n", + reverse_cmp, -cmp); + + talloc_free_children(ctx); + } + } + + printf("\n- osmo_sdp_codec_from_str():\n"); + for (t = codec_tests; (t - codec_tests) < ARRAY_SIZE(codec_tests); t++) { + struct osmo_sdp_codec *codec = osmo_sdp_codec_alloc(ctx); + int rc = osmo_sdp_codec_from_str(codec, t->expect_str, -1); + printf(" osmo_sdp_codec_from_str('%s') rc=%d", + t->expect_str, rc); + if (!rc) { + printf(" res=%s", osmo_sdp_codec_to_str_c(ctx, codec)); + rc = osmo_sdp_codec_cmp(codec, &t->set, &osmo_sdp_codec_cmp_exact); + if (rc) + printf(" *** ERROR: osmo_sdp_codec_cmp(res,orig) = %d", rc); + } + printf("\n"); + talloc_free_children(ctx); + } + + talloc_free(ctx); +} + + +struct my_obj { + struct osmo_sdp_codec *codec; +}; + +struct my_obj *my_obj_alloc(void *ctx) +{ + struct my_obj *o = talloc_zero(ctx, struct my_obj); + return o; +} + +void test_obj_members(void) +{ + void *ctx = talloc_named_const(test_ctx, 0, __func__); + void *print_ctx = talloc_named_const(test_ctx, 0, "print"); + + struct my_obj *o; + + printf("\n\n--- %s()\n", __func__); + o = my_obj_alloc(ctx); + + o->codec = osmo_sdp_codec_alloc(o); + osmo_sdp_codec_set(o->codec, 96, "AMR", 8000, "octet-align=1"); + + printf("o->codec = %s\n", osmo_sdp_codec_to_str_c(print_ctx, o->codec)); + + report(ctx); + printf("talloc_free(o)\n"); + talloc_free(o); + report(ctx); + talloc_free(ctx); + talloc_free(print_ctx); +} + +typedef void (*test_func_t)(void); +test_func_t test_func[] = { + test_codec, + test_obj_members, +}; + +int main(void) +{ + int i; + test_ctx = talloc_named_const(NULL, 0, "sdp_codec_test"); + + for (i = 0; i < ARRAY_SIZE(test_func); i++) { + + test_func[i](); + + if (talloc_total_blocks(test_ctx) != 1) { + talloc_report_full(test_ctx, stderr); + printf("ERROR after test %d: memory leak\n", i); + return -1; + } + } + + talloc_free(test_ctx); + return 0; +} diff --git a/tests/sdp/sdp_codec_test.err b/tests/sdp/sdp_codec_test.err new file mode 100644 index 000000000..e69de29bb diff --git a/tests/sdp/sdp_codec_test.ok b/tests/sdp/sdp_codec_test.ok new file mode 100644 index 000000000..cec73863f --- /dev/null +++ b/tests/sdp/sdp_codec_test.ok @@ -0,0 +1,186 @@ + + +--- test_codec() +- osmo_sdp_codec_set(): +osmo_sdp_codec_set [0] 'encoding-name#23' + osmo_sdp_codec_cmp() ok + osmo_sdp_codec_is_set() = true +osmo_sdp_codec_set [1] 'AMR:octet-align=1;mode-set=0,2,4#112' + osmo_sdp_codec_cmp() ok + osmo_sdp_codec_is_set() = true +osmo_sdp_codec_set [2] 'AMR:mode-set=0,2,4;octet-align=1#96' + osmo_sdp_codec_cmp() ok + osmo_sdp_codec_is_set() = true +osmo_sdp_codec_set [3] 'AMR:mode-set=0,2,4#114' + osmo_sdp_codec_cmp() ok + osmo_sdp_codec_is_set() = true +osmo_sdp_codec_set [4] 'AMR:mode-set=0,2,4;octet-align=0#97' + osmo_sdp_codec_cmp() ok + osmo_sdp_codec_is_set() = true +osmo_sdp_codec_set [5] 'AMR:octet-align=1#98' + osmo_sdp_codec_cmp() ok + osmo_sdp_codec_is_set() = true +osmo_sdp_codec_set [6] 'AMR-WB/16000#96' + osmo_sdp_codec_cmp() ok + osmo_sdp_codec_is_set() = true +osmo_sdp_codec_set [7] 'GSM#3' + osmo_sdp_codec_cmp() ok + osmo_sdp_codec_is_set() = true +osmo_sdp_codec_set [8] '/0#0' + osmo_sdp_codec_cmp() ok + osmo_sdp_codec_is_set() = false +osmo_sdp_codec_set [9] ':octet-align=1#112' + osmo_sdp_codec_cmp() ok + osmo_sdp_codec_is_set() = false +osmo_sdp_codec_set [10] ':octet-align=1#112' + osmo_sdp_codec_cmp() ok + osmo_sdp_codec_is_set() = false + +- osmo_sdp_codec_cmp(equivalent): + encoding-name#23 == encoding-name#23 == encoding-name#23 + encoding-name#23 > AMR:octet-align=1;mode-set=0,2,4#112 < encoding-name#23 + encoding-name#23 > AMR:mode-set=0,2,4;octet-align=1#96 < encoding-name#23 + encoding-name#23 > AMR:mode-set=0,2,4#114 < encoding-name#23 + encoding-name#23 > AMR:mode-set=0,2,4;octet-align=0#97 < encoding-name#23 + encoding-name#23 > AMR:octet-align=1#98 < encoding-name#23 + encoding-name#23 > AMR-WB/16000#96 < encoding-name#23 + encoding-name#23 > GSM#3 < encoding-name#23 + encoding-name#23 > /0#0 < encoding-name#23 + encoding-name#23 > :octet-align=1#112 < encoding-name#23 + encoding-name#23 > :octet-align=1#112 < encoding-name#23 + AMR:octet-align=1;mode-set=0,2,4#112 < encoding-name#23 > AMR:octet-align=1;mode-set=0,2,4#112 + AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1;mode-set=0,2,4#112 + AMR:octet-align=1;mode-set=0,2,4#112 == AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1;mode-set=0,2,4#112 + AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4#114 < AMR:octet-align=1;mode-set=0,2,4#112 + AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1;mode-set=0,2,4#112 + AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1#98 == AMR:octet-align=1;mode-set=0,2,4#112 + AMR:octet-align=1;mode-set=0,2,4#112 < AMR-WB/16000#96 > AMR:octet-align=1;mode-set=0,2,4#112 + AMR:octet-align=1;mode-set=0,2,4#112 < GSM#3 > AMR:octet-align=1;mode-set=0,2,4#112 + AMR:octet-align=1;mode-set=0,2,4#112 > /0#0 < AMR:octet-align=1;mode-set=0,2,4#112 + AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112 + AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112 + AMR:mode-set=0,2,4;octet-align=1#96 < encoding-name#23 > AMR:mode-set=0,2,4;octet-align=1#96 + AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1;mode-set=0,2,4#112 == AMR:mode-set=0,2,4;octet-align=1#96 + AMR:mode-set=0,2,4;octet-align=1#96 == AMR:mode-set=0,2,4;octet-align=1#96 == AMR:mode-set=0,2,4;octet-align=1#96 + AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4#114 < AMR:mode-set=0,2,4;octet-align=1#96 + AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR:mode-set=0,2,4;octet-align=1#96 + AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1#98 == AMR:mode-set=0,2,4;octet-align=1#96 + AMR:mode-set=0,2,4;octet-align=1#96 < AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=1#96 + AMR:mode-set=0,2,4;octet-align=1#96 < GSM#3 > AMR:mode-set=0,2,4;octet-align=1#96 + AMR:mode-set=0,2,4;octet-align=1#96 > /0#0 < AMR:mode-set=0,2,4;octet-align=1#96 + AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96 + AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96 + AMR:mode-set=0,2,4#114 < encoding-name#23 > AMR:mode-set=0,2,4#114 + AMR:mode-set=0,2,4#114 < AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4#114 + AMR:mode-set=0,2,4#114 < AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4#114 + AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4#114 + AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4#114 + AMR:mode-set=0,2,4#114 < AMR:octet-align=1#98 > AMR:mode-set=0,2,4#114 + AMR:mode-set=0,2,4#114 < AMR-WB/16000#96 > AMR:mode-set=0,2,4#114 + AMR:mode-set=0,2,4#114 < GSM#3 > AMR:mode-set=0,2,4#114 + AMR:mode-set=0,2,4#114 > /0#0 < AMR:mode-set=0,2,4#114 + AMR:mode-set=0,2,4#114 > :octet-align=1#112 < AMR:mode-set=0,2,4#114 + AMR:mode-set=0,2,4#114 > :octet-align=1#112 < AMR:mode-set=0,2,4#114 + AMR:mode-set=0,2,4;octet-align=0#97 < encoding-name#23 > AMR:mode-set=0,2,4;octet-align=0#97 + AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4;octet-align=0#97 + AMR:mode-set=0,2,4;octet-align=0#97 < AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4;octet-align=0#97 + AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4;octet-align=0#97 + AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4;octet-align=0#97 + AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1#98 > AMR:mode-set=0,2,4;octet-align=0#97 + AMR:mode-set=0,2,4;octet-align=0#97 < AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=0#97 + AMR:mode-set=0,2,4;octet-align=0#97 < GSM#3 > AMR:mode-set=0,2,4;octet-align=0#97 + AMR:mode-set=0,2,4;octet-align=0#97 > /0#0 < AMR:mode-set=0,2,4;octet-align=0#97 + AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97 + AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97 + AMR:octet-align=1#98 < encoding-name#23 > AMR:octet-align=1#98 + AMR:octet-align=1#98 == AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1#98 + AMR:octet-align=1#98 == AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1#98 + AMR:octet-align=1#98 > AMR:mode-set=0,2,4#114 < AMR:octet-align=1#98 + AMR:octet-align=1#98 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1#98 + AMR:octet-align=1#98 == AMR:octet-align=1#98 == AMR:octet-align=1#98 + AMR:octet-align=1#98 < AMR-WB/16000#96 > AMR:octet-align=1#98 + AMR:octet-align=1#98 < GSM#3 > AMR:octet-align=1#98 + AMR:octet-align=1#98 > /0#0 < AMR:octet-align=1#98 + AMR:octet-align=1#98 > :octet-align=1#112 < AMR:octet-align=1#98 + AMR:octet-align=1#98 > :octet-align=1#112 < AMR:octet-align=1#98 + AMR-WB/16000#96 < encoding-name#23 > AMR-WB/16000#96 + AMR-WB/16000#96 > AMR:octet-align=1;mode-set=0,2,4#112 < AMR-WB/16000#96 + AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=1#96 < AMR-WB/16000#96 + AMR-WB/16000#96 > AMR:mode-set=0,2,4#114 < AMR-WB/16000#96 + AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR-WB/16000#96 + AMR-WB/16000#96 > AMR:octet-align=1#98 < AMR-WB/16000#96 + AMR-WB/16000#96 == AMR-WB/16000#96 == AMR-WB/16000#96 + AMR-WB/16000#96 < GSM#3 > AMR-WB/16000#96 + AMR-WB/16000#96 > /0#0 < AMR-WB/16000#96 + AMR-WB/16000#96 > :octet-align=1#112 < AMR-WB/16000#96 + AMR-WB/16000#96 > :octet-align=1#112 < AMR-WB/16000#96 + GSM#3 < encoding-name#23 > GSM#3 + GSM#3 > AMR:octet-align=1;mode-set=0,2,4#112 < GSM#3 + GSM#3 > AMR:mode-set=0,2,4;octet-align=1#96 < GSM#3 + GSM#3 > AMR:mode-set=0,2,4#114 < GSM#3 + GSM#3 > AMR:mode-set=0,2,4;octet-align=0#97 < GSM#3 + GSM#3 > AMR:octet-align=1#98 < GSM#3 + GSM#3 > AMR-WB/16000#96 < GSM#3 + GSM#3 == GSM#3 == GSM#3 + GSM#3 > /0#0 < GSM#3 + GSM#3 > :octet-align=1#112 < GSM#3 + GSM#3 > :octet-align=1#112 < GSM#3 + /0#0 < encoding-name#23 > /0#0 + /0#0 < AMR:octet-align=1;mode-set=0,2,4#112 > /0#0 + /0#0 < AMR:mode-set=0,2,4;octet-align=1#96 > /0#0 + /0#0 < AMR:mode-set=0,2,4#114 > /0#0 + /0#0 < AMR:mode-set=0,2,4;octet-align=0#97 > /0#0 + /0#0 < AMR:octet-align=1#98 > /0#0 + /0#0 < AMR-WB/16000#96 > /0#0 + /0#0 < GSM#3 > /0#0 + /0#0 == /0#0 == /0#0 + /0#0 < :octet-align=1#112 > /0#0 + /0#0 < :octet-align=1#112 > /0#0 + :octet-align=1#112 < encoding-name#23 > :octet-align=1#112 + :octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112 + :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112 + :octet-align=1#112 < AMR:mode-set=0,2,4#114 > :octet-align=1#112 + :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112 + :octet-align=1#112 < AMR:octet-align=1#98 > :octet-align=1#112 + :octet-align=1#112 < AMR-WB/16000#96 > :octet-align=1#112 + :octet-align=1#112 < GSM#3 > :octet-align=1#112 + :octet-align=1#112 > /0#0 < :octet-align=1#112 + :octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112 + :octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112 + :octet-align=1#112 < encoding-name#23 > :octet-align=1#112 + :octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112 + :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112 + :octet-align=1#112 < AMR:mode-set=0,2,4#114 > :octet-align=1#112 + :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112 + :octet-align=1#112 < AMR:octet-align=1#98 > :octet-align=1#112 + :octet-align=1#112 < AMR-WB/16000#96 > :octet-align=1#112 + :octet-align=1#112 < GSM#3 > :octet-align=1#112 + :octet-align=1#112 > /0#0 < :octet-align=1#112 + :octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112 + :octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112 + +- osmo_sdp_codec_from_str(): + osmo_sdp_codec_from_str('encoding-name#23') rc=0 res=encoding-name#23 + osmo_sdp_codec_from_str('AMR:octet-align=1;mode-set=0,2,4#112') rc=0 res=AMR:octet-align=1;mode-set=0,2,4#112 + osmo_sdp_codec_from_str('AMR:mode-set=0,2,4;octet-align=1#96') rc=0 res=AMR:mode-set=0,2,4;octet-align=1#96 + osmo_sdp_codec_from_str('AMR:mode-set=0,2,4#114') rc=0 res=AMR:mode-set=0,2,4#114 + osmo_sdp_codec_from_str('AMR:mode-set=0,2,4;octet-align=0#97') rc=0 res=AMR:mode-set=0,2,4;octet-align=0#97 + osmo_sdp_codec_from_str('AMR:octet-align=1#98') rc=0 res=AMR:octet-align=1#98 + osmo_sdp_codec_from_str('AMR-WB/16000#96') rc=0 res=AMR-WB/16000#96 + osmo_sdp_codec_from_str('GSM#3') rc=0 res=GSM#3 + osmo_sdp_codec_from_str('/0#0') rc=0 res=/0#0 + osmo_sdp_codec_from_str(':octet-align=1#112') rc=0 res=:octet-align=1#112 + osmo_sdp_codec_from_str(':octet-align=1#112') rc=0 res=:octet-align=1#112 + + +--- test_obj_members() +o->codec = AMR:octet-align=1#96 +ctx + | 5 test_obj_members + | 4 struct my_obj + | 3 struct osmo_sdp_codec + | 1 octet-align=1 + | 1 AMR +talloc_free(o) +ctx + | 1 test_obj_members diff --git a/tests/testsuite.at b/tests/testsuite.at index eb1481a62..d5512d478 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -20,3 +20,10 @@ cat $abs_srcdir/sdp/sdp_fmtp_test.ok > expout cat $abs_srcdir/sdp/sdp_fmtp_test.err > experr AT_CHECK([$abs_top_builddir/tests/sdp/sdp_fmtp_test], [], [expout], [experr]) AT_CLEANUP + +AT_SETUP([sdp_codec]) +AT_KEYWORDS([sdp_codec]) +cat $abs_srcdir/sdp/sdp_codec_test.ok > expout +cat $abs_srcdir/sdp/sdp_codec_test.err > experr +AT_CHECK([$abs_top_builddir/tests/sdp/sdp_codec_test], [], [expout], [experr]) +AT_CLEANUP