add libosmo-sdp: sdp_codec.h,c

The primary user is osmo-msc. This API's predecessor currently lives in
osmo-msc.git and is now being moved here, in a matured form.

Besides osmo-msc, any MGCP client program may need to handle SDP codecs.

(Besides clients, osmo-mgw itself may use this SDP implementation to
untangle parsing from the MGCP server code, in some distant future.)

Change-Id: I2cec2667796dae91441ad9357d11854239880aea
This commit is contained in:
Neels Hofmeyr
2024-02-16 02:51:20 +01:00
parent ef5f1d3d7b
commit 0051a29df9
12 changed files with 881 additions and 0 deletions

View File

@@ -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)

View File

@@ -0,0 +1,3 @@
noinst_HEADERS = \
sdp_internal.h \
$(NULL)

View File

@@ -0,0 +1,123 @@
/* Public API for codec management in SDP messages. */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
/* RFC-8866 5.14 and 6.6.
*
* Represent the items describing an SDP codec entry, as in:
*
* m=audio 1234 RTP/AVP <payload_type>
* a=rtpmap:<payload_type> <encoding-name>/<clock-rate>
* a=fmtp:<payload_type> <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:<payload-type> <format-specific-params>'. 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);

View File

@@ -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);

View File

@@ -19,6 +19,8 @@ lib_LTLIBRARIES = \
$(NULL)
libosmo_sdp_la_SOURCES = \
sdp_codec.c \
sdp_internal.c \
fmtp.c \
$(NULL)

251
src/libosmo-sdp/sdp_codec.c Normal file
View File

@@ -0,0 +1,251 @@
/* Codec management in SDP messages. */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include <ctype.h>
#include <osmocom/core/utils.h>
#include <osmocom/sdp/fmtp.h>
#include <osmocom/sdp/sdp_codec.h>
#include <osmocom/sdp/sdp_internal.h>
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 <encoding_name>[:<fmtp-string>][#<payload-type-nr>]
* 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;
}

View File

@@ -0,0 +1,47 @@
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include <stddef.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/sdp/sdp_internal.h>
/* 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);
}

View File

@@ -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

237
tests/sdp/sdp_codec_test.c Normal file
View File

@@ -0,0 +1,237 @@
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/sdp/sdp_codec.h>
#include <osmocom/sdp/fmtp.h>
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;
}

View File

186
tests/sdp/sdp_codec_test.ok Normal file
View File

@@ -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

View File

@@ -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