From c43d76dc4d594bbc155b21562b6b9cc4c228f721 Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Fri, 16 Feb 2024 19:04:21 +0100 Subject: [PATCH] add libosmo-sdp: osmo_sdp_codec_list.h,c, first part Change-Id: If170566c666c4f4010091bc90912b13a12f77de8 --- include/Makefile.am | 1 + include/osmocom/sdp/sdp_codec.h | 3 + include/osmocom/sdp/sdp_codec_list.h | 52 ++++++++ src/libosmo-sdp/Makefile.am | 1 + src/libosmo-sdp/sdp_codec_list.c | 151 +++++++++++++++++++++ tests/sdp/sdp_codec_test.c | 117 ++++++++++++++++- tests/sdp/sdp_codec_test.ok | 187 ++++++++++++++++++++++++++- 7 files changed, 509 insertions(+), 3 deletions(-) create mode 100644 include/osmocom/sdp/sdp_codec_list.h create mode 100644 src/libosmo-sdp/sdp_codec_list.c diff --git a/include/Makefile.am b/include/Makefile.am index ec024cce9..02b9d8027 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -10,6 +10,7 @@ nobase_include_HEADERS = \ osmocom/mgcp_client/mgcp_client_pool.h \ osmocom/sdp/fmtp.h \ osmocom/sdp/sdp_codec.h \ + osmocom/sdp/sdp_codec_list.h \ osmocom/sdp/sdp_strings.h \ $(NULL) diff --git a/include/osmocom/sdp/sdp_codec.h b/include/osmocom/sdp/sdp_codec.h index 2a0cf8a98..d4974b25f 100644 --- a/include/osmocom/sdp/sdp_codec.h +++ b/include/osmocom/sdp/sdp_codec.h @@ -67,6 +67,9 @@ struct osmo_sdp_codec { * holds only the , "param1=val1;param2=val2" part. For the buffer size, see fmtp_size. */ char *fmtp; + /* Entry used by osmo_sdp_codec_list. */ + struct llist_head entry; + /* For future extension, always set to false. */ bool v2; }; diff --git a/include/osmocom/sdp/sdp_codec_list.h b/include/osmocom/sdp/sdp_codec_list.h new file mode 100644 index 000000000..42ec4674c --- /dev/null +++ b/include/osmocom/sdp/sdp_codec_list.h @@ -0,0 +1,52 @@ +/* Public API for codec management in SDP messages: list of struct osmo_sdp_codec. */ +/* + * (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 + +struct osmo_sdp_codec_list { + struct llist_head list; + + /* For future extension, always set to false. */ + bool v2; +}; + +struct osmo_sdp_codec_list *osmo_sdp_codec_list_alloc(void *ctx); +void osmo_sdp_codec_list_free_items(struct osmo_sdp_codec_list *codec_list); + +int8_t osmo_sdp_codec_list_get_unused_dyn_pt_nr(const struct osmo_sdp_codec_list *codec_list, int8_t suggest_pt_nr); + +struct osmo_sdp_codec *osmo_sdp_codec_list_add_empty(struct osmo_sdp_codec_list *codec_list); +struct osmo_sdp_codec *osmo_sdp_codec_list_add(struct osmo_sdp_codec_list *codec_list, + const struct osmo_sdp_codec *codec, + const struct osmo_sdp_codec_cmp_flags *once, bool pick_unused_pt_nr); + +int osmo_sdp_codec_list_remove(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec, + const struct osmo_sdp_codec_cmp_flags *cmpf); +void osmo_sdp_codec_list_remove_entry(struct osmo_sdp_codec *codec); + +#define osmo_sdp_codec_list_foreach(STRUCT_SDP_CODEC_P, SDP_CODEC_LIST) \ + llist_for_each_entry(STRUCT_SDP_CODEC_P, &(SDP_CODEC_LIST)->list, entry) +#define osmo_sdp_codec_list_foreach_safe(STRUCT_SDP_CODEC_P, SAFE_P, SDP_CODEC_LIST) \ + llist_for_each_entry_safe(STRUCT_SDP_CODEC_P, SAFE_P, &(SDP_CODEC_LIST)->list, entry) diff --git a/src/libosmo-sdp/Makefile.am b/src/libosmo-sdp/Makefile.am index 6e5de1b84..7901065e1 100644 --- a/src/libosmo-sdp/Makefile.am +++ b/src/libosmo-sdp/Makefile.am @@ -20,6 +20,7 @@ lib_LTLIBRARIES = \ libosmo_sdp_la_SOURCES = \ sdp_codec.c \ + sdp_codec_list.c \ sdp_internal.c \ fmtp.c \ $(NULL) diff --git a/src/libosmo-sdp/sdp_codec_list.c b/src/libosmo-sdp/sdp_codec_list.c new file mode 100644 index 000000000..ca5c20e7c --- /dev/null +++ b/src/libosmo-sdp/sdp_codec_list.c @@ -0,0 +1,151 @@ +/* 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 + +struct osmo_sdp_codec_list *osmo_sdp_codec_list_alloc(void *ctx) +{ + struct osmo_sdp_codec_list *codec_list = talloc_zero(ctx, struct osmo_sdp_codec_list); + INIT_LLIST_HEAD(&codec_list->list); + return codec_list; +} + +/*! Free all items contained in this list, do not free the list itself (leave an empty list). */ +void osmo_sdp_codec_list_free_items(struct osmo_sdp_codec_list *codec_list) +{ + struct osmo_sdp_codec *c; + while ((c = llist_first_entry_or_null(&codec_list->list, struct osmo_sdp_codec, entry))) { + osmo_sdp_codec_list_remove_entry(c); + talloc_free(c); + } +} + +struct osmo_sdp_codec *osmo_sdp_codec_list_add_empty(struct osmo_sdp_codec_list *codec_list) +{ + struct osmo_sdp_codec *c = osmo_sdp_codec_alloc(codec_list); + llist_add_tail(&c->entry, &codec_list->list); + return c; +} + +int8_t osmo_sdp_codec_list_get_unused_dyn_pt_nr(const struct osmo_sdp_codec_list *codec_list, int8_t suggest_pt_nr) +{ + bool present[127 - 96 + 1] = {}; + const struct osmo_sdp_codec *c; + bool suggest_pt_nr_exists = false; + int i; + + osmo_sdp_codec_list_foreach (c, codec_list) { + if (c->payload_type >= 96 && c->payload_type <= 127) + present[c->payload_type - 96] = true; + if (c->payload_type == suggest_pt_nr) + suggest_pt_nr_exists = true; + } + + if (!suggest_pt_nr_exists) + return suggest_pt_nr; + + /* The desired number is already taken, see which of the dynamic types is not taken yet */ + for (i = 96; i <= 127; i++) { + /* For dynamic allocations, skip these predefined numbers, taken from enum mgcp_codecs: + * CODEC_GSMEFR_8000_1 = 110, 3GPP TS 48.103 table 5.4.2.2.1 + * CODEC_GSMHR_8000_1 = 111, 3GPP TS 48.103 table 5.4.2.2.1 + * CODEC_AMR_8000_1 = 112, 3GPP TS 48.103 table 5.4.2.2.1 + * CODEC_AMRWB_16000_1 = 113, 3GPP TS 48.103 table 5.4.2.2.1 + * CODEC_CLEARMODE = 120, 3GPP TS 48.103 table 5.4.2.2.1 + */ + if (i >= 110 && i <= 113) + continue; + else if (i == 120) + continue; + + if (!present[i - 96]) + return i; + } + + return -1; +} + +/*! Allocate a new entry in codec_list and copy codec's values to it. + * If once is NULL, unconditionally add a new codec entry. + * If once is non-NULL, do not add a new entry when the list already contains a matching entry; for determining a match, + * use the once->flags. For example, if once = &osmo_sdp_codec_cmp_equivalent, look up if codec_list has a similar + * codec, and add the new entry only if it is not listed. + * See osmo_sdp_codec_cmp() and osmo_sdp_fmtp_amr_match() for details. + * Return the new entry, or the equivalent entry already present in the list. + */ +struct osmo_sdp_codec *osmo_sdp_codec_list_add(struct osmo_sdp_codec_list *codec_list, + const struct osmo_sdp_codec *codec, + const struct osmo_sdp_codec_cmp_flags *once, bool pick_unused_pt_nr) +{ + struct osmo_sdp_codec *new_entry; + int8_t payload_type; + + if (once) { + struct osmo_sdp_codec *c; + osmo_sdp_codec_list_foreach (c, codec_list) + if (!osmo_sdp_codec_cmp(codec, c, once)) + return c; + } + + /* Adjust payload_type number? */ + payload_type = codec->payload_type; + if (pick_unused_pt_nr) + payload_type = osmo_sdp_codec_list_get_unused_dyn_pt_nr(codec_list, payload_type); + + /* Take provided values, possibly modified payload_type */ + new_entry = osmo_sdp_codec_list_add_empty(codec_list); + osmo_sdp_codec_set(new_entry, payload_type, codec->encoding_name, codec->rate, codec->fmtp); + + return new_entry; +} + +/*! Remove and free all entries from the codec_list that match the given codec according to osmo_sdp_codec_cmp(cmpf). + * Return the number of entries freed. */ +int osmo_sdp_codec_list_remove(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec, + const struct osmo_sdp_codec_cmp_flags *cmpf) +{ + struct osmo_sdp_codec *i, *j; + int count = 0; + osmo_sdp_codec_list_foreach_safe (i, j, codec_list) { + if (osmo_sdp_codec_cmp(i, codec, cmpf)) + continue; + osmo_sdp_codec_list_remove_entry(i); + talloc_free(i); + count++; + } + return count; +} + +/*! Unlink an osmo_sdp_codec from an osmo_sdp_codec_list, if the codec instance is part of a list. Do not free the + * struct osmo_sdp_codec. + */ +void osmo_sdp_codec_list_remove_entry(struct osmo_sdp_codec *codec) +{ + /* The codec is not part of a list in these cases: + * After talloc_zero(), next == NULL. + * After llist_del(), next == LLIST_POISON1. */ + if (codec->entry.next != NULL + && codec->entry.next != (struct llist_head *)LLIST_POISON1) + llist_del(&codec->entry); +} diff --git a/tests/sdp/sdp_codec_test.c b/tests/sdp/sdp_codec_test.c index 46e9372a9..bdef0d9a4 100644 --- a/tests/sdp/sdp_codec_test.c +++ b/tests/sdp/sdp_codec_test.c @@ -8,7 +8,7 @@ #include #include -#include +#include #include void *test_ctx = NULL; @@ -176,14 +176,118 @@ void test_codec(void) talloc_free(ctx); } +void test_codec_list(void) +{ + void *list_ctx = talloc_named_const(test_ctx, 0, __func__); + void *print_ctx = talloc_named_const(test_ctx, 0, "print"); + int i; + int rc; + struct osmo_sdp_codec *codec; + + const struct osmo_sdp_codec all_codecs[] = { + { .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" }, + { .payload_type = 3, .encoding_name = "GSM", .rate = 8000 }, + { .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 }, + }; + + struct osmo_sdp_codec_list *codec_list; + + printf("\n\n--- %s()\n", __func__); + + codec_list = osmo_sdp_codec_list_alloc(list_ctx); + report(list_ctx); + + for (i = 0; i < ARRAY_SIZE(all_codecs); i++) { + struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], NULL, false); + printf("[%d] osmo_sdp_codec_list_add(%s)\n", i, osmo_sdp_codec_to_str_c(print_ctx, added)); + } + + i = 0; + osmo_sdp_codec_list_foreach(codec, codec_list) { + printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec)); + } + report(list_ctx); + + printf("\n"); + printf("- add same entries again with once=exact, nothing should change\n"); + for (i = 0; i < ARRAY_SIZE(all_codecs); i++) { + struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], + &osmo_sdp_codec_cmp_exact, false); + printf("[] osmo_sdp_codec_list_add(%s)\n", osmo_sdp_codec_to_str_c(print_ctx, added)); + } + i = 0; + osmo_sdp_codec_list_foreach(codec, codec_list) { + printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec)); + } + report(list_ctx); + + printf("\n"); + printf("- add same entries again with once=NULL, duplicates are added\n"); + for (i = 0; i < ARRAY_SIZE(all_codecs); i++) { + struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], NULL, false); + printf("[] osmo_sdp_codec_list_add(%s)\n", osmo_sdp_codec_to_str_c(print_ctx, added)); + } + i = 0; + osmo_sdp_codec_list_foreach(codec, codec_list) { + printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec)); + } + report(list_ctx); + + printf("\n"); + printf("- add same entries again with once=NULL,pick_unused_pt_nr=true, duplicates are added with new #nr\n"); + for (i = 0; i < ARRAY_SIZE(all_codecs); i++) { + struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], NULL, true); + printf("[] osmo_sdp_codec_list_add(%s)\n", osmo_sdp_codec_to_str_c(print_ctx, added)); + } + i = 0; + osmo_sdp_codec_list_foreach(codec, codec_list) { + printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec)); + } + report(list_ctx); + + printf("\n"); + printf("- remove all 'GSM#3' entries, with osmo_sdp_codec_cmp_exact\n"); + rc = osmo_sdp_codec_list_remove(codec_list, &all_codecs[1], &osmo_sdp_codec_cmp_exact); + printf(" osmo_sdp_codec_list_remove() = %d\n", rc); + i = 0; + osmo_sdp_codec_list_foreach(codec, codec_list) { + printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec)); + } + report(list_ctx); + + + printf("- remove all 'GSM' entries, with osmo_sdp_codec_cmp_equivalent\n"); + rc = osmo_sdp_codec_list_remove(codec_list, &all_codecs[1], &osmo_sdp_codec_cmp_equivalent); + printf(" osmo_sdp_codec_list_remove() = %d\n", rc); + i = 0; + osmo_sdp_codec_list_foreach(codec, codec_list) { + printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec)); + } + report(list_ctx); + + printf("- osmo_sdp_codec_list_free_items()\n"); + osmo_sdp_codec_list_free_items(codec_list); + i = 0; + osmo_sdp_codec_list_foreach(codec, codec_list) { + printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec)); + } + printf(" %d entries\n", i); + report(list_ctx); + + talloc_free(print_ctx); + talloc_free(list_ctx); +} + struct my_obj { struct osmo_sdp_codec *codec; + struct osmo_sdp_codec_list *codec_list; }; struct my_obj *my_obj_alloc(void *ctx) { struct my_obj *o = talloc_zero(ctx, struct my_obj); + o->codec_list = osmo_sdp_codec_list_alloc(o); return o; } @@ -191,6 +295,8 @@ void test_obj_members(void) { void *ctx = talloc_named_const(test_ctx, 0, __func__); void *print_ctx = talloc_named_const(test_ctx, 0, "print"); + int i; + struct osmo_sdp_codec *codec; struct my_obj *o; @@ -201,6 +307,14 @@ void test_obj_members(void) 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); + + osmo_sdp_codec_list_add(o->codec_list, o->codec, false, false); + osmo_sdp_codec_list_add(o->codec_list, o->codec, false, true); + i = 0; + osmo_sdp_codec_list_foreach(codec, o->codec_list) { + printf("o->codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec)); + } report(ctx); printf("talloc_free(o)\n"); @@ -213,6 +327,7 @@ void test_obj_members(void) typedef void (*test_func_t)(void); test_func_t test_func[] = { test_codec, + test_codec_list, test_obj_members, }; diff --git a/tests/sdp/sdp_codec_test.ok b/tests/sdp/sdp_codec_test.ok index cec73863f..666bcee85 100644 --- a/tests/sdp/sdp_codec_test.ok +++ b/tests/sdp/sdp_codec_test.ok @@ -173,14 +173,197 @@ osmo_sdp_codec_set [10] ':octet-align=1#112' osmo_sdp_codec_from_str(':octet-align=1#112') rc=0 res=:octet-align=1#112 +--- test_codec_list() +list_ctx + | 2 test_codec_list + | 1 struct osmo_sdp_codec_list +[0] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#112) +[1] osmo_sdp_codec_list_add(GSM#3) +[2] osmo_sdp_codec_list_add(GSM-HR-08#111) +codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112 +codec_list[1] = GSM#3 +codec_list[2] = GSM-HR-08#111 +list_ctx + | 9 test_codec_list + | 8 struct osmo_sdp_codec_list + | 2 struct osmo_sdp_codec + | 1 GSM-HR-08 + | 2 struct osmo_sdp_codec + | 1 GSM + | 3 struct osmo_sdp_codec + | 1 octet-align=1;mode-set=0,2,4 + | 1 AMR + +- add same entries again with once=exact, nothing should change +[] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#112) +[] osmo_sdp_codec_list_add(GSM#3) +[] osmo_sdp_codec_list_add(GSM-HR-08#111) +codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112 +codec_list[1] = GSM#3 +codec_list[2] = GSM-HR-08#111 +list_ctx + | 9 test_codec_list + | 8 struct osmo_sdp_codec_list + | 2 struct osmo_sdp_codec + | 1 GSM-HR-08 + | 2 struct osmo_sdp_codec + | 1 GSM + | 3 struct osmo_sdp_codec + | 1 octet-align=1;mode-set=0,2,4 + | 1 AMR + +- add same entries again with once=NULL, duplicates are added +[] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#112) +[] osmo_sdp_codec_list_add(GSM#3) +[] osmo_sdp_codec_list_add(GSM-HR-08#111) +codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112 +codec_list[1] = GSM#3 +codec_list[2] = GSM-HR-08#111 +codec_list[3] = AMR:octet-align=1;mode-set=0,2,4#112 +codec_list[4] = GSM#3 +codec_list[5] = GSM-HR-08#111 +list_ctx + | 16 test_codec_list + | 15 struct osmo_sdp_codec_list + | 2 struct osmo_sdp_codec + | 1 GSM-HR-08 + | 2 struct osmo_sdp_codec + | 1 GSM + | 3 struct osmo_sdp_codec + | 1 octet-align=1;mode-set=0,2,4 + | 1 AMR + | 2 struct osmo_sdp_codec + | 1 GSM-HR-08 + | 2 struct osmo_sdp_codec + | 1 GSM + | 3 struct osmo_sdp_codec + | 1 octet-align=1;mode-set=0,2,4 + | 1 AMR + +- add same entries again with once=NULL,pick_unused_pt_nr=true, duplicates are added with new #nr +[] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#96) +[] osmo_sdp_codec_list_add(GSM#97) +[] osmo_sdp_codec_list_add(GSM-HR-08#98) +codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112 +codec_list[1] = GSM#3 +codec_list[2] = GSM-HR-08#111 +codec_list[3] = AMR:octet-align=1;mode-set=0,2,4#112 +codec_list[4] = GSM#3 +codec_list[5] = GSM-HR-08#111 +codec_list[6] = AMR:octet-align=1;mode-set=0,2,4#96 +codec_list[7] = GSM#97 +codec_list[8] = GSM-HR-08#98 +list_ctx + | 23 test_codec_list + | 22 struct osmo_sdp_codec_list + | 2 struct osmo_sdp_codec + | 1 GSM-HR-08 + | 2 struct osmo_sdp_codec + | 1 GSM + | 3 struct osmo_sdp_codec + | 1 octet-align=1;mode-set=0,2,4 + | 1 AMR + | 2 struct osmo_sdp_codec + | 1 GSM-HR-08 + | 2 struct osmo_sdp_codec + | 1 GSM + | 3 struct osmo_sdp_codec + | 1 octet-align=1;mode-set=0,2,4 + | 1 AMR + | 2 struct osmo_sdp_codec + | 1 GSM-HR-08 + | 2 struct osmo_sdp_codec + | 1 GSM + | 3 struct osmo_sdp_codec + | 1 octet-align=1;mode-set=0,2,4 + | 1 AMR + +- remove all 'GSM#3' entries, with osmo_sdp_codec_cmp_exact + osmo_sdp_codec_list_remove() = 2 +codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112 +codec_list[1] = GSM-HR-08#111 +codec_list[2] = AMR:octet-align=1;mode-set=0,2,4#112 +codec_list[3] = GSM-HR-08#111 +codec_list[4] = AMR:octet-align=1;mode-set=0,2,4#96 +codec_list[5] = GSM#97 +codec_list[6] = GSM-HR-08#98 +list_ctx + | 19 test_codec_list + | 18 struct osmo_sdp_codec_list + | 2 struct osmo_sdp_codec + | 1 GSM-HR-08 + | 2 struct osmo_sdp_codec + | 1 GSM + | 3 struct osmo_sdp_codec + | 1 octet-align=1;mode-set=0,2,4 + | 1 AMR + | 2 struct osmo_sdp_codec + | 1 GSM-HR-08 + | 3 struct osmo_sdp_codec + | 1 octet-align=1;mode-set=0,2,4 + | 1 AMR + | 2 struct osmo_sdp_codec + | 1 GSM-HR-08 + | 3 struct osmo_sdp_codec + | 1 octet-align=1;mode-set=0,2,4 + | 1 AMR +- remove all 'GSM' entries, with osmo_sdp_codec_cmp_equivalent + osmo_sdp_codec_list_remove() = 1 +codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112 +codec_list[1] = GSM-HR-08#111 +codec_list[2] = AMR:octet-align=1;mode-set=0,2,4#112 +codec_list[3] = GSM-HR-08#111 +codec_list[4] = AMR:octet-align=1;mode-set=0,2,4#96 +codec_list[5] = GSM-HR-08#98 +list_ctx + | 17 test_codec_list + | 16 struct osmo_sdp_codec_list + | 2 struct osmo_sdp_codec + | 1 GSM-HR-08 + | 3 struct osmo_sdp_codec + | 1 octet-align=1;mode-set=0,2,4 + | 1 AMR + | 2 struct osmo_sdp_codec + | 1 GSM-HR-08 + | 3 struct osmo_sdp_codec + | 1 octet-align=1;mode-set=0,2,4 + | 1 AMR + | 2 struct osmo_sdp_codec + | 1 GSM-HR-08 + | 3 struct osmo_sdp_codec + | 1 octet-align=1;mode-set=0,2,4 + | 1 AMR +- osmo_sdp_codec_list_free_items() + 0 entries +list_ctx + | 2 test_codec_list + | 1 struct osmo_sdp_codec_list + + --- test_obj_members() o->codec = AMR:octet-align=1#96 ctx - | 5 test_obj_members - | 4 struct my_obj + | 6 test_obj_members + | 5 struct my_obj | 3 struct osmo_sdp_codec | 1 octet-align=1 | 1 AMR + | 1 struct osmo_sdp_codec_list +o->codec_list[0] = AMR:octet-align=1#96 +o->codec_list[1] = AMR:octet-align=1#97 +ctx + | 12 test_obj_members + | 11 struct my_obj + | 3 struct osmo_sdp_codec + | 1 octet-align=1 + | 1 AMR + | 7 struct osmo_sdp_codec_list + | 3 struct osmo_sdp_codec + | 1 octet-align=1 + | 1 AMR + | 3 struct osmo_sdp_codec + | 1 octet-align=1 + | 1 AMR talloc_free(o) ctx | 1 test_obj_members